Add Custom Scripts Editor window

This commit is contained in:
GriffinR 2023-09-01 14:00:09 -04:00
parent 143e5cf79b
commit d6d27ae8b3
18 changed files with 651 additions and 94 deletions

View file

@ -0,0 +1,164 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>CustomScriptsEditor</class>
<widget class="QMainWindow" name="CustomScriptsEditor">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>374</width>
<height>355</height>
</rect>
</property>
<property name="windowTitle">
<string>Custom Scripts Editor</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QFrame" name="window">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QFrame" name="header">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="button_AddNewScript">
<property name="text">
<string>Add New Script...</string>
</property>
<property name="icon">
<iconset resource="../resources/images.qrc">
<normaloff>:/icons/add.ico</normaloff>:/icons/add.ico</iconset>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="button_ReloadScripts">
<property name="text">
<string>Reload Scripts</string>
</property>
<property name="icon">
<iconset resource="../resources/images.qrc">
<normaloff>:/icons/refresh.ico</normaloff>:/icons/refresh.ico</iconset>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_header">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QLabel" name="label_Manual">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;https://huderlem.github.io/porymap/manual/scripting-capabilities.html&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;What are custom scripts?&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="openExternalLinks">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Minimum</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>5</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label_ListHeader">
<property name="text">
<string>Scripts</string>
</property>
</widget>
</item>
<item>
<widget class="QListWidget" name="list">
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="showDropIndicator" stdset="0">
<bool>false</bool>
</property>
<property name="dragDropMode">
<enum>QAbstractItemView::NoDragDrop</enum>
</property>
<property name="defaultDropAction">
<enum>Qt::IgnoreAction</enum>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
<property name="textElideMode">
<enum>Qt::ElideLeft</enum>
</property>
<property name="movement">
<enum>QListView::Free</enum>
</property>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</widget>
<resources>
<include location="../resources/images.qrc"/>
</resources>
<connections/>
</ui>

View file

@ -0,0 +1,87 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>CustomScriptsListItem</class>
<widget class="QFrame" name="CustomScriptsListItem">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>129</width>
<height>30</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QHBoxLayout">
<property name="topMargin">
<number>4</number>
</property>
<property name="bottomMargin">
<number>4</number>
</property>
<item>
<widget class="QLineEdit" name="lineEdit_filepath">
<property name="minimumSize">
<size>
<width>1</width>
<height>0</height>
</size>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="b_Choose">
<property name="toolTip">
<string>Choose a new filepath for this script</string>
</property>
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../resources/images.qrc">
<normaloff>:/icons/folder.ico</normaloff>:/icons/folder.ico</iconset>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="b_Edit">
<property name="toolTip">
<string>Open this script file</string>
</property>
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../resources/images.qrc">
<normaloff>:/icons/edit_document.ico</normaloff>:/icons/edit_document.ico</iconset>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="b_Delete">
<property name="toolTip">
<string>Remove this script</string>
</property>
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../resources/images.qrc">
<normaloff>:/icons/delete.ico</normaloff>:/icons/delete.ico</iconset>
</property>
</widget>
</item>
</layout>
</widget>
<resources>
<include location="../resources/images.qrc"/>
</resources>
<connections/>
</ui>

View file

@ -3018,6 +3018,7 @@
<addaction name="actionEdit_Project_Settings"/> <addaction name="actionEdit_Project_Settings"/>
<addaction name="actionEdit_Preferences"/> <addaction name="actionEdit_Preferences"/>
<addaction name="actionEdit_Shortcuts"/> <addaction name="actionEdit_Shortcuts"/>
<addaction name="actionCustom_Scripts"/>
</widget> </widget>
<addaction name="menuFile"/> <addaction name="menuFile"/>
<addaction name="menuEdit"/> <addaction name="menuEdit"/>
@ -3314,6 +3315,11 @@
<string>Edit Project Settings...</string> <string>Edit Project Settings...</string>
</property> </property>
</action> </action>
<action name="actionCustom_Scripts">
<property name="text">
<string>Custom Scripts...</string>
</property>
</action>
</widget> </widget>
<layoutdefault spacing="6" margin="11"/> <layoutdefault spacing="6" margin="11"/>
<customwidgets> <customwidgets>

View file

@ -348,8 +348,8 @@ public:
bool getEncounterJsonActive(); bool getEncounterJsonActive();
void setProjectDir(QString projectDir); void setProjectDir(QString projectDir);
QString getProjectDir(); QString getProjectDir();
void setCustomScripts(QList<QString> scripts); void setCustomScripts(QStringList scripts);
QList<QString> getCustomScripts(); QStringList getCustomScripts();
protected: protected:
virtual QString getConfigFilepath() override; virtual QString getConfigFilepath() override;
virtual void parseConfigKeyValue(QString key, QString value) override; virtual void parseConfigKeyValue(QString key, QString value) override;
@ -363,7 +363,7 @@ private:
QString projectDir; QString projectDir;
QString recentMap; QString recentMap;
bool useEncounterJson; bool useEncounterJson;
QList<QString> customScripts; QStringList customScripts;
QStringList readKeys; QStringList readKeys;
}; };

View file

@ -149,7 +149,7 @@ public:
void shouldReselectEvents(); void shouldReselectEvents();
void scaleMapView(int); 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); bool eventLimitReached(Event::Type type);
public slots: public slots:
@ -179,9 +179,9 @@ private:
void updateEncounterFields(EncounterFields newFields); void updateEncounterFields(EncounterFields newFields);
QString getMovementPermissionText(uint16_t collision, uint16_t elevation); QString getMovementPermissionText(uint16_t collision, uint16_t elevation);
QString getMetatileDisplayMessage(uint16_t metatileId); QString getMetatileDisplayMessage(uint16_t metatileId);
bool startDetachedProcess(const QString &command, static bool startDetachedProcess(const QString &command,
const QString &workingDirectory = QString(), const QString &workingDirectory = QString(),
qint64 *pid = nullptr) const; qint64 *pid = nullptr);
private slots: private slots:
void onMapStartPaint(QGraphicsSceneMouseEvent *event, MapPixmapItem *item); void onMapStartPaint(QGraphicsSceneMouseEvent *event, MapPixmapItem *item);

View file

@ -26,6 +26,7 @@
#include "shortcutseditor.h" #include "shortcutseditor.h"
#include "preferenceeditor.h" #include "preferenceeditor.h"
#include "projectsettingseditor.h" #include "projectsettingseditor.h"
#include "customscriptseditor.h"
@ -286,6 +287,7 @@ private slots:
void on_actionEdit_Preferences_triggered(); void on_actionEdit_Preferences_triggered();
void togglePreferenceSpecificUi(); void togglePreferenceSpecificUi();
void on_actionEdit_Project_Settings_triggered(); void on_actionEdit_Project_Settings_triggered();
void on_actionCustom_Scripts_triggered();
public: public:
Ui::MainWindow *ui; Ui::MainWindow *ui;
@ -300,6 +302,7 @@ private:
QPointer<NewMapPopup> newMapPrompt = nullptr; QPointer<NewMapPopup> newMapPrompt = nullptr;
QPointer<PreferenceEditor> preferenceEditor = nullptr; QPointer<PreferenceEditor> preferenceEditor = nullptr;
QPointer<ProjectSettingsEditor> projectSettingsEditor = nullptr; QPointer<ProjectSettingsEditor> projectSettingsEditor = nullptr;
QPointer<CustomScriptsEditor> customScriptsEditor = nullptr;
FilterChildrenProxyModel *mapListProxyModel; FilterChildrenProxyModel *mapListProxyModel;
QStandardItemModel *mapListModel; QStandardItemModel *mapListModel;
QList<QStandardItem*> *mapGroupItemsList; QList<QStandardItem*> *mapGroupItemsList;
@ -343,6 +346,7 @@ private:
bool loadProjectCombos(); bool loadProjectCombos();
bool populateMapList(); bool populateMapList();
void sortMapList(); void sortMapList();
void openSubWindow(QWidget * window);
QString getExistingDirectory(QString); QString getExistingDirectory(QString);
bool openProject(QString dir); bool openProject(QString dir);
QString getDefaultMap(); QString getDefaultMap();

View file

@ -0,0 +1,60 @@
#ifndef CUSTOMSCRIPTSEDITOR_H
#define CUSTOMSCRIPTSEDITOR_H
#include <QMainWindow>
#include <QListWidget>
#include <QAbstractButton>
#include <QMessageBox>
#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

View file

@ -0,0 +1,22 @@
#ifndef CUSTOMSCRIPTSLISTITEM_H
#define CUSTOMSCRIPTSLISTITEM_H
#include <QFrame>
namespace Ui {
class CustomScriptsListItem;
}
class CustomScriptsListItem : public QFrame
{
Q_OBJECT
public:
explicit CustomScriptsListItem(QWidget *parent = nullptr);
~CustomScriptsListItem();
public:
Ui::CustomScriptsListItem *ui;
};
#endif // CUSTOMSCRIPTSLISTITEM_H

View file

@ -42,6 +42,8 @@ SOURCES += src/core/block.cpp \
src/scriptapi/apiutility.cpp \ src/scriptapi/apiutility.cpp \
src/scriptapi/scripting.cpp \ src/scriptapi/scripting.cpp \
src/ui/aboutporymap.cpp \ src/ui/aboutporymap.cpp \
src/ui/customscriptseditor.cpp \
src/ui/customscriptslistitem.cpp \
src/ui/draggablepixmapitem.cpp \ src/ui/draggablepixmapitem.cpp \
src/ui/bordermetatilespixmapitem.cpp \ src/ui/bordermetatilespixmapitem.cpp \
src/ui/collisionpixmapitem.cpp \ src/ui/collisionpixmapitem.cpp \
@ -129,6 +131,8 @@ HEADERS += include/core/block.h \
include/lib/orderedmap.h \ include/lib/orderedmap.h \
include/lib/orderedjson.h \ include/lib/orderedjson.h \
include/ui/aboutporymap.h \ include/ui/aboutporymap.h \
include/ui/customscriptseditor.h \
include/ui/customscriptslistitem.h \
include/ui/draggablepixmapitem.h \ include/ui/draggablepixmapitem.h \
include/ui/bordermetatilespixmapitem.h \ include/ui/bordermetatilespixmapitem.h \
include/ui/collisionpixmapitem.h \ include/ui/collisionpixmapitem.h \
@ -204,7 +208,9 @@ FORMS += forms/mainwindow.ui \
forms/preferenceeditor.ui \ forms/preferenceeditor.ui \
forms/regionmappropertiesdialog.ui \ forms/regionmappropertiesdialog.ui \
forms/colorpicker.ui \ forms/colorpicker.ui \
forms/projectsettingseditor.ui forms/projectsettingseditor.ui \
forms/customscriptseditor.ui \
forms/customscriptslistitem.ui
RESOURCES += \ RESOURCES += \
resources/images.qrc \ resources/images.qrc \

BIN
resources/icons/edit_document.ico Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
resources/icons/refresh.ico Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View file

@ -4,6 +4,7 @@
<file>icons/collapse_all.ico</file> <file>icons/collapse_all.ico</file>
<file>icons/cursor.ico</file> <file>icons/cursor.ico</file>
<file>icons/delete.ico</file> <file>icons/delete.ico</file>
<file>icons/edit_document.ico</file>
<file>icons/expand_all.ico</file> <file>icons/expand_all.ico</file>
<file>icons/fill_color_cursor.ico</file> <file>icons/fill_color_cursor.ico</file>
<file>icons/fill_color.ico</file> <file>icons/fill_color.ico</file>
@ -24,6 +25,7 @@
<file>icons/porymap-icon-1.ico</file> <file>icons/porymap-icon-1.ico</file>
<file>icons/porymap-icon-2.ico</file> <file>icons/porymap-icon-2.ico</file>
<file>icons/porymap.icns</file> <file>icons/porymap.icns</file>
<file>icons/refresh.ico</file>
<file>icons/shift_cursor.ico</file> <file>icons/shift_cursor.ico</file>
<file>icons/shift.ico</file> <file>icons/shift.ico</file>
<file>icons/sort_alphabet.ico</file> <file>icons/sort_alphabet.ico</file>

View file

@ -647,7 +647,7 @@ void ProjectConfig::parseConfigKeyValue(QString key, QString value) {
userConfig.useEncounterJson = getConfigBool(key, value); userConfig.useEncounterJson = getConfigBool(key, value);
} else if (key == "custom_scripts") { } else if (key == "custom_scripts") {
userConfig.customScripts.clear(); userConfig.customScripts.clear();
QList<QString> paths = value.split(","); QList<QString> paths = value.split(",", Qt::SkipEmptyParts);
paths.removeDuplicates(); paths.removeDuplicates();
for (QString script : paths) { for (QString script : paths) {
if (!script.isEmpty()) { if (!script.isEmpty()) {
@ -1077,7 +1077,7 @@ void UserConfig::parseConfigKeyValue(QString key, QString value) {
this->useEncounterJson = getConfigBool(key, value); this->useEncounterJson = getConfigBool(key, value);
} else if (key == "custom_scripts") { } else if (key == "custom_scripts") {
this->customScripts.clear(); this->customScripts.clear();
QList<QString> paths = value.split(","); QList<QString> paths = value.split(",", Qt::SkipEmptyParts);
paths.removeDuplicates(); paths.removeDuplicates();
for (QString script : paths) { for (QString script : paths) {
if (!script.isEmpty()) { if (!script.isEmpty()) {
@ -1133,12 +1133,12 @@ bool UserConfig::getEncounterJsonActive() {
return this->useEncounterJson; return this->useEncounterJson;
} }
void UserConfig::setCustomScripts(QList<QString> scripts) { void UserConfig::setCustomScripts(QStringList scripts) {
this->customScripts = scripts; this->customScripts = scripts;
this->save(); this->save();
} }
QList<QString> UserConfig::getCustomScripts() { QStringList UserConfig::getCustomScripts() {
return this->customScripts; return this->customScripts;
} }

View file

@ -2146,7 +2146,7 @@ void Editor::openScript(const QString &scriptLabel) const {
openInTextEditor(scriptPath, lineNum); openInTextEditor(scriptPath, lineNum);
} }
void Editor::openInTextEditor(const QString &path, int lineNum) const { void Editor::openInTextEditor(const QString &path, int lineNum) {
QString command = porymapConfig.getTextEditorGotoLine(); QString command = porymapConfig.getTextEditorGotoLine();
if (command.isEmpty()) { if (command.isEmpty()) {
// Open map scripts in the system's default editor. // Open map scripts in the system's default editor.
@ -2159,7 +2159,7 @@ void Editor::openInTextEditor(const QString &path, int lineNum) const {
} else { } else {
command += " \"" + path + '\"'; command += " \"" + path + '\"';
} }
startDetachedProcess(command); Editor::startDetachedProcess(command);
} }
} }
@ -2172,7 +2172,7 @@ void Editor::openProjectInTextEditor() const {
startDetachedProcess(command); 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); logInfo("Executing command: " + command);
QProcess process; QProcess process;
#ifdef Q_OS_WIN #ifdef Q_OS_WIN

View file

@ -590,6 +590,19 @@ QString MainWindow::getDefaultMap() {
return QString(); 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) { QString MainWindow::getExistingDirectory(QString dir) {
return QFileDialog::getExistingDirectory(this, "Open Directory", dir, QFileDialog::ShowDirsOnly); return QFileDialog::getExistingDirectory(this, "Open Directory", dir, QFileDialog::ShowDirsOnly);
} }
@ -1184,12 +1197,9 @@ void MainWindow::openNewMapPopupWindow() {
if (!this->newMapPrompt) { if (!this->newMapPrompt) {
this->newMapPrompt = new NewMapPopup(this, this->editor->project); this->newMapPrompt = new NewMapPopup(this, this->editor->project);
} }
if (!this->newMapPrompt->isVisible()) {
this->newMapPrompt->show(); openSubWindow(this->newMapPrompt);
} else {
this->newMapPrompt->raise();
this->newMapPrompt->activateWindow();
}
connect(this->newMapPrompt, &NewMapPopup::applied, this, &MainWindow::onNewMapCreated); connect(this->newMapPrompt, &NewMapPopup::applied, this, &MainWindow::onNewMapCreated);
this->newMapPrompt->setAttribute(Qt::WA_DeleteOnClose); this->newMapPrompt->setAttribute(Qt::WA_DeleteOnClose);
} }
@ -1775,14 +1785,7 @@ void MainWindow::on_actionEdit_Shortcuts_triggered()
if (!shortcutsEditor) if (!shortcutsEditor)
initShortcutsEditor(); initShortcutsEditor();
if (shortcutsEditor->isHidden()) { openSubWindow(shortcutsEditor);
shortcutsEditor->show();
} else if (shortcutsEditor->isMinimized()) {
shortcutsEditor->showNormal();
} else {
shortcutsEditor->raise();
shortcutsEditor->activateWindow();
}
} }
void MainWindow::initShortcutsEditor() { void MainWindow::initShortcutsEditor() {
@ -2461,13 +2464,7 @@ void MainWindow::showExportMapImageWindow(ImageExporterMode mode) {
this->mapImageExporter = new MapImageExporter(this, this->editor, mode); this->mapImageExporter = new MapImageExporter(this, this->editor, mode);
this->mapImageExporter->setAttribute(Qt::WA_DeleteOnClose); this->mapImageExporter->setAttribute(Qt::WA_DeleteOnClose);
if (!this->mapImageExporter->isVisible()) { openSubWindow(this->mapImageExporter);
this->mapImageExporter->show();
} else if (this->mapImageExporter->isMinimized()) {
this->mapImageExporter->showNormal();
} else {
this->mapImageExporter->activateWindow();
}
} }
void MainWindow::on_comboBox_ConnectionDirection_currentTextChanged(const QString &direction) void MainWindow::on_comboBox_ConnectionDirection_currentTextChanged(const QString &direction)
@ -2663,14 +2660,7 @@ void MainWindow::on_actionTileset_Editor_triggered()
initTilesetEditor(); initTilesetEditor();
} }
if (!this->tilesetEditor->isVisible()) { openSubWindow(this->tilesetEditor);
this->tilesetEditor->show();
} else if (this->tilesetEditor->isMinimized()) {
this->tilesetEditor->showNormal();
} else {
this->tilesetEditor->raise();
this->tilesetEditor->activateWindow();
}
MetatileSelection selection = this->editor->metatile_selector_item->getMetatileSelection(); MetatileSelection selection = this->editor->metatile_selector_item->getMetatileSelection();
this->tilesetEditor->selectMetatile(selection.metatileItems.first().metatileId); this->tilesetEditor->selectMetatile(selection.metatileItems.first().metatileId);
@ -2723,14 +2713,7 @@ void MainWindow::on_actionEdit_Preferences_triggered() {
this, &MainWindow::togglePreferenceSpecificUi); this, &MainWindow::togglePreferenceSpecificUi);
} }
if (!preferenceEditor->isVisible()) { openSubWindow(preferenceEditor);
preferenceEditor->show();
} else if (preferenceEditor->isMinimized()) {
preferenceEditor->showNormal();
} else {
preferenceEditor->raise();
preferenceEditor->activateWindow();
}
} }
void MainWindow::togglePreferenceSpecificUi() { void MainWindow::togglePreferenceSpecificUi() {
@ -2747,14 +2730,15 @@ void MainWindow::on_actionEdit_Project_Settings_triggered() {
this, &MainWindow::on_action_Reload_Project_triggered); this, &MainWindow::on_action_Reload_Project_triggered);
} }
if (!this->projectSettingsEditor->isVisible()) { openSubWindow(this->projectSettingsEditor);
this->projectSettingsEditor->show(); }
} else if (this->projectSettingsEditor->isMinimized()) {
this->projectSettingsEditor->showNormal(); void MainWindow::on_actionCustom_Scripts_triggered() {
} else { if (!this->customScriptsEditor) {
this->projectSettingsEditor->raise(); this->customScriptsEditor = new CustomScriptsEditor(this);
this->projectSettingsEditor->activateWindow();
} }
openSubWindow(this->customScriptsEditor);
} }
void MainWindow::on_pushButton_AddCustomHeaderField_clicked() void MainWindow::on_pushButton_AddCustomHeaderField_clicked()
@ -2806,14 +2790,7 @@ void MainWindow::on_actionRegion_Map_Editor_triggered() {
} }
} }
if (!this->regionMapEditor->isVisible()) { openSubWindow(this->regionMapEditor);
this->regionMapEditor->show();
} else if (this->regionMapEditor->isMinimized()) {
this->regionMapEditor->showNormal();
} else {
this->regionMapEditor->raise();
this->regionMapEditor->activateWindow();
}
} }
void MainWindow::on_pushButton_CreatePrefab_clicked() { void MainWindow::on_pushButton_CreatePrefab_clicked() {

View file

@ -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 <QDir>
#include <QFileDialog>
// 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<QAction *>())
if (!action->objectName().isEmpty())
shortcutable_objects.append(qobject_cast<QObject *>(action));
for (auto *shortcut : findChildren<Shortcut *>())
if (!shortcut->objectName().isEmpty())
shortcutable_objects.append(qobject_cast<QObject *>(shortcut));
return shortcutable_objects;
}
// TODO: Connect to shorcuts editor
void CustomScriptsEditor::applyUserShortcuts() {
for (auto *action : findChildren<QAction *>())
if (!action->objectName().isEmpty())
action->setShortcuts(shortcutsConfig.userShortcuts(action));
for (auto *shortcut : findChildren<Shortcut *>())
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<CustomScriptsListItem *>(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<CustomScriptsListItem *>(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<QListWidgetItem *> 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()
);
*/
}

View file

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

View file

@ -8,7 +8,7 @@
#include <QFormLayout> #include <QFormLayout>
/* /*
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) : ProjectSettingsEditor::ProjectSettingsEditor(QWidget *parent, Project *project) :
@ -32,31 +32,20 @@ ProjectSettingsEditor::~ProjectSettingsEditor()
// TODO: Move tool tips to editable areas // TODO: Move tool tips to editable areas
void ProjectSettingsEditor::connectSignals() { void ProjectSettingsEditor::connectSignals() {
// Connect buttons
connect(ui->buttonBox, &QDialogButtonBox::clicked, this, &ProjectSettingsEditor::dialogButtonClicked); connect(ui->buttonBox, &QDialogButtonBox::clicked, this, &ProjectSettingsEditor::dialogButtonClicked);
connect(ui->button_ChoosePrefabs, &QAbstractButton::clicked, this, &ProjectSettingsEditor::choosePrefabsFileClicked); connect(ui->button_ChoosePrefabs, &QAbstractButton::clicked, this, &ProjectSettingsEditor::choosePrefabsFileClicked);
connect(ui->button_ImportDefaultPrefabs, &QAbstractButton::clicked, this, &ProjectSettingsEditor::importDefaultPrefabsClicked); connect(ui->button_ImportDefaultPrefabs, &QAbstractButton::clicked, this, &ProjectSettingsEditor::importDefaultPrefabsClicked);
// Connect combo boxes
QList<NoScrollComboBox *> combos = ui->centralwidget->findChildren<NoScrollComboBox *>();
foreach(auto i, combos)
connect(i, &QComboBox::currentTextChanged, this, &ProjectSettingsEditor::markEdited);
connect(ui->comboBox_BaseGameVersion, &QComboBox::currentTextChanged, this, &ProjectSettingsEditor::promptRestoreDefaults); connect(ui->comboBox_BaseGameVersion, &QComboBox::currentTextChanged, this, &ProjectSettingsEditor::promptRestoreDefaults);
// Connect check boxes // Record that there are unsaved changes if any of the settings are modified
QList<QCheckBox *> checkboxes = ui->centralwidget->findChildren<QCheckBox *>(); for (auto combo : ui->centralwidget->findChildren<NoScrollComboBox *>())
foreach(auto i, checkboxes) connect(combo, &QComboBox::currentTextChanged, this, &ProjectSettingsEditor::markEdited);
connect(i, &QCheckBox::stateChanged, this, &ProjectSettingsEditor::markEdited); for (auto checkBox : ui->centralwidget->findChildren<QCheckBox *>())
connect(checkBox, &QCheckBox::stateChanged, this, &ProjectSettingsEditor::markEdited);
// Connect spin boxes for (auto spinBox : ui->centralwidget->findChildren<QSpinBox *>())
QList<QSpinBox *> spinBoxes = ui->centralwidget->findChildren<QSpinBox *>(); connect(spinBox, QOverload<int>::of(&QSpinBox::valueChanged), [this](int) { this->markEdited(); });
foreach(auto i, spinBoxes) for (auto lineEdit : ui->centralwidget->findChildren<QLineEdit *>())
connect(i, QOverload<int>::of(&QSpinBox::valueChanged), [this](int) { this->markEdited(); }); connect(lineEdit, &QLineEdit::textEdited, this, &ProjectSettingsEditor::markEdited);
// Connect line edits
QList<QLineEdit *> lineEdits = ui->centralwidget->findChildren<QLineEdit *>();
foreach(auto i, lineEdits)
connect(i, &QLineEdit::textEdited, this, &ProjectSettingsEditor::markEdited);
} }
void ProjectSettingsEditor::markEdited() { void ProjectSettingsEditor::markEdited() {
@ -143,7 +132,6 @@ void ProjectSettingsEditor::save() {
// Prevent a call to save() for each of the config settings // Prevent a call to save() for each of the config settings
projectConfig.setSaveDisabled(true); projectConfig.setSaveDisabled(true);
userConfig.setSaveDisabled(true);
projectConfig.setDefaultPrimaryTileset(ui->comboBox_DefaultPrimaryTileset->currentText()); projectConfig.setDefaultPrimaryTileset(ui->comboBox_DefaultPrimaryTileset->currentText());
projectConfig.setDefaultSecondaryTileset(ui->comboBox_DefaultSecondaryTileset->currentText()); projectConfig.setDefaultSecondaryTileset(ui->comboBox_DefaultSecondaryTileset->currentText());
@ -183,8 +171,6 @@ void ProjectSettingsEditor::save() {
projectConfig.setSaveDisabled(false); projectConfig.setSaveDisabled(false);
projectConfig.save(); projectConfig.save();
userConfig.setSaveDisabled(false);
userConfig.save();
this->hasUnsavedChanges = false; this->hasUnsavedChanges = false;
// Technically, a reload is not required for several of the config settings. // Technically, a reload is not required for several of the config settings.
@ -192,6 +178,7 @@ void ProjectSettingsEditor::save() {
this->projectNeedsReload = true; this->projectNeedsReload = true;
} }
// TODO: If the selected file is in the project directory use a relative path
void ProjectSettingsEditor::choosePrefabsFileClicked(bool) { void ProjectSettingsEditor::choosePrefabsFileClicked(bool) {
QString startPath = this->project->importExportPath; QString startPath = this->project->importExportPath;
QFileInfo fileInfo(ui->lineEdit_PrefabsPath->text()); 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. // Restore defaults by resetting config in memory, refreshing the UI, then restoring the config.
// Don't want to save changes until user accepts them. // Don't want to save changes until user accepts them.
ProjectConfig tempProject = projectConfig; ProjectConfig tempProject = projectConfig;
UserConfig tempUser = userConfig;
projectConfig.reset(projectConfig.stringToBaseGameVersion(versionText)); projectConfig.reset(projectConfig.stringToBaseGameVersion(versionText));
userConfig.reset();
this->refresh(); this->refresh();
projectConfig = tempProject; projectConfig = tempProject;
userConfig = tempUser;
this->hasUnsavedChanges = true; this->hasUnsavedChanges = true;
return true; return true;