From decd5c1828f495be4e230c9fc6fb79dd9bfdfb81 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Wed, 6 Nov 2019 07:28:11 -0500 Subject: renamed lockwidget files to uilocker --- src/CMakeLists.txt | 6 +- src/iuserinterface.h | 1 - src/lockwidget.cpp | 548 --------------------------------------------------- src/lockwidget.h | 95 --------- src/organizercore.h | 2 +- src/processrunner.h | 2 +- src/uilocker.cpp | 548 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/uilocker.h | 95 +++++++++ 8 files changed, 648 insertions(+), 649 deletions(-) delete mode 100644 src/lockwidget.cpp delete mode 100644 src/lockwidget.h create mode 100644 src/uilocker.cpp create mode 100644 src/uilocker.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 06f2cd1e..d935981d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -141,7 +141,7 @@ SET(organizer_SRCS colortable.cpp sanitychecks.cpp processrunner.cpp - lockwidget.cpp + uilocker.cpp shared/windows_error.cpp shared/error_report.cpp @@ -262,7 +262,7 @@ SET(organizer_HDRS envwindows.h colortable.h processrunner.h - lockwidget.h + uilocker.h shared/windows_error.h shared/error_report.h @@ -344,6 +344,7 @@ set(core organizerproxy apiuseraccount processrunner + uilocker ) set(dialogs @@ -473,7 +474,6 @@ set(widgets filterwidget icondelegate lcdnumber - lockwidget loglist loghighlighter modflagicondelegate diff --git a/src/iuserinterface.h b/src/iuserinterface.h index 99caceb1..aa48194f 100644 --- a/src/iuserinterface.h +++ b/src/iuserinterface.h @@ -3,7 +3,6 @@ #include "modinfodialogfwd.h" -#include "lockwidget.h" #include #include #include diff --git a/src/lockwidget.cpp b/src/lockwidget.cpp deleted file mode 100644 index 150d2847..00000000 --- a/src/lockwidget.cpp +++ /dev/null @@ -1,548 +0,0 @@ -#include "lockwidget.h" -#include "mainwindow.h" -#include -#include -#include - -class UILockerInterface -{ -public: - UILockerInterface(QWidget* mainUI) : - m_mainUI(mainUI), m_target(nullptr), m_message(nullptr), m_info(nullptr), - m_buttons(nullptr), m_reason(UILocker::NoReason) - { - m_timer.reset(new QTimer); - QObject::connect(m_timer.get(), &QTimer::timeout, [&]{ checkTarget(); }); - m_timer->start(200); - - set(); - } - - ~UILockerInterface() - { - } - - void checkTarget() - { - if (set()) { - update(m_reason); - } - } - - bool set() - { - QWidget* newTarget = nullptr; - - newTarget = m_mainUI; - if (auto* w = QApplication::activeModalWidget()) { - newTarget = w; - } - - if (newTarget == m_target) { - return false; - } - - m_target = newTarget; - - QFrame* center = nullptr; - - if (m_target) { - center = createOverlay(m_target); - } else { - center = createDialog(); - } - - 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(); - - return true; - } - - void update(UILocker::Reasons reason) - { - m_reason = reason; - updateMessage(reason); - updateButtons(reason); - setInfo(m_labels); - } - - void setInfo(const QStringList& labels) - { - const int MaxLabels = 2; - - m_labels = labels; - - QString s; - - if (labels.size() > MaxLabels) { - s = labels.mid(0, MaxLabels).join(", ") + "..."; - } else { - s = labels.join(", "); - } - - m_info->setText(s); - } - - QWidget* topLevel() - { - return m_topLevel.get(); - } - -private: - class Filter : public QObject - { - public: - std::function resized; - std::function 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); - } - }; - - - std::unique_ptr m_timer; - QWidget* m_mainUI; - QWidget* m_target; - std::unique_ptr m_topLevel; - QLabel* m_message; - QLabel* m_info; - QStringList m_labels; - QWidget* m_buttons; - std::unique_ptr m_filter; - UILocker::Reasons m_reason; - - - bool hasMainUI() const - { - return (m_target != nullptr); - } - - 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; - } - - QFrame* createOverlay(QWidget* mainUI) - { - m_topLevel.reset(createTransparentWidget(mainUI)); - m_topLevel->setWindowFlags(m_topLevel->windowFlags() & Qt::FramelessWindowHint); - m_topLevel->setGeometry(mainUI->rect()); - - m_filter.reset(new Filter); - m_filter->resized = [=]{ m_topLevel->setGeometry(mainUI->rect()); }; - m_filter->closed = [=]{ checkTarget(); }; - - mainUI->installEventFilter(m_filter.get()); - - return createFrame(); - } - - QFrame* createDialog() - { - m_topLevel.reset(new QDialog); - - return createFrame(); - } - - QFrame* createFrame() - { - auto* frame = new QFrame; - auto* ly = new QVBoxLayout(frame); - - if (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)); - 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 (!hasMainUI()) { - grid->setContentsMargins(0, 0, 0, 0); - } - - grid->setRowStretch(0, 1); - grid->setRowStretch(2, 1); - grid->setColumnStretch(0, 1); - grid->setColumnStretch(2, 1); - - return frame; - } - - void createMessageLabel() - { - m_message = new QLabel; - m_message->setAlignment(Qt::AlignCenter | Qt::AlignHCenter); - } - - void createInfoLabel() - { - m_info = new QLabel(" "); - m_info->setAlignment(Qt::AlignCenter | Qt::AlignHCenter); - } - - void createButtonsPanel() - { - m_buttons = new QWidget; - m_buttons->setLayout(new QHBoxLayout); - } - - - void updateMessage(UILocker::Reasons reason) - { - switch (reason) - { - case UILocker::LockUI: - { - QString s; - - if (hasMainUI()) { - s = QObject::tr( - "Mod Organizer is locked while the application is running."); - } else { - s = QObject::tr("Mod Organizer is currently running an application."); - } - - m_message->setText(s); - - break; - } - - case UILocker::OutputRequired: - { - m_message->setText(QObject::tr( - "The application must run to completion because its output is " - "required.")); - - break; - } - - case UILocker::PreventExit: - { - m_message->setText(QObject::tr( - "Mod Organizer is waiting on application to close before exiting.")); - - break; - } - } - } - - void updateButtons(UILocker::Reasons reason) - { - MOBase::deleteChildWidgets(m_buttons); - auto* ly = m_buttons->layout(); - - switch (reason) - { - case UILocker::LockUI: // fall-through - case UILocker::OutputRequired: - { - auto* unlock = new QPushButton(QObject::tr("Unlock")); - - QObject::connect(unlock, &QPushButton::clicked, [&]{ - UILocker::instance().onForceUnlock(); - }); - - ly->addWidget(unlock); - - break; - } - - case UILocker::PreventExit: - { - auto* exit = new QPushButton(QObject::tr("Exit Now")); - QObject::connect(exit, &QPushButton::clicked, [&]{ - UILocker::instance().onForceUnlock(); - }); - - ly->addWidget(exit); - - auto* cancel = new QPushButton(QObject::tr("Cancel")); - QObject::connect(cancel, &QPushButton::clicked, [&]{ - UILocker::instance().onCancel(); - }); - - ly->addWidget(cancel); - - break; - } - } - } -}; - -UILocker::Session::~Session() -{ - unlock(); -} - -void UILocker::Session::unlock() -{ - QMetaObject::invokeMethod(qApp, [this]{ - UILocker::instance().unlock(this); - }); -} - -void UILocker::Session::setInfo(DWORD pid, const QString& name) -{ - { - std::scoped_lock lock(m_mutex); - m_pid = pid; - m_name = name; - } - - QMetaObject::invokeMethod(qApp, [this]{ - UILocker::instance().updateLabel(); - }); -} - -DWORD UILocker::Session::pid() const -{ - std::scoped_lock lock(m_mutex); - return m_pid; -} - -const QString& UILocker::Session::name() const -{ - std::scoped_lock lock(m_mutex); - return m_name; -} - -UILocker::Results UILocker::Session::result() const -{ - return UILocker::instance().result(); -} - - -static UILocker* g_instance = nullptr; - - -UILocker::UILocker() - : m_parent(nullptr), m_result(NoResult) -{ - Q_ASSERT(!g_instance); - g_instance = this; -} - -UILocker::~UILocker() -{ - const auto v = m_sessions; - - for (auto& wp : v) { - if (auto s=wp.lock()) { - unlock(s.get()); - } - } -} - -UILocker& UILocker::instance() -{ - Q_ASSERT(g_instance); - return *g_instance; -} - -void UILocker::setUserInterface(QWidget* parent) -{ - m_parent = parent; -} - -std::shared_ptr UILocker::lock(Reasons reason) -{ - m_result = StillLocked; - createUi(reason); - - auto ls = std::make_shared(); - m_sessions.push_back(ls); - - updateLabel(); - - return ls; -} - -void UILocker::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; - } - - if (m_sessions.empty()) { - m_ui.reset(); - enableAll(); - } else { - updateLabel(); - } -} - -void UILocker::unlockCurrent() -{ - if (m_sessions.empty()) { - return; - } - - auto s = m_sessions.back().lock(); - if (!s) { - m_sessions.pop_back(); - return; - } - - unlock(s.get()); -} - -void UILocker::updateLabel() -{ - QStringList labels; - - for (auto itor=m_sessions.rbegin(); itor!=m_sessions.rend(); ++itor) { - if (auto ss=itor->lock()) { - labels.push_back(QString("%1 (%2)").arg(ss->name()).arg(ss->pid())); - } - } - - m_ui->setInfo(labels); -} - -UILocker::Results UILocker::result() const -{ - return m_result; -} - -void UILocker::createUi(Reasons reason) -{ - if (!m_ui) { - m_ui.reset(new UILockerInterface(m_parent)); - } - - m_ui->update(reason); - - disableAll(); -} - -void UILocker::onForceUnlock() -{ - m_result = ForceUnlocked; - unlockCurrent(); -} - -void UILocker::onCancel() -{ - m_result = Cancelled; - unlockCurrent(); -} - -template -QList findChildrenImmediate(QWidget* parent) -{ - return parent->findChildren(QString(), Qt::FindDirectChildrenOnly); -} - -void UILocker::disableAll() -{ - const auto topLevels = QApplication::topLevelWidgets(); - - for (auto* w : topLevels) { - if (auto* mw=dynamic_cast(w)) { - disable(mw->centralWidget()); - disable(mw->menuBar()); - disable(mw->statusBar()); - - for (auto* tb : findChildrenImmediate(w)) { - disable(tb); - } - - for (auto* d : findChildrenImmediate(w)) { - disable(d); - } - } - - if (auto* d=dynamic_cast(w)) { - // don't disable stuff if this dialog is the overlay, which happens when - // there's no ui - if (d != m_ui->topLevel()) { - // no central widget, just disable the children, except for the overlay - for (auto* child : findChildrenImmediate(d)) { - if (child != m_ui->topLevel()) { - disable(child); - } - } - } - } - } -} - -void UILocker::enableAll() -{ - for (auto w : m_disabled) { - if (w) { - w->setEnabled(true); - } - } - - m_disabled.clear(); -} - -void UILocker::disable(QWidget* w) -{ - if (w->isEnabled()) { - w->setEnabled(false); - m_disabled.push_back(w); - } -} diff --git a/src/lockwidget.h b/src/lockwidget.h deleted file mode 100644 index d8c22999..00000000 --- a/src/lockwidget.h +++ /dev/null @@ -1,95 +0,0 @@ -#pragma once - -#include -#include - -class UILockerInterface; - -class UILocker -{ - friend class UILockerInterface; - -public: - // reason to show the widget - // - enum Reasons - { - NoReason = 0, - - // lock the ui - LockUI, - - // because the output is required - OutputRequired, - - // to prevent exiting until all processes are completed - PreventExit - }; - - // returned by result() - // - enum Results - { - NoResult = 0, - - // the widget is still up - StillLocked, - - // force unlock was clicked - ForceUnlocked, - - // cancel was clicked - Cancelled - }; - - - class Session - { - public: - ~Session(); - - void unlock(); - void setInfo(DWORD pid, const QString& name); - Results result() const; - - DWORD pid() const; - const QString& name() const; - - private: - mutable std::mutex m_mutex; - DWORD m_pid; - QString m_name; - }; - - - UILocker(); - ~UILocker(); - - static UILocker& instance(); - - void setUserInterface(QWidget* parent); - - std::shared_ptr lock(Reasons reason); - - Results result() const; - -private: - QWidget* m_parent; - std::unique_ptr m_ui; - std::vector> m_sessions; - std::atomic m_result; - std::vector> m_disabled; - - void createUi(Reasons reason); - - void unlockCurrent(); - void unlock(Session* s); - void updateLabel(); - - void onForceUnlock(); - void onCancel(); - - void disableAll(); - void enableAll(); - void disable(QWidget* w); -}; diff --git a/src/organizercore.h b/src/organizercore.h index 47630dc2..6c9edb9f 100644 --- a/src/organizercore.h +++ b/src/organizercore.h @@ -14,7 +14,7 @@ #include "usvfsconnector.h" #include "moshortcut.h" #include "processrunner.h" -#include "lockwidget.h" +#include "uilocker.h" #include #include #include diff --git a/src/processrunner.h b/src/processrunner.h index af5cd416..c61d6b70 100644 --- a/src/processrunner.h +++ b/src/processrunner.h @@ -2,7 +2,7 @@ #define PROCESSRUNNER_H #include "spawn.h" -#include "lockwidget.h" +#include "uilocker.h" #include "envmodule.h" #include diff --git a/src/uilocker.cpp b/src/uilocker.cpp new file mode 100644 index 00000000..01794d6b --- /dev/null +++ b/src/uilocker.cpp @@ -0,0 +1,548 @@ +#include "uilocker.h" +#include "mainwindow.h" +#include +#include +#include + +class UILockerInterface +{ +public: + UILockerInterface(QWidget* mainUI) : + m_mainUI(mainUI), m_target(nullptr), m_message(nullptr), m_info(nullptr), + m_buttons(nullptr), m_reason(UILocker::NoReason) + { + m_timer.reset(new QTimer); + QObject::connect(m_timer.get(), &QTimer::timeout, [&]{ checkTarget(); }); + m_timer->start(200); + + set(); + } + + ~UILockerInterface() + { + } + + void checkTarget() + { + if (set()) { + update(m_reason); + } + } + + bool set() + { + QWidget* newTarget = nullptr; + + newTarget = m_mainUI; + if (auto* w = QApplication::activeModalWidget()) { + newTarget = w; + } + + if (newTarget == m_target) { + return false; + } + + m_target = newTarget; + + QFrame* center = nullptr; + + if (m_target) { + center = createOverlay(m_target); + } else { + center = createDialog(); + } + + 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(); + + return true; + } + + void update(UILocker::Reasons reason) + { + m_reason = reason; + updateMessage(reason); + updateButtons(reason); + setInfo(m_labels); + } + + void setInfo(const QStringList& labels) + { + const int MaxLabels = 2; + + m_labels = labels; + + QString s; + + if (labels.size() > MaxLabels) { + s = labels.mid(0, MaxLabels).join(", ") + "..."; + } else { + s = labels.join(", "); + } + + m_info->setText(s); + } + + QWidget* topLevel() + { + return m_topLevel.get(); + } + +private: + class Filter : public QObject + { + public: + std::function resized; + std::function 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); + } + }; + + + std::unique_ptr m_timer; + QWidget* m_mainUI; + QWidget* m_target; + std::unique_ptr m_topLevel; + QLabel* m_message; + QLabel* m_info; + QStringList m_labels; + QWidget* m_buttons; + std::unique_ptr m_filter; + UILocker::Reasons m_reason; + + + bool hasMainUI() const + { + return (m_target != nullptr); + } + + 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; + } + + QFrame* createOverlay(QWidget* mainUI) + { + m_topLevel.reset(createTransparentWidget(mainUI)); + m_topLevel->setWindowFlags(m_topLevel->windowFlags() & Qt::FramelessWindowHint); + m_topLevel->setGeometry(mainUI->rect()); + + m_filter.reset(new Filter); + m_filter->resized = [=]{ m_topLevel->setGeometry(mainUI->rect()); }; + m_filter->closed = [=]{ checkTarget(); }; + + mainUI->installEventFilter(m_filter.get()); + + return createFrame(); + } + + QFrame* createDialog() + { + m_topLevel.reset(new QDialog); + + return createFrame(); + } + + QFrame* createFrame() + { + auto* frame = new QFrame; + auto* ly = new QVBoxLayout(frame); + + if (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)); + 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 (!hasMainUI()) { + grid->setContentsMargins(0, 0, 0, 0); + } + + grid->setRowStretch(0, 1); + grid->setRowStretch(2, 1); + grid->setColumnStretch(0, 1); + grid->setColumnStretch(2, 1); + + return frame; + } + + void createMessageLabel() + { + m_message = new QLabel; + m_message->setAlignment(Qt::AlignCenter | Qt::AlignHCenter); + } + + void createInfoLabel() + { + m_info = new QLabel(" "); + m_info->setAlignment(Qt::AlignCenter | Qt::AlignHCenter); + } + + void createButtonsPanel() + { + m_buttons = new QWidget; + m_buttons->setLayout(new QHBoxLayout); + } + + + void updateMessage(UILocker::Reasons reason) + { + switch (reason) + { + case UILocker::LockUI: + { + QString s; + + if (hasMainUI()) { + s = QObject::tr( + "Mod Organizer is locked while the application is running."); + } else { + s = QObject::tr("Mod Organizer is currently running an application."); + } + + m_message->setText(s); + + break; + } + + case UILocker::OutputRequired: + { + m_message->setText(QObject::tr( + "The application must run to completion because its output is " + "required.")); + + break; + } + + case UILocker::PreventExit: + { + m_message->setText(QObject::tr( + "Mod Organizer is waiting on application to close before exiting.")); + + break; + } + } + } + + void updateButtons(UILocker::Reasons reason) + { + MOBase::deleteChildWidgets(m_buttons); + auto* ly = m_buttons->layout(); + + switch (reason) + { + case UILocker::LockUI: // fall-through + case UILocker::OutputRequired: + { + auto* unlock = new QPushButton(QObject::tr("Unlock")); + + QObject::connect(unlock, &QPushButton::clicked, [&]{ + UILocker::instance().onForceUnlock(); + }); + + ly->addWidget(unlock); + + break; + } + + case UILocker::PreventExit: + { + auto* exit = new QPushButton(QObject::tr("Exit Now")); + QObject::connect(exit, &QPushButton::clicked, [&]{ + UILocker::instance().onForceUnlock(); + }); + + ly->addWidget(exit); + + auto* cancel = new QPushButton(QObject::tr("Cancel")); + QObject::connect(cancel, &QPushButton::clicked, [&]{ + UILocker::instance().onCancel(); + }); + + ly->addWidget(cancel); + + break; + } + } + } +}; + +UILocker::Session::~Session() +{ + unlock(); +} + +void UILocker::Session::unlock() +{ + QMetaObject::invokeMethod(qApp, [this]{ + UILocker::instance().unlock(this); + }); +} + +void UILocker::Session::setInfo(DWORD pid, const QString& name) +{ + { + std::scoped_lock lock(m_mutex); + m_pid = pid; + m_name = name; + } + + QMetaObject::invokeMethod(qApp, [this]{ + UILocker::instance().updateLabel(); + }); +} + +DWORD UILocker::Session::pid() const +{ + std::scoped_lock lock(m_mutex); + return m_pid; +} + +const QString& UILocker::Session::name() const +{ + std::scoped_lock lock(m_mutex); + return m_name; +} + +UILocker::Results UILocker::Session::result() const +{ + return UILocker::instance().result(); +} + + +static UILocker* g_instance = nullptr; + + +UILocker::UILocker() + : m_parent(nullptr), m_result(NoResult) +{ + Q_ASSERT(!g_instance); + g_instance = this; +} + +UILocker::~UILocker() +{ + const auto v = m_sessions; + + for (auto& wp : v) { + if (auto s=wp.lock()) { + unlock(s.get()); + } + } +} + +UILocker& UILocker::instance() +{ + Q_ASSERT(g_instance); + return *g_instance; +} + +void UILocker::setUserInterface(QWidget* parent) +{ + m_parent = parent; +} + +std::shared_ptr UILocker::lock(Reasons reason) +{ + m_result = StillLocked; + createUi(reason); + + auto ls = std::make_shared(); + m_sessions.push_back(ls); + + updateLabel(); + + return ls; +} + +void UILocker::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; + } + + if (m_sessions.empty()) { + m_ui.reset(); + enableAll(); + } else { + updateLabel(); + } +} + +void UILocker::unlockCurrent() +{ + if (m_sessions.empty()) { + return; + } + + auto s = m_sessions.back().lock(); + if (!s) { + m_sessions.pop_back(); + return; + } + + unlock(s.get()); +} + +void UILocker::updateLabel() +{ + QStringList labels; + + for (auto itor=m_sessions.rbegin(); itor!=m_sessions.rend(); ++itor) { + if (auto ss=itor->lock()) { + labels.push_back(QString("%1 (%2)").arg(ss->name()).arg(ss->pid())); + } + } + + m_ui->setInfo(labels); +} + +UILocker::Results UILocker::result() const +{ + return m_result; +} + +void UILocker::createUi(Reasons reason) +{ + if (!m_ui) { + m_ui.reset(new UILockerInterface(m_parent)); + } + + m_ui->update(reason); + + disableAll(); +} + +void UILocker::onForceUnlock() +{ + m_result = ForceUnlocked; + unlockCurrent(); +} + +void UILocker::onCancel() +{ + m_result = Cancelled; + unlockCurrent(); +} + +template +QList findChildrenImmediate(QWidget* parent) +{ + return parent->findChildren(QString(), Qt::FindDirectChildrenOnly); +} + +void UILocker::disableAll() +{ + const auto topLevels = QApplication::topLevelWidgets(); + + for (auto* w : topLevels) { + if (auto* mw=dynamic_cast(w)) { + disable(mw->centralWidget()); + disable(mw->menuBar()); + disable(mw->statusBar()); + + for (auto* tb : findChildrenImmediate(w)) { + disable(tb); + } + + for (auto* d : findChildrenImmediate(w)) { + disable(d); + } + } + + if (auto* d=dynamic_cast(w)) { + // don't disable stuff if this dialog is the overlay, which happens when + // there's no ui + if (d != m_ui->topLevel()) { + // no central widget, just disable the children, except for the overlay + for (auto* child : findChildrenImmediate(d)) { + if (child != m_ui->topLevel()) { + disable(child); + } + } + } + } + } +} + +void UILocker::enableAll() +{ + for (auto w : m_disabled) { + if (w) { + w->setEnabled(true); + } + } + + m_disabled.clear(); +} + +void UILocker::disable(QWidget* w) +{ + if (w->isEnabled()) { + w->setEnabled(false); + m_disabled.push_back(w); + } +} diff --git a/src/uilocker.h b/src/uilocker.h new file mode 100644 index 00000000..d8c22999 --- /dev/null +++ b/src/uilocker.h @@ -0,0 +1,95 @@ +#pragma once + +#include +#include + +class UILockerInterface; + +class UILocker +{ + friend class UILockerInterface; + +public: + // reason to show the widget + // + enum Reasons + { + NoReason = 0, + + // lock the ui + LockUI, + + // because the output is required + OutputRequired, + + // to prevent exiting until all processes are completed + PreventExit + }; + + // returned by result() + // + enum Results + { + NoResult = 0, + + // the widget is still up + StillLocked, + + // force unlock was clicked + ForceUnlocked, + + // cancel was clicked + Cancelled + }; + + + class Session + { + public: + ~Session(); + + void unlock(); + void setInfo(DWORD pid, const QString& name); + Results result() const; + + DWORD pid() const; + const QString& name() const; + + private: + mutable std::mutex m_mutex; + DWORD m_pid; + QString m_name; + }; + + + UILocker(); + ~UILocker(); + + static UILocker& instance(); + + void setUserInterface(QWidget* parent); + + std::shared_ptr lock(Reasons reason); + + Results result() const; + +private: + QWidget* m_parent; + std::unique_ptr m_ui; + std::vector> m_sessions; + std::atomic m_result; + std::vector> m_disabled; + + void createUi(Reasons reason); + + void unlockCurrent(); + void unlock(Session* s); + void updateLabel(); + + void onForceUnlock(); + void onCancel(); + + void disableAll(); + void enableAll(); + void disable(QWidget* w); +}; -- cgit v1.3.1