NOISSUE refactor window management and launch, make MultiMC a single instance application.

This commit is contained in:
Petr Mrázek
2016-10-30 02:37:38 +01:00
parent deabfa78f8
commit 412855ae3d
18 changed files with 1164 additions and 185 deletions

View File

@ -322,7 +322,7 @@ qt5_add_resources(MULTIMC_RESOURCES ${MULTIMC_QRCS})
# Add executable
add_executable(MultiMC MACOSX_BUNDLE WIN32 ${MULTIMC_SOURCES} ${MULTIMC_UI} ${MULTIMC_RESOURCES} ${MULTIMC_RCS})
target_link_libraries(MultiMC MultiMC_gui ${QUAZIP_LIBRARIES} hoedown rainbow)
target_link_libraries(MultiMC MultiMC_gui ${QUAZIP_LIBRARIES} hoedown rainbow LocalPeer)
if(APPLE)
find_library(OSX_CORE_FOUNDATION CoreFoundation)

View File

@ -174,11 +174,6 @@ void InstanceWindow::closeEvent(QCloseEvent *event)
MMC->settings()->set("ConsoleWindowGeometry", saveGeometry().toBase64());
emit isClosing();
event->accept();
if(m_shouldQuit)
{
// this needs to be delayed so we don't do horrible things
QMetaObject::invokeMethod(MMC, "quit", Qt::QueuedConnection);
}
}
bool InstanceWindow::saveAll()
@ -203,11 +198,7 @@ void InstanceWindow::on_btnKillMinecraft_clicked()
// FIXME: duplicate logic between MainWindow and InstanceWindow
else if(saveAll())
{
m_launchController.reset(new LaunchController());
m_launchController->setInstance(m_instance);
m_launchController->setOnline(true);
m_launchController->setParentWidget(this);
m_launchController->start();
MMC->launch(m_instance, true, nullptr);
}
}

View File

@ -36,11 +36,6 @@ public:
QString instanceId();
void setQuitOnClose(bool shouldQuit = false)
{
m_shouldQuit = shouldQuit;
}
// save all settings and changes (prepare for launch)
bool saveAll();
@ -68,9 +63,7 @@ private:
private:
std::shared_ptr<LaunchTask> m_proc;
unique_qobject_ptr<LaunchController> m_launchController;
InstancePtr m_instance;
bool m_shouldQuit = false;
bool m_doNotSave = false;
PageContainer *m_container = nullptr;
QPushButton *m_closeButton = nullptr;

View File

@ -205,21 +205,10 @@ void LaunchController::launchInstance()
return;
}
auto mainWindow = qobject_cast<MainWindow *>(m_parentWidget);
auto instanceWindow = qobject_cast<InstanceWindow *>(m_parentWidget);
if(mainWindow)
auto console = qobject_cast<InstanceWindow *>(m_parentWidget);
if(!console)
{
m_console = mainWindow->showInstanceWindow(m_instance);
}
else if(instanceWindow)
{
// NOOP
}
else
{
// this is used when launching directly from command line
m_console = new InstanceWindow(m_instance);
m_console->setQuitOnClose(true);
MMC->showInstanceWindow(m_instance);
}
connect(m_launcher.get(), &LaunchTask::readyForLaunch, this, &LaunchController::readyForLaunch);

View File

@ -691,7 +691,7 @@ void MainWindow::updateToolsMenu()
QAction *normalLaunch = launchMenu->addAction(tr("Launch"));
connect(normalLaunch, &QAction::triggered, [this]()
{
launch(m_selectedInstance);
MMC->launch(m_selectedInstance);
});
launchMenu->addSeparator()->setText(tr("Profilers"));
for (auto profiler : MMC->profilers().values())
@ -707,7 +707,7 @@ void MainWindow::updateToolsMenu()
{
connect(profilerAction, &QAction::triggered, [this, profiler]()
{
launch(m_selectedInstance, true, profiler.get());
MMC->launch(m_selectedInstance, true, profiler.get());
});
}
}
@ -953,7 +953,7 @@ void MainWindow::downloadUpdates(GoUpdate::Status status)
{
qDebug() << "Downloading updates.";
ProgressDialog updateDlg(this);
status.rootPath = MMC->rootPath;
status.rootPath = MMC->root();
auto dlPath = FS::PathCombine(MMC->root(), "update", "XXXXXX");
if (!FS::ensureFilePathExists(dlPath))
@ -1004,8 +1004,8 @@ void MainWindow::waitForMinecraftVersions()
if (!MMC->minecraftlist()->isLoaded() && m_versionLoadTask && m_versionLoadTask->isRunning())
{
QEventLoop waitLoop;
waitLoop.connect(m_versionLoadTask, SIGNAL(failed(QString)), SLOT(quit()));
waitLoop.connect(m_versionLoadTask, SIGNAL(succeeded()), SLOT(quit()));
waitLoop.connect(m_versionLoadTask, &Task::failed, &waitLoop, &QEventLoop::quit);
waitLoop.connect(m_versionLoadTask, &Task::succeeded, &waitLoop, &QEventLoop::quit);
waitLoop.exec();
}
}
@ -1261,62 +1261,24 @@ 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);
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()
{
showInstanceWindow(m_selectedInstance, "settings");
MMC->showInstanceWindow(m_selectedInstance, "settings");
}
void MainWindow::on_actionEditInstNotes_triggered()
{
showInstanceWindow(m_selectedInstance, "notes");
MMC->showInstanceWindow(m_selectedInstance, "notes");
}
void MainWindow::on_actionEditInstance_triggered()
{
showInstanceWindow(m_selectedInstance);
MMC->showInstanceWindow(m_selectedInstance);
}
void MainWindow::on_actionScreenshots_triggered()
{
showInstanceWindow(m_selectedInstance, "screenshots");
MMC->showInstanceWindow(m_selectedInstance, "screenshots");
}
void MainWindow::on_actionManageAccounts_triggered()
@ -1440,14 +1402,14 @@ void MainWindow::instanceActivated(QModelIndex index)
if (!inst)
return;
launch(inst);
MMC->launch(inst);
}
void MainWindow::on_actionLaunchInstance_triggered()
{
if (m_selectedInstance)
{
launch(m_selectedInstance);
MMC->launch(m_selectedInstance);
}
}
@ -1455,33 +1417,7 @@ void MainWindow::on_actionLaunchInstanceOffline_triggered()
{
if (m_selectedInstance)
{
launch(m_selectedInstance, false);
}
}
void MainWindow::launch(InstancePtr instance, bool online, BaseProfilerFactory *profiler)
{
if(instance->canLaunch())
{
// FIXME: duplicate logic between MainWindow and InstanceWindow
auto window = m_instanceWindows.find(instance->id());
if(window != m_instanceWindows.end())
{
if(!(*window)->saveAll())
{
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");
MMC->launch(m_selectedInstance, false);
}
}

View File

@ -37,7 +37,6 @@ class MinecraftLauncher;
class BaseProfilerFactory;
class GroupView;
class ServerStatus;
class InstanceWindow;
class MainWindow : public QMainWindow
{
@ -55,8 +54,6 @@ public:
void checkSetDefaultJava();
void checkInstancePathForProblems();
InstanceWindow *showInstanceWindow(InstancePtr instance, QString page = QString());
private slots:
void onCatToggled(bool);
@ -162,8 +159,6 @@ private slots:
*/
void downloadUpdates(GoUpdate::Status status);
void on_instanceWindowClose();
private:
void setCatBackground(bool enabled);
void updateInstanceToolIcon(QString new_icon);
@ -174,7 +169,6 @@ private:
void instanceFromVersion(QString instName, QString instGroup, QString instIcon, BaseVersionPtr version);
void instanceFromZipPack(QString instName, QString instGroup, QString instIcon, QUrl url);
void finalizeInstance(InstancePtr inst);
void launch(InstancePtr instance, bool online = true, BaseProfilerFactory *profiler = nullptr);
private:
std::unique_ptr<Ui> ui;
@ -194,14 +188,10 @@ private:
unique_qobject_ptr<NetJob> skin_download_job;
unique_qobject_ptr<NewsChecker> m_newsChecker;
unique_qobject_ptr<NotificationChecker> m_notificationChecker;
unique_qobject_ptr<LaunchController> m_launchController;
InstancePtr m_selectedInstance;
QString m_currentInstIcon;
// managed by the application object
Task *m_versionLoadTask;
// map from instance ID to its window
QMap<QString, InstanceWindow *> m_instanceWindows;
};

View File

@ -1,5 +1,7 @@
#include "MultiMC.h"
#include "BuildConfig.h"
#include "MainWindow.h"
#include "InstanceWindow.h"
#include "pages/BasePageProvider.h"
#include "pages/global/MultiMCPage.h"
#include "pages/global/MinecraftPage.h"
@ -59,6 +61,7 @@
#include <Commandline.h>
#include <FileSystem.h>
#include <DesktopServices.h>
#include <LocalPeer.h>
#if defined Q_OS_WIN32
#ifndef WIN32_LEAN_AND_MEAN
@ -159,6 +162,7 @@ MultiMC::MultiMC(int &argc, char **argv) : QApplication(argc, argv)
return;
}
}
m_instanceIdToLaunch = args["launch"].toString();
QString origcwdPath = QDir::currentPath();
QString binPath = applicationDirPath();
@ -179,21 +183,32 @@ MultiMC::MultiMC(int &argc, char **argv) : QApplication(argc, argv)
adjustedBy += "Fallback to binary path " + dataPath;
}
instanceIdToLaunch = args["launch"].toString();
if (!FS::ensureFolderPathExists(dataPath) || !QDir::setCurrent(dataPath))
{
// BAD STUFF. WHAT DO?
initLogger();
qCritical() << "Failed to set work path. Will exit. NOW.";
m_status = MultiMC::Failed;
return;
}
m_peerInstance = new LocalPeer(this, ApplicationId::fromPathAndVersion(dataPath, BuildConfig.printableVersionString()));
connect(m_peerInstance, &LocalPeer::messageReceived, this, &MultiMC::messageReceived);
if(m_peerInstance->isClient())
{
if(m_instanceIdToLaunch.isEmpty())
{
m_peerInstance->sendMessage("activate", 2000);
}
else
{
m_peerInstance->sendMessage(m_instanceIdToLaunch, 2000);
}
quit();
return;
}
// in test mode, root path is the same as the binary path.
#ifdef Q_OS_LINUX
QDir foo(FS::PathCombine(binPath, ".."));
rootPath = foo.absolutePath();
m_rootPath = foo.absolutePath();
#elif defined(Q_OS_WIN32)
rootPath = binPath;
#elif defined(Q_OS_MAC)
@ -219,10 +234,10 @@ MultiMC::MultiMC(int &argc, char **argv) : QApplication(argc, argv)
qDebug() << "Work dir : " << QDir::currentPath();
}
qDebug() << "Binary path : " << binPath;
qDebug() << "Application root path : " << rootPath;
if(!instanceIdToLaunch.isEmpty())
qDebug() << "Application root path : " << m_rootPath;
if(!m_instanceIdToLaunch.isEmpty())
{
qDebug() << "ID of instance to launch : " << instanceIdToLaunch;
qDebug() << "ID of instance to launch : " << m_instanceIdToLaunch;
}
// load settings
@ -309,7 +324,22 @@ MultiMC::MultiMC(int &argc, char **argv) : QApplication(argc, argv)
}
connect(this, SIGNAL(aboutToQuit()), SLOT(onExit()));
m_status = MultiMC::Initialized;
setIconTheme(settings()->get("IconTheme").toString());
setApplicationTheme(settings()->get("ApplicationTheme").toString());
if(!m_instanceIdToLaunch.isEmpty())
{
auto inst = instances()->getInstanceById(m_instanceIdToLaunch);
if(inst)
{
minecraftlist();
launch(inst, true, nullptr);
return;
}
}
showMainWindow();
}
MultiMC::~MultiMC()
@ -333,6 +363,22 @@ MultiMC::~MultiMC()
#endif
}
void MultiMC::messageReceived(const QString& message)
{
if(message == "activate")
{
showMainWindow();
}
else
{
auto inst = instances()->getInstanceById(message);
if(inst)
{
launch(inst, true, nullptr);
}
}
}
#ifdef Q_OS_MAC
#include "CertWorkaround.h"
#endif
@ -1024,4 +1070,101 @@ bool MultiMC::openJsonEditor(const QString &filename)
}
}
void MultiMC::launch(InstancePtr instance, bool online, BaseProfilerFactory *profiler)
{
if(instance->canLaunch())
{
m_launchController.reset(new LaunchController());
m_launchController->setInstance(instance);
m_launchController->setOnline(online);
m_launchController->setProfiler(profiler);
auto windowIter = m_instanceWindows.find(instance->id());
if(windowIter != m_instanceWindows.end())
{
auto window = *windowIter;
if(!window->saveAll())
{
return;
}
m_launchController->setParentWidget(window);
}
if(m_mainWindow)
{
m_launchController->setParentWidget(m_mainWindow);
}
m_launchController->start();
}
else if (instance->isRunning())
{
showInstanceWindow(instance, "console");
}
}
MainWindow * MultiMC::showMainWindow()
{
if(m_mainWindow)
{
m_mainWindow->setWindowState(m_mainWindow->windowState() & ~Qt::WindowMinimized);
m_mainWindow->raise();
m_mainWindow->activateWindow();
}
else
{
m_mainWindow = new MainWindow();
m_mainWindow->restoreState(QByteArray::fromBase64(MMC->settings()->get("MainWindowState").toByteArray()));
m_mainWindow->restoreGeometry(QByteArray::fromBase64(MMC->settings()->get("MainWindowGeometry").toByteArray()));
m_mainWindow->show();
m_mainWindow->checkSetDefaultJava();
m_mainWindow->checkInstancePathForProblems();
}
return m_mainWindow;
}
InstanceWindow *MultiMC::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);
m_instanceWindows[id] = window;
connect(window, &InstanceWindow::isClosing, this, &MultiMC::on_windowClose);
}
if(!page.isEmpty())
{
window->selectPage(page);
}
return window;
}
void MultiMC::on_windowClose()
{
auto instWindow = qobject_cast<InstanceWindow *>(QObject::sender());
if(instWindow)
{
m_instanceWindows.remove(instWindow->instanceId());
return;
}
auto mainWindow = qobject_cast<MainWindow *>(QObject::sender());
if(mainWindow)
{
m_mainWindow = nullptr;
}
// quit when there are no more windows.
if(m_instanceWindows.isEmpty() && !m_mainWindow)
{
quit();
}
}
#include "MultiMC.moc"

View File

@ -7,8 +7,14 @@
#include <QIcon>
#include <QDateTime>
#include <updater/GoUpdate.h>
class FolderInstanceProvider;
#include <BaseInstance.h>
class LaunchController;
class LocalPeer;
class InstanceWindow;
class MainWindow;
class FolderInstanceProvider;
class GenericPageProvider;
class QFile;
class MinecraftVersionList;
@ -36,8 +42,6 @@ class ITheme;
class MultiMC : public QApplication
{
// friends for the purpose of limiting access to deprecated stuff
friend class MultiMCPage;
friend class MainWindow;
Q_OBJECT
public:
enum Status
@ -51,13 +55,12 @@ public:
MultiMC(int &argc, char **argv);
virtual ~MultiMC();
// InstanceList, IconList, OneSixFTBInstance, LegacyUpdate, LegacyInstance, MCEditTool, JVisualVM, MinecraftInstance, JProfiler, BaseInstance
std::shared_ptr<SettingsObject> settings()
std::shared_ptr<SettingsObject> settings() const
{
return m_settings;
}
std::shared_ptr<GenericPageProvider> globalSettingsPages()
std::shared_ptr<GenericPageProvider> globalSettingsPages() const
{
return m_globalSettingsProvider;
}
@ -72,6 +75,7 @@ public:
void setIconTheme(const QString& name);
std::vector<ITheme *> getValidApplicationThemes();
void setApplicationTheme(const QString& name);
// DownloadUpdateTask
@ -86,7 +90,6 @@ public:
std::shared_ptr<LiteLoaderVersionList> liteloaderlist();
std::shared_ptr<JavaInstallList> javalist();
// APPLICATION ONLY
std::shared_ptr<InstanceList> instances() const
{
return m_instances;
@ -102,46 +105,44 @@ public:
return m_icons;
}
// APPLICATION ONLY
std::shared_ptr<MojangAccountList> accounts() const
{
return m_accounts;
}
// APPLICATION ONLY
Status status() const
{
return m_status;
}
// APPLICATION ONLY
const QMap<QString, std::shared_ptr<BaseProfilerFactory>> &profilers() const
{
return m_profilers;
}
// APPLICATION ONLY
const QMap<QString, std::shared_ptr<BaseDetachedToolFactory>> &tools() const
{
return m_tools;
}
// APPLICATION ONLY
/// this is the root of the 'installation'. Used for automatic updates
const QString &root()
{
return m_rootPath;
}
// install updates now.
void installUpdates(const QString updateFilesDir, GoUpdate::OperationList operations);
/*!
* Opens a json file using either a system default editor, or, if note empty, the editor
* Opens a json file using either a system default editor, or, if not empty, the editor
* specified in the settings
*/
bool openJsonEditor(const QString &filename);
protected: /* to be removed! */
// FIXME: remove. used by MainWindow to create application update tasks
/// this is the root of the 'installation'. Used for automatic updates
const QString &root()
{
return rootPath;
}
InstanceWindow *showInstanceWindow(InstancePtr instance, QString page = QString());
MainWindow *showMainWindow();
void launch(InstancePtr instance, bool online = true, BaseProfilerFactory *profiler = nullptr);
private slots:
/**
@ -149,6 +150,10 @@ private slots:
*/
void onExit();
void on_windowClose();
void messageReceived(const QString & message);
private:
void initLogger();
void initIcons();
@ -160,6 +165,7 @@ private:
private:
QDateTime startTime;
unique_qobject_ptr<LaunchController> m_launchController;
std::shared_ptr<QTranslator> m_qt_translator;
std::shared_ptr<QTranslator> m_mmc_translator;
std::shared_ptr<SettingsObject> m_settings;
@ -180,12 +186,19 @@ private:
QMap<QString, std::shared_ptr<BaseProfilerFactory>> m_profilers;
QMap<QString, std::shared_ptr<BaseDetachedToolFactory>> m_tools;
QString rootPath;
QString m_rootPath;
Status m_status = MultiMC::Failed;
// used on Windows to attach the standard IO streams
bool consoleAttached = false;
// map from instance ID to its window
QMap<QString, InstanceWindow *> m_instanceWindows;
// main window, if any
MainWindow * m_mainWindow = nullptr;
LocalPeer * m_peerInstance = nullptr;
public:
QString instanceIdToLaunch;
QString m_instanceIdToLaunch;
std::unique_ptr<QFile> logFile;
};

View File

@ -4,41 +4,6 @@
#include <InstanceList.h>
#include <QDebug>
int launchMainWindow(MultiMC &app)
{
MainWindow mainWin;
mainWin.restoreState(QByteArray::fromBase64(MMC->settings()->get("MainWindowState").toByteArray()));
mainWin.restoreGeometry(QByteArray::fromBase64(MMC->settings()->get("MainWindowGeometry").toByteArray()));
mainWin.show();
mainWin.checkSetDefaultJava();
mainWin.checkInstancePathForProblems();
return app.exec();
}
int launchInstance(MultiMC &app, InstancePtr inst)
{
app.minecraftlist();
LaunchController launchController;
launchController.setInstance(inst);
launchController.setOnline(true);
QMetaObject::invokeMethod(&launchController, "start", Qt::QueuedConnection);
return app.exec();
}
int main_gui(MultiMC &app)
{
app.setIconTheme(MMC->settings()->get("IconTheme").toString());
app.setApplicationTheme(MMC->settings()->get("ApplicationTheme").toString());
// show main window
auto inst = app.instances()->getInstanceById(app.instanceIdToLaunch);
if(inst)
{
return launchInstance(app, inst);
}
return launchMainWindow(app);
}
int main(int argc, char *argv[])
{
// initialize Qt
@ -59,7 +24,7 @@ int main(int argc, char *argv[])
switch (app.status())
{
case MultiMC::Initialized:
return main_gui(app);
return app.exec();
case MultiMC::Failed:
return 1;
case MultiMC::Succeeded: