From bc6d1b5304f715ad0d8be27efd6630f820572da4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Sat, 6 Aug 2016 15:39:29 +0200 Subject: [PATCH] GH-338, GH-513, GH-700 Unify edit instance with console window * The resulting instance window can be closed at any point. * Main window is kept open and running instances are marked with a badge. * Multiple instances can now run from the same MultiMC - it's even more **multi** now. * MultiMC can be entirely closed, keeping Minecraft(s) running. --- api/logic/BaseInstance.cpp | 16 +- api/logic/BaseInstance.h | 8 + api/logic/minecraft/MinecraftInstance.cpp | 4 +- application/CMakeLists.txt | 5 +- application/ConsoleWindow.cpp | 260 ------------------ application/InstancePageProvider.h | 2 + application/InstanceWindow.cpp | 226 +++++++++++++++ .../{ConsoleWindow.h => InstanceWindow.h} | 32 ++- application/LaunchInteraction.cpp | 33 ++- application/LaunchInteraction.h | 5 +- application/MainWindow.cpp | 66 ++++- application/MainWindow.h | 8 + application/SettingsUI.cpp | 9 - application/SettingsUI.h | 2 - application/groupview/InstanceDelegate.cpp | 4 + application/pages/LogPage.cpp | 75 +++-- application/pages/LogPage.h | 5 +- application/resources/instances/instances.qrc | 1 + .../resources/instances/status-running.png | Bin 0 -> 1059 bytes .../multimc/16x16/status-running.png | Bin 0 -> 675 bytes .../multimc/22x22/status-running.png | Bin 0 -> 957 bytes .../multimc/24x24/status-running.png | Bin 0 -> 1059 bytes .../multimc/32x32/status-running.png | Bin 0 -> 1425 bytes .../multimc/48x48/status-running.png | Bin 0 -> 2288 bytes .../multimc/64x64/status-running.png | Bin 0 -> 3178 bytes application/resources/multimc/multimc.qrc | 9 + .../multimc/scalable/status-running.svg | 187 +++++++++++++ application/widgets/PageContainer.cpp | 22 +- application/widgets/PageContainer.h | 2 + 29 files changed, 630 insertions(+), 351 deletions(-) delete mode 100644 application/ConsoleWindow.cpp create mode 100644 application/InstanceWindow.cpp rename application/{ConsoleWindow.h => InstanceWindow.h} (66%) delete mode 100644 application/SettingsUI.cpp create mode 100644 application/resources/instances/status-running.png create mode 100644 application/resources/multimc/16x16/status-running.png create mode 100644 application/resources/multimc/22x22/status-running.png create mode 100644 application/resources/multimc/24x24/status-running.png create mode 100644 application/resources/multimc/32x32/status-running.png create mode 100644 application/resources/multimc/48x48/status-running.png create mode 100644 application/resources/multimc/64x64/status-running.png create mode 100644 application/resources/multimc/scalable/status-running.svg diff --git a/api/logic/BaseInstance.cpp b/api/logic/BaseInstance.cpp index ce55d5e4..9dee2c38 100644 --- a/api/logic/BaseInstance.cpp +++ b/api/logic/BaseInstance.cpp @@ -92,11 +92,14 @@ bool BaseInstance::isRunning() const void BaseInstance::setRunning(bool running) { - if(running && !m_isRunning) + if(running == m_isRunning) + return; + + if(running) { m_timeStarted = QDateTime::currentDateTime(); } - else if(!running && m_isRunning) + else { qint64 current = settings()->get("totalTimePlayed").toLongLong(); QDateTime timeEnded = QDateTime::currentDateTime(); @@ -104,6 +107,8 @@ void BaseInstance::setRunning(bool running) emit propertiesChanged(this); } m_isRunning = running; + + emit runningStatusChanged(running); } int64_t BaseInstance::totalTimePlayed() const @@ -179,7 +184,7 @@ void BaseInstance::unsetFlag(const BaseInstance::InstanceFlag flag) bool BaseInstance::canLaunch() const { - return !(flags() & VersionBrokenFlag); + return (!(flags() & VersionBrokenFlag)) && (!isRunning()); } bool BaseInstance::reload() @@ -268,3 +273,8 @@ QStringList BaseInstance::extraArguments() const { return Commandline::splitArgs(settings()->get("JvmArgs").toString()); } + +std::shared_ptr BaseInstance::getLaunchTask() +{ + return m_launchProcess; +} diff --git a/api/logic/BaseInstance.h b/api/logic/BaseInstance.h index f0fb6096..9a6976cb 100644 --- a/api/logic/BaseInstance.h +++ b/api/logic/BaseInstance.h @@ -157,6 +157,9 @@ public: /// returns a valid launcher (task container) virtual std::shared_ptr createLaunchTask(AuthSessionPtr account) = 0; + /// returns the current launch task (if any) + std::shared_ptr getLaunchTask(); + /*! * Returns a task that should be done right before launch * This task should do any extra preparations needed @@ -231,6 +234,10 @@ signals: void flagsChanged(); + void launchTaskChanged(std::shared_ptr); + + void runningStatusChanged(bool running); + protected slots: void iconUpdated(QString key); @@ -240,6 +247,7 @@ protected: SettingsObjectPtr m_settings; InstanceFlags m_flags; bool m_isRunning = false; + std::shared_ptr m_launchProcess; QDateTime m_timeStarted; }; diff --git a/api/logic/minecraft/MinecraftInstance.cpp b/api/logic/minecraft/MinecraftInstance.cpp index f706c16d..b64d9bd2 100644 --- a/api/logic/minecraft/MinecraftInstance.cpp +++ b/api/logic/minecraft/MinecraftInstance.cpp @@ -465,7 +465,9 @@ std::shared_ptr MinecraftInstance::createLaunchTask(AuthSessionPtr s { process->setCensorFilter(createCensorFilterFromSession(session)); } - return process; + m_launchProcess = process; + emit launchTaskChanged(m_launchProcess); + return m_launchProcess; } QString MinecraftInstance::launchMethod() diff --git a/application/CMakeLists.txt b/application/CMakeLists.txt index 9d71f977..46c496e2 100644 --- a/application/CMakeLists.txt +++ b/application/CMakeLists.txt @@ -103,12 +103,11 @@ SET(MULTIMC_SOURCES # GUI - windows MainWindow.h MainWindow.cpp - ConsoleWindow.h - ConsoleWindow.cpp + InstanceWindow.h + InstanceWindow.cpp # GUI - settings-specific wrappers for paged dialog SettingsUI.h - SettingsUI.cpp # Processes LaunchInteraction.h diff --git a/application/ConsoleWindow.cpp b/application/ConsoleWindow.cpp deleted file mode 100644 index e620d700..00000000 --- a/application/ConsoleWindow.cpp +++ /dev/null @@ -1,260 +0,0 @@ -/* Copyright 2013-2015 MultiMC Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "ConsoleWindow.h" -#include "MultiMC.h" - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include "widgets/PageContainer.h" -#include "pages/LogPage.h" -#include "InstancePageProvider.h" - -#include "icons/IconList.h" - -class LogPageProvider : public BasePageProvider -{ -public: - LogPageProvider(BasePageProviderPtr parent, BasePage * log_page) - { - m_parent = parent; - m_log_page = log_page; - } - virtual QString dialogTitle() {return "Fake";}; - virtual QList getPages() - { - auto pages = m_parent->getPages(); - pages.prepend(m_log_page); - return pages; - } -private: - BasePageProviderPtr m_parent; - BasePage * m_log_page; -}; - -ConsoleWindow::ConsoleWindow(std::shared_ptr proc, QWidget *parent) - : QMainWindow(parent), m_proc(proc) -{ - setAttribute(Qt::WA_DeleteOnClose); - - auto instance = m_proc->instance(); - auto icon = MMC->icons()->getIcon(instance->iconKey()); - QString windowTitle = tr("Console window for ") + instance->name(); - - // Set window properties - { - setWindowIcon(icon); - setWindowTitle(windowTitle); - } - - // Add page container - { - auto mainLayout = new QVBoxLayout; - auto provider = std::make_shared(m_proc->instance()); - auto baseprovider = std::dynamic_pointer_cast(provider); - auto proxy_provider = std::make_shared(baseprovider, new LogPage(m_proc)); - m_container = new PageContainer(proxy_provider, "console", this); - mainLayout->addWidget(m_container); - mainLayout->setSpacing(0); - mainLayout->setContentsMargins(0,0,0,0); - setLayout(mainLayout); - setCentralWidget(m_container); - } - - // Add custom buttons to the page container layout. - { - auto horizontalLayout = new QHBoxLayout(); - horizontalLayout->setObjectName(QStringLiteral("horizontalLayout")); - horizontalLayout->setContentsMargins(6, -1, 6, -1); - - auto btnHelp = new QPushButton(); - btnHelp->setText(tr("Help")); - horizontalLayout->addWidget(btnHelp); - connect(btnHelp, SIGNAL(clicked(bool)), m_container, SLOT(help())); - - auto spacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); - horizontalLayout->addSpacerItem(spacer); - - m_killButton = new QPushButton(); - m_killButton->setText(tr("Kill Minecraft")); - horizontalLayout->addWidget(m_killButton); - connect(m_killButton, SIGNAL(clicked(bool)), SLOT(on_btnKillMinecraft_clicked())); - - m_closeButton = new QPushButton(); - m_closeButton->setText(tr("Close")); - horizontalLayout->addWidget(m_closeButton); - connect(m_closeButton, SIGNAL(clicked(bool)), SLOT(on_closeButton_clicked())); - - m_container->addButtons(horizontalLayout); - } - - // restore window state - { - auto base64State = MMC->settings()->get("ConsoleWindowState").toByteArray(); - restoreState(QByteArray::fromBase64(base64State)); - auto base64Geometry = MMC->settings()->get("ConsoleWindowGeometry").toByteArray(); - restoreGeometry(QByteArray::fromBase64(base64Geometry)); - } - - // Set up tray icon - { - m_trayIcon = new QSystemTrayIcon(icon, this); - m_trayIcon->setToolTip(windowTitle); - - connect(m_trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), - SLOT(iconActivated(QSystemTrayIcon::ActivationReason))); - m_trayIcon->show(); - } - - // Set up signal connections - connect(m_proc.get(), &LaunchTask::succeeded, this, &ConsoleWindow::onSucceeded); - connect(m_proc.get(), &LaunchTask::failed, this, &ConsoleWindow::onFailed); - connect(m_proc.get(), &LaunchTask::requestProgress, this, &ConsoleWindow::onProgressRequested); - - setMayClose(false); - - if (m_proc->instance()->settings()->get("ShowConsole").toBool()) - { - show(); - } -} - -void ConsoleWindow::iconActivated(QSystemTrayIcon::ActivationReason reason) -{ - switch (reason) - { - case QSystemTrayIcon::Trigger: - { - toggleConsole(); - } - default: - return; - } -} - -void ConsoleWindow::on_closeButton_clicked() -{ - close(); -} - -void ConsoleWindow::setMayClose(bool mayclose) -{ - if(mayclose) - m_closeButton->setText(tr("Close")); - else - m_closeButton->setText(tr("Hide")); - m_mayclose = mayclose; -} - -void ConsoleWindow::toggleConsole() -{ - if (isVisible()) - { - if(!isActiveWindow()) - { - activateWindow(); - return; - } - hide(); - } - else - { - show(); - } -} - -void ConsoleWindow::closeEvent(QCloseEvent *event) -{ - if (!m_mayclose) - { - toggleConsole(); - event->ignore(); - } - else if(m_container->requestClose(event)) - { - MMC->settings()->set("ConsoleWindowState", saveState().toBase64()); - MMC->settings()->set("ConsoleWindowGeometry", saveGeometry().toBase64()); - - emit isClosing(); - m_trayIcon->hide(); - event->accept(); - } -} - -void ConsoleWindow::on_btnKillMinecraft_clicked() -{ - m_killButton->setEnabled(false); - auto response = CustomMessageBox::selectable( - this, tr("Kill Minecraft?"), - tr("This can cause the instance to get corrupted and should only be used if Minecraft " - "is frozen for some reason"), - QMessageBox::Question, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes)->exec(); - if (response == QMessageBox::Yes) - m_proc->abort(); - else - m_killButton->setEnabled(true); -} - -void ConsoleWindow::onSucceeded() -{ - m_killButton->setEnabled(false); - setMayClose(true); - if (m_proc->instance()->settings()->get("AutoCloseConsole").toBool() && m_container->requestClose(nullptr)) - { - this->close(); - return; - } - if (!isVisible()) - { - show(); - } - // Raise Window - if (MMC->settings()->get("RaiseConsole").toBool()) - { - raise(); - activateWindow(); - } -} - -void ConsoleWindow::onFailed(QString reason) -{ - m_killButton->setEnabled(false); - setMayClose(true); - if (!isVisible()) - { - show(); - } -} - -void ConsoleWindow::onProgressRequested(Task* task) -{ - ProgressDialog progDialog(this); - m_proc->proceed(); - progDialog.execWithTask(task); -} - - -ConsoleWindow::~ConsoleWindow() -{ - -} diff --git a/application/InstancePageProvider.h b/application/InstancePageProvider.h index dfc2e4dd..1d6cc5d7 100644 --- a/application/InstancePageProvider.h +++ b/application/InstancePageProvider.h @@ -3,6 +3,7 @@ #include "minecraft/legacy/LegacyInstance.h" #include #include "pages/BasePage.h" +#include "pages/LogPage.h" #include "pages/VersionPage.h" #include "pages/ModFolderPage.h" #include "pages/ResourcePackPage.h" @@ -29,6 +30,7 @@ public: virtual QList getPages() override { QList values; + values.append(new LogPage(inst)); std::shared_ptr onesix = std::dynamic_pointer_cast(inst); if(onesix) { diff --git a/application/InstanceWindow.cpp b/application/InstanceWindow.cpp new file mode 100644 index 00000000..dfc7b815 --- /dev/null +++ b/application/InstanceWindow.cpp @@ -0,0 +1,226 @@ +/* Copyright 2013-2015 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "InstanceWindow.h" +#include "MultiMC.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include "widgets/PageContainer.h" +#include "InstancePageProvider.h" + +#include "icons/IconList.h" + +InstanceWindow::InstanceWindow(InstancePtr instance, QWidget *parent) + : QMainWindow(parent), m_instance(instance) +{ + setAttribute(Qt::WA_DeleteOnClose); + + auto icon = MMC->icons()->getIcon(m_instance->iconKey()); + QString windowTitle = tr("Console window for ") + m_instance->name(); + + // Set window properties + { + setWindowIcon(icon); + setWindowTitle(windowTitle); + } + + // Add page container + { + auto mainLayout = new QVBoxLayout; + auto provider = std::make_shared(m_instance); + m_container = new PageContainer(provider, "console", this); + mainLayout->addWidget(m_container); + mainLayout->setSpacing(0); + mainLayout->setContentsMargins(0,0,0,0); + setLayout(mainLayout); + setCentralWidget(m_container); + } + + // Add custom buttons to the page container layout. + { + auto horizontalLayout = new QHBoxLayout(); + horizontalLayout->setObjectName(QStringLiteral("horizontalLayout")); + horizontalLayout->setContentsMargins(6, -1, 6, -1); + + auto btnHelp = new QPushButton(); + btnHelp->setText(tr("Help")); + horizontalLayout->addWidget(btnHelp); + connect(btnHelp, SIGNAL(clicked(bool)), m_container, SLOT(help())); + + auto spacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); + horizontalLayout->addSpacerItem(spacer); + + m_killButton = new QPushButton(); + horizontalLayout->addWidget(m_killButton); + setKillButton(m_instance->isRunning()); + connect(m_killButton, SIGNAL(clicked(bool)), SLOT(on_btnKillMinecraft_clicked())); + + m_closeButton = new QPushButton(); + m_closeButton->setText(tr("Close")); + horizontalLayout->addWidget(m_closeButton); + connect(m_closeButton, SIGNAL(clicked(bool)), SLOT(on_closeButton_clicked())); + + m_container->addButtons(horizontalLayout); + } + + // restore window state + { + auto base64State = MMC->settings()->get("ConsoleWindowState").toByteArray(); + restoreState(QByteArray::fromBase64(base64State)); + auto base64Geometry = MMC->settings()->get("ConsoleWindowGeometry").toByteArray(); + restoreGeometry(QByteArray::fromBase64(base64Geometry)); + } + + // set up instance and launch process recognition + { + auto launchTask = m_instance->getLaunchTask(); + on_InstanceLaunchTask_changed(launchTask); + connect(m_instance.get(), &BaseInstance::launchTaskChanged, + this, &InstanceWindow::on_InstanceLaunchTask_changed); + connect(m_instance.get(), &BaseInstance::runningStatusChanged, + this, &InstanceWindow::on_RunningState_changed); + } + show(); +} + +void InstanceWindow::setKillButton(bool kill) +{ + if(kill) + { + m_killButton->setText(tr("Kill")); + m_killButton->setToolTip(tr("Kill the running instance")); + } + else + { + m_killButton->setText(tr("Launch")); + m_killButton->setToolTip(tr("Launch the instance")); + } +} + +void InstanceWindow::on_InstanceLaunchTask_changed(std::shared_ptr proc) +{ + if(m_proc) + { + disconnect(m_proc.get(), &LaunchTask::succeeded, this, &InstanceWindow::onSucceeded); + disconnect(m_proc.get(), &LaunchTask::failed, this, &InstanceWindow::onFailed); + disconnect(m_proc.get(), &LaunchTask::requestProgress, this, &InstanceWindow::onProgressRequested); + } + + m_proc = proc; + + if(m_proc) + { + // Set up signal connections + connect(m_proc.get(), &LaunchTask::succeeded, this, &InstanceWindow::onSucceeded); + connect(m_proc.get(), &LaunchTask::failed, this, &InstanceWindow::onFailed); + connect(m_proc.get(), &LaunchTask::requestProgress, this, &InstanceWindow::onProgressRequested); + } +} + +void InstanceWindow::on_RunningState_changed(bool running) +{ + setKillButton(running); + m_container->refresh(); +} + +void InstanceWindow::on_closeButton_clicked() +{ + close(); +} + +void InstanceWindow::closeEvent(QCloseEvent *event) +{ + MMC->settings()->set("ConsoleWindowState", saveState().toBase64()); + MMC->settings()->set("ConsoleWindowGeometry", saveGeometry().toBase64()); + + if(m_container->requestClose(event)) + { + emit isClosing(); + event->accept(); + } +} + +void InstanceWindow::on_btnKillMinecraft_clicked() +{ + if(m_instance->isRunning()) + { + auto response = CustomMessageBox::selectable( + this, tr("Kill Minecraft?"), + tr("This can cause the instance to get corrupted and should only be used if Minecraft " + "is frozen for some reason"), + QMessageBox::Question, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes)->exec(); + if (response == QMessageBox::Yes) + { + m_proc->abort(); + } + } + else + { + m_launchController.reset(new LaunchController()); + m_launchController->setInstance(m_instance); + m_launchController->setOnline(true); + m_launchController->setParentWidget(this); + m_launchController->start(); + } +} + +void InstanceWindow::onSucceeded() +{ + if (m_instance->settings()->get("AutoCloseConsole").toBool() && m_container->requestClose(nullptr)) + { + this->close(); + return; + } + // Raise Window + if (MMC->settings()->get("RaiseConsole").toBool()) + { + show(); + raise(); + activateWindow(); + } +} + +void InstanceWindow::onFailed(QString reason) +{ +} + +void InstanceWindow::onProgressRequested(Task* task) +{ + ProgressDialog progDialog(this); + m_proc->proceed(); + progDialog.execWithTask(task); +} + +QString InstanceWindow::instanceId() +{ + return m_instance->id(); +} + +bool InstanceWindow::selectPage(QString pageId) +{ + return m_container->selectPage(pageId); +} + +InstanceWindow::~InstanceWindow() +{ +} diff --git a/application/ConsoleWindow.h b/application/InstanceWindow.h similarity index 66% rename from application/ConsoleWindow.h rename to application/InstanceWindow.h index ac5a6fd1..7ffc4142 100644 --- a/application/ConsoleWindow.h +++ b/application/InstanceWindow.h @@ -16,25 +16,25 @@ #pragma once #include +#include "LaunchInteraction.h" +#include #include #include "launch/LaunchTask.h" +#include "pages/BasePageContainer.h" class QPushButton; class PageContainer; -class ConsoleWindow : public QMainWindow +class InstanceWindow : public QMainWindow, public BasePageContainer { Q_OBJECT public: - explicit ConsoleWindow(std::shared_ptr proc, QWidget *parent = 0); - virtual ~ConsoleWindow(); + explicit InstanceWindow(InstancePtr proc, QWidget *parent = 0); + virtual ~InstanceWindow(); - /** - * @brief specify if the window is allowed to close - * @param mayclose - * used to keep it alive while MC runs - */ - void setMayClose(bool mayclose); + bool selectPage(QString pageId) override; + + QString instanceId(); signals: void isClosing(); @@ -48,18 +48,20 @@ slots: void onFailed(QString reason); void onProgressRequested(Task *task); - // FIXME: add handlers for the other MinecraftLauncher signals (pre/post launch command - // failures) + void on_InstanceLaunchTask_changed(std::shared_ptr proc); + void on_RunningState_changed(bool running); - void iconActivated(QSystemTrayIcon::ActivationReason); - void toggleConsole(); protected: - void closeEvent(QCloseEvent *); + void closeEvent(QCloseEvent *) override; + +private: + void setKillButton(bool kill); private: std::shared_ptr m_proc; + unique_qobject_ptr m_launchController; + InstancePtr m_instance; bool m_mayclose = true; - QSystemTrayIcon *m_trayIcon = nullptr; PageContainer *m_container = nullptr; QPushButton *m_closeButton = nullptr; QPushButton *m_killButton = nullptr; diff --git a/application/LaunchInteraction.cpp b/application/LaunchInteraction.cpp index 1caa054a..80a3368d 100644 --- a/application/LaunchInteraction.cpp +++ b/application/LaunchInteraction.cpp @@ -1,11 +1,12 @@ #include "LaunchInteraction.h" +#include "MainWindow.h" #include #include "MultiMC.h" #include "dialogs/CustomMessageBox.h" #include "dialogs/AccountSelectDialog.h" #include "dialogs/ProgressDialog.h" #include "dialogs/EditAccountDialog.h" -#include "ConsoleWindow.h" +#include "InstanceWindow.h" #include "BuildConfig.h" #include "JavaCommon.h" #include "SettingsUI.h" @@ -204,13 +205,20 @@ void LaunchController::launchInstance() return; } - if(m_parentWidget) + auto mainWindow = qobject_cast(m_parentWidget); + auto instanceWindow = qobject_cast(m_parentWidget); + if(mainWindow) { - m_parentWidget->hide(); + m_console = mainWindow->showInstanceWindow(m_instance); + } + else if(instanceWindow) + { + // NOOP + } + else + { + m_console = new InstanceWindow(m_instance); } - - m_console = new ConsoleWindow(m_launcher); - connect(m_console, &ConsoleWindow::isClosing, this, &LaunchController::instanceEnded); connect(m_launcher.get(), &LaunchTask::readyForLaunch, this, &LaunchController::readyForLaunch); m_launcher->prependStep(std::make_shared(m_launcher.get(), "MultiMC version: " + BuildConfig.printableVersionString() + "\n\n", MessageLevel::MultiMC)); @@ -222,6 +230,7 @@ void LaunchController::readyForLaunch() if (!m_profiler) { m_launcher->proceed(); + emitSucceeded(); return; } @@ -230,6 +239,7 @@ void LaunchController::readyForLaunch() { m_launcher->abort(); QMessageBox::critical(m_parentWidget, tr("Error"), tr("Couldn't start profiler: %1").arg(error)); + emitFailed("Profiler startup failed"); return; } BaseProfiler *profilerInstance = m_profiler->createProfiler(m_launcher->instance(), this); @@ -246,6 +256,7 @@ void LaunchController::readyForLaunch() msg.setModal(true); msg.exec(); m_launcher->proceed(); + emitSucceeded(); }); connect(profilerInstance, &BaseProfiler::abortLaunch, [this](const QString & message) { @@ -257,15 +268,7 @@ void LaunchController::readyForLaunch() msg.setModal(true); msg.exec(); m_launcher->abort(); + emitFailed("Profiler startup failed"); }); profilerInstance->beginProfiling(m_launcher); } - -void LaunchController::instanceEnded() -{ - if(m_parentWidget) - { - m_parentWidget->show(); - } - emitSucceeded(); -} diff --git a/application/LaunchInteraction.h b/application/LaunchInteraction.h index b0932e9b..55cb1e58 100644 --- a/application/LaunchInteraction.h +++ b/application/LaunchInteraction.h @@ -3,7 +3,7 @@ #include #include -class ConsoleWindow; +class InstanceWindow; class LaunchController: public Task { Q_OBJECT @@ -36,14 +36,13 @@ private: private slots: void readyForLaunch(); - void instanceEnded(); private: BaseProfilerFactory *m_profiler = nullptr; bool m_online = true; InstancePtr m_instance; QWidget * m_parentWidget = nullptr; - ConsoleWindow *m_console = nullptr; + InstanceWindow *m_console = nullptr; AuthSessionPtr m_session; std::shared_ptr m_launcher; }; diff --git a/application/MainWindow.cpp b/application/MainWindow.cpp index 06d165da..88c0fc09 100644 --- a/application/MainWindow.cpp +++ b/application/MainWindow.cpp @@ -67,6 +67,7 @@ #include #include +#include "InstanceWindow.h" #include "InstancePageProvider.h" #include "InstanceProxyModel.h" #include "JavaCommon.h" @@ -1424,24 +1425,62 @@ void MainWindow::on_actionSettings_triggered() update(); } +InstanceWindow *MainWindow::showInstanceWindow(InstancePtr instance, QString page) +{ + if(!instance) + return nullptr; + auto id = instance->id(); + InstanceWindow * window = nullptr; + + auto iter = m_instanceWindows.find(id); + if(iter != m_instanceWindows.end()) + { + window = *iter; + window->raise(); + window->activateWindow(); + } + else + { + window = new InstanceWindow(instance, this); + m_instanceWindows[id] = window; + connect(window, &InstanceWindow::isClosing, this, &MainWindow::on_instanceWindowClose); + } + if(!page.isEmpty()) + { + window->selectPage(page); + } + return window; +} + +void MainWindow::on_instanceWindowClose() +{ + auto senderWindow = qobject_cast(QObject::sender()); + if(!senderWindow) + { + return; + } + m_instanceWindows.remove(senderWindow->instanceId()); +} + + void MainWindow::on_actionInstanceSettings_triggered() { - SettingsUI::ShowInstancePageDialog(m_selectedInstance, this, "settings"); + showInstanceWindow(m_selectedInstance, "settings"); } void MainWindow::on_actionEditInstNotes_triggered() { - SettingsUI::ShowInstancePageDialog(m_selectedInstance, this, "notes"); + showInstanceWindow(m_selectedInstance, "notes"); } void MainWindow::on_actionEditInstance_triggered() { - SettingsUI::ShowInstancePageDialog(m_selectedInstance, this); + showInstanceWindow(m_selectedInstance); } void MainWindow::on_actionScreenshots_triggered() { - SettingsUI::ShowInstancePageDialog(m_selectedInstance, this, "screenshots"); + showInstanceWindow(m_selectedInstance, "screenshots"); } void MainWindow::on_actionManageAccounts_triggered() @@ -1586,16 +1625,19 @@ void MainWindow::on_actionLaunchInstanceOffline_triggered() void MainWindow::launch(InstancePtr instance, bool online, BaseProfilerFactory *profiler) { - if(!instance->canLaunch()) + if(instance->canLaunch()) { - return; + m_launchController.reset(new LaunchController()); + m_launchController->setInstance(instance); + m_launchController->setOnline(online); + m_launchController->setParentWidget(this); + m_launchController->setProfiler(profiler); + m_launchController->start(); + } + else if (instance->isRunning()) + { + showInstanceWindow(instance, "console"); } - m_launchController.reset(new LaunchController()); - m_launchController->setInstance(instance); - m_launchController->setOnline(online); - m_launchController->setParentWidget(this); - m_launchController->setProfiler(profiler); - m_launchController->start(); } void MainWindow::taskEnd() diff --git a/application/MainWindow.h b/application/MainWindow.h index e3fb0467..3f0ec6e6 100644 --- a/application/MainWindow.h +++ b/application/MainWindow.h @@ -37,6 +37,7 @@ class MinecraftLauncher; class BaseProfilerFactory; class GroupView; class ServerStatus; +class InstanceWindow; class MainWindow : public QMainWindow { @@ -54,6 +55,8 @@ public: void checkSetDefaultJava(); void checkInstancePathForProblems(); + InstanceWindow *showInstanceWindow(InstancePtr instance, QString page = QString()); + private slots: void onCatToggled(bool); @@ -159,6 +162,8 @@ private slots: */ void downloadUpdates(GoUpdate::Status status); + void on_instanceWindowClose(); + private: void setCatBackground(bool enabled); void updateInstanceToolIcon(QString new_icon); @@ -195,4 +200,7 @@ private: // managed by the application object Task *m_versionLoadTask; + + // map from instance ID to its window + QMap m_instanceWindows; }; diff --git a/application/SettingsUI.cpp b/application/SettingsUI.cpp deleted file mode 100644 index 2a2010de..00000000 --- a/application/SettingsUI.cpp +++ /dev/null @@ -1,9 +0,0 @@ -#include "SettingsUI.h" -namespace SettingsUI -{ -void ShowInstancePageDialog(InstancePtr instance, QWidget * parent, QString open_page) -{ - auto provider = std::make_shared(instance); - ShowPageDialog(provider, parent, open_page); -} -} diff --git a/application/SettingsUI.h b/application/SettingsUI.h index 1e6dc8d0..5b8badf2 100644 --- a/application/SettingsUI.h +++ b/application/SettingsUI.h @@ -23,6 +23,4 @@ void ShowPageDialog(T raw_provider, QWidget * parent, QString open_page = QStrin dlg.exec(); } } - -void ShowInstancePageDialog(InstancePtr instance, QWidget * parent, QString open_page = QString()); } diff --git a/application/groupview/InstanceDelegate.cpp b/application/groupview/InstanceDelegate.cpp index b9ad353c..359dd3cf 100644 --- a/application/groupview/InstanceDelegate.cpp +++ b/application/groupview/InstanceDelegate.cpp @@ -122,6 +122,10 @@ void drawBadges(QPainter *painter, const QStyleOptionViewItemV4 &option, BaseIns { pixmaps.append("updateavailable"); } + if (instance->isRunning()) + { + pixmaps.append("status-running"); + } // begin easter eggs if (instance->name().contains("btw", Qt::CaseInsensitive) || diff --git a/application/pages/LogPage.cpp b/application/pages/LogPage.cpp index af96148b..de4ed4f3 100644 --- a/application/pages/LogPage.cpp +++ b/application/pages/LogPage.cpp @@ -12,40 +12,57 @@ #include "GuiUtil.h" #include -LogPage::LogPage(std::shared_ptr proc, QWidget *parent) - : QWidget(parent), ui(new Ui::LogPage), m_process(proc) +LogPage::LogPage(InstancePtr instance, QWidget *parent) + : QWidget(parent), ui(new Ui::LogPage), m_instance(instance) { ui->setupUi(this); ui->tabWidget->tabBar()->hide(); - connect(m_process.get(), SIGNAL(log(QString, MessageLevel::Enum)), this, - SLOT(write(QString, MessageLevel::Enum))); // create the format and set its font - defaultFormat = new QTextCharFormat(ui->text->currentCharFormat()); - QString fontFamily = MMC->settings()->get("ConsoleFont").toString(); - bool conversionOk = false; - int fontSize = MMC->settings()->get("ConsoleFontSize").toInt(&conversionOk); - if(!conversionOk) { - fontSize = 11; + defaultFormat = new QTextCharFormat(ui->text->currentCharFormat()); + QString fontFamily = MMC->settings()->get("ConsoleFont").toString(); + bool conversionOk = false; + int fontSize = MMC->settings()->get("ConsoleFontSize").toInt(&conversionOk); + if(!conversionOk) + { + fontSize = 11; + } + defaultFormat->setFont(QFont(fontFamily, fontSize)); } - defaultFormat->setFont(QFont(fontFamily, fontSize)); // ensure we don't eat all the RAM - auto lineSetting = MMC->settings()->getSetting("ConsoleMaxLines"); - int maxLines = lineSetting->get().toInt(&conversionOk); - if(!conversionOk) { - maxLines = lineSetting->defValue().toInt(); - qWarning() << "ConsoleMaxLines has nonsensical value, defaulting to" << maxLines; + auto lineSetting = MMC->settings()->getSetting("ConsoleMaxLines"); + bool conversionOk = false; + int maxLines = lineSetting->get().toInt(&conversionOk); + if(!conversionOk) + { + maxLines = lineSetting->defValue().toInt(); + qWarning() << "ConsoleMaxLines has nonsensical value, defaulting to" << maxLines; + } + ui->text->setMaximumBlockCount(maxLines); + + m_stopOnOverflow = MMC->settings()->get("ConsoleOverflowStop").toBool(); } - ui->text->setMaximumBlockCount(maxLines); - auto origForeground = ui->text->palette().color(ui->text->foregroundRole()); - auto origBackground = ui->text->palette().color(ui->text->backgroundRole()); - m_colors.reset(new LogColorCache(origForeground, origBackground)); + // set up instance and launch process recognition + { + auto launchTask = m_instance->getLaunchTask(); + if(launchTask) + { + on_InstanceLaunchTask_changed(launchTask); + } + connect(m_instance.get(), &BaseInstance::launchTaskChanged, + this, &LogPage::on_InstanceLaunchTask_changed); + } - m_stopOnOverflow = MMC->settings()->get("ConsoleOverflowStop").toBool(); + // set up text colors and adapt them to the current theme foreground and background + { + auto origForeground = ui->text->palette().color(ui->text->foregroundRole()); + auto origBackground = ui->text->palette().color(ui->text->backgroundRole()); + m_colors.reset(new LogColorCache(origForeground, origBackground)); + } auto findShortcut = new QShortcut(QKeySequence(QKeySequence::Find), this); connect(findShortcut, SIGNAL(activated()), SLOT(findActivated())); @@ -62,6 +79,20 @@ LogPage::~LogPage() delete defaultFormat; } +void LogPage::on_InstanceLaunchTask_changed(std::shared_ptr proc) +{ + if(m_process) + { + disconnect(m_process.get(), &LaunchTask::log, this, &LogPage::write); + } + m_process = proc; + if(m_process) + { + ui->text->clear(); + connect(m_process.get(), &LaunchTask::log, this, &LogPage::write); + } +} + bool LogPage::apply() { return true; @@ -69,7 +100,7 @@ bool LogPage::apply() bool LogPage::shouldDisplay() const { - return m_process->instance()->isRunning(); + return m_instance->isRunning() || ui->text->blockCount() > 1; } void LogPage::on_btnPaste_clicked() diff --git a/application/pages/LogPage.h b/application/pages/LogPage.h index 9b694323..e902ad13 100644 --- a/application/pages/LogPage.h +++ b/application/pages/LogPage.h @@ -34,7 +34,7 @@ class LogPage : public QWidget, public BasePage Q_OBJECT public: - explicit LogPage(std::shared_ptr proc, QWidget *parent = 0); + explicit LogPage(InstancePtr instance, QWidget *parent = 0); virtual ~LogPage(); virtual QString displayName() const override { @@ -77,8 +77,11 @@ private slots: void findNextActivated(); void findPreviousActivated(); + void on_InstanceLaunchTask_changed(std::shared_ptr proc); + private: Ui::LogPage *ui; + InstancePtr m_instance; std::shared_ptr m_process; int m_last_scroll_value = 0; bool m_scroll_active = true; diff --git a/application/resources/instances/instances.qrc b/application/resources/instances/instances.qrc index 09ae25d0..b1f1bb4d 100644 --- a/application/resources/instances/instances.qrc +++ b/application/resources/instances/instances.qrc @@ -37,6 +37,7 @@ enderman.png herobrine.png derp.png + status-running.png updateavailable.png diff --git a/application/resources/instances/status-running.png b/application/resources/instances/status-running.png new file mode 100644 index 0000000000000000000000000000000000000000..ecd64451f0f6ee861adf9a3090986da80b3ce991 GIT binary patch literal 1059 zcmV+;1l;?HP)(^b8FWQhbW?9;ba!ELWdL_~cP?peYja~^ zaAhuUa%Y?FJQ@H11E5JnK~zYIm6l&jTtyhge>3;qg@v^(2nfgn4PY8;D?ErzuuIn1 zL=$Y&8huh9OvG4i+9uZ2Ka_1yVl_q)ttf*h1F@ z#BEvN-kljA?h=Zy3q8qXlKY)G=bO1R-;5v|_Fb+8nm{!obPYlkBQ&7m2t5R<2cf!X zuREJbLM-`p?>~)5uN9#qpdi=sn)pi$j~a`L@uLX2gP;u+`QXTG3%Jne??1r3 z2Bs1?3?S z>4}{`4U;1)GZ7L>Dj_EXhGq`*Z%G>%f!6O}X((SkFAh}2``LB1lZu}@{AE~)NpY3m z8}k+zVX^=SR>3WK3;0EU_44U~ojiP32~(%u=E9^#71v}pkrrqT?zK`j|Jfx$ zZ9`~S6UsQIcH;()biBdQEe;LcyJ757c9T^lfB-4BNidrUSm4-M$2g9|)(0QpoA+9I zEpUppKOO>aW@)mW15OZx2up-2CpVBiqZ?3ISja1TpXb|`9^vtUmkIv-d4`Pg!B8ea zmtto)NLjG%j%to{9OA33C4^7!g4obPg5>~|NrVEUZ`xp4_?yw#2tA5+m)RPNasKyV zUVry6$5b8D_jfKPC>JoJHt12@sTCVcOib|MXJ7Kkz!aD6Z3icJX`-71A+Fj;bp|gP z#+wBvC!?_Ed3^Un4{v^PlD-Xl;J&q)vq;T=mjt1EB-)%nD7YTa$Ui&qD^Km-&tujc?m?Mu;uqky^&tHkc(HgB@0YHFJIj|B27fn>4TDi�wzId}V9x`v^@l zc|}lE?H8<@fzdE_5t8G8{+PCgXW+*BVDQRLggM4^9AdxuQ(Y-ivk)7E2&n+or9xY6uAmEhKpLQu-+#Dq1~y zsf9|c7jI%|kA;B6gNSEE(IOTV51wijr3!9y@FreNMWaZ`8Uy`M(+%0#@vzb+ZHn(S z%=^#p{ol+R!SjR{ONCz~vcMu3Y@EuI%P7(;t(;4$Cdx}e6r^yoId2ATWmO4%pEWZa; z9+F6PmiFcv+uhwgJe?ZlPShf~^aQlt<|F{EQGj8IaC-=wJBpa5$%QjRELXil?W9h6s+Gf5RXQ=K0e0$V1?6PZbE6*PZ$jVF^On+a8FWQhbW?9;ba!ELWdL_~cP?peYja~^ zaAhuUa%Y?FJQ@H113F1WK~y-6os~~)R8 z6Bg1KYBZ+N=uV9W7SgnaOiXLAn#jUL6JtctAQIyOB-DioH7<-qk+uAT#BCTnp%m_i|D5i?XoBW)h;*+#Dei5pM!h`2m(YOhS@#zw2YMp@4mbpt3Jgs z%UBYHrPU#@l(dGc6Ofq%>rX-O9NiBdF9z>UZw2>TqHc_29AZw{U79C<7B2n;!8IR( zC+KcDF$eDOnh0b7Hxj35F}Mn>0Vz{$QS||MzeAAA;@Z1OJ#;=y2EjWh6DOGnp#YP^ ztoynh(tj)rc3il14Tuv*OK({5>%(_|I!oPRSYA74p7@iGR{cyt8$S5q0)zLy3bFW& zKX7FfrpLhQwF>l;h=nCJMZS1Vf|s6umcH&6cUl4pA%DUOu^Vn2r5! zK=x9xF}4UGsle3ZR@hoVRaF)3ZLJ)8`xQE`9iaZV&*yKC69s5ch_+Z$F}Olm7cptH zzoybeKA-2v(G%=Hc8Xs%?t=A8=NohZu$~KggxTzZU~4U>PM@LoEqK#+2zBpzCB3rP>hL0;~hO;(BBP7{c1{ fXWH}VXsP($Zbm=4v!Am300000NkvXXu0mjfHDR=A literal 0 HcmV?d00001 diff --git a/application/resources/multimc/24x24/status-running.png b/application/resources/multimc/24x24/status-running.png new file mode 100644 index 0000000000000000000000000000000000000000..ecd64451f0f6ee861adf9a3090986da80b3ce991 GIT binary patch literal 1059 zcmV+;1l;?HP)(^b8FWQhbW?9;ba!ELWdL_~cP?peYja~^ zaAhuUa%Y?FJQ@H11E5JnK~zYIm6l&jTtyhge>3;qg@v^(2nfgn4PY8;D?ErzuuIn1 zL=$Y&8huh9OvG4i+9uZ2Ka_1yVl_q)ttf*h1F@ z#BEvN-kljA?h=Zy3q8qXlKY)G=bO1R-;5v|_Fb+8nm{!obPYlkBQ&7m2t5R<2cf!X zuREJbLM-`p?>~)5uN9#qpdi=sn)pi$j~a`L@uLX2gP;u+`QXTG3%Jne??1r3 z2Bs1?3?S z>4}{`4U;1)GZ7L>Dj_EXhGq`*Z%G>%f!6O}X((SkFAh}2``LB1lZu}@{AE~)NpY3m z8}k+zVX^=SR>3WK3;0EU_44U~ojiP32~(%u=E9^#71v}pkrrqT?zK`j|Jfx$ zZ9`~S6UsQIcH;()biBdQEe;LcyJ757c9T^lfB-4BNidrUSm4-M$2g9|)(0QpoA+9I zEpUppKOO>aW@)mW15OZx2up-2CpVBiqZ?3ISja1TpXb|`9^vtUmkIv-d4`Pg!B8ea zmtto)NLjG%j%to{9OA33C4^7!g4obPg5>~|NrVEUZ`xp4_?yw#2tA5+m)RPNasKyV zUVry6$5b8D_jfKPC>JoJHt12@sTCVcOib|MXJ7Kkz!aD6Z3icJX`-71A+Fj;bp|gP z#+wBvC!?_Ed3^Un4{v^PlD-Xl;J&q)vq;T=mjt1EB-)%nD7YTa$Ui&qD^Km-&tujc?m?Mu;uqky^&tHkc(HgB@0YHFJIj|B27fn>4TDi�wzId}V9x`v^@l zc|}lE?H8<@fzdE_5t8G8{+PCgXW+*BVDQRLggM4^9AdxuQ(Y-ivk)7EK~z|Ut(SjHl~owWKj*#g{lSl`fTBdoR?b?eWhpZdX=Bw~ z{y{5rHM3Rr%jQy&qPb}(XkiwimOsEobEYNLT&-ej)DXce`>AOewIMno1oZ0VUheyz z(;x4>hzQ;v_5J5P=iT!>-|so+d7tOJf=KaFTRKwYgPD(}rhrOA$gv0s$S#CD3+6Ed z4H$t2Hr#P0lCUDI#fr{ckR=E?4cSP|9PxqF^ee6dFyHc#qHT(YeZ=}2@aT;6iJFk z2Bo0mB>2vQkOr7}l+A7du$EV_>dLk89#IHSBueGYONJT3!4t9Hv|_ZGLf!@mjz%hLr6$; zfy*(h|LEi~?5(Y#GAqE8-39R1q3A_86Cpmu8cVg@5>V1Q4b*K238|y5>7SgO%+hC` zVt-8~OFVlSzwc@AbVes4X#yw#LQH2>^E3@%+>4OlvV+|oQH)8GCb8+wjeM|j5e3c5 zaDKB1q#2n2y9*rg!HK#@1CQ7L5Mkjco&op00s(BNW(UEVqN8X&SS#X0q$uYPLNvnXJ0`aH8RA zLwz9E+`82|0mc7U`NCmP8PP#`K&D^@Ky&`5xKc{KJMsgsZrDh@%*0o?5A-LFH+#F357en5fZv=00Zaw`6d5SJJ=vB%@# zlh3|jbL}3!zkLNP$PMr0^H@(IPU;A=1+)k8_Ciux#KeYSaIm?B$}R8kr866UVI61@ zeo1}Z!GUbnk)j?=ZX|FK0zRu!g+Xg;D{oY7=HoxSbj+;+_qd3K6X*+G*%YAaHP&Yx z1^E+7LQnfOeS3O(*s{Hvf+vgE>VAlh`8%Q`2k1Q&BkuNiDI{2s4vS`6xKz=;qLHx`mIK##Cx2l}jR_nr&BP(FlO%G%tW zLrq6MQ`DNW%UL!lUHBef8P%00000NkvXXu0mjfkxh}b literal 0 HcmV?d00001 diff --git a/application/resources/multimc/48x48/status-running.png b/application/resources/multimc/48x48/status-running.png new file mode 100644 index 0000000000000000000000000000000000000000..b8c0bf7cdb220b37b35222d7c1adceed13ad507d GIT binary patch literal 2288 zcmV8fA`Is#p_@LHZ~4R+=R$jtX9D?G!m7P z3W7wVLJ)<7q6*TcjoJVSG^Vja$|8{pNC=C{mfAExVo?et5EW7acCm;;(y(MfARxvz z!N&V|mUsHcn;Fk|V{FgxNh8gD@80vyckempoqO(6^c$jh<@+&+kq<^5BH1Vzfij#R z$tVG60AD>yu7g}fjPr;pXZ`3e@ptGeRG&w_R5cmRSO_u`C7EFOb@Bg{(anDWjKKd& z;9n@u9{w`%zYlf2`SQ^%$883op zVW#4+K#BvB<3Q`>Vj4bZYJkRCXltg!iWn%_gdV?wqS5ZY6L|SfCLZhiKz^7R#91IM z4iap=Q@T5}G(v3^c>F|yE5c{!>S>DdZuFkO(z@Y@oCNi7@R|msIm}t_yZd(of%+ey z?ly>*kQKQB-f6s1aJ8#ZU8}LQayVi=4QiO#MSZ^oh7J_R)q6o>+c6^12&9`$MO8io(5z5{m?AyMiJTH^}XFt$0gtJZ0- z#kvC}6N9!GZ-EgHP8DPv0+Mi8>hmyOFFk(P+ zZHyQIN`oOk0;?l%0Z{WPESnYDt(`#8H3y1q?NI>8a)PDvA_3ra!>f+WX<&;d z=ntqBEIw~-Coo{(43LcAi)xTF;y9m~naOW|^E_MLew$?lX^ht zZ^6Lf{U^~_19z_lQkoVl&J>L{eG(W6Am(}`-QV`_hxGJx7X4}=+qP|E#Z%+S-ZdQ# z7lF5}e}WTI&G2d>9#7qmH4F;cvr9W@-Gt3vLMcT~P7Z~CTFH)g-eO+Tccg5X1phnM zccGdN_C$gXs%cYnl<^oS(^2AkXZw-B;czf*+B9-=b9wUkaY~9eaq9GTJkM-|K_mMh zFd5n!Fi9Arbd)?wVEWd;%&j-G8vf8rn+KQIZ%mp(r_Uq)1IoW$xT(7&mS#rH2mj zkA3s_D(h!Go0STif`|<$OEkAtwtS9nlMNOz~>E5U1Nl{wl*p%DtPzr#T-6aM$O~PVa~IW4K_TXttK7h zdlbVEz|#g+dkj==$E8b`%rJW312c@iRNlKV>T!j(Zs`c$faIe9cMHTPM_FYPH8nMS zbo@9a#l@Vu zplzJdu_T_u+uV#I0SsuagQP+IQod6VQ}HX1@<2}z`;XDxR$#BmhA>Tin*2R9(Y>X+e+;PFbULHj*{-a4O#{Q)ZK)* zWFpxZS5{VX`qML%loV58c?@I8zacq2dWr)eaPKAra~^I}Wk1$VU`L*Z!m{-!*@(id z64zIQv*(E9^ZD4ibt@;oxkJ;>--PV^C@bzqeU%yF!B0Zgu_Mm|p>-j>Z95pZg0;x{ zoBo*FJ+A-I$YJMnnwFl4o$~G$Xt+g4?7xY{y(4ft+(!OA_glLE!PQA+`i?4hKg{|9J@DsxuW2i+TczGQ7TDu;wSX zyBi+CG3zlqsfp_l^&(+D<0q1s0r3O7j#NFt@Iv!lGd=KhP#nx-RxPEU$E=}Pbv7R* zTR_HlmZE_z2^<5!nqZ!)XtwsO*6@MhHa8>g7I4*ryRq}Zhysw$5%mJA3!-$;9R^XX zIy)OBD}iy5i|p<(VP6znKv>J_f*5^j&=EyvCS%A#U?z~+x5xQE_R!QGil#h>k3G7A zC{~>r11cXSc?j7kH3Gy51oo#5pz0C1j^Zk!&V!V*dP=`PxA{N4KT=v{B(7-y0000< KMNUMnLSTYDKsGP{ literal 0 HcmV?d00001 diff --git a/application/resources/multimc/64x64/status-running.png b/application/resources/multimc/64x64/status-running.png new file mode 100644 index 0000000000000000000000000000000000000000..38afda0f9f82e6c704f2b914a2b3c568a924c0d4 GIT binary patch literal 3178 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 Qj{pDw07*qoM6N<$f?-PWQUCw| literal 0 HcmV?d00001 diff --git a/application/resources/multimc/multimc.qrc b/application/resources/multimc/multimc.qrc index 3f7c5e8f..86b472d8 100644 --- a/application/resources/multimc/multimc.qrc +++ b/application/resources/multimc/multimc.qrc @@ -150,6 +150,15 @@ 48x48/status-yellow.png 64x64/status-yellow.png + + 16x16/status-running.png + 24x24/status-running.png + 22x22/status-running.png + 32x32/status-running.png + 48x48/status-running.png + 64x64/status-running.png + scalable/status-running.svg + 16x16/loadermods.png 24x24/loadermods.png diff --git a/application/resources/multimc/scalable/status-running.svg b/application/resources/multimc/scalable/status-running.svg new file mode 100644 index 00000000..18209940 --- /dev/null +++ b/application/resources/multimc/scalable/status-running.svg @@ -0,0 +1,187 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + diff --git a/application/widgets/PageContainer.cpp b/application/widgets/PageContainer.cpp index 90aad26e..04da5c8b 100644 --- a/application/widgets/PageContainer.cpp +++ b/application/widgets/PageContainer.cpp @@ -60,7 +60,6 @@ PageContainer::PageContainer(BasePageProviderPtr pageProvider, QString defaultId createUI(); m_model = new PageModel(this); m_proxyModel = new PageEntryFilterModel(this); - int firstIndex = -1; int counter = 0; auto pages = pageProvider->getPages(); for (auto page : pages) @@ -69,10 +68,6 @@ PageContainer::PageContainer(BasePageProviderPtr pageProvider, QString defaultId page->listIndex = counter; page->setParentContainer(this); counter++; - if (firstIndex == -1) - { - firstIndex = page->stackIndex; - } } m_model->setPages(pages); @@ -111,6 +106,23 @@ bool PageContainer::selectPage(QString pageId) return false; } +void PageContainer::refresh() +{ + m_proxyModel->invalidate(); + if(!m_currentPage->shouldDisplay()) + { + auto index = m_proxyModel->index(0, 0); + if(index.isValid()) + { + m_pageList->setCurrentIndex(index); + } + else + { + // FIXME: unhandled corner case: what to do when there's no page to select? + } + } +} + void PageContainer::createUI() { m_pageStack = new QStackedLayout; diff --git a/application/widgets/PageContainer.h b/application/widgets/PageContainer.h index 381e84e5..84504fb6 100644 --- a/application/widgets/PageContainer.h +++ b/application/widgets/PageContainer.h @@ -45,6 +45,8 @@ public: virtual bool selectPage(QString pageId) override; + void refresh(); + private: void createUI(); private