mirror of https://github.com/mgba-emu/mgba.git
PopupManager
This commit is contained in:
parent
f48655e2f3
commit
7fd40044bc
|
@ -110,6 +110,7 @@ set(SOURCE_FILES
|
|||
ConfigController.cpp
|
||||
ColorPicker.cpp
|
||||
CoreManager.cpp
|
||||
CoreConsumer.cpp
|
||||
CoreController.cpp
|
||||
Display.cpp
|
||||
DisplayGL.cpp
|
||||
|
@ -152,6 +153,7 @@ set(SOURCE_FILES
|
|||
OverrideView.cpp
|
||||
PaletteView.cpp
|
||||
PlacementControl.cpp
|
||||
PopupManager.cpp
|
||||
RegisterView.cpp
|
||||
ReportView.cpp
|
||||
ROMInfo.cpp
|
||||
|
|
|
@ -0,0 +1,96 @@
|
|||
/* Copyright (c) 2013-2025 Jeffrey Pfau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#include "CoreConsumer.h"
|
||||
|
||||
#include "CoreController.h"
|
||||
|
||||
using namespace QGBA;
|
||||
|
||||
CoreProvider::CoreProvider(std::shared_ptr<CoreController> controller) {
|
||||
setController(controller);
|
||||
}
|
||||
|
||||
CoreProvider::~CoreProvider() {
|
||||
for (CoreConsumer* consumer : m_consumers) {
|
||||
consumer->m_provider = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void CoreProvider::setController(std::shared_ptr<CoreController> controller) {
|
||||
if (m_controller) {
|
||||
// Disconnect all signals from old controller
|
||||
QObject::disconnect(m_controller.get(), nullptr, this, nullptr);
|
||||
}
|
||||
std::shared_ptr<CoreController> oldController = m_controller;
|
||||
m_controller = controller;
|
||||
for (CoreConsumer* consumer : m_consumers) {
|
||||
if (consumer->onControllerChanged) {
|
||||
consumer->onControllerChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CoreProvider::setController(CoreController* controller) {
|
||||
setController(std::shared_ptr<CoreController>(controller));
|
||||
}
|
||||
|
||||
CoreController* CoreProvider::get() const {
|
||||
return m_controller.get();
|
||||
}
|
||||
|
||||
void CoreProvider::addConsumer(CoreConsumer* consumer) {
|
||||
m_consumers.insert(consumer);
|
||||
}
|
||||
|
||||
void CoreProvider::removeConsumer(CoreConsumer* consumer) {
|
||||
m_consumers.erase(consumer);
|
||||
}
|
||||
|
||||
void CoreProvider::swap(std::shared_ptr<CoreController>& controller) {
|
||||
m_controller.swap(controller);
|
||||
}
|
||||
|
||||
CoreConsumer::CoreConsumer(CoreProvider* provider) {
|
||||
setCoreProvider(provider);
|
||||
}
|
||||
|
||||
CoreConsumer::CoreConsumer(const CoreConsumer& other) {
|
||||
setCoreProvider(other.m_provider);
|
||||
}
|
||||
|
||||
CoreConsumer::~CoreConsumer() {
|
||||
if (m_provider) {
|
||||
m_provider->removeConsumer(this);
|
||||
}
|
||||
}
|
||||
|
||||
void CoreConsumer::setCoreProvider(CoreProvider* provider) {
|
||||
if (m_provider) {
|
||||
m_provider->removeConsumer(this);
|
||||
}
|
||||
m_provider = provider;
|
||||
if (provider) {
|
||||
provider->addConsumer(this);
|
||||
}
|
||||
}
|
||||
|
||||
CoreController* CoreConsumer::controller() const {
|
||||
if (!m_provider) {
|
||||
return nullptr;
|
||||
}
|
||||
return m_provider->get();
|
||||
}
|
||||
|
||||
std::shared_ptr<CoreController> CoreConsumer::sharedController() const {
|
||||
if (!m_provider) {
|
||||
return nullptr;
|
||||
}
|
||||
return *m_provider;
|
||||
}
|
||||
|
||||
void CoreConsumer::providerDestroyed() {
|
||||
m_provider = nullptr;
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
/* Copyright (c) 2013-2025 Jeffrey Pfau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include <memory>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace QGBA {
|
||||
|
||||
class CoreController;
|
||||
class CoreConsumer;
|
||||
|
||||
class CoreProvider : public QObject {
|
||||
public:
|
||||
CoreProvider() = default;
|
||||
CoreProvider(std::shared_ptr<CoreController> controller);
|
||||
virtual ~CoreProvider();
|
||||
|
||||
void addConsumer(CoreConsumer* consumer);
|
||||
void removeConsumer(CoreConsumer* consumer);
|
||||
|
||||
void setController(std::shared_ptr<CoreController> controller);
|
||||
void setController(CoreController* controller);
|
||||
CoreController* get() const;
|
||||
inline CoreController* operator->() const { return get(); }
|
||||
inline operator std::shared_ptr<CoreController>&() { return m_controller; }
|
||||
inline operator bool() const { return get(); }
|
||||
|
||||
void swap(std::shared_ptr<CoreController>& controller);
|
||||
|
||||
private:
|
||||
std::shared_ptr<CoreController> m_controller;
|
||||
std::unordered_set<CoreConsumer*> m_consumers;
|
||||
};
|
||||
|
||||
class CoreConsumer {
|
||||
friend class CoreProvider;
|
||||
public:
|
||||
using ControllerCallback = std::function<void()>;
|
||||
|
||||
CoreConsumer() = default;
|
||||
CoreConsumer(const CoreConsumer& other);
|
||||
CoreConsumer(CoreProvider* provider);
|
||||
virtual ~CoreConsumer();
|
||||
|
||||
void setCoreProvider(CoreProvider* provider);
|
||||
inline CoreProvider* coreProvider() const { return m_provider; }
|
||||
CoreController* controller() const;
|
||||
std::shared_ptr<CoreController> sharedController() const;
|
||||
inline operator bool() const { return controller(); }
|
||||
|
||||
ControllerCallback onControllerChanged;
|
||||
|
||||
private:
|
||||
void providerDestroyed();
|
||||
|
||||
CoreProvider* m_provider = nullptr;
|
||||
};
|
||||
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
/* Copyright (c) 2013-2025 Jeffrey Pfau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#include "PopupManager.h"
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
#include "CoreController.h"
|
||||
|
||||
using namespace QGBA;
|
||||
using CorePtr = PopupManagerBase::CorePtr;
|
||||
|
||||
PopupManagerBase::PopupManagerBase(PopupManagerBase::Private* d)
|
||||
: m_d(d)
|
||||
{
|
||||
d->controller.onControllerChanged = [d]{
|
||||
d->updateConnections();
|
||||
d->notifyWindow();
|
||||
};
|
||||
}
|
||||
|
||||
void PopupManagerBase::show() {
|
||||
QWidget* w = construct();
|
||||
if (!w) {
|
||||
return;
|
||||
}
|
||||
if (d()->isModal) {
|
||||
w->setWindowModality(Qt::ApplicationModal);
|
||||
}
|
||||
w->show();
|
||||
w->activateWindow();
|
||||
w->raise();
|
||||
}
|
||||
|
||||
QWidget* PopupManagerBase::construct() {
|
||||
QWidget* w = d()->window();
|
||||
if (w) {
|
||||
return w;
|
||||
}
|
||||
constructImpl();
|
||||
w = d()->window();
|
||||
if (w && d()->keepAlive) {
|
||||
w->setAttribute(Qt::WA_DeleteOnClose);
|
||||
}
|
||||
d()->updateConnections();
|
||||
return w;
|
||||
}
|
||||
|
||||
void PopupManagerBase::Private::setProvider(CoreProvider* provider) {
|
||||
controller.setCoreProvider(provider);
|
||||
updateConnections();
|
||||
}
|
||||
|
||||
void PopupManagerBase::Private::updateConnections() {
|
||||
if (stopConnection) {
|
||||
QObject::disconnect(stopConnection);
|
||||
}
|
||||
CoreController* c = controller.controller();
|
||||
QWidget* w = window();
|
||||
if (c && w) {
|
||||
stopConnection = QObject::connect(c, &CoreController::stopping, w, &QWidget::close);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,176 @@
|
|||
/* Copyright (c) 2013-2025 Jeffrey Pfau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#pragma once
|
||||
|
||||
#include <QExplicitlySharedDataPointer>
|
||||
#include <QPointer>
|
||||
#include <QSharedData>
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
|
||||
#include "CoreConsumer.h"
|
||||
|
||||
namespace QGBA {
|
||||
|
||||
class CoreController;
|
||||
|
||||
class PopupManagerBase {
|
||||
public:
|
||||
using CorePtr = std::shared_ptr<CoreController>;
|
||||
|
||||
PopupManagerBase(const PopupManagerBase&) = default;
|
||||
virtual ~PopupManagerBase() = default;
|
||||
|
||||
QWidget* construct();
|
||||
void show();
|
||||
inline void operator()() { show(); }
|
||||
|
||||
protected:
|
||||
class Private : public QSharedData {
|
||||
public:
|
||||
Private(PopupManagerBase* pub) : pub(pub) {}
|
||||
Private(const Private& other) = default;
|
||||
|
||||
PopupManagerBase* pub;
|
||||
bool isModal = false;
|
||||
bool keepAlive = false;
|
||||
CoreConsumer controller;
|
||||
QMetaObject::Connection stopConnection;
|
||||
|
||||
void setProvider(CoreProvider* provide);
|
||||
void updateConnections();
|
||||
virtual QWidget* window() const = 0;
|
||||
virtual void notifyWindow() = 0;
|
||||
};
|
||||
|
||||
PopupManagerBase(Private* d);
|
||||
|
||||
virtual void constructImpl() = 0;
|
||||
|
||||
virtual Private* d() const { return m_d.data(); }
|
||||
virtual Private* d() { return m_d.data(); }
|
||||
|
||||
private:
|
||||
QExplicitlySharedDataPointer<Private> m_d;
|
||||
};
|
||||
|
||||
template <class WINDOW>
|
||||
class PopupManager : public PopupManagerBase {
|
||||
static_assert(std::is_convertible<WINDOW*, QWidget*>::value, "class must derive from QWidget");
|
||||
|
||||
protected:
|
||||
class Private;
|
||||
Private* d() const override { return static_cast<Private*>(PopupManagerBase::d()); }
|
||||
Private* d() override { return static_cast<Private*>(PopupManagerBase::d()); }
|
||||
|
||||
template <typename T>
|
||||
struct HasSetController {
|
||||
using Pass = char;
|
||||
using Fail = int;
|
||||
struct Base { bool setController; };
|
||||
struct Test : T, Base {};
|
||||
template <typename U> static Fail Check(decltype(U::setController)*);
|
||||
template <typename U> static Pass Check(U*);
|
||||
static constexpr bool value = sizeof(Check<Test>(nullptr)) == sizeof(Pass);
|
||||
};
|
||||
|
||||
public:
|
||||
PopupManager() : PopupManagerBase(new Private(this)) {}
|
||||
PopupManager(const PopupManager&) = default;
|
||||
|
||||
bool isNull() const { return d()->ptr.isNull(); }
|
||||
WINDOW* operator->() const { return d()->ptr; }
|
||||
WINDOW& operator*() const { return &d()->ptr; }
|
||||
operator WINDOW*() const { return d()->ptr; }
|
||||
|
||||
PopupManager& withController(CoreProvider& provider) { d()->setProvider(&provider); return *this; }
|
||||
PopupManager& setModal(bool modal) { d()->isModal = modal; return *this; }
|
||||
PopupManager& setKeepAlive(bool keepAlive) { d()->keepAlive = keepAlive; return *this; }
|
||||
PopupManager& constructWith(const std::function<WINDOW*()>& ctor) { d()->construct = ctor; return *this; }
|
||||
|
||||
template <typename... Args>
|
||||
PopupManager& constructWith(Args... args) {
|
||||
d()->construct = makeConstruct<WINDOW, Args...>(args...);
|
||||
return *this;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void constructImpl() override {
|
||||
Private* d = this->d();
|
||||
if (!d->construct) {
|
||||
qWarning("No valid constructor specified for popup");
|
||||
return;
|
||||
}
|
||||
WINDOW* w = d->construct();
|
||||
if (!w) {
|
||||
qWarning("Constructor did not return a window");
|
||||
return;
|
||||
}
|
||||
d->ptr = w;
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
typename std::enable_if<std::is_constructible<T, CorePtr, Args...>::value, std::function<T*()>>::type makeConstruct(Args... args) {
|
||||
return [=]() -> T* { return new T(d()->controller.sharedController(), args...); };
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
typename std::enable_if<!std::is_constructible<T, CorePtr, Args...>::value, std::function<T*()>>::type makeConstruct(Args... args) {
|
||||
return [=]() -> T* { return new T(args...); };
|
||||
}
|
||||
|
||||
class Private : public PopupManagerBase::Private {
|
||||
template <class T = WINDOW>
|
||||
struct Ctors {
|
||||
static constexpr bool useController = std::is_constructible<T, CorePtr>::value;
|
||||
static constexpr bool useDefault = !useController && std::is_default_constructible<T>::value;
|
||||
static constexpr bool noDefault = !useController && !useDefault;
|
||||
};
|
||||
|
||||
public:
|
||||
template<class T = WINDOW>
|
||||
Private(PopupManagerBase* pub, typename std::enable_if<Ctors<T>::useDefault>::type* = 0)
|
||||
: PopupManagerBase::Private(pub), construct([]{ return new WINDOW(); }) {}
|
||||
|
||||
template<class T = WINDOW>
|
||||
Private(PopupManagerBase* pub, typename std::enable_if<Ctors<T>::useController>::type* = 0)
|
||||
: PopupManagerBase::Private(pub), construct([this]{ return new WINDOW(controller.sharedController()); }) {}
|
||||
|
||||
template<class T = WINDOW>
|
||||
Private(PopupManagerBase* pub, typename std::enable_if<Ctors<T>::noDefault>::type* = 0)
|
||||
: PopupManagerBase::Private(pub) {}
|
||||
|
||||
~Private() {
|
||||
if (ptr && !keepAlive) {
|
||||
ptr->close();
|
||||
ptr->deleteLater();
|
||||
}
|
||||
}
|
||||
|
||||
virtual QWidget* window() const override { return ptr.data(); }
|
||||
|
||||
virtual void notifyWindow() override { notifyWindow<WINDOW>(); }
|
||||
|
||||
template<class T>
|
||||
typename std::enable_if<HasSetController<T>::value>::type notifyWindow() {
|
||||
if (ptr) {
|
||||
ptr->setController(controller.sharedController());
|
||||
}
|
||||
}
|
||||
|
||||
template<class T>
|
||||
typename std::enable_if<!HasSetController<T>::value>::type notifyWindow() {
|
||||
// Nothing to do
|
||||
}
|
||||
|
||||
QPointer<WINDOW> ptr;
|
||||
std::function<WINDOW*()> construct;
|
||||
};
|
||||
};
|
||||
|
||||
}
|
|
@ -1724,7 +1724,8 @@ void Window::setupMenu(QMenuBar* menubar) {
|
|||
|
||||
m_actions.addMenu(tr("&Tools"), "tools");
|
||||
m_actions.addAction(tr("View &logs..."), "viewLogs", openNamedTView(&m_logView, true, &m_log, this), "tools");
|
||||
m_actions.addAction(tr("Game &overrides..."), "overrideWindow", openNamedControllerTView(&m_overrideView, true, m_config, this), "tools");
|
||||
m_overrideView.withController(m_controller).constructWith(m_config, this);
|
||||
m_actions.addAction(tr("Game &overrides..."), "overrideWindow", m_overrideView, "tools");
|
||||
m_actions.addAction(tr("Game Pak sensors..."), "sensorWindow", openNamedControllerTView(&m_sensorView, true, &m_inputController, this), "tools");
|
||||
|
||||
addGameAction(tr("&Cheats..."), "cheatsWindow", openNamedControllerTView(&m_cheatsView, false), "tools");
|
||||
|
@ -2155,7 +2156,7 @@ void Window::setController(CoreController* controller, const QString& fname) {
|
|||
reloadDisplayDriver();
|
||||
}
|
||||
|
||||
m_controller = std::shared_ptr<CoreController>(controller);
|
||||
m_controller.setController(controller);
|
||||
m_controller->setInputController(&m_inputController);
|
||||
m_controller->setLogger(&m_log);
|
||||
|
||||
|
@ -2234,10 +2235,6 @@ void Window::setController(CoreController* controller, const QString& fname) {
|
|||
m_sensorView->setController(m_controller);
|
||||
}
|
||||
|
||||
if (m_overrideView) {
|
||||
m_overrideView->setController(m_controller);
|
||||
}
|
||||
|
||||
if (!m_pendingPatch.isEmpty()) {
|
||||
m_controller->loadPatch(m_pendingPatch);
|
||||
m_pendingPatch = QString();
|
||||
|
|
|
@ -22,6 +22,8 @@
|
|||
#include "InputController.h"
|
||||
#include "LoadSaveState.h"
|
||||
#include "LogController.h"
|
||||
#include "OverrideView.h"
|
||||
#include "PopupManager.h"
|
||||
#include "SettingsView.h"
|
||||
#ifdef ENABLE_SCRIPTING
|
||||
#include "scripting/ScriptingController.h"
|
||||
|
@ -194,7 +196,7 @@ private:
|
|||
QString getFiltersArchive() const;
|
||||
|
||||
CoreManager* m_manager;
|
||||
std::shared_ptr<CoreController> m_controller;
|
||||
CoreProvider m_controller;
|
||||
std::unique_ptr<AudioProcessor> m_audioProcessor;
|
||||
|
||||
std::unique_ptr<QGBA::Display> m_display;
|
||||
|
@ -245,7 +247,7 @@ private:
|
|||
bool m_multiActive = true;
|
||||
int m_playerId;
|
||||
|
||||
QPointer<OverrideView> m_overrideView;
|
||||
PopupManager<OverrideView> m_overrideView;
|
||||
QPointer<SensorView> m_sensorView;
|
||||
QPointer<DolphinConnector> m_dolphinView;
|
||||
QPointer<FrameView> m_frameView;
|
||||
|
|
Loading…
Reference in New Issue