From 16536eb940458285ef9f13f6a25f624559ce3cb1 Mon Sep 17 00:00:00 2001 From: GriffinR Date: Fri, 27 Sep 2024 11:31:55 -0400 Subject: [PATCH] Add grid settings window --- forms/gridsettingsdialog.ui | 144 ++++++++++++++++++++++++++++++++ forms/mainwindow.ui | 28 ++++++- include/editor.h | 7 +- include/mainwindow.h | 4 + include/ui/gridsettingsdialog.h | 60 +++++++++++++ porymap.pro | 3 + src/editor.cpp | 59 ++++++++----- src/mainwindow.cpp | 50 ++++++++--- src/ui/graphicsview.cpp | 8 +- src/ui/gridsettingsdialog.cpp | 100 ++++++++++++++++++++++ 10 files changed, 419 insertions(+), 44 deletions(-) create mode 100644 forms/gridsettingsdialog.ui create mode 100644 include/ui/gridsettingsdialog.h create mode 100644 src/ui/gridsettingsdialog.cpp diff --git a/forms/gridsettingsdialog.ui b/forms/gridsettingsdialog.ui new file mode 100644 index 00000000..855b098b --- /dev/null +++ b/forms/gridsettingsdialog.ui @@ -0,0 +1,144 @@ + + + GridSettingsDialog + + + + 0 + 0 + 416 + 350 + + + + Grid Settings + + + + + + true + + + + + 0 + 0 + 390 + 284 + + + + + + + Offset (in metatiles) + + + + + + X + + + + + + + Y + + + + + + + + + + + + + + + + + 0 + 0 + + + + Style + + + + + + + Dimensions (in metatiles) + + + + + + Width + + + + + + + Height + + + + + + + 1 + + + + + + + 1 + + + + + + + + + + + + + + 0 + 0 + + + + Color + + + + + + + + + + + Qt::Orientation::Horizontal + + + QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok|QDialogButtonBox::StandardButton::RestoreDefaults + + + + + + + diff --git a/forms/mainwindow.ui b/forms/mainwindow.ui index d4538dcb..60dc95e7 100644 --- a/forms/mainwindow.ui +++ b/forms/mainwindow.ui @@ -3077,10 +3077,13 @@ + + + @@ -3312,7 +3315,7 @@ true - Player View Rectangle + Show Player View Rectangle <html><head/><body><p>Show the player's view rectangle on the map based on the cursor's position.</p></body></html> @@ -3329,7 +3332,7 @@ true - Cursor Tile Outline + Show Cursor Tile Outline C @@ -3428,7 +3431,26 @@ true - Dive/Emerge Map + Show Dive/Emerge Map + + + + + true + + + true + + + Show Grid + + + Ctrl+G + + + + + Grid Settings... diff --git a/include/editor.h b/include/editor.h index 7ef5ba10..9e8c7d28 100644 --- a/include/editor.h +++ b/include/editor.h @@ -23,6 +23,7 @@ #include "collisionpixmapitem.h" #include "mappixmapitem.h" #include "settings.h" +#include "gridsettingsdialog.h" #include "movablerect.h" #include "cursortilerect.h" #include "mapruler.h" @@ -48,6 +49,7 @@ public: QPointer project = nullptr; Map *map = nullptr; Settings *settings; + GridSettings gridSettings; void setProject(Project * project); void saveProject(); void save(); @@ -118,7 +120,7 @@ public: QPointer collision_item = nullptr; QGraphicsItemGroup *events_group = nullptr; QList borderItems; - QList gridLines; + QGraphicsItemGroup *mapGrid = nullptr; MovableRect *playerViewRect = nullptr; CursorTileRect *cursorMapTileRect = nullptr; MapRuler *map_ruler = nullptr; @@ -165,6 +167,7 @@ public slots: void maskNonVisibleConnectionTiles(); void onBorderMetatilesChanged(); void selectedEventIndexChanged(int index, Event::Group eventGroup); + void toggleGrid(bool); private: const QImage defaultCollisionImgSheet = QImage(":/images/collisions.png"); @@ -219,7 +222,6 @@ private slots: void onHoveredMapMovementPermissionCleared(); void onSelectedMetatilesChanged(); void onWheelZoom(int); - void onToggleGridClicked(bool); signals: void objectsChanged(); @@ -231,6 +233,7 @@ signals: void currentMetatilesSelectionChanged(); void mapRulerStatusChanged(const QString &); void tilesetUpdated(QString); + void gridToggled(bool); }; #endif // EDITOR_H diff --git a/include/mainwindow.h b/include/mainwindow.h index c2726efc..3f4625db 100644 --- a/include/mainwindow.h +++ b/include/mainwindow.h @@ -26,6 +26,7 @@ #include "shortcutseditor.h" #include "preferenceeditor.h" #include "projectsettingseditor.h" +#include "gridsettingsdialog.h" #include "customscriptseditor.h" #include "wildmonchart.h" #include "updatepromoter.h" @@ -302,6 +303,8 @@ private slots: void on_actionProject_Settings_triggered(); void on_actionCustom_Scripts_triggered(); void reloadScriptEngine(); + void on_actionShow_Grid_triggered(); + void on_actionGrid_Settings_triggered(); public: Ui::MainWindow *ui; @@ -316,6 +319,7 @@ private: QPointer newMapPrompt = nullptr; QPointer preferenceEditor = nullptr; QPointer projectSettingsEditor = nullptr; + QPointer gridSettingsDialog = nullptr; QPointer customScriptsEditor = nullptr; QPointer updatePromoter = nullptr; QPointer networkAccessManager = nullptr; diff --git a/include/ui/gridsettingsdialog.h b/include/ui/gridsettingsdialog.h new file mode 100644 index 00000000..806608a0 --- /dev/null +++ b/include/ui/gridsettingsdialog.h @@ -0,0 +1,60 @@ +#ifndef GRIDSETTINGSDIALOG_H +#define GRIDSETTINGSDIALOG_H + +#include +#include + +namespace Ui { +class GridSettingsDialog; +} + +struct GridSettings { + uint width = 16; + uint height = 16; + int offsetX = 0; + int offsetY = 0; + QString style; + QColor color; +}; + + +class GridSettingsDialog : public QDialog +{ + Q_OBJECT +public: + explicit GridSettingsDialog(GridSettings *settings = nullptr, QWidget *parent = nullptr); + ~GridSettingsDialog(); + +signals: + void changedGridSettings(); + +private: + Ui::GridSettingsDialog *ui; + GridSettings *settings; + GridSettings originalSettings; + + void reset(bool force = false); + +private slots: + void dialogButtonClicked(QAbstractButton *button); + void on_spinBox_Width_valueChanged(int value); + void on_spinBox_Height_valueChanged(int value); + void on_spinBox_X_valueChanged(int value); + void on_spinBox_Y_valueChanged(int value); + void on_comboBox_Style_currentTextChanged(QString style); +}; + +inline bool operator==(const struct GridSettings &a, const struct GridSettings &b) { + return a.width == b.width + && a.height == b.height + && a.offsetX == b.offsetX + && a.offsetY == b.offsetY + && a.style == b.style + && a.color == b.color; +} + +inline bool operator!=(const struct GridSettings &a, const struct GridSettings &b) { + return !(operator==(a, b)); +} + +#endif // GRIDSETTINGSDIALOG_H diff --git a/porymap.pro b/porymap.pro index f36f536f..ae49c4aa 100644 --- a/porymap.pro +++ b/porymap.pro @@ -60,6 +60,7 @@ SOURCES += src/core/block.cpp \ src/ui/collisionpixmapitem.cpp \ src/ui/connectionpixmapitem.cpp \ src/ui/currentselectedmetatilespixmapitem.cpp \ + src/ui/gridsettingsdialog.cpp \ src/ui/newmapconnectiondialog.cpp \ src/ui/overlay.cpp \ src/ui/prefab.cpp \ @@ -157,6 +158,7 @@ HEADERS += include/core/block.h \ include/ui/collisionpixmapitem.h \ include/ui/connectionpixmapitem.h \ include/ui/currentselectedmetatilespixmapitem.h \ + include/ui/gridsettingsdialog.h \ include/ui/newmapconnectiondialog.h \ include/ui/prefabframe.h \ include/ui/projectsettingseditor.h \ @@ -219,6 +221,7 @@ HEADERS += include/core/block.h \ FORMS += forms/mainwindow.ui \ forms/connectionslistitem.ui \ + forms/gridsettingsdialog.ui \ forms/newmapconnectiondialog.ui \ forms/prefabcreationdialog.ui \ forms/prefabframe.ui \ diff --git a/src/editor.cpp b/src/editor.cpp index ad228d77..dc15b059 100644 --- a/src/editor.cpp +++ b/src/editor.cpp @@ -47,6 +47,10 @@ Editor::Editor(Ui::MainWindow* ui) connect(ui->stackedWidget_WildMons, &QStackedWidget::currentChanged, [this] { emit wildMonTableOpened(getCurrentWildMonTable()); }); + + connect(ui->toolButton_Open_Scripts, &QToolButton::pressed, this, &Editor::openMapScripts); + connect(ui->actionOpen_Project_in_Text_Editor, &QAction::triggered, this, &Editor::openProjectInTextEditor); + connect(ui->checkBox_ToggleGrid, &QCheckBox::toggled, this, &Editor::toggleGrid); } Editor::~Editor() @@ -1842,40 +1846,49 @@ int Editor::getBorderDrawDistance(int dimension) { } } -void Editor::onToggleGridClicked(bool checked) { +void Editor::toggleGrid(bool checked) { + if (porymapConfig.showGrid == checked) + return; porymapConfig.showGrid = checked; + + // Synchronize action and checkbox + const QSignalBlocker b_Action(ui->actionShow_Grid); + const QSignalBlocker b_Checkbox(ui->checkBox_ToggleGrid); + ui->actionShow_Grid->setChecked(checked); + ui->checkBox_ToggleGrid->setChecked(checked); + + this->mapGrid->setVisible(checked); + if (ui->graphicsView_Map->scene()) ui->graphicsView_Map->scene()->update(); } void Editor::clearMapGrid() { - for (QGraphicsLineItem* item : gridLines) { - if (item) delete item; - } - gridLines.clear(); + delete this->mapGrid; + this->mapGrid = nullptr; } void Editor::displayMapGrid() { clearMapGrid(); - ui->checkBox_ToggleGrid->disconnect(); - int pixelWidth = map->getWidth() * 16; - int pixelHeight = map->getHeight() * 16; - for (int i = 0; i <= map->getWidth(); i++) { - int x = i * 16; - QGraphicsLineItem *line = new QGraphicsLineItem(x, 0, x, pixelHeight); - line->setVisible(ui->checkBox_ToggleGrid->isChecked()); - gridLines.append(line); - connect(ui->checkBox_ToggleGrid, &QCheckBox::toggled, [=](bool checked){line->setVisible(checked);}); - } - for (int j = 0; j <= map->getHeight(); j++) { - int y = j * 16; - QGraphicsLineItem *line = new QGraphicsLineItem(0, y, pixelWidth, y); - line->setVisible(ui->checkBox_ToggleGrid->isChecked()); - gridLines.append(line); - connect(ui->checkBox_ToggleGrid, &QCheckBox::toggled, [=](bool checked){line->setVisible(checked);}); - } - connect(ui->checkBox_ToggleGrid, &QCheckBox::toggled, this, &Editor::onToggleGridClicked); + // Note: The grid lines are not added to the scene. They need to be drawn on top of the overlay + // elements of the scripting API, so they're painted manually in MapView::drawForeground. + this->mapGrid = new QGraphicsItemGroup(); + + const uint pixelMapWidth = map->getWidth() * 16; + const uint pixelMapHeight = map->getHeight() * 16; + + // Create vertical lines + int offset = this->gridSettings.offsetX % this->gridSettings.width; + for (uint i = offset; i <= pixelMapWidth; i += this->gridSettings.width) + this->mapGrid->addToGroup(new QGraphicsLineItem(i, 0, i, pixelMapHeight)); + + // Create horizontal lines + offset = this->gridSettings.offsetY % this->gridSettings.height; + for (uint i = offset; i <= pixelMapHeight; i += this->gridSettings.height) + this->mapGrid->addToGroup(new QGraphicsLineItem(0, i, pixelMapWidth, i)); + + this->mapGrid->setVisible(porymapConfig.showGrid); } void Editor::updatePrimaryTileset(QString tilesetLabel, bool forceLoad) diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 588ce90f..1623d055 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -149,10 +149,6 @@ void MainWindow::initExtraShortcuts() { shortcutReset_Zoom->setObjectName("shortcutZoom_Reset"); shortcutReset_Zoom->setWhatsThis("Zoom Reset"); - auto *shortcutToggle_Grid = new Shortcut(QKeySequence("Ctrl+G"), ui->checkBox_ToggleGrid, SLOT(toggle())); - shortcutToggle_Grid->setObjectName("shortcutToggle_Grid"); - shortcutToggle_Grid->setWhatsThis("Toggle Grid"); - auto *shortcutDuplicate_Events = new Shortcut(QKeySequence("Ctrl+D"), this, SLOT(duplicate())); shortcutDuplicate_Events->setObjectName("shortcutDuplicate_Events"); shortcutDuplicate_Events->setWhatsThis("Duplicate Selected Event(s)"); @@ -317,8 +313,6 @@ void MainWindow::initEditor() { connect(this->editor, &Editor::wildMonTableEdited, [this] { this->markMapEdited(); }); connect(this->editor, &Editor::mapRulerStatusChanged, this, &MainWindow::onMapRulerStatusChanged); connect(this->editor, &Editor::tilesetUpdated, this, &Scripting::cb_TilesetUpdated); - connect(ui->toolButton_Open_Scripts, &QToolButton::pressed, this->editor, &Editor::openMapScripts); - connect(ui->actionOpen_Project_in_Text_Editor, &QAction::triggered, this->editor, &Editor::openProjectInTextEditor); this->loadUserSettings(); @@ -465,27 +459,45 @@ void MainWindow::applyMapListFilter(QString filterText) } void MainWindow::loadUserSettings() { - const QSignalBlocker blocker1(ui->horizontalSlider_CollisionTransparency); - const QSignalBlocker blocker2(ui->slider_DiveEmergeMapOpacity); - const QSignalBlocker blocker3(ui->slider_DiveMapOpacity); - const QSignalBlocker blocker4(ui->slider_EmergeMapOpacity); - const QSignalBlocker blocker5(ui->horizontalSlider_MetatileZoom); - const QSignalBlocker blocker6(ui->horizontalSlider_CollisionZoom); - + // Better Cursors ui->actionBetter_Cursors->setChecked(porymapConfig.prettyCursors); this->editor->settings->betterCursors = porymapConfig.prettyCursors; + + // Player view rectangle ui->actionPlayer_View_Rectangle->setChecked(porymapConfig.showPlayerView); this->editor->settings->playerViewRectEnabled = porymapConfig.showPlayerView; + + // Cursor tile outline ui->actionCursor_Tile_Outline->setChecked(porymapConfig.showCursorTile); this->editor->settings->cursorTileRectEnabled = porymapConfig.showCursorTile; + + // Border ui->checkBox_ToggleBorder->setChecked(porymapConfig.showBorder); + + // Grid + const QSignalBlocker b_Grid(ui->checkBox_ToggleGrid); + ui->actionShow_Grid->setChecked(porymapConfig.showGrid); ui->checkBox_ToggleGrid->setChecked(porymapConfig.showGrid); + + // Mirror connections ui->checkBox_MirrorConnections->setChecked(porymapConfig.mirrorConnectingMaps); + + // Collision opacity/transparency + const QSignalBlocker b_CollisionTransparency(ui->horizontalSlider_CollisionTransparency); this->editor->collisionOpacity = static_cast(porymapConfig.collisionOpacity) / 100; ui->horizontalSlider_CollisionTransparency->setValue(porymapConfig.collisionOpacity); + + // Dive map opacity/transparency + const QSignalBlocker b_DiveEmergeOpacity(ui->slider_DiveEmergeMapOpacity); + const QSignalBlocker b_DiveMapOpacity(ui->slider_DiveMapOpacity); + const QSignalBlocker b_EmergeMapOpacity(ui->slider_EmergeMapOpacity); ui->slider_DiveEmergeMapOpacity->setValue(porymapConfig.diveEmergeMapOpacity); ui->slider_DiveMapOpacity->setValue(porymapConfig.diveMapOpacity); ui->slider_EmergeMapOpacity->setValue(porymapConfig.emergeMapOpacity); + + // Zoom + const QSignalBlocker b_MetatileZoom(ui->horizontalSlider_MetatileZoom); + const QSignalBlocker b_CollisionZoom(ui->horizontalSlider_CollisionZoom); ui->horizontalSlider_MetatileZoom->setValue(porymapConfig.metatilesZoom); ui->horizontalSlider_CollisionZoom->setValue(porymapConfig.collisionZoom); @@ -1910,6 +1922,18 @@ void MainWindow::on_actionCursor_Tile_Outline_triggered() } } +void MainWindow::on_actionShow_Grid_triggered() { + this->editor->toggleGrid(ui->actionShow_Grid->isChecked()); +} + +void MainWindow::on_actionGrid_Settings_triggered() { + if (!this->gridSettingsDialog) { + this->gridSettingsDialog = new GridSettingsDialog(&this->editor->gridSettings, this); + connect(this->gridSettingsDialog, &GridSettingsDialog::changedGridSettings, this->editor, &Editor::displayMapGrid); + } + openSubWindow(this->gridSettingsDialog); +} + void MainWindow::on_actionShortcuts_triggered() { if (!shortcutsEditor) diff --git a/src/ui/graphicsview.cpp b/src/ui/graphicsview.cpp index 1c86e004..c6b78bba 100644 --- a/src/ui/graphicsview.cpp +++ b/src/ui/graphicsview.cpp @@ -31,9 +31,11 @@ void MapView::drawForeground(QPainter *painter, const QRectF&) { if (!editor) return; QStyleOptionGraphicsItem option; - for (QGraphicsLineItem* line : editor->gridLines) { - if (line && line->isVisible()) - line->paint(painter, &option, this); + if (editor->mapGrid) { + for (auto item : editor->mapGrid->childItems()) { + if (item->isVisible()) + item->paint(painter, &option, this); + } } if (editor->playerViewRect && editor->playerViewRect->isVisible()) editor->playerViewRect->paint(painter, &option, this); diff --git a/src/ui/gridsettingsdialog.cpp b/src/ui/gridsettingsdialog.cpp new file mode 100644 index 00000000..14a0d8b3 --- /dev/null +++ b/src/ui/gridsettingsdialog.cpp @@ -0,0 +1,100 @@ +#include "ui_gridsettingsdialog.h" +#include "gridsettingsdialog.h" + +// TODO: Add color picker +// TODO: Add styles +// TODO: Update units in UI +// TODO: Add linking chain button to width/height +// TODO: Add "snap to metatile" check box? +// TODO: Save settings in config +// TODO: Look into custom painting to improve performance +// TODO: Add tooltips + +GridSettingsDialog::GridSettingsDialog(GridSettings *settings, QWidget *parent) : + QDialog(parent), + ui(new Ui::GridSettingsDialog), + settings(settings) +{ + ui->setupUi(this); + setAttribute(Qt::WA_DeleteOnClose); + + // TODO: Populate comboBox_Style + + ui->spinBox_Width->setMaximum(INT_MAX); + ui->spinBox_Height->setMaximum(INT_MAX); + ui->spinBox_X->setMaximum(INT_MAX); + ui->spinBox_Y->setMaximum(INT_MAX); + + // Initialize UI values + if (!this->settings) + this->settings = new GridSettings; // TODO: Don't leak this + this->originalSettings = *this->settings; + reset(true); + + connect(ui->buttonBox, &QDialogButtonBox::clicked, this, &GridSettingsDialog::dialogButtonClicked); + + // TODO: Connect color picker + // connect(ui->, &, this, &GridSettingsDialog::changedGridSettings); +} + +void GridSettingsDialog::reset(bool force) { + if (!force && *this->settings == this->originalSettings) + return; + *this->settings = this->originalSettings; + + // Avoid sending changedGridSettings multiple times + const QSignalBlocker b_Width(ui->spinBox_Width); + const QSignalBlocker b_Height(ui->spinBox_Height); + const QSignalBlocker b_X(ui->spinBox_X); + const QSignalBlocker b_Y(ui->spinBox_Y); + + ui->spinBox_Width->setValue(this->settings->width); + ui->spinBox_Height->setValue(this->settings->height); + ui->spinBox_X->setValue(this->settings->offsetX); + ui->spinBox_Y->setValue(this->settings->offsetY); + // TODO: Initialize comboBox_Style with settings->style + // TODO: Initialize color with settings-color + + emit changedGridSettings(); +} + +void GridSettingsDialog::on_spinBox_Width_valueChanged(int value) { + this->settings->width = value; + emit changedGridSettings(); +} + +void GridSettingsDialog::on_spinBox_Height_valueChanged(int value) { + this->settings->height = value; + emit changedGridSettings(); +} + +void GridSettingsDialog::on_spinBox_X_valueChanged(int value) { + this->settings->offsetX = value; + emit changedGridSettings(); +} + +void GridSettingsDialog::on_spinBox_Y_valueChanged(int value) { + this->settings->offsetY = value; + emit changedGridSettings(); +} + +void GridSettingsDialog::on_comboBox_Style_currentTextChanged(QString text) { + this->settings->style = text; + emit changedGridSettings(); +} + +void GridSettingsDialog::dialogButtonClicked(QAbstractButton *button) { + auto role = ui->buttonBox->buttonRole(button); + if (role == QDialogButtonBox::AcceptRole) { + close(); + } else if (role == QDialogButtonBox::RejectRole) { + reset(); + close(); + } else if (role == QDialogButtonBox::ResetRole) { + reset(); + } +} + +GridSettingsDialog::~GridSettingsDialog() { + delete ui; +}