diff options
| author | isanae <14251494+isanae@users.noreply.github.com> | 2019-11-06 05:23:22 -0500 |
|---|---|---|
| committer | isanae <14251494+isanae@users.noreply.github.com> | 2019-11-06 07:45:02 -0500 |
| commit | 642ddacab802df75d21a1ccf7508268d3efa141a (patch) | |
| tree | 2b9cc615fd747da2bd049c9a5625d4b6cedfcb3c | |
| parent | 3d0197fa5e6c4209d021398415f779994c21bd24 (diff) | |
moved LockWidget back to OrganizerCore to support having multiple locks active
moved the ui out of LockWidget into LockInterface
added LockWidget::Session to manage multi locks
| -rw-r--r-- | src/lockwidget.cpp | 490 | ||||
| -rw-r--r-- | src/lockwidget.h | 65 | ||||
| -rw-r--r-- | src/organizercore.cpp | 1 | ||||
| -rw-r--r-- | src/organizercore.h | 3 | ||||
| -rw-r--r-- | src/processrunner.cpp | 33 | ||||
| -rw-r--r-- | src/processrunner.h | 2 |
6 files changed, 414 insertions, 180 deletions
diff --git a/src/lockwidget.cpp b/src/lockwidget.cpp index 35d51dab..68eb9c2d 100644 --- a/src/lockwidget.cpp +++ b/src/lockwidget.cpp @@ -4,203 +4,431 @@ #include <QMenuBar> #include <QStatusBar> -QWidget* createTransparentWidget(QWidget* parent=nullptr) +class LockInterface { - auto* w = new QWidget(parent); +public: + LockInterface() : + m_hasMainUI(false), m_message(nullptr), m_info(nullptr), m_buttons(nullptr) + { + } - w->setWindowOpacity(0); - w->setAttribute(Qt::WA_NoSystemBackground); - w->setAttribute(Qt::WA_TranslucentBackground); + ~LockInterface() + { + } - return w; -} + void set(QWidget* target) + { + QFrame* center = nullptr; + if (target) { + center = createOverlay(target); + } else { + center = createDialog(); + } -LockWidget::LockWidget(QWidget* parent, Reasons reason) : - m_parent(parent), m_overlay(nullptr), m_info(nullptr), m_result(NoResult), - m_filter(nullptr) -{ - if (reason != NoReason) { - lock(reason); + createMessageLabel(); + createInfoLabel(); + createButtonsPanel(); + + center->layout()->addWidget(m_message); + center->layout()->addWidget(m_info); + center->layout()->addWidget(m_buttons); + + m_topLevel->setFocusPolicy(Qt::TabFocus); + m_topLevel->setFocus(); + m_topLevel->show(); + m_topLevel->setEnabled(true); + + m_topLevel->raise(); + m_topLevel->activateWindow(); } -} -LockWidget::~LockWidget() -{ - unlock(); -} + void update(LockWidget::Reasons reason) + { + updateMessage(reason); + updateButtons(reason); + } -void LockWidget::lock(Reasons reason) -{ - m_result = StillLocked; - createUi(reason); -} + void setInfo(const QString& s) + { + m_info->setText(s); + } + + QWidget* topLevel() + { + return m_topLevel.get(); + } + +private: + class Filter : public QObject + { + public: + std::function<void ()> resized; + std::function<void ()> closed; + + protected: + bool eventFilter(QObject* o, QEvent* e) override + { + if (e->type() == QEvent::Resize) { + if (resized) { + resized(); + } + } else if (e->type() == QEvent::Close) { + if (closed) { + closed(); + } + } + + return QObject::eventFilter(o, e); + } + }; -void LockWidget::unlock() -{ - m_overlay.reset(); - if (m_filter && m_parent) { - m_parent->removeEventFilter(m_filter.get()); + bool m_hasMainUI; + std::unique_ptr<QWidget> m_topLevel; + QLabel* m_message; + QLabel* m_info; + QWidget* m_buttons; + std::unique_ptr<Filter> m_filter; + + QWidget* createTransparentWidget(QWidget* parent=nullptr) + { + auto* w = new QWidget(parent); + + w->setWindowOpacity(0); + w->setAttribute(Qt::WA_NoSystemBackground); + w->setAttribute(Qt::WA_TranslucentBackground); + + return w; } - enableAll(); -} + QFrame* createOverlay(QWidget* mainUI) + { + m_hasMainUI = true; -void LockWidget::setInfo(DWORD pid, const QString& name) -{ - m_info->setText(QString("%1 (%2)").arg(name).arg(pid)); -} + m_topLevel.reset(createTransparentWidget(mainUI)); + m_topLevel->setWindowFlags(m_topLevel->windowFlags() & Qt::FramelessWindowHint); + m_topLevel->setGeometry(mainUI->rect()); -LockWidget::Results LockWidget::result() const -{ - return m_result; -} + m_filter.reset(new Filter); + m_filter->resized = [=]{ m_topLevel->setGeometry(mainUI->rect()); }; + m_filter->closed = [=]{ LockWidget::instance().onForceUnlock(); }; -void LockWidget::createUi(Reasons reason) -{ - QWidget* overlayTarget = m_parent; - if (auto* w = qApp->activeWindow()) { - overlayTarget = w; + mainUI->installEventFilter(m_filter.get()); + + return createFrame(); } - if (overlayTarget) { - m_overlay.reset(createTransparentWidget(overlayTarget)); - m_overlay->setWindowFlags(m_overlay->windowFlags() & Qt::FramelessWindowHint); - m_overlay->setGeometry(overlayTarget->rect()); - } else { - m_overlay.reset(new QDialog); + QFrame* createDialog() + { + m_hasMainUI = false; + m_topLevel.reset(new QDialog); + + return createFrame(); } - auto* center = new QFrame; + QFrame* createFrame() + { + auto* frame = new QFrame; + auto* ly = new QVBoxLayout(frame); - if (overlayTarget) { - center->setFrameStyle(QFrame::StyledPanel); - center->setLineWidth(1); - center->setAutoFillBackground(true); + if (m_hasMainUI) { + frame->setFrameStyle(QFrame::StyledPanel); + frame->setLineWidth(1); + frame->setAutoFillBackground(true); - auto* shadow = new QGraphicsDropShadowEffect; - shadow->setBlurRadius(50); - shadow->setOffset(0); - shadow->setColor(QColor(0, 0, 0, 100)); - center->setGraphicsEffect(shadow); - } + auto* shadow = new QGraphicsDropShadowEffect; + shadow->setBlurRadius(50); + shadow->setOffset(0); + shadow->setColor(QColor(0, 0, 0, 100)); + frame->setGraphicsEffect(shadow); + } else { + ly->setContentsMargins(0, 0, 0, 0); + } + + auto* grid = new QGridLayout(m_topLevel.get()); + grid->addWidget(createTransparentWidget(), 0, 1); + grid->addWidget(createTransparentWidget(), 2, 1); + grid->addWidget(createTransparentWidget(), 1, 0); + grid->addWidget(createTransparentWidget(), 1, 2); + grid->addWidget(frame, 1, 1); + + if (!m_hasMainUI) { + grid->setContentsMargins(0, 0, 0, 0); + } + + grid->setRowStretch(0, 1); + grid->setRowStretch(2, 1); + grid->setColumnStretch(0, 1); + grid->setColumnStretch(2, 1); - m_info = new QLabel(" "); - m_info->setAlignment(Qt::AlignCenter | Qt::AlignHCenter); + return frame; + } - auto* ly = new QVBoxLayout(center); + void createMessageLabel() + { + m_message = new QLabel; + } - if (!overlayTarget) { - ly->setContentsMargins(0, 0, 0, 0); + void createInfoLabel() + { + m_info = new QLabel(" "); + m_info->setAlignment(Qt::AlignCenter | Qt::AlignHCenter); } - auto* message = new QLabel; - ly->addWidget(message); - ly->addWidget(m_info); + void createButtonsPanel() + { + m_buttons = new QWidget; + m_buttons->setLayout(new QHBoxLayout); + } - auto* buttons = new QWidget; - auto* buttonsLayout = new QHBoxLayout(buttons); - ly->addWidget(buttons); - switch (reason) + void updateMessage(LockWidget::Reasons reason) { - case LockUI: + switch (reason) { - QString s; + case LockWidget::LockUI: + { + QString s; + + if (m_hasMainUI) { + s = QObject::tr( + "Mod Organizer is locked while the application is running."); + } else { + s = QObject::tr("Mod Organizer is currently running an application."); + } - if (!m_parent) { - s = QObject::tr("Mod Organizer is currently running an application."); - } else { - s = QObject::tr( - "Mod Organizer is locked while the application is running."); + m_message->setText(s); + + break; } - message->setText(s); + case LockWidget::OutputRequired: + { + m_message->setText(QObject::tr( + "The application must run to completion because its output is " + "required.")); - auto* unlockButton = new QPushButton(QObject::tr("Unlock")); - QObject::connect(unlockButton, &QPushButton::clicked, [&]{ onForceUnlock(); }); - buttonsLayout->addWidget(unlockButton); + break; + } - break; + case LockWidget::PreventExit: + { + m_message->setText(QObject::tr( + "Mod Organizer is waiting on application to close before exiting.")); + + break; + } } + } + + void updateButtons(LockWidget::Reasons reason) + { + MOBase::deleteChildWidgets(m_buttons); + auto* ly = m_buttons->layout(); - case OutputRequired: + switch (reason) { - message->setText(QObject::tr( - "The application must run to completion because its output is " - "required.")); + case LockWidget::LockUI: // fall-through + case LockWidget::OutputRequired: + { + auto* unlock = new QPushButton(QObject::tr("Unlock")); - auto* unlockButton = new QPushButton(QObject::tr("Unlock")); - QObject::connect(unlockButton, &QPushButton::clicked, [&]{ onForceUnlock(); }); - buttonsLayout->addWidget(unlockButton); + QObject::connect(unlock, &QPushButton::clicked, [&]{ + LockWidget::instance().onForceUnlock(); + }); - break; + ly->addWidget(unlock); + + break; + } + + case LockWidget::PreventExit: + { + auto* exit = new QPushButton(QObject::tr("Exit Now")); + QObject::connect(exit, &QPushButton::clicked, [&]{ + LockWidget::instance().onForceUnlock(); + }); + + ly->addWidget(exit); + + auto* cancel = new QPushButton(QObject::tr("Cancel")); + QObject::connect(cancel, &QPushButton::clicked, [&]{ + LockWidget::instance().onCancel(); + }); + + ly->addWidget(cancel); + + break; + } } + } +}; - case PreventExit: - { - message->setText(QObject::tr( - "Mod Organizer is waiting on application to close before exiting.")); +LockWidget::Session::~Session() +{ + LockWidget::instance().unlock(this); +} + +void LockWidget::Session::setInfo(DWORD pid, const QString& name) +{ + m_pid = pid; + m_name = name; + + LockWidget::instance().updateLabel(); +} + +DWORD LockWidget::Session::pid() const +{ + return m_pid; +} + +const QString& LockWidget::Session::name() const +{ + return m_name; +} + +LockWidget::Results LockWidget::Session::result() const +{ + return LockWidget::instance().result(); +} + + +static LockWidget* g_instance = nullptr; + + +LockWidget::LockWidget() + : m_parent(nullptr), m_result(NoResult) +{ + Q_ASSERT(!g_instance); + g_instance = this; +} + +LockWidget::~LockWidget() +{ + const auto v = m_sessions; + + for (auto& wp : v) { + if (auto s=wp.lock()) { + unlock(s.get()); + } + } +} + +LockWidget& LockWidget::instance() +{ + Q_ASSERT(g_instance); + return *g_instance; +} - auto* exit = new QPushButton(QObject::tr("Exit Now")); - QObject::connect(exit, &QPushButton::clicked, [&]{ onForceUnlock(); }); - buttonsLayout->addWidget(exit); +void LockWidget::setUserInterface(QWidget* parent) +{ + m_parent = parent; +} + +std::shared_ptr<LockWidget::Session> LockWidget::lock(Reasons reason) +{ + m_result = StillLocked; + createUi(reason); + + auto ls = std::make_shared<Session>(); + m_sessions.push_back(ls); - auto* cancel = new QPushButton(QObject::tr("Cancel")); - QObject::connect(cancel, &QPushButton::clicked, [&]{ onCancel(); }); - buttonsLayout->addWidget(cancel); + updateLabel(); + return ls; +} + +void LockWidget::unlock(Session* s) +{ + auto itor = m_sessions.begin(); + for (;;) { + if (itor == m_sessions.end()) { break; } + + if (auto ss=itor->lock()) { + if (ss.get() == s) { + itor = m_sessions.erase(itor); + continue; + } + } else { + itor = m_sessions.erase(itor); + continue; + } + + ++itor; } - auto* grid = new QGridLayout(m_overlay.get()); - grid->addWidget(createTransparentWidget(), 0, 1); - grid->addWidget(createTransparentWidget(), 2, 1); - grid->addWidget(createTransparentWidget(), 1, 0); - grid->addWidget(createTransparentWidget(), 1, 2); - grid->addWidget(center, 1, 1); + if (m_sessions.empty()) { + m_ui.reset(); + enableAll(); + } else { + updateLabel(); + } +} - if (!overlayTarget) { - grid->setContentsMargins(0, 0, 0, 0); +void LockWidget::unlockCurrent() +{ + if (m_sessions.empty()) { + return; } - grid->setRowStretch(0, 1); - grid->setRowStretch(2, 1); - grid->setColumnStretch(0, 1); - grid->setColumnStretch(2, 1); + auto s = m_sessions.back().lock(); + if (!s) { + m_sessions.pop_back(); + return; + } - disableAll(); + unlock(s.get()); +} - if (overlayTarget) { - m_filter.reset(new Filter); - m_filter->resized = [=]{ m_overlay->setGeometry(overlayTarget->rect()); }; - m_filter->closed = [=]{ onForceUnlock(); }; - overlayTarget->installEventFilter(m_filter.get()); +void LockWidget::updateLabel() +{ + QString label; + + for (auto itor=m_sessions.rbegin(); itor!=m_sessions.rend(); ++itor) { + if (auto ss=itor->lock()) { + label += QString("%1 (%2)").arg(ss->name()).arg(ss->pid()); + break; + } } - m_overlay->setFocusPolicy(Qt::TabFocus); - m_overlay->setFocus(); - m_overlay->show(); - m_overlay->setEnabled(true); + m_ui->setInfo(label); +} + +LockWidget::Results LockWidget::result() const +{ + return m_result; +} + +void LockWidget::createUi(Reasons reason) +{ + QWidget* target = m_parent; + if (auto* w = qApp->activeWindow()) { + target = w; + } - if (!overlayTarget) { - m_overlay->raise(); - m_overlay->activateWindow(); + if (!m_ui) { + m_ui.reset(new LockInterface); } + + m_ui->set(target); + m_ui->update(reason); + + disableAll(); } void LockWidget::onForceUnlock() { m_result = ForceUnlocked; - unlock(); + unlockCurrent(); } void LockWidget::onCancel() { m_result = Cancelled; - unlock(); + unlockCurrent(); } template <class T> @@ -231,10 +459,10 @@ void LockWidget::disableAll() if (auto* d=dynamic_cast<QDialog*>(w)) { // don't disable stuff if this dialog is the overlay, which happens when // there's no ui - if (d != m_overlay.get()) { + if (d != m_ui->topLevel()) { // no central widget, just disable the children, except for the overlay for (auto* child : findChildrenImmediate<QWidget*>(d)) { - if (child != m_overlay.get()) { + if (child != m_ui->topLevel()) { disable(child); } } diff --git a/src/lockwidget.h b/src/lockwidget.h index d3ff2f7d..8062c478 100644 --- a/src/lockwidget.h +++ b/src/lockwidget.h @@ -2,8 +2,12 @@ #include <QMainWindow> +class LockInterface; + class LockWidget { + friend class LockInterface; + public: // reason to show the widget // @@ -37,51 +41,50 @@ public: Cancelled }; - // if `reason` is not NoReason, lock() is called with it - // - LockWidget(QWidget* parent, Reasons reason=NoReason); - ~LockWidget(); - - void lock(Reasons reason); - void unlock(); - void setInfo(DWORD pid, const QString& name); - Results result() const; - -private: - class Filter : public QObject + class Session { public: - std::function<void ()> resized; - std::function<void ()> closed; + ~Session(); - protected: - bool eventFilter(QObject* o, QEvent* e) override - { - if (e->type() == QEvent::Resize) { - if (resized) { - resized(); - } - } else if (e->type() == QEvent::Close) { - if (closed) { - closed(); - } - } + void setInfo(DWORD pid, const QString& name); + Results result() const; - return QObject::eventFilter(o, e); - } + DWORD pid() const; + const QString& name() const; + + private: + DWORD m_pid; + QString m_name; }; + // if `reason` is not NoReason, lock() is called with it + // + LockWidget(); + ~LockWidget(); + + static LockWidget& instance(); + + void setUserInterface(QWidget* parent); + + std::shared_ptr<Session> lock(Reasons reason); + + Results result() const; + +private: QWidget* m_parent; - std::unique_ptr<QWidget> m_overlay; - QLabel* m_info; + std::unique_ptr<LockInterface> m_ui; + std::vector<std::weak_ptr<Session>> m_sessions; Results m_result; - std::unique_ptr<Filter> m_filter; std::vector<QPointer<QWidget>> m_disabled; void createUi(Reasons reason); + void unlockCurrent(); + void unlock(Session* s); + void updateLabel(); + void onForceUnlock(); void onCancel(); diff --git a/src/organizercore.cpp b/src/organizercore.cpp index dda60f76..3d4d8e4b 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -250,6 +250,7 @@ void OrganizerCore::setUserInterface(IUserInterface* ui) m_InstallationManager.setParentWidget(w); m_Updater.setUserInterface(w); + m_LockWidget.setUserInterface(w); checkForUpdates(); } diff --git a/src/organizercore.h b/src/organizercore.h index 0ba52284..7e0e4b7f 100644 --- a/src/organizercore.h +++ b/src/organizercore.h @@ -14,6 +14,7 @@ #include "usvfsconnector.h"
#include "moshortcut.h"
#include "processrunner.h"
+#include "lockwidget.h"
#include <directoryentry.h>
#include <imoinfo.h>
#include <iplugindiagnose.h>
@@ -343,6 +344,8 @@ private: MOBase::DelayedFileWriter m_PluginListsWriter;
UsvfsConnector m_USVFS;
+ LockWidget m_LockWidget;
+
static CrashDumpsType m_globalCrashDumpsType;
};
diff --git a/src/processrunner.cpp b/src/processrunner.cpp index 220ffe38..d97c00ef 100644 --- a/src/processrunner.cpp +++ b/src/processrunner.cpp @@ -195,7 +195,8 @@ const std::chrono::milliseconds Infinite(-1); // waits for completion, times out after `wait` if not Infinite // std::optional<ProcessRunner::Results> timedWait( - HANDLE handle, DWORD pid, LockWidget& lock, std::chrono::milliseconds wait) + HANDLE handle, DWORD pid, LockWidget::Session& ls, + std::chrono::milliseconds wait) { using namespace std::chrono; @@ -220,7 +221,7 @@ std::optional<ProcessRunner::Results> timedWait( QCoreApplication::processEvents(); // check the lock widget - switch (lock.result()) + switch (ls.result()) { case LockWidget::StillLocked: { @@ -245,7 +246,7 @@ std::optional<ProcessRunner::Results> timedWait( // shouldn't happen log::debug( "unexpected result {} while waiting for {}", - static_cast<int>(lock.result()), pid); + static_cast<int>(ls.result()), pid); return ProcessRunner::Error; } @@ -263,7 +264,7 @@ std::optional<ProcessRunner::Results> timedWait( } ProcessRunner::Results waitForProcesses( - const std::vector<HANDLE>& initialProcesses, LockWidget& lock) + const std::vector<HANDLE>& initialProcesses, LockWidget::Session& ls) { using namespace std::chrono; @@ -287,7 +288,7 @@ ProcessRunner::Results waitForProcesses( } // update the lock widget - lock.setInfo(p.pid(), p.name()); + ls.setInfo(p.pid(), p.name()); // open the process auto interestingHandle = p.openHandleForWait(); @@ -309,7 +310,7 @@ ProcessRunner::Results waitForProcesses( wait = Infinite; } - const auto r = timedWait(interestingHandle.get(), p.pid(), lock, wait); + const auto r = timedWait(interestingHandle.get(), p.pid(), ls, wait); if (r) { // the process has completed or returned an error return *r; @@ -322,11 +323,11 @@ ProcessRunner::Results waitForProcesses( } ProcessRunner::Results waitForProcess( - HANDLE initialProcess, LPDWORD exitCode, LockWidget& lock) + HANDLE initialProcess, LPDWORD exitCode, LockWidget::Session& ls) { std::vector<HANDLE> processes = {initialProcess}; - const auto r = waitForProcesses(processes, lock); + const auto r = waitForProcesses(processes, ls); // as long as it's not running anymore, try to get the exit code if (exitCode && r != ProcessRunner::Running) { @@ -682,8 +683,8 @@ ProcessRunner::Results ProcessRunner::postRun() auto r = Error; - withLock([&](auto& lock) { - r = waitForProcess(m_handle.get(), &m_exitCode, lock); + withLock([&](auto& ls) { + r = waitForProcess(m_handle.get(), &m_exitCode, ls); }); if (r == Completed && (m_waitFlags & Refresh)) { @@ -740,14 +741,14 @@ ProcessRunner::Results ProcessRunner::waitForAllUSVFSProcessesWithLock( auto r = Error; - withLock([&](auto& lock) { + withLock([&](auto& ls) { for (;;) { const auto processes = getRunningUSVFSProcesses(); if (processes.empty()) { break; } - r = waitForProcesses(processes, lock); + r = waitForProcesses(processes, ls); if (r != Completed) { // error, cancelled, or unlocked @@ -763,10 +764,8 @@ ProcessRunner::Results ProcessRunner::waitForAllUSVFSProcessesWithLock( return r; } -void ProcessRunner::withLock(std::function<void (LockWidget&)> f) +void ProcessRunner::withLock(std::function<void (LockWidget::Session&)> f) { - auto lk = std::make_unique<LockWidget>( - m_ui ? m_ui->qtWidget() : nullptr, m_lockReason); - - f(*lk); + auto ls = LockWidget::instance().lock(m_lockReason); + f(*ls); } diff --git a/src/processrunner.h b/src/processrunner.h index fd451e38..276c46b1 100644 --- a/src/processrunner.h +++ b/src/processrunner.h @@ -164,7 +164,7 @@ private: // creates the lock widget and calls f() // - void withLock(std::function<void (LockWidget&)> f); + void withLock(std::function<void (LockWidget::Session&)> f); }; Q_DECLARE_OPERATORS_FOR_FLAGS(ProcessRunner::WaitFlags); |
