From d6d27ae8b33dbba6db661b875b4926dab64aa2c9 Mon Sep 17 00:00:00 2001 From: GriffinR Date: Fri, 1 Sep 2023 14:00:09 -0400 Subject: [PATCH] Add Custom Scripts Editor window --- forms/customscriptseditor.ui | 164 ++++++++++++++++++++ forms/customscriptslistitem.ui | 87 +++++++++++ forms/mainwindow.ui | 6 + include/config.h | 6 +- include/editor.h | 8 +- include/mainwindow.h | 4 + include/ui/customscriptseditor.h | 60 ++++++++ include/ui/customscriptslistitem.h | 22 +++ porymap.pro | 8 +- resources/icons/edit_document.ico | Bin 0 -> 1675 bytes resources/icons/refresh.ico | Bin 0 -> 1664 bytes resources/images.qrc | 2 + src/config.cpp | 8 +- src/editor.cpp | 6 +- src/mainwindow.cpp | 81 ++++------ src/ui/customscriptseditor.cpp | 231 +++++++++++++++++++++++++++++ src/ui/customscriptslistitem.cpp | 14 ++ src/ui/projectsettingseditor.cpp | 38 ++--- 18 files changed, 651 insertions(+), 94 deletions(-) create mode 100644 forms/customscriptseditor.ui create mode 100644 forms/customscriptslistitem.ui create mode 100644 include/ui/customscriptseditor.h create mode 100644 include/ui/customscriptslistitem.h create mode 100755 resources/icons/edit_document.ico create mode 100755 resources/icons/refresh.ico create mode 100644 src/ui/customscriptseditor.cpp create mode 100644 src/ui/customscriptslistitem.cpp diff --git a/forms/customscriptseditor.ui b/forms/customscriptseditor.ui new file mode 100644 index 00000000..12acb332 --- /dev/null +++ b/forms/customscriptseditor.ui @@ -0,0 +1,164 @@ + + + CustomScriptsEditor + + + + 0 + 0 + 374 + 355 + + + + Custom Scripts Editor + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + Add New Script... + + + + :/icons/add.ico:/icons/add.ico + + + + + + + Reload Scripts + + + + :/icons/refresh.ico:/icons/refresh.ico + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + <html><head/><body><p><a href="https://huderlem.github.io/porymap/manual/scripting-capabilities.html"><span style=" text-decoration: underline; color:#0000ff;">What are custom scripts?</span></a></p></body></html> + + + true + + + + + + + Qt::Vertical + + + QSizePolicy::Minimum + + + + 20 + 5 + + + + + + + + Scripts + + + + + + + QAbstractItemView::NoEditTriggers + + + false + + + QAbstractItemView::NoDragDrop + + + Qt::IgnoreAction + + + QAbstractItemView::ExtendedSelection + + + Qt::ElideLeft + + + QListView::Free + + + + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + + + + + + diff --git a/forms/customscriptslistitem.ui b/forms/customscriptslistitem.ui new file mode 100644 index 00000000..5784b442 --- /dev/null +++ b/forms/customscriptslistitem.ui @@ -0,0 +1,87 @@ + + + CustomScriptsListItem + + + + 0 + 0 + 129 + 30 + + + + + 0 + 0 + + + + + 4 + + + 4 + + + + + + 1 + 0 + + + + + + + + + + + Choose a new filepath for this script + + + ... + + + + :/icons/folder.ico:/icons/folder.ico + + + + + + + Open this script file + + + ... + + + + :/icons/edit_document.ico:/icons/edit_document.ico + + + + + + + Remove this script + + + ... + + + + :/icons/delete.ico:/icons/delete.ico + + + + + + + + + + diff --git a/forms/mainwindow.ui b/forms/mainwindow.ui index f153538c..f23028b6 100644 --- a/forms/mainwindow.ui +++ b/forms/mainwindow.ui @@ -3018,6 +3018,7 @@ + @@ -3314,6 +3315,11 @@ Edit Project Settings... + + + Custom Scripts... + + diff --git a/include/config.h b/include/config.h index 7ffcca38..f715e36c 100644 --- a/include/config.h +++ b/include/config.h @@ -348,8 +348,8 @@ public: bool getEncounterJsonActive(); void setProjectDir(QString projectDir); QString getProjectDir(); - void setCustomScripts(QList scripts); - QList getCustomScripts(); + void setCustomScripts(QStringList scripts); + QStringList getCustomScripts(); protected: virtual QString getConfigFilepath() override; virtual void parseConfigKeyValue(QString key, QString value) override; @@ -363,7 +363,7 @@ private: QString projectDir; QString recentMap; bool useEncounterJson; - QList customScripts; + QStringList customScripts; QStringList readKeys; }; diff --git a/include/editor.h b/include/editor.h index e28d9120..2282fba7 100644 --- a/include/editor.h +++ b/include/editor.h @@ -149,7 +149,7 @@ public: void shouldReselectEvents(); void scaleMapView(int); - void openInTextEditor(const QString &path, int lineNum = 0) const; + static void openInTextEditor(const QString &path, int lineNum = 0); bool eventLimitReached(Event::Type type); public slots: @@ -179,9 +179,9 @@ private: void updateEncounterFields(EncounterFields newFields); QString getMovementPermissionText(uint16_t collision, uint16_t elevation); QString getMetatileDisplayMessage(uint16_t metatileId); - bool startDetachedProcess(const QString &command, - const QString &workingDirectory = QString(), - qint64 *pid = nullptr) const; + static bool startDetachedProcess(const QString &command, + const QString &workingDirectory = QString(), + qint64 *pid = nullptr); private slots: void onMapStartPaint(QGraphicsSceneMouseEvent *event, MapPixmapItem *item); diff --git a/include/mainwindow.h b/include/mainwindow.h index 32bb03eb..4ccaa6ff 100644 --- a/include/mainwindow.h +++ b/include/mainwindow.h @@ -26,6 +26,7 @@ #include "shortcutseditor.h" #include "preferenceeditor.h" #include "projectsettingseditor.h" +#include "customscriptseditor.h" @@ -286,6 +287,7 @@ private slots: void on_actionEdit_Preferences_triggered(); void togglePreferenceSpecificUi(); void on_actionEdit_Project_Settings_triggered(); + void on_actionCustom_Scripts_triggered(); public: Ui::MainWindow *ui; @@ -300,6 +302,7 @@ private: QPointer newMapPrompt = nullptr; QPointer preferenceEditor = nullptr; QPointer projectSettingsEditor = nullptr; + QPointer customScriptsEditor = nullptr; FilterChildrenProxyModel *mapListProxyModel; QStandardItemModel *mapListModel; QList *mapGroupItemsList; @@ -343,6 +346,7 @@ private: bool loadProjectCombos(); bool populateMapList(); void sortMapList(); + void openSubWindow(QWidget * window); QString getExistingDirectory(QString); bool openProject(QString dir); QString getDefaultMap(); diff --git a/include/ui/customscriptseditor.h b/include/ui/customscriptseditor.h new file mode 100644 index 00000000..1d8b88c2 --- /dev/null +++ b/include/ui/customscriptseditor.h @@ -0,0 +1,60 @@ +#ifndef CUSTOMSCRIPTSEDITOR_H +#define CUSTOMSCRIPTSEDITOR_H + +#include +#include +#include +#include + +#include "customscriptslistitem.h" + +namespace Ui { +class CustomScriptsEditor; +} + + +class CustomScriptsEditor : public QMainWindow +{ + Q_OBJECT + +public: + explicit CustomScriptsEditor(QWidget *parent = nullptr); + ~CustomScriptsEditor(); + +signals: + void reloadScriptEngine(); + +private: + Ui::CustomScriptsEditor *ui; + + bool hasUnsavedChanges = false; + QString importDir; + const QString baseDir; + + void displayScript(const QString &filepath); + QString chooseScript(QString dir); + + void removeScript(QListWidgetItem * item); + void replaceScript(QListWidgetItem * item); + void openScript(QListWidgetItem * item); + + QString getListItemFilepath(QListWidgetItem * item) const; + void setListItemFilepath(QListWidgetItem * item, QString filepath) const; + + int prompt(const QString &text, QMessageBox::StandardButton defaultButton); + void save(); + void closeEvent(QCloseEvent*); + + void initShortcuts(); + QObjectList shortcutableObjects() const; + void applyUserShortcuts(); + +private slots: + void dialogButtonClicked(QAbstractButton *button); + void addNewScript(); + void reloadScripts(); + void removeSelectedScripts(); + void openSelectedScripts(); +}; + +#endif // CUSTOMSCRIPTSEDITOR_H diff --git a/include/ui/customscriptslistitem.h b/include/ui/customscriptslistitem.h new file mode 100644 index 00000000..d166db8a --- /dev/null +++ b/include/ui/customscriptslistitem.h @@ -0,0 +1,22 @@ +#ifndef CUSTOMSCRIPTSLISTITEM_H +#define CUSTOMSCRIPTSLISTITEM_H + +#include + +namespace Ui { +class CustomScriptsListItem; +} + +class CustomScriptsListItem : public QFrame +{ + Q_OBJECT + +public: + explicit CustomScriptsListItem(QWidget *parent = nullptr); + ~CustomScriptsListItem(); + +public: + Ui::CustomScriptsListItem *ui; +}; + +#endif // CUSTOMSCRIPTSLISTITEM_H diff --git a/porymap.pro b/porymap.pro index 4728f177..4a2a2008 100644 --- a/porymap.pro +++ b/porymap.pro @@ -42,6 +42,8 @@ SOURCES += src/core/block.cpp \ src/scriptapi/apiutility.cpp \ src/scriptapi/scripting.cpp \ src/ui/aboutporymap.cpp \ + src/ui/customscriptseditor.cpp \ + src/ui/customscriptslistitem.cpp \ src/ui/draggablepixmapitem.cpp \ src/ui/bordermetatilespixmapitem.cpp \ src/ui/collisionpixmapitem.cpp \ @@ -129,6 +131,8 @@ HEADERS += include/core/block.h \ include/lib/orderedmap.h \ include/lib/orderedjson.h \ include/ui/aboutporymap.h \ + include/ui/customscriptseditor.h \ + include/ui/customscriptslistitem.h \ include/ui/draggablepixmapitem.h \ include/ui/bordermetatilespixmapitem.h \ include/ui/collisionpixmapitem.h \ @@ -204,7 +208,9 @@ FORMS += forms/mainwindow.ui \ forms/preferenceeditor.ui \ forms/regionmappropertiesdialog.ui \ forms/colorpicker.ui \ - forms/projectsettingseditor.ui + forms/projectsettingseditor.ui \ + forms/customscriptseditor.ui \ + forms/customscriptslistitem.ui RESOURCES += \ resources/images.qrc \ diff --git a/resources/icons/edit_document.ico b/resources/icons/edit_document.ico new file mode 100755 index 0000000000000000000000000000000000000000..cd4bb026137a915d7478de409c616f9952501e85 GIT binary patch literal 1675 zcmV;626Xv}P)6i>#fJ}@g}2A>p=fkB!F{b7BR_vF zispS>fu5v*D}Cn7e{JIZ`}c9Gyu9R%Idf*yqiKWhD?H?Nxt(}fpNvLNGn(8jh`v*U z=e~I}N~HzSj0Nnzb^8uELW<0OdUr_5U>qzg#Jst4F?rIY^rGUDyRXkmAIx2|r?AwU zHsUGt?Gumu1A8$|ei1D#Ug&-=e?3m-i)^>cvMfYVlrD+AUYsTM4u=!{`}fEAaTAjY z%1&PU>Eje{OmrN2#rMXIx|>Ms`#6f4%FyqRT8!E8?il`_8fp!<-rle)jjy+eH#V7K zVq;P7Xhu>(Ukol^l$y4D0n(C_vAy6NRDUzBU%w7BN=91lk_oi@9!awtzF~L7=5v&; zA+C2E&SfRx`DOEgq)ec46P6hZQFreFWIY<&H*MzNw>V`u3Bx=fN6Z0xed?3LQCNBc zPn=nd3E7K)flGj@4Zw|K~=EaI63C%H!yNhMyWFfBic(Y83i zJo?w*F%?|N8bFpWYy+<&aM{A{pHuM8&d$LF@k1X}MT4fsLQ|qhaaHuudOT+B=t;lv zIl1-RWtpNt)uBYkqjvQmOjy3K&Fyt%A{XUeN!niWirNd@Z=?JmNNr6l?hn7=Ng!&fp6W^8F&W;7%UuL z!SHI1gjmW^uWXfY^nC_D^P;Gz09USFCb>f3F(^55oWKVKzz2qc`;k+Xhz0qzVo%HP zK9a}@-xn7%-sQ-Bc5d<0aiAn;?N?r`w8_SeGnc)=_m50EakFcRv}DQWPYvs@KX5RK_yYtS{C z*L1jUUqtFlt6|(Nhtc98%Y6bo^FS?~87WB~ltL0z#0pH?q9H~nU@ew<4O73AaCS8V z5g&hEEyb`D$_2k41AFibs_Xtj-laq=C2+~0NFh%s!2;D)1=RMn)k4K8hAEpQoc@&h z4b-m+U*Noen?Y8D66mxcvnsSV6j@04H7`;ow zq`>zgd7|kbmyz@)8A8%0q72gYFV}|Ph1Bsd1vLJR7W&EjA2)t8X(sV^_T%3I3;>il V-yZDPnl}Id002ovPDHLkV1k)VA`Jil literal 0 HcmV?d00001 diff --git a/resources/icons/refresh.ico b/resources/icons/refresh.ico new file mode 100755 index 0000000000000000000000000000000000000000..fdf20e26cd11305fd5cb59a633da930067ee30eb GIT binary patch literal 1664 zcmV-`27md9P)+8^?#X=0li+or^58?{(eDn76k ztkFc+79S{&738tJ7FhN<({pF;D&j7?HKdbVc9%PI&v(9a&UelLV|#_HJqRWDOO(=g zDVPUzV+GQ%;I0Lfez_kY^IIXzM9Q2n1!pk3H zJt5yvkhu|dT|G=~-%vbh!$2PC1cV!6U=WQIei1so4!!m|)xgM0Ci>k@emJ3Sd#mf*8;X$3{2SToS7#f?=a7u1dQeG_W}r^ zWHL;e+Cv#{rDV_m%#O;$u`7qr--eT9`~ltfhQrY^(;DGYC{Ovr}0=NjtFN7V#p zeWk~yGU8zR6U;myvf+Xa86GW1!UIvidM9Sh-Gm#Pm*v427KtWH8Ec@>k-OwP9#SE@ z2k=KQ^MJ^P3N~D@A*13!5q#-6Gn%s>%bYfNNV*Hj$h(`Cufft;FXB$?kFa^H5cO&V zRJ!kX0d2Q%m@z4hYMlD`OLLH)k+ek}GT5;3%09IF>H%!FnmuzeVxuFmDj^fb&@kL> zFM-8&NA;XN!+L|Mg+O@V{QDHDnWjj7X(!_;^N^pFRDfH}N3c5KD}4RtT8g1WnJPTh z`(#IwT6walz~!-_YoHGITPomK82%}J@=$_OzGwNN;ct060zd?1~jmsB`@D|--_Qyb&K-tcQ2sTWxS-@*l z4wNv%lY$;nv!00=sY&@wqTYorGR`Erla>R1CRStIa;%UdI%bZa;L!wM&|<7(U_?L5 zJEVx}$OtM~$n;ztb&xAHlx+j3Dc^(Fx#zuJe|!Ou8Y)pO$724(7ce7wqDCz{n6+7I z#gGg>R)|(o?6Z@OzemzmRZH1!fy-Ql&#oQ9L2kTz_*^LX=?{@io>OPzaCR4R z0!XvM&tQF|kFS0nb0ygK0?%*|o*Ii5um=_Np)EO0000< KMNUMnLSTZocMvN8 literal 0 HcmV?d00001 diff --git a/resources/images.qrc b/resources/images.qrc index 4c739582..2399cbe9 100644 --- a/resources/images.qrc +++ b/resources/images.qrc @@ -4,6 +4,7 @@ icons/collapse_all.ico icons/cursor.ico icons/delete.ico + icons/edit_document.ico icons/expand_all.ico icons/fill_color_cursor.ico icons/fill_color.ico @@ -24,6 +25,7 @@ icons/porymap-icon-1.ico icons/porymap-icon-2.ico icons/porymap.icns + icons/refresh.ico icons/shift_cursor.ico icons/shift.ico icons/sort_alphabet.ico diff --git a/src/config.cpp b/src/config.cpp index 7541a894..d5e7e0fe 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -647,7 +647,7 @@ void ProjectConfig::parseConfigKeyValue(QString key, QString value) { userConfig.useEncounterJson = getConfigBool(key, value); } else if (key == "custom_scripts") { userConfig.customScripts.clear(); - QList paths = value.split(","); + QList paths = value.split(",", Qt::SkipEmptyParts); paths.removeDuplicates(); for (QString script : paths) { if (!script.isEmpty()) { @@ -1077,7 +1077,7 @@ void UserConfig::parseConfigKeyValue(QString key, QString value) { this->useEncounterJson = getConfigBool(key, value); } else if (key == "custom_scripts") { this->customScripts.clear(); - QList paths = value.split(","); + QList paths = value.split(",", Qt::SkipEmptyParts); paths.removeDuplicates(); for (QString script : paths) { if (!script.isEmpty()) { @@ -1133,12 +1133,12 @@ bool UserConfig::getEncounterJsonActive() { return this->useEncounterJson; } -void UserConfig::setCustomScripts(QList scripts) { +void UserConfig::setCustomScripts(QStringList scripts) { this->customScripts = scripts; this->save(); } -QList UserConfig::getCustomScripts() { +QStringList UserConfig::getCustomScripts() { return this->customScripts; } diff --git a/src/editor.cpp b/src/editor.cpp index 9996460e..4dc12885 100644 --- a/src/editor.cpp +++ b/src/editor.cpp @@ -2146,7 +2146,7 @@ void Editor::openScript(const QString &scriptLabel) const { openInTextEditor(scriptPath, lineNum); } -void Editor::openInTextEditor(const QString &path, int lineNum) const { +void Editor::openInTextEditor(const QString &path, int lineNum) { QString command = porymapConfig.getTextEditorGotoLine(); if (command.isEmpty()) { // Open map scripts in the system's default editor. @@ -2159,7 +2159,7 @@ void Editor::openInTextEditor(const QString &path, int lineNum) const { } else { command += " \"" + path + '\"'; } - startDetachedProcess(command); + Editor::startDetachedProcess(command); } } @@ -2172,7 +2172,7 @@ void Editor::openProjectInTextEditor() const { startDetachedProcess(command); } -bool Editor::startDetachedProcess(const QString &command, const QString &workingDirectory, qint64 *pid) const { +bool Editor::startDetachedProcess(const QString &command, const QString &workingDirectory, qint64 *pid) { logInfo("Executing command: " + command); QProcess process; #ifdef Q_OS_WIN diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 71283bc6..ef9f90c6 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -590,6 +590,19 @@ QString MainWindow::getDefaultMap() { return QString(); } +void MainWindow::openSubWindow(QWidget * window) { + if (!window) return; + + if (!window->isVisible()) { + window->show(); + } else if (window->isMinimized()) { + window->showNormal(); + } else { + window->raise(); + window->activateWindow(); + } +} + QString MainWindow::getExistingDirectory(QString dir) { return QFileDialog::getExistingDirectory(this, "Open Directory", dir, QFileDialog::ShowDirsOnly); } @@ -1184,12 +1197,9 @@ void MainWindow::openNewMapPopupWindow() { if (!this->newMapPrompt) { this->newMapPrompt = new NewMapPopup(this, this->editor->project); } - if (!this->newMapPrompt->isVisible()) { - this->newMapPrompt->show(); - } else { - this->newMapPrompt->raise(); - this->newMapPrompt->activateWindow(); - } + + openSubWindow(this->newMapPrompt); + connect(this->newMapPrompt, &NewMapPopup::applied, this, &MainWindow::onNewMapCreated); this->newMapPrompt->setAttribute(Qt::WA_DeleteOnClose); } @@ -1775,14 +1785,7 @@ void MainWindow::on_actionEdit_Shortcuts_triggered() if (!shortcutsEditor) initShortcutsEditor(); - if (shortcutsEditor->isHidden()) { - shortcutsEditor->show(); - } else if (shortcutsEditor->isMinimized()) { - shortcutsEditor->showNormal(); - } else { - shortcutsEditor->raise(); - shortcutsEditor->activateWindow(); - } + openSubWindow(shortcutsEditor); } void MainWindow::initShortcutsEditor() { @@ -2461,13 +2464,7 @@ void MainWindow::showExportMapImageWindow(ImageExporterMode mode) { this->mapImageExporter = new MapImageExporter(this, this->editor, mode); this->mapImageExporter->setAttribute(Qt::WA_DeleteOnClose); - if (!this->mapImageExporter->isVisible()) { - this->mapImageExporter->show(); - } else if (this->mapImageExporter->isMinimized()) { - this->mapImageExporter->showNormal(); - } else { - this->mapImageExporter->activateWindow(); - } + openSubWindow(this->mapImageExporter); } void MainWindow::on_comboBox_ConnectionDirection_currentTextChanged(const QString &direction) @@ -2663,14 +2660,7 @@ void MainWindow::on_actionTileset_Editor_triggered() initTilesetEditor(); } - if (!this->tilesetEditor->isVisible()) { - this->tilesetEditor->show(); - } else if (this->tilesetEditor->isMinimized()) { - this->tilesetEditor->showNormal(); - } else { - this->tilesetEditor->raise(); - this->tilesetEditor->activateWindow(); - } + openSubWindow(this->tilesetEditor); MetatileSelection selection = this->editor->metatile_selector_item->getMetatileSelection(); this->tilesetEditor->selectMetatile(selection.metatileItems.first().metatileId); @@ -2723,14 +2713,7 @@ void MainWindow::on_actionEdit_Preferences_triggered() { this, &MainWindow::togglePreferenceSpecificUi); } - if (!preferenceEditor->isVisible()) { - preferenceEditor->show(); - } else if (preferenceEditor->isMinimized()) { - preferenceEditor->showNormal(); - } else { - preferenceEditor->raise(); - preferenceEditor->activateWindow(); - } + openSubWindow(preferenceEditor); } void MainWindow::togglePreferenceSpecificUi() { @@ -2747,14 +2730,15 @@ void MainWindow::on_actionEdit_Project_Settings_triggered() { this, &MainWindow::on_action_Reload_Project_triggered); } - if (!this->projectSettingsEditor->isVisible()) { - this->projectSettingsEditor->show(); - } else if (this->projectSettingsEditor->isMinimized()) { - this->projectSettingsEditor->showNormal(); - } else { - this->projectSettingsEditor->raise(); - this->projectSettingsEditor->activateWindow(); + openSubWindow(this->projectSettingsEditor); +} + +void MainWindow::on_actionCustom_Scripts_triggered() { + if (!this->customScriptsEditor) { + this->customScriptsEditor = new CustomScriptsEditor(this); } + + openSubWindow(this->customScriptsEditor); } void MainWindow::on_pushButton_AddCustomHeaderField_clicked() @@ -2806,14 +2790,7 @@ void MainWindow::on_actionRegion_Map_Editor_triggered() { } } - if (!this->regionMapEditor->isVisible()) { - this->regionMapEditor->show(); - } else if (this->regionMapEditor->isMinimized()) { - this->regionMapEditor->showNormal(); - } else { - this->regionMapEditor->raise(); - this->regionMapEditor->activateWindow(); - } + openSubWindow(this->regionMapEditor); } void MainWindow::on_pushButton_CreatePrefab_clicked() { diff --git a/src/ui/customscriptseditor.cpp b/src/ui/customscriptseditor.cpp new file mode 100644 index 00000000..36babd51 --- /dev/null +++ b/src/ui/customscriptseditor.cpp @@ -0,0 +1,231 @@ +#include "customscriptseditor.h" +#include "ui_customscriptseditor.h" +#include "ui_customscriptslistitem.h" +#include "config.h" +#include "editor.h" +#include "shortcut.h" + +#include +#include + +// TODO: System for turning scripts on or off +// +// TODO: Better URL colors on dark themes +// TODO: Save window state + +CustomScriptsEditor::CustomScriptsEditor(QWidget *parent) : + QMainWindow(parent), + ui(new Ui::CustomScriptsEditor), + baseDir(userConfig.getProjectDir() + QDir::separator()) +{ + ui->setupUi(this); + setAttribute(Qt::WA_DeleteOnClose); + // This property seems to be reset if we don't set it programmatically + ui->list->setDragDropMode(QAbstractItemView::NoDragDrop); + + for (auto path : userConfig.getCustomScripts()) + this->displayScript(path); + + this->importDir = userConfig.getProjectDir(); + + connect(ui->button_AddNewScript, &QAbstractButton::clicked, this, &CustomScriptsEditor::addNewScript); + connect(ui->button_ReloadScripts, &QAbstractButton::clicked, this, &CustomScriptsEditor::reloadScripts); + connect(ui->buttonBox, &QDialogButtonBox::clicked, this, &CustomScriptsEditor::dialogButtonClicked); + + this->initShortcuts(); +} + +CustomScriptsEditor::~CustomScriptsEditor() +{ + ui->list->clear(); + delete ui; +} + +void CustomScriptsEditor::initShortcuts() { + auto *shortcut_remove = new Shortcut({QKeySequence("Del"), QKeySequence("Backspace")}, this, SLOT(removeSelectedScripts())); + shortcut_remove->setObjectName("shortcut_remove"); + shortcut_remove->setWhatsThis("Remove Selected Scripts"); + + // TODO: Prevent ambiguity with MainWindow + auto *shortcut_open = new Shortcut(QKeySequence("Ctrl+O"), this, SLOT(openSelectedScripts())); + shortcut_open->setObjectName("shortcut_open"); + shortcut_open->setWhatsThis("Open Selected Scripts"); + + auto *shortcut_addNew = new Shortcut(QKeySequence(), this, SLOT(addNewScript())); + shortcut_addNew->setObjectName("shortcut_addNew"); + shortcut_addNew->setWhatsThis("Add New Script..."); + + auto *shortcut_reload = new Shortcut(QKeySequence(), this, SLOT(reloadScripts())); + shortcut_reload->setObjectName("shortcut_reload"); + shortcut_reload->setWhatsThis("Reload Scripts"); + + //connect(new QShortcut(QKeySequence("Backspace"), this), &QShortcut::activated, this, &CustomScriptsEditor::removeSelectedScripts); + //connect(new QShortcut(QKeySequence("Ctrl+O"), this), &QShortcut::activated, this, &CustomScriptsEditor::openSelectedScripts); + + shortcutsConfig.load(); + shortcutsConfig.setDefaultShortcuts(shortcutableObjects()); + applyUserShortcuts(); +} + +QObjectList CustomScriptsEditor::shortcutableObjects() const { + QObjectList shortcutable_objects; + + for (auto *action : findChildren()) + if (!action->objectName().isEmpty()) + shortcutable_objects.append(qobject_cast(action)); + for (auto *shortcut : findChildren()) + if (!shortcut->objectName().isEmpty()) + shortcutable_objects.append(qobject_cast(shortcut)); + + return shortcutable_objects; +} + +// TODO: Connect to shorcuts editor +void CustomScriptsEditor::applyUserShortcuts() { + for (auto *action : findChildren()) + if (!action->objectName().isEmpty()) + action->setShortcuts(shortcutsConfig.userShortcuts(action)); + for (auto *shortcut : findChildren()) + if (!shortcut->objectName().isEmpty()) + shortcut->setKeys(shortcutsConfig.userShortcuts(shortcut)); +} + +void CustomScriptsEditor::displayScript(const QString &filepath) { + auto item = new QListWidgetItem(); + auto buttons = new CustomScriptsListItem(); + + buttons->ui->lineEdit_filepath->setText(filepath); + item->setSizeHint(buttons->sizeHint()); + + connect(buttons->ui->b_Choose, &QAbstractButton::clicked, [this, item](bool) { this->replaceScript(item); }); + connect(buttons->ui->b_Edit, &QAbstractButton::clicked, [this, item](bool) { this->openScript(item); }); + connect(buttons->ui->b_Delete, &QAbstractButton::clicked, [this, item](bool) { this->removeScript(item); }); + connect(buttons->ui->lineEdit_filepath, &QLineEdit::textEdited, [this](const QString&) { this->hasUnsavedChanges = true; }); + + // Per the Qt manual, for performance reasons QListWidget::setItemWidget shouldn't be used with non-static items. + // There's an assumption here that users won't have enough scripts for that to be a problem. + ui->list->addItem(item); + ui->list->setItemWidget(item, buttons); +} + +QString CustomScriptsEditor::getListItemFilepath(QListWidgetItem * item) const { + auto widget = dynamic_cast(ui->list->itemWidget(item)); + if (!widget) return QString(); + + QString path = widget->ui->lineEdit_filepath->text(); + QFileInfo fileInfo(path); + if (fileInfo.isRelative()) + path.prepend(this->baseDir); + return path; +} + +void CustomScriptsEditor::setListItemFilepath(QListWidgetItem * item, QString filepath) const { + auto widget = dynamic_cast(ui->list->itemWidget(item)); + if (!widget) return; + + if (filepath.startsWith(this->baseDir)) + filepath.remove(0, this->baseDir.length()); + widget->ui->lineEdit_filepath->setText(filepath); +} + +QString CustomScriptsEditor::chooseScript(QString dir) { + return QFileDialog::getOpenFileName(this, "Choose Custom Script File", dir, "JavaScript Files (*.js)"); +} + +void CustomScriptsEditor::addNewScript() { + QString filepath = this->chooseScript(this->importDir); + if (filepath.isEmpty()) + return; + this->importDir = filepath; + if (filepath.startsWith(this->baseDir)) + filepath.remove(0, this->baseDir.length()); + this->displayScript(filepath); + this->hasUnsavedChanges = true; +} + +void CustomScriptsEditor::removeScript(QListWidgetItem * item) { + ui->list->takeItem(ui->list->row(item)); + this->hasUnsavedChanges = true; +} + +void CustomScriptsEditor::removeSelectedScripts() { + QList items = ui->list->selectedItems(); + if (items.length() == 0) + return; + for (auto item : items) + this->removeScript(item); +} + +void CustomScriptsEditor::replaceScript(QListWidgetItem * item) { + const QString filepath = this->chooseScript(this->getListItemFilepath(item)); + if (filepath.isEmpty()) + return; + this->setListItemFilepath(item, filepath); + this->hasUnsavedChanges = true; +} + +void CustomScriptsEditor::openScript(QListWidgetItem * item) { + const QString path = this->getListItemFilepath(item); + QFileInfo fileInfo(path); + if (!fileInfo.exists() || !fileInfo.isFile()){ + QMessageBox::warning(this, "", QString("Failed to open script '%1'").arg(path)); + return; + } + Editor::openInTextEditor(path); +} + +void CustomScriptsEditor::openSelectedScripts() { + for (auto item : ui->list->selectedItems()) + this->openScript(item); +} + +void CustomScriptsEditor::reloadScripts() { + if (this->hasUnsavedChanges) { + if (this->prompt("Scripts have been modified, save changes and reload the script engine?", QMessageBox::Yes) == QMessageBox::No) + return; + this->save(); + } + emit reloadScriptEngine(); // TODO: Catch this signal +} + +void CustomScriptsEditor::save() { + if (!this->hasUnsavedChanges) + return; + // TODO: Set new paths in config + this->hasUnsavedChanges = false; + this->reloadScripts(); +} + +int CustomScriptsEditor::prompt(const QString &text, QMessageBox::StandardButton defaultButton) { + QMessageBox messageBox(this); + messageBox.setText(text); + messageBox.setIcon(QMessageBox::Question); + messageBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No | defaultButton); + messageBox.setDefaultButton(defaultButton); + return messageBox.exec(); +} + +void CustomScriptsEditor::dialogButtonClicked(QAbstractButton *button) { + if (ui->buttonBox->buttonRole(button) == QDialogButtonBox::AcceptRole) + this->save(); + close(); // All buttons (OK and Cancel) close the window +} + +void CustomScriptsEditor::closeEvent(QCloseEvent* event) { + if (this->hasUnsavedChanges) { + int result = this->prompt("Scripts have been modified, save changes?", QMessageBox::Cancel); + if (result == QMessageBox::Cancel) { + event->ignore(); + return; + } + if (result == QMessageBox::Yes) + this->save(); + } +/* + // TODO + porymapConfig.setProjectSettingsEditorGeometry( + this->saveGeometry(), + this->saveState() + ); +*/ +} diff --git a/src/ui/customscriptslistitem.cpp b/src/ui/customscriptslistitem.cpp new file mode 100644 index 00000000..e1452425 --- /dev/null +++ b/src/ui/customscriptslistitem.cpp @@ -0,0 +1,14 @@ +#include "customscriptslistitem.h" +#include "ui_customscriptslistitem.h" + +CustomScriptsListItem::CustomScriptsListItem(QWidget *parent) : + QFrame(parent), + ui(new Ui::CustomScriptsListItem) +{ + ui->setupUi(this); +} + +CustomScriptsListItem::~CustomScriptsListItem() +{ + delete ui; +} diff --git a/src/ui/projectsettingseditor.cpp b/src/ui/projectsettingseditor.cpp index 48428624..bf8d17ae 100644 --- a/src/ui/projectsettingseditor.cpp +++ b/src/ui/projectsettingseditor.cpp @@ -8,7 +8,7 @@ #include /* - Editor for the settings in a user's porymap.project.cfg and porymap.user.cfg files. + Editor for the settings in a user's porymap.project.cfg file (and 'use_encounter_json' in porymap.user.cfg). */ ProjectSettingsEditor::ProjectSettingsEditor(QWidget *parent, Project *project) : @@ -32,31 +32,20 @@ ProjectSettingsEditor::~ProjectSettingsEditor() // TODO: Move tool tips to editable areas void ProjectSettingsEditor::connectSignals() { - // Connect buttons connect(ui->buttonBox, &QDialogButtonBox::clicked, this, &ProjectSettingsEditor::dialogButtonClicked); connect(ui->button_ChoosePrefabs, &QAbstractButton::clicked, this, &ProjectSettingsEditor::choosePrefabsFileClicked); connect(ui->button_ImportDefaultPrefabs, &QAbstractButton::clicked, this, &ProjectSettingsEditor::importDefaultPrefabsClicked); - - // Connect combo boxes - QList combos = ui->centralwidget->findChildren(); - foreach(auto i, combos) - connect(i, &QComboBox::currentTextChanged, this, &ProjectSettingsEditor::markEdited); connect(ui->comboBox_BaseGameVersion, &QComboBox::currentTextChanged, this, &ProjectSettingsEditor::promptRestoreDefaults); - // Connect check boxes - QList checkboxes = ui->centralwidget->findChildren(); - foreach(auto i, checkboxes) - connect(i, &QCheckBox::stateChanged, this, &ProjectSettingsEditor::markEdited); - - // Connect spin boxes - QList spinBoxes = ui->centralwidget->findChildren(); - foreach(auto i, spinBoxes) - connect(i, QOverload::of(&QSpinBox::valueChanged), [this](int) { this->markEdited(); }); - - // Connect line edits - QList lineEdits = ui->centralwidget->findChildren(); - foreach(auto i, lineEdits) - connect(i, &QLineEdit::textEdited, this, &ProjectSettingsEditor::markEdited); + // Record that there are unsaved changes if any of the settings are modified + for (auto combo : ui->centralwidget->findChildren()) + connect(combo, &QComboBox::currentTextChanged, this, &ProjectSettingsEditor::markEdited); + for (auto checkBox : ui->centralwidget->findChildren()) + connect(checkBox, &QCheckBox::stateChanged, this, &ProjectSettingsEditor::markEdited); + for (auto spinBox : ui->centralwidget->findChildren()) + connect(spinBox, QOverload::of(&QSpinBox::valueChanged), [this](int) { this->markEdited(); }); + for (auto lineEdit : ui->centralwidget->findChildren()) + connect(lineEdit, &QLineEdit::textEdited, this, &ProjectSettingsEditor::markEdited); } void ProjectSettingsEditor::markEdited() { @@ -143,7 +132,6 @@ void ProjectSettingsEditor::save() { // Prevent a call to save() for each of the config settings projectConfig.setSaveDisabled(true); - userConfig.setSaveDisabled(true); projectConfig.setDefaultPrimaryTileset(ui->comboBox_DefaultPrimaryTileset->currentText()); projectConfig.setDefaultSecondaryTileset(ui->comboBox_DefaultSecondaryTileset->currentText()); @@ -183,8 +171,6 @@ void ProjectSettingsEditor::save() { projectConfig.setSaveDisabled(false); projectConfig.save(); - userConfig.setSaveDisabled(false); - userConfig.save(); this->hasUnsavedChanges = false; // Technically, a reload is not required for several of the config settings. @@ -192,6 +178,7 @@ void ProjectSettingsEditor::save() { this->projectNeedsReload = true; } +// TODO: If the selected file is in the project directory use a relative path void ProjectSettingsEditor::choosePrefabsFileClicked(bool) { QString startPath = this->project->importExportPath; QFileInfo fileInfo(ui->lineEdit_PrefabsPath->text()); @@ -253,12 +240,9 @@ bool ProjectSettingsEditor::promptRestoreDefaults() { // Restore defaults by resetting config in memory, refreshing the UI, then restoring the config. // Don't want to save changes until user accepts them. ProjectConfig tempProject = projectConfig; - UserConfig tempUser = userConfig; projectConfig.reset(projectConfig.stringToBaseGameVersion(versionText)); - userConfig.reset(); this->refresh(); projectConfig = tempProject; - userConfig = tempUser; this->hasUnsavedChanges = true; return true;