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.
This commit is contained in:
Petr Mrázek 2016-08-06 15:39:29 +02:00
parent c44d41ee9b
commit bc6d1b5304
29 changed files with 630 additions and 351 deletions

View File

@ -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<LaunchTask> BaseInstance::getLaunchTask()
{
return m_launchProcess;
}

View File

@ -157,6 +157,9 @@ public:
/// returns a valid launcher (task container)
virtual std::shared_ptr<LaunchTask> createLaunchTask(AuthSessionPtr account) = 0;
/// returns the current launch task (if any)
std::shared_ptr<LaunchTask> 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<LaunchTask>);
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<LaunchTask> m_launchProcess;
QDateTime m_timeStarted;
};

View File

@ -465,7 +465,9 @@ std::shared_ptr<LaunchTask> MinecraftInstance::createLaunchTask(AuthSessionPtr s
{
process->setCensorFilter(createCensorFilterFromSession(session));
}
return process;
m_launchProcess = process;
emit launchTaskChanged(m_launchProcess);
return m_launchProcess;
}
QString MinecraftInstance::launchMethod()

View File

@ -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

View File

@ -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 <QScrollBar>
#include <QMessageBox>
#include <QSystemTrayIcon>
#include <QHBoxLayout>
#include <QPushButton>
#include <qlayoutitem.h>
#include <QCloseEvent>
#include <dialogs/CustomMessageBox.h>
#include <dialogs/ProgressDialog.h>
#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<BasePage *> 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<LaunchTask> 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<InstancePageProvider>(m_proc->instance());
auto baseprovider = std::dynamic_pointer_cast<BasePageProvider>(provider);
auto proxy_provider = std::make_shared<LogPageProvider>(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()
{
}

View File

@ -3,6 +3,7 @@
#include "minecraft/legacy/LegacyInstance.h"
#include <FileSystem.h>
#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<BasePage *> getPages() override
{
QList<BasePage *> values;
values.append(new LogPage(inst));
std::shared_ptr<OneSixInstance> onesix = std::dynamic_pointer_cast<OneSixInstance>(inst);
if(onesix)
{

View File

@ -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 <QScrollBar>
#include <QMessageBox>
#include <QHBoxLayout>
#include <QPushButton>
#include <qlayoutitem.h>
#include <QCloseEvent>
#include <dialogs/CustomMessageBox.h>
#include <dialogs/ProgressDialog.h>
#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<InstancePageProvider>(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<LaunchTask> 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()
{
}

View File

@ -16,25 +16,25 @@
#pragma once
#include <QMainWindow>
#include "LaunchInteraction.h"
#include <QObjectPtr.h>
#include <QSystemTrayIcon>
#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<LaunchTask> 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<LaunchTask> 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<LaunchTask> m_proc;
unique_qobject_ptr<LaunchController> 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;

View File

@ -1,11 +1,12 @@
#include "LaunchInteraction.h"
#include "MainWindow.h"
#include <minecraft/auth/MojangAccountList.h>
#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<MainWindow *>(m_parentWidget);
auto instanceWindow = qobject_cast<InstanceWindow *>(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<TextPrint>(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();
}

View File

@ -3,7 +3,7 @@
#include <BaseInstance.h>
#include <tools/BaseProfiler.h>
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 <LaunchTask> m_launcher;
};

View File

@ -67,6 +67,7 @@
#include <updater/UpdateChecker.h>
#include <DesktopServices.h>
#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<InstanceWindow *>(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");
}
}
void MainWindow::taskEnd()

View File

@ -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<QString, InstanceWindow *> m_instanceWindows;
};

View File

@ -1,9 +0,0 @@
#include "SettingsUI.h"
namespace SettingsUI
{
void ShowInstancePageDialog(InstancePtr instance, QWidget * parent, QString open_page)
{
auto provider = std::make_shared<InstancePageProvider>(instance);
ShowPageDialog(provider, parent, open_page);
}
}

View File

@ -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());
}

View File

@ -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) ||

View File

@ -12,15 +12,14 @@
#include "GuiUtil.h"
#include <ColorCache.h>
LogPage::LogPage(std::shared_ptr<LaunchTask> 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;
@ -30,9 +29,12 @@ LogPage::LogPage(std::shared_ptr<LaunchTask> proc, QWidget *parent)
fontSize = 11;
}
defaultFormat->setFont(QFont(fontFamily, fontSize));
}
// ensure we don't eat all the RAM
{
auto lineSetting = MMC->settings()->getSetting("ConsoleMaxLines");
bool conversionOk = false;
int maxLines = lineSetting->get().toInt(&conversionOk);
if(!conversionOk)
{
@ -41,11 +43,26 @@ LogPage::LogPage(std::shared_ptr<LaunchTask> proc, QWidget *parent)
}
ui->text->setMaximumBlockCount(maxLines);
m_stopOnOverflow = MMC->settings()->get("ConsoleOverflowStop").toBool();
}
// 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);
}
// 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));
m_stopOnOverflow = MMC->settings()->get("ConsoleOverflowStop").toBool();
}
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<LaunchTask> 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()

View File

@ -34,7 +34,7 @@ class LogPage : public QWidget, public BasePage
Q_OBJECT
public:
explicit LogPage(std::shared_ptr<LaunchTask> 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<LaunchTask> proc);
private:
Ui::LogPage *ui;
InstancePtr m_instance;
std::shared_ptr<LaunchTask> m_process;
int m_last_scroll_value = 0;
bool m_scroll_active = true;

View File

@ -37,6 +37,7 @@
<file>enderman.png</file>
<file>herobrine.png</file>
<file>derp.png</file>
<file>status-running.png</file>
<!-- Update. GPLv2, https://code.google.com/p/gnome-colors/ -->
<file>updateavailable.png</file>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 675 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 957 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@ -150,6 +150,15 @@
<file>48x48/status-yellow.png</file>
<file>64x64/status-yellow.png</file>
<!-- A status icon for things that are in progress/running... Our own. -->
<file>16x16/status-running.png</file>
<file>24x24/status-running.png</file>
<file>22x22/status-running.png</file>
<file>32x32/status-running.png</file>
<file>48x48/status-running.png</file>
<file>64x64/status-running.png</file>
<file>scalable/status-running.svg</file>
<!-- Plugin (blue recolor), CC-BY-SA 3.0, Oxygen icons. -->
<file>16x16/loadermods.png</file>
<file>24x24/loadermods.png</file>

View File

@ -0,0 +1,187 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
width="32"
height="32"
id="svg2"
inkscape:version="0.91 r13725"
sodipodi:docname="status-running.svg"
inkscape:export-filename="/home/peterix/minecraft/src/MultiMC5/application/resources/multimc/64x64/status-running.png"
inkscape:export-xdpi="180"
inkscape:export-ydpi="180">
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="3840"
inkscape:window-height="2125"
id="namedview32"
showgrid="true"
inkscape:snap-bbox="true"
inkscape:bbox-nodes="false"
inkscape:bbox-paths="false"
inkscape:snap-bbox-midpoints="false"
inkscape:snap-bbox-edge-midpoints="false"
inkscape:object-paths="true"
inkscape:snap-intersection-paths="true"
inkscape:object-nodes="true"
inkscape:snap-smooth-nodes="true"
inkscape:snap-midpoints="false"
inkscape:zoom="10.429825"
inkscape:cx="2.5496161"
inkscape:cy="28.936353"
inkscape:window-x="1200"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer1">
<inkscape:grid
type="xygrid"
id="grid4160"
spacingx="0.5"
spacingy="0.5"
empspacing="8" />
</sodipodi:namedview>
<defs
id="defs4">
<linearGradient
id="linearGradient4162">
<stop
offset="0"
style="stop-color:#0071f1;stop-opacity:1"
id="stop4164" />
<stop
offset="1"
style="stop-color:#007ec3;stop-opacity:1"
id="stop4166" />
</linearGradient>
<linearGradient
id="linearGradient3827">
<stop
id="stop3829"
style="stop-color:#b80000;stop-opacity:1"
offset="0" />
<stop
id="stop3831"
style="stop-color:#600000;stop-opacity:1"
offset="1" />
</linearGradient>
<linearGradient
id="linearGradient3801">
<stop
id="stop3803"
style="stop-color:#f1ab00;stop-opacity:1"
offset="0" />
<stop
id="stop3805"
style="stop-color:#c39a00;stop-opacity:1"
offset="1" />
</linearGradient>
<linearGradient
id="linearGradient3005">
<stop
id="stop3007"
style="stop-color:#ffffff;stop-opacity:1"
offset="0" />
<stop
id="stop3781"
style="stop-color:#ffffff;stop-opacity:0.49803922"
offset="0.8142857" />
<stop
id="stop3009"
style="stop-color:#ffffff;stop-opacity:0"
offset="1" />
</linearGradient>
<filter
color-interpolation-filters="sRGB"
id="filter3797">
<feGaussianBlur
id="feGaussianBlur3799"
stdDeviation="0.52592593" />
</filter>
<radialGradient
cx="3.9371533"
cy="7.5016646"
r="2.5"
fx="3.9371533"
fy="7.5016646"
id="radialGradient3807"
xlink:href="#linearGradient4162"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.4496779,1.5407764,-0.90127514,0.84794135,4.9906134,-4.9255796)" />
<radialGradient
cx="3.9371533"
cy="7.5016646"
r="2.5"
fx="3.9371533"
fy="7.5016646"
id="radialGradient3823"
xlink:href="#linearGradient3827"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.4496779,1.5407764,-0.90127514,0.84794135,4.9906134,-4.9255796)" />
<radialGradient
cx="3.9371533"
cy="7.5016646"
r="2.5"
fx="3.9371533"
fy="7.5016646"
id="radialGradient3786"
xlink:href="#linearGradient3801"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.4496779,1.5407764,-0.90127514,0.84794135,4.9906134,-4.9255796)" />
</defs>
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
transform="translate(0,-1020.3622)"
id="layer1">
<path
d="m 8,9.5 a 2.5,2.5 0 1 1 -5,0 2.5,2.5 0 1 1 5,0 z"
transform="matrix(6.4,0,0,6.4,-19.2,975.5622)"
id="path2997"
style="color:#000000;fill:url(#radialGradient3807);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.5;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
<path
d="M 31,16 A 15,15 0 1 1 1,16 15,15 0 1 1 31,16 z"
transform="matrix(0.93333333,0,0,0.93333444,1.0666666,1021.4288)"
id="path2999"
style="color:#000000;fill:#009cff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.5;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
<g
id="g4187">
<path
inkscape:connector-curvature="0"
id="path4168"
d="m 10.455414,1028.3241 0,16.0761 L 24,1036.3622 Z"
style="fill:#ffffff;fill-rule:evenodd;stroke:#000000;stroke-width:0.95251006px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
sodipodi:nodetypes="cccc"
inkscape:connector-curvature="0"
style="opacity:0.54887217;fill:#ffffff;stroke:none"
id="path3809-3"
d="m 10.5,1044.3622 0,-16 13.5,8 z" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.0 KiB

View File

@ -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;

View File

@ -45,6 +45,8 @@ public:
virtual bool selectPage(QString pageId) override;
void refresh();
private:
void createUI();
private