From fb52ca651867f79832ef5a56fae4f2d19f185a71 Mon Sep 17 00:00:00 2001 From: Gary Talent Date: Wed, 17 May 2017 00:10:16 -0500 Subject: [PATCH] Add plugin system --- src/nostalgia/core/CMakeLists.txt | 4 + src/nostalgia/core/studio/CMakeLists.txt | 22 +++++ .../core/studio/import_tilesheet_wizard.cpp | 48 +++++++++++ .../core/studio/import_tilesheet_wizard.hpp | 29 +++++++ src/nostalgia/core/studio/plugin.cpp | 32 +++++++ src/nostalgia/core/studio/plugin.hpp | 28 ++++++ src/nostalgia/studio/lib/plugin.cpp | 31 +++++++ src/nostalgia/studio/lib/plugin.hpp | 45 ++++++++++ src/nostalgia/studio/lib/wizard.cpp | 26 ++++-- src/nostalgia/studio/lib/wizard.hpp | 13 +-- src/nostalgia/studio/main.cpp | 12 +-- src/nostalgia/studio/mainwindow.cpp | 86 +++++++++++-------- src/nostalgia/studio/mainwindow.hpp | 34 ++++++-- src/nostalgia/studio/nostalgia-studio.json | 8 +- src/nostalgia/studio/studio.hpp | 15 ++++ 15 files changed, 368 insertions(+), 65 deletions(-) create mode 100644 src/nostalgia/core/studio/CMakeLists.txt create mode 100644 src/nostalgia/core/studio/import_tilesheet_wizard.cpp create mode 100644 src/nostalgia/core/studio/import_tilesheet_wizard.hpp create mode 100644 src/nostalgia/core/studio/plugin.cpp create mode 100644 src/nostalgia/core/studio/plugin.hpp create mode 100644 src/nostalgia/studio/lib/plugin.cpp create mode 100644 src/nostalgia/studio/lib/plugin.hpp create mode 100644 src/nostalgia/studio/studio.hpp diff --git a/src/nostalgia/core/CMakeLists.txt b/src/nostalgia/core/CMakeLists.txt index 20e2755e..c1fc78e7 100644 --- a/src/nostalgia/core/CMakeLists.txt +++ b/src/nostalgia/core/CMakeLists.txt @@ -23,6 +23,10 @@ add_library( core.cpp ) +if(WOMBAT_BUILD_TYPE STREQUAL "Native") + add_subdirectory(studio) +endif() + install( FILES core.hpp diff --git a/src/nostalgia/core/studio/CMakeLists.txt b/src/nostalgia/core/studio/CMakeLists.txt new file mode 100644 index 00000000..63e3a925 --- /dev/null +++ b/src/nostalgia/core/studio/CMakeLists.txt @@ -0,0 +1,22 @@ +cmake_minimum_required(VERSION 2.8.11) + +if(WOMBAT_BUILD_TYPE STREQUAL "Native") + add_library( + NostalgiaCore-Studio + SHARED + import_tilesheet_wizard.cpp + plugin.cpp + ) +endif() + +target_link_libraries( + NostalgiaCore-Studio + Qt5::Core + Qt5::Widgets + NostalgiaStudio + OxFS + OxStd +) + +install(TARGETS NostalgiaCore-Studio + LIBRARY DESTINATION lib/nostalgia) diff --git a/src/nostalgia/core/studio/import_tilesheet_wizard.cpp b/src/nostalgia/core/studio/import_tilesheet_wizard.cpp new file mode 100644 index 00000000..9b1aae06 --- /dev/null +++ b/src/nostalgia/core/studio/import_tilesheet_wizard.cpp @@ -0,0 +1,48 @@ +/* + * Copyright 2016-2017 gtalent2@gmail.com + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include + +#include "import_tilesheet_wizard.hpp" + +namespace nostalgia { +namespace core { + +const QString ImportTilesheetWizardPage::TILESHEET_NAME = "projectName"; +const QString ImportTilesheetWizardPage::IMPORT_PATH = "projectPath"; +const QString ImportTilesheetWizardPage::BPP = "bpp"; + +ImportTilesheetWizardPage::ImportTilesheetWizardPage() { + addLineEdit(tr("&Tile Sheet Name:"), TILESHEET_NAME + "*", "", [this](QString) { + auto importPath = field(IMPORT_PATH).toString(); + if (QFile(importPath).exists()) { + return 0; + } else { + this->showValidationError(tr("Invalid image file: %1").arg(importPath)); + return 1; + } + } + ); + auto fileTypes = "(*.png);;(*.bmp);;(*.jpg);;(*.jpeg)"; + addPathBrowse(tr("Tile Sheet &Path:"), IMPORT_PATH + "*", "", + QFileDialog::ExistingFile, fileTypes); + addComboBox(tr("Bits Per Pixe&l:"), BPP, {"4", "8"}); +} + +int ImportTilesheetWizardPage::accept() { + auto tilesheetName = field(TILESHEET_NAME).toString(); + auto importPath = field(IMPORT_PATH).toString(); + if (QFile(importPath).exists()) { + return 0; + } else { + return 1; + } +} + +} +} diff --git a/src/nostalgia/core/studio/import_tilesheet_wizard.hpp b/src/nostalgia/core/studio/import_tilesheet_wizard.hpp new file mode 100644 index 00000000..6f4de009 --- /dev/null +++ b/src/nostalgia/core/studio/import_tilesheet_wizard.hpp @@ -0,0 +1,29 @@ +/* + * Copyright 2016-2017 gtalent2@gmail.com + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#include + +namespace nostalgia { +namespace core { + +class ImportTilesheetWizardPage: public studio::WizardFormPage { + private: + static const QString TILESHEET_NAME; + static const QString IMPORT_PATH; + static const QString BPP; + + public: + ImportTilesheetWizardPage(); + + int accept(); +}; + +} +} diff --git a/src/nostalgia/core/studio/plugin.cpp b/src/nostalgia/core/studio/plugin.cpp new file mode 100644 index 00000000..04716c4d --- /dev/null +++ b/src/nostalgia/core/studio/plugin.cpp @@ -0,0 +1,32 @@ +/* + * Copyright 2016-2017 gtalent2@gmail.com + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include + +#include "import_tilesheet_wizard.hpp" + +#include "plugin.hpp" + +using namespace nostalgia::studio; + +namespace nostalgia { +namespace core { + +Plugin::Plugin() { + addImportWizard( + tr("Tile Sheet"), + []() { + QVector pgs; + pgs.push_back(new ImportTilesheetWizardPage()); + return pgs; + } + ); +} + +} +} diff --git a/src/nostalgia/core/studio/plugin.hpp b/src/nostalgia/core/studio/plugin.hpp new file mode 100644 index 00000000..6193ca48 --- /dev/null +++ b/src/nostalgia/core/studio/plugin.hpp @@ -0,0 +1,28 @@ +/* + * Copyright 2016-2017 gtalent2@gmail.com + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#include + +#include + +namespace nostalgia { +namespace core { + +class Plugin: public QObject, studio::Plugin { + Q_OBJECT + Q_PLUGIN_METADATA(IID "net.drinkingtea.nostalgia.studio.Plugin") + Q_INTERFACES(nostalgia::studio::Plugin) + + public: + Plugin(); +}; + +} +} diff --git a/src/nostalgia/studio/lib/plugin.cpp b/src/nostalgia/studio/lib/plugin.cpp new file mode 100644 index 00000000..f9ed6bfb --- /dev/null +++ b/src/nostalgia/studio/lib/plugin.cpp @@ -0,0 +1,31 @@ +/* + * Copyright 2016-2017 gtalent2@gmail.com + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "plugin.hpp" + +namespace nostalgia { +namespace studio { + +void Plugin::addNewWizard(QString name, std::function()> make) { + m_newWizards.push_back({name, make}); +} + +void Plugin::addImportWizard(QString name, std::function()> make) { + m_importWizards.push_back({name, make}); +} + +QVector Plugin::newWizards() { + return m_newWizards; +} + +QVector Plugin::importWizards() { + return m_importWizards; +} + +} +} diff --git a/src/nostalgia/studio/lib/plugin.hpp b/src/nostalgia/studio/lib/plugin.hpp new file mode 100644 index 00000000..fd98c14a --- /dev/null +++ b/src/nostalgia/studio/lib/plugin.hpp @@ -0,0 +1,45 @@ +/* + * Copyright 2016-2017 gtalent2@gmail.com + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#include + +#include +#include +#include + +namespace nostalgia { +namespace studio { + +struct WizardMaker { + QString name; + std::function()> make; +}; + +class Plugin { + private: + QVector m_newWizards; + QVector m_importWizards; + + public: + void addNewWizard(QString name, std::function()> make); + + void addImportWizard(QString name, std::function()> make); + + QVector newWizards(); + + QVector importWizards(); +}; + +} +} + +#define PluginInterface_iid "net.drinkingtea.nostalgia.studio.Plugin" + +Q_DECLARE_INTERFACE(nostalgia::studio::Plugin, PluginInterface_iid) diff --git a/src/nostalgia/studio/lib/wizard.cpp b/src/nostalgia/studio/lib/wizard.cpp index 8079f72a..b439020d 100644 --- a/src/nostalgia/studio/lib/wizard.cpp +++ b/src/nostalgia/studio/lib/wizard.cpp @@ -132,6 +132,10 @@ WizardFormPage::~WizardFormPage() { } } +int WizardFormPage::accept() { + return 0; +} + void WizardFormPage::initializePage() { for (auto it = m_fields.begin(); it != m_fields.end(); it++) { auto key = it.key(); @@ -223,8 +227,8 @@ void WizardFormPage::addLineEdit(QString displayName, QString fieldName, QString m_currentLine++; } -void WizardFormPage::addPathBrowse(QString displayName, QString fieldName, - QString defaultVal, QFileDialog::FileMode fileMode) { +void WizardFormPage::addPathBrowse(QString displayName, QString fieldName, QString defaultVal, + QFileDialog::FileMode fileMode, QString fileExtensions) { auto layout = new QHBoxLayout(); auto lbl = new QLabel(displayName, this); auto le = new QLineEdit("", this); @@ -272,14 +276,18 @@ void WizardFormPage::addPathBrowse(QString displayName, QString fieldName, } ); - connect(btn, &QPushButton::clicked, [this, defaultVal, le, fileMode]() { + connect(btn, &QPushButton::clicked, [this, defaultVal, le, fileMode, fileExtensions]() { + auto dir = defaultVal; + if (dir == "") { + dir = QDir::homePath(); + } if (fileMode == QFileDialog::Directory) { - auto p = QFileDialog::getExistingDirectory(this, tr("Select Directory..."), defaultVal); + auto p = QFileDialog::getExistingDirectory(this, tr("Select Directory..."), dir); if (p != "") { le->setText(p); } } else if (fileMode == QFileDialog::ExistingFile) { - auto p = QFileDialog::getOpenFileName(this, tr("Select File..."), defaultVal); + auto p = QFileDialog::getOpenFileName(this, tr("Select File..."), dir, fileExtensions); if (p != "") { le->setText(p); } @@ -317,13 +325,15 @@ Wizard::Wizard(QString windowTitle, QWidget *parent): QWizard(parent) { setModal(true); } -void Wizard::setAccept(std::function acceptFunc) { +void Wizard::setAccept(std::function acceptFunc) { m_acceptFunc = acceptFunc; } void Wizard::accept() { - m_acceptFunc(); - QDialog::accept(); + auto page = dynamic_cast(currentPage()); + if (page == nullptr || page->accept() == 0) { + QDialog::accept(); + } } } diff --git a/src/nostalgia/studio/lib/wizard.hpp b/src/nostalgia/studio/lib/wizard.hpp index 33629fe4..014457bd 100644 --- a/src/nostalgia/studio/lib/wizard.hpp +++ b/src/nostalgia/studio/lib/wizard.hpp @@ -69,6 +69,8 @@ class WizardFormPage: public QWizardPage { ~WizardFormPage(); + virtual int accept(); + void initializePage() override; bool validatePage() override; @@ -79,8 +81,10 @@ class WizardFormPage: public QWizardPage { QString defaultVal = "", std::function validator = [](QString) { return 0; }); - void addPathBrowse(QString displayName, QString fieldName, QString defaultVal = QDir::homePath(), - QFileDialog::FileMode fileMode = QFileDialog::AnyFile); + void addPathBrowse(QString displayName, QString fieldName, + QString defaultVal = QDir::homePath(), + QFileDialog::FileMode fileMode = QFileDialog::AnyFile, + QString fileExtensions = ""); void showValidationError(QString msg); }; @@ -104,14 +108,13 @@ class WizardConclusionPage: public QWizardPage { class Wizard: public QWizard { Q_OBJECT - private: - std::function m_acceptFunc; + std::function m_acceptFunc; public: Wizard(QString windowTitle, QWidget *parent = 0); - void setAccept(std::function acceptFunc); + void setAccept(std::function acceptFunc); void accept(); }; diff --git a/src/nostalgia/studio/main.cpp b/src/nostalgia/studio/main.cpp index 3ab89aeb..a8b82c28 100644 --- a/src/nostalgia/studio/main.cpp +++ b/src/nostalgia/studio/main.cpp @@ -22,18 +22,10 @@ int run(int argc, char **args) { NostalgiaStudioProfile config; - // load in config file - QFile file(argProfilePath); - if (file.exists()) { - file.open(QIODevice::ReadOnly); - QTextStream in(&file); - readJson(in.readAll(), &config); - } - QApplication app(argc, args); - app.setApplicationName(config.appName); - MainWindow w(config); + MainWindow w(argProfilePath); + app.setApplicationName(w.windowTitle()); w.show(); return app.exec(); diff --git a/src/nostalgia/studio/mainwindow.cpp b/src/nostalgia/studio/mainwindow.cpp index d2941443..e168af77 100644 --- a/src/nostalgia/studio/mainwindow.cpp +++ b/src/nostalgia/studio/mainwindow.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -15,14 +16,16 @@ #include #include #include +#include #include #include #include #include "lib/json.hpp" -#include "lib/wizard.hpp" #include "lib/oxfstreeview.hpp" #include "lib/project.hpp" +#include "lib/wizard.hpp" + #include "mainwindow.hpp" namespace nostalgia { @@ -30,7 +33,17 @@ namespace studio { const QString MainWindow::StateFilePath = "studio_state.json"; -MainWindow::MainWindow(NostalgiaStudioProfile config, QWidget *parent) { +MainWindow::MainWindow(QString profilePath) { + m_profilePath = profilePath; + // load in profile file + NostalgiaStudioProfile profile; + QFile file(profilePath); + if (file.exists()) { + file.open(QIODevice::ReadOnly); + QTextStream in(&file); + readJson(in.readAll(), &profile); + } + auto screenSize = QApplication::desktop()->screenGeometry(); // set window to 75% of screen width, and center NostalgiaStudioProfile @@ -39,7 +52,7 @@ MainWindow::MainWindow(NostalgiaStudioProfile config, QWidget *parent) { move(-x(), -y()); move(screenSize.width() * (1 - sizePct) / 2, screenSize.height() * (1 - sizePct) / 2); - setWindowTitle(config.appName); + setWindowTitle(profile.appName); auto tabbar = new QTabBar(this); setCentralWidget(tabbar); @@ -47,6 +60,8 @@ MainWindow::MainWindow(NostalgiaStudioProfile config, QWidget *parent) { setupMenu(); setupProjectExplorer(); + loadPlugins(profile); + readState(); } @@ -57,6 +72,27 @@ MainWindow::~MainWindow() { } } +void MainWindow::loadPlugins(NostalgiaStudioProfile profile) { + for (auto p : profile.plugins) { +#if defined(Q_OS_WIN) + auto libName = p.libName + ".dll"; +#else + auto libName = "lib" + p.libName + ".so"; +#endif + auto pluginPath = QFileInfo(m_profilePath).absolutePath() + "/" + p.dir + "/" + libName; + + auto loader = new QPluginLoader(pluginPath); + auto plugin = qobject_cast(loader->instance()); + if (plugin) { + m_cleanupTasks.push_back([loader]() { + loader->unload(); + delete loader; + }); + m_plugins.push_back(plugin); + } + } +} + void MainWindow::setupMenu() { auto menu = menuBar(); auto fileMenu = menu->addMenu(tr("&File")); @@ -132,7 +168,7 @@ QAction *MainWindow::addAction(QMenu *menu, QString text, QString toolTip, const } QAction *MainWindow::addAction(QMenu *menu, QString text, QString toolTip, - QKeySequence::StandardKey key, const QObject *tgt, const char *cb) { + QKeySequence::StandardKey key, const QObject *tgt, const char *cb) { auto action = menu->addAction(text); action->setShortcuts(key); action->setStatusTip(toolTip); @@ -144,7 +180,7 @@ QAction *MainWindow::addAction(QMenu *menu, QString text, QString toolTip, } QAction *MainWindow::addAction(QMenu *menu, QString text, QString toolTip, - QKeySequence::StandardKey key, void (*cb)()) { + QKeySequence::StandardKey key, void (*cb)()) { auto action = menu->addAction(text); action->setShortcuts(key); action->setStatusTip(toolTip); @@ -248,14 +284,19 @@ void MainWindow::showNewWizard() { return pgs; } ); - wizard.setAccept([&wizard, ws, PROJECT_NAME, PROJECT_PATH]() { + wizard.setAccept([&wizard, ws, PROJECT_NAME, PROJECT_PATH]() -> int { auto projectName = wizard.field(PROJECT_NAME).toString(); auto projectPath = wizard.field(PROJECT_PATH).toString(); if (QDir(projectPath).exists()) { auto path = projectPath + "/" + projectName; if (!QDir(path).exists()) { Project(path).create(); + return 0; + } else { + return 1; } + } else { + return 2; } } ); @@ -271,36 +312,11 @@ void MainWindow::showImportWizard() { auto ws = new WizardSelect(); wizard.addPage(ws); - ws->addOption(tr("Tile Sheet"), - [&wizard, TILESHEET_NAME, IMPORT_PATH, BPP]() { - QVector pgs; - auto pg = new WizardFormPage(); - pg->addLineEdit(tr("Tile Sheet &Name:"), TILESHEET_NAME + "*", "", [IMPORT_PATH, pg, &wizard](QString projectName) { - auto projectPath = wizard.field(IMPORT_PATH).toString(); - auto path = projectPath + "/" + projectName; - if (!QDir(path).exists()) { - return 0; - } else { - pg->showValidationError(tr("This project directory already exists.")); - return 1; - } - } - ); - pg->addPathBrowse(tr("Tile Sheet &Path:"), IMPORT_PATH + "*", QDir::homePath(), QFileDialog::ExistingFile); - pg->addComboBox(tr("Bits Per Pixe&l:"), BPP, {"4", "8"}); - pgs.push_back(pg); - pgs.push_back(new WizardConclusionPage(tr("Importing tile sheet: %1 as %2"), {IMPORT_PATH, TILESHEET_NAME})); - return pgs; + for (auto p : m_plugins) { + for (auto w : p->importWizards()) { + ws->addOption(w.name, w.make); } - ); - - wizard.setAccept([&wizard, ws, TILESHEET_NAME, IMPORT_PATH]() { - auto tilesheetName = wizard.field(TILESHEET_NAME).toString(); - auto importPath = wizard.field(IMPORT_PATH).toString(); - if (QFile(importPath).exists()) { - } - } - ); + } wizard.show(); wizard.exec(); diff --git a/src/nostalgia/studio/mainwindow.hpp b/src/nostalgia/studio/mainwindow.hpp index 87acc6ef..75bd01d5 100644 --- a/src/nostalgia/studio/mainwindow.hpp +++ b/src/nostalgia/studio/mainwindow.hpp @@ -22,6 +22,7 @@ #include +#include "lib/plugin.hpp" #include "lib/project.hpp" namespace nostalgia { @@ -32,7 +33,7 @@ struct NostalgiaStudioState { }; template -int ioOp(T *io, NostalgiaStudioState *obj) { +ox::Error ioOp(T *io, NostalgiaStudioState *obj) { ox::Error err = 0; io->setFields(1); err |= io->op("project_path", &obj->projectPath); @@ -40,15 +41,32 @@ int ioOp(T *io, NostalgiaStudioState *obj) { } -struct NostalgiaStudioProfile { - QString appName; +struct NostalgiaStudioPluginDef { + QString dir; + QString libName; }; template -int ioOp(T *io, NostalgiaStudioProfile *obj) { +ox::Error ioOp(T *io, NostalgiaStudioPluginDef *obj) { ox::Error err = 0; - io->setFields(1); + io->setFields(2); + err |= io->op("dir", &obj->dir); + err |= io->op("lib_name", &obj->libName); + return err; +} + + +struct NostalgiaStudioProfile { + QString appName; + QVector plugins; +}; + +template +ox::Error ioOp(T *io, NostalgiaStudioProfile *obj) { + ox::Error err = 0; + io->setFields(2); err |= io->op("app_name", &obj->appName); + err |= io->op("plugins", &obj->plugins); return err; } @@ -61,6 +79,7 @@ class MainWindow: public QMainWindow { static const QString StateFilePath; private: + QString m_profilePath; NostalgiaStudioState m_state; QAction *m_importAction = nullptr; QSharedPointer m_project; @@ -68,13 +87,16 @@ class MainWindow: public QMainWindow { QVector> m_cleanupTasks; QVector> m_dockWidgets; QTreeView *m_projectExplorer = nullptr; + QVector m_plugins; public: - MainWindow(NostalgiaStudioProfile config, QWidget *parent = 0); + MainWindow(QString profilePath); virtual ~MainWindow(); private: + void loadPlugins(NostalgiaStudioProfile profile); + void setupDockWidgets(); void setupMenu(); diff --git a/src/nostalgia/studio/nostalgia-studio.json b/src/nostalgia/studio/nostalgia-studio.json index 91e15fa9..2d711219 100644 --- a/src/nostalgia/studio/nostalgia-studio.json +++ b/src/nostalgia/studio/nostalgia-studio.json @@ -1,3 +1,9 @@ { - "app_name": "Nostalgia Studio" + "app_name": "Nostalgia Studio", + "plugins": [ + { + "dir": "../lib/nostalgia", + "lib_name": "NostalgiaCore-Studio" + } + ] } diff --git a/src/nostalgia/studio/studio.hpp b/src/nostalgia/studio/studio.hpp new file mode 100644 index 00000000..2385c1e6 --- /dev/null +++ b/src/nostalgia/studio/studio.hpp @@ -0,0 +1,15 @@ +/* + * Copyright 2016-2017 gtalent2@gmail.com + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#include "lib/json.hpp" +#include "lib/plugin.hpp" +#include "lib/project.hpp" +#include "lib/oxfstreeview.hpp" +#include "lib/wizard.hpp"