diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e80de0f..0d9fd720 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,10 @@ 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. +- 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 diff --git a/forms/colorinputwidget.ui b/forms/colorinputwidget.ui new file mode 100644 index 00000000..11f0bc25 --- /dev/null +++ b/forms/colorinputwidget.ui @@ -0,0 +1,310 @@ + + + ColorInputWidget + + + + 0 + 0 + 221 + 212 + + + + + + + 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 new file mode 100644 index 00000000..75d31de1 --- /dev/null +++ b/forms/gridsettingsdialog.ui @@ -0,0 +1,275 @@ + + + GridSettingsDialog + + + + 0 + 0 + 331 + 467 + + + + Grid Settings + + + + + + true + + + + + 0 + 0 + 305 + 401 + + + + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + + + + + + 0 + 0 + + + + Color + + + + + + + false + + + QComboBox::SizeAdjustPolicy::AdjustToContentsOnFirstShow + + + 0 + + + + + + + + 0 + 0 + + + + Style + + + + + + + Dimensions (in pixels) + + + + + + ... + + + + :/icons/link_broken.ico + :/icons/link.ico:/icons/link_broken.ico + + + true + + + true + + + QToolButton::ToolButtonPopupMode::InstantPopup + + + true + + + + + + + 2 + + + 999 + + + + + + + Height + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + + + + Width + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + + + + 2 + + + 999 + + + + + + + + + + Offset (in pixels) + + + + + + Y + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + + + + X + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + + + + 0 + + + 999 + + + + + + + 0 + + + 999 + + + + + + + ... + + + + :/icons/link_broken.ico + :/icons/link.ico:/icons/link_broken.ico + + + true + + + true + + + QToolButton::ToolButtonPopupMode::InstantPopup + + + true + + + + + + + + + + + + + + + + + + + + + Qt::Orientation::Horizontal + + + QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok|QDialogButtonBox::StandardButton::RestoreDefaults + + + + + + + + NoScrollSpinBox + QSpinBox +
noscrollspinbox.h
+
+ + NoScrollComboBox + QComboBox +
noscrollcombobox.h
+
+ + ColorInputWidget + QGroupBox +
colorinputwidget.h
+ 1 +
+
+ + + + +
diff --git a/forms/mainwindow.ui b/forms/mainwindow.ui index e954f701..bc03f823 100644 --- a/forms/mainwindow.ui +++ b/forms/mainwindow.ui @@ -2939,12 +2939,6 @@ 0 - - - 0 - 32 - - QFrame::StyledPanel @@ -3459,10 +3453,13 @@ + + + @@ -3694,7 +3691,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> @@ -3711,7 +3708,7 @@ true - Cursor Tile Outline + Show Cursor Tile Outline C @@ -3810,7 +3807,26 @@ true - Dive/Emerge Map + Show Dive/Emerge Map + + + + + true + + + true + + + Show Grid + + + Ctrl+G + + + + + Grid Settings... diff --git a/forms/mapimageexporter.ui b/forms/mapimageexporter.ui index d97099a7..933aab67 100644 --- a/forms/mapimageexporter.ui +++ b/forms/mapimageexporter.ui @@ -6,8 +6,8 @@ 0 0 - 696 - 396 + 817 + 535 @@ -16,15 +16,313 @@ true - - - + + + + + + 0 + 0 + + + + + + + + + Map + + + + + + + QComboBox::SizeAdjustPolicy::AdjustToContents + + + + + + + + + Events + + + + + + + + Triggers + + + + + + + Objects + + + + + + + Heal Locations + + + + + + + Warps + + + + + + + All + + + + + + + BGs + + + + + + + + + + + + Connections + + + + + + + + Left + + + + + + + Up + + + + + + + Right + + + + + + + Down + + + + + + + All + + + + + + + + + + + + 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 +331,24 @@ 0 0 - 403 - 343 + 469 + 464 - + + 0 + + + 0 + + + 0 + + + 0 + + @@ -53,65 +363,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 +377,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/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/core/filedialog.h b/include/core/filedialog.h new file mode 100644 index 00000000..a02f6193 --- /dev/null +++ b/include/core/filedialog.h @@ -0,0 +1,65 @@ +#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 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(), + 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/editor.h b/include/editor.h index 93869fc4..ad2ae672 100644 --- a/include/editor.h +++ b/include/editor.h @@ -24,6 +24,7 @@ #include "collisionpixmapitem.h" #include "layoutpixmapitem.h" #include "settings.h" +#include "gridsettings.h" #include "movablerect.h" #include "cursortilerect.h" #include "mapruler.h" @@ -54,6 +55,7 @@ public: QUndoGroup editGroup; // Manages the undo history for each map Settings *settings; + GridSettings gridSettings; void setProject(Project * project); void save(); @@ -83,6 +85,7 @@ public: void displayMapConnections(); void displayMapBorder(); void displayMapGrid(); + void updateMapGrid(); void displayWildMonTables(); void updateMapBorder(); @@ -132,7 +135,7 @@ public: QGraphicsItemGroup *events_group = nullptr; QList borderItems; - QList gridLines; + QGraphicsItemGroup *mapGrid = nullptr; MapRuler *map_ruler = nullptr; MovableRect *playerViewRect = nullptr; @@ -197,6 +200,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"); @@ -251,7 +255,6 @@ private slots: void onHoveredMapMovementPermissionCleared(); void onSelectedMetatilesChanged(); void onWheelZoom(int); - void onToggleGridClicked(bool); signals: void objectsChanged(); @@ -263,6 +266,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 c8502d82..194cc52c 100644 --- a/include/mainwindow.h +++ b/include/mainwindow.h @@ -27,6 +27,7 @@ #include "shortcutseditor.h" #include "preferenceeditor.h" #include "projectsettingseditor.h" +#include "gridsettings.h" #include "customscriptseditor.h" #include "wildmonchart.h" #include "updatepromoter.h" @@ -326,6 +327,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; @@ -340,6 +343,7 @@ private: QPointer newMapPrompt = nullptr; QPointer preferenceEditor = nullptr; QPointer projectSettingsEditor = nullptr; + QPointer gridSettingsDialog = nullptr; QPointer customScriptsEditor = nullptr; FilterChildrenProxyModel *groupListProxyModel; diff --git a/include/project.h b/include/project.h index 8d6ce9e1..d93f5601 100644 --- a/include/project.h +++ b/include/project.h @@ -77,7 +77,6 @@ public: QFileSystemWatcher fileWatcher; QMap modifiedFileTimestamps; bool usingAsmTilesets; - QString importExportPath; QSet disabledSettingsNames; int pokemonMinLevel; int pokemonMaxLevel; @@ -227,7 +226,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/colorinputwidget.h b/include/ui/colorinputwidget.h new file mode 100644 index 00000000..cd871e0b --- /dev/null +++ b/include/ui/colorinputwidget.h @@ -0,0 +1,46 @@ +#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); + void editingFinished(); + +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/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/gridsettings.h b/include/ui/gridsettings.h new file mode 100644 index 00000000..807a0685 --- /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; + 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; + + QVector getCenteredDashPattern(uint length, qreal dashLength, qreal gapLength) const; + QVector 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/mapimageexporter.h b/include/ui/mapimageexporter.h index b61348bf..37df3038 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 @@ -34,50 +52,44 @@ private: QPixmap preview; - bool showObjects = false; - bool showWarps = false; - bool showBGs = false; - bool showTriggers = false; - bool showHealSpots = false; - bool showUpConnections = false; - bool showDownConnections = false; - bool showLeftConnections = false; - bool showRightConnections = false; - bool showGrid = false; - bool showBorder = false; - bool showCollision = false; - int timelapseSkipAmount = 1; - int timelapseDelayMs = 200; + ImageExporterSettings settings; 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); 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); + + void on_checkBox_ActualSize_stateChanged(int state); }; #endif // MAPIMAGEEXPORTER_H diff --git a/include/ui/paletteeditor.h b/include/ui/paletteeditor.h index 9b846d60..63581eb1 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,27 @@ 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(); + 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/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/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/porymap.pro b/porymap.pro index 14e83635..d65bdf07 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 \ @@ -51,6 +52,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 \ @@ -60,6 +62,7 @@ SOURCES += src/core/block.cpp \ src/ui/collisionpixmapitem.cpp \ src/ui/connectionpixmapitem.cpp \ src/ui/currentselectedmetatilespixmapitem.cpp \ + src/ui/gridsettings.cpp \ src/ui/newmapconnectiondialog.cpp \ src/ui/overlay.cpp \ src/ui/prefab.cpp \ @@ -122,6 +125,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 \ @@ -158,6 +162,7 @@ HEADERS += include/core/block.h \ include/ui/collisionpixmapitem.h \ include/ui/connectionpixmapitem.h \ include/ui/currentselectedmetatilespixmapitem.h \ + include/ui/gridsettings.h \ include/ui/newmapconnectiondialog.h \ include/ui/prefabframe.h \ include/ui/projectsettingseditor.h \ @@ -176,6 +181,7 @@ HEADERS += include/core/block.h \ include/ui/prefabcreationdialog.h \ include/ui/regionmappixmapitem.h \ include/ui/citymappixmapitem.h \ + include/ui/colorinputwidget.h \ include/ui/metatilelayersitem.h \ include/ui/metatileselector.h \ include/ui/movablerect.h \ @@ -220,7 +226,9 @@ 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 \ forms/prefabcreationdialog.ui \ forms/prefabframe.ui \ diff --git a/resources/icons/link.ico b/resources/icons/link.ico new file mode 100755 index 00000000..b0c35b59 Binary files /dev/null and b/resources/icons/link.ico differ diff --git a/resources/icons/link_broken.ico b/resources/icons/link_broken.ico new file mode 100755 index 00000000..fd7774ed Binary files /dev/null and b/resources/icons/link_broken.ico differ diff --git a/resources/images.qrc b/resources/images.qrc index 65bff5d6..15650a65 100644 --- a/resources/images.qrc +++ b/resources/images.qrc @@ -21,6 +21,8 @@ icons/lock_edit.ico icons/unlock_edit.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/core/filedialog.cpp b/src/core/filedialog.cpp new file mode 100644 index 00000000..94826090 --- /dev/null +++ b/src/core/filedialog.cpp @@ -0,0 +1,51 @@ +#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; +} + +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); + restoreFocus(parent); + 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; +} diff --git a/src/editor.cpp b/src/editor.cpp index a032ab7e..f674e864 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() @@ -1913,40 +1917,69 @@ 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 = this->layout->getWidth() * 16; - int pixelHeight = this->layout->getHeight() * 16; - for (int i = 0; i <= this->layout->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);}); + // 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 int pixelMapWidth = this->layout->getWidth() * 16; + const int pixelMapHeight = this->layout->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.setColor(this->gridSettings.color); + + // Create vertical lines + 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); } - for (int j = 0; j <= this->layout->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);}); + + // Create horizontal lines + 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); } - connect(ui->checkBox_ToggleGrid, &QCheckBox::toggled, this, &Editor::onToggleGridClicked); + + 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) diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 6666196f..21d2b348 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -22,8 +22,8 @@ #include "eventfilters.h" #include "newmapconnectiondialog.h" #include "config.h" +#include "filedialog.h" -#include #include #include #include @@ -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)"); @@ -358,8 +354,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(); @@ -501,27 +495,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); @@ -825,7 +837,7 @@ QString MainWindow::getDefaultLayout() { } 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() @@ -2332,6 +2344,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::updateMapGrid); + } + openSubWindow(this->gridSettingsDialog); +} + void MainWindow::on_actionShortcuts_triggered() { if (!shortcutsEditor) @@ -3011,15 +3035,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; Layout *mapLayout = parser.parse(filepath, &error, editor->project); diff --git a/src/project.cpp b/src/project.cpp index 44e740ae..194f2ecd 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); } @@ -2980,11 +2981,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/colorinputwidget.cpp b/src/ui/colorinputwidget.cpp new file mode 100644 index 00000000..8b40be27 --- /dev/null +++ b/src/ui/colorinputwidget.cpp @@ -0,0 +1,229 @@ +#include "colorinputwidget.h" +#include "ui_colorinputwidget.h" +#include "colorpicker.h" + +#include + +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 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); + + 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); + + // 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); +} + +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()); + emit editingFinished(); + } +} 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/graphicsview.cpp b/src/ui/graphicsview.cpp index 1c86e004..f4de5074 100644 --- a/src/ui/graphicsview.cpp +++ b/src/ui/graphicsview.cpp @@ -31,10 +31,24 @@ 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); + + // 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..b4e180af --- /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. +QVector 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 + QVector 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; +} + +QVector 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/mapimageexporter.cpp b/src/ui/mapimageexporter.cpp index e5f96132..a7a26629 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 @@ -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) @@ -34,6 +47,7 @@ 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); @@ -43,7 +57,8 @@ MapImageExporter::MapImageExporter(QWidget *parent_, Editor *editor_, ImageExpor this->ui->comboBox_MapSelection->setEnabled(false);// TODO: allow selecting map from drop-down } - updatePreview(); + connect(ui->pushButton_Save, &QPushButton::pressed, this, &MapImageExporter::saveImage); + connect(ui->pushButton_Cancel, &QPushButton::pressed, this, &MapImageExporter::close); } MapImageExporter::~MapImageExporter() { @@ -51,7 +66,25 @@ 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() { + // 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) @@ -68,40 +101,27 @@ 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: + 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: // Timelapse will play in order of layout changes then map changes (events) // TODO: potentially update in the future? QGifImage timelapseImg; - timelapseImg.setDefaultDelay(timelapseDelayMs); + timelapseImg.setDefaultDelay(this->settings.timelapseDelayMs); timelapseImg.setDefaultTransparentColor(QColor(0, 0, 0)); // lambda to avoid redundancy - auto generateTimelapseFromHistory = [=, this, &timelapseImg](QString progressText, QUndoStack &historyStack){ + auto generateTimelapseFromHistory = [this, &timelapseImg](QString progressText, QUndoStack &historyStack){ QProgressDialog progress(progressText, "Cancel", 0, 1, this); progress.setAutoClose(true); progress.setWindowModality(Qt::WindowModal); @@ -111,7 +131,7 @@ void MapImageExporter::saveImage() { int maxWidth = this->layout->getWidth() * 16; int maxHeight = this->layout->getHeight() * 16; - if (showBorder) { + if (this->settings.showBorder) { maxWidth += 2 * STITCH_MODE_BORDER_DISTANCE * 16; maxHeight += 2 * STITCH_MODE_BORDER_DISTANCE * 16; } @@ -122,7 +142,7 @@ void MapImageExporter::saveImage() { historyStack.undo(); int width = this->layout->getWidth() * 16; int height = this->layout->getHeight() * 16; - if (showBorder) { + if (this->settings.showBorder) { width += 2 * STITCH_MODE_BORDER_DISTANCE * 16; height += 2 * STITCH_MODE_BORDER_DISTANCE * 16; } @@ -153,7 +173,7 @@ void MapImageExporter::saveImage() { historyStack.redo(); } progress.setValue(progress.maximum() - i); - QPixmap pixmap = this->getFormattedMapPixmap(this->map, !this->showBorder); + QPixmap pixmap = this->getFormattedMapPixmap(this->map); if (pixmap.width() < maxWidth || pixmap.height() < maxHeight) { QPixmap pixmap2 = QPixmap(maxWidth, maxHeight); QPainter painter(&pixmap2); @@ -163,7 +183,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--; historyStack.redo(); @@ -209,26 +229,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->showHealSpots && (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: @@ -371,19 +391,33 @@ 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); + progress.setMinimumDuration(1000); + 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. + 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->settings.previewActualSize){ + ui->graphicsView_Preview->fitInView(this->scene->sceneRect(), Qt::KeepAspectRatioByExpanding); + } } // THIS @@ -403,7 +437,7 @@ QPixmap MapImageExporter::getFormattedMapPixmap(Map *map, bool ignoreBorder) { pixmap = map->layout->pixmap; } - if (showCollision) { + if (this->settings.showCollision) { QPainter collisionPainter(&pixmap); layout->renderCollision(true); collisionPainter.setOpacity(editor->collisionOpacity); @@ -414,7 +448,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; layout->renderBorder(); int borderHorzDist = editor->getBorderDrawDistance(layout->getBorderWidth()); @@ -437,15 +471,16 @@ QPixmap MapImageExporter::getFormattedMapPixmap(Map *map, bool ignoreBorder) { return pixmap; } - 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()); } @@ -453,27 +488,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->settings.showObjects || this->settings.showWarps || this->settings.showBGs || this->settings.showTriggers || this->settings.showHealLocations) { + QPainter eventPainter(&pixmap); + int pixelOffset = 0; + 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->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()); + } + } + 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->settings.showGrid) { int addX = 1, addY = 1; if (borderHeight) addY = 0; if (borderWidth) addX = 0; @@ -496,97 +534,161 @@ 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 = (this->settings.showUpConnections || this->settings.showDownConnections || this->settings.showLeftConnections || this->settings.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); + 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_HealSpots_stateChanged(int state) { - showHealSpots = (state == Qt::Checked); +void MapImageExporter::on_checkBox_HealLocations_stateChanged(int state) { + this->settings.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); + this->settings.showObjects = on; + + const QSignalBlocker b_Warps(ui->checkBox_Warps); + ui->checkBox_Warps->setChecked(on); + ui->checkBox_Warps->setDisabled(on); + this->settings.showWarps = on; + + const QSignalBlocker b_BGs(ui->checkBox_BGs); + ui->checkBox_BGs->setChecked(on); + ui->checkBox_BGs->setDisabled(on); + this->settings.showBGs = on; + + const QSignalBlocker b_Triggers(ui->checkBox_Triggers); + ui->checkBox_Triggers->setChecked(on); + ui->checkBox_Triggers->setDisabled(on); + this->settings.showTriggers = on; + + const QSignalBlocker b_HealLocations(ui->checkBox_HealLocations); + ui->checkBox_HealLocations->setChecked(on); + ui->checkBox_HealLocations->setDisabled(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(); } -void MapImageExporter::on_pushButton_Save_pressed() { - saveImage(); +// 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); + this->settings.showUpConnections = on; + + const QSignalBlocker b_Down(ui->checkBox_ConnectionDown); + ui->checkBox_ConnectionDown->setChecked(on); + ui->checkBox_ConnectionDown->setDisabled(on); + this->settings.showDownConnections = on; + + const QSignalBlocker b_Left(ui->checkBox_ConnectionLeft); + ui->checkBox_ConnectionLeft->setChecked(on); + ui->checkBox_ConnectionLeft->setDisabled(on); + this->settings.showLeftConnections = on; + + const QSignalBlocker b_Right(ui->checkBox_ConnectionRight); + ui->checkBox_ConnectionRight->setChecked(on); + ui->checkBox_ConnectionRight->setDisabled(on); + this->settings.showRightConnections = on; + + updateShowBorderState(); + updatePreview(); +} + +void MapImageExporter::on_checkBox_ActualSize_stateChanged(int state) { + this->settings.previewActualSize = (state == Qt::Checked); + if (this->settings.previewActualSize) { + ui->graphicsView_Preview->resetTransform(); + } else { + scalePreview(); + } } void MapImageExporter::on_pushButton_Reset_pressed() { - for (auto widget : this->findChildren()) + this->settings = {}; + for (auto widget : this->findChildren()) { + const QSignalBlocker b(widget); // Prevent calls to updatePreview widget->setChecked(false); -} - -void MapImageExporter::on_pushButton_Cancel_pressed() { - this->close(); + } + 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; } 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/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) { diff --git a/src/ui/paletteeditor.cpp b/src/ui/paletteeditor.cpp index 8dcd2a7a..d570f540 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 "filedialog.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,14 @@ 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); }); + connect(colorInput, &ColorInputWidget::editingFinished, [this] { commitEditHistory(); }); + this->colorInputs.append(colorInput); + ui->layout_Colors->addWidget(colorInput, i / numColorsPerRow, i % numColorsPerRow); } // Setup edit-undo history for each of the palettes. @@ -82,11 +34,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); @@ -100,246 +47,87 @@ 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(); } 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; + Tileset *tileset = getTileset(paletteId); + tileset->palettes[paletteId][colorIndex] = rgb; + tileset->palettePreviews[paletteId][colorIndex] = rgb; - this->updateColorUi(colorIndex, rgb); - - this->commitEditHistory(paletteNum); - emit this->changedPaletteColor(); + 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); } emit this->changedPalette(paletteId); } +void PaletteEditor::commitEditHistory() { + commitEditHistory(ui->spinBox_PaletteId->value()); +} + 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,44 +144,24 @@ 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() { - 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) { @@ -407,10 +175,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 +188,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*) { 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 59ef3621..2a23b454 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 3f7b7c0f..bbb105e5 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) { 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); }); }