From d369806c944e3d9e4ca01aee09d30e02e1552de5 Mon Sep 17 00:00:00 2001 From: GriffinR Date: Wed, 25 Sep 2024 16:37:37 -0400 Subject: [PATCH 01/19] Fix height minimum for top bar on the Connections tab --- forms/mainwindow.ui | 6 ------ 1 file changed, 6 deletions(-) diff --git a/forms/mainwindow.ui b/forms/mainwindow.ui index f7253615..d4538dcb 100644 --- a/forms/mainwindow.ui +++ b/forms/mainwindow.ui @@ -2563,12 +2563,6 @@ 0 - - - 0 - 32 - - QFrame::StyledPanel From 16536eb940458285ef9f13f6a25f624559ce3cb1 Mon Sep 17 00:00:00 2001 From: GriffinR Date: Fri, 27 Sep 2024 11:31:55 -0400 Subject: [PATCH 02/19] 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; +} From 2cd4cb933436c32981a4641823197ae90f8dfedb Mon Sep 17 00:00:00 2001 From: GriffinR Date: Fri, 27 Sep 2024 22:19:52 -0400 Subject: [PATCH 03/19] Add styles to grid settings --- forms/gridsettingsdialog.ui | 78 ++++++++++++++++++++++++--------- include/editor.h | 1 - include/settings.h | 9 ++++ include/ui/gridsettingsdialog.h | 14 ++---- src/editor.cpp | 18 ++++++-- src/ui/gridsettingsdialog.cpp | 41 ++++++++++++----- 6 files changed, 113 insertions(+), 48 deletions(-) diff --git a/forms/gridsettingsdialog.ui b/forms/gridsettingsdialog.ui index 855b098b..c5aa299d 100644 --- a/forms/gridsettingsdialog.ui +++ b/forms/gridsettingsdialog.ui @@ -6,8 +6,8 @@ 0 0 - 416 - 350 + 423 + 375 @@ -24,15 +24,15 @@ 0 0 - 390 - 284 + 397 + 309 - Offset (in metatiles) + Offset (in pixels) @@ -50,14 +50,27 @@ - + - + + + + + + 0 + 0 + + + + Color + + + @@ -74,7 +87,7 @@ - Dimensions (in metatiles) + Dimensions (in pixels) @@ -92,14 +105,14 @@ - + 1 - + 1 @@ -109,21 +122,31 @@ - - - - - - - 0 - 0 - + + + false - - Color + + QComboBox::SizeAdjustPolicy::AdjustToContentsOnFirstShow + + + 0 + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + @@ -140,5 +163,18 @@ + + + NoScrollSpinBox + QSpinBox +
noscrollspinbox.h
+
+ + NoScrollComboBox + QComboBox +
noscrollcombobox.h
+
+
+ diff --git a/include/editor.h b/include/editor.h index 9e8c7d28..671c70d0 100644 --- a/include/editor.h +++ b/include/editor.h @@ -23,7 +23,6 @@ #include "collisionpixmapitem.h" #include "mappixmapitem.h" #include "settings.h" -#include "gridsettingsdialog.h" #include "movablerect.h" #include "cursortilerect.h" #include "mapruler.h" diff --git a/include/settings.h b/include/settings.h index d37283cd..0e0e8df5 100644 --- a/include/settings.h +++ b/include/settings.h @@ -4,6 +4,15 @@ #include +struct GridSettings { + uint width = 16; + uint height = 16; + int offsetX = 0; + int offsetY = 0; + Qt::PenStyle style = Qt::SolidLine; + QColor color = Qt::black; +}; + class Settings { public: diff --git a/include/ui/gridsettingsdialog.h b/include/ui/gridsettingsdialog.h index 806608a0..957edb43 100644 --- a/include/ui/gridsettingsdialog.h +++ b/include/ui/gridsettingsdialog.h @@ -4,20 +4,12 @@ #include #include +#include "settings.h" + 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 @@ -41,7 +33,7 @@ private slots: 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); + void on_comboBox_Style_currentIndexChanged(int index); }; inline bool operator==(const struct GridSettings &a, const struct GridSettings &b) { diff --git a/src/editor.cpp b/src/editor.cpp index dc15b059..99e18c3e 100644 --- a/src/editor.cpp +++ b/src/editor.cpp @@ -1878,15 +1878,25 @@ void Editor::displayMapGrid() { const uint pixelMapWidth = map->getWidth() * 16; const uint pixelMapHeight = map->getHeight() * 16; + QPen pen; + pen.setStyle(this->gridSettings.style); + pen.setColor(this->gridSettings.color); + // 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)); + for (uint i = offset; i <= pixelMapWidth; i += this->gridSettings.width) { + auto line = new QGraphicsLineItem(i, 0, i, pixelMapHeight); + line->setPen(pen); + this->mapGrid->addToGroup(line); + } // 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)); + for (uint i = offset; i <= pixelMapHeight; i += this->gridSettings.height) { + auto line = new QGraphicsLineItem(0, i, pixelMapWidth, i); + line->setPen(pen); + this->mapGrid->addToGroup(line); + } this->mapGrid->setVisible(porymapConfig.showGrid); } diff --git a/src/ui/gridsettingsdialog.cpp b/src/ui/gridsettingsdialog.cpp index 14a0d8b3..10934196 100644 --- a/src/ui/gridsettingsdialog.cpp +++ b/src/ui/gridsettingsdialog.cpp @@ -2,14 +2,19 @@ #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 +const QList> penStyleMap = { + {"Solid", Qt::SolidLine}, + {"Large Dashes", Qt::DashLine}, + {"Small Dashes", Qt::DotLine}, + {"Dots", Qt::CustomDashLine}, // TODO: Implement a custom pattern for this +}; + GridSettingsDialog::GridSettingsDialog(GridSettings *settings, QWidget *parent) : QDialog(parent), ui(new Ui::GridSettingsDialog), @@ -18,14 +23,16 @@ GridSettingsDialog::GridSettingsDialog(GridSettings *settings, QWidget *parent) ui->setupUi(this); setAttribute(Qt::WA_DeleteOnClose); - // TODO: Populate comboBox_Style + // Populate the styles combo box + for (const auto &pair : penStyleMap) + ui->comboBox_Style->addItem(pair.first, static_cast(pair.second)); 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 + // Initialize the settings if (!this->settings) this->settings = new GridSettings; // TODO: Don't leak this this->originalSettings = *this->settings; @@ -37,6 +44,10 @@ GridSettingsDialog::GridSettingsDialog(GridSettings *settings, QWidget *parent) // connect(ui->, &, this, &GridSettingsDialog::changedGridSettings); } +GridSettingsDialog::~GridSettingsDialog() { + delete ui; +} + void GridSettingsDialog::reset(bool force) { if (!force && *this->settings == this->originalSettings) return; @@ -47,12 +58,21 @@ void GridSettingsDialog::reset(bool force) { const QSignalBlocker b_Height(ui->spinBox_Height); const QSignalBlocker b_X(ui->spinBox_X); const QSignalBlocker b_Y(ui->spinBox_Y); + const QSignalBlocker b_Style(ui->comboBox_Style); 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: Debug + //ui->comboBox_Style->setCurrentIndex(ui->comboBox_Style->findData(static_cast(this->settings->style))); + for (const auto &pair : penStyleMap) { + if (pair.second == this->settings->style) { + ui->comboBox_Style->setCurrentText(pair.first); + break; + } + } // TODO: Initialize color with settings-color emit changedGridSettings(); @@ -78,8 +98,11 @@ void GridSettingsDialog::on_spinBox_Y_valueChanged(int value) { emit changedGridSettings(); } -void GridSettingsDialog::on_comboBox_Style_currentTextChanged(QString text) { - this->settings->style = text; +void GridSettingsDialog::on_comboBox_Style_currentIndexChanged(int index) { + if (index < 0 || index >= penStyleMap.length()) + return; + + this->settings->style = penStyleMap.at(index).second; emit changedGridSettings(); } @@ -94,7 +117,3 @@ void GridSettingsDialog::dialogButtonClicked(QAbstractButton *button) { reset(); } } - -GridSettingsDialog::~GridSettingsDialog() { - delete ui; -} From 274d95eef5dd9f3a3f02ae93786643359d256537 Mon Sep 17 00:00:00 2001 From: GriffinR Date: Sat, 28 Sep 2024 03:50:51 -0400 Subject: [PATCH 04/19] Add color input to grid settings --- forms/colorinputwidget.ui | 313 ++++++++++++++++++++++++++++++++ forms/gridsettingsdialog.ui | 117 ++++++------ include/ui/colorinputwidget.h | 45 +++++ include/ui/gridsettingsdialog.h | 1 + porymap.pro | 3 + src/ui/colorinputwidget.cpp | 216 ++++++++++++++++++++++ src/ui/gridsettingsdialog.cpp | 15 +- 7 files changed, 652 insertions(+), 58 deletions(-) create mode 100644 forms/colorinputwidget.ui create mode 100644 include/ui/colorinputwidget.h create mode 100644 src/ui/colorinputwidget.cpp diff --git a/forms/colorinputwidget.ui b/forms/colorinputwidget.ui new file mode 100644 index 00000000..eebd7955 --- /dev/null +++ b/forms/colorinputwidget.ui @@ -0,0 +1,313 @@ + + + ColorInputWidget + + + + 0 + 0 + 221 + 212 + + + + Color + + + + + + QFrame::Shape::NoFrame + + + QFrame::Shadow::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QFrame::Shape::NoFrame + + + QFrame::Shadow::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Red + + + + + + + Green + + + + + + + Blue + + + + + + + + + + QFrame::Shape::NoFrame + + + QFrame::Shadow::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 31 + + + 4 + + + Qt::Orientation::Horizontal + + + + + + + 31 + + + 4 + + + Qt::Orientation::Horizontal + + + + + + + 31 + + + 4 + + + Qt::Orientation::Horizontal + + + + + + + + + + QFrame::Shape::NoFrame + + + QFrame::Shadow::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 255 + + + 8 + + + + + + + 255 + + + 8 + + + + + + + 255 + + + 8 + + + + + + + + + + + + + + 32 + 32 + + + + + 32 + 32 + + + + QFrame::Shadow::Raised + + + + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + + + + + QFrame::Shape::NoFrame + + + QFrame::Shadow::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QFrame::Shape::NoFrame + + + QFrame::Shadow::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + # + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + + + + + + + + + + ... + + + + :/icons/pipette.ico:/icons/pipette.ico + + + + + + + + + + + + + diff --git a/forms/gridsettingsdialog.ui b/forms/gridsettingsdialog.ui index c5aa299d..73411742 100644 --- a/forms/gridsettingsdialog.ui +++ b/forms/gridsettingsdialog.ui @@ -6,8 +6,8 @@ 0 0 - 423 - 375 + 331 + 467 @@ -24,39 +24,23 @@ 0 0 - 397 - 309 + 305 + 401 - - - - Offset (in pixels) + + + + Qt::Orientation::Vertical - - - - - X - - - - - - - Y - - - - - - - - - - - + + + 20 + 40 + + + @@ -71,6 +55,19 @@ + + + + false + + + QComboBox::SizeAdjustPolicy::AdjustToContentsOnFirstShow + + + 0 + + + @@ -121,31 +118,41 @@ - - - - false - - - QComboBox::SizeAdjustPolicy::AdjustToContentsOnFirstShow - - - 0 + + + + Offset (in pixels) + + + + + X + + + + + + + Y + + + + + + + + + + - - - - Qt::Orientation::Vertical + + + + - - - 20 - 40 - - - + @@ -174,6 +181,12 @@ QComboBox
noscrollcombobox.h
+ + ColorInputWidget + QGroupBox +
colorinputwidget.h
+ 1 +
diff --git a/include/ui/colorinputwidget.h b/include/ui/colorinputwidget.h new file mode 100644 index 00000000..5b414776 --- /dev/null +++ b/include/ui/colorinputwidget.h @@ -0,0 +1,45 @@ +#ifndef COLORINPUTWIDGET_H +#define COLORINPUTWIDGET_H + +#include +#include + +namespace Ui { +class ColorInputWidget; +} + + +class ColorInputWidget : public QGroupBox { + Q_OBJECT +public: + explicit ColorInputWidget(QWidget *parent = nullptr); + explicit ColorInputWidget(const QString &title, QWidget *parent = nullptr); + ~ColorInputWidget(); + + void setColor(QRgb color); + QRgb color() const { return m_color; } + + bool setBitDepth(int bits); + int bitDepth() const { return m_bitDepth; } + +signals: + void colorChanged(QRgb color); + void bitDepthChanged(int bits); + +private: + Ui::ColorInputWidget *ui; + + QRgb m_color = 0; + int m_bitDepth = 0; + + void init(); + void updateColorUi(); + void pickColor(); + void blockEditSignals(bool block); + + void setRgbFromSliders(); + void setRgbFromSpinners(); + void setRgbFromHexString(const QString &); +}; + +#endif // COLORINPUTWIDGET_H diff --git a/include/ui/gridsettingsdialog.h b/include/ui/gridsettingsdialog.h index 957edb43..9c64f29d 100644 --- a/include/ui/gridsettingsdialog.h +++ b/include/ui/gridsettingsdialog.h @@ -34,6 +34,7 @@ private slots: void on_spinBox_X_valueChanged(int value); void on_spinBox_Y_valueChanged(int value); void on_comboBox_Style_currentIndexChanged(int index); + void onColorChanged(QRgb color); }; inline bool operator==(const struct GridSettings &a, const struct GridSettings &b) { diff --git a/porymap.pro b/porymap.pro index ae49c4aa..182ea11a 100644 --- a/porymap.pro +++ b/porymap.pro @@ -51,6 +51,7 @@ SOURCES += src/core/block.cpp \ src/scriptapi/apiutility.cpp \ src/scriptapi/scripting.cpp \ src/ui/aboutporymap.cpp \ + src/ui/colorinputwidget.cpp \ src/ui/connectionslistitem.cpp \ src/ui/customscriptseditor.cpp \ src/ui/customscriptslistitem.cpp \ @@ -175,6 +176,7 @@ HEADERS += include/core/block.h \ include/ui/prefabcreationdialog.h \ include/ui/regionmappixmapitem.h \ include/ui/citymappixmapitem.h \ + include/ui/colorinputwidget.h \ include/ui/mapsceneeventfilter.h \ include/ui/metatilelayersitem.h \ include/ui/metatileselector.h \ @@ -220,6 +222,7 @@ HEADERS += include/core/block.h \ include/ui/wildmonchart.h FORMS += forms/mainwindow.ui \ + forms/colorinputwidget.ui \ forms/connectionslistitem.ui \ forms/gridsettingsdialog.ui \ forms/newmapconnectiondialog.ui \ diff --git a/src/ui/colorinputwidget.cpp b/src/ui/colorinputwidget.cpp new file mode 100644 index 00000000..200567af --- /dev/null +++ b/src/ui/colorinputwidget.cpp @@ -0,0 +1,216 @@ +#include "colorinputwidget.h" +#include "ui_colorinputwidget.h" +#include "colorpicker.h" + +#include + +// TODO: Refactor palette editor to make use of this class + +class HexCodeValidator : public QValidator { + virtual QValidator::State validate(QString &input, int &) const override { + input = input.toUpper(); + return QValidator::Acceptable; + } +}; + +static inline int rgb5(int rgb) { return round(static_cast(rgb * 31) / 255.0); } +static inline int rgb8(int rgb) { return round(rgb * 255. / 31.); } +static inline int gbaRed(int rgb) { return rgb & 0x1f; } +static inline int gbaGreen(int rgb) { return (rgb >> 5) & 0x1f; } +static inline int gbaBlue(int rgb) { return (rgb >> 10) & 0x1f; } + +ColorInputWidget::ColorInputWidget(QWidget *parent) : + QGroupBox(parent), + ui(new Ui::ColorInputWidget) +{ + init(); +} + +ColorInputWidget::ColorInputWidget(const QString &title, QWidget *parent) : + QGroupBox(title, parent), + ui(new Ui::ColorInputWidget) +{ + init(); +} + +void ColorInputWidget::init() { + ui->setupUi(this); + + connect(ui->slider_Red, &QSlider::valueChanged, this, &ColorInputWidget::setRgbFromSliders); + connect(ui->slider_Green, &QSlider::valueChanged, this, &ColorInputWidget::setRgbFromSliders); + connect(ui->slider_Blue, &QSlider::valueChanged, this, &ColorInputWidget::setRgbFromSliders); + + connect(ui->spinBox_Red, QOverload::of(&QSpinBox::valueChanged), this, &ColorInputWidget::setRgbFromSpinners); + connect(ui->spinBox_Green, QOverload::of(&QSpinBox::valueChanged), this, &ColorInputWidget::setRgbFromSpinners); + connect(ui->spinBox_Blue, QOverload::of(&QSpinBox::valueChanged), this, &ColorInputWidget::setRgbFromSpinners); + + static const HexCodeValidator hexValidator; + ui->lineEdit_Hex->setValidator(&hexValidator); + connect(ui->lineEdit_Hex, &QLineEdit::textEdited, this, &ColorInputWidget::setRgbFromHexString); + + connect(ui->button_Eyedrop, &QToolButton::clicked, this, &ColorInputWidget::pickColor); + + setBitDepth(24); +} + +ColorInputWidget::~ColorInputWidget() { + delete ui; +} + +void ColorInputWidget::updateColorUi() { + blockEditSignals(true); + + int red = qRed(m_color); + int green = qGreen(m_color); + int blue = qBlue(m_color); + + if (m_bitDepth == 15) { + // Sliders + ui->slider_Red->setValue(rgb5(red)); + ui->slider_Green->setValue(rgb5(green)); + ui->slider_Blue->setValue(rgb5(blue)); + + // Hex + int hex15 = (rgb5(blue) << 10) | (rgb5(green) << 5) | rgb5(red); + ui->lineEdit_Hex->setText(QString("%1").arg(hex15, 4, 16, QLatin1Char('0')).toUpper()); + + // Spinners + ui->spinBox_Red->setValue(rgb5(red)); + ui->spinBox_Green->setValue(rgb5(green)); + ui->spinBox_Blue->setValue(rgb5(blue)); + } else { + // Sliders + ui->slider_Red->setValue(red); + ui->slider_Green->setValue(green); + ui->slider_Blue->setValue(blue); + + // Hex + QColor color(red, green, blue); + ui->lineEdit_Hex->setText(color.name().remove(0, 1).toUpper()); + + // Spinners + ui->spinBox_Red->setValue(red); + ui->spinBox_Green->setValue(green); + ui->spinBox_Blue->setValue(blue); + } + + ui->frame_ColorDisplay->setStyleSheet(QString("background-color: rgb(%1, %2, %3);").arg(red).arg(green).arg(blue)); + + blockEditSignals(false); +} + +void ColorInputWidget::blockEditSignals(bool block) { + ui->slider_Red->blockSignals(block); + ui->slider_Green->blockSignals(block); + ui->slider_Blue->blockSignals(block); + + ui->spinBox_Red->blockSignals(block); + ui->spinBox_Green->blockSignals(block); + ui->spinBox_Blue->blockSignals(block); + + ui->lineEdit_Hex->blockSignals(block); +} + +bool ColorInputWidget::setBitDepth(int bits) { + if (m_bitDepth == bits) + return true; + + int singleStep, pageStep, maximum; + QString hexInputMask; + if (bits == 15) { + singleStep = 1; + pageStep = 4; + maximum = 31; + hexInputMask = "HHHH"; + } else if (bits == 24) { + singleStep = 8; + pageStep = 24; + maximum = 255; + hexInputMask = "HHHHHH"; + } else { + // Unsupported bit depth + return false; + } + m_bitDepth = bits; + + blockEditSignals(true); + ui->slider_Red->setSingleStep(singleStep); + ui->slider_Green->setSingleStep(singleStep); + ui->slider_Blue->setSingleStep(singleStep); + ui->slider_Red->setPageStep(pageStep); + ui->slider_Green->setPageStep(pageStep); + ui->slider_Blue->setPageStep(pageStep); + ui->slider_Red->setMaximum(maximum); + ui->slider_Green->setMaximum(maximum); + ui->slider_Blue->setMaximum(maximum); + + ui->spinBox_Red->setSingleStep(singleStep); + ui->spinBox_Green->setSingleStep(singleStep); + ui->spinBox_Blue->setSingleStep(singleStep); + ui->spinBox_Red->setMaximum(maximum); + ui->spinBox_Green->setMaximum(maximum); + ui->spinBox_Blue->setMaximum(maximum); + + ui->lineEdit_Hex->setInputMask(hexInputMask); + ui->lineEdit_Hex->setMaxLength(hexInputMask.length()); + + updateColorUi(); + blockEditSignals(false); + emit bitDepthChanged(m_bitDepth); + return true; +} + +void ColorInputWidget::setColor(QRgb rgb) { + if (m_color == rgb) + return; + m_color = rgb; + updateColorUi(); + emit colorChanged(m_color); +} + +void ColorInputWidget::setRgbFromSliders() { + if (m_bitDepth == 15) { + setColor(qRgb(rgb8(ui->slider_Red->value()), + rgb8(ui->slider_Green->value()), + rgb8(ui->slider_Blue->value()))); + } else { + setColor(qRgb(ui->slider_Red->value(), + ui->slider_Green->value(), + ui->slider_Blue->value())); + } +} + +void ColorInputWidget::setRgbFromSpinners() { + if (m_bitDepth == 15) { + setColor(qRgb(rgb8(ui->spinBox_Red->value()), rgb8(ui->spinBox_Green->value()), rgb8(ui->spinBox_Blue->value()))); + } else { + setColor(qRgb(ui->spinBox_Red->value(), ui->spinBox_Green->value(), ui->spinBox_Blue->value())); + } +} + +void ColorInputWidget::setRgbFromHexString(const QString &text) { + if ((m_bitDepth == 24 && text.length() != 6) + || (m_bitDepth == 15 && text.length() != 4)) + return; + + bool ok = false; + int rgb = text.toInt(&ok, 16); + if (!ok) rgb = 0xFFFFFFFF; + + if (m_bitDepth == 15) { + int rc = gbaRed(rgb); + int gc = gbaGreen(rgb); + int bc = gbaBlue(rgb); + setColor(qRgb(rgb8(rc), rgb8(gc), rgb8(bc))); + } else { + setColor(qRgb(qRed(rgb), qGreen(rgb), qBlue(rgb))); + } +} + +void ColorInputWidget::pickColor() { + ColorPicker picker(this); + if (picker.exec() == QDialog::Accepted) { + QColor c = picker.getColor(); + setColor(c.rgb()); + } +} diff --git a/src/ui/gridsettingsdialog.cpp b/src/ui/gridsettingsdialog.cpp index 10934196..bfc814bc 100644 --- a/src/ui/gridsettingsdialog.cpp +++ b/src/ui/gridsettingsdialog.cpp @@ -1,7 +1,6 @@ #include "ui_gridsettingsdialog.h" #include "gridsettingsdialog.h" -// TODO: Add color picker // TODO: Add linking chain button to width/height // TODO: Add "snap to metatile" check box? // TODO: Save settings in config @@ -39,9 +38,7 @@ GridSettingsDialog::GridSettingsDialog(GridSettings *settings, QWidget *parent) reset(true); connect(ui->buttonBox, &QDialogButtonBox::clicked, this, &GridSettingsDialog::dialogButtonClicked); - - // TODO: Connect color picker - // connect(ui->, &, this, &GridSettingsDialog::changedGridSettings); + connect(ui->colorInput, &ColorInputWidget::colorChanged, this, &GridSettingsDialog::onColorChanged); } GridSettingsDialog::~GridSettingsDialog() { @@ -59,11 +56,13 @@ void GridSettingsDialog::reset(bool force) { const QSignalBlocker b_X(ui->spinBox_X); const QSignalBlocker b_Y(ui->spinBox_Y); const QSignalBlocker b_Style(ui->comboBox_Style); + const QSignalBlocker b_Color(ui->colorInput); 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); + ui->colorInput->setColor(this->settings->color.rgb()); // TODO: Debug //ui->comboBox_Style->setCurrentIndex(ui->comboBox_Style->findData(static_cast(this->settings->style))); @@ -73,7 +72,6 @@ void GridSettingsDialog::reset(bool force) { break; } } - // TODO: Initialize color with settings-color emit changedGridSettings(); } @@ -106,6 +104,11 @@ void GridSettingsDialog::on_comboBox_Style_currentIndexChanged(int index) { emit changedGridSettings(); } +void GridSettingsDialog::onColorChanged(QRgb color) { + this->settings->color = QColor::fromRgb(color); + emit changedGridSettings(); +} + void GridSettingsDialog::dialogButtonClicked(QAbstractButton *button) { auto role = ui->buttonBox->buttonRole(button); if (role == QDialogButtonBox::AcceptRole) { @@ -114,6 +117,6 @@ void GridSettingsDialog::dialogButtonClicked(QAbstractButton *button) { reset(); close(); } else if (role == QDialogButtonBox::ResetRole) { - reset(); + reset(); // TODO: We should restore to original defaults, not to the values when the window was opened. } } From a277e193346487d849fdd4623659ba72e211e5d5 Mon Sep 17 00:00:00 2001 From: GriffinR Date: Sat, 28 Sep 2024 19:28:07 -0400 Subject: [PATCH 05/19] Add linking buttons to grid settings --- forms/gridsettingsdialog.ui | 114 ++++++++++++++++++++++++-------- include/ui/gridsettingsdialog.h | 6 ++ resources/icons/link.ico | Bin 0 -> 1282 bytes resources/icons/link_broken.ico | Bin 0 -> 2069 bytes resources/images.qrc | 2 + src/ui/gridsettingsdialog.cpp | 68 ++++++++++++++----- 6 files changed, 147 insertions(+), 43 deletions(-) create mode 100755 resources/icons/link.ico create mode 100755 resources/icons/link_broken.ico diff --git a/forms/gridsettingsdialog.ui b/forms/gridsettingsdialog.ui index 73411742..8673a9b3 100644 --- a/forms/gridsettingsdialog.ui +++ b/forms/gridsettingsdialog.ui @@ -86,30 +86,60 @@ Dimensions (in pixels) - - - + + + - Width + ... + + + + :/icons/link_broken.ico + :/icons/link.ico:/icons/link_broken.ico + + + true + + + true + + + QToolButton::ToolButtonPopupMode::InstantPopup + + + true - - - - Height - - - - - + + 1 - + + + Height + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + + + + Width + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + + 1 @@ -123,26 +153,56 @@ Offset (in pixels) - - - - - X - - - - + + Y + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + X + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + + + + - - + + + + ... + + + + :/icons/link_broken.ico + :/icons/link.ico:/icons/link_broken.ico + + + true + + + true + + + QToolButton::ToolButtonPopupMode::InstantPopup + + + true + + @@ -188,6 +248,8 @@ 1 - + + + diff --git a/include/ui/gridsettingsdialog.h b/include/ui/gridsettingsdialog.h index 9c64f29d..cd04a048 100644 --- a/include/ui/gridsettingsdialog.h +++ b/include/ui/gridsettingsdialog.h @@ -24,8 +24,14 @@ private: Ui::GridSettingsDialog *ui; GridSettings *settings; GridSettings originalSettings; + bool dimensionsLinked = true; + bool offsetsLinked = true; void reset(bool force = false); + void setWidth(int value); + void setHeight(int value); + void setOffsetX(int value); + void setOffsetY(int value); private slots: void dialogButtonClicked(QAbstractButton *button); diff --git a/resources/icons/link.ico b/resources/icons/link.ico new file mode 100755 index 0000000000000000000000000000000000000000..b0c35b59a32a35922e235ce13cfaea86fb337aa3 GIT binary patch literal 1282 zcmV+d1^xPoP)20c}Tt0!?8C z=xDn*fBM6aEEdGD1Q?8M&Y9_)i5akqKa4SPh|I~&Y>66U;$^0z!)-+4-0+8)a_I%U zEl{ADC53V5Mi*U6ZE3c)r#Ij4^t1vi!Z=-alJ}&^cTS)8d%x#>pXcx*kqA7@D}~&6 zJYKT@m)F|XS&}XiT?~c8d7)qsBB3zQ0s3OgyG?s>Js5wGR3OO-@Y_329w;g(R99>( z2cOpuF1H)(4hKw6O~ahc25s*je-xjiA4CHD*7gq$$cl>8H8s^>7#;>{#tH%24?=+u zBvL8ZY&U?Sti2z9cxpGPL`pJuz8GAqGl1D_2Ghj&r{+JenN(%vZ}@266FE6KnAB`7 zJ}(Q+YtOLuHVAAbFlu>?>CD>Xxb$L#{SQhBQJ&=DPBID+pZ;MLWYpw%0&4X0q{ z`gF%1zyGGk^}ESPAT<;U!MyVyu>XA< z_x1fmj>aCF1Dr`1!U?C>YN4>G0Cv{Z!slO}2A|IdTedvevmCrX3Vw^-eIywi0GG?@ zk|^X^yXtm;`b-yGy*dJ~A3X|$aQ%bYMBsNY!pRuLsc#>9XLj>ujF8&{$uO ztrlk3&RI$mtJ;5(+H>LDIb6T_)#)#`&CT6x%Fo{j8Ch9iw>wA$=96Md?@={$GOU^C zW_vie|CLv_%jJp=GG6-!w5V#-5>?}x0HbPNpP$$zKnwNJBJh}`eionYQh%=AnbHH2hq1bORO`x*Qe6b9y2~yrUDVF8gVkKsoJ_d9XpCe_*>rGa>6K)xosNU38rrDURE=u_q!PIp#RnS>pWGl6ZY7)`u~t>%ngG*^7}*$k7@6$xtXb8P skwA z>43aYs3lk-Ckl>Y1yT7@F`jF{;8kK7!bYLId{iR=9Eoy4aIus+^^mx_uAh#{B@sKm(<1yx1Ee$b^Nd&4yW3w(gU zNi^&qrlMG(fS*(j0UmfFj!z_k0LdK>x)Ukx6j#8F2oedP8-eJF1Bp}*Pbv`vK0a6^ znp_Z14QB*=jD?(N*hCnXQ3(X4Qi)f7jhD(32q1+*ArRdNZf-aPfm5VQV6F-$QLI@| zU_c7KTquKuQVC#C7Kp}e-HNsE$@n|FbZNB5}5(Xg=vr=KrR&nA5x|Y7x&`1sNP4e zU~w9D}z0mZ${eipeXA=3@6ncibZ??F5^H~Y+)(~^x>5v3J35UIcAK&7rn-lnCtei=1u}x0%FhkmF6tX4#Xf(r>&_jp zKIL8X$isq3`ABudi2GIFU7O9w&vE34m025I2cMR7=d@8Y(={QMhjs;dcJa&JDjq4f zb)7q9RF-mFB*`+;wgSd;T(jFeD$s)-+`|2i}D)pB9$)H)~Ry}1?m1pzV z(Ce9W%HYKK;Om(a5Awb^{&}Qb|6c8E-HQ9$-l_9XHS61tO}~F+6O7)QqPhPvGe=Zh z5%6Q6fWUV>ody61~LTN-!mqYAou==*m3-u$<^Bx zI6=#hN6TG)18X`b4QDcyHBBDT@~twna+YKLXB91Zbw2s@Nxk=6q3aIIR^`qF{PqxF+EF_-2_I{nRbn%NF!TdGTA#W$wOQDG z{c7pMUw#a_Lae^a?7%sWrnG3R;~l=p-Z=KMjqa0H@5PFDQZ<$y+y}Xl-q*&4u3Mq^ zxIn{X9rJRI_^O2O6`4~0o$jXpJ4Z@Uf`FX7gTGOwL9ZN7VdUQwjLxw8pCFS;0tckVw=w zsj7M^_Z3gv$*pAO3J{GO3 z5A6NfvSp2hKzOTp2?U3S_vmg%3^N`c2#C!;ebn?O&qy88FY#h=dKc3;%fg1}w6q@Gy)e$-TMtV&5(h_;X}ZQ@4m~8~0{g*) z>wL`qZ$H~H*grE9644k5Fz=NyboP+|MKTypi7pBsMEO#fg;6+KS-U)efXDgXcg literal 0 HcmV?d00001 diff --git a/resources/images.qrc b/resources/images.qrc index 73ed0620..26f51f42 100644 --- a/resources/images.qrc +++ b/resources/images.qrc @@ -17,6 +17,8 @@ icons/folder_map.ico icons/folder.ico icons/help.ico + icons/link_broken.ico + icons/link.ico icons/map_edited.ico icons/map_opened.ico icons/map.ico diff --git a/src/ui/gridsettingsdialog.cpp b/src/ui/gridsettingsdialog.cpp index bfc814bc..3ce0d4df 100644 --- a/src/ui/gridsettingsdialog.cpp +++ b/src/ui/gridsettingsdialog.cpp @@ -1,8 +1,6 @@ #include "ui_gridsettingsdialog.h" #include "gridsettingsdialog.h" -// 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 @@ -31,6 +29,9 @@ GridSettingsDialog::GridSettingsDialog(GridSettings *settings, QWidget *parent) ui->spinBox_X->setMaximum(INT_MAX); ui->spinBox_Y->setMaximum(INT_MAX); + ui->button_LinkDimensions->setChecked(this->dimensionsLinked); + ui->button_LinkOffsets->setChecked(this->offsetsLinked); + // Initialize the settings if (!this->settings) this->settings = new GridSettings; // TODO: Don't leak this @@ -38,6 +39,8 @@ GridSettingsDialog::GridSettingsDialog(GridSettings *settings, QWidget *parent) reset(true); connect(ui->buttonBox, &QDialogButtonBox::clicked, this, &GridSettingsDialog::dialogButtonClicked); + connect(ui->button_LinkDimensions, &QAbstractButton::toggled, [this](bool on) { this->dimensionsLinked = on; }); + connect(ui->button_LinkOffsets, &QAbstractButton::toggled, [this](bool on) { this->offsetsLinked = on; }); connect(ui->colorInput, &ColorInputWidget::colorChanged, this, &GridSettingsDialog::onColorChanged); } @@ -50,20 +53,15 @@ void GridSettingsDialog::reset(bool force) { 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); - const QSignalBlocker b_Style(ui->comboBox_Style); - const QSignalBlocker b_Color(ui->colorInput); + setWidth(this->settings->width); + setHeight(this->settings->height); + setOffsetX(this->settings->offsetX); + setOffsetY(this->settings->offsetY); - 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); + const QSignalBlocker b_Color(ui->colorInput); ui->colorInput->setColor(this->settings->color.rgb()); + const QSignalBlocker b_Style(ui->comboBox_Style); // TODO: Debug //ui->comboBox_Style->setCurrentIndex(ui->comboBox_Style->findData(static_cast(this->settings->style))); for (const auto &pair : penStyleMap) { @@ -76,23 +74,59 @@ void GridSettingsDialog::reset(bool force) { emit changedGridSettings(); } -void GridSettingsDialog::on_spinBox_Width_valueChanged(int value) { +void GridSettingsDialog::setWidth(int value) { + const QSignalBlocker b(ui->spinBox_Width); + ui->spinBox_Width->setValue(value); this->settings->width = value; +} + +void GridSettingsDialog::setHeight(int value) { + const QSignalBlocker b(ui->spinBox_Height); + ui->spinBox_Height->setValue(value); + this->settings->height = value; +} + +void GridSettingsDialog::setOffsetX(int value) { + const QSignalBlocker b(ui->spinBox_X); + ui->spinBox_X->setValue(value); + this->settings->offsetX = value; +} + +void GridSettingsDialog::setOffsetY(int value) { + const QSignalBlocker b(ui->spinBox_Y); + ui->spinBox_Y->setValue(value); + this->settings->offsetY = value; +} + +void GridSettingsDialog::on_spinBox_Width_valueChanged(int value) { + setWidth(value); + if (this->dimensionsLinked) + setHeight(value); + emit changedGridSettings(); } void GridSettingsDialog::on_spinBox_Height_valueChanged(int value) { - this->settings->height = value; + setHeight(value); + if (this->dimensionsLinked) + setWidth(value); + emit changedGridSettings(); } void GridSettingsDialog::on_spinBox_X_valueChanged(int value) { - this->settings->offsetX = value; + setOffsetX(value); + if (this->offsetsLinked) + setOffsetY(value); + emit changedGridSettings(); } void GridSettingsDialog::on_spinBox_Y_valueChanged(int value) { - this->settings->offsetY = value; + setOffsetY(value); + if (this->offsetsLinked) + setOffsetX(value); + emit changedGridSettings(); } From 44642c347f4ca894ce3db015407b7ab43cdad8eb Mon Sep 17 00:00:00 2001 From: GriffinR Date: Sun, 29 Sep 2024 16:10:48 -0400 Subject: [PATCH 06/19] Add custom dash patterns to grid settings --- forms/gridsettingsdialog.ui | 28 +++- include/editor.h | 2 + include/mainwindow.h | 2 +- include/settings.h | 9 -- include/ui/gridsettings.h | 99 ++++++++++++++ include/ui/gridsettingsdialog.h | 59 -------- porymap.pro | 4 +- src/editor.cpp | 28 ++-- src/mainwindow.cpp | 2 +- src/ui/graphicsview.cpp | 20 ++- src/ui/gridsettings.cpp | 230 ++++++++++++++++++++++++++++++++ src/ui/gridsettingsdialog.cpp | 156 ---------------------- 12 files changed, 394 insertions(+), 245 deletions(-) create mode 100644 include/ui/gridsettings.h delete mode 100644 include/ui/gridsettingsdialog.h create mode 100644 src/ui/gridsettings.cpp delete mode 100644 src/ui/gridsettingsdialog.cpp diff --git a/forms/gridsettingsdialog.ui b/forms/gridsettingsdialog.ui index 8673a9b3..75d31de1 100644 --- a/forms/gridsettingsdialog.ui +++ b/forms/gridsettingsdialog.ui @@ -114,7 +114,10 @@ - 1 + 2 + + + 999 @@ -141,7 +144,10 @@ - 1 + 2 + + + 999 @@ -175,10 +181,24 @@ - + + + 0 + + + 999 + + - + + + 0 + + + 999 + + diff --git a/include/editor.h b/include/editor.h index 671c70d0..88eec307 100644 --- a/include/editor.h +++ b/include/editor.h @@ -23,6 +23,7 @@ #include "collisionpixmapitem.h" #include "mappixmapitem.h" #include "settings.h" +#include "gridsettings.h" #include "movablerect.h" #include "cursortilerect.h" #include "mapruler.h" @@ -68,6 +69,7 @@ public: void displayMapConnections(); void displayMapBorder(); void displayMapGrid(); + void updateMapGrid(); void displayWildMonTables(); void updateMapBorder(); diff --git a/include/mainwindow.h b/include/mainwindow.h index 3f4625db..f2a31741 100644 --- a/include/mainwindow.h +++ b/include/mainwindow.h @@ -26,7 +26,7 @@ #include "shortcutseditor.h" #include "preferenceeditor.h" #include "projectsettingseditor.h" -#include "gridsettingsdialog.h" +#include "gridsettings.h" #include "customscriptseditor.h" #include "wildmonchart.h" #include "updatepromoter.h" diff --git a/include/settings.h b/include/settings.h index 0e0e8df5..d37283cd 100644 --- a/include/settings.h +++ b/include/settings.h @@ -4,15 +4,6 @@ #include -struct GridSettings { - uint width = 16; - uint height = 16; - int offsetX = 0; - int offsetY = 0; - Qt::PenStyle style = Qt::SolidLine; - QColor color = Qt::black; -}; - class Settings { public: diff --git a/include/ui/gridsettings.h b/include/ui/gridsettings.h new file mode 100644 index 00000000..0a966e4c --- /dev/null +++ b/include/ui/gridsettings.h @@ -0,0 +1,99 @@ +#ifndef GRIDSETTINGS_H +#define GRIDSETTINGS_H + +#include +#include + +class GridSettings { +public: + explicit GridSettings() {}; + ~GridSettings() {}; + + enum Style { + Solid, + LargeDashes, + SmallDashes, + Crosshairs, + Dots, + }; + + uint width = 16; + uint height = 16; + int offsetX = 0; + int offsetY = 0; + Style style = Style::Solid; + QColor color = Qt::black; + QList getHorizontalDashPattern() const { return this->getDashPattern(this->width); } + QList getVerticalDashPattern() const { return this->getDashPattern(this->height); } + + static QString getStyleName(Style style); + static GridSettings::Style getStyleFromName(const QString &name); +private: + static const QMap styleToName; + + QList getCenteredDashPattern(uint length, qreal dashLength, qreal gapLength) const; + QList getDashPattern(uint length) const; +}; + +inline bool operator==(const GridSettings &a, const 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 GridSettings &a, const GridSettings &b) { + return !(operator==(a, b)); +} + + + +namespace Ui { +class GridSettingsDialog; +} + +class GridSettingsDialog : public QDialog { + Q_OBJECT +public: + explicit GridSettingsDialog(QWidget *parent = nullptr); + explicit GridSettingsDialog(GridSettings *settings, QWidget *parent = nullptr); + ~GridSettingsDialog(); + + void setSettings(const GridSettings &settings); + GridSettings settings() const { return *m_settings; } + + void setDefaultSettings(const GridSettings &settings); + GridSettings defaultSettings() const { return m_defaultSettings; } + +signals: + void changedGridSettings(); + +private: + Ui::GridSettingsDialog *ui; + GridSettings *const m_settings; + const GridSettings m_originalSettings; + GridSettings m_defaultSettings; + bool m_dimensionsLinked = true; + bool m_offsetsLinked = true; + bool m_ownedSettings = false; + + void init(); + void updateInput(); + void setWidth(int value); + void setHeight(int value); + void setOffsetX(int value); + void setOffsetY(int value); + +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(const QString &text); + void onColorChanged(QRgb color); +}; + +#endif // GRIDSETTINGS_H diff --git a/include/ui/gridsettingsdialog.h b/include/ui/gridsettingsdialog.h deleted file mode 100644 index cd04a048..00000000 --- a/include/ui/gridsettingsdialog.h +++ /dev/null @@ -1,59 +0,0 @@ -#ifndef GRIDSETTINGSDIALOG_H -#define GRIDSETTINGSDIALOG_H - -#include -#include - -#include "settings.h" - -namespace Ui { -class GridSettingsDialog; -} - -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; - bool dimensionsLinked = true; - bool offsetsLinked = true; - - void reset(bool force = false); - void setWidth(int value); - void setHeight(int value); - void setOffsetX(int value); - void setOffsetY(int value); - -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_currentIndexChanged(int index); - void onColorChanged(QRgb color); -}; - -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 182ea11a..6b7fc59d 100644 --- a/porymap.pro +++ b/porymap.pro @@ -61,7 +61,7 @@ SOURCES += src/core/block.cpp \ src/ui/collisionpixmapitem.cpp \ src/ui/connectionpixmapitem.cpp \ src/ui/currentselectedmetatilespixmapitem.cpp \ - src/ui/gridsettingsdialog.cpp \ + src/ui/gridsettings.cpp \ src/ui/newmapconnectiondialog.cpp \ src/ui/overlay.cpp \ src/ui/prefab.cpp \ @@ -159,7 +159,7 @@ HEADERS += include/core/block.h \ include/ui/collisionpixmapitem.h \ include/ui/connectionpixmapitem.h \ include/ui/currentselectedmetatilespixmapitem.h \ - include/ui/gridsettingsdialog.h \ + include/ui/gridsettings.h \ include/ui/newmapconnectiondialog.h \ include/ui/prefabframe.h \ include/ui/projectsettingseditor.h \ diff --git a/src/editor.cpp b/src/editor.cpp index 99e18c3e..3929d1e3 100644 --- a/src/editor.cpp +++ b/src/editor.cpp @@ -1875,25 +1875,29 @@ void Editor::displayMapGrid() { // 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; + const int pixelMapWidth = map->getWidth() * 16; + const int pixelMapHeight = map->getHeight() * 16; + + // The grid can be moved with a user-specified x/y offset. The grid's dash patterns will only wrap in full pattern increments, + // so we draw an additional row/column outside the map that can be revealed as the offset changes. + const int offsetX = (this->gridSettings.offsetX % this->gridSettings.width) - this->gridSettings.width; + const int offsetY = (this->gridSettings.offsetY % this->gridSettings.height) - this->gridSettings.height; QPen pen; - pen.setStyle(this->gridSettings.style); pen.setColor(this->gridSettings.color); // Create vertical lines - int offset = this->gridSettings.offsetX % this->gridSettings.width; - for (uint i = offset; i <= pixelMapWidth; i += this->gridSettings.width) { - auto line = new QGraphicsLineItem(i, 0, i, pixelMapHeight); + pen.setDashPattern(this->gridSettings.getVerticalDashPattern()); + for (int i = offsetX; i <= pixelMapWidth; i += this->gridSettings.width) { + auto line = new QGraphicsLineItem(i, offsetY, i, pixelMapHeight); line->setPen(pen); this->mapGrid->addToGroup(line); } // Create horizontal lines - offset = this->gridSettings.offsetY % this->gridSettings.height; - for (uint i = offset; i <= pixelMapHeight; i += this->gridSettings.height) { - auto line = new QGraphicsLineItem(0, i, pixelMapWidth, i); + pen.setDashPattern(this->gridSettings.getHorizontalDashPattern()); + for (int i = offsetY; i <= pixelMapHeight; i += this->gridSettings.height) { + auto line = new QGraphicsLineItem(offsetX, i, pixelMapWidth, i); line->setPen(pen); this->mapGrid->addToGroup(line); } @@ -1901,6 +1905,12 @@ void Editor::displayMapGrid() { this->mapGrid->setVisible(porymapConfig.showGrid); } +void Editor::updateMapGrid() { + displayMapGrid(); + if (ui->graphicsView_Map->scene()) + ui->graphicsView_Map->scene()->update(); +} + void Editor::updatePrimaryTileset(QString tilesetLabel, bool forceLoad) { if (map->layout->tileset_primary_label != tilesetLabel || forceLoad) diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 1623d055..93ed1dcb 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -1929,7 +1929,7 @@ void MainWindow::on_actionShow_Grid_triggered() { 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); + connect(this->gridSettingsDialog, &GridSettingsDialog::changedGridSettings, this->editor, &Editor::updateMapGrid); } openSubWindow(this->gridSettingsDialog); } diff --git a/src/ui/graphicsview.cpp b/src/ui/graphicsview.cpp index c6b78bba..f4de5074 100644 --- a/src/ui/graphicsview.cpp +++ b/src/ui/graphicsview.cpp @@ -31,12 +31,24 @@ void MapView::drawForeground(QPainter *painter, const QRectF&) { if (!editor) return; QStyleOptionGraphicsItem option; - if (editor->mapGrid) { - for (auto item : editor->mapGrid->childItems()) { - if (item->isVisible()) - item->paint(painter, &option, this); + + // Draw elements of the map view that should always render on top of anything added by the user with the scripting API. + + // Draw map grid + if (editor->mapGrid && editor->mapGrid->isVisible()) { + painter->save(); + if (editor->map) { + // We're clipping here to hide parts of the grid that are outside the map. + const QRectF mapRect(-0.5, -0.5, editor->map->getWidth() * 16 + 1.5, editor->map->getHeight() * 16 + 1.5); + painter->setClipping(true); + painter->setClipRect(mapRect); } + for (auto item : editor->mapGrid->childItems()) + item->paint(painter, &option, this); + painter->restore(); } + + // Draw cursor rectangles if (editor->playerViewRect && editor->playerViewRect->isVisible()) editor->playerViewRect->paint(painter, &option, this); if (editor->cursorMapTileRect && editor->cursorMapTileRect->isVisible()) diff --git a/src/ui/gridsettings.cpp b/src/ui/gridsettings.cpp new file mode 100644 index 00000000..96494e83 --- /dev/null +++ b/src/ui/gridsettings.cpp @@ -0,0 +1,230 @@ +#include "ui_gridsettingsdialog.h" +#include "gridsettings.h" + +// TODO: Save settings in config + +const QMap GridSettings::styleToName = { + {Style::Solid, "Solid"}, + {Style::LargeDashes, "Large Dashes"}, + {Style::SmallDashes, "Small Dashes"}, + {Style::Crosshairs, "Crosshairs"}, + {Style::Dots, "Dots"}, +}; + +QString GridSettings::getStyleName(GridSettings::Style style) { + return styleToName.value(style); +} + +GridSettings::Style GridSettings::getStyleFromName(const QString &name) { + return styleToName.key(name, GridSettings::Style::Solid); +} + +// We do some extra work here to A: try and center the dashes away from the intersections, and B: keep the dash pattern's total +// length equal to the length of a grid square. This keeps the patterns looking reasonable regardless of the grid size. +// Otherwise, the dashes can start to intersect in weird ways and create grid patterns that don't look like a rectangular grid. +QList GridSettings::getCenteredDashPattern(uint length, qreal dashLength, qreal gapLength) const { + const qreal minEdgesLength = 0.6*2; + if (length <= dashLength + minEdgesLength) + return {dashLength}; + + // Every dash after the first one needs to have room for a 'gapLength' segment. + const int numDashes = 1 + ((length - minEdgesLength) - dashLength) / (dashLength + gapLength); + + // Total length of the pattern excluding the centering edges. There are always 1 fewer gap segments than dashes. + const qreal mainLength = (dashLength * numDashes) + (gapLength * (numDashes-1)); + + const qreal edgeLength = (length - mainLength) / 2; + + // Fill the pattern + QList pattern = {0, edgeLength}; + for (int i = 0; i < numDashes-1; i++) { + pattern.append(dashLength); + pattern.append(gapLength); + } + pattern.append(dashLength); + pattern.append(edgeLength); + + return pattern; +} + +QList GridSettings::getDashPattern(uint length) const { + switch (this->style) { + + // Equivalent to setting Qt::PenStyle::Solid with no dash pattern. + case Style::Solid: return {1, 0}; + + // Roughly equivalent to Qt::PenStyle::DashLine but with centering. + case Style::LargeDashes: return getCenteredDashPattern(length, 3.0, 2.0); + + // Roughly equivalent to Qt::PenStyle::DotLine but with centering. + case Style::SmallDashes: return getCenteredDashPattern(length, 1.0, 2.5); + + // Dashes only at intersections, in the shape of a crosshair. + case Style::Crosshairs: { + const qreal crosshairLength = 2.0; + return {crosshairLength / 2, length - crosshairLength, crosshairLength / 2, 0}; + } + + // Dots only at intersections. + case Style::Dots: { + const qreal dotLength = 0.1; + return {dotLength, length - dotLength}; + } + + // Invalid + default: return {}; + } +} + + + +GridSettingsDialog::GridSettingsDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::GridSettingsDialog), + m_settings(new GridSettings), + m_originalSettings(*m_settings) +{ + m_ownedSettings = true; + init(); +} + +GridSettingsDialog::GridSettingsDialog(GridSettings *settings, QWidget *parent) : + QDialog(parent), + ui(new Ui::GridSettingsDialog), + m_settings(settings), + m_originalSettings(*settings) +{ + m_ownedSettings = false; + init(); +} + +void GridSettingsDialog::init() { + ui->setupUi(this); + setAttribute(Qt::WA_DeleteOnClose); + + // Populate the styles combo box + const QSignalBlocker b_Style(ui->comboBox_Style); + ui->comboBox_Style->addItem(GridSettings::getStyleName(GridSettings::Style::Solid)); + ui->comboBox_Style->addItem(GridSettings::getStyleName(GridSettings::Style::LargeDashes)); + ui->comboBox_Style->addItem(GridSettings::getStyleName(GridSettings::Style::SmallDashes)); + ui->comboBox_Style->addItem(GridSettings::getStyleName(GridSettings::Style::Crosshairs)); + ui->comboBox_Style->addItem(GridSettings::getStyleName(GridSettings::Style::Dots)); + + ui->button_LinkDimensions->setChecked(m_dimensionsLinked); + ui->button_LinkOffsets->setChecked(m_offsetsLinked); + + connect(ui->buttonBox, &QDialogButtonBox::clicked, this, &GridSettingsDialog::dialogButtonClicked); + connect(ui->button_LinkDimensions, &QAbstractButton::toggled, [this](bool on) { m_dimensionsLinked = on; }); + connect(ui->button_LinkOffsets, &QAbstractButton::toggled, [this](bool on) { m_offsetsLinked = on; }); + connect(ui->colorInput, &ColorInputWidget::colorChanged, this, &GridSettingsDialog::onColorChanged); + + updateInput(); +} + +GridSettingsDialog::~GridSettingsDialog() { + delete ui; + if (m_ownedSettings) + delete m_settings; +} + +void GridSettingsDialog::setSettings(const GridSettings &settings) { + if (*m_settings == settings) + return; + *m_settings = settings; + updateInput(); + emit changedGridSettings(); +} + +void GridSettingsDialog::updateInput() { + setWidth(m_settings->width); + setHeight(m_settings->height); + setOffsetX(m_settings->offsetX); + setOffsetY(m_settings->offsetY); + + const QSignalBlocker b_Color(ui->colorInput); + ui->colorInput->setColor(m_settings->color.rgb()); + + const QSignalBlocker b_Style(ui->comboBox_Style); + ui->comboBox_Style->setCurrentText(GridSettings::getStyleName(m_settings->style)); +} + +void GridSettingsDialog::setWidth(int value) { + const QSignalBlocker b(ui->spinBox_Width); + ui->spinBox_Width->setValue(value); + m_settings->width = value; +} + +void GridSettingsDialog::setHeight(int value) { + const QSignalBlocker b(ui->spinBox_Height); + ui->spinBox_Height->setValue(value); + m_settings->height = value; +} + +void GridSettingsDialog::setOffsetX(int value) { + const QSignalBlocker b(ui->spinBox_X); + ui->spinBox_X->setValue(value); + m_settings->offsetX = value; +} + +void GridSettingsDialog::setOffsetY(int value) { + const QSignalBlocker b(ui->spinBox_Y); + ui->spinBox_Y->setValue(value); + m_settings->offsetY = value; +} + +void GridSettingsDialog::on_spinBox_Width_valueChanged(int value) { + setWidth(value); + if (m_dimensionsLinked) + setHeight(value); + + emit changedGridSettings(); +} + +void GridSettingsDialog::on_spinBox_Height_valueChanged(int value) { + setHeight(value); + if (m_dimensionsLinked) + setWidth(value); + + emit changedGridSettings(); +} + +void GridSettingsDialog::on_spinBox_X_valueChanged(int value) { + setOffsetX(value); + if (m_offsetsLinked) + setOffsetY(value); + + emit changedGridSettings(); +} + +void GridSettingsDialog::on_spinBox_Y_valueChanged(int value) { + setOffsetY(value); + if (m_offsetsLinked) + setOffsetX(value); + + emit changedGridSettings(); +} + +void GridSettingsDialog::on_comboBox_Style_currentTextChanged(const QString &text) { + m_settings->style = GridSettings::getStyleFromName(text); + emit changedGridSettings(); +} + +void GridSettingsDialog::onColorChanged(QRgb color) { + m_settings->color = QColor::fromRgb(color); + emit changedGridSettings(); +} + +void GridSettingsDialog::dialogButtonClicked(QAbstractButton *button) { + auto role = ui->buttonBox->buttonRole(button); + if (role == QDialogButtonBox::AcceptRole) { + // "OK" + close(); + } else if (role == QDialogButtonBox::RejectRole) { + // "Cancel" + setSettings(m_originalSettings); + close(); + } else if (role == QDialogButtonBox::ResetRole) { + // "Restore Defaults" + setSettings(m_defaultSettings); + } +} diff --git a/src/ui/gridsettingsdialog.cpp b/src/ui/gridsettingsdialog.cpp deleted file mode 100644 index 3ce0d4df..00000000 --- a/src/ui/gridsettingsdialog.cpp +++ /dev/null @@ -1,156 +0,0 @@ -#include "ui_gridsettingsdialog.h" -#include "gridsettingsdialog.h" - -// TODO: Save settings in config -// TODO: Look into custom painting to improve performance -// TODO: Add tooltips - -const QList> penStyleMap = { - {"Solid", Qt::SolidLine}, - {"Large Dashes", Qt::DashLine}, - {"Small Dashes", Qt::DotLine}, - {"Dots", Qt::CustomDashLine}, // TODO: Implement a custom pattern for this -}; - -GridSettingsDialog::GridSettingsDialog(GridSettings *settings, QWidget *parent) : - QDialog(parent), - ui(new Ui::GridSettingsDialog), - settings(settings) -{ - ui->setupUi(this); - setAttribute(Qt::WA_DeleteOnClose); - - // Populate the styles combo box - for (const auto &pair : penStyleMap) - ui->comboBox_Style->addItem(pair.first, static_cast(pair.second)); - - ui->spinBox_Width->setMaximum(INT_MAX); - ui->spinBox_Height->setMaximum(INT_MAX); - ui->spinBox_X->setMaximum(INT_MAX); - ui->spinBox_Y->setMaximum(INT_MAX); - - ui->button_LinkDimensions->setChecked(this->dimensionsLinked); - ui->button_LinkOffsets->setChecked(this->offsetsLinked); - - // Initialize the settings - 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); - connect(ui->button_LinkDimensions, &QAbstractButton::toggled, [this](bool on) { this->dimensionsLinked = on; }); - connect(ui->button_LinkOffsets, &QAbstractButton::toggled, [this](bool on) { this->offsetsLinked = on; }); - connect(ui->colorInput, &ColorInputWidget::colorChanged, this, &GridSettingsDialog::onColorChanged); -} - -GridSettingsDialog::~GridSettingsDialog() { - delete ui; -} - -void GridSettingsDialog::reset(bool force) { - if (!force && *this->settings == this->originalSettings) - return; - *this->settings = this->originalSettings; - - setWidth(this->settings->width); - setHeight(this->settings->height); - setOffsetX(this->settings->offsetX); - setOffsetY(this->settings->offsetY); - - const QSignalBlocker b_Color(ui->colorInput); - ui->colorInput->setColor(this->settings->color.rgb()); - - const QSignalBlocker b_Style(ui->comboBox_Style); - // TODO: Debug - //ui->comboBox_Style->setCurrentIndex(ui->comboBox_Style->findData(static_cast(this->settings->style))); - for (const auto &pair : penStyleMap) { - if (pair.second == this->settings->style) { - ui->comboBox_Style->setCurrentText(pair.first); - break; - } - } - - emit changedGridSettings(); -} - -void GridSettingsDialog::setWidth(int value) { - const QSignalBlocker b(ui->spinBox_Width); - ui->spinBox_Width->setValue(value); - this->settings->width = value; -} - -void GridSettingsDialog::setHeight(int value) { - const QSignalBlocker b(ui->spinBox_Height); - ui->spinBox_Height->setValue(value); - this->settings->height = value; -} - -void GridSettingsDialog::setOffsetX(int value) { - const QSignalBlocker b(ui->spinBox_X); - ui->spinBox_X->setValue(value); - this->settings->offsetX = value; -} - -void GridSettingsDialog::setOffsetY(int value) { - const QSignalBlocker b(ui->spinBox_Y); - ui->spinBox_Y->setValue(value); - this->settings->offsetY = value; -} - -void GridSettingsDialog::on_spinBox_Width_valueChanged(int value) { - setWidth(value); - if (this->dimensionsLinked) - setHeight(value); - - emit changedGridSettings(); -} - -void GridSettingsDialog::on_spinBox_Height_valueChanged(int value) { - setHeight(value); - if (this->dimensionsLinked) - setWidth(value); - - emit changedGridSettings(); -} - -void GridSettingsDialog::on_spinBox_X_valueChanged(int value) { - setOffsetX(value); - if (this->offsetsLinked) - setOffsetY(value); - - emit changedGridSettings(); -} - -void GridSettingsDialog::on_spinBox_Y_valueChanged(int value) { - setOffsetY(value); - if (this->offsetsLinked) - setOffsetX(value); - - emit changedGridSettings(); -} - -void GridSettingsDialog::on_comboBox_Style_currentIndexChanged(int index) { - if (index < 0 || index >= penStyleMap.length()) - return; - - this->settings->style = penStyleMap.at(index).second; - emit changedGridSettings(); -} - -void GridSettingsDialog::onColorChanged(QRgb color) { - this->settings->color = QColor::fromRgb(color); - 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(); // TODO: We should restore to original defaults, not to the values when the window was opened. - } -} From 932c29993508e897dc215ebaef7a51d396d5aed6 Mon Sep 17 00:00:00 2001 From: GriffinR Date: Tue, 1 Oct 2024 02:20:26 -0400 Subject: [PATCH 07/19] Refactor palette editor to use new color input widget --- forms/colorinputwidget.ui | 3 - forms/paletteeditor.ui | 5084 +---------------------------------- include/ui/paletteeditor.h | 35 +- src/ui/colorinputwidget.cpp | 2 - src/ui/paletteeditor.cpp | 342 +-- 5 files changed, 104 insertions(+), 5362 deletions(-) diff --git a/forms/colorinputwidget.ui b/forms/colorinputwidget.ui index eebd7955..11f0bc25 100644 --- a/forms/colorinputwidget.ui +++ b/forms/colorinputwidget.ui @@ -10,9 +10,6 @@ 212 - - Color - diff --git a/forms/paletteeditor.ui b/forms/paletteeditor.ui index 1cd7c959..c1a4ee7a 100644 --- a/forms/paletteeditor.ui +++ b/forms/paletteeditor.ui @@ -7,7 +7,7 @@ 0 0 907 - 886 + 933 @@ -15,5039 +15,13 @@ - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Color 0 - - - - - - - 32 - 32 - - - - - 32 - 32 - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Red - - - - - - - Green - - - - - - - Blue - - - - - - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 31 - - - 4 - - - Qt::Horizontal - - - - - - - 31 - - - 4 - - - Qt::Horizontal - - - - - - - 31 - - - 4 - - - Qt::Horizontal - - - - - - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 255 - - - 8 - - - - - - - 255 - - - 8 - - - - - - - 255 - - - 8 - - - - - - - - - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - # - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - - - - - ... - - - - :/icons/pipette.ico:/icons/pipette.ico - - - - - - - - - - - - - - - - Color 1 - - - - - - - 32 - 32 - - - - - 32 - 32 - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Red - - - - - - - Green - - - - - - - Blue - - - - - - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 31 - - - 4 - - - Qt::Horizontal - - - - - - - 31 - - - 4 - - - Qt::Horizontal - - - - - - - 31 - - - 4 - - - Qt::Horizontal - - - - - - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 255 - - - 8 - - - - - - - 255 - - - 8 - - - - - - - 255 - - - 8 - - - - - - - - - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - # - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - - - - - ... - - - - :/icons/pipette.ico:/icons/pipette.ico - - - - - - - - - - - - - - - - Color 2 - - - - - - - 32 - 32 - - - - - 32 - 32 - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Red - - - - - - - Green - - - - - - - Blue - - - - - - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 31 - - - 4 - - - Qt::Horizontal - - - - - - - 31 - - - 4 - - - Qt::Horizontal - - - - - - - 31 - - - 4 - - - Qt::Horizontal - - - - - - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 255 - - - 8 - - - - - - - 255 - - - 8 - - - - - - - 255 - - - 8 - - - - - - - - - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - # - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - - - - - ... - - - - :/icons/pipette.ico:/icons/pipette.ico - - - - - - - - - - - - - - - - Color 3 - - - - - - - 32 - 32 - - - - - 32 - 32 - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Red - - - - - - - Green - - - - - - - Blue - - - - - - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 31 - - - 4 - - - Qt::Horizontal - - - - - - - 31 - - - 4 - - - Qt::Horizontal - - - - - - - 31 - - - 4 - - - Qt::Horizontal - - - - - - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 255 - - - 8 - - - - - - - 255 - - - 8 - - - - - - - 255 - - - 8 - - - - - - - - - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - # - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - - - - - ... - - - - :/icons/pipette.ico:/icons/pipette.ico - - - - - - - - - - - - - - - - Color 4 - - - - - - - 32 - 32 - - - - - 32 - 32 - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Red - - - - - - - Green - - - - - - - Blue - - - - - - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 31 - - - 4 - - - Qt::Horizontal - - - - - - - 31 - - - 4 - - - Qt::Horizontal - - - - - - - 31 - - - 4 - - - Qt::Horizontal - - - - - - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 255 - - - 8 - - - - - - - 255 - - - 8 - - - - - - - 255 - - - 8 - - - - - - - - - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - # - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - - - - - ... - - - - :/icons/pipette.ico:/icons/pipette.ico - - - - - - - - - - - - - - - - Color 5 - - - - - - - 32 - 32 - - - - - 32 - 32 - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Red - - - - - - - Green - - - - - - - Blue - - - - - - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 31 - - - 4 - - - Qt::Horizontal - - - - - - - 31 - - - 4 - - - Qt::Horizontal - - - - - - - 31 - - - 4 - - - Qt::Horizontal - - - - - - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 255 - - - 8 - - - - - - - 255 - - - 8 - - - - - - - 255 - - - 8 - - - - - - - - - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - # - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - - - - - ... - - - - :/icons/pipette.ico:/icons/pipette.ico - - - - - - - - - - - - - - - - Color 6 - - - - - - - 32 - 32 - - - - - 32 - 32 - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Red - - - - - - - Green - - - - - - - Blue - - - - - - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 31 - - - 4 - - - Qt::Horizontal - - - - - - - 31 - - - 4 - - - Qt::Horizontal - - - - - - - 31 - - - 4 - - - Qt::Horizontal - - - - - - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 255 - - - 8 - - - - - - - 255 - - - 8 - - - - - - - 255 - - - 8 - - - - - - - - - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - # - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - - - - - ... - - - - :/icons/pipette.ico:/icons/pipette.ico - - - - - - - - - - - - - - - - Color 7 - - - - - - - 32 - 32 - - - - - 32 - 32 - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Red - - - - - - - Green - - - - - - - Blue - - - - - - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 31 - - - 4 - - - Qt::Horizontal - - - - - - - 31 - - - 4 - - - Qt::Horizontal - - - - - - - 31 - - - 4 - - - Qt::Horizontal - - - - - - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 255 - - - 8 - - - - - - - 255 - - - 8 - - - - - - - 255 - - - 8 - - - - - - - - - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - # - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - - - - - ... - - - - :/icons/pipette.ico:/icons/pipette.ico - - - - - - - - - - - - - - - - Color 8 - - - - - - - 32 - 32 - - - - - 32 - 32 - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Red - - - - - - - Green - - - - - - - Blue - - - - - - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 31 - - - 4 - - - Qt::Horizontal - - - - - - - 31 - - - 4 - - - Qt::Horizontal - - - - - - - 31 - - - 4 - - - Qt::Horizontal - - - - - - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 255 - - - 8 - - - - - - - 255 - - - 8 - - - - - - - 255 - - - 8 - - - - - - - - - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - # - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - - - - - ... - - - - :/icons/pipette.ico:/icons/pipette.ico - - - - - - - - - - - - - - - - Color 9 - - - - - - - 32 - 32 - - - - - 32 - 32 - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Red - - - - - - - Green - - - - - - - Blue - - - - - - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 31 - - - 4 - - - Qt::Horizontal - - - - - - - 31 - - - 4 - - - Qt::Horizontal - - - - - - - 31 - - - 4 - - - Qt::Horizontal - - - - - - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 255 - - - 8 - - - - - - - 255 - - - 8 - - - - - - - 255 - - - 8 - - - - - - - - - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - # - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - - - - - ... - - - - :/icons/pipette.ico:/icons/pipette.ico - - - - - - - - - - - - - - - - Color 10 - - - - - - - 32 - 32 - - - - - 32 - 32 - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Red - - - - - - - Green - - - - - - - Blue - - - - - - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 31 - - - 4 - - - Qt::Horizontal - - - - - - - 31 - - - 4 - - - Qt::Horizontal - - - - - - - 31 - - - 4 - - - Qt::Horizontal - - - - - - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 255 - - - 8 - - - - - - - 255 - - - 8 - - - - - - - 255 - - - 8 - - - - - - - - - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - # - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - - - - - ... - - - - :/icons/pipette.ico:/icons/pipette.ico - - - - - - - - - - - - - - - - Color 11 - - - - - - - 32 - 32 - - - - - 32 - 32 - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Red - - - - - - - Green - - - - - - - Blue - - - - - - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 31 - - - 4 - - - Qt::Horizontal - - - - - - - 31 - - - 4 - - - Qt::Horizontal - - - - - - - 31 - - - 4 - - - Qt::Horizontal - - - - - - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 255 - - - 8 - - - - - - - 255 - - - 8 - - - - - - - 255 - - - 8 - - - - - - - - - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - # - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - - - - - ... - - - - :/icons/pipette.ico:/icons/pipette.ico - - - - - - - - - - - - - - - - Color 12 - - - - - - - 32 - 32 - - - - - 32 - 32 - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Red - - - - - - - Green - - - - - - - Blue - - - - - - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 31 - - - 4 - - - Qt::Horizontal - - - - - - - 31 - - - 4 - - - Qt::Horizontal - - - - - - - 31 - - - 4 - - - Qt::Horizontal - - - - - - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 255 - - - 8 - - - - - - - 255 - - - 8 - - - - - - - 255 - - - 8 - - - - - - - - - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - # - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - - - - - ... - - - - :/icons/pipette.ico:/icons/pipette.ico - - - - - - - - - - - - - - - - Color 13 - - - - - - - 32 - 32 - - - - - 32 - 32 - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Red - - - - - - - Green - - - - - - - Blue - - - - - - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 31 - - - 4 - - - Qt::Horizontal - - - - - - - 31 - - - 4 - - - Qt::Horizontal - - - - - - - 31 - - - 4 - - - Qt::Horizontal - - - - - - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 255 - - - 8 - - - - - - - 255 - - - 8 - - - - - - - 255 - - - 8 - - - - - - - - - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - # - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - - - - - ... - - - - :/icons/pipette.ico:/icons/pipette.ico - - - - - - - - - - - - - - - - Color 14 - - - - - - - 32 - 32 - - - - - 32 - 32 - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Red - - - - - - - Green - - - - - - - Blue - - - - - - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 31 - - - 4 - - - Qt::Horizontal - - - - - - - 31 - - - 4 - - - Qt::Horizontal - - - - - - - 31 - - - 4 - - - Qt::Horizontal - - - - - - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 255 - - - 8 - - - - - - - 255 - - - 8 - - - - - - - 255 - - - 8 - - - - - - - - - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - # - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - - - - - ... - - - - :/icons/pipette.ico:/icons/pipette.ico - - - - - - - - - - - - - - - - Color 15 - - - - - - - 32 - 32 - - - - - 32 - 32 - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Red - - - - - - - Green - - - - - - - Blue - - - - - - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 31 - - - 4 - - - Qt::Horizontal - - - - - - - 31 - - - 4 - - - Qt::Horizontal - - - - - - - 31 - - - 4 - - - Qt::Horizontal - - - - - - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 255 - - - 8 - - - - - - - 255 - - - 8 - - - - - - - 255 - - - 8 - - - - - - - - - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - # - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - - - - - ... - - - - :/icons/pipette.ico:/icons/pipette.ico - - - - - - - - - - - - - - - - + - QFrame::NoFrame + QFrame::Shape::NoFrame - QFrame::Raised + QFrame::Shadow::Raised @@ -5060,14 +34,14 @@ - Qt::StrongFocus + Qt::FocusPolicy::StrongFocus - Qt::Horizontal + Qt::Orientation::Horizontal @@ -5078,7 +52,7 @@ - + Bit Depth: @@ -5101,6 +75,40 @@ + + + + QFrame::Shape::NoFrame + + + true + + + + + 0 + 0 + 883 + 784 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + @@ -5109,7 +117,7 @@ 0 0 907 - 22 + 37 @@ -5137,10 +145,10 @@ Ctrl+Z - Qt::WindowShortcut + Qt::ShortcutContext::WindowShortcut - QAction::NormalPriority + QAction::Priority::NormalPriority @@ -5157,8 +165,6 @@ - - - + diff --git a/include/ui/paletteeditor.h b/include/ui/paletteeditor.h index 9b846d60..6aa9e131 100644 --- a/include/ui/paletteeditor.h +++ b/include/ui/paletteeditor.h @@ -2,10 +2,8 @@ #define PALETTEEDITOR_H #include -#include -#include -#include -#include + +#include "colorinputwidget.h" #include "project.h" #include "history.h" @@ -32,43 +30,26 @@ public: private: Ui::PaletteEditor *ui; Project *project = nullptr; - - QList> sliders; - QList> spinners; - QList frames; - QList pickButtons; - QList hexEdits; + QList colorInputs; Tileset *primaryTileset; Tileset *secondaryTileset; QList> palettesHistory; - void refreshColorUis(); - void updateColorUi(int index, QRgb color); - void commitEditHistory(int paletteid); + Tileset* getTileset(int paletteId); + void refreshColorInputs(); + void commitEditHistory(int paletteId); void restoreWindowState(); - void setSignalsEnabled(bool enabled); - void setColorsFromHistory(PaletteHistoryItem*, int); void closeEvent(QCloseEvent*); - void pickColor(int i); void setRgb(int index, QRgb rgb); - void setRgbFromSliders(int colorIndex); - void setRgbFromHexEdit(int colorIndex); - void setRgbFromSpinners(int colorIndex); + void setPalette(int paletteId, const QList &palette); void setBitDepth(int bits); int bitDepth = 24; - class HexCodeValidator : public QValidator { - virtual QValidator::State validate(QString &input, int &) const override { - input = input.toUpper(); - return QValidator::Acceptable; - } - }; - - HexCodeValidator *hexValidator = nullptr; + static const int numColors = 16; signals: void closed(); diff --git a/src/ui/colorinputwidget.cpp b/src/ui/colorinputwidget.cpp index 200567af..bbd6f764 100644 --- a/src/ui/colorinputwidget.cpp +++ b/src/ui/colorinputwidget.cpp @@ -4,8 +4,6 @@ #include -// TODO: Refactor palette editor to make use of this class - class HexCodeValidator : public QValidator { virtual QValidator::State validate(QString &input, int &) const override { input = input.toUpper(); diff --git a/src/ui/paletteeditor.cpp b/src/ui/paletteeditor.cpp index 8dcd2a7a..456d1f99 100644 --- a/src/ui/paletteeditor.cpp +++ b/src/ui/paletteeditor.cpp @@ -1,19 +1,12 @@ #include "paletteeditor.h" #include "ui_paletteeditor.h" -#include "colorpicker.h" #include "paletteutil.h" #include "config.h" #include "log.h" -#include #include #include -static inline int rgb5(int rgb) { return round(static_cast(rgb * 31) / 255.0); } -static inline int rgb8(int rgb) { return round(rgb * 255. / 31.); } -static inline int gbaRed(int rgb) { return rgb & 0x1f; } -static inline int gbaGreen(int rgb) { return (rgb >> 5) & 0x1f; } -static inline int gbaBlue(int rgb) { return (rgb >> 10) & 0x1f; } PaletteEditor::PaletteEditor(Project *project, Tileset *primaryTileset, Tileset *secondaryTileset, int paletteId, QWidget *parent) : QMainWindow(parent), @@ -26,55 +19,13 @@ PaletteEditor::PaletteEditor(Project *project, Tileset *primaryTileset, Tileset this->ui->spinBox_PaletteId->setMinimum(0); this->ui->spinBox_PaletteId->setMaximum(Project::getNumPalettesTotal() - 1); - this->sliders.clear(); - for (int i = 0; i < 16; i++) { - QList rgbSliders; - rgbSliders.append(this->ui->container->findChild("slider_red_" + QString::number(i))); - rgbSliders.append(this->ui->container->findChild("slider_green_" + QString::number(i))); - rgbSliders.append(this->ui->container->findChild("slider_blue_" + QString::number(i))); - this->sliders.append(rgbSliders); - - connect(this->sliders[i][0], &QSlider::valueChanged, [=](int) { setRgbFromSliders(i); }); - connect(this->sliders[i][1], &QSlider::valueChanged, [=](int) { setRgbFromSliders(i); }); - connect(this->sliders[i][2], &QSlider::valueChanged, [=](int) { setRgbFromSliders(i); }); - } - - this->spinners.clear(); - for (int i = 0; i < 16; i++) { - QList rgbSpinners; - rgbSpinners.append(this->ui->container->findChild("spin_red_" + QString::number(i))); - rgbSpinners.append(this->ui->container->findChild("spin_green_" + QString::number(i))); - rgbSpinners.append(this->ui->container->findChild("spin_blue_" + QString::number(i))); - this->spinners.append(rgbSpinners); - - connect(this->spinners[i][0], QOverload::of(&QSpinBox::valueChanged), [=](int) { setRgbFromSpinners(i); }); - connect(this->spinners[i][1], QOverload::of(&QSpinBox::valueChanged), [=](int) { setRgbFromSpinners(i); }); - connect(this->spinners[i][2], QOverload::of(&QSpinBox::valueChanged), [=](int) { setRgbFromSpinners(i); }); - } - - this->frames.clear(); - for (int i = 0; i < 16; i++) { - this->frames.append(this->ui->container->findChild("colorFrame_" + QString::number(i))); - this->frames[i]->setFrameStyle(QFrame::NoFrame); - } - - this->pickButtons.clear(); - for (int i = 0; i < 16; i++) { - this->pickButtons.append(this->ui->container->findChild("pick_" + QString::number(i))); - } - - this->hexValidator = new HexCodeValidator; - this->hexEdits.clear(); - for (int i = 0; i < 16; i++) { - this->hexEdits.append(this->ui->container->findChild("hex_" + QString::number(i))); - this->hexEdits[i]->setValidator(hexValidator); - } - - // Connect to function that will update color when hex edit is changed - for (int i = 0; i < this->hexEdits.length(); i++) { - connect(this->hexEdits[i], &QLineEdit::textEdited, [this, i](QString text){ - if ((this->bitDepth == 24 && text.length() == 6) || (this->bitDepth == 15 && text.length() == 4)) setRgbFromHexEdit(i); - }); + this->colorInputs.clear(); + const int numColorsPerRow = 4; + for (int i = 0; i < this->numColors; i++) { + auto colorInput = new ColorInputWidget(QString("Color %1").arg(i)); + connect(colorInput, &ColorInputWidget::colorChanged, [this, i](QRgb color) { setRgb(i, color); }); + this->colorInputs.append(colorInput); + ui->layout_Colors->addWidget(colorInput, i / numColorsPerRow, i % numColorsPerRow); } // Setup edit-undo history for each of the palettes. @@ -82,11 +33,6 @@ PaletteEditor::PaletteEditor(Project *project, Tileset *primaryTileset, Tileset this->palettesHistory.append(History()); } - // Connect the color picker's selection to the correct color index - for (int i = 0; i < 16; i++) { - connect(this->pickButtons[i], &QToolButton::clicked, [this, i](){ this->pickColor(i); }); - } - int bitDepth = porymapConfig.paletteEditorBitDepth; if (bitDepth == 15) { this->ui->bit_depth_15->setChecked(true); @@ -107,229 +53,67 @@ PaletteEditor::PaletteEditor(Project *project, Tileset *primaryTileset, Tileset PaletteEditor::~PaletteEditor() { delete ui; - delete this->hexValidator; } -void PaletteEditor::updateColorUi(int colorIndex, QRgb rgb) { - setSignalsEnabled(false); - - int red = qRed(rgb); - int green = qGreen(rgb); - int blue = qBlue(rgb); - - if (this->bitDepth == 15) { - // sliders - this->sliders[colorIndex][0]->setValue(rgb5(red)); - this->sliders[colorIndex][1]->setValue(rgb5(green)); - this->sliders[colorIndex][2]->setValue(rgb5(blue)); - - // hex - int hex15 = (rgb5(blue) << 10) | (rgb5(green) << 5) | rgb5(red); - QString hexcode = QString("%1").arg(hex15, 4, 16, QLatin1Char('0')).toUpper(); - this->hexEdits[colorIndex]->setText(hexcode); - - // spinners - this->spinners[colorIndex][0]->setValue(rgb5(red)); - this->spinners[colorIndex][1]->setValue(rgb5(green)); - this->spinners[colorIndex][2]->setValue(rgb5(blue)); - } else { - // sliders - this->sliders[colorIndex][0]->setValue(red); - this->sliders[colorIndex][1]->setValue(green); - this->sliders[colorIndex][2]->setValue(blue); - - // hex - QColor color(red, green, blue); - QString hexcode = color.name().remove(0, 1).toUpper(); - this->hexEdits[colorIndex]->setText(hexcode); - - // spinners - this->spinners[colorIndex][0]->setValue(red); - this->spinners[colorIndex][1]->setValue(green); - this->spinners[colorIndex][2]->setValue(blue); - } - - // frame - QString stylesheet = QString("background-color: rgb(%1, %2, %3);").arg(red).arg(green).arg(blue); - this->frames[colorIndex]->setStyleSheet(stylesheet); - - setSignalsEnabled(true); -} - -void PaletteEditor::setSignalsEnabled(bool enabled) { - // spinners, sliders, hexbox - for (int i = 0; i < this->sliders.length(); i++) { - this->sliders.at(i).at(0)->blockSignals(!enabled); - this->sliders.at(i).at(1)->blockSignals(!enabled); - this->sliders.at(i).at(2)->blockSignals(!enabled); - } - - for (int i = 0; i < this->spinners.length(); i++) { - this->spinners.at(i).at(0)->blockSignals(!enabled); - this->spinners.at(i).at(1)->blockSignals(!enabled); - this->spinners.at(i).at(2)->blockSignals(!enabled); - } - - for (int i = 0; i < this->hexEdits.length(); i++) { - this->hexEdits.at(i)->blockSignals(!enabled); - } +Tileset* PaletteEditor::getTileset(int paletteId) { + return (paletteId < Project::getNumPalettesPrimary()) + ? this->primaryTileset + : this->secondaryTileset; } void PaletteEditor::setBitDepth(int bits) { - setSignalsEnabled(false); - switch (bits) { - case 15: - for (int i = 0; i < 16; i++) { - // sliders ranged [0, 31] with 1 single step and 4 page step - this->sliders[i][0]->setSingleStep(1); - this->sliders[i][1]->setSingleStep(1); - this->sliders[i][2]->setSingleStep(1); - this->sliders[i][0]->setPageStep(4); - this->sliders[i][1]->setPageStep(4); - this->sliders[i][2]->setPageStep(4); - this->sliders[i][0]->setMaximum(31); - this->sliders[i][1]->setMaximum(31); - this->sliders[i][2]->setMaximum(31); - - // spinners limited [0, 31] with 1 step - this->spinners[i][0]->setSingleStep(1); - this->spinners[i][1]->setSingleStep(1); - this->spinners[i][2]->setSingleStep(1); - this->spinners[i][0]->setMaximum(31); - this->spinners[i][1]->setMaximum(31); - this->spinners[i][2]->setMaximum(31); - - // hex box now 4 digits - this->hexEdits[i]->setInputMask("HHHH"); - this->hexEdits[i]->setMaxLength(4); - } - break; - case 24: - default: - for (int i = 0; i < 16; i++) { - // sliders ranged [0, 31] with 1 single step and 4 page step - this->sliders[i][0]->setSingleStep(8); - this->sliders[i][1]->setSingleStep(8); - this->sliders[i][2]->setSingleStep(8); - this->sliders[i][0]->setPageStep(24); - this->sliders[i][1]->setPageStep(24); - this->sliders[i][2]->setPageStep(24); - this->sliders[i][0]->setMaximum(255); - this->sliders[i][1]->setMaximum(255); - this->sliders[i][2]->setMaximum(255); - - // spinners limited [0, 31] with 1 step - this->spinners[i][0]->setSingleStep(8); - this->spinners[i][1]->setSingleStep(8); - this->spinners[i][2]->setSingleStep(8); - this->spinners[i][0]->setMaximum(255); - this->spinners[i][1]->setMaximum(255); - this->spinners[i][2]->setMaximum(255); - - // hex box now 4 digits - this->hexEdits[i]->setInputMask("HHHHHH"); - this->hexEdits[i]->setMaxLength(6); - } - break; - } this->bitDepth = bits; porymapConfig.paletteEditorBitDepth = bits; - refreshColorUis(); - setSignalsEnabled(true); + for (const auto &colorInput : this->colorInputs) { + colorInput->setBitDepth(bits); + } } void PaletteEditor::setRgb(int colorIndex, QRgb rgb) { - int paletteNum = this->ui->spinBox_PaletteId->value(); + const int paletteId = this->ui->spinBox_PaletteId->value(); - Tileset *tileset = paletteNum < Project::getNumPalettesPrimary() - ? this->primaryTileset - : this->secondaryTileset; - tileset->palettes[paletteNum][colorIndex] = rgb; - tileset->palettePreviews[paletteNum][colorIndex] = rgb; - - this->updateColorUi(colorIndex, rgb); + Tileset *tileset = getTileset(paletteId); + tileset->palettes[paletteId][colorIndex] = rgb; + tileset->palettePreviews[paletteId][colorIndex] = rgb; - this->commitEditHistory(paletteNum); - emit this->changedPaletteColor(); + commitEditHistory(paletteId); + emit changedPaletteColor(); } -void PaletteEditor::setRgbFromSliders(int colorIndex) { - if (this->bitDepth == 15) { - setRgb(colorIndex, qRgb(rgb8(this->sliders[colorIndex][0]->value()), - rgb8(this->sliders[colorIndex][1]->value()), - rgb8(this->sliders[colorIndex][2]->value()))); - } else { - setRgb(colorIndex, qRgb(this->sliders[colorIndex][0]->value(), - this->sliders[colorIndex][1]->value(), - this->sliders[colorIndex][2]->value())); +void PaletteEditor::setPalette(int paletteId, const QList &palette) { + Tileset *tileset = getTileset(paletteId); + for (int i = 0; i < this->numColors; i++) { + tileset->palettes[paletteId][i] = palette.at(i); + tileset->palettePreviews[paletteId][i] = palette.at(i); } + refreshColorInputs(); + emit changedPaletteColor(); } -void PaletteEditor::setRgbFromHexEdit(int colorIndex) { - QString text = this->hexEdits[colorIndex]->text(); - bool ok = false; - int rgb = text.toInt(&ok, 16); - if (!ok) rgb = 0xFFFFFFFF; - if (this->bitDepth == 15) { - int rc = gbaRed(rgb); - int gc = gbaGreen(rgb); - int bc = gbaBlue(rgb); - setRgb(colorIndex, qRgb(rgb8(rc), rgb8(gc), rgb8(bc))); - } else { - setRgb(colorIndex, qRgb(qRed(rgb), qGreen(rgb), qBlue(rgb))); - } -} - -void PaletteEditor::setRgbFromSpinners(int colorIndex) { - if (this->bitDepth == 15) { - setRgb(colorIndex, qRgb(rgb8(this->spinners[colorIndex][0]->value()), - rgb8(this->spinners[colorIndex][1]->value()), - rgb8(this->spinners[colorIndex][2]->value()))); - } else { - setRgb(colorIndex, qRgb(this->spinners[colorIndex][0]->value(), - this->spinners[colorIndex][1]->value(), - this->spinners[colorIndex][2]->value())); - } -} - -void PaletteEditor::refreshColorUis() { - int paletteNum = this->ui->spinBox_PaletteId->value(); - for (int i = 0; i < 16; i++) { - QRgb color; - if (paletteNum < Project::getNumPalettesPrimary()) { - color = this->primaryTileset->palettes.at(paletteNum).at(i); - } else { - color = this->secondaryTileset->palettes.at(paletteNum).at(i); - } - - this->updateColorUi(i, color); +void PaletteEditor::refreshColorInputs() { + const int paletteId = ui->spinBox_PaletteId->value(); + Tileset *tileset = getTileset(paletteId); + for (int i = 0; i < this->numColors; i++) { + auto colorInput = this->colorInputs.at(i); + const QSignalBlocker b(colorInput); + colorInput->setColor(tileset->palettes.at(paletteId).at(i)); } } void PaletteEditor::setPaletteId(int paletteId) { - this->ui->spinBox_PaletteId->blockSignals(true); + const QSignalBlocker b(ui->spinBox_PaletteId); this->ui->spinBox_PaletteId->setValue(paletteId); - this->refreshColorUis(); - this->ui->spinBox_PaletteId->blockSignals(false); + this->refreshColorInputs(); } void PaletteEditor::setTilesets(Tileset *primaryTileset, Tileset *secondaryTileset) { this->primaryTileset = primaryTileset; this->secondaryTileset = secondaryTileset; - this->refreshColorUis(); -} - -void PaletteEditor::pickColor(int index) { - ColorPicker picker(this); - if (picker.exec() == QDialog::Accepted) { - QColor c = picker.getColor(); - this->setRgb(index, c.rgb()); - } - return; + this->refreshColorInputs(); } void PaletteEditor::on_spinBox_PaletteId_valueChanged(int paletteId) { - this->refreshColorUis(); + this->refreshColorInputs(); if (!this->palettesHistory[paletteId].current()) { this->commitEditHistory(paletteId); } @@ -338,8 +122,8 @@ void PaletteEditor::on_spinBox_PaletteId_valueChanged(int paletteId) { void PaletteEditor::commitEditHistory(int paletteId) { QList colors; - for (int i = 0; i < 16; i++) { - colors.append(qRgb(this->spinners[i][0]->value(), this->spinners[i][1]->value(), this->spinners[i][2]->value())); + for (int i = 0; i < this->numColors; i++) { + colors.append(this->colorInputs.at(i)->color()); } PaletteHistoryItem *commit = new PaletteHistoryItem(colors); this->palettesHistory[paletteId].push(commit); @@ -356,31 +140,16 @@ void PaletteEditor::on_actionUndo_triggered() { int paletteId = this->ui->spinBox_PaletteId->value(); PaletteHistoryItem *prev = this->palettesHistory[paletteId].back(); - this->setColorsFromHistory(prev, paletteId); + if (prev) + setPalette(paletteId, prev->colors); } void PaletteEditor::on_actionRedo_triggered() { int paletteId = this->ui->spinBox_PaletteId->value(); PaletteHistoryItem *next = this->palettesHistory[paletteId].next(); - this->setColorsFromHistory(next, paletteId); -} - -void PaletteEditor::setColorsFromHistory(PaletteHistoryItem *history, int paletteId) { - if (!history) return; - - for (int i = 0; i < 16; i++) { - if (paletteId < Project::getNumPalettesPrimary()) { - this->primaryTileset->palettes[paletteId][i] = history->colors.at(i); - this->primaryTileset->palettePreviews[paletteId][i] = history->colors.at(i); - } else { - this->secondaryTileset->palettes[paletteId][i] = history->colors.at(i); - this->secondaryTileset->palettePreviews[paletteId][i] = history->colors.at(i); - } - } - - this->refreshColorUis(); - emit this->changedPaletteColor(); + if (next) + setPalette(paletteId, next->colors); } void PaletteEditor::on_actionImport_Palette_triggered() @@ -407,10 +176,12 @@ void PaletteEditor::on_actionImport_Palette_triggered() return; } - if (palette.length() < 16) { + if (palette.length() < this->numColors) { QMessageBox msgBox(this); msgBox.setText("Failed to import palette."); - QString message = QString("The palette file has %1 colors, but it must have 16 colors.").arg(palette.length()); + QString message = QString("The palette file has %1 colors, but it must have %2 colors.") + .arg(palette.length()) + .arg(this->numColors); msgBox.setInformativeText(message); msgBox.setDefaultButton(QMessageBox::Ok); msgBox.setIcon(QMessageBox::Icon::Critical); @@ -418,20 +189,9 @@ void PaletteEditor::on_actionImport_Palette_triggered() return; } - int paletteId = this->ui->spinBox_PaletteId->value(); - for (int i = 0; i < 16; i++) { - if (paletteId < Project::getNumPalettesPrimary()) { - this->primaryTileset->palettes[paletteId][i] = palette.at(i); - this->primaryTileset->palettePreviews[paletteId][i] = palette.at(i); - } else { - this->secondaryTileset->palettes[paletteId][i] = palette.at(i); - this->secondaryTileset->palettePreviews[paletteId][i] = palette.at(i); - } - } - - this->refreshColorUis(); - this->commitEditHistory(paletteId); - emit this->changedPaletteColor(); + const int paletteId = ui->spinBox_PaletteId->value(); + setPalette(paletteId, palette); + commitEditHistory(paletteId); } void PaletteEditor::closeEvent(QCloseEvent*) { From eefa46a2a2751dc454419e6cf0b2e94a76eb268e Mon Sep 17 00:00:00 2001 From: GriffinR Date: Tue, 1 Oct 2024 10:19:40 -0400 Subject: [PATCH 08/19] Fix Qt 5.15 build --- include/ui/gridsettings.h | 8 ++++---- src/ui/gridsettings.cpp | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/include/ui/gridsettings.h b/include/ui/gridsettings.h index 0a966e4c..807a0685 100644 --- a/include/ui/gridsettings.h +++ b/include/ui/gridsettings.h @@ -23,16 +23,16 @@ public: int offsetY = 0; Style style = Style::Solid; QColor color = Qt::black; - QList getHorizontalDashPattern() const { return this->getDashPattern(this->width); } - QList getVerticalDashPattern() const { return this->getDashPattern(this->height); } + QVector getHorizontalDashPattern() const { return this->getDashPattern(this->width); } + QVector getVerticalDashPattern() const { return this->getDashPattern(this->height); } static QString getStyleName(Style style); static GridSettings::Style getStyleFromName(const QString &name); private: static const QMap styleToName; - QList getCenteredDashPattern(uint length, qreal dashLength, qreal gapLength) const; - QList getDashPattern(uint length) const; + QVector getCenteredDashPattern(uint length, qreal dashLength, qreal gapLength) const; + QVector getDashPattern(uint length) const; }; inline bool operator==(const GridSettings &a, const GridSettings &b) { diff --git a/src/ui/gridsettings.cpp b/src/ui/gridsettings.cpp index 96494e83..b4e180af 100644 --- a/src/ui/gridsettings.cpp +++ b/src/ui/gridsettings.cpp @@ -22,7 +22,7 @@ GridSettings::Style GridSettings::getStyleFromName(const QString &name) { // We do some extra work here to A: try and center the dashes away from the intersections, and B: keep the dash pattern's total // length equal to the length of a grid square. This keeps the patterns looking reasonable regardless of the grid size. // Otherwise, the dashes can start to intersect in weird ways and create grid patterns that don't look like a rectangular grid. -QList GridSettings::getCenteredDashPattern(uint length, qreal dashLength, qreal gapLength) const { +QVector GridSettings::getCenteredDashPattern(uint length, qreal dashLength, qreal gapLength) const { const qreal minEdgesLength = 0.6*2; if (length <= dashLength + minEdgesLength) return {dashLength}; @@ -36,7 +36,7 @@ QList GridSettings::getCenteredDashPattern(uint length, qreal dashLength, const qreal edgeLength = (length - mainLength) / 2; // Fill the pattern - QList pattern = {0, edgeLength}; + QVector pattern = {0, edgeLength}; for (int i = 0; i < numDashes-1; i++) { pattern.append(dashLength); pattern.append(gapLength); @@ -47,7 +47,7 @@ QList GridSettings::getCenteredDashPattern(uint length, qreal dashLength, return pattern; } -QList GridSettings::getDashPattern(uint length) const { +QVector GridSettings::getDashPattern(uint length) const { switch (this->style) { // Equivalent to setting Qt::PenStyle::Solid with no dash pattern. From ae6312c131188adce506a0c9a671b09518c06b17 Mon Sep 17 00:00:00 2001 From: GriffinR Date: Wed, 2 Oct 2024 01:44:09 -0400 Subject: [PATCH 09/19] Add actual preview to Export Map Stitch Image --- forms/mapimageexporter.ui | 632 +++++++++++++++++----------------- include/ui/mapimageexporter.h | 8 + src/ui/mapimageexporter.cpp | 86 +++-- 3 files changed, 387 insertions(+), 339 deletions(-) diff --git a/forms/mapimageexporter.ui b/forms/mapimageexporter.ui index d97099a7..ddce54a1 100644 --- a/forms/mapimageexporter.ui +++ b/forms/mapimageexporter.ui @@ -6,8 +6,8 @@ 0 0 - 696 - 396 + 817 + 518 @@ -16,15 +16,299 @@ true - - - + + + + + + 0 + 0 + + + + + + + + + Map + + + + + + + QComboBox::SizeAdjustPolicy::AdjustToContents + + + + + + + + + Events + + + + + + + + Warps + + + + + + + Objects + + + + + + + BGs + + + + + + + Triggers + + + + + + + Heal Spots + + + + + + + + + + + + Connections + + + + + + + + Up + + + + + + + Down + + + + + + + Left + + + + + + + Right + + + + + + + + + + + + Miscellaneous + + + + + + + + Grid + + + + + + + Collision + + + + + + + Border + + + + + + + + + + + + Timelapse + + + + + + + + + ms + + + 1 + + + 2000 + + + 200 + + + + + + + Frame Delay + + + + + + + + + + 1 + + + 999 + + + + + + + Edit Frame Skip + + + + + + + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + + + + + Preview actual size + + + + + + + + + Reset + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + Cancel + + + + + + + Save + + + + + + + + + + + + + 0 + 0 + + Preview + + 6 + + + 6 + + + 6 + + + 6 + - + true @@ -33,12 +317,24 @@ 0 0 - 403 - 343 + 500 + 439 - + + 0 + + + 0 + + + 0 + + + 0 + + @@ -53,65 +349,13 @@ false - QAbstractScrollArea::AdjustIgnored + QAbstractScrollArea::SizeAdjustPolicy::AdjustIgnored - QGraphicsView::NoDrag + QGraphicsView::DragMode::NoDrag - - - - Qt::Vertical - - - - 10 - 100 - - - - - - - - Qt::Vertical - - - - 10 - 100 - - - - - - - - Qt::Horizontal - - - - 100 - 10 - - - - - - - - Qt::Horizontal - - - - 100 - 10 - - - - @@ -119,256 +363,18 @@ - - - - - - - - Map - - - - - - - QComboBox::AdjustToContents - - - - - - - - - Events - - - - - - - - Warps - - - - - - - Objects - - - - - - - BGs - - - - - - - Triggers - - - - - - - Heal Spots - - - - - - - - - - - - Connections - - - - - - - - Up - - - - - - - Down - - - - - - - Left - - - - - - - Right - - - - - - - - - - - - Miscellaneous - - - - - - - - Grid - - - - - - - Collision - - - - - - - Border - - - - - - - - - - - - Timelapse - - - - - - - - - ms - - - 1 - - - 2000 - - - 200 - - - - - - - Frame Delay - - - - - - - - - - 1 - - - 999 - - - - - - - Edit Frame Skip - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - Reset - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Cancel - - - - - - - Save - - - - - - + + + + + + + Qt::AlignmentFlag::AlignCenter + + + true + + diff --git a/include/ui/mapimageexporter.h b/include/ui/mapimageexporter.h index b9cc20bd..28f404c2 100644 --- a/include/ui/mapimageexporter.h +++ b/include/ui/mapimageexporter.h @@ -45,17 +45,23 @@ private: bool showGrid = false; bool showBorder = false; bool showCollision = false; + bool previewActualSize = false; int timelapseSkipAmount = 1; int timelapseDelayMs = 200; ImageExporterMode mode = ImageExporterMode::Normal; void updatePreview(); + void scalePreview(); void updateShowBorderState(); void saveImage(); QPixmap getStitchedImage(QProgressDialog *progress, bool includeBorder); QPixmap getFormattedMapPixmap(Map *map, bool ignoreBorder = false); bool historyItemAppliesToFrame(const QUndoCommand *command); +protected: + virtual void showEvent(QShowEvent *) override; + virtual void resizeEvent(QResizeEvent *) override; + private slots: void on_checkBox_Objects_stateChanged(int state); void on_checkBox_Warps_stateChanged(int state); @@ -77,6 +83,8 @@ private slots: void on_pushButton_Cancel_pressed(); void on_spinBox_TimelapseDelay_valueChanged(int delayMs); void on_spinBox_FrameSkip_valueChanged(int skip); + + void on_checkBox_ActualSize_stateChanged(int state); }; #endif // MAPIMAGEEXPORTER_H diff --git a/src/ui/mapimageexporter.cpp b/src/ui/mapimageexporter.cpp index 10194b8b..e9c3e18c 100644 --- a/src/ui/mapimageexporter.cpp +++ b/src/ui/mapimageexporter.cpp @@ -23,6 +23,19 @@ QString getTitle(ImageExporterMode mode) { return ""; } +QString getDescription(ImageExporterMode mode) { + switch (mode) + { + case ImageExporterMode::Normal: + return "Exports an image of the selected map."; + case ImageExporterMode::Stitch: + return "Exports a combined image of all the maps connected to the selected map."; + case ImageExporterMode::Timelapse: + return "Exports a GIF of the edit history for the selected map."; + } + return ""; +} + MapImageExporter::MapImageExporter(QWidget *parent_, Editor *editor_, ImageExporterMode mode) : QDialog(parent_), ui(new Ui::MapImageExporter) @@ -33,14 +46,13 @@ MapImageExporter::MapImageExporter(QWidget *parent_, Editor *editor_, ImageExpor this->editor = editor_; this->mode = mode; this->setWindowTitle(getTitle(this->mode)); + this->ui->label_Description->setText(getDescription(this->mode)); this->ui->groupBox_Connections->setVisible(this->mode != ImageExporterMode::Stitch); this->ui->groupBox_Timelapse->setVisible(this->mode == ImageExporterMode::Timelapse); this->ui->comboBox_MapSelection->addItems(editor->project->mapNames); this->ui->comboBox_MapSelection->setCurrentText(map->name); this->ui->comboBox_MapSelection->setEnabled(false);// TODO: allow selecting map from drop-down - - updatePreview(); } MapImageExporter::~MapImageExporter() { @@ -48,6 +60,18 @@ MapImageExporter::~MapImageExporter() { delete ui; } +// Allow the window to open before displaying the preview. +void MapImageExporter::showEvent(QShowEvent *event) { + QWidget::showEvent(event); + if (!event->spontaneous()) + QTimer::singleShot(0, this, &MapImageExporter::updatePreview); +} + +void MapImageExporter::resizeEvent(QResizeEvent *event) { + QDialog::resizeEvent(event); + scalePreview(); +} + void MapImageExporter::saveImage() { QString title = getTitle(this->mode); QString defaultFilename; @@ -74,22 +98,10 @@ void MapImageExporter::saveImage() { editor->project->setImportExportPath(filepath); switch (this->mode) { case ImageExporterMode::Normal: + case ImageExporterMode::Stitch: + // Normal and Stitch modes already have the image ready to go in the preview. this->preview.save(filepath); break; - case ImageExporterMode::Stitch: { - QProgressDialog progress("Building map stitch...", "Cancel", 0, 1, this); - progress.setAutoClose(true); - progress.setWindowModality(Qt::WindowModal); - progress.setModal(true); - QPixmap pixmap = this->getStitchedImage(&progress, this->showBorder); - if (progress.wasCanceled()) { - progress.close(); - return; - } - pixmap.save(filepath); - progress.close(); - break; - } case ImageExporterMode::Timelapse: QProgressDialog progress("Building map timelapse...", "Cancel", 0, 1, this); progress.setAutoClose(true); @@ -354,19 +366,32 @@ QPixmap MapImageExporter::getStitchedImage(QProgressDialog *progress, bool inclu } void MapImageExporter::updatePreview() { - if (scene) { - delete scene; - scene = nullptr; + if (this->scene) { + delete this->scene; + this->scene = nullptr; } + this->scene = new QGraphicsScene; - preview = getFormattedMapPixmap(this->map); - scene = new QGraphicsScene; - scene->addPixmap(preview); - this->scene->setSceneRect(this->scene->itemsBoundingRect()); + if (this->mode == ImageExporterMode::Stitch) { + QProgressDialog progress("Building map stitch...", "Cancel", 0, 1, this); + progress.setAutoClose(true); + progress.setWindowModality(Qt::WindowModal); + progress.setModal(true); + this->preview = getStitchedImage(&progress, this->showBorder); + progress.close(); + } else { + // Timelapse mode doesn't currently have a real preview. It just displays the current map as in Normal mode. + this->preview = getFormattedMapPixmap(this->map); + } + this->scene->addPixmap(this->preview); + ui->graphicsView_Preview->setScene(scene); + scalePreview(); +} - this->ui->graphicsView_Preview->setScene(scene); - this->ui->graphicsView_Preview->setFixedSize(scene->itemsBoundingRect().width() + 2, - scene->itemsBoundingRect().height() + 2); +void MapImageExporter::scalePreview() { + if (this->scene && !this->previewActualSize){ + ui->graphicsView_Preview->fitInView(this->scene->sceneRect(), Qt::KeepAspectRatioByExpanding); + } } QPixmap MapImageExporter::getFormattedMapPixmap(Map *map, bool ignoreBorder) { @@ -539,6 +564,15 @@ void MapImageExporter::on_checkBox_ConnectionRight_stateChanged(int state) { updatePreview(); } +void MapImageExporter::on_checkBox_ActualSize_stateChanged(int state) { + previewActualSize = (state == Qt::Checked); + if (previewActualSize) { + ui->graphicsView_Preview->resetTransform(); + } else { + scalePreview(); + } +} + void MapImageExporter::on_pushButton_Save_pressed() { saveImage(); } From 354680d125c9ea192f1614d5769dbce09d82d413 Mon Sep 17 00:00:00 2001 From: GriffinR Date: Wed, 2 Oct 2024 02:44:33 -0400 Subject: [PATCH 10/19] Reduce time minimum for stitch image progress bar --- src/ui/mapimageexporter.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ui/mapimageexporter.cpp b/src/ui/mapimageexporter.cpp index e9c3e18c..b06bb415 100644 --- a/src/ui/mapimageexporter.cpp +++ b/src/ui/mapimageexporter.cpp @@ -377,6 +377,7 @@ void MapImageExporter::updatePreview() { progress.setAutoClose(true); progress.setWindowModality(Qt::WindowModal); progress.setModal(true); + progress.setMinimumDuration(1000); this->preview = getStitchedImage(&progress, this->showBorder); progress.close(); } else { From 6e79c6c4c3ccb9e1f4282d2702ada25bd562cfe8 Mon Sep 17 00:00:00 2001 From: GriffinR Date: Thu, 3 Oct 2024 20:26:27 -0400 Subject: [PATCH 11/19] Add 'All' settings, avoid unnecessary work in a few places --- forms/mapimageexporter.ui | 88 ++++++++++++--------- include/ui/mapimageexporter.h | 8 +- src/ui/mapimageexporter.cpp | 145 ++++++++++++++++++++++++---------- 3 files changed, 160 insertions(+), 81 deletions(-) diff --git a/forms/mapimageexporter.ui b/forms/mapimageexporter.ui index ddce54a1..933aab67 100644 --- a/forms/mapimageexporter.ui +++ b/forms/mapimageexporter.ui @@ -7,7 +7,7 @@ 0 0 817 - 518 + 535 @@ -52,7 +52,28 @@ + + + + Triggers + + + + + + Objects + + + + + + + Heal Locations + + + + Warps @@ -60,30 +81,16 @@ - + - Objects - - - - - - - BGs + All - + - Triggers - - - - - - - Heal Spots + BGs @@ -100,34 +107,41 @@ - - - - Up - - - - - - - Down - - - - + Left - + + + + Up + + + + Right + + + + Down + + + + + + + All + + + @@ -317,8 +331,8 @@ 0 0 - 500 - 439 + 469 + 464 diff --git a/include/ui/mapimageexporter.h b/include/ui/mapimageexporter.h index 28f404c2..cadb616f 100644 --- a/include/ui/mapimageexporter.h +++ b/include/ui/mapimageexporter.h @@ -37,7 +37,7 @@ private: bool showWarps = false; bool showBGs = false; bool showTriggers = false; - bool showHealSpots = false; + bool showHealLocations = false; bool showUpConnections = false; bool showDownConnections = false; bool showLeftConnections = false; @@ -67,20 +67,20 @@ private slots: void on_checkBox_Warps_stateChanged(int state); void on_checkBox_BGs_stateChanged(int state); void on_checkBox_Triggers_stateChanged(int state); - void on_checkBox_HealSpots_stateChanged(int state); + void on_checkBox_HealLocations_stateChanged(int state); + void on_checkBox_AllEvents_stateChanged(int state); void on_checkBox_ConnectionUp_stateChanged(int state); void on_checkBox_ConnectionDown_stateChanged(int state); void on_checkBox_ConnectionLeft_stateChanged(int state); void on_checkBox_ConnectionRight_stateChanged(int state); + void on_checkBox_AllConnections_stateChanged(int state); void on_checkBox_Elevation_stateChanged(int state); void on_checkBox_Grid_stateChanged(int state); void on_checkBox_Border_stateChanged(int state); - void on_pushButton_Save_pressed(); void on_pushButton_Reset_pressed(); - void on_pushButton_Cancel_pressed(); void on_spinBox_TimelapseDelay_valueChanged(int delayMs); void on_spinBox_FrameSkip_valueChanged(int skip); diff --git a/src/ui/mapimageexporter.cpp b/src/ui/mapimageexporter.cpp index b06bb415..437b4cac 100644 --- a/src/ui/mapimageexporter.cpp +++ b/src/ui/mapimageexporter.cpp @@ -53,6 +53,9 @@ MapImageExporter::MapImageExporter(QWidget *parent_, Editor *editor_, ImageExpor this->ui->comboBox_MapSelection->addItems(editor->project->mapNames); this->ui->comboBox_MapSelection->setCurrentText(map->name); this->ui->comboBox_MapSelection->setEnabled(false);// TODO: allow selecting map from drop-down + + connect(ui->pushButton_Save, &QPushButton::pressed, this, &MapImageExporter::saveImage); + connect(ui->pushButton_Cancel, &QPushButton::pressed, this, &MapImageExporter::close); } MapImageExporter::~MapImageExporter() { @@ -73,6 +76,12 @@ void MapImageExporter::resizeEvent(QResizeEvent *event) { } void MapImageExporter::saveImage() { + // Make sure preview is up-to-date before we save. + if (this->preview.isNull()) + updatePreview(); + if (this->preview.isNull()) + return; + QString title = getTitle(this->mode); QString defaultFilename; switch (this->mode) @@ -219,11 +228,11 @@ bool MapImageExporter::historyItemAppliesToFrame(const QUndoCommand *command) { case CommandId::ID_EventDelete: case CommandId::ID_EventDuplicate: { bool eventTypeIsApplicable = - (this->showObjects && (command->id() & IDMask_EventType_Object) != 0) - || (this->showWarps && (command->id() & IDMask_EventType_Warp) != 0) - || (this->showBGs && (command->id() & IDMask_EventType_BG) != 0) - || (this->showTriggers && (command->id() & IDMask_EventType_Trigger) != 0) - || (this->showHealSpots && (command->id() & IDMask_EventType_Heal) != 0); + (this->showObjects && (command->id() & IDMask_EventType_Object) != 0) + || (this->showWarps && (command->id() & IDMask_EventType_Warp) != 0) + || (this->showBGs && (command->id() & IDMask_EventType_BG) != 0) + || (this->showTriggers && (command->id() & IDMask_EventType_Trigger) != 0) + || (this->showHealLocations && (command->id() & IDMask_EventType_Heal) != 0); return eventTypeIsApplicable; } default: @@ -435,6 +444,7 @@ QPixmap MapImageExporter::getFormattedMapPixmap(Map *map, bool ignoreBorder) { if (!ignoreBorder && (this->showUpConnections || this->showDownConnections || this->showLeftConnections || this->showRightConnections)) { // if showing connections, draw on outside of image QPainter connectionPainter(&pixmap); + // TODO: Reading the connections from the editor and not 'map' is incorrect. for (auto connectionItem : editor->connection_items) { const QString direction = connectionItem->connection->direction(); if ((showUpConnections && direction == "up") @@ -448,27 +458,30 @@ QPixmap MapImageExporter::getFormattedMapPixmap(Map *map, bool ignoreBorder) { } // draw events - QPainter eventPainter(&pixmap); - QList events = map->getAllEvents(); - int pixelOffset = 0; - if (!ignoreBorder && this->showBorder) { - pixelOffset = this->mode == ImageExporterMode::Normal ? BORDER_DISTANCE * 16 : STITCH_MODE_BORDER_DISTANCE * 16; + if (this->showObjects || this->showWarps || this->showBGs || this->showTriggers || this->showHealLocations) { + QPainter eventPainter(&pixmap); + int pixelOffset = 0; + if (!ignoreBorder && this->showBorder) { + pixelOffset = this->mode == ImageExporterMode::Normal ? BORDER_DISTANCE * 16 : STITCH_MODE_BORDER_DISTANCE * 16; + } + const QList events = map->getAllEvents(); + for (const auto &event : events) { + Event::Group group = event->getEventGroup(); + if ((this->showObjects && group == Event::Group::Object) + || (this->showWarps && group == Event::Group::Warp) + || (this->showBGs && group == Event::Group::Bg) + || (this->showTriggers && group == Event::Group::Coord) + || (this->showHealLocations && group == Event::Group::Heal)) { + editor->project->setEventPixmap(event); + eventPainter.drawImage(QPoint(event->getPixelX() + pixelOffset, event->getPixelY() + pixelOffset), event->getPixmap().toImage()); + } + } + eventPainter.end(); } - for (Event *event : events) { - editor->project->setEventPixmap(event); - Event::Group group = event->getEventGroup(); - if ((showObjects && group == Event::Group::Object) - || (showWarps && group == Event::Group::Warp) - || (showBGs && group == Event::Group::Bg) - || (showTriggers && group == Event::Group::Coord) - || (showHealSpots && group == Event::Group::Heal)) - eventPainter.drawImage(QPoint(event->getPixelX() + pixelOffset, event->getPixelY() + pixelOffset), event->getPixmap().toImage()); - } - eventPainter.end(); // draw grid directly onto the pixmap // since the last grid lines are outside of the pixmap, add a pixel to the bottom and right - if (showGrid) { + if (this->showGrid) { int addX = 1, addY = 1; if (borderHeight) addY = 0; if (borderWidth) addX = 0; @@ -491,14 +504,11 @@ QPixmap MapImageExporter::getFormattedMapPixmap(Map *map, bool ignoreBorder) { void MapImageExporter::updateShowBorderState() { // If any of the Connections settings are enabled then this setting is locked (it's implicitly enabled) + bool on = (showUpConnections || showDownConnections || showLeftConnections || showRightConnections); const QSignalBlocker blocker(ui->checkBox_Border); - if (showUpConnections || showDownConnections || showLeftConnections || showRightConnections) { - ui->checkBox_Border->setChecked(true); - ui->checkBox_Border->setDisabled(true); - showBorder = true; - } else { - ui->checkBox_Border->setDisabled(false); - } + ui->checkBox_Border->setChecked(on); + ui->checkBox_Border->setDisabled(on); + showBorder = on; } void MapImageExporter::on_checkBox_Elevation_stateChanged(int state) { @@ -536,8 +546,40 @@ void MapImageExporter::on_checkBox_Triggers_stateChanged(int state) { updatePreview(); } -void MapImageExporter::on_checkBox_HealSpots_stateChanged(int state) { - showHealSpots = (state == Qt::Checked); +void MapImageExporter::on_checkBox_HealLocations_stateChanged(int state) { + showHealLocations = (state == Qt::Checked); + updatePreview(); +} + +// Shortcut setting for enabling all events +void MapImageExporter::on_checkBox_AllEvents_stateChanged(int state) { + bool on = (state == Qt::Checked); + + const QSignalBlocker b_Objects(ui->checkBox_Objects); + ui->checkBox_Objects->setChecked(on); + ui->checkBox_Objects->setDisabled(on); + showObjects = on; + + const QSignalBlocker b_Warps(ui->checkBox_Warps); + ui->checkBox_Warps->setChecked(on); + ui->checkBox_Warps->setDisabled(on); + showWarps = on; + + const QSignalBlocker b_BGs(ui->checkBox_BGs); + ui->checkBox_BGs->setChecked(on); + ui->checkBox_BGs->setDisabled(on); + showBGs = on; + + const QSignalBlocker b_Triggers(ui->checkBox_Triggers); + ui->checkBox_Triggers->setChecked(on); + ui->checkBox_Triggers->setDisabled(on); + showTriggers = on; + + const QSignalBlocker b_HealLocations(ui->checkBox_HealLocations); + ui->checkBox_HealLocations->setChecked(on); + ui->checkBox_HealLocations->setDisabled(on); + showHealLocations = on; + updatePreview(); } @@ -565,6 +607,34 @@ void MapImageExporter::on_checkBox_ConnectionRight_stateChanged(int state) { updatePreview(); } +// Shortcut setting for enabling all connection directions +void MapImageExporter::on_checkBox_AllConnections_stateChanged(int state) { + bool on = (state == Qt::Checked); + + const QSignalBlocker b_Up(ui->checkBox_ConnectionUp); + ui->checkBox_ConnectionUp->setChecked(on); + ui->checkBox_ConnectionUp->setDisabled(on); + showUpConnections = on; + + const QSignalBlocker b_Down(ui->checkBox_ConnectionDown); + ui->checkBox_ConnectionDown->setChecked(on); + ui->checkBox_ConnectionDown->setDisabled(on); + showDownConnections = on; + + const QSignalBlocker b_Left(ui->checkBox_ConnectionLeft); + ui->checkBox_ConnectionLeft->setChecked(on); + ui->checkBox_ConnectionLeft->setDisabled(on); + showLeftConnections = on; + + const QSignalBlocker b_Right(ui->checkBox_ConnectionRight); + ui->checkBox_ConnectionRight->setChecked(on); + ui->checkBox_ConnectionRight->setDisabled(on); + showRightConnections = on; + + updateShowBorderState(); + updatePreview(); +} + void MapImageExporter::on_checkBox_ActualSize_stateChanged(int state) { previewActualSize = (state == Qt::Checked); if (previewActualSize) { @@ -574,17 +644,12 @@ void MapImageExporter::on_checkBox_ActualSize_stateChanged(int state) { } } -void MapImageExporter::on_pushButton_Save_pressed() { - saveImage(); -} - void MapImageExporter::on_pushButton_Reset_pressed() { - for (auto widget : this->findChildren()) + for (auto widget : this->findChildren()) { + const QSignalBlocker b(widget); widget->setChecked(false); -} - -void MapImageExporter::on_pushButton_Cancel_pressed() { - this->close(); + } + updatePreview(); } void MapImageExporter::on_spinBox_TimelapseDelay_valueChanged(int delayMs) { From bdcfc0467b16890d510fbb3ead06ebb43c8366d6 Mon Sep 17 00:00:00 2001 From: GriffinR Date: Fri, 4 Oct 2024 03:45:47 -0400 Subject: [PATCH 12/19] Fix image exporter reset button --- include/ui/mapimageexporter.h | 34 +++++----- src/ui/mapimageexporter.cpp | 117 +++++++++++++++++----------------- 2 files changed, 79 insertions(+), 72 deletions(-) diff --git a/include/ui/mapimageexporter.h b/include/ui/mapimageexporter.h index cadb616f..82dae443 100644 --- a/include/ui/mapimageexporter.h +++ b/include/ui/mapimageexporter.h @@ -16,6 +16,24 @@ enum ImageExporterMode { Timelapse, }; +struct ImageExporterSettings { + bool showObjects = false; + bool showWarps = false; + bool showBGs = false; + bool showTriggers = false; + bool showHealLocations = false; + bool showUpConnections = false; + bool showDownConnections = false; + bool showLeftConnections = false; + bool showRightConnections = false; + bool showGrid = false; + bool showBorder = false; + bool showCollision = false; + bool previewActualSize = false; + int timelapseSkipAmount = 1; + int timelapseDelayMs = 200; +}; + class MapImageExporter : public QDialog { Q_OBJECT @@ -33,21 +51,7 @@ private: QPixmap preview; - bool showObjects = false; - bool showWarps = false; - bool showBGs = false; - bool showTriggers = false; - bool showHealLocations = false; - bool showUpConnections = false; - bool showDownConnections = false; - bool showLeftConnections = false; - bool showRightConnections = false; - bool showGrid = false; - bool showBorder = false; - bool showCollision = false; - bool previewActualSize = false; - int timelapseSkipAmount = 1; - int timelapseDelayMs = 200; + ImageExporterSettings settings; ImageExporterMode mode = ImageExporterMode::Normal; void updatePreview(); diff --git a/src/ui/mapimageexporter.cpp b/src/ui/mapimageexporter.cpp index 437b4cac..c812c5f8 100644 --- a/src/ui/mapimageexporter.cpp +++ b/src/ui/mapimageexporter.cpp @@ -121,7 +121,7 @@ void MapImageExporter::saveImage() { int maxWidth = this->map->getWidth() * 16; int maxHeight = this->map->getHeight() * 16; - if (showBorder) { + if (this->settings.showBorder) { maxWidth += 2 * STITCH_MODE_BORDER_DISTANCE * 16; maxHeight += 2 * STITCH_MODE_BORDER_DISTANCE * 16; } @@ -132,7 +132,7 @@ void MapImageExporter::saveImage() { this->map->editHistory.undo(); int width = this->map->getWidth() * 16; int height = this->map->getHeight() * 16; - if (showBorder) { + if (this->settings.showBorder) { width += 2 * STITCH_MODE_BORDER_DISTANCE * 16; height += 2 * STITCH_MODE_BORDER_DISTANCE * 16; } @@ -145,7 +145,7 @@ void MapImageExporter::saveImage() { i++; } QGifImage timelapseImg(QSize(maxWidth, maxHeight)); - timelapseImg.setDefaultDelay(timelapseDelayMs); + timelapseImg.setDefaultDelay(this->settings.timelapseDelayMs); timelapseImg.setDefaultTransparentColor(QColor(0, 0, 0)); // Draw each frame, skpping the specified number of map edits in // the undo history. @@ -175,7 +175,7 @@ void MapImageExporter::saveImage() { pixmap = pixmap2; } timelapseImg.addFrame(pixmap.toImage()); - for (int j = 0; j < timelapseSkipAmount; j++) { + for (int j = 0; j < this->settings.timelapseSkipAmount; j++) { if (i > 0) { i--; this->map->editHistory.redo(); @@ -213,26 +213,26 @@ bool MapImageExporter::historyItemAppliesToFrame(const QUndoCommand *command) { case CommandId::ID_PaintCollision: case CommandId::ID_BucketFillCollision: case CommandId::ID_MagicFillCollision: - return this->showCollision; + return this->settings.showCollision; case CommandId::ID_PaintBorder: - return this->showBorder; + return this->settings.showBorder; case CommandId::ID_MapConnectionMove: case CommandId::ID_MapConnectionChangeDirection: case CommandId::ID_MapConnectionChangeMap: case CommandId::ID_MapConnectionAdd: case CommandId::ID_MapConnectionRemove: - return this->showUpConnections || this->showDownConnections || this->showLeftConnections || this->showRightConnections; + return this->settings.showUpConnections || this->settings.showDownConnections || this->settings.showLeftConnections || this->settings.showRightConnections; case CommandId::ID_EventMove: case CommandId::ID_EventShift: case CommandId::ID_EventCreate: case CommandId::ID_EventDelete: case CommandId::ID_EventDuplicate: { bool eventTypeIsApplicable = - (this->showObjects && (command->id() & IDMask_EventType_Object) != 0) - || (this->showWarps && (command->id() & IDMask_EventType_Warp) != 0) - || (this->showBGs && (command->id() & IDMask_EventType_BG) != 0) - || (this->showTriggers && (command->id() & IDMask_EventType_Trigger) != 0) - || (this->showHealLocations && (command->id() & IDMask_EventType_Heal) != 0); + (this->settings.showObjects && (command->id() & IDMask_EventType_Object) != 0) + || (this->settings.showWarps && (command->id() & IDMask_EventType_Warp) != 0) + || (this->settings.showBGs && (command->id() & IDMask_EventType_BG) != 0) + || (this->settings.showTriggers && (command->id() & IDMask_EventType_Trigger) != 0) + || (this->settings.showHealLocations && (command->id() & IDMask_EventType_Heal) != 0); return eventTypeIsApplicable; } default: @@ -387,7 +387,7 @@ void MapImageExporter::updatePreview() { progress.setWindowModality(Qt::WindowModal); progress.setModal(true); progress.setMinimumDuration(1000); - this->preview = getStitchedImage(&progress, this->showBorder); + this->preview = getStitchedImage(&progress, this->settings.showBorder); progress.close(); } else { // Timelapse mode doesn't currently have a real preview. It just displays the current map as in Normal mode. @@ -399,7 +399,7 @@ void MapImageExporter::updatePreview() { } void MapImageExporter::scalePreview() { - if (this->scene && !this->previewActualSize){ + if (this->scene && !this->settings.previewActualSize){ ui->graphicsView_Preview->fitInView(this->scene->sceneRect(), Qt::KeepAspectRatioByExpanding); } } @@ -411,7 +411,7 @@ QPixmap MapImageExporter::getFormattedMapPixmap(Map *map, bool ignoreBorder) { map->render(true); pixmap = map->pixmap; - if (showCollision) { + if (this->settings.showCollision) { QPainter collisionPainter(&pixmap); map->renderCollision(true); collisionPainter.setOpacity(editor->collisionOpacity); @@ -422,7 +422,7 @@ QPixmap MapImageExporter::getFormattedMapPixmap(Map *map, bool ignoreBorder) { // draw map border // note: this will break when allowing map to be selected from drop down maybe int borderHeight = 0, borderWidth = 0; - if (!ignoreBorder && this->showBorder) { + if (!ignoreBorder && this->settings.showBorder) { int borderDistance = this->mode ? STITCH_MODE_BORDER_DISTANCE : BORDER_DISTANCE; map->renderBorder(); int borderHorzDist = editor->getBorderDrawDistance(map->getBorderWidth()); @@ -441,16 +441,16 @@ QPixmap MapImageExporter::getFormattedMapPixmap(Map *map, bool ignoreBorder) { pixmap = newPixmap; } - if (!ignoreBorder && (this->showUpConnections || this->showDownConnections || this->showLeftConnections || this->showRightConnections)) { + if (!ignoreBorder && (this->settings.showUpConnections || this->settings.showDownConnections || this->settings.showLeftConnections || this->settings.showRightConnections)) { // if showing connections, draw on outside of image QPainter connectionPainter(&pixmap); // TODO: Reading the connections from the editor and not 'map' is incorrect. for (auto connectionItem : editor->connection_items) { const QString direction = connectionItem->connection->direction(); - if ((showUpConnections && direction == "up") - || (showDownConnections && direction == "down") - || (showLeftConnections && direction == "left") - || (showRightConnections && direction == "right")) + if ((this->settings.showUpConnections && direction == "up") + || (this->settings.showDownConnections && direction == "down") + || (this->settings.showLeftConnections && direction == "left") + || (this->settings.showRightConnections && direction == "right")) connectionPainter.drawImage(connectionItem->x() + borderWidth, connectionItem->y() + borderHeight, connectionItem->connection->getPixmap().toImage()); } @@ -458,20 +458,20 @@ QPixmap MapImageExporter::getFormattedMapPixmap(Map *map, bool ignoreBorder) { } // draw events - if (this->showObjects || this->showWarps || this->showBGs || this->showTriggers || this->showHealLocations) { + if (this->settings.showObjects || this->settings.showWarps || this->settings.showBGs || this->settings.showTriggers || this->settings.showHealLocations) { QPainter eventPainter(&pixmap); int pixelOffset = 0; - if (!ignoreBorder && this->showBorder) { + if (!ignoreBorder && this->settings.showBorder) { pixelOffset = this->mode == ImageExporterMode::Normal ? BORDER_DISTANCE * 16 : STITCH_MODE_BORDER_DISTANCE * 16; } const QList events = map->getAllEvents(); for (const auto &event : events) { Event::Group group = event->getEventGroup(); - if ((this->showObjects && group == Event::Group::Object) - || (this->showWarps && group == Event::Group::Warp) - || (this->showBGs && group == Event::Group::Bg) - || (this->showTriggers && group == Event::Group::Coord) - || (this->showHealLocations && group == Event::Group::Heal)) { + if ((this->settings.showObjects && group == Event::Group::Object) + || (this->settings.showWarps && group == Event::Group::Warp) + || (this->settings.showBGs && group == Event::Group::Bg) + || (this->settings.showTriggers && group == Event::Group::Coord) + || (this->settings.showHealLocations && group == Event::Group::Heal)) { editor->project->setEventPixmap(event); eventPainter.drawImage(QPoint(event->getPixelX() + pixelOffset, event->getPixelY() + pixelOffset), event->getPixmap().toImage()); } @@ -481,7 +481,7 @@ QPixmap MapImageExporter::getFormattedMapPixmap(Map *map, bool ignoreBorder) { // draw grid directly onto the pixmap // since the last grid lines are outside of the pixmap, add a pixel to the bottom and right - if (this->showGrid) { + if (this->settings.showGrid) { int addX = 1, addY = 1; if (borderHeight) addY = 0; if (borderWidth) addX = 0; @@ -504,50 +504,50 @@ QPixmap MapImageExporter::getFormattedMapPixmap(Map *map, bool ignoreBorder) { void MapImageExporter::updateShowBorderState() { // If any of the Connections settings are enabled then this setting is locked (it's implicitly enabled) - bool on = (showUpConnections || showDownConnections || showLeftConnections || showRightConnections); + bool on = (this->settings.showUpConnections || this->settings.showDownConnections || this->settings.showLeftConnections || this->settings.showRightConnections); const QSignalBlocker blocker(ui->checkBox_Border); ui->checkBox_Border->setChecked(on); ui->checkBox_Border->setDisabled(on); - showBorder = on; + this->settings.showBorder = on; } void MapImageExporter::on_checkBox_Elevation_stateChanged(int state) { - showCollision = (state == Qt::Checked); + this->settings.showCollision = (state == Qt::Checked); updatePreview(); } void MapImageExporter::on_checkBox_Grid_stateChanged(int state) { - showGrid = (state == Qt::Checked); + this->settings.showGrid = (state == Qt::Checked); updatePreview(); } void MapImageExporter::on_checkBox_Border_stateChanged(int state) { - showBorder = (state == Qt::Checked); + this->settings.showBorder = (state == Qt::Checked); updatePreview(); } void MapImageExporter::on_checkBox_Objects_stateChanged(int state) { - showObjects = (state == Qt::Checked); + this->settings.showObjects = (state == Qt::Checked); updatePreview(); } void MapImageExporter::on_checkBox_Warps_stateChanged(int state) { - showWarps = (state == Qt::Checked); + this->settings.showWarps = (state == Qt::Checked); updatePreview(); } void MapImageExporter::on_checkBox_BGs_stateChanged(int state) { - showBGs = (state == Qt::Checked); + this->settings.showBGs = (state == Qt::Checked); updatePreview(); } void MapImageExporter::on_checkBox_Triggers_stateChanged(int state) { - showTriggers = (state == Qt::Checked); + this->settings.showTriggers = (state == Qt::Checked); updatePreview(); } void MapImageExporter::on_checkBox_HealLocations_stateChanged(int state) { - showHealLocations = (state == Qt::Checked); + this->settings.showHealLocations = (state == Qt::Checked); updatePreview(); } @@ -558,51 +558,51 @@ void MapImageExporter::on_checkBox_AllEvents_stateChanged(int state) { const QSignalBlocker b_Objects(ui->checkBox_Objects); ui->checkBox_Objects->setChecked(on); ui->checkBox_Objects->setDisabled(on); - showObjects = on; + this->settings.showObjects = on; const QSignalBlocker b_Warps(ui->checkBox_Warps); ui->checkBox_Warps->setChecked(on); ui->checkBox_Warps->setDisabled(on); - showWarps = on; + this->settings.showWarps = on; const QSignalBlocker b_BGs(ui->checkBox_BGs); ui->checkBox_BGs->setChecked(on); ui->checkBox_BGs->setDisabled(on); - showBGs = on; + this->settings.showBGs = on; const QSignalBlocker b_Triggers(ui->checkBox_Triggers); ui->checkBox_Triggers->setChecked(on); ui->checkBox_Triggers->setDisabled(on); - showTriggers = on; + this->settings.showTriggers = on; const QSignalBlocker b_HealLocations(ui->checkBox_HealLocations); ui->checkBox_HealLocations->setChecked(on); ui->checkBox_HealLocations->setDisabled(on); - showHealLocations = on; + this->settings.showHealLocations = on; updatePreview(); } void MapImageExporter::on_checkBox_ConnectionUp_stateChanged(int state) { - showUpConnections = (state == Qt::Checked); + this->settings.showUpConnections = (state == Qt::Checked); updateShowBorderState(); updatePreview(); } void MapImageExporter::on_checkBox_ConnectionDown_stateChanged(int state) { - showDownConnections = (state == Qt::Checked); + this->settings.showDownConnections = (state == Qt::Checked); updateShowBorderState(); updatePreview(); } void MapImageExporter::on_checkBox_ConnectionLeft_stateChanged(int state) { - showLeftConnections = (state == Qt::Checked); + this->settings.showLeftConnections = (state == Qt::Checked); updateShowBorderState(); updatePreview(); } void MapImageExporter::on_checkBox_ConnectionRight_stateChanged(int state) { - showRightConnections = (state == Qt::Checked); + this->settings.showRightConnections = (state == Qt::Checked); updateShowBorderState(); updatePreview(); } @@ -614,30 +614,30 @@ void MapImageExporter::on_checkBox_AllConnections_stateChanged(int state) { const QSignalBlocker b_Up(ui->checkBox_ConnectionUp); ui->checkBox_ConnectionUp->setChecked(on); ui->checkBox_ConnectionUp->setDisabled(on); - showUpConnections = on; + this->settings.showUpConnections = on; const QSignalBlocker b_Down(ui->checkBox_ConnectionDown); ui->checkBox_ConnectionDown->setChecked(on); ui->checkBox_ConnectionDown->setDisabled(on); - showDownConnections = on; + this->settings.showDownConnections = on; const QSignalBlocker b_Left(ui->checkBox_ConnectionLeft); ui->checkBox_ConnectionLeft->setChecked(on); ui->checkBox_ConnectionLeft->setDisabled(on); - showLeftConnections = on; + this->settings.showLeftConnections = on; const QSignalBlocker b_Right(ui->checkBox_ConnectionRight); ui->checkBox_ConnectionRight->setChecked(on); ui->checkBox_ConnectionRight->setDisabled(on); - showRightConnections = on; + this->settings.showRightConnections = on; updateShowBorderState(); updatePreview(); } void MapImageExporter::on_checkBox_ActualSize_stateChanged(int state) { - previewActualSize = (state == Qt::Checked); - if (previewActualSize) { + this->settings.previewActualSize = (state == Qt::Checked); + if (this->settings.previewActualSize) { ui->graphicsView_Preview->resetTransform(); } else { scalePreview(); @@ -645,17 +645,20 @@ void MapImageExporter::on_checkBox_ActualSize_stateChanged(int state) { } void MapImageExporter::on_pushButton_Reset_pressed() { + this->settings = {}; for (auto widget : this->findChildren()) { - const QSignalBlocker b(widget); + const QSignalBlocker b(widget); // Prevent calls to updatePreview widget->setChecked(false); } + ui->spinBox_TimelapseDelay->setValue(this->settings.timelapseDelayMs); + ui->spinBox_FrameSkip->setValue(this->settings.timelapseSkipAmount); updatePreview(); } void MapImageExporter::on_spinBox_TimelapseDelay_valueChanged(int delayMs) { - timelapseDelayMs = delayMs; + this->settings.timelapseDelayMs = delayMs; } void MapImageExporter::on_spinBox_FrameSkip_valueChanged(int skip) { - timelapseSkipAmount = skip; + this->settings.timelapseSkipAmount = skip; } From 4dc598455f58ce9addcfb9474f396f6955cac464 Mon Sep 17 00:00:00 2001 From: GriffinR Date: Fri, 4 Oct 2024 13:36:50 -0400 Subject: [PATCH 13/19] Improve color slider edit history --- include/ui/colorinputwidget.h | 1 + include/ui/paletteeditor.h | 1 + src/ui/colorinputwidget.cpp | 15 +++++++++++++++ src/ui/paletteeditor.cpp | 10 +++++++--- 4 files changed, 24 insertions(+), 3 deletions(-) diff --git a/include/ui/colorinputwidget.h b/include/ui/colorinputwidget.h index 5b414776..cd871e0b 100644 --- a/include/ui/colorinputwidget.h +++ b/include/ui/colorinputwidget.h @@ -25,6 +25,7 @@ public: signals: void colorChanged(QRgb color); void bitDepthChanged(int bits); + void editingFinished(); private: Ui::ColorInputWidget *ui; diff --git a/include/ui/paletteeditor.h b/include/ui/paletteeditor.h index 6aa9e131..63581eb1 100644 --- a/include/ui/paletteeditor.h +++ b/include/ui/paletteeditor.h @@ -39,6 +39,7 @@ private: Tileset* getTileset(int paletteId); void refreshColorInputs(); + void commitEditHistory(); void commitEditHistory(int paletteId); void restoreWindowState(); void closeEvent(QCloseEvent*); diff --git a/src/ui/colorinputwidget.cpp b/src/ui/colorinputwidget.cpp index bbd6f764..8b40be27 100644 --- a/src/ui/colorinputwidget.cpp +++ b/src/ui/colorinputwidget.cpp @@ -34,6 +34,7 @@ ColorInputWidget::ColorInputWidget(const QString &title, QWidget *parent) : void ColorInputWidget::init() { ui->setupUi(this); + // Connect color change signals connect(ui->slider_Red, &QSlider::valueChanged, this, &ColorInputWidget::setRgbFromSliders); connect(ui->slider_Green, &QSlider::valueChanged, this, &ColorInputWidget::setRgbFromSliders); connect(ui->slider_Blue, &QSlider::valueChanged, this, &ColorInputWidget::setRgbFromSliders); @@ -46,6 +47,19 @@ void ColorInputWidget::init() { ui->lineEdit_Hex->setValidator(&hexValidator); connect(ui->lineEdit_Hex, &QLineEdit::textEdited, this, &ColorInputWidget::setRgbFromHexString); + // We have separate signals for when color input editing finishes. + // This is mostly useful for external commit histories, esp. for the sliders which can rapidly emit color change signals. + connect(ui->slider_Red, &QSlider::sliderReleased, this, &ColorInputWidget::editingFinished); + connect(ui->slider_Green, &QSlider::sliderReleased, this, &ColorInputWidget::editingFinished); + connect(ui->slider_Blue, &QSlider::sliderReleased, this, &ColorInputWidget::editingFinished); + + connect(ui->spinBox_Red, &QSpinBox::editingFinished, this, &ColorInputWidget::editingFinished); + connect(ui->spinBox_Green, &QSpinBox::editingFinished, this, &ColorInputWidget::editingFinished); + connect(ui->spinBox_Blue, &QSpinBox::editingFinished, this, &ColorInputWidget::editingFinished); + + connect(ui->lineEdit_Hex, &QLineEdit::editingFinished, this, &ColorInputWidget::editingFinished); + + // Connect color picker connect(ui->button_Eyedrop, &QToolButton::clicked, this, &ColorInputWidget::pickColor); setBitDepth(24); @@ -210,5 +224,6 @@ void ColorInputWidget::pickColor() { if (picker.exec() == QDialog::Accepted) { QColor c = picker.getColor(); setColor(c.rgb()); + emit editingFinished(); } } diff --git a/src/ui/paletteeditor.cpp b/src/ui/paletteeditor.cpp index 456d1f99..393f3a6e 100644 --- a/src/ui/paletteeditor.cpp +++ b/src/ui/paletteeditor.cpp @@ -24,6 +24,7 @@ PaletteEditor::PaletteEditor(Project *project, Tileset *primaryTileset, Tileset for (int i = 0; i < this->numColors; i++) { auto colorInput = new ColorInputWidget(QString("Color %1").arg(i)); connect(colorInput, &ColorInputWidget::colorChanged, [this, i](QRgb color) { setRgb(i, color); }); + connect(colorInput, &ColorInputWidget::editingFinished, [this] { commitEditHistory(); }); this->colorInputs.append(colorInput); ui->layout_Colors->addWidget(colorInput, i / numColorsPerRow, i % numColorsPerRow); } @@ -46,7 +47,7 @@ PaletteEditor::PaletteEditor(Project *project, Tileset *primaryTileset, Tileset connect(this->ui->bit_depth_24, &QRadioButton::toggled, [this](bool checked){ if (checked) this->setBitDepth(24); }); this->setPaletteId(paletteId); - this->commitEditHistory(this->ui->spinBox_PaletteId->value()); + this->commitEditHistory(); this->restoreWindowState(); } @@ -75,8 +76,7 @@ void PaletteEditor::setRgb(int colorIndex, QRgb rgb) { Tileset *tileset = getTileset(paletteId); tileset->palettes[paletteId][colorIndex] = rgb; tileset->palettePreviews[paletteId][colorIndex] = rgb; - - commitEditHistory(paletteId); + emit changedPaletteColor(); } @@ -120,6 +120,10 @@ void PaletteEditor::on_spinBox_PaletteId_valueChanged(int paletteId) { emit this->changedPalette(paletteId); } +void PaletteEditor::commitEditHistory() { + commitEditHistory(ui->spinBox_PaletteId->value()); +} + void PaletteEditor::commitEditHistory(int paletteId) { QList colors; for (int i = 0; i < this->numColors; i++) { From a31a014b5d00ac58f19c0e8c073c287f918611e8 Mon Sep 17 00:00:00 2001 From: GriffinR Date: Tue, 8 Oct 2024 01:40:40 -0400 Subject: [PATCH 14/19] Fix Summary Chart animations stopping prematurely --- include/ui/wildmonchart.h | 2 +- src/ui/wildmonchart.cpp | 28 +++++++++++++++++++++------- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/include/ui/wildmonchart.h b/include/ui/wildmonchart.h index d4e3d83f..3be7e468 100644 --- a/include/ui/wildmonchart.h +++ b/include/ui/wildmonchart.h @@ -69,7 +69,7 @@ private: void applySpeciesColors(const QList &); QChart::ChartTheme currentTheme() const; void updateTheme(); - void limitChartAnimation(QChart*); + void limitChartAnimation(); void showHelpDialog(); }; diff --git a/src/ui/wildmonchart.cpp b/src/ui/wildmonchart.cpp index 1e8c1017..375edbf4 100644 --- a/src/ui/wildmonchart.cpp +++ b/src/ui/wildmonchart.cpp @@ -31,6 +31,8 @@ WildMonChart::WildMonChart(QWidget *parent, const EncounterTableModel *table) : connect(ui->comboBox_Species, &QComboBox::currentTextChanged, this, &WildMonChart::refreshLevelDistributionChart); connect(ui->comboBox_Group, &QComboBox::currentTextChanged, this, &WildMonChart::refreshLevelDistributionChart); + connect(ui->tabWidget, &QTabWidget::currentChanged, this, &WildMonChart::limitChartAnimation); + // Set up Theme combo box for (auto i : themes) ui->comboBox_Theme->addItem(i.first, i.second); @@ -176,14 +178,16 @@ void WildMonChart::refreshSpeciesDistributionChart() { if (ui->chartView_SpeciesDistribution->chart()) ui->chartView_SpeciesDistribution->chart()->deleteLater(); ui->chartView_SpeciesDistribution->setChart(createSpeciesDistributionChart()); - limitChartAnimation(ui->chartView_SpeciesDistribution->chart()); + if (ui->tabWidget->currentWidget() == ui->tabSpecies) + limitChartAnimation(); } void WildMonChart::refreshLevelDistributionChart() { if (ui->chartView_LevelDistribution->chart()) ui->chartView_LevelDistribution->chart()->deleteLater(); ui->chartView_LevelDistribution->setChart(createLevelDistributionChart()); - limitChartAnimation(ui->chartView_LevelDistribution->chart()); + if (ui->tabWidget->currentWidget() == ui->tabLevels) + limitChartAnimation(); } QStringList WildMonChart::getSpeciesNamesAlphabetical() const { @@ -408,17 +412,27 @@ void WildMonChart::applySpeciesColors(const QList &barSets) { set->setColor(this->speciesToColor.value(set->label())); } -// Turn off the animation once it's played, otherwise it replays any time the window changes size. -void WildMonChart::limitChartAnimation(QChart *chart) { +// Turn off the chart animation once it's played, otherwise it replays any time the window changes size. +// The animation only begins when it's first displayed, so we'll only ever consider the chart for the current tab, +// and when the tab changes we'll call this again. +void WildMonChart::limitChartAnimation() { // Chart may be destroyed before the animation finishes - QPointer safeChart = chart; + QPointer chart; + if (ui->tabWidget->currentWidget() == ui->tabSpecies) { + chart = ui->chartView_SpeciesDistribution->chart(); + } else if (ui->tabWidget->currentWidget() == ui->tabLevels) { + chart = ui->chartView_LevelDistribution->chart(); + } + + if (!chart || chart->animationOptions() == QChart::NoAnimation) + return; // QChart has no signal for when the animation is finished, so we use a timer to stop the animation. // It is technically possible to get the chart to freeze mid-animation by resizing the window after // the timer starts but before it finishes, but 1. animations are short so this is difficult to do, // and 2. the issue resolves if the window is resized afterwards, so it's probably fine. - QTimer::singleShot(chart->animationDuration() + 500, [safeChart] { - if (safeChart) safeChart->setAnimationOptions(QChart::NoAnimation); + QTimer::singleShot(chart->animationDuration(), Qt::PreciseTimer, [chart] { + if (chart) chart->setAnimationOptions(QChart::NoAnimation); }); } From 1ed9b1ee10bb11938bea487eedb06dd9351c035d Mon Sep 17 00:00:00 2001 From: GriffinR Date: Wed, 9 Oct 2024 12:35:12 -0400 Subject: [PATCH 15/19] Correctly restore window focus for file dialogs --- include/core/filedialog.h | 53 ++++++++++++++++++++++++++ include/project.h | 2 - include/ui/customscriptseditor.h | 1 - include/ui/regionmappropertiesdialog.h | 3 +- porymap.pro | 2 + src/core/filedialog.cpp | 36 +++++++++++++++++ src/mainwindow.cpp | 12 ++---- src/project.cpp | 8 +--- src/ui/customscriptseditor.cpp | 17 ++------- src/ui/mapimageexporter.cpp | 7 ++-- src/ui/newtilesetdialog.cpp | 1 - src/ui/paletteeditor.cpp | 9 +---- src/ui/projectsettingseditor.cpp | 8 ++-- src/ui/regionmapeditor.cpp | 1 - src/ui/regionmappropertiesdialog.cpp | 19 ++++----- src/ui/tileseteditor.cpp | 46 ++++++++-------------- 16 files changed, 134 insertions(+), 91 deletions(-) create mode 100644 include/core/filedialog.h create mode 100644 src/core/filedialog.cpp diff --git a/include/core/filedialog.h b/include/core/filedialog.h new file mode 100644 index 00000000..374f243d --- /dev/null +++ b/include/core/filedialog.h @@ -0,0 +1,53 @@ +#ifndef FILEDIALOG_H +#define FILEDIALOG_H + +#include + +/* + Static QFileDialog functions will (unless otherwise specified) use native file dialogs. + In general this is good (we want our file dialogs to be visually seamless) but unfortunately + the native file dialogs ignore the parent widget, so in some cases they'll return focus to + the main window rather than the window that opened the file dialog. + + To make working around this a little easier we use this class, which will use the native + file dialog and manually return focus to the parent widget. + + It will also save the directory of the previous file selected in a file dialog, and if + no 'dir' argument is specified it will open new dialogs at that directory. + +*/ + +class FileDialog : public QFileDialog +{ +public: + FileDialog(QWidget *parent, Qt::WindowFlags flags) : QFileDialog(parent, flags) {}; + FileDialog(QWidget *parent = nullptr, + const QString &caption = QString(), + const QString &directory = QString(), + const QString &filter = QString()) : QFileDialog(parent, caption, directory, filter) {}; + + static void setDirectory(const QString &dir) { FileDialog::prevDirectory = dir; } + static QString getDirectory() { return FileDialog::prevDirectory; } + + static QString getOpenFileName(QWidget *parent = nullptr, + const QString &caption = QString(), + const QString &dir = QString(), + const QString &filter = QString(), + QString *selectedFilter = nullptr, + QFileDialog::Options options = Options()); + + static QString getSaveFileName(QWidget *parent = nullptr, + const QString &caption = QString(), + const QString &dir = QString(), + const QString &filter = QString(), + QString *selectedFilter = nullptr, + QFileDialog::Options options = Options()); + +private: + static QString prevDirectory; + static QString getDirectoryFromInput(const QString &dir); + static void setDirectoryFromFile(const QString &fileName); + static void restoreFocus(QWidget *parent); +}; + +#endif // FILEDIALOG_H diff --git a/include/project.h b/include/project.h index 9b90a450..f9fc704d 100644 --- a/include/project.h +++ b/include/project.h @@ -76,7 +76,6 @@ public: QFileSystemWatcher fileWatcher; QMap modifiedFileTimestamps; bool usingAsmTilesets; - QString importExportPath; QSet disabledSettingsNames; int pokemonMinLevel; int pokemonMaxLevel; @@ -216,7 +215,6 @@ public: QString buildMetatileLabelsText(const QMap defines); QString findMetatileLabelsTileset(QString label); - void setImportExportPath(QString filename); static QString getExistingFilepath(QString filepath); void applyParsedLimits(); diff --git a/include/ui/customscriptseditor.h b/include/ui/customscriptseditor.h index 0e5d3489..c93c7b7d 100644 --- a/include/ui/customscriptseditor.h +++ b/include/ui/customscriptseditor.h @@ -31,7 +31,6 @@ private: Ui::CustomScriptsEditor *ui; bool hasUnsavedChanges = false; - QString fileDialogDir; const QString baseDir; void displayScript(const QString &filepath, bool enabled); diff --git a/include/ui/regionmappropertiesdialog.h b/include/ui/regionmappropertiesdialog.h index 6097d842..3abacc64 100644 --- a/include/ui/regionmappropertiesdialog.h +++ b/include/ui/regionmappropertiesdialog.h @@ -4,7 +4,6 @@ #include "orderedjson.h" #include -#include class Project; @@ -33,7 +32,7 @@ private: void hideMessages(); - QString browse(QString filter, QFileDialog::FileMode mode); + QString browse(QString filter); private slots: void on_browse_tilesetImagePath_clicked(); diff --git a/porymap.pro b/porymap.pro index 6b7fc59d..51df62f1 100644 --- a/porymap.pro +++ b/porymap.pro @@ -25,6 +25,7 @@ SOURCES += src/core/block.cpp \ src/core/bitpacker.cpp \ src/core/blockdata.cpp \ src/core/events.cpp \ + src/core/filedialog.cpp \ src/core/heallocation.cpp \ src/core/imageexport.cpp \ src/core/map.cpp \ @@ -123,6 +124,7 @@ HEADERS += include/core/block.h \ include/core/bitpacker.h \ include/core/blockdata.h \ include/core/events.h \ + include/core/filedialog.h \ include/core/heallocation.h \ include/core/history.h \ include/core/imageexport.h \ diff --git a/src/core/filedialog.cpp b/src/core/filedialog.cpp new file mode 100644 index 00000000..9e17cce3 --- /dev/null +++ b/src/core/filedialog.cpp @@ -0,0 +1,36 @@ +#include "filedialog.h" + +QString FileDialog::prevDirectory; + + QString FileDialog::getDirectoryFromInput(const QString &dir) { + if (dir.isEmpty()) + return FileDialog::prevDirectory; + return dir; + } + +void FileDialog::setDirectoryFromFile(const QString &fileName) { + if (!fileName.isEmpty()) + FileDialog::prevDirectory = QFileInfo(fileName).absolutePath(); +} + +void FileDialog::restoreFocus(QWidget *parent) { + if (parent) { + parent->raise(); + parent->activateWindow(); + } +} + +QString FileDialog::getOpenFileName(QWidget *parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options) { + const QString fileName = QFileDialog::getOpenFileName(parent, caption, getDirectoryFromInput(dir), filter, selectedFilter, options); + setDirectoryFromFile(fileName); + restoreFocus(parent); + return fileName; +} + +QString FileDialog::getSaveFileName(QWidget *parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options) { + const QString fileName = QFileDialog::getSaveFileName(parent, caption, getDirectoryFromInput(dir), filter, selectedFilter, options); + setDirectoryFromFile(fileName); + restoreFocus(parent); + return fileName; +} + diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 93ed1dcb..103d0fda 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -19,8 +19,8 @@ #include "montabwidget.h" #include "imageexport.h" #include "newmapconnectiondialog.h" +#include "filedialog.h" -#include #include #include #include @@ -730,7 +730,7 @@ void MainWindow::openSubWindow(QWidget * window) { } QString MainWindow::getExistingDirectory(QString dir) { - return QFileDialog::getExistingDirectory(this, "Open Directory", dir, QFileDialog::ShowDirsOnly); + return FileDialog::getExistingDirectory(this, "Open Directory", dir, QFileDialog::ShowDirsOnly); } void MainWindow::on_action_Open_Project_triggered() @@ -2596,15 +2596,11 @@ void MainWindow::on_actionImport_Map_from_Advance_Map_1_92_triggered(){ void MainWindow::importMapFromAdvanceMap1_92() { - QString filepath = QFileDialog::getOpenFileName( - this, - QString("Import Map from Advance Map 1.92"), - this->editor->project->importExportPath, - "Advance Map 1.92 Map Files (*.map)"); + QString filepath = FileDialog::getOpenFileName(this, "Import Map from Advance Map 1.92", "", "Advance Map 1.92 Map Files (*.map)"); if (filepath.isEmpty()) { return; } - this->editor->project->setImportExportPath(filepath); + MapParser parser; bool error = false; MapLayout *mapLayout = parser.parse(filepath, &error, editor->project); diff --git a/src/project.cpp b/src/project.cpp index 5ffc3c43..e569ac69 100644 --- a/src/project.cpp +++ b/src/project.cpp @@ -7,6 +7,7 @@ #include "tile.h" #include "tileset.h" #include "map.h" +#include "filedialog.h" #include "orderedjson.h" @@ -89,7 +90,7 @@ void Project::initSignals() { void Project::set_root(QString dir) { this->root = dir; - this->importExportPath = dir; + FileDialog::setDirectory(dir); this->parser.set_root(dir); } @@ -2829,11 +2830,6 @@ QString Project::getDynamicMapDefineName() { return prefix + projectConfig.getIdentifier(ProjectIdentifier::define_map_dynamic); } -void Project::setImportExportPath(QString filename) -{ - this->importExportPath = QFileInfo(filename).absolutePath(); -} - // If the provided filepath is an absolute path to an existing file, return filepath. // If not, and the provided filepath is a relative path from the project dir to an existing file, return the relative path. // Otherwise return empty string. diff --git a/src/ui/customscriptseditor.cpp b/src/ui/customscriptseditor.cpp index b7180b92..40195d18 100644 --- a/src/ui/customscriptseditor.cpp +++ b/src/ui/customscriptseditor.cpp @@ -4,9 +4,9 @@ #include "config.h" #include "editor.h" #include "shortcut.h" +#include "filedialog.h" #include -#include CustomScriptsEditor::CustomScriptsEditor(QWidget *parent) : QMainWindow(parent), @@ -23,8 +23,6 @@ CustomScriptsEditor::CustomScriptsEditor(QWidget *parent) : for (int i = 0; i < paths.length(); i++) this->displayScript(paths.at(i), enabled.at(i)); - this->fileDialogDir = userConfig.projectDir; - connect(ui->button_CreateNewScript, &QAbstractButton::clicked, this, &CustomScriptsEditor::createNewScript); connect(ui->button_LoadScript, &QAbstractButton::clicked, this, &CustomScriptsEditor::loadScript); connect(ui->button_RefreshScripts, &QAbstractButton::clicked, this, &CustomScriptsEditor::userRefreshScripts); @@ -147,19 +145,13 @@ bool CustomScriptsEditor::getScriptEnabled(QListWidgetItem * item) const { } QString CustomScriptsEditor::chooseScript(QString dir) { - return QFileDialog::getOpenFileName(this, "Choose Custom Script File", dir, "JavaScript Files (*.js)"); + return FileDialog::getOpenFileName(this, "Choose Custom Script File", dir, "JavaScript Files (*.js)"); } void CustomScriptsEditor::createNewScript() { - QString filepath = QFileDialog::getSaveFileName(this, "Create New Script File", this->fileDialogDir + "/new_script.js", "JavaScript Files (*.js)"); - - // QFileDialog::getSaveFileName returns focus to the main editor window when closed. Workaround for this below - this->raise(); - this->activateWindow(); - + const QString filepath = FileDialog::getSaveFileName(this, "Create New Script File", FileDialog::getDirectory() + "/new_script.js", "JavaScript Files (*.js)"); if (filepath.isEmpty()) return; - this->fileDialogDir = filepath; QFile scriptFile(filepath); if (!scriptFile.open(QIODevice::WriteOnly)) { @@ -179,10 +171,9 @@ void CustomScriptsEditor::createNewScript() { } void CustomScriptsEditor::loadScript() { - QString filepath = this->chooseScript(this->fileDialogDir); + QString filepath = this->chooseScript(FileDialog::getDirectory()); if (filepath.isEmpty()) return; - this->fileDialogDir = filepath; this->displayNewScript(filepath); } diff --git a/src/ui/mapimageexporter.cpp b/src/ui/mapimageexporter.cpp index 10194b8b..c5b66e38 100644 --- a/src/ui/mapimageexporter.cpp +++ b/src/ui/mapimageexporter.cpp @@ -2,8 +2,8 @@ #include "ui_mapimageexporter.h" #include "qgifimage.h" #include "editcommands.h" +#include "filedialog.h" -#include #include #include #include @@ -65,13 +65,12 @@ void MapImageExporter::saveImage() { } QString defaultFilepath = QString("%1/%2.%3") - .arg(editor->project->importExportPath) + .arg(FileDialog::getDirectory()) .arg(defaultFilename) .arg(this->mode == ImageExporterMode::Timelapse ? "gif" : "png"); QString filter = this->mode == ImageExporterMode::Timelapse ? "Image Files (*.gif)" : "Image Files (*.png *.jpg *.bmp)"; - QString filepath = QFileDialog::getSaveFileName(this, title, defaultFilepath, filter); + QString filepath = FileDialog::getSaveFileName(this, title, defaultFilepath, filter); if (!filepath.isEmpty()) { - editor->project->setImportExportPath(filepath); switch (this->mode) { case ImageExporterMode::Normal: this->preview.save(filepath); diff --git a/src/ui/newtilesetdialog.cpp b/src/ui/newtilesetdialog.cpp index fa951879..e9eee946 100644 --- a/src/ui/newtilesetdialog.cpp +++ b/src/ui/newtilesetdialog.cpp @@ -1,6 +1,5 @@ #include "newtilesetdialog.h" #include "ui_newtilesetdialog.h" -#include #include "project.h" NewTilesetDialog::NewTilesetDialog(Project* project, QWidget *parent) : diff --git a/src/ui/paletteeditor.cpp b/src/ui/paletteeditor.cpp index 393f3a6e..d570f540 100644 --- a/src/ui/paletteeditor.cpp +++ b/src/ui/paletteeditor.cpp @@ -3,8 +3,8 @@ #include "paletteutil.h" #include "config.h" #include "log.h" +#include "filedialog.h" -#include #include @@ -158,15 +158,10 @@ void PaletteEditor::on_actionRedo_triggered() void PaletteEditor::on_actionImport_Palette_triggered() { - QString filepath = QFileDialog::getOpenFileName( - this, - QString("Import Tileset Palette"), - this->project->importExportPath, - "Palette Files (*.pal *.act *tpl *gpl)"); + QString filepath = FileDialog::getOpenFileName(this, "Import Tileset Palette", "", "Palette Files (*.pal *.act *tpl *gpl)"); if (filepath.isEmpty()) { return; } - this->project->setImportExportPath(filepath); bool error = false; QList palette = PaletteUtil::parse(filepath, &error); if (error) { diff --git a/src/ui/projectsettingseditor.cpp b/src/ui/projectsettingseditor.cpp index d84ef56b..ec476558 100644 --- a/src/ui/projectsettingseditor.cpp +++ b/src/ui/projectsettingseditor.cpp @@ -2,6 +2,7 @@ #include "config.h" #include "noscrollcombobox.h" #include "prefab.h" +#include "filedialog.h" #include #include @@ -383,10 +384,10 @@ QString ProjectSettingsEditor::chooseProjectFile(const QString &defaultFilepath) QString path; if (defaultFilepath.endsWith("/")){ // Default filepath is a folder, choose a new folder - path = QFileDialog::getExistingDirectory(this, "Choose Project File Folder", startDir) + QDir::separator(); + path = FileDialog::getExistingDirectory(this, "Choose Project File Folder", startDir) + QDir::separator(); } else{ // Default filepath is not a folder, choose a new file - path = QFileDialog::getOpenFileName(this, "Choose Project File", startDir); + path = FileDialog::getOpenFileName(this, "Choose Project File", startDir); } if (!path.startsWith(this->baseDir)){ @@ -573,10 +574,9 @@ void ProjectSettingsEditor::chooseImageFile(QLineEdit * filepathEdit) { } void ProjectSettingsEditor::chooseFile(QLineEdit * filepathEdit, const QString &description, const QString &extensions) { - QString filepath = QFileDialog::getOpenFileName(this, description, this->project->importExportPath, extensions); + QString filepath = FileDialog::getOpenFileName(this, description, "", extensions); if (filepath.isEmpty()) return; - this->project->setImportExportPath(filepath); if (filepathEdit) filepathEdit->setText(this->stripProjectDir(filepath)); diff --git a/src/ui/regionmapeditor.cpp b/src/ui/regionmapeditor.cpp index 372c6e05..b2a58536 100644 --- a/src/ui/regionmapeditor.cpp +++ b/src/ui/regionmapeditor.cpp @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include diff --git a/src/ui/regionmappropertiesdialog.cpp b/src/ui/regionmappropertiesdialog.cpp index 9c49def0..10ce3c4a 100644 --- a/src/ui/regionmappropertiesdialog.cpp +++ b/src/ui/regionmappropertiesdialog.cpp @@ -1,6 +1,7 @@ #include "project.h" #include "regionmappropertiesdialog.h" #include "ui_regionmappropertiesdialog.h" +#include "filedialog.h" RegionMapPropertiesDialog::RegionMapPropertiesDialog(QWidget *parent) : QDialog(parent), @@ -30,13 +31,9 @@ void RegionMapPropertiesDialog::hideMessages() { this->adjustSize(); } -QString RegionMapPropertiesDialog::browse(QString filter, QFileDialog::FileMode mode) { +QString RegionMapPropertiesDialog::browse(QString filter) { if (!this->project) return QString(); - QFileDialog browser; - browser.setFileMode(mode); - QString filepath = browser.getOpenFileName(this, "Select a File", this->project->importExportPath, filter); - if (!filepath.isEmpty()) - this->project->setImportExportPath(filepath); + QString filepath = FileDialog::getOpenFileName(this, "Select a File", "", filter); // remove the project root from the filepath return filepath.replace(this->project->root + "/", ""); @@ -107,21 +104,21 @@ poryjson::Json RegionMapPropertiesDialog::saveToJson() { } void RegionMapPropertiesDialog::on_browse_tilesetImagePath_clicked() { - QString path = browse("Images (*.png *.bmp)", QFileDialog::ExistingFile); + QString path = browse("Images (*.png *.bmp)"); if (!path.isEmpty()) { ui->config_tilemapImagePath->setText(path); } } void RegionMapPropertiesDialog::on_browse_tilemapBinPath_clicked() { - QString path = browse("Binary (*.bin *.tilemap *.4bpp *.8bpp)", QFileDialog::AnyFile); + QString path = browse("Binary (*.bin *.tilemap *.4bpp *.8bpp)"); if (!path.isEmpty()) { ui->config_tilemapBinPath->setText(path); } } void RegionMapPropertiesDialog::on_browse_tilemapPalettePath_clicked() { - QString path = browse("Text (*.pal)", QFileDialog::AnyFile); + QString path = browse("Text (*.pal)"); if (!path.isEmpty()) { ui->config_tilemapPalettePath->setText(path); } @@ -129,12 +126,12 @@ void RegionMapPropertiesDialog::on_browse_tilemapPalettePath_clicked() { void RegionMapPropertiesDialog::on_browse_layoutPath_clicked() { if (ui->config_layoutFormat->currentIndex() == 0) { - QString path = browse("Text File (*.h *.c *.inc *.txt)", QFileDialog::AnyFile); + QString path = browse("Text File (*.h *.c *.inc *.txt)"); if (!path.isEmpty()) { ui->config_layoutPath->setText(path); } } else { - QString path = browse("Binary (*.bin)", QFileDialog::AnyFile); + QString path = browse("Binary (*.bin)"); if (!path.isEmpty()) { ui->config_layoutPath->setText(path); } diff --git a/src/ui/tileseteditor.cpp b/src/ui/tileseteditor.cpp index 60a469e0..288c7bb6 100644 --- a/src/ui/tileseteditor.cpp +++ b/src/ui/tileseteditor.cpp @@ -7,7 +7,7 @@ #include "imageexport.h" #include "config.h" #include "shortcut.h" -#include +#include "filedialog.h" #include #include #include @@ -637,15 +637,11 @@ void TilesetEditor::importTilesetTiles(Tileset *tileset, bool primary) { QString descriptor = primary ? "primary" : "secondary"; QString descriptorCaps = primary ? "Primary" : "Secondary"; - QString filepath = QFileDialog::getOpenFileName( - this, - QString("Import %1 Tileset Tiles Image").arg(descriptorCaps), - this->project->importExportPath, - "Image Files (*.png *.bmp *.jpg *.dib)"); + QString filepath = FileDialog::getOpenFileName(this, QString("Import %1 Tileset Tiles Image").arg(descriptorCaps), "", "Image Files (*.png *.bmp *.jpg *.dib)"); if (filepath.isEmpty()) { return; } - this->project->setImportExportPath(filepath); + logInfo(QString("Importing %1 tileset tiles '%2'").arg(descriptor).arg(filepath)); // Read image data from buffer so that the built-in QImage doesn't try to detect file format @@ -698,15 +694,11 @@ void TilesetEditor::importTilesetTiles(Tileset *tileset, bool primary) { msgBox.setIcon(QMessageBox::Icon::Warning); msgBox.exec(); - QString filepath = QFileDialog::getOpenFileName( - this, - QString("Select Palette for Tiles Image").arg(descriptorCaps), - this->project->importExportPath, - "Palette Files (*.pal *.act *tpl *gpl)"); + QString filepath = FileDialog::getOpenFileName(this, "Select Palette for Tiles Image", "", "Palette Files (*.pal *.act *tpl *gpl)"); if (filepath.isEmpty()) { return; } - this->project->setImportExportPath(filepath); + bool error = false; QList palette = PaletteUtil::parse(filepath, &error); if (error) { @@ -939,10 +931,9 @@ void TilesetEditor::pasteMetatile(const Metatile * toPaste, QString newLabel) void TilesetEditor::on_actionExport_Primary_Tiles_Image_triggered() { QString defaultName = QString("%1_Tiles_Pal%2").arg(this->primaryTileset->name).arg(this->paletteId); - QString defaultFilepath = QString("%1/%2.png").arg(this->project->importExportPath).arg(defaultName); - QString filepath = QFileDialog::getSaveFileName(this, "Export Primary Tiles Image", defaultFilepath, "Image Files (*.png)"); + QString defaultFilepath = QString("%1/%2.png").arg(FileDialog::getDirectory()).arg(defaultName); + QString filepath = FileDialog::getSaveFileName(this, "Export Primary Tiles Image", defaultFilepath, "Image Files (*.png)"); if (!filepath.isEmpty()) { - this->project->setImportExportPath(filepath); QImage image = this->tileSelector->buildPrimaryTilesIndexedImage(); exportIndexed4BPPPng(image, filepath); } @@ -951,10 +942,9 @@ void TilesetEditor::on_actionExport_Primary_Tiles_Image_triggered() void TilesetEditor::on_actionExport_Secondary_Tiles_Image_triggered() { QString defaultName = QString("%1_Tiles_Pal%2").arg(this->secondaryTileset->name).arg(this->paletteId); - QString defaultFilepath = QString("%1/%2.png").arg(this->project->importExportPath).arg(defaultName); - QString filepath = QFileDialog::getSaveFileName(this, "Export Secondary Tiles Image", defaultFilepath, "Image Files (*.png)"); + QString defaultFilepath = QString("%1/%2.png").arg(FileDialog::getDirectory()).arg(defaultName); + QString filepath = FileDialog::getSaveFileName(this, "Export Secondary Tiles Image", defaultFilepath, "Image Files (*.png)"); if (!filepath.isEmpty()) { - this->project->setImportExportPath(filepath); QImage image = this->tileSelector->buildSecondaryTilesIndexedImage(); exportIndexed4BPPPng(image, filepath); } @@ -963,10 +953,9 @@ void TilesetEditor::on_actionExport_Secondary_Tiles_Image_triggered() void TilesetEditor::on_actionExport_Primary_Metatiles_Image_triggered() { QString defaultName = QString("%1_Metatiles").arg(this->primaryTileset->name); - QString defaultFilepath = QString("%1/%2.png").arg(this->project->importExportPath).arg(defaultName); - QString filepath = QFileDialog::getSaveFileName(this, "Export Primary Metatiles Image", defaultFilepath, "Image Files (*.png)"); + QString defaultFilepath = QString("%1/%2.png").arg(FileDialog::getDirectory()).arg(defaultName); + QString filepath = FileDialog::getSaveFileName(this, "Export Primary Metatiles Image", defaultFilepath, "Image Files (*.png)"); if (!filepath.isEmpty()) { - this->project->setImportExportPath(filepath); QImage image = this->metatileSelector->buildPrimaryMetatilesImage(); image.save(filepath, "PNG"); } @@ -975,10 +964,9 @@ void TilesetEditor::on_actionExport_Primary_Metatiles_Image_triggered() void TilesetEditor::on_actionExport_Secondary_Metatiles_Image_triggered() { QString defaultName = QString("%1_Metatiles").arg(this->secondaryTileset->name); - QString defaultFilepath = QString("%1/%2.png").arg(this->project->importExportPath).arg(defaultName); - QString filepath = QFileDialog::getSaveFileName(this, "Export Secondary Metatiles Image", defaultFilepath, "Image Files (*.png)"); + QString defaultFilepath = QString("%1/%2.png").arg(FileDialog::getDirectory()).arg(defaultName); + QString filepath = FileDialog::getSaveFileName(this, "Export Secondary Metatiles Image", defaultFilepath, "Image Files (*.png)"); if (!filepath.isEmpty()) { - this->project->setImportExportPath(filepath); QImage image = this->metatileSelector->buildSecondaryMetatilesImage(); image.save(filepath, "PNG"); } @@ -998,15 +986,11 @@ void TilesetEditor::importTilesetMetatiles(Tileset *tileset, bool primary) { QString descriptorCaps = primary ? "Primary" : "Secondary"; - QString filepath = QFileDialog::getOpenFileName( - this, - QString("Import %1 Tileset Metatiles from Advance Map 1.92").arg(descriptorCaps), - this->project->importExportPath, - "Advance Map 1.92 Metatile Files (*.bvd)"); + QString filepath = FileDialog::getOpenFileName(this, QString("Import %1 Tileset Metatiles from Advance Map 1.92").arg(descriptorCaps), "", "Advance Map 1.92 Metatile Files (*.bvd)"); if (filepath.isEmpty()) { return; } - this->project->setImportExportPath(filepath); + bool error = false; QList metatiles = MetatileParser::parse(filepath, &error, primary); if (error) { From f192b745ddf274ad202f02a03b027a31cd70946d Mon Sep 17 00:00:00 2001 From: GriffinR Date: Thu, 10 Oct 2024 01:43:35 -0400 Subject: [PATCH 16/19] Add additional static functions to filedialog --- include/core/filedialog.h | 12 ++++++++++++ src/core/filedialog.cpp | 15 +++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/include/core/filedialog.h b/include/core/filedialog.h index 374f243d..a02f6193 100644 --- a/include/core/filedialog.h +++ b/include/core/filedialog.h @@ -36,6 +36,18 @@ public: QString *selectedFilter = nullptr, QFileDialog::Options options = Options()); + static QStringList getOpenFileNames(QWidget *parent = nullptr, + const QString &caption = QString(), + const QString &dir = QString(), + const QString &filter = QString(), + QString *selectedFilter = nullptr, + QFileDialog::Options options = Options()); + + static QString getExistingDirectory(QWidget *parent = nullptr, + const QString &caption = QString(), + const QString &dir = QString(), + QFileDialog::Options options = ShowDirsOnly); + static QString getSaveFileName(QWidget *parent = nullptr, const QString &caption = QString(), const QString &dir = QString(), diff --git a/src/core/filedialog.cpp b/src/core/filedialog.cpp index 9e17cce3..94826090 100644 --- a/src/core/filedialog.cpp +++ b/src/core/filedialog.cpp @@ -27,6 +27,14 @@ QString FileDialog::getOpenFileName(QWidget *parent, const QString &caption, con return fileName; } +QStringList FileDialog::getOpenFileNames(QWidget *parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options) { + const QStringList fileNames = QFileDialog::getOpenFileNames(parent, caption, getDirectoryFromInput(dir), filter, selectedFilter, options); + if (!fileNames.isEmpty()) + setDirectoryFromFile(fileNames.last()); + restoreFocus(parent); + return fileNames; +} + QString FileDialog::getSaveFileName(QWidget *parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options) { const QString fileName = QFileDialog::getSaveFileName(parent, caption, getDirectoryFromInput(dir), filter, selectedFilter, options); setDirectoryFromFile(fileName); @@ -34,3 +42,10 @@ QString FileDialog::getSaveFileName(QWidget *parent, const QString &caption, con return fileName; } +QString FileDialog::getExistingDirectory(QWidget *parent, const QString &caption, const QString &dir, QFileDialog::Options options) { + const QString existingDir = QFileDialog::getExistingDirectory(parent, caption, getDirectoryFromInput(dir), options); + if (!existingDir.isEmpty()) + setDirectory(existingDir); + restoreFocus(parent); + return existingDir; +} From e38e05e95a22a63d980bbf687b43c72c6b9ef0d6 Mon Sep 17 00:00:00 2001 From: GriffinR Date: Thu, 10 Oct 2024 13:56:12 -0400 Subject: [PATCH 17/19] Update changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e80de0f..359b77e2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ The **"Breaking Changes"** listed below are changes that have been made in the d - Redesigned the Connections tab, adding a number of new features including the option to open or display diving maps and a list UI for easier edit access. - Add a `Close Project` option - Add charts to the `Wild Pokémon` tab that show species and level distributions. +- Add options for customizing the map grid under `View -> Grid Settings`. - An alert will be displayed when attempting to open a seemingly invalid project. - Add support for defining project values with `enum` where `#define` was expected. @@ -22,6 +23,7 @@ The **"Breaking Changes"** listed below are changes that have been made in the d - The max encounter rate is now read from the project, rather than assuming the default value from RSE. - It's now possible to cancel quitting if there are unsaved changes in sub-windows. - The triple-layer metatiles setting can now be set automatically using a project constant. +- `Export Map Stitch Image` now shows a preview of the full image, not just the current map. ### Fixed - Fix `Add Region Map...` not updating the region map settings file. @@ -46,6 +48,8 @@ The **"Breaking Changes"** listed below are changes that have been made in the d - Fix the map list mishandling value gaps when sorting by Area. - Fix a freeze on startup if project values are defined with mismatched parentheses. - Fix stitched map images sometimes rendering garbage +- Fix the `Reset` button on `Export Map Timelapse Image` not resetting the Timelapse settings. +- Stop sliders in the Palette Editor from creating a bunch of edit history when used. ## [5.4.1] - 2024-03-21 ### Fixed From caeaeac1f67014e43787b8af112f4ee736d7cec4 Mon Sep 17 00:00:00 2001 From: GriffinR Date: Thu, 10 Oct 2024 16:11:01 -0400 Subject: [PATCH 18/19] Fix NoScrollComboBox and NoScrollSpinBox stopping parent's scrolling --- CHANGELOG.md | 1 + src/ui/noscrollcombobox.cpp | 6 +++++- src/ui/noscrollspinbox.cpp | 6 +++++- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 359b77e2..c2c55fdf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -50,6 +50,7 @@ The **"Breaking Changes"** listed below are changes that have been made in the d - Fix stitched map images sometimes rendering garbage - Fix the `Reset` button on `Export Map Timelapse Image` not resetting the Timelapse settings. - Stop sliders in the Palette Editor from creating a bunch of edit history when used. +- Fix scrolling on some containers locking up when the mouse stops over a spin box or combo box. ## [5.4.1] - 2024-03-21 ### Fixed diff --git a/src/ui/noscrollcombobox.cpp b/src/ui/noscrollcombobox.cpp index 542bf5fd..e6e21a4e 100644 --- a/src/ui/noscrollcombobox.cpp +++ b/src/ui/noscrollcombobox.cpp @@ -2,6 +2,7 @@ #include #include +#include NoScrollComboBox::NoScrollComboBox(QWidget *parent) : QComboBox(parent) @@ -39,8 +40,11 @@ void NoScrollComboBox::wheelEvent(QWheelEvent *event) { // By default NoScrollComboBoxes will allow scrolling to modify its contents only when it explicitly has focus. // If focusedScrollingEnabled is false it won't allow scrolling even with focus. - if (this->focusedScrollingEnabled && hasFocus()) + if (this->focusedScrollingEnabled && hasFocus()) { QComboBox::wheelEvent(event); + } else { + event->ignore(); + } } void NoScrollComboBox::setFocusedScrollingEnabled(bool enabled) diff --git a/src/ui/noscrollspinbox.cpp b/src/ui/noscrollspinbox.cpp index 144b0e91..f8d1d444 100644 --- a/src/ui/noscrollspinbox.cpp +++ b/src/ui/noscrollspinbox.cpp @@ -1,4 +1,5 @@ #include "noscrollspinbox.h" +#include unsigned actionId = 0xffff; @@ -12,8 +13,11 @@ NoScrollSpinBox::NoScrollSpinBox(QWidget *parent) void NoScrollSpinBox::wheelEvent(QWheelEvent *event) { // Only allow scrolling to modify contents when it explicitly has focus. - if (hasFocus()) + if (hasFocus()) { QSpinBox::wheelEvent(event); + } else { + event->ignore(); + } } void NoScrollSpinBox::focusOutEvent(QFocusEvent *event) { From 3b6d3bef0410074b6044480582e52a32b377ef4a Mon Sep 17 00:00:00 2001 From: GriffinR Date: Wed, 16 Oct 2024 10:42:44 -0400 Subject: [PATCH 19/19] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c2c55fdf..0d9fd720 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -51,6 +51,7 @@ The **"Breaking Changes"** listed below are changes that have been made in the d - Fix the `Reset` button on `Export Map Timelapse Image` not resetting the Timelapse settings. - Stop sliders in the Palette Editor from creating a bunch of edit history when used. - Fix scrolling on some containers locking up when the mouse stops over a spin box or combo box. +- Fix some file dialogs returning to an incorrect window when closed. ## [5.4.1] - 2024-03-21 ### Fixed