diff --git a/docsrc/manual/project-files.rst b/docsrc/manual/project-files.rst index 30f0acc89..e4f0479b5 100644 --- a/docsrc/manual/project-files.rst +++ b/docsrc/manual/project-files.rst @@ -59,7 +59,6 @@ The filepath that Porymap expects for each file can be overridden on the ``Files include/constants/event_object_movement.h, yes, no, ``constants_obj_event_movement``, include/constants/event_objects.h, yes, no, ``constants_obj_events``, include/constants/event_bg.h, yes, no, ``constants_event_bg``, - include/constants/region_map_sections.h, yes, no, ``constants_region_map_sections``, include/constants/metatile_labels.h, yes, yes, ``constants_metatile_labels``, include/constants/metatile_behaviors.h, yes, no, ``constants_metatile_behaviors``, include/constants/species.h, yes, no, ``constants_metatile_behaviors``, for the Wild Pokémon tab @@ -122,7 +121,6 @@ In addition to these files, there are some specific symbol and macro names that ``define_map_empty``, ``UNDEFINED``, macro name after prefix for empty maps ``define_map_section_prefix``, ``MAPSEC_``, expected prefix for location macro names ``define_map_section_empty``, ``NONE``, macro name after prefix for empty region map sections - ``define_map_section_count``, ``COUNT``, macro name after prefix for total number of region map sections ``define_species_prefix``, ``SPECIES_``, expected prefix for species macro names ``regex_behaviors``, ``\bMB_``, regex to find metatile behavior macro names ``regex_obj_event_gfx``, ``\bOBJ_EVENT_GFX_``, regex to find Object Event graphics ID macro names diff --git a/forms/mainwindow.ui b/forms/mainwindow.ui index 60dc95e7b..44d69d307 100644 --- a/forms/mainwindow.ui +++ b/forms/mainwindow.ui @@ -6,8 +6,8 @@ 0 0 - 1287 - 903 + 1298 + 963 @@ -30,9 +30,9 @@ - Qt::Horizontal + Qt::Orientation::Horizontal - + true @@ -42,168 +42,162 @@ 0 - - - 0 - - - 0 - - - 0 - - - 3 - - - 0 - - - - - 0 - - - 3 - - - 3 - - - 3 - - - - - true - - - <html><head/><body><p>Sort map list</p></body></html> - - - - :/icons/sort_alphabet.ico:/icons/sort_alphabet.ico - - - - 16 - 16 - - - - QToolButton::InstantPopup - - - Qt::ToolButtonIconOnly - - - true - - - Qt::NoArrow - - - - - - - <html><head/><body><p>Expand all map folders</p></body></html> - - - - - - - :/icons/expand_all.ico:/icons/expand_all.ico - - - QToolButton::InstantPopup - - - true - - - - - - - <html><head/><body><p>Collapse all map list folders</p></body></html> - - - - - - - :/icons/collapse_all.ico:/icons/collapse_all.ico - - - QToolButton::InstantPopup - - - true - - - - - - - Qt::Horizontal - - - QSizePolicy::Preferred - - - - 12 - 20 - - - - - - - - true - - - - - - Filter maps... - - - true - - - - - - - - - - 0 - 0 - - - - - 200 - 0 - - - - QAbstractItemView::SingleSelection - - - QAbstractItemView::SelectItems - - - false - - - - + + 0 + + + + Groups + + + + 0 + + + 0 + + + 0 + + + 3 + + + 0 + + + + + + + + + 0 + 0 + + + + + 200 + 0 + + + + QAbstractItemView::SelectionMode::SingleSelection + + + QAbstractItemView::SelectionBehavior::SelectItems + + + false + + + + + + + + Areas + + + + 0 + + + 0 + + + 0 + + + 3 + + + 0 + + + + + + + + + 0 + 0 + + + + + 200 + 0 + + + + QAbstractItemView::SelectionMode::SingleSelection + + + QAbstractItemView::SelectionBehavior::SelectItems + + + false + + + + + + + + Layouts + + + + 0 + + + 0 + + + 0 + + + 3 + + + 0 + + + + + + + + + 0 + 0 + + + + + 200 + 0 + + + + QAbstractItemView::SelectionMode::SingleSelection + + + QAbstractItemView::SelectionBehavior::SelectItems + + + false + + + false + + + + + @@ -236,7 +230,7 @@ - Qt::Horizontal + Qt::Orientation::Horizontal @@ -288,7 +282,7 @@ - Qt::Horizontal + Qt::Orientation::Horizontal @@ -298,10 +292,10 @@ - QFrame::StyledPanel + QFrame::Shape::StyledPanel - QFrame::Raised + QFrame::Shadow::Raised 1 @@ -325,10 +319,10 @@ - QFrame::StyledPanel + QFrame::Shape::StyledPanel - QFrame::Raised + QFrame::Shadow::Raised @@ -377,10 +371,10 @@ - QFrame::StyledPanel + QFrame::Shape::StyledPanel - QFrame::Raised + QFrame::Shadow::Raised @@ -401,10 +395,10 @@ - QFrame::NoFrame + QFrame::Shape::NoFrame - QFrame::Raised + QFrame::Shadow::Raised @@ -585,7 +579,7 @@ - Qt::Horizontal + Qt::Orientation::Horizontal @@ -608,10 +602,10 @@ - QFrame::StyledPanel + QFrame::Shape::StyledPanel - QFrame::Raised + QFrame::Shadow::Raised @@ -658,6 +652,9 @@ Metatiles + + QLayout::SizeConstraint::SetNoConstraint + 3 @@ -685,17 +682,17 @@ 30 - Qt::Horizontal + Qt::Orientation::Horizontal - + - QFrame::NoFrame + QFrame::Shape::NoFrame - QFrame::Plain + QFrame::Shadow::Plain @@ -719,7 +716,7 @@ - Qt::Vertical + Qt::Orientation::Vertical @@ -756,10 +753,10 @@ - QFrame::NoFrame + QFrame::Shape::NoFrame - QFrame::Plain + QFrame::Shadow::Plain true @@ -769,7 +766,7 @@ 0 0 - 423 + 424 79 @@ -792,7 +789,7 @@ - Qt::Horizontal + Qt::Orientation::Horizontal @@ -814,17 +811,17 @@ <html><head/><body><p>The border is a 2x2 metatile which is repeated outside of the map layout's boundary. Draw on this border area to modify it.</p></body></html> - Qt::ScrollBarAlwaysOff + Qt::ScrollBarPolicy::ScrollBarAlwaysOff - Qt::ScrollBarAlwaysOff + Qt::ScrollBarPolicy::ScrollBarAlwaysOff - Qt::Horizontal + Qt::Orientation::Horizontal @@ -875,10 +872,10 @@ - QFrame::NoFrame + QFrame::Shape::NoFrame - QFrame::Plain + QFrame::Shadow::Plain true @@ -888,7 +885,7 @@ 0 0 - 423 + 424 79 @@ -911,7 +908,7 @@ - Qt::Horizontal + Qt::Orientation::Horizontal @@ -936,17 +933,17 @@ - Qt::ScrollBarAlwaysOff + Qt::ScrollBarPolicy::ScrollBarAlwaysOff - Qt::ScrollBarAlwaysOff + Qt::ScrollBarPolicy::ScrollBarAlwaysOff - Qt::Horizontal + Qt::Orientation::Horizontal @@ -970,19 +967,19 @@ - Qt::ScrollBarAlwaysOn + Qt::ScrollBarPolicy::ScrollBarAlwaysOn - Qt::ScrollBarAsNeeded + Qt::ScrollBarPolicy::ScrollBarAsNeeded - QAbstractScrollArea::AdjustIgnored + QAbstractScrollArea::SizeAdjustPolicy::AdjustIgnored true - Qt::AlignHCenter|Qt::AlignTop + Qt::AlignmentFlag::AlignHCenter|Qt::AlignmentFlag::AlignTop @@ -992,7 +989,7 @@ 8 0 - 411 + 412 446 @@ -1021,7 +1018,7 @@ - Qt::Horizontal + Qt::Orientation::Horizontal @@ -1043,20 +1040,20 @@ - Qt::ScrollBarAlwaysOff + Qt::ScrollBarPolicy::ScrollBarAlwaysOff - Qt::ScrollBarAlwaysOff + Qt::ScrollBarPolicy::ScrollBarAlwaysOff - QAbstractScrollArea::AdjustIgnored + QAbstractScrollArea::SizeAdjustPolicy::AdjustIgnored - Qt::Horizontal + Qt::Orientation::Horizontal @@ -1069,7 +1066,7 @@ - Qt::Vertical + Qt::Orientation::Vertical @@ -1087,7 +1084,7 @@ - + @@ -1096,23 +1093,23 @@ - QFrame::StyledPanel + QFrame::Shape::StyledPanel - QFrame::Raised + QFrame::Shadow::Raised - + Primary Tileset - + - Qt::StrongFocus + Qt::FocusPolicy::StrongFocus <html><head/><body><p>Primary Tileset</p><p>Defines the first 0x200 metatiles available for the map.</p></body></html> @@ -1122,17 +1119,17 @@ - + Secondary Tileset - + - Qt::StrongFocus + Qt::FocusPolicy::StrongFocus <html><head/><body><p>Secondary Tileset</p><p>Defines the second 0x200 metatiles available for the map.</p></body></html> @@ -1142,6 +1139,34 @@ + + + + QFrame::Shape::NoFrame + + + QFrame::Shadow::Raised + + + + 2 + + + 2 + + + + + Layout + + + + + + + + + @@ -1162,7 +1187,7 @@ - QLayout::SetDefaultConstraint + QLayout::SizeConstraint::SetDefaultConstraint 3 @@ -1193,8 +1218,8 @@ 0 0 - 427 - 594 + 428 + 633 @@ -1216,7 +1241,7 @@ - Qt::Horizontal + Qt::Orientation::Horizontal @@ -1241,17 +1266,17 @@ - Qt::ScrollBarAlwaysOff + Qt::ScrollBarPolicy::ScrollBarAlwaysOff - Qt::ScrollBarAlwaysOff + Qt::ScrollBarPolicy::ScrollBarAlwaysOff - Qt::Horizontal + Qt::Orientation::Horizontal @@ -1264,7 +1289,7 @@ - Qt::Vertical + Qt::Orientation::Vertical @@ -1290,7 +1315,7 @@ 30 - Qt::Horizontal + Qt::Orientation::Horizontal @@ -1306,17 +1331,17 @@ 50 - Qt::Horizontal + Qt::Orientation::Horizontal - QFrame::StyledPanel + QFrame::Shape::StyledPanel - QFrame::Raised + QFrame::Shadow::Raised @@ -1342,7 +1367,7 @@ - Qt::Horizontal + Qt::Orientation::Horizontal @@ -1403,7 +1428,7 @@ - QFrame::NoFrame + QFrame::Shape::NoFrame true @@ -1413,8 +1438,8 @@ 0 0 - 382 - 699 + 383 + 744 @@ -1436,10 +1461,10 @@ <html><head/><body><p>No prefabs have been created for the currently-used tilesets. Create some by using the button above!</p><p>Prefabs are &quot;prefabricated&quot; metatile selections that are used for easy selecting of complicated map structures. For example, a useful prefab could be a building or tree formation, which would otherwise be annoying to paint with the regular metatile picker.</p></body></html> - Qt::RichText + Qt::TextFormat::RichText - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop true @@ -1512,10 +1537,10 @@ - QFrame::NoFrame + QFrame::Shape::NoFrame - QFrame::Raised + QFrame::Shadow::Raised 0 @@ -1552,7 +1577,7 @@ :/icons/add.ico:/icons/add.ico - Qt::ToolButtonTextBesideIcon + Qt::ToolButtonStyle::ToolButtonTextBesideIcon @@ -1575,7 +1600,7 @@ :/icons/delete.ico:/icons/delete.ico - Qt::ToolButtonTextBesideIcon + Qt::ToolButtonStyle::ToolButtonTextBesideIcon false @@ -1585,7 +1610,7 @@ - Qt::Horizontal + Qt::Orientation::Horizontal @@ -1620,7 +1645,7 @@ There are no events on the current map. - Qt::AlignCenter + Qt::AlignmentFlag::AlignCenter @@ -1686,7 +1711,7 @@ - Qt::Horizontal + Qt::Orientation::Horizontal @@ -1701,13 +1726,13 @@ - QFrame::NoFrame + QFrame::Shape::NoFrame true - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop @@ -1715,7 +1740,7 @@ 0 0 100 - 30 + 16 @@ -1780,7 +1805,7 @@ - Qt::Horizontal + Qt::Orientation::Horizontal @@ -1795,13 +1820,13 @@ - QFrame::NoFrame + QFrame::Shape::NoFrame true - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop @@ -1809,7 +1834,7 @@ 0 0 100 - 30 + 16 @@ -1874,7 +1899,7 @@ - Qt::Horizontal + Qt::Orientation::Horizontal @@ -1889,13 +1914,13 @@ - QFrame::NoFrame + QFrame::Shape::NoFrame true - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop @@ -1903,7 +1928,7 @@ 0 0 100 - 30 + 16 @@ -1974,7 +1999,7 @@ - Qt::Horizontal + Qt::Orientation::Horizontal @@ -1989,13 +2014,13 @@ - QFrame::NoFrame + QFrame::Shape::NoFrame true - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop @@ -2003,7 +2028,7 @@ 0 0 100 - 30 + 16 @@ -2068,7 +2093,7 @@ - Qt::Horizontal + Qt::Orientation::Horizontal @@ -2083,13 +2108,13 @@ - QFrame::NoFrame + QFrame::Shape::NoFrame true - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop @@ -2097,7 +2122,7 @@ 0 0 100 - 30 + 16 @@ -2137,13 +2162,13 @@ - QFrame::NoFrame + QFrame::Shape::NoFrame true - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop @@ -2206,14 +2231,14 @@ - QFrame::StyledPanel + QFrame::Shape::StyledPanel - QFrame::Raised + QFrame::Shadow::Raised - QFormLayout::FieldsStayAtSizeHint + QFormLayout::FieldGrowthPolicy::FieldsStayAtSizeHint 12 @@ -2417,10 +2442,10 @@ - QFrame::StyledPanel + QFrame::Shape::StyledPanel - QFrame::Raised + QFrame::Shadow::Raised @@ -2433,10 +2458,10 @@ - QFrame::NoFrame + QFrame::Shape::NoFrame - QFrame::Plain + QFrame::Shadow::Plain @@ -2468,7 +2493,7 @@ - Qt::Horizontal + Qt::Orientation::Horizontal @@ -2549,10 +2574,10 @@ - QFrame::StyledPanel + QFrame::Shape::StyledPanel - QFrame::Raised + QFrame::Shadow::Raised @@ -2564,10 +2589,10 @@ - QFrame::StyledPanel + QFrame::Shape::StyledPanel - QFrame::Raised + QFrame::Shadow::Raised @@ -2632,7 +2657,7 @@ - Qt::Horizontal + Qt::Orientation::Horizontal @@ -2717,7 +2742,7 @@ 30 - Qt::Horizontal + Qt::Orientation::Horizontal @@ -2733,7 +2758,7 @@ 30 - Qt::Horizontal + Qt::Orientation::Horizontal @@ -2765,7 +2790,7 @@ 30 - Qt::Horizontal + Qt::Orientation::Horizontal @@ -2796,7 +2821,7 @@ - Qt::Horizontal + Qt::Orientation::Horizontal @@ -2812,22 +2837,22 @@ false - Qt::ScrollBarAsNeeded + Qt::ScrollBarPolicy::ScrollBarAsNeeded - Qt::ScrollBarAsNeeded + Qt::ScrollBarPolicy::ScrollBarAsNeeded - QAbstractScrollArea::AdjustIgnored + QAbstractScrollArea::SizeAdjustPolicy::AdjustIgnored - QGraphicsView::NoDrag + QGraphicsView::DragMode::NoDrag - QGraphicsView::AnchorUnderMouse + QGraphicsView::ViewportAnchor::AnchorUnderMouse - QGraphicsView::AnchorUnderMouse + QGraphicsView::ViewportAnchor::AnchorUnderMouse @@ -2838,10 +2863,10 @@ - QFrame::StyledPanel + QFrame::Shape::StyledPanel - QFrame::Raised + QFrame::Shadow::Raised @@ -2858,10 +2883,10 @@ - QFrame::NoFrame + QFrame::Shape::NoFrame - Qt::ScrollBarAlwaysOff + Qt::ScrollBarPolicy::ScrollBarAlwaysOff true @@ -2871,8 +2896,8 @@ 0 0 - 365 - 651 + 204 + 16 @@ -2894,7 +2919,7 @@ - Qt::Vertical + Qt::Orientation::Vertical @@ -2922,19 +2947,19 @@ - QFrame::NoFrame + QFrame::Shape::NoFrame - QFrame::Raised + QFrame::Shadow::Raised - QFrame::NoFrame + QFrame::Shape::NoFrame - QFrame::Plain + QFrame::Shadow::Plain @@ -2947,7 +2972,7 @@ - QComboBox::AdjustToContents + QComboBox::SizeAdjustPolicy::AdjustToContents @@ -2988,7 +3013,7 @@ - Qt::Horizontal + Qt::Orientation::Horizontal @@ -3040,8 +3065,8 @@ 0 0 - 1287 - 22 + 1298 + 37 @@ -3412,7 +3437,7 @@ Check for Updates... - QAction::ApplicationSpecificRole + QAction::MenuRole::ApplicationSpecificRole @@ -3482,11 +3507,22 @@ QWidget
mapview.h
+ + MapTree + QTreeView +
maplistmodels.h
+
NoScrollGraphicsView QGraphicsView
mapview.h
+ + MapListToolBar + QFrame +
maplisttoolbar.h
+ 1 +
diff --git a/forms/maplisttoolbar.ui b/forms/maplisttoolbar.ui new file mode 100644 index 000000000..54eb48d08 --- /dev/null +++ b/forms/maplisttoolbar.ui @@ -0,0 +1,175 @@ + + + MapListToolBar + + + + 0 + 0 + 274 + 32 + + + + Form + + + + 0 + + + 3 + + + 3 + + + 0 + + + 3 + + + + + Add a new folder to the list. + + + + + + + :/icons/folder_add.ico:/icons/folder_add.ico + + + QToolButton::ToolButtonPopupMode::InstantPopup + + + true + + + + + + + Hide empty folders in the list. + + + + :/icons/folder_eye_open.ico + :/icons/folder_eye_closed.ico:/icons/folder_eye_open.ico + + + true + + + QToolButton::ToolButtonPopupMode::InstantPopup + + + true + + + + + + + Expand all folders in the list. + + + + + + + :/icons/expand_all.ico:/icons/expand_all.ico + + + QToolButton::ToolButtonPopupMode::InstantPopup + + + true + + + + + + + Collapse all folders in the list. + + + + + + + :/icons/collapse_all.ico:/icons/collapse_all.ico + + + QToolButton::ToolButtonPopupMode::InstantPopup + + + true + + + + + + + If enabled, folders may be renamed and items in the list may be rearranged. + + + + + + + :/icons/lock_edit.ico + :/icons/unlock_edit.ico:/icons/lock_edit.ico + + + true + + + QToolButton::ToolButtonPopupMode::InstantPopup + + + true + + + + + + + Qt::Orientation::Horizontal + + + QSizePolicy::Policy::Preferred + + + + 12 + 19 + + + + + + + + true + + + + + + Filter... + + + true + + + + + + + + + + diff --git a/forms/newmappopup.ui b/forms/newmappopup.ui index b102b1ccc..3a83073f8 100644 --- a/forms/newmappopup.ui +++ b/forms/newmappopup.ui @@ -7,7 +7,7 @@ 0 0 410 - 621 + 687
@@ -73,14 +73,14 @@
- + Map Width - + <html><head/><body><p>Width (in blocks) of the new map.</p></body></html> @@ -90,14 +90,14 @@ - + Map Height - + <html><head/><body><p>Height (in blocks) of the new map.</p></body></html> @@ -107,14 +107,14 @@ - + Border Width - + <html><head/><body><p>Width (in blocks) of the new map's border.</p></body></html> @@ -124,14 +124,14 @@ - + Border Height - + <html><head/><body><p>Height (in blocks) of the new map's border.</p></body></html> @@ -141,14 +141,14 @@ - + Primary Tileset - + <html><head/><body><p>The primary tileset for the new map.</p></body></html> @@ -158,14 +158,14 @@ - + Secondary Tileset - + <html><head/><body><p>The secondary tileset for the new map.</p></body></html> @@ -175,14 +175,14 @@ - + Type - + <html><head/><body><p>The map type is a general attribute, which is used for many different things. For example. it determines whether biking or running is allowed.</p></body></html> @@ -192,14 +192,14 @@ - + Location - + <html><head/><body><p>The section of the region map which the map is grouped under. This also determines the name of the map that is displayed when the player enters it.</p></body></html> @@ -209,14 +209,14 @@ - + Song - + <html><head/><body><p>The default background music for this map.</p></body></html> @@ -226,14 +226,14 @@ - + Can Fly To - + <html><head/><body><p>Whether to add a heal location to the new map.</p></body></html> @@ -243,14 +243,14 @@ - + Show Location Name - + <html><head/><body><p>Whether or not to display the location name when the player enters the map.</p></body></html> @@ -260,14 +260,14 @@ - + Allow Running - + <html><head/><body><p>Allows the player to use Running Shoes</p></body></html> @@ -277,14 +277,14 @@ - + Allow Biking - + <html><head/><body><p>Allows the player to use a Bike</p></body></html> @@ -294,14 +294,14 @@ - + Allow Dig & Escape Rope - + <html><head/><body><p>Allows the player to use Dig or Escape Rope</p></body></html> @@ -311,14 +311,14 @@ - + Floor Number - + <html><head/><body><p>Floor number to be used for maps with elevators.</p></body></html> @@ -328,6 +328,96 @@ + + + + false + + + + + + + Layout + + + + + + + + + Use Existing Layout + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + +
@@ -396,7 +486,7 @@ 0 0 410 - 21 + 22
diff --git a/include/config.h b/include/config.h index b42bd4f8d..6b64b612c 100644 --- a/include/config.h +++ b/include/config.h @@ -22,12 +22,6 @@ static const QVersionNumber porymapVersion = QVersionNumber::fromString(PORYMAP_ #define CONFIG_BACKWARDS_COMPATABILITY -enum MapSortOrder { - Group = 0, - Area = 1, - Layout = 2, -}; - class KeyValueConfigBase { public: @@ -56,7 +50,7 @@ class PorymapConfig: public KeyValueConfigBase this->recentProjects.clear(); this->projectManuallyClosed = false; this->reopenOnLaunch = true; - this->mapSortOrder = MapSortOrder::Group; + this->mapListTab = 0; this->prettyCursors = true; this->mirrorConnectingMaps = true; this->showDiveEmergeMaps = false; @@ -107,7 +101,7 @@ class PorymapConfig: public KeyValueConfigBase bool reopenOnLaunch; bool projectManuallyClosed; - MapSortOrder mapSortOrder; + int mapListTab; bool prettyCursors; bool mirrorConnectingMaps; bool showDiveEmergeMaps; @@ -219,7 +213,6 @@ enum ProjectIdentifier { define_map_empty, define_map_section_prefix, define_map_section_empty, - define_map_section_count, define_species_prefix, regex_behaviors, regex_obj_event_gfx, @@ -275,7 +268,6 @@ enum ProjectFilePath { constants_obj_event_movement, constants_obj_events, constants_event_bg, - constants_region_map_sections, constants_metatile_labels, constants_metatile_behaviors, constants_species, @@ -406,7 +398,7 @@ class UserConfig: public KeyValueConfigBase reset(); } virtual void reset() override { - this->recentMap = QString(); + this->recentMapOrLayout = QString(); this->useEncounterJson = true; this->customScripts.clear(); this->readKeys.clear(); @@ -418,7 +410,7 @@ class UserConfig: public KeyValueConfigBase QList getCustomScriptsEnabled(); QString projectDir; - QString recentMap; + QString recentMapOrLayout; bool useEncounterJson; protected: diff --git a/include/core/editcommands.h b/include/core/editcommands.h index 40f0cd62e..5bf99784f 100644 --- a/include/core/editcommands.h +++ b/include/core/editcommands.h @@ -9,8 +9,8 @@ #include #include -class MapPixmapItem; class Map; +class Layout; class Blockdata; class Event; class DraggablePixmapItem; @@ -24,9 +24,9 @@ enum CommandId { ID_PaintCollision, ID_BucketFillCollision, ID_MagicFillCollision, - ID_ResizeMap, + ID_ResizeLayout, ID_PaintBorder, - ID_ScriptEditMap, + ID_ScriptEditLayout, ID_EventMove, ID_EventShift, ID_EventCreate, @@ -50,7 +50,7 @@ enum CommandId { /// onto the map using the pencil tool. class PaintMetatile : public QUndoCommand { public: - PaintMetatile(Map *map, + PaintMetatile(Layout *layout, const Blockdata &oldMetatiles, const Blockdata &newMetatiles, unsigned actionId, QUndoCommand *parent = nullptr); @@ -61,7 +61,7 @@ class PaintMetatile : public QUndoCommand { int id() const override { return CommandId::ID_PaintMetatile; } private: - Map *map; + Layout *layout; Blockdata newMetatiles; Blockdata oldMetatiles; @@ -75,10 +75,10 @@ class PaintMetatile : public QUndoCommand { /// on the metatile collision and elevation. class PaintCollision : public PaintMetatile { public: - PaintCollision(Map *map, + PaintCollision(Layout *layout, const Blockdata &oldCollision, const Blockdata &newCollision, unsigned actionId, QUndoCommand *parent = nullptr) - : PaintMetatile(map, oldCollision, newCollision, actionId, parent) { + : PaintMetatile(layout, oldCollision, newCollision, actionId, parent) { setText("Paint Collision"); } @@ -90,7 +90,7 @@ class PaintCollision : public PaintMetatile { /// Implements a command to commit paint actions on the map border. class PaintBorder : public QUndoCommand { public: - PaintBorder(Map *map, + PaintBorder(Layout *layout, const Blockdata &oldBorder, const Blockdata &newBorder, unsigned actionId, QUndoCommand *parent = nullptr); @@ -101,7 +101,7 @@ class PaintBorder : public QUndoCommand { int id() const override { return CommandId::ID_PaintBorder; } private: - Map *map; + Layout *layout; Blockdata newBorder; Blockdata oldBorder; @@ -115,10 +115,10 @@ class PaintBorder : public QUndoCommand { /// with the bucket tool onto the map. class BucketFillMetatile : public PaintMetatile { public: - BucketFillMetatile(Map *map, + BucketFillMetatile(Layout *layout, const Blockdata &oldMetatiles, const Blockdata &newMetatiles, unsigned actionId, QUndoCommand *parent = nullptr) - : PaintMetatile(map, oldMetatiles, newMetatiles, actionId, parent) { + : PaintMetatile(layout, oldMetatiles, newMetatiles, actionId, parent) { setText("Bucket Fill Metatiles"); } @@ -131,10 +131,10 @@ class BucketFillMetatile : public PaintMetatile { /// on the metatile collision and elevation. class BucketFillCollision : public PaintCollision { public: - BucketFillCollision(Map *map, + BucketFillCollision(Layout *layout, const Blockdata &oldCollision, const Blockdata &newCollision, QUndoCommand *parent = nullptr) - : PaintCollision(map, oldCollision, newCollision, -1, parent) { + : PaintCollision(layout, oldCollision, newCollision, -1, parent) { setText("Flood Fill Collision"); } @@ -148,10 +148,10 @@ class BucketFillCollision : public PaintCollision { /// with the bucket or paint tool onto the map. class MagicFillMetatile : public PaintMetatile { public: - MagicFillMetatile(Map *map, + MagicFillMetatile(Layout *layout, const Blockdata &oldMetatiles, const Blockdata &newMetatiles, unsigned actionId, QUndoCommand *parent = nullptr) - : PaintMetatile(map, oldMetatiles, newMetatiles, actionId, parent) { + : PaintMetatile(layout, oldMetatiles, newMetatiles, actionId, parent) { setText("Magic Fill Metatiles"); } @@ -163,10 +163,10 @@ class MagicFillMetatile : public PaintMetatile { /// Implements a command to commit magic fill collision actions. class MagicFillCollision : public PaintCollision { public: - MagicFillCollision(Map *map, + MagicFillCollision(Layout *layout, const Blockdata &oldCollision, const Blockdata &newCollision, QUndoCommand *parent = nullptr) - : PaintCollision(map, oldCollision, newCollision, -1, parent) { + : PaintCollision(layout, oldCollision, newCollision, -1, parent) { setText("Magic Fill Collision"); } @@ -179,7 +179,7 @@ class MagicFillCollision : public PaintCollision { /// Implements a command to commit metatile shift actions. class ShiftMetatiles : public QUndoCommand { public: - ShiftMetatiles(Map *map, + ShiftMetatiles(Layout *layout, const Blockdata &oldMetatiles, const Blockdata &newMetatiles, unsigned actionId, QUndoCommand *parent = nullptr); @@ -190,7 +190,7 @@ class ShiftMetatiles : public QUndoCommand { int id() const override { return CommandId::ID_ShiftMetatiles; } private: - Map *map; + Layout *layout= nullptr; Blockdata newMetatiles; Blockdata oldMetatiles; @@ -201,9 +201,9 @@ class ShiftMetatiles : public QUndoCommand { /// Implements a command to commit a map or border resize action. -class ResizeMap : public QUndoCommand { +class ResizeLayout : public QUndoCommand { public: - ResizeMap(Map *map, QSize oldMapDimensions, QSize newMapDimensions, + ResizeLayout(Layout *layout, QSize oldLayoutDimensions, QSize newLayoutDimensions, const Blockdata &oldMetatiles, const Blockdata &newMetatiles, QSize oldBorderDimensions, QSize newBorderDimensions, const Blockdata &oldBorder, const Blockdata &newBorder, @@ -213,15 +213,15 @@ class ResizeMap : public QUndoCommand { void redo() override; bool mergeWith(const QUndoCommand *) override { return false; } - int id() const override { return CommandId::ID_ResizeMap; } + int id() const override { return CommandId::ID_ResizeLayout; } private: - Map *map; + Layout *layout = nullptr; - int oldMapWidth; - int oldMapHeight; - int newMapWidth; - int newMapHeight; + int oldLayoutWidth; + int oldLayoutHeight; + int newLayoutWidth; + int newLayoutHeight; int oldBorderWidth; int oldBorderHeight; @@ -351,10 +351,10 @@ class EventPaste : public EventDuplicate { /// Implements a command to commit map edits from the scripting API. /// The scripting api can edit map/border blocks and dimensions. -class ScriptEditMap : public QUndoCommand { +class ScriptEditLayout : public QUndoCommand { public: - ScriptEditMap(Map *map, - QSize oldMapDimensions, QSize newMapDimensions, + ScriptEditLayout(Layout *layout, + QSize oldLayoutDimensions, QSize newLayoutDimensions, const Blockdata &oldMetatiles, const Blockdata &newMetatiles, QSize oldBorderDimensions, QSize newBorderDimensions, const Blockdata &oldBorder, const Blockdata &newBorder, @@ -364,10 +364,10 @@ class ScriptEditMap : public QUndoCommand { void redo() override; bool mergeWith(const QUndoCommand *) override { return false; } - int id() const override { return CommandId::ID_ScriptEditMap; } + int id() const override { return CommandId::ID_ScriptEditLayout; } private: - Map *map; + Layout *layout = nullptr; Blockdata newMetatiles; Blockdata oldMetatiles; @@ -375,10 +375,10 @@ class ScriptEditMap : public QUndoCommand { Blockdata newBorder; Blockdata oldBorder; - int oldMapWidth; - int oldMapHeight; - int newMapWidth; - int newMapHeight; + int oldLayoutWidth; + int oldLayoutHeight; + int newLayoutWidth; + int newLayoutHeight; int oldBorderWidth; int oldBorderHeight; diff --git a/include/core/map.h b/include/core/map.h index 2333c862a..acc52d908 100644 --- a/include/core/map.h +++ b/include/core/map.h @@ -25,7 +25,7 @@ // porymap will reflect changes to it, but the value is hard-coded in the projects at the moment #define BORDER_DISTANCE 7 -class MapPixmapItem; +class LayoutPixmapItem; class CollisionPixmapItem; class BorderMetatilesPixmapItem; @@ -39,6 +39,7 @@ class Map : public QObject public: QString name; QString constantName; + QString song; QString layoutId; QString location; @@ -51,20 +52,22 @@ class Map : public QObject bool allowEscaping; int floorNumber = 0; QString battle_scene; + QString sharedEventsMap = ""; QString sharedScriptsMap = ""; + QStringList scriptsFileLabels; QMap customHeaders; - MapLayout *layout; + + Layout *layout = nullptr; + void setLayout(Layout *layout); + bool isPersistedToFile = true; bool hasUnsavedDataChanges = false; + bool needsLayoutDir = true; bool needsHealLocation = false; bool scriptsLoaded = false; - QImage collision_image; - QPixmap collision_pixmap; - QImage image; - QPixmap pixmap; QMap> events; QList ownedEvents; // for memory management @@ -74,64 +77,34 @@ class Map : public QObject void setName(QString mapName); static QString mapConstantFromName(QString mapName, bool includePrefix = true); + int getWidth(); int getHeight(); int getBorderWidth(); int getBorderHeight(); - QPixmap render(bool ignoreCache = false, MapLayout *fromLayout = nullptr, QRect bounds = QRect(0, 0, -1, -1)); - QPixmap renderCollision(bool ignoreCache); - bool mapBlockChanged(int i, const Blockdata &cache); - bool borderBlockChanged(int i, const Blockdata &cache); - void cacheBlockdata(); - void cacheCollision(); - bool getBlock(int x, int y, Block *out); - void setBlock(int x, int y, Block block, bool enableScriptCallback = false); - void setBlockdata(Blockdata blockdata, bool enableScriptCallback = false); - uint16_t getBorderMetatileId(int x, int y); - void setBorderMetatileId(int x, int y, uint16_t metatileId, bool enableScriptCallback = false); - void setBorderBlockData(Blockdata blockdata, bool enableScriptCallback = false); - void floodFillCollisionElevation(int x, int y, uint16_t collision, uint16_t elevation); - void _floodFillCollisionElevation(int x, int y, uint16_t collision, uint16_t elevation); - void magicFillCollisionElevation(int x, int y, uint16_t collision, uint16_t elevation); + QList getAllEvents() const; QStringList getScriptLabels(Event::Group group = Event::Group::None); + QString getScriptsFilePath() const; + void openScript(QString label); void removeEvent(Event *); void addEvent(Event *); + void deleteConnections(); QList getConnections() const; void removeConnection(MapConnection *); void addConnection(MapConnection *); void loadConnection(MapConnection *); - QRect getConnectionRect(const QString &direction, MapLayout *fromLayout = nullptr); - QPixmap renderConnection(const QString &direction, MapLayout *fromLayout = nullptr); - QPixmap renderBorder(bool ignoreCache = false); - void setDimensions(int newWidth, int newHeight, bool setNewBlockdata = true, bool enableScriptCallback = false); - void setBorderDimensions(int newWidth, int newHeight, bool setNewBlockdata = true, bool enableScriptCallback = false); - void clearBorderCache(); - void cacheBorder(); - bool hasUnsavedChanges(); - bool isWithinBounds(int x, int y); - bool isWithinBorderBounds(int x, int y); - void openScript(QString label); - QString getScriptsFilePath() const; - - MapPixmapItem *mapItem = nullptr; - void setMapItem(MapPixmapItem *item) { mapItem = item; } - - CollisionPixmapItem *collisionItem = nullptr; - void setCollisionItem(CollisionPixmapItem *item) { collisionItem = item; } - - BorderMetatilesPixmapItem *borderItem = nullptr; - void setBorderItem(BorderMetatilesPixmapItem *item) { borderItem = item; } + QRect getConnectionRect(const QString &direction, Layout *fromLayout = nullptr); + QPixmap renderConnection(const QString &direction, Layout *fromLayout = nullptr); QUndoStack editHistory; void modify(); void clean(); + bool hasUnsavedChanges() const; void pruneEditHistory(); private: - void setNewDimensionsBlockdata(int newWidth, int newHeight); - void setNewBorderDimensionsBlockdata(int newWidth, int newHeight); void trackConnection(MapConnection*); // MapConnections in 'ownedConnections' but not 'connections' persist in the edit history. @@ -141,7 +114,6 @@ class Map : public QObject signals: void modified(); void mapDimensionsChanged(const QSize &size); - void mapNeedsRedrawing(); void openScriptRequested(QString label); void connectionAdded(MapConnection*); void connectionRemoved(MapConnection*); diff --git a/include/core/maplayout.h b/include/core/maplayout.h index 13215fff0..b617002fa 100644 --- a/include/core/maplayout.h +++ b/include/core/maplayout.h @@ -7,41 +7,135 @@ #include #include #include +#include -class MapLayout { +class Map; +class LayoutPixmapItem; +class CollisionPixmapItem; +class BorderMetatilesPixmapItem; + +class Layout : public QObject { + Q_OBJECT public: - MapLayout() {} + Layout() {} + static QString layoutConstantFromName(QString mapName); + + bool loaded = false; + QString id; QString name; + int width; int height; int border_width; int border_height; + QString border_path; QString blockdata_path; + QString tileset_primary_label; QString tileset_secondary_label; + Tileset *tileset_primary = nullptr; Tileset *tileset_secondary = nullptr; + Blockdata blockdata; + + QImage image; + QPixmap pixmap; QImage border_image; QPixmap border_pixmap; + QImage collision_image; + QPixmap collision_pixmap; + Blockdata border; Blockdata cached_blockdata; Blockdata cached_collision; Blockdata cached_border; struct { Blockdata blocks; - QSize mapDimensions; + QSize layoutDimensions; Blockdata border; QSize borderDimensions; } lastCommitBlocks; // to track map changes + QList metatileLayerOrder; + QList metatileLayerOpacity; + + LayoutPixmapItem *layoutItem = nullptr; + CollisionPixmapItem *collisionItem = nullptr; + BorderMetatilesPixmapItem *borderItem = nullptr; + + QUndoStack editHistory; + + // to simplify new layout settings transfer between functions + struct SimpleSettings { + QString id; + QString name; + int width; + int height; + QString tileset_primary_label; + QString tileset_secondary_label; + QString from_id = QString(); + }; + +public: + Layout *copy(); + void copyFrom(Layout *other); + int getWidth(); int getHeight(); int getBorderWidth(); int getBorderHeight(); + + bool isWithinBounds(int x, int y); + bool isWithinBorderBounds(int x, int y); + + bool getBlock(int x, int y, Block *out); + void setBlock(int x, int y, Block block, bool enableScriptCallback = false); + void setBlockdata(Blockdata blockdata, bool enableScriptCallback = false); + + void setDimensions(int newWidth, int newHeight, bool setNewBlockdata = true, bool enableScriptCallback = false); + void setBorderDimensions(int newWidth, int newHeight, bool setNewBlockdata = true, bool enableScriptCallback = false); + + void cacheBlockdata(); + void cacheCollision(); + void clearBorderCache(); + void cacheBorder(); + + bool hasUnsavedChanges() const; + + bool layoutBlockChanged(int i, const Blockdata &cache); + + uint16_t getBorderMetatileId(int x, int y); + void setBorderMetatileId(int x, int y, uint16_t metatileId, bool enableScriptCallback = false); + void setBorderBlockData(Blockdata blockdata, bool enableScriptCallback = false); + + void floodFillCollisionElevation(int x, int y, uint16_t collision, uint16_t elevation); + void _floodFillCollisionElevation(int x, int y, uint16_t collision, uint16_t elevation); + void magicFillCollisionElevation(int x, int y, uint16_t collision, uint16_t elevation); + + QPixmap render(bool ignoreCache = false, Layout *fromLayout = nullptr, QRect bounds = QRect(0, 0, -1, -1)); + QPixmap renderCollision(bool ignoreCache); + // QPixmap renderConnection(MapConnection, Layout *); + QPixmap renderBorder(bool ignoreCache = false); + + QPixmap getLayoutItemPixmap(); + + void setLayoutItem(LayoutPixmapItem *item) { layoutItem = item; } + void setCollisionItem(CollisionPixmapItem *item) { collisionItem = item; } + void setBorderItem(BorderMetatilesPixmapItem *item) { borderItem = item; } + +private: + void setNewDimensionsBlockdata(int newWidth, int newHeight); + void setNewBorderDimensionsBlockdata(int newWidth, int newHeight); + +signals: + void layoutChanged(Layout *layout); + //void modified(); + void layoutDimensionsChanged(const QSize &size); + void needsRedrawing(); }; #endif // MAPLAYOUT_H diff --git a/include/core/mapparser.h b/include/core/mapparser.h index 21e3074e4..4032154a5 100644 --- a/include/core/mapparser.h +++ b/include/core/mapparser.h @@ -10,7 +10,7 @@ class MapParser { public: MapParser(); - MapLayout *parse(QString filepath, bool *error, Project *project); + Layout *parse(QString filepath, bool *error, Project *project); }; #endif // MAPPARSER_H diff --git a/include/core/regionmap.h b/include/core/regionmap.h index bc05a84f7..822df79b3 100644 --- a/include/core/regionmap.h +++ b/include/core/regionmap.h @@ -57,8 +57,8 @@ class RegionMap : public QObject bool loadLayout(poryjson::Json); bool loadEntries(); - void setEntries(tsl::ordered_map *entries) { this->region_map_entries = entries; } - void setEntries(tsl::ordered_map entries) { *(this->region_map_entries) = entries; } + void setEntries(QMap *entries) { this->region_map_entries = entries; } + void setEntries(const QMap &entries) { *(this->region_map_entries) = entries; } void clearEntries() { this->region_map_entries->clear(); } MapSectionEntry getEntry(QString section); void setEntry(QString section, MapSectionEntry entry); @@ -114,8 +114,6 @@ class RegionMap : public QObject void setLayer(QString layer) { this->current_layer = layer; } QString getLayer() { return this->current_layer; } - QString fixCase(QString); - int padLeft() { return this->offset_left; } int padTop() { return this->offset_top; } int padRight() { return this->tilemap_width - this->layout_width - this->offset_left; } @@ -149,14 +147,12 @@ class RegionMap : public QObject const QString section_prefix; const QString default_map_section; - const QString count_map_section; signals: void mapNeedsDisplaying(); private: - // TODO: defaults needed? - tsl::ordered_map *region_map_entries = nullptr; + QMap *region_map_entries = nullptr; QString alias = ""; diff --git a/include/core/regionmapeditcommands.h b/include/core/regionmapeditcommands.h index 69bea2510..05b12bc3b 100644 --- a/include/core/regionmapeditcommands.h +++ b/include/core/regionmapeditcommands.h @@ -64,9 +64,9 @@ class EditLayout : public QUndoCommand { /// Edit Layout Dimensions -class ResizeLayout : public QUndoCommand { +class ResizeRMLayout : public QUndoCommand { public: - ResizeLayout(RegionMap *map, int oldWidth, int oldHeight, int newWidth, int newHeight, + ResizeRMLayout(RegionMap *map, int oldWidth, int oldHeight, int newWidth, int newHeight, QMap> oldLayouts, QMap> newLayouts, QUndoCommand *parent = nullptr); void undo() override; @@ -153,7 +153,7 @@ class ResizeTilemap : public EditTilemap { /// ClearEntries class ClearEntries : public QUndoCommand { public: - ClearEntries(RegionMap *map, tsl::ordered_map, QUndoCommand *parent = nullptr); + ClearEntries(RegionMap *map, QMap, QUndoCommand *parent = nullptr); void undo() override; void redo() override; @@ -163,7 +163,7 @@ class ClearEntries : public QUndoCommand { private: RegionMap *map; - tsl::ordered_map entries; + QMap entries; }; #endif // REGIONMAPEDITCOMMANDS_H diff --git a/include/core/wildmoninfo.h b/include/core/wildmoninfo.h index 6c93d0d20..d0aa29dce 100644 --- a/include/core/wildmoninfo.h +++ b/include/core/wildmoninfo.h @@ -8,7 +8,7 @@ struct WildPokemon { int minLevel = 5; int maxLevel = 5; - QString species = "SPECIES_NONE"; + QString species = "SPECIES_NONE"; // TODO: Use define_species_prefix }; struct WildMonInfo { diff --git a/include/editor.h b/include/editor.h index 88eec3074..f5e94d288 100644 --- a/include/editor.h +++ b/include/editor.h @@ -10,6 +10,7 @@ #include #include #include +#include #include "mapconnection.h" #include "metatileselector.h" @@ -21,7 +22,7 @@ #include "divingmappixmapitem.h" #include "currentselectedmetatilespixmapitem.h" #include "collisionpixmapitem.h" -#include "mappixmapitem.h" +#include "layoutpixmapitem.h" #include "settings.h" #include "gridsettings.h" #include "movablerect.h" @@ -46,18 +47,33 @@ class Editor : public QObject public: Ui::MainWindow* ui; QObject *parent = nullptr; + QPointer project = nullptr; - Map *map = nullptr; + QPointer map = nullptr; + QPointer layout = nullptr; + + QUndoGroup editGroup; // Manages the undo history for each map + Settings *settings; GridSettings gridSettings; + void setProject(Project * project); - void saveProject(); void save(); - void closeProject(); - bool setMap(QString map_name); + void saveProject(); void saveUiFields(); void saveEncounterTabData(); + + void closeProject(); + + bool setMap(QString map_name); + bool setLayout(QString layoutName); + void unsetMap(); + + Tileset *getCurrentMapPrimaryTileset(); + bool displayMap(); + bool displayLayout(); + void displayMetatileSelector(); void displayMapMetatiles(); void displayMapMovementPermissions(); @@ -75,56 +91,54 @@ class Editor : public QObject void updateMapBorder(); void updateMapConnections(); - void setEditingMap(); - void setEditingCollision(); - void setEditingObjects(); - void setEditingConnections(); - void setMapEditingButtonsEnabled(bool enabled); void setConnectionsVisibility(bool visible); void updateDivingMapsVisibility(); void renderDivingConnections(); void addConnection(MapConnection* connection); void removeConnection(MapConnection* connection); - void removeSelectedConnection(); void addNewWildMonGroup(QWidget *window); void deleteWildMonGroup(); + void configureEncounterJSON(QWidget *); EncounterTableModel* getCurrentWildMonTable(); void updateDiveMap(QString mapName); void updateEmergeMap(QString mapName); void setSelectedConnection(MapConnection *connection); + void updatePrimaryTileset(QString tilesetLabel, bool forceLoad = false); void updateSecondaryTileset(QString tilesetLabel, bool forceLoad = false); void toggleBorderVisibility(bool visible, bool enableScriptCallback = true); void updateCustomMapHeaderValues(QTableWidget *); - void configureEncounterJSON(QWidget *); - Tileset *getCurrentMapPrimaryTileset(); DraggablePixmapItem *addMapEvent(Event *event); + bool eventLimitReached(Map *, Event::Type); void selectMapEvent(DraggablePixmapItem *object, bool toggle = false); DraggablePixmapItem *addNewEvent(Event::Type type); void updateSelectedEvents(); void duplicateSelectedEvents(); void redrawObject(DraggablePixmapItem *item); QList getObjects(); + void updateCursorRectPos(int x, int y); void setCursorRectVisible(bool visible); + void updateWarpEventWarning(Event *event); void updateWarpEventWarnings(); - bool eventLimitReached(Map *, Event::Type); QPointer scene = nullptr; QGraphicsPixmapItem *current_view = nullptr; - QPointer map_item = nullptr; + QPointer map_item = nullptr; QList> connection_items; QMap> diving_map_items; QGraphicsPathItem *connection_mask = nullptr; QPointer collision_item = nullptr; QGraphicsItemGroup *events_group = nullptr; + QList borderItems; QGraphicsItemGroup *mapGrid = nullptr; + MapRuler *map_ruler = nullptr; + MovableRect *playerViewRect = nullptr; CursorTileRect *cursorMapTileRect = nullptr; - MapRuler *map_ruler = nullptr; QPointer scene_metatiles = nullptr; QPointer scene_current_metatile_selection = nullptr; @@ -140,8 +154,27 @@ class Editor : public QObject QPointer selected_connection_item = nullptr; QPointer connection_to_select = nullptr; - QString map_edit_mode = "paint"; - QString obj_edit_mode = "select"; + enum class EditAction { None, Paint, Select, Fill, Shift, Pick, Move }; + EditAction mapEditAction = EditAction::Paint; + EditAction objectEditAction = EditAction::Select; + + enum class EditMode { None, Disabled, Metatiles, Collision, Header, Events, Connections, Encounters }; + EditMode editMode = EditMode::None; + void setEditMode(EditMode mode) { this->editMode = mode; } + EditMode getEditMode() { return this->editMode; } + + bool getEditingLayout(); + + void setEditorView(); + + void setEditingMetatiles(); + void setEditingCollision(); + void setEditingHeader(); + void setEditingObjects(); + void setEditingConnections(); + void setEditingEncounters(); + + void setMapEditingButtonsEnabled(bool enabled); int scaleIndex = 2; qreal collisionOpacity = 0.5; @@ -151,10 +184,9 @@ class Editor : public QObject int getBorderDrawDistance(int dimension); - QUndoGroup editGroup; // Manages the undo history for each map - bool selectingEvent = false; + void deleteSelectedEvents(); void shouldReselectEvents(); void scaleMapView(int); static void openInTextEditor(const QString &path, int lineNum = 0); @@ -206,11 +238,11 @@ public slots: qint64 *pid = nullptr); private slots: - void onMapStartPaint(QGraphicsSceneMouseEvent *event, MapPixmapItem *item); - void onMapEndPaint(QGraphicsSceneMouseEvent *event, MapPixmapItem *item); + void onMapStartPaint(QGraphicsSceneMouseEvent *event, LayoutPixmapItem *item); + void onMapEndPaint(QGraphicsSceneMouseEvent *event, LayoutPixmapItem *item); void setSmartPathCursorMode(QGraphicsSceneMouseEvent *event); void setStraightPathCursorMode(QGraphicsSceneMouseEvent *event); - void mouseEvent_map(QGraphicsSceneMouseEvent *event, MapPixmapItem *item); + void mouseEvent_map(QGraphicsSceneMouseEvent *event, LayoutPixmapItem *item); void mouseEvent_collision(QGraphicsSceneMouseEvent *event, CollisionPixmapItem *item); void setSelectedConnectionItem(ConnectionPixmapItem *connectionItem); void onHoveredMovementPermissionChanged(uint16_t, uint16_t); diff --git a/include/mainwindow.h b/include/mainwindow.h index c69425a24..397a1d722 100644 --- a/include/mainwindow.h +++ b/include/mainwindow.h @@ -21,6 +21,7 @@ #include "regionmapeditor.h" #include "mapimageexporter.h" #include "filterchildrenproxymodel.h" +#include "maplistmodels.h" #include "newmappopup.h" #include "newtilesetdialog.h" #include "shortcutseditor.h" @@ -172,8 +173,8 @@ private slots: void on_action_Open_Project_triggered(); void on_action_Reload_Project_triggered(); void on_action_Close_Project_triggered(); - void on_mapList_activated(const QModelIndex &index); void on_action_Save_Project_triggered(); + void openWarpMap(QString map_name, int event_id, Event::Group event_group); void duplicate(); @@ -182,16 +183,17 @@ private slots: void copy(); void paste(); + void onLayoutChanged(Layout *layout); void onOpenConnectedMap(MapConnection*); - void onMapNeedsRedrawing(); void onTilesetsSaved(QString, QString); void openNewMapPopupWindow(); void onNewMapCreated(); - void onMapCacheCleared(); void onMapLoaded(Map *map); void importMapFromAdvanceMap1_92(); void onMapRulerStatusChanged(const QString &); void applyUserShortcuts(); + void markMapEdited(); + void markSpecificMapEdited(Map*); void on_action_NewMap_triggered(); void on_actionNew_Tileset_triggered(); @@ -202,6 +204,7 @@ private slots: void on_comboBox_Weather_currentTextChanged(const QString &arg1); void on_comboBox_Type_currentTextChanged(const QString &arg1); void on_comboBox_BattleScene_currentTextChanged(const QString &arg1); + void on_comboBox_LayoutSelector_currentTextChanged(const QString &arg1); void on_checkBox_ShowLocation_stateChanged(int selected); void on_checkBox_AllowRunning_stateChanged(int selected); void on_checkBox_AllowBiking_stateChanged(int selected); @@ -221,9 +224,6 @@ private slots: void on_actionMove_triggered(); void on_actionMap_Shift_triggered(); - void onDeleteKeyPressed(); - void on_toolButton_deleteObject_clicked(); - void addNewEvent(Event::Type type); void tryAddEventTab(QWidget * tab); void displayEventTabs(); @@ -238,9 +238,6 @@ private slots: void on_toolButton_Shift_clicked(); void onOpenMapListContextMenu(const QPoint &point); - void onAddNewMapToGroupClick(QAction* triggeredAction); - void onAddNewMapToAreaClick(QAction* triggeredAction); - void onAddNewMapToLayoutClick(QAction* triggeredAction); void currentMetatilesSelectionChanged(); void on_action_Export_Map_Image_triggered(); @@ -264,10 +261,6 @@ private slots: void on_actionTileset_Editor_triggered(); - void mapSortOrder_changed(QAction *action); - - void on_lineEdit_filterBox_textChanged(const QString &arg1); - void moveEvent(QMoveEvent *event); void closeEvent(QCloseEvent *); @@ -280,8 +273,11 @@ private slots: void on_slider_DiveMapOpacity_valueChanged(int value); void on_slider_EmergeMapOpacity_valueChanged(int value); void on_horizontalSlider_CollisionTransparency_valueChanged(int value); - void on_toolButton_ExpandAll_clicked(); - void on_toolButton_CollapseAll_clicked(); + + void mapListShortcut_ToggleEmptyFolders(); + void mapListShortcut_ExpandAll(); + void mapListShortcut_CollapseAll(); + void on_actionAbout_Porymap_triggered(); void on_actionOpen_Log_File_triggered(); void on_actionOpen_Config_Folder_triggered(); @@ -322,15 +318,18 @@ private slots: QPointer projectSettingsEditor = nullptr; QPointer gridSettingsDialog = nullptr; QPointer customScriptsEditor = nullptr; + + QPointer groupListProxyModel = nullptr; + QPointer mapGroupModel = nullptr; + QPointer areaListProxyModel = nullptr; + QPointer mapAreaModel = nullptr; + QPointer layoutListProxyModel = nullptr; + QPointer layoutTreeModel = nullptr; + QPointer updatePromoter = nullptr; QPointer networkAccessManager = nullptr; QPointer aboutWindow = nullptr; QPointer wildMonChart = nullptr; - FilterChildrenProxyModel *mapListProxyModel; - QStandardItemModel *mapListModel; - QList *mapGroupItemsList; - QMap mapListIndexes; - QIcon mapIcon; QAction *undoAction = nullptr; QAction *redoAction = nullptr; @@ -342,48 +341,61 @@ private slots: bool isProgrammaticEventTabChange; bool newMapDefaultsSet = false; + bool tilesetNeedsRedraw = false; - bool userSetMap(QString, bool scrollTreeView = false); - bool setMap(QString, bool scrollTreeView = false); + bool setLayout(QString layoutId); + bool setMap(QString); + void unsetMap(); + bool userSetLayout(QString layoutId); + bool userSetMap(QString); void redrawMapScene(); void refreshMapScene(); + void setLayoutOnlyMode(bool layoutOnly); + bool checkProjectSanity(); bool loadProjectData(); bool setProjectUI(); void clearProjectUI(); - void sortMapList(); + void openSubWindow(QWidget * window); + void scrollMapList(MapTree *list, QString itemName); + void scrollMapListToCurrentMap(MapTree *list); + void scrollMapListToCurrentLayout(MapTree *list); + void resetMapListFilters(); + void showFileWatcherWarning(QString filepath); QString getExistingDirectory(QString); bool openProject(QString dir, bool initial = false); bool closeProject(); void showProjectOpenFailure(); - void saveGlobalConfigs(); + bool setInitialMap(); - QStandardItem* createMapItem(QString mapName, int groupNum, int inGroupNum); + void saveGlobalConfigs(); + void refreshRecentProjectsMenu(); - void updateMapListIcon(const QString &mapName); void updateMapList(); + void mapListAddGroup(); + void mapListAddLayout(); + void mapListAddArea(); + void openMapListItem(const QModelIndex &index); + void saveMapListTab(int index); void displayMapProperties(); void checkToolButtons(); - void clickToolButtonFromEditMode(QString editMode); + void clickToolButtonFromEditAction(Editor::EditAction editAction); - void markMapEdited(); - void markMapEdited(Map*); - void showWindowTitle(); + void updateWindowTitle(); void initWindow(); void initCustomUI(); void initExtraSignals(); void initEditor(); void initMiscHeapObjects(); - void initMapSortOrder(); + void initMapList(); void initShortcuts(); void initExtraShortcuts(); void loadUserSettings(); - void applyMapListFilter(QString filterText); void restoreWindowState(); void setTheme(QString); void updateTilesetEditor(); @@ -403,6 +415,9 @@ private slots: double getMetatilesZoomScale(); void redrawMetatileSelection(); void scrollMetatileSelectorToSelection(); + MapListToolBar* getCurrentMapListToolBar(); + MapTree* getCurrentMapList(); + void refreshLocationsComboBox(); QObjectList shortcutableObjects() const; void addCustomHeaderValue(QString key, QJsonValue value, bool isNew = false); @@ -412,12 +427,6 @@ private slots: void setDivingMapsVisible(bool visible); }; -enum MapListUserRoles { - GroupRole = Qt::UserRole + 1, // Used to hold the map group number. - TypeRole, // Used to differentiate between the different layers of the map list tree view. - TypeRole2, // Used for various extra data needed. -}; - // These are namespaced in a struct to avoid colliding with e.g. class Map. struct MainTab { enum { @@ -437,4 +446,10 @@ struct MapViewTab { }; }; +struct MapListTab { + enum { + Groups = 0, Areas, Layouts + }; +}; + #endif // MAINWINDOW_H diff --git a/include/project.h b/include/project.h index f9fc704de..efb7db7c6 100644 --- a/include/project.h +++ b/include/project.h @@ -25,7 +25,7 @@ class Project : public QObject { Q_OBJECT public: - Project(QWidget *parent = nullptr); + Project(QObject *parent = nullptr); ~Project(); Project(const Project &) = delete; @@ -46,11 +46,9 @@ class Project : public QObject QStringList mapLayoutsTable; QStringList mapLayoutsTableMaster; QString layoutsLabel; - QMap mapLayouts; - QMap mapLayoutsMaster; - QMap mapSecToMapHoverName; - QMap mapSectionNameToValue; - QMap mapSectionValueToName; + QMap layoutIdsToNames; + QMap mapLayouts; + QMap mapLayoutsMaster; QMap eventGraphicsMap; QMap gfxDefines; QString defaultSong; @@ -67,6 +65,8 @@ class Project : public QObject QStringList bgEventFacingDirections; QStringList trainerTypes; QStringList globalScriptLabels; + QStringList mapSectionIdNames; + QMap regionMapEntries; QMap> metatileLabelsMap; QMap unusedMetatileLabels; QMap metatileBehaviorMap; @@ -81,11 +81,10 @@ class Project : public QObject int pokemonMaxLevel; int maxEncounterRate; bool wildEncountersLoaded; + bool saveEmptyMapsec; void set_root(QString); - void initSignals(); - void clearMapCache(); void clearTilesetCache(); void clearMapLayouts(); @@ -115,8 +114,8 @@ class Project : public QObject QStringList tilesetLabelsOrdered; Blockdata readBlockdata(QString); - bool loadBlockdata(MapLayout*); - bool loadLayoutBorder(MapLayout*); + bool loadBlockdata(Layout *); + bool loadLayoutBorder(Layout *); void saveTextFile(QString path, QString text); void appendTextFile(QString path, QString text); @@ -140,12 +139,20 @@ class Project : public QObject bool readSpeciesIconPaths(); QMap speciesToIconPath; + void addNewMapsec(const QString &name); + void removeMapsec(const QString &name); + + bool hasUnsavedChanges(); + bool hasUnsavedDataChanges = false; + QSet getTopLevelMapFields(); bool loadMapData(Map*); bool readMapLayouts(); - bool loadLayout(MapLayout *); + Layout *loadLayout(QString layoutId); + Layout *createNewLayout(Layout::SimpleSettings &layoutSettings); + bool loadLayout(Layout *); bool loadMapLayout(Map*); - bool loadLayoutTilesets(MapLayout*); + bool loadLayoutTilesets(Layout *); void loadTilesetAssets(Tileset*); void loadTilesetTiles(Tileset*, QImage); void loadTilesetMetatiles(Tileset*); @@ -153,15 +160,17 @@ class Project : public QObject void loadTilesetPalettes(Tileset*); void readTilesetPaths(Tileset* tileset); - void saveLayoutBlockdata(Map*); - void saveLayoutBorder(Map*); + void saveLayout(Layout *); + void saveLayoutBlockdata(Layout *); + void saveLayoutBorder(Layout *); void writeBlockdata(QString, const Blockdata &); void saveAllMaps(); - void saveMap(Map*); + void saveMap(Map *); void saveAllDataStructures(); void saveConfig(); void saveMapLayouts(); void saveMapGroups(); + void saveRegionMapSections(); void saveWildMonData(); void saveMapConstantsHeader(); void saveHealLocations(Map*); @@ -232,12 +241,13 @@ class Project : public QObject static bool mapDimensionsValid(int width, int height); bool calculateDefaultMapSize(); static int getMaxObjectEvents(); + static QString getEmptyMapsecName(); private: - void updateMapLayout(Map*); + void updateLayout(Layout *); - void setNewMapBlockdata(Map* map); - void setNewMapBorder(Map *map); + void setNewLayoutBlockdata(Layout *layout); + void setNewLayoutBorder(Layout *layout); void setNewMapEvents(Map *map); void setNewMapConnections(Map *map); @@ -256,9 +266,8 @@ class Project : public QObject static int max_object_events; signals: - void reloadProject(); - void uncheckMonitorFilesAction(); - void mapCacheCleared(); + void fileChanged(QString filepath); + void mapSectionIdNamesChanged(); void mapLoaded(Map *map); }; diff --git a/include/ui/bordermetatilespixmapitem.h b/include/ui/bordermetatilespixmapitem.h index f4af8bfe6..cf2ba0d42 100644 --- a/include/ui/bordermetatilespixmapitem.h +++ b/include/ui/bordermetatilespixmapitem.h @@ -1,21 +1,21 @@ #ifndef BORDERMETATILESPIXMAPITEM_H #define BORDERMETATILESPIXMAPITEM_H -#include "map.h" +#include "maplayout.h" #include "metatileselector.h" #include class BorderMetatilesPixmapItem : public QObject, public QGraphicsPixmapItem { Q_OBJECT public: - BorderMetatilesPixmapItem(Map *map_, MetatileSelector *metatileSelector) { - this->map = map_; - this->map->setBorderItem(this); + BorderMetatilesPixmapItem(Layout *layout, MetatileSelector *metatileSelector) { + this->layout = layout; + this->layout->setBorderItem(this); this->metatileSelector = metatileSelector; setAcceptHoverEvents(true); } MetatileSelector *metatileSelector; - Map *map; + Layout *layout; void draw(); signals: void hoveredBorderMetatileSelectionChanged(uint16_t); diff --git a/include/ui/collisionpixmapitem.h b/include/ui/collisionpixmapitem.h index c489ddd71..1419f2e4b 100644 --- a/include/ui/collisionpixmapitem.h +++ b/include/ui/collisionpixmapitem.h @@ -4,19 +4,20 @@ #include #include "metatileselector.h" -#include "mappixmapitem.h" +#include "movementpermissionsselector.h" +#include "layoutpixmapitem.h" #include "map.h" #include "settings.h" -class CollisionPixmapItem : public MapPixmapItem { +class CollisionPixmapItem : public LayoutPixmapItem { Q_OBJECT public: - CollisionPixmapItem(Map *map, QSpinBox * selectedCollision, QSpinBox * selectedElevation, MetatileSelector *metatileSelector, Settings *settings, qreal *opacity) - : MapPixmapItem(map, metatileSelector, settings){ + CollisionPixmapItem(Layout *layout, QSpinBox * selectedCollision, QSpinBox * selectedElevation, MetatileSelector *metatileSelector, Settings *settings, qreal *opacity) + : LayoutPixmapItem(layout, metatileSelector, settings){ this->selectedCollision = selectedCollision; this->selectedElevation = selectedElevation; this->opacity = opacity; - map->setCollisionItem(this); + layout->setCollisionItem(this); } QSpinBox * selectedCollision; QSpinBox * selectedElevation; diff --git a/include/ui/connectionpixmapitem.h b/include/ui/connectionpixmapitem.h index 62eda6fe4..183f2d795 100644 --- a/include/ui/connectionpixmapitem.h +++ b/include/ui/connectionpixmapitem.h @@ -5,6 +5,7 @@ #include #include #include +#include class ConnectionPixmapItem : public QObject, public QGraphicsPixmapItem { Q_OBJECT @@ -36,14 +37,17 @@ class ConnectionPixmapItem : public QObject, public QGraphicsPixmapItem { static const int mHeight = 16; protected: - QVariant itemChange(GraphicsItemChange change, const QVariant &value) override; - void mousePressEvent(QGraphicsSceneMouseEvent*) override; - void mouseReleaseEvent(QGraphicsSceneMouseEvent*) override; - void mouseDoubleClickEvent(QGraphicsSceneMouseEvent*) override; + virtual QVariant itemChange(GraphicsItemChange change, const QVariant &value) override; + virtual void mousePressEvent(QGraphicsSceneMouseEvent*) override; + virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent*) override; + virtual void mouseDoubleClickEvent(QGraphicsSceneMouseEvent*) override; + virtual void keyPressEvent(QKeyEvent*) override; + virtual void focusInEvent(QFocusEvent*) override; signals: void connectionItemDoubleClicked(MapConnection*); void selectionChanged(bool selected); + void deleteRequested(MapConnection*); }; #endif // CONNECTIONPIXMAPITEM_H diff --git a/include/ui/connectionslistitem.h b/include/ui/connectionslistitem.h index bbe0f2d31..7ba6a9d8e 100644 --- a/include/ui/connectionslistitem.h +++ b/include/ui/connectionslistitem.h @@ -34,11 +34,12 @@ class ConnectionsListItem : public QFrame unsigned actionId = 0; protected: - void mousePressEvent(QMouseEvent*) override; + virtual void mousePressEvent(QMouseEvent*) override; + virtual void focusInEvent(QFocusEvent*) override; + virtual void keyPressEvent(QKeyEvent*) override; signals: void selected(); - void removed(MapConnection*); void openMapClicked(MapConnection*); private slots: diff --git a/include/ui/currentselectedmetatilespixmapitem.h b/include/ui/currentselectedmetatilespixmapitem.h index 5e4bd2756..109f52c56 100644 --- a/include/ui/currentselectedmetatilespixmapitem.h +++ b/include/ui/currentselectedmetatilespixmapitem.h @@ -1,23 +1,24 @@ #ifndef CURRENTSELECTEDMETATILESPIXMAPITEM_H #define CURRENTSELECTEDMETATILESPIXMAPITEM_H -#include "map.h" #include "metatileselector.h" #include +class Layout; + class CurrentSelectedMetatilesPixmapItem : public QGraphicsPixmapItem { public: - CurrentSelectedMetatilesPixmapItem(Map *map, MetatileSelector *metatileSelector) { - this->map = map; + CurrentSelectedMetatilesPixmapItem(Layout *layout, MetatileSelector *metatileSelector) { + this->layout = layout; this->metatileSelector = metatileSelector; } - Map* map = nullptr; + Layout *layout = nullptr; MetatileSelector *metatileSelector; void draw(); - void setMap(Map *map) { this->map = map; } + void setLayout(Layout *layout) { this->layout = layout; } }; -QPixmap drawMetatileSelection(MetatileSelection selection, Map *map); +QPixmap drawMetatileSelection(MetatileSelection selection, Layout *layout); #endif // CURRENTSELECTEDMETATILESPIXMAPITEM_H diff --git a/include/ui/eventfilters.h b/include/ui/eventfilters.h new file mode 100644 index 000000000..851c344bd --- /dev/null +++ b/include/ui/eventfilters.h @@ -0,0 +1,28 @@ +#include +#include + + + +/// Prevent wheel scroll +class WheelFilter : public QObject { + Q_OBJECT +public: + WheelFilter(QObject *parent) : QObject(parent) {} + virtual ~WheelFilter() {} + bool eventFilter(QObject *obj, QEvent *event) override; +}; + + + +/// Ctrl+Wheel = zoom +class MapSceneEventFilter : public QObject { + Q_OBJECT +protected: + bool eventFilter(QObject *obj, QEvent *event) override; +public: + explicit MapSceneEventFilter(QObject *parent = nullptr) : QObject(parent) {} + +signals: + void wheelZoom(int delta); +public slots: +}; diff --git a/include/ui/filterchildrenproxymodel.h b/include/ui/filterchildrenproxymodel.h index b73cbd623..507693b33 100644 --- a/include/ui/filterchildrenproxymodel.h +++ b/include/ui/filterchildrenproxymodel.h @@ -9,9 +9,11 @@ class FilterChildrenProxyModel : public QSortFilterProxyModel public: explicit FilterChildrenProxyModel(QObject *parent = nullptr); + void setHideEmpty(bool hidden) { this->hideEmpty = hidden; } protected: bool filterAcceptsRow(int source_row, const QModelIndex & source_parent) const; - +private: + bool hideEmpty = false; }; #endif // FILTERCHILDRENPROXYMODEL_H diff --git a/include/ui/graphicsview.h b/include/ui/graphicsview.h index c0d1592cf..92771cf7a 100644 --- a/include/ui/graphicsview.h +++ b/include/ui/graphicsview.h @@ -34,6 +34,7 @@ class ClickableGraphicsView : public NoScrollGraphicsView class Editor; +// TODO: This should just be MapView. It makes map-based assumptions, and no other class inherits GraphicsView. class GraphicsView : public QGraphicsView { public: @@ -44,10 +45,10 @@ class GraphicsView : public QGraphicsView // GraphicsView_Object object; Editor *editor; protected: - void mousePressEvent(QMouseEvent *event); - void mouseMoveEvent(QMouseEvent *event); - void mouseReleaseEvent(QMouseEvent *event); - void moveEvent(QMoveEvent *event); + virtual void mousePressEvent(QMouseEvent *event) override; + virtual void mouseMoveEvent(QMouseEvent *event) override; + virtual void mouseReleaseEvent(QMouseEvent *event) override; + virtual void moveEvent(QMoveEvent *event) override; }; //Q_DECLARE_METATYPE(GraphicsView) diff --git a/include/ui/mappixmapitem.h b/include/ui/layoutpixmapitem.h similarity index 80% rename from include/ui/mappixmapitem.h rename to include/ui/layoutpixmapitem.h index cd2d335cd..08496c54c 100644 --- a/include/ui/mappixmapitem.h +++ b/include/ui/layoutpixmapitem.h @@ -1,54 +1,58 @@ #ifndef MAPPIXMAPITEM_H #define MAPPIXMAPITEM_H -#include "map.h" #include "settings.h" #include "metatileselector.h" #include -class MapPixmapItem : public QObject, public QGraphicsPixmapItem { +class Layout; + +class LayoutPixmapItem : public QObject, public QGraphicsPixmapItem { Q_OBJECT private: using QGraphicsPixmapItem::paint; public: - enum class PaintMode { - Disabled, - Metatiles, - EventObjects - }; - MapPixmapItem(Map *map_, MetatileSelector *metatileSelector, Settings *settings) { - this->map = map_; - this->map->setMapItem(this); + LayoutPixmapItem(Layout *layout, MetatileSelector *metatileSelector, Settings *settings) { + this->layout = layout; + // this->map->setMapItem(this); this->metatileSelector = metatileSelector; this->settings = settings; - this->paintingMode = PaintMode::Metatiles; - this->lockedAxis = MapPixmapItem::Axis::None; + this->lockedAxis = LayoutPixmapItem::Axis::None; this->prevStraightPathState = false; setAcceptHoverEvents(true); } - MapPixmapItem::PaintMode paintingMode; - Map *map; + + Layout *layout; + MetatileSelector *metatileSelector; + Settings *settings; + bool active; bool has_mouse = false; bool right_click; + int paint_tile_initial_x; int paint_tile_initial_y; bool prevStraightPathState; int straight_path_initial_x; int straight_path_initial_y; + QPoint metatilePos; + enum Axis { None = 0, X, Y }; - MapPixmapItem::Axis lockedAxis; + + LayoutPixmapItem::Axis lockedAxis; + QPoint selection_origin; QList selection; + virtual void paint(QGraphicsSceneMouseEvent*); virtual void floodFill(QGraphicsSceneMouseEvent*); virtual void magicFill(QGraphicsSceneMouseEvent*); @@ -70,26 +74,33 @@ class MapPixmapItem : public QObject, public QGraphicsPixmapItem { QList selectedCollisions, bool fromScriptCall = false); void floodFillSmartPath(int initialX, int initialY, bool fromScriptCall = false); + virtual void pick(QGraphicsSceneMouseEvent*); virtual void select(QGraphicsSceneMouseEvent*); virtual void shift(QGraphicsSceneMouseEvent*); void shift(int xDelta, int yDelta, bool fromScriptCall = false); virtual void draw(bool ignoreCache = false); + void updateMetatileSelection(QGraphicsSceneMouseEvent *event); void paintNormal(int x, int y, bool fromScriptCall = false); void lockNondominantAxis(QGraphicsSceneMouseEvent *event); QPoint adjustCoords(QPoint pos); + void setEditsEnabled(bool enabled) { this->editsEnabled = enabled; } + bool getEditsEnabled() { return this->editsEnabled; } + private: void paintSmartPath(int x, int y, bool fromScriptCall = false); static QList smartPathTable; unsigned actionId_ = 0; + bool editsEnabled = true; + signals: - void startPaint(QGraphicsSceneMouseEvent *, MapPixmapItem *); - void endPaint(QGraphicsSceneMouseEvent *, MapPixmapItem *); - void mouseEvent(QGraphicsSceneMouseEvent *, MapPixmapItem *); + void startPaint(QGraphicsSceneMouseEvent *, LayoutPixmapItem *); + void endPaint(QGraphicsSceneMouseEvent *, LayoutPixmapItem *); + void mouseEvent(QGraphicsSceneMouseEvent *, LayoutPixmapItem *); void hoveredMapMetatileChanged(const QPoint &pos); void hoveredMapMetatileCleared(); diff --git a/include/ui/mapimageexporter.h b/include/ui/mapimageexporter.h index 82dae4432..37df3038c 100644 --- a/include/ui/mapimageexporter.h +++ b/include/ui/mapimageexporter.h @@ -45,6 +45,7 @@ class MapImageExporter : public QDialog private: Ui::MapImageExporter *ui; + Layout *layout = nullptr; Map *map = nullptr; Editor *editor = nullptr; QGraphicsScene *scene = nullptr; diff --git a/include/ui/maplistmodels.h b/include/ui/maplistmodels.h new file mode 100644 index 000000000..17810c7ed --- /dev/null +++ b/include/ui/maplistmodels.h @@ -0,0 +1,208 @@ +#pragma once +#ifndef MAPLISTMODELS_H +#define MAPLISTMODELS_H + +#include +#include +#include +#include +#include + + + +class Project; + +enum MapListUserRoles { + GroupRole = Qt::UserRole + 1, // Used to hold the map group number. + TypeRole, // Used to differentiate between the different layers of the map list tree view. + TypeRole2, // Used for various extra data needed. +}; + + + +class MapTree : public QTreeView { + Q_OBJECT +public: + MapTree(QWidget *parent) : QTreeView(parent) { + this->setDropIndicatorShown(true); + this->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont)); + this->setFocusPolicy(Qt::StrongFocus); + this->setContextMenuPolicy(Qt::CustomContextMenu); + } + +protected: + virtual void keyPressEvent(QKeyEvent *event) override; + +public slots: + void removeSelected(); +}; + + + +class GroupNameDelegate : public QStyledItemDelegate { + Q_OBJECT + +public: + GroupNameDelegate(Project *project, QObject *parent = nullptr) : QStyledItemDelegate(parent), project(project) {} + + QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override; + void setEditorData(QWidget *editor, const QModelIndex &index) const override; + void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override; + void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const override; + +private: + Project *project = nullptr; +}; + + + +class QRegularExpressionValidator; + +class MapListModel : public QStandardItemModel { + Q_OBJECT + +public: + MapListModel(QObject *parent = nullptr) : QStandardItemModel(parent) {}; + ~MapListModel() { } + + virtual QModelIndex indexOf(QString id) const = 0; + virtual void removeItemAt(const QModelIndex &index); + virtual QStandardItem *getItem(const QModelIndex &index) const = 0; + +protected: + virtual void removeItem(QStandardItem *item) = 0; +}; + +class MapGroupModel : public MapListModel { + Q_OBJECT + +public: + MapGroupModel(Project *project, QObject *parent = nullptr); + ~MapGroupModel() { } + + QVariant data(const QModelIndex &index, int role) const override; + + Qt::DropActions supportedDropActions() const override; + QStringList mimeTypes() const override; + virtual QMimeData *mimeData(const QModelIndexList &indexes) const override; + virtual bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override; + + virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; + +public: + void setMap(QString mapName) { this->openMap = mapName; } + + QStandardItem *createGroupItem(QString groupName, int groupIndex, QStandardItem *fromItem = nullptr); + QStandardItem *createMapItem(QString mapName, QStandardItem *fromItem = nullptr); + + QStandardItem *insertGroupItem(QString groupName); + QStandardItem *insertMapItem(QString mapName, QString groupName); + + virtual QStandardItem *getItem(const QModelIndex &index) const override; + virtual QModelIndex indexOf(QString mapName) const override; + + void initialize(); + +protected: + virtual void removeItem(QStandardItem *item) override; + +private: + friend class MapTree; + void updateProject(); + +private: + Project *project; + QStandardItem *root = nullptr; + + QMap groupItems; + QMap mapItems; + + QString openMap; + +signals: + void dragMoveCompleted(); +}; + + + +class MapAreaModel : public MapListModel { + Q_OBJECT + +public: + MapAreaModel(Project *project, QObject *parent = nullptr); + ~MapAreaModel() {} + + QVariant data(const QModelIndex &index, int role) const override; + +public: + void setMap(QString mapName) { this->openMap = mapName; } + + QStandardItem *createAreaItem(QString areaName); + QStandardItem *createMapItem(QString mapName, int areaIndex, int mapIndex); + + QStandardItem *insertAreaItem(QString areaName); + QStandardItem *insertMapItem(QString mapName, QString areaName, int groupIndex); + + virtual QStandardItem *getItem(const QModelIndex &index) const override; + virtual QModelIndex indexOf(QString mapName) const override; + + void initialize(); + +protected: + virtual void removeItem(QStandardItem *item) override; + +private: + Project *project; + QStandardItem *root = nullptr; + + QMap areaItems; + QMap mapItems; + + QString openMap; + +signals: + void edited(); +}; + + + +class LayoutTreeModel : public MapListModel { + Q_OBJECT + +public: + LayoutTreeModel(Project *project, QObject *parent = nullptr); + ~LayoutTreeModel() {} + + QVariant data(const QModelIndex &index, int role) const override; + +public: + void setLayout(QString layoutId) { this->openLayout = layoutId; } + + QStandardItem *createLayoutItem(QString layoutId); + QStandardItem *createMapItem(QString mapName); + + QStandardItem *insertLayoutItem(QString layoutId); + QStandardItem *insertMapItem(QString mapName, QString layoutId); + + virtual QStandardItem *getItem(const QModelIndex &index) const override; + virtual QModelIndex indexOf(QString layoutName) const override; + + void initialize(); + +protected: + virtual void removeItem(QStandardItem *item) override; + +private: + Project *project; + QStandardItem *root = nullptr; + + QMap layoutItems; + QMap mapItems; + + QString openLayout; + +signals: + void edited(); +}; + +#endif // MAPLISTMODELS_H diff --git a/include/ui/maplisttoolbar.h b/include/ui/maplisttoolbar.h new file mode 100644 index 000000000..bf749a3e1 --- /dev/null +++ b/include/ui/maplisttoolbar.h @@ -0,0 +1,52 @@ +#ifndef MAPLISTTOOLBAR_H +#define MAPLISTTOOLBAR_H + +#include "maplistmodels.h" +#include "filterchildrenproxymodel.h" + +#include +#include + +namespace Ui { +class MapListToolBar; +} + +class MapListToolBar : public QFrame +{ + Q_OBJECT + +public: + explicit MapListToolBar(QWidget *parent = nullptr); + ~MapListToolBar(); + + MapTree* list() const { return m_list; } + void setList(MapTree *list); + + void setEditsAllowedButtonVisible(bool visible); + void setEditsAllowed(bool allowed); + void toggleEditsAllowed(); + + void setEmptyFoldersVisible(bool visible); + void toggleEmptyFolders(); + + void expandList(); + void collapseList(); + + void applyFilter(const QString &filterText); + void clearFilter(); + void setFilterLocked(bool locked) { m_filterLocked = locked; } + bool isFilterLocked() const { return m_filterLocked; } + +signals: + void filterCleared(MapTree*); + void addFolderClicked(); + +private: + Ui::MapListToolBar *ui; + QPointer m_list; + bool m_filterLocked = false; + bool m_editsAllowed = false; + bool m_emptyFoldersVisible = true; +}; + +#endif // MAPLISTTOOLBAR_H diff --git a/include/ui/mapsceneeventfilter.h b/include/ui/mapsceneeventfilter.h deleted file mode 100644 index 7de427e31..000000000 --- a/include/ui/mapsceneeventfilter.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef MAPSCENEEVENTFILTER_H -#define MAPSCENEEVENTFILTER_H - -#include - -class MapSceneEventFilter : public QObject -{ - Q_OBJECT -protected: - bool eventFilter(QObject *obj, QEvent *event) override; -public: - explicit MapSceneEventFilter(QObject *parent = nullptr); - -signals: - void wheelZoom(int delta); -public slots: -}; - -#endif // MAPSCENEEVENTFILTER_H diff --git a/include/ui/mapview.h b/include/ui/mapview.h index 9176c825a..7355da9d0 100644 --- a/include/ui/mapview.h +++ b/include/ui/mapview.h @@ -73,7 +73,8 @@ class MapView : public GraphicsView private: QMap overlayMap; protected: - void drawForeground(QPainter *painter, const QRectF &rect); + virtual void drawForeground(QPainter *painter, const QRectF &rect) override; + virtual void keyPressEvent(QKeyEvent*) override; }; #endif // GRAPHICSVIEW_H diff --git a/include/ui/metatileselector.h b/include/ui/metatileselector.h index ec2c49f41..ae11c58e0 100644 --- a/include/ui/metatileselector.h +++ b/include/ui/metatileselector.h @@ -31,13 +31,13 @@ struct MetatileSelection class MetatileSelector: public SelectablePixmapItem { Q_OBJECT public: - MetatileSelector(int numMetatilesWide, Map *map): SelectablePixmapItem(16, 16) { + MetatileSelector(int numMetatilesWide, Layout *layout): SelectablePixmapItem(16, 16) { this->externalSelection = false; this->prefabSelection = false; this->numMetatilesWide = numMetatilesWide; - this->map = map; - this->primaryTileset = map->layout->tileset_primary; - this->secondaryTileset = map->layout->tileset_secondary; + this->layout = layout; + this->primaryTileset = layout->tileset_primary; + this->secondaryTileset = layout->tileset_secondary; this->selection = MetatileSelection{}; this->cellPos = QPoint(-1, -1); setAcceptHoverEvents(true); @@ -51,7 +51,7 @@ class MetatileSelector: public SelectablePixmapItem { void setPrefabSelection(MetatileSelection selection); void setExternalSelection(int, int, QList, QList>); QPoint getMetatileIdCoordsOnWidget(uint16_t); - void setMap(Map*); + void setLayout(Layout *layout); bool isInternalSelection() const { return (!this->externalSelection && !this->prefabSelection); } Tileset *primaryTileset; Tileset *secondaryTileset; @@ -65,7 +65,7 @@ class MetatileSelector: public SelectablePixmapItem { bool externalSelection; bool prefabSelection; int numMetatilesWide; - Map *map; + Layout *layout; int externalSelectionWidth; int externalSelectionHeight; QList externalSelectedMetatiles; diff --git a/include/ui/montabwidget.h b/include/ui/montabwidget.h index 4b66969c3..6d9160689 100644 --- a/include/ui/montabwidget.h +++ b/include/ui/montabwidget.h @@ -32,8 +32,6 @@ public slots: void deactivateTab(int tabIndex); private: - bool eventFilter(QObject *object, QEvent *event); - void actionCopyTab(int index); void actionAddDeleteTab(int index); diff --git a/include/ui/newmappopup.h b/include/ui/newmappopup.h index 826e1609f..f160876a0 100644 --- a/include/ui/newmappopup.h +++ b/include/ui/newmappopup.h @@ -23,8 +23,9 @@ class NewMapPopup : public QMainWindow bool importedMap; QString layoutId; void init(); - void init(MapSortOrder type, QVariant data); - void init(MapLayout *); + void initUi(); + void init(int tabIndex, QString data); + void init(Layout *); static void setDefaultSettings(Project *project); signals: @@ -37,7 +38,7 @@ class NewMapPopup : public QMainWindow bool checkNewMapGroup(); void saveSettings(); void useLayout(QString layoutId); - void useLayoutSettings(MapLayout *mapLayout); + void useLayoutSettings(Layout *mapLayout); struct Settings { QString group; @@ -60,6 +61,8 @@ class NewMapPopup : public QMainWindow static struct Settings settings; private slots: + void on_checkBox_UseExistingLayout_stateChanged(int state); + void on_comboBox_Layout_currentTextChanged(const QString &text); void on_pushButton_NewMap_Accept_clicked(); void on_lineEdit_NewMap_Name_textChanged(const QString &); }; diff --git a/include/ui/prefab.h b/include/ui/prefab.h index 7bd9e0b24..2ac8fa042 100644 --- a/include/ui/prefab.h +++ b/include/ui/prefab.h @@ -20,9 +20,9 @@ struct PrefabItem class Prefab { public: - void initPrefabUI(MetatileSelector *selector, QWidget *prefabWidget, QLabel *emptyPrefabLabel, Map *map); - void addPrefab(MetatileSelection selection, Map *map, QString name); - void updatePrefabUi(Map *map); + void initPrefabUI(MetatileSelector *selector, QWidget *prefabWidget, QLabel *emptyPrefabLabel, Layout *layout); + void addPrefab(MetatileSelection selection, Layout *layout, QString name); + void updatePrefabUi(Layout *layout); bool tryImportDefaultPrefabs(QWidget * parent, BaseGameVersion version, QString filepath = ""); private: diff --git a/include/ui/prefabcreationdialog.h b/include/ui/prefabcreationdialog.h index 5748f35af..0821f7512 100644 --- a/include/ui/prefabcreationdialog.h +++ b/include/ui/prefabcreationdialog.h @@ -2,10 +2,11 @@ #define PREFABCREATIONDIALOG_H #include "metatileselector.h" -#include "map.h" #include +class Layout; + namespace Ui { class PrefabCreationDialog; } @@ -15,12 +16,12 @@ class PrefabCreationDialog : public QDialog Q_OBJECT public: - explicit PrefabCreationDialog(QWidget *parent, MetatileSelector *metatileSelector, Map *map); + explicit PrefabCreationDialog(QWidget *parent, MetatileSelector *metatileSelector, Layout *layout); ~PrefabCreationDialog(); void savePrefab(); private: - Map *map; + Layout *layout = nullptr; Ui::PrefabCreationDialog *ui; MetatileSelection selection; }; diff --git a/include/ui/regionmapeditor.h b/include/ui/regionmapeditor.h index 3d889f881..c19416516 100644 --- a/include/ui/regionmapeditor.h +++ b/include/ui/regionmapeditor.h @@ -57,7 +57,6 @@ public slots: tsl::ordered_map region_maps; QString configFilepath; - QString mapSectionFilepath; poryjson::Json rmConfigJson; @@ -96,7 +95,7 @@ public slots: void saveConfig(); bool loadRegionMapEntries(); bool saveRegionMapEntries(); - tsl::ordered_map region_map_entries; + QMap region_map_entries; bool buildConfigDialog(); poryjson::Json configRegionMapDialog(); diff --git a/include/ui/tileseteditor.h b/include/ui/tileseteditor.h index 9ce9777dd..32bdc97f7 100644 --- a/include/ui/tileseteditor.h +++ b/include/ui/tileseteditor.h @@ -8,7 +8,8 @@ #include "tileseteditormetatileselector.h" #include "tileseteditortileselector.h" #include "metatilelayersitem.h" -#include "map.h" + +class Layout; namespace Ui { class TilesetEditor; @@ -39,10 +40,10 @@ class TilesetEditor : public QMainWindow Q_OBJECT public: - explicit TilesetEditor(Project*, Map*, QWidget *parent = nullptr); + explicit TilesetEditor(Project *project, Layout *layout, QWidget *parent = nullptr); ~TilesetEditor(); - void update(Map *map, QString primaryTilsetLabel, QString secondaryTilesetLabel); - void updateMap(Map *map); + void update(Layout *layout, QString primaryTilsetLabel, QString secondaryTilesetLabel); + void updateLayout(Layout *layout); void updateTilesets(QString primaryTilsetLabel, QString secondaryTilesetLabel); bool selectMetatile(uint16_t metatileId); uint16_t getSelectedMetatileId(); @@ -155,7 +156,7 @@ private slots: MetatileLayersItem *metatileLayersItem = nullptr; PaletteEditor *paletteEditor = nullptr; Project *project = nullptr; - Map *map = nullptr; + Layout *layout = nullptr; Metatile *metatile = nullptr; Metatile *copiedMetatile = nullptr; QString copiedMetatileLabel; diff --git a/include/ui/tileseteditormetatileselector.h b/include/ui/tileseteditormetatileselector.h index f8b6011da..6b2c9a6f2 100644 --- a/include/ui/tileseteditormetatileselector.h +++ b/include/ui/tileseteditormetatileselector.h @@ -3,13 +3,14 @@ #include "selectablepixmapitem.h" #include "tileset.h" -#include "map.h" + +class Layout; class TilesetEditorMetatileSelector: public SelectablePixmapItem { Q_OBJECT public: - TilesetEditorMetatileSelector(Tileset *primaryTileset, Tileset *secondaryTileset, Map *map); - Map *map = nullptr; + TilesetEditorMetatileSelector(Tileset *primaryTileset, Tileset *secondaryTileset, Layout *layout); + Layout *layout = nullptr; void draw(); bool select(uint16_t metatileId); void setTilesets(Tileset*, Tileset*, bool draw = true); diff --git a/porymap.pro b/porymap.pro index 51df62f10..1b1c693e3 100644 --- a/porymap.pro +++ b/porymap.pro @@ -72,14 +72,16 @@ SOURCES += src/core/block.cpp \ src/ui/cursortilerect.cpp \ src/ui/customattributestable.cpp \ src/ui/eventframes.cpp \ + src/ui/eventfilters.cpp \ src/ui/filterchildrenproxymodel.cpp \ + src/ui/maplistmodels.cpp \ + src/ui/maplisttoolbar.cpp \ src/ui/graphicsview.cpp \ src/ui/imageproviders.cpp \ - src/ui/mappixmapitem.cpp \ + src/ui/layoutpixmapitem.cpp \ src/ui/prefabcreationdialog.cpp \ src/ui/regionmappixmapitem.cpp \ src/ui/citymappixmapitem.cpp \ - src/ui/mapsceneeventfilter.cpp \ src/ui/metatilelayersitem.cpp \ src/ui/metatileselector.cpp \ src/ui/movablerect.cpp \ @@ -170,16 +172,18 @@ HEADERS += include/core/block.h \ include/ui/cursortilerect.h \ include/ui/customattributestable.h \ include/ui/eventframes.h \ + include/ui/eventfilters.h \ include/ui/filterchildrenproxymodel.h \ + include/ui/maplistmodels.h \ + include/ui/maplisttoolbar.h \ include/ui/graphicsview.h \ include/ui/imageproviders.h \ - include/ui/mappixmapitem.h \ + include/ui/layoutpixmapitem.h \ include/ui/mapview.h \ include/ui/prefabcreationdialog.h \ include/ui/regionmappixmapitem.h \ include/ui/citymappixmapitem.h \ include/ui/colorinputwidget.h \ - include/ui/mapsceneeventfilter.h \ include/ui/metatilelayersitem.h \ include/ui/metatileselector.h \ include/ui/movablerect.h \ @@ -227,6 +231,7 @@ FORMS += forms/mainwindow.ui \ forms/colorinputwidget.ui \ forms/connectionslistitem.ui \ forms/gridsettingsdialog.ui \ + forms/maplisttoolbar.ui \ forms/newmapconnectiondialog.ui \ forms/prefabcreationdialog.ui \ forms/prefabframe.ui \ diff --git a/resources/icons/application_form_edit.ico b/resources/icons/application_form_edit.ico index 7bb403eab..5d9cc7daf 100644 Binary files a/resources/icons/application_form_edit.ico and b/resources/icons/application_form_edit.ico differ diff --git a/resources/icons/collapse_all.ico b/resources/icons/collapse_all.ico index f6c7f3158..806f2435f 100644 Binary files a/resources/icons/collapse_all.ico and b/resources/icons/collapse_all.ico differ diff --git a/resources/icons/connections.ico b/resources/icons/connections.ico new file mode 100644 index 000000000..effb20c98 Binary files /dev/null and b/resources/icons/connections.ico differ diff --git a/resources/icons/expand_all.ico b/resources/icons/expand_all.ico index 0707936c5..ca913a132 100644 Binary files a/resources/icons/expand_all.ico and b/resources/icons/expand_all.ico differ diff --git a/resources/icons/folder_add.ico b/resources/icons/folder_add.ico new file mode 100755 index 000000000..d881adf8e Binary files /dev/null and b/resources/icons/folder_add.ico differ diff --git a/resources/icons/folder_closed_map.ico b/resources/icons/folder_closed_map.ico index 27f9810b5..d299acb70 100644 Binary files a/resources/icons/folder_closed_map.ico and b/resources/icons/folder_closed_map.ico differ diff --git a/resources/icons/folder_eye_closed.ico b/resources/icons/folder_eye_closed.ico new file mode 100644 index 000000000..354abadb4 Binary files /dev/null and b/resources/icons/folder_eye_closed.ico differ diff --git a/resources/icons/folder_eye_open.ico b/resources/icons/folder_eye_open.ico new file mode 100644 index 000000000..15e7175f3 Binary files /dev/null and b/resources/icons/folder_eye_open.ico differ diff --git a/resources/icons/lock_edit.ico b/resources/icons/lock_edit.ico new file mode 100644 index 000000000..f30f42491 Binary files /dev/null and b/resources/icons/lock_edit.ico differ diff --git a/resources/icons/map_grayed.ico b/resources/icons/map_grayed.ico new file mode 100644 index 000000000..86c3f5fb7 Binary files /dev/null and b/resources/icons/map_grayed.ico differ diff --git a/resources/icons/minimap.ico b/resources/icons/minimap.ico new file mode 100644 index 000000000..548c7c8fc Binary files /dev/null and b/resources/icons/minimap.ico differ diff --git a/resources/icons/unlock_edit.ico b/resources/icons/unlock_edit.ico new file mode 100644 index 000000000..85a99e980 Binary files /dev/null and b/resources/icons/unlock_edit.ico differ diff --git a/resources/images.qrc b/resources/images.qrc index 26f51f42b..a89535a90 100644 --- a/resources/images.qrc +++ b/resources/images.qrc @@ -10,18 +10,24 @@ icons/file_put.ico icons/fill_color_cursor.ico icons/fill_color.ico + icons/folder_add.ico icons/folder_closed_map.ico icons/folder_closed.ico + icons/folder_eye_closed.ico + icons/folder_eye_open.ico icons/folder_map_edited.ico icons/folder_map_opened.ico icons/folder_map.ico icons/folder.ico + 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 + icons/map_grayed.ico icons/move.ico icons/pencil_cursor.ico icons/pencil.ico @@ -37,7 +43,10 @@ icons/sort_map.ico icons/sort_number.ico icons/tall_grass.ico + icons/minimap.ico icons/viewsprites.ico + icons/application_form_edit.ico + icons/connections.ico icons/ui/dark_checkbox_checked_disabled.png icons/ui/dark_checkbox_checked_disabled@2x.png icons/ui/dark_checkbox_checked.png diff --git a/src/config.cpp b/src/config.cpp index 8c4213d83..3d604da60 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -110,7 +110,6 @@ const QMap> ProjectConfig::defaultIde {ProjectIdentifier::define_map_empty, {"define_map_empty", "UNDEFINED"}}, {ProjectIdentifier::define_map_section_prefix, {"define_map_section_prefix", "MAPSEC_"}}, {ProjectIdentifier::define_map_section_empty, {"define_map_section_empty", "NONE"}}, - {ProjectIdentifier::define_map_section_count, {"define_map_section_count", "COUNT"}}, {ProjectIdentifier::define_species_prefix, {"define_species_prefix", "SPECIES_"}}, // Regex {ProjectIdentifier::regex_behaviors, {"regex_behaviors", "\\bMB_"}}, @@ -167,7 +166,6 @@ const QMap> ProjectConfig::defaultPaths {ProjectFilePath::constants_obj_event_movement, { "constants_obj_event_movement", "include/constants/event_object_movement.h"}}, {ProjectFilePath::constants_obj_events, { "constants_obj_events", "include/constants/event_objects.h"}}, {ProjectFilePath::constants_event_bg, { "constants_event_bg", "include/constants/event_bg.h"}}, - {ProjectFilePath::constants_region_map_sections, { "constants_region_map_sections", "include/constants/region_map_sections.h"}}, {ProjectFilePath::constants_metatile_labels, { "constants_metatile_labels", "include/constants/metatile_labels.h"}}, {ProjectFilePath::constants_metatile_behaviors, { "constants_metatile_behaviors", "include/constants/metatile_behaviors.h"}}, {ProjectFilePath::constants_species, { "constants_species", "include/constants/species.h"}}, @@ -278,18 +276,6 @@ uint32_t KeyValueConfigBase::getConfigUint32(QString key, QString value, uint32_ return qMin(max, qMax(min, result)); } -const QMap mapSortOrderMap = { - {MapSortOrder::Group, "group"}, - {MapSortOrder::Layout, "layout"}, - {MapSortOrder::Area, "area"}, -}; - -const QMap mapSortOrderReverseMap = { - {"group", MapSortOrder::Group}, - {"layout", MapSortOrder::Layout}, - {"area", MapSortOrder::Area}, -}; - PorymapConfig porymapConfig; QString PorymapConfig::getConfigFilepath() { @@ -314,14 +300,8 @@ void PorymapConfig::parseConfigKeyValue(QString key, QString value) { this->reopenOnLaunch = getConfigBool(key, value); } else if (key == "pretty_cursors") { this->prettyCursors = getConfigBool(key, value); - } else if (key == "map_sort_order") { - QString sortOrder = value.toLower(); - if (mapSortOrderReverseMap.contains(sortOrder)) { - this->mapSortOrder = mapSortOrderReverseMap.value(sortOrder); - } else { - this->mapSortOrder = MapSortOrder::Group; - logWarn(QString("Invalid config value for map_sort_order: '%1'. Must be 'group', 'area', or 'layout'.").arg(value)); - } + } else if (key == "map_list_tab") { + this->mapListTab = getConfigInteger(key, value, 0, 2, 0); } else if (key == "main_window_geometry") { this->mainWindowGeometry = bytesFromString(value); } else if (key == "main_window_state") { @@ -438,7 +418,7 @@ QMap PorymapConfig::getKeyValueMap() { map.insert("project_manually_closed", this->projectManuallyClosed ? "1" : "0"); map.insert("reopen_on_launch", this->reopenOnLaunch ? "1" : "0"); map.insert("pretty_cursors", this->prettyCursors ? "1" : "0"); - map.insert("map_sort_order", mapSortOrderMap.value(this->mapSortOrder)); + map.insert("map_list_tab", QString::number(this->mapListTab)); map.insert("main_window_geometry", stringFromByteArray(this->mainWindowGeometry)); map.insert("main_window_state", stringFromByteArray(this->mainWindowState)); map.insert("map_splitter_state", stringFromByteArray(this->mapSplitterState)); @@ -740,8 +720,8 @@ void ProjectConfig::parseConfigKeyValue(QString key, QString value) { } else if (key == "enable_map_allow_flags") { this->mapAllowFlagsEnabled = getConfigBool(key, value); #ifdef CONFIG_BACKWARDS_COMPATABILITY - } else if (key == "recent_map") { - userConfig.recentMap = value; + } else if (key == "recent_map_or_layout") { + userConfig.recentMapOrLayout = value; } else if (key == "use_encounter_json") { userConfig.useEncounterJson = getConfigBool(key, value); } else if (key == "custom_scripts") { @@ -1035,8 +1015,8 @@ QString UserConfig::getConfigFilepath() { } void UserConfig::parseConfigKeyValue(QString key, QString value) { - if (key == "recent_map") { - this->recentMap = value; + if (key == "recent_map_or_layout") { + this->recentMapOrLayout = value; } else if (key == "use_encounter_json") { this->useEncounterJson = getConfigBool(key, value); } else if (key == "custom_scripts") { @@ -1052,7 +1032,7 @@ void UserConfig::setUnreadKeys() { QMap UserConfig::getKeyValueMap() { QMap map; - map.insert("recent_map", this->recentMap); + map.insert("recent_map_or_layout", this->recentMapOrLayout); map.insert("use_encounter_json", QString::number(this->useEncounterJson)); map.insert("custom_scripts", this->outputCustomScripts()); return map; diff --git a/src/core/editcommands.cpp b/src/core/editcommands.cpp index f557ddddb..c500c8c0d 100644 --- a/src/core/editcommands.cpp +++ b/src/core/editcommands.cpp @@ -1,5 +1,4 @@ #include "editcommands.h" -#include "mappixmapitem.h" #include "draggablepixmapitem.h" #include "bordermetatilespixmapitem.h" #include "editor.h" @@ -25,17 +24,17 @@ int getEventTypeMask(QList events) { return eventTypeMask; } -void renderMapBlocks(Map *map, bool ignoreCache = false) { - map->mapItem->draw(ignoreCache); - map->collisionItem->draw(ignoreCache); +void renderBlocks(Layout *layout, bool ignoreCache = false) { + layout->layoutItem->draw(ignoreCache); + layout->collisionItem->draw(ignoreCache); } -PaintMetatile::PaintMetatile(Map *map, +PaintMetatile::PaintMetatile(Layout *layout, const Blockdata &oldMetatiles, const Blockdata &newMetatiles, unsigned actionId, QUndoCommand *parent) : QUndoCommand(parent) { setText("Paint Metatiles"); - this->map = map; + this->layout = layout; this->oldMetatiles = oldMetatiles; this->newMetatiles = newMetatiles; @@ -45,23 +44,23 @@ PaintMetatile::PaintMetatile(Map *map, void PaintMetatile::redo() { QUndoCommand::redo(); - if (!map) return; + if (!layout) return; - map->setBlockdata(newMetatiles, true); + layout->setBlockdata(newMetatiles, true); - map->layout->lastCommitBlocks.blocks = map->layout->blockdata; + layout->lastCommitBlocks.blocks = layout->blockdata; - renderMapBlocks(map); + renderBlocks(layout); } void PaintMetatile::undo() { - if (!map) return; + if (!layout) return; - map->setBlockdata(oldMetatiles, true); + layout->setBlockdata(oldMetatiles, true); - map->layout->lastCommitBlocks.blocks = map->layout->blockdata; + layout->lastCommitBlocks.blocks = layout->blockdata; - renderMapBlocks(map); + renderBlocks(layout); QUndoCommand::undo(); } @@ -69,7 +68,7 @@ void PaintMetatile::undo() { bool PaintMetatile::mergeWith(const QUndoCommand *command) { const PaintMetatile *other = static_cast(command); - if (map != other->map) + if (layout != other->layout) return false; if (actionId != other->actionId) @@ -84,12 +83,12 @@ bool PaintMetatile::mergeWith(const QUndoCommand *command) { ************************************************************************ ******************************************************************************/ -PaintBorder::PaintBorder(Map *map, +PaintBorder::PaintBorder(Layout *layout, const Blockdata &oldBorder, const Blockdata &newBorder, unsigned actionId, QUndoCommand *parent) : QUndoCommand(parent) { setText("Paint Border"); - this->map = map; + this->layout = layout; this->oldBorder = oldBorder; this->newBorder = newBorder; @@ -99,23 +98,23 @@ PaintBorder::PaintBorder(Map *map, void PaintBorder::redo() { QUndoCommand::redo(); - if (!map) return; + if (!layout) return; - map->setBorderBlockData(newBorder, true); + layout->setBorderBlockData(newBorder, true); - map->layout->lastCommitBlocks.border = map->layout->border; + layout->lastCommitBlocks.border = layout->border; - map->borderItem->draw(); + layout->borderItem->draw(); } void PaintBorder::undo() { - if (!map) return; + if (!layout) return; - map->setBorderBlockData(oldBorder, true); + layout->setBorderBlockData(oldBorder, true); - map->layout->lastCommitBlocks.border = map->layout->border; + layout->lastCommitBlocks.border = layout->border; - map->borderItem->draw(); + layout->borderItem->draw(); QUndoCommand::undo(); } @@ -124,12 +123,12 @@ void PaintBorder::undo() { ************************************************************************ ******************************************************************************/ -ShiftMetatiles::ShiftMetatiles(Map *map, +ShiftMetatiles::ShiftMetatiles(Layout *layout, const Blockdata &oldMetatiles, const Blockdata &newMetatiles, unsigned actionId, QUndoCommand *parent) : QUndoCommand(parent) { setText("Shift Metatiles"); - this->map = map; + this->layout = layout; this->oldMetatiles = oldMetatiles; this->newMetatiles = newMetatiles; @@ -139,23 +138,23 @@ ShiftMetatiles::ShiftMetatiles(Map *map, void ShiftMetatiles::redo() { QUndoCommand::redo(); - if (!map) return; + if (!layout) return; - map->setBlockdata(newMetatiles, true); + layout->setBlockdata(newMetatiles, true); - map->layout->lastCommitBlocks.blocks = map->layout->blockdata; + layout->lastCommitBlocks.blocks = layout->blockdata; - renderMapBlocks(map, true); + renderBlocks(layout, true); } void ShiftMetatiles::undo() { - if (!map) return; + if (!layout) return; - map->setBlockdata(oldMetatiles, true); + layout->setBlockdata(oldMetatiles, true); - map->layout->lastCommitBlocks.blocks = map->layout->blockdata; + layout->lastCommitBlocks.blocks = layout->blockdata; - renderMapBlocks(map, true); + renderBlocks(layout, true); QUndoCommand::undo(); } @@ -163,7 +162,7 @@ void ShiftMetatiles::undo() { bool ShiftMetatiles::mergeWith(const QUndoCommand *command) { const ShiftMetatiles *other = static_cast(command); - if (this->map != other->map) + if (this->layout != other->layout) return false; if (actionId != other->actionId) @@ -178,20 +177,20 @@ bool ShiftMetatiles::mergeWith(const QUndoCommand *command) { ************************************************************************ ******************************************************************************/ -ResizeMap::ResizeMap(Map *map, QSize oldMapDimensions, QSize newMapDimensions, +ResizeLayout::ResizeLayout(Layout *layout, QSize oldLayoutDimensions, QSize newLayoutDimensions, const Blockdata &oldMetatiles, const Blockdata &newMetatiles, QSize oldBorderDimensions, QSize newBorderDimensions, const Blockdata &oldBorder, const Blockdata &newBorder, QUndoCommand *parent) : QUndoCommand(parent) { setText("Resize Map"); - this->map = map; + this->layout = layout; - this->oldMapWidth = oldMapDimensions.width(); - this->oldMapHeight = oldMapDimensions.height(); + this->oldLayoutWidth = oldLayoutDimensions.width(); + this->oldLayoutHeight = oldLayoutDimensions.height(); - this->newMapWidth = newMapDimensions.width(); - this->newMapHeight = newMapDimensions.height(); + this->newLayoutWidth = newLayoutDimensions.width(); + this->newLayoutHeight = newLayoutDimensions.height(); this->oldMetatiles = oldMetatiles; this->newMetatiles = newMetatiles; @@ -206,36 +205,36 @@ ResizeMap::ResizeMap(Map *map, QSize oldMapDimensions, QSize newMapDimensions, this->newBorder = newBorder; } -void ResizeMap::redo() { +void ResizeLayout::redo() { QUndoCommand::redo(); - if (!map) return; + if (!layout) return; - map->layout->blockdata = newMetatiles; - map->setDimensions(newMapWidth, newMapHeight, false, true); + layout->blockdata = newMetatiles; + layout->setDimensions(newLayoutWidth, newLayoutHeight, false, true); - map->layout->border = newBorder; - map->setBorderDimensions(newBorderWidth, newBorderHeight, false, true); + layout->border = newBorder; + layout->setBorderDimensions(newBorderWidth, newBorderHeight, false, true); - map->layout->lastCommitBlocks.mapDimensions = QSize(map->getWidth(), map->getHeight()); - map->layout->lastCommitBlocks.borderDimensions = QSize(map->getBorderWidth(), map->getBorderHeight()); + layout->lastCommitBlocks.layoutDimensions = QSize(layout->getWidth(), layout->getHeight()); + layout->lastCommitBlocks.borderDimensions = QSize(layout->getBorderWidth(), layout->getBorderHeight()); - map->mapNeedsRedrawing(); + layout->needsRedrawing(); } -void ResizeMap::undo() { - if (!map) return; +void ResizeLayout::undo() { + if (!layout) return; - map->layout->blockdata = oldMetatiles; - map->setDimensions(oldMapWidth, oldMapHeight, false, true); + layout->blockdata = oldMetatiles; + layout->setDimensions(oldLayoutWidth, oldLayoutHeight, false, true); - map->layout->border = oldBorder; - map->setBorderDimensions(oldBorderWidth, oldBorderHeight, false, true); + layout->border = oldBorder; + layout->setBorderDimensions(oldBorderWidth, oldBorderHeight, false, true); - map->layout->lastCommitBlocks.mapDimensions = QSize(map->getWidth(), map->getHeight()); - map->layout->lastCommitBlocks.borderDimensions = QSize(map->getBorderWidth(), map->getBorderHeight()); + layout->lastCommitBlocks.layoutDimensions = QSize(layout->getWidth(), layout->getHeight()); + layout->lastCommitBlocks.borderDimensions = QSize(layout->getBorderWidth(), layout->getBorderHeight()); - map->mapNeedsRedrawing(); + layout->needsRedrawing(); QUndoCommand::undo(); } @@ -487,23 +486,23 @@ int EventPaste::id() const { ************************************************************************ ******************************************************************************/ -ScriptEditMap::ScriptEditMap(Map *map, - QSize oldMapDimensions, QSize newMapDimensions, +ScriptEditLayout::ScriptEditLayout(Layout *layout, + QSize oldLayoutDimensions, QSize newLayoutDimensions, const Blockdata &oldMetatiles, const Blockdata &newMetatiles, QSize oldBorderDimensions, QSize newBorderDimensions, const Blockdata &oldBorder, const Blockdata &newBorder, QUndoCommand *parent) : QUndoCommand(parent) { - setText("Script Edit Map"); + setText("Script Edit Layout"); - this->map = map; + this->layout = layout; this->newMetatiles = newMetatiles; this->oldMetatiles = oldMetatiles; - this->oldMapWidth = oldMapDimensions.width(); - this->oldMapHeight = oldMapDimensions.height(); - this->newMapWidth = newMapDimensions.width(); - this->newMapHeight = newMapDimensions.height(); + this->oldLayoutWidth = oldLayoutDimensions.width(); + this->oldLayoutHeight = oldLayoutDimensions.height(); + this->newLayoutWidth = newLayoutDimensions.width(); + this->newLayoutHeight = newLayoutDimensions.height(); this->oldBorder = oldBorder; this->newBorder = newBorder; @@ -514,58 +513,58 @@ ScriptEditMap::ScriptEditMap(Map *map, this->newBorderHeight = newBorderDimensions.height(); } -void ScriptEditMap::redo() { +void ScriptEditLayout::redo() { QUndoCommand::redo(); - if (!map) return; + if (!layout) return; - if (newMapWidth != map->getWidth() || newMapHeight != map->getHeight()) { - map->layout->blockdata = newMetatiles; - map->setDimensions(newMapWidth, newMapHeight, false); + if (newLayoutWidth != layout->getWidth() || newLayoutHeight != layout->getHeight()) { + layout->blockdata = newMetatiles; + layout->setDimensions(newLayoutWidth, newLayoutHeight, false); } else { - map->setBlockdata(newMetatiles); + layout->setBlockdata(newMetatiles); } - if (newBorderWidth != map->getBorderWidth() || newBorderHeight != map->getBorderHeight()) { - map->layout->border = newBorder; - map->setBorderDimensions(newBorderWidth, newBorderHeight, false); + if (newBorderWidth != layout->getBorderWidth() || newBorderHeight != layout->getBorderHeight()) { + layout->border = newBorder; + layout->setBorderDimensions(newBorderWidth, newBorderHeight, false); } else { - map->setBorderBlockData(newBorder); + layout->setBorderBlockData(newBorder); } - map->layout->lastCommitBlocks.blocks = newMetatiles; - map->layout->lastCommitBlocks.mapDimensions = QSize(newMapWidth, newMapHeight); - map->layout->lastCommitBlocks.border = newBorder; - map->layout->lastCommitBlocks.borderDimensions = QSize(newBorderWidth, newBorderHeight); + layout->lastCommitBlocks.blocks = newMetatiles; + layout->lastCommitBlocks.layoutDimensions = QSize(newLayoutWidth, newLayoutHeight); + layout->lastCommitBlocks.border = newBorder; + layout->lastCommitBlocks.borderDimensions = QSize(newBorderWidth, newBorderHeight); - renderMapBlocks(map); - map->borderItem->draw(); + renderBlocks(layout, true); + layout->borderItem->draw(); } -void ScriptEditMap::undo() { - if (!map) return; +void ScriptEditLayout::undo() { + if (!layout) return; - if (oldMapWidth != map->getWidth() || oldMapHeight != map->getHeight()) { - map->layout->blockdata = oldMetatiles; - map->setDimensions(oldMapWidth, oldMapHeight, false); + if (oldLayoutWidth != layout->getWidth() || oldLayoutHeight != layout->getHeight()) { + layout->blockdata = oldMetatiles; + layout->setDimensions(oldLayoutWidth, oldLayoutHeight, false); } else { - map->setBlockdata(oldMetatiles); + layout->setBlockdata(oldMetatiles); } - if (oldBorderWidth != map->getBorderWidth() || oldBorderHeight != map->getBorderHeight()) { - map->layout->border = oldBorder; - map->setBorderDimensions(oldBorderWidth, oldBorderHeight, false); + if (oldBorderWidth != layout->getBorderWidth() || oldBorderHeight != layout->getBorderHeight()) { + layout->border = oldBorder; + layout->setBorderDimensions(oldBorderWidth, oldBorderHeight, false); } else { - map->setBorderBlockData(oldBorder); + layout->setBorderBlockData(oldBorder); } - map->layout->lastCommitBlocks.blocks = oldMetatiles; - map->layout->lastCommitBlocks.mapDimensions = QSize(oldMapWidth, oldMapHeight); - map->layout->lastCommitBlocks.border = oldBorder; - map->layout->lastCommitBlocks.borderDimensions = QSize(oldBorderWidth, oldBorderHeight); + layout->lastCommitBlocks.blocks = oldMetatiles; + layout->lastCommitBlocks.layoutDimensions = QSize(oldLayoutWidth, oldLayoutHeight); + layout->lastCommitBlocks.border = oldBorder; + layout->lastCommitBlocks.borderDimensions = QSize(oldBorderWidth, oldBorderHeight); - renderMapBlocks(map); - map->borderItem->draw(); + renderBlocks(layout, true); + layout->borderItem->draw(); QUndoCommand::undo(); } diff --git a/src/core/map.cpp b/src/core/map.cpp index 86df7947c..2a7d96dcc 100644 --- a/src/core/map.cpp +++ b/src/core/map.cpp @@ -28,6 +28,13 @@ void Map::setName(QString mapName) { scriptsLoaded = false; } +void Map::setLayout(Layout *layout) { + this->layout = layout; + if (layout) { + this->layoutId = layout->id; + } +} + QString Map::mapConstantFromName(QString mapName, bool includePrefix) { // Transform map names of the form 'GraniteCave_B1F` into map constants like 'MAP_GRANITE_CAVE_B1F'. static const QRegularExpression caseChange("([a-z])([A-Z])"); @@ -60,165 +67,10 @@ int Map::getBorderHeight() { return layout->getBorderHeight(); } -bool Map::mapBlockChanged(int i, const Blockdata &cache) { - if (cache.length() <= i) - return true; - if (layout->blockdata.length() <= i) - return true; - - return layout->blockdata.at(i) != cache.at(i); -} - -bool Map::borderBlockChanged(int i, const Blockdata &cache) { - if (cache.length() <= i) - return true; - if (layout->border.length() <= i) - return true; - - return layout->border.at(i) != cache.at(i); -} - -void Map::clearBorderCache() { - layout->cached_border.clear(); -} - -void Map::cacheBorder() { - layout->cached_border.clear(); - for (const auto &block : layout->border) - layout->cached_border.append(block); -} - -void Map::cacheBlockdata() { - layout->cached_blockdata.clear(); - for (const auto &block : layout->blockdata) - layout->cached_blockdata.append(block); -} - -void Map::cacheCollision() { - layout->cached_collision.clear(); - for (const auto &block : layout->blockdata) - layout->cached_collision.append(block); -} - -QPixmap Map::renderCollision(bool ignoreCache) { - bool changed_any = false; - int width_ = getWidth(); - int height_ = getHeight(); - if (collision_image.isNull() || collision_image.width() != width_ * 16 || collision_image.height() != height_ * 16) { - collision_image = QImage(width_ * 16, height_ * 16, QImage::Format_RGBA8888); - changed_any = true; - } - if (layout->blockdata.isEmpty() || !width_ || !height_) { - collision_pixmap = collision_pixmap.fromImage(collision_image); - return collision_pixmap; - } - QPainter painter(&collision_image); - for (int i = 0; i < layout->blockdata.length(); i++) { - if (!ignoreCache && !mapBlockChanged(i, layout->cached_collision)) { - continue; - } - changed_any = true; - Block block = layout->blockdata.at(i); - QImage collision_metatile_image = getCollisionMetatileImage(block); - int map_y = width_ ? i / width_ : 0; - int map_x = width_ ? i % width_ : 0; - QPoint metatile_origin = QPoint(map_x * 16, map_y * 16); - painter.drawImage(metatile_origin, collision_metatile_image); - } - painter.end(); - cacheCollision(); - if (changed_any) { - collision_pixmap = collision_pixmap.fromImage(collision_image); - } - return collision_pixmap; -} - -QPixmap Map::render(bool ignoreCache, MapLayout * fromLayout, QRect bounds) { - bool changed_any = false; - int width_ = getWidth(); - int height_ = getHeight(); - if (image.isNull() || image.width() != width_ * 16 || image.height() != height_ * 16) { - image = QImage(width_ * 16, height_ * 16, QImage::Format_RGBA8888); - changed_any = true; - } - if (layout->blockdata.isEmpty() || !width_ || !height_) { - pixmap = pixmap.fromImage(image); - return pixmap; - } - - QPainter painter(&image); - for (int i = 0; i < layout->blockdata.length(); i++) { - if (!ignoreCache && !mapBlockChanged(i, layout->cached_blockdata)) { - continue; - } - changed_any = true; - int map_y = width_ ? i / width_ : 0; - int map_x = width_ ? i % width_ : 0; - if (bounds.isValid() && !bounds.contains(map_x, map_y)) { - continue; - } - QPoint metatile_origin = QPoint(map_x * 16, map_y * 16); - Block block = layout->blockdata.at(i); - QImage metatile_image = getMetatileImage( - block.metatileId(), - fromLayout ? fromLayout->tileset_primary : layout->tileset_primary, - fromLayout ? fromLayout->tileset_secondary : layout->tileset_secondary, - metatileLayerOrder, - metatileLayerOpacity - ); - painter.drawImage(metatile_origin, metatile_image); - } - painter.end(); - if (changed_any) { - cacheBlockdata(); - pixmap = pixmap.fromImage(image); - } - - return pixmap; -} - -QPixmap Map::renderBorder(bool ignoreCache) { - bool changed_any = false, border_resized = false; - int width_ = getBorderWidth(); - int height_ = getBorderHeight(); - if (layout->border_image.isNull()) { - layout->border_image = QImage(width_ * 16, height_ * 16, QImage::Format_RGBA8888); - changed_any = true; - } - if (layout->border_image.width() != width_ * 16 || layout->border_image.height() != height_ * 16) { - layout->border_image = QImage(width_ * 16, height_ * 16, QImage::Format_RGBA8888); - border_resized = true; - } - if (layout->border.isEmpty()) { - layout->border_pixmap = layout->border_pixmap.fromImage(layout->border_image); - return layout->border_pixmap; - } - QPainter painter(&layout->border_image); - for (int i = 0; i < layout->border.length(); i++) { - if (!ignoreCache && (!border_resized && !borderBlockChanged(i, layout->cached_border))) { - continue; - } - - changed_any = true; - Block block = layout->border.at(i); - uint16_t metatileId = block.metatileId(); - QImage metatile_image = getMetatileImage(metatileId, layout->tileset_primary, layout->tileset_secondary, metatileLayerOrder, metatileLayerOpacity); - int map_y = width_ ? i / width_ : 0; - int map_x = width_ ? i % width_ : 0; - painter.drawImage(QPoint(map_x * 16, map_y * 16), metatile_image); - } - painter.end(); - if (changed_any) { - cacheBorder(); - layout->border_pixmap = layout->border_pixmap.fromImage(layout->border_image); - } - return layout->border_pixmap; -} - // Get the portion of the map that can be rendered when rendered as a map connection. // Cardinal connections render the nearest segment of their map and within the bounds of the border draw distance, // Dive/Emerge connections are rendered normally within the bounds of their parent map. -QRect Map::getConnectionRect(const QString &direction, MapLayout * fromLayout) { +QRect Map::getConnectionRect(const QString &direction, Layout * fromLayout) { int x = 0, y = 0; int w = getWidth(), h = getHeight(); @@ -244,7 +96,7 @@ QRect Map::getConnectionRect(const QString &direction, MapLayout * fromLayout) { return QRect(x, y, w, h); } -QPixmap Map::renderConnection(const QString &direction, MapLayout * fromLayout) { +QPixmap Map::renderConnection(const QString &direction, Layout * fromLayout) { QRect bounds = getConnectionRect(direction, fromLayout); if (!bounds.isValid()) return QPixmap(); @@ -254,214 +106,14 @@ QPixmap Map::renderConnection(const QString &direction, MapLayout * fromLayout) if (MapConnection::isDiving(direction)) fromLayout = nullptr; - render(true, fromLayout, bounds); - QImage connection_image = image.copy(bounds.x() * 16, bounds.y() * 16, bounds.width() * 16, bounds.height() * 16); - return QPixmap::fromImage(connection_image); -} - -void Map::setNewDimensionsBlockdata(int newWidth, int newHeight) { - int oldWidth = getWidth(); - int oldHeight = getHeight(); - - Blockdata newBlockdata; - - for (int y = 0; y < newHeight; y++) - for (int x = 0; x < newWidth; x++) { - if (x < oldWidth && y < oldHeight) { - int index = y * oldWidth + x; - newBlockdata.append(layout->blockdata.value(index)); - } else { - newBlockdata.append(0); - } - } - - layout->blockdata = newBlockdata; -} - -void Map::setNewBorderDimensionsBlockdata(int newWidth, int newHeight) { - int oldWidth = getBorderWidth(); - int oldHeight = getBorderHeight(); - - Blockdata newBlockdata; - - for (int y = 0; y < newHeight; y++) - for (int x = 0; x < newWidth; x++) { - if (x < oldWidth && y < oldHeight) { - int index = y * oldWidth + x; - newBlockdata.append(layout->border.value(index)); - } else { - newBlockdata.append(0); - } - } - - layout->border = newBlockdata; -} - -void Map::setDimensions(int newWidth, int newHeight, bool setNewBlockdata, bool enableScriptCallback) { - if (setNewBlockdata) { - setNewDimensionsBlockdata(newWidth, newHeight); - } - - int oldWidth = layout->width; - int oldHeight = layout->height; - layout->width = newWidth; - layout->height = newHeight; - - if (enableScriptCallback && (oldWidth != newWidth || oldHeight != newHeight)) { - Scripting::cb_MapResized(oldWidth, oldHeight, newWidth, newHeight); - } - - emit mapDimensionsChanged(QSize(getWidth(), getHeight())); - modify(); -} - -void Map::setBorderDimensions(int newWidth, int newHeight, bool setNewBlockdata, bool enableScriptCallback) { - if (setNewBlockdata) { - setNewBorderDimensionsBlockdata(newWidth, newHeight); - } - - int oldWidth = layout->border_width; - int oldHeight = layout->border_height; - layout->border_width = newWidth; - layout->border_height = newHeight; - - if (enableScriptCallback && (oldWidth != newWidth || oldHeight != newHeight)) { - Scripting::cb_BorderResized(oldWidth, oldHeight, newWidth, newHeight); - } - - modify(); + QPixmap connectionPixmap = this->layout->render(true, fromLayout, bounds); + return connectionPixmap.copy(bounds.x() * 16, bounds.y() * 16, bounds.width() * 16, bounds.height() * 16); } void Map::openScript(QString label) { emit openScriptRequested(label); } -bool Map::getBlock(int x, int y, Block *out) { - if (isWithinBounds(x, y)) { - int i = y * getWidth() + x; - *out = layout->blockdata.value(i); - return true; - } - return false; -} - -void Map::setBlock(int x, int y, Block block, bool enableScriptCallback) { - if (!isWithinBounds(x, y)) return; - int i = y * getWidth() + x; - if (i < layout->blockdata.size()) { - Block prevBlock = layout->blockdata.at(i); - layout->blockdata.replace(i, block); - if (enableScriptCallback) { - Scripting::cb_MetatileChanged(x, y, prevBlock, block); - } - } -} - -void Map::setBlockdata(Blockdata blockdata, bool enableScriptCallback) { - int width = getWidth(); - int size = qMin(blockdata.size(), layout->blockdata.size()); - for (int i = 0; i < size; i++) { - Block prevBlock = layout->blockdata.at(i); - Block newBlock = blockdata.at(i); - if (prevBlock != newBlock) { - layout->blockdata.replace(i, newBlock); - if (enableScriptCallback) - Scripting::cb_MetatileChanged(i % width, i / width, prevBlock, newBlock); - } - } -} - -uint16_t Map::getBorderMetatileId(int x, int y) { - int i = y * getBorderWidth() + x; - return layout->border[i].metatileId(); -} - -void Map::setBorderMetatileId(int x, int y, uint16_t metatileId, bool enableScriptCallback) { - int i = y * getBorderWidth() + x; - if (i < layout->border.size()) { - uint16_t prevMetatileId = layout->border[i].metatileId(); - layout->border[i].setMetatileId(metatileId); - if (prevMetatileId != metatileId && enableScriptCallback) { - Scripting::cb_BorderMetatileChanged(x, y, prevMetatileId, metatileId); - } - } -} - -void Map::setBorderBlockData(Blockdata blockdata, bool enableScriptCallback) { - int width = getBorderWidth(); - int size = qMin(blockdata.size(), layout->border.size()); - for (int i = 0; i < size; i++) { - Block prevBlock = layout->border.at(i); - Block newBlock = blockdata.at(i); - if (prevBlock != newBlock) { - layout->border.replace(i, newBlock); - if (enableScriptCallback) - Scripting::cb_BorderMetatileChanged(i % width, i / width, prevBlock.metatileId(), newBlock.metatileId()); - } - } -} - -void Map::_floodFillCollisionElevation(int x, int y, uint16_t collision, uint16_t elevation) { - QList todo; - todo.append(QPoint(x, y)); - while (todo.length()) { - QPoint point = todo.takeAt(0); - x = point.x(); - y = point.y(); - Block block; - if (!getBlock(x, y, &block)) { - continue; - } - - uint old_coll = block.collision(); - uint old_elev = block.elevation(); - if (old_coll == collision && old_elev == elevation) { - continue; - } - - block.setCollision(collision); - block.setElevation(elevation); - setBlock(x, y, block, true); - if (getBlock(x + 1, y, &block) && block.collision() == old_coll && block.elevation() == old_elev) { - todo.append(QPoint(x + 1, y)); - } - if (getBlock(x - 1, y, &block) && block.collision() == old_coll && block.elevation() == old_elev) { - todo.append(QPoint(x - 1, y)); - } - if (getBlock(x, y + 1, &block) && block.collision() == old_coll && block.elevation() == old_elev) { - todo.append(QPoint(x, y + 1)); - } - if (getBlock(x, y - 1, &block) && block.collision() == old_coll && block.elevation() == old_elev) { - todo.append(QPoint(x, y - 1)); - } - } -} - -void Map::floodFillCollisionElevation(int x, int y, uint16_t collision, uint16_t elevation) { - Block block; - if (getBlock(x, y, &block) && (block.collision() != collision || block.elevation() != elevation)) { - _floodFillCollisionElevation(x, y, collision, elevation); - } -} - -void Map::magicFillCollisionElevation(int initialX, int initialY, uint16_t collision, uint16_t elevation) { - Block block; - if (getBlock(initialX, initialY, &block) && (block.collision() != collision || block.elevation() != elevation)) { - uint old_coll = block.collision(); - uint old_elev = block.elevation(); - - for (int y = 0; y < getHeight(); y++) { - for (int x = 0; x < getWidth(); x++) { - if (getBlock(x, y, &block) && block.collision() == old_coll && block.elevation() == old_elev) { - block.setCollision(collision); - block.setElevation(elevation); - setBlock(x, y, block, true); - } - } - } - } -} - QList Map::getAllEvents() const { QList all_events; for (const auto &event_list : events) { @@ -604,8 +256,8 @@ void Map::clean() { this->hasUnsavedDataChanges = false; } -bool Map::hasUnsavedChanges() { - return !editHistory.isClean() || hasUnsavedDataChanges || !isPersistedToFile; +bool Map::hasUnsavedChanges() const { + return !editHistory.isClean() || this->layout->hasUnsavedChanges() || hasUnsavedDataChanges || !isPersistedToFile; } void Map::pruneEditHistory() { @@ -628,11 +280,3 @@ void Map::pruneEditHistory() { command->setObsolete(true); } } - -bool Map::isWithinBounds(int x, int y) { - return (x >= 0 && x < this->getWidth() && y >= 0 && y < this->getHeight()); -} - -bool Map::isWithinBorderBounds(int x, int y) { - return (x >= 0 && x < this->getBorderWidth() && y >= 0 && y < this->getBorderHeight()); -} diff --git a/src/core/maplayout.cpp b/src/core/maplayout.cpp index 0f5deecd5..34033ac55 100644 --- a/src/core/maplayout.cpp +++ b/src/core/maplayout.cpp @@ -2,7 +2,35 @@ #include -QString MapLayout::layoutConstantFromName(QString mapName) { +#include "scripting.h" +#include "imageproviders.h" + + + +Layout *Layout::copy() { + Layout *layout = new Layout; + layout->copyFrom(this); + return layout; +} + +void Layout::copyFrom(Layout *other) { + this->id = other->id; + this->name = other->name; + this->width = other->width; + this->height = other->height; + this->border_width = other->border_width; + this->border_height = other->border_height; + this->border_path = other->border_path; + this->blockdata_path = other->blockdata_path; + this->tileset_primary_label = other->tileset_primary_label; + this->tileset_secondary_label = other->tileset_secondary_label; + this->tileset_primary = other->tileset_primary; + this->tileset_secondary = other->tileset_secondary; + this->blockdata = other->blockdata; + this->border = other->border; +} + +QString Layout::layoutConstantFromName(QString mapName) { // Transform map names of the form 'GraniteCave_B1F` into layout constants like 'LAYOUT_GRANITE_CAVE_B1F'. static const QRegularExpression caseChange("([a-z])([A-Z])"); QString nameWithUnderscores = mapName.replace(caseChange, "\\1_\\2"); @@ -17,18 +45,379 @@ QString MapLayout::layoutConstantFromName(QString mapName) { return constantName; } -int MapLayout::getWidth() { +int Layout::getWidth() { return width; } -int MapLayout::getHeight() { +int Layout::getHeight() { return height; } -int MapLayout::getBorderWidth() { +int Layout::getBorderWidth() { return border_width; } -int MapLayout::getBorderHeight() { +int Layout::getBorderHeight() { return border_height; } + +bool Layout::isWithinBounds(int x, int y) { + return (x >= 0 && x < this->getWidth() && y >= 0 && y < this->getHeight()); +} + +bool Layout::isWithinBorderBounds(int x, int y) { + return (x >= 0 && x < this->getBorderWidth() && y >= 0 && y < this->getBorderHeight()); +} + +bool Layout::getBlock(int x, int y, Block *out) { + if (isWithinBounds(x, y)) { + int i = y * getWidth() + x; + *out = this->blockdata.value(i); + return true; + } + return false; +} + +void Layout::setBlock(int x, int y, Block block, bool enableScriptCallback) { + if (!isWithinBounds(x, y)) return; + int i = y * getWidth() + x; + if (i < this->blockdata.size()) { + Block prevBlock = this->blockdata.at(i); + this->blockdata.replace(i, block); + if (enableScriptCallback) { + Scripting::cb_MetatileChanged(x, y, prevBlock, block); + } + } +} + +void Layout::setBlockdata(Blockdata newBlockdata, bool enableScriptCallback) { + int width = getWidth(); + int size = qMin(newBlockdata.size(), this->blockdata.size()); + for (int i = 0; i < size; i++) { + Block prevBlock = this->blockdata.at(i); + Block newBlock = newBlockdata.at(i); + if (prevBlock != newBlock) { + this->blockdata.replace(i, newBlock); + if (enableScriptCallback) + Scripting::cb_MetatileChanged(i % width, i / width, prevBlock, newBlock); + } + } +} + +void Layout::clearBorderCache() { + this->cached_border.clear(); +} + +void Layout::cacheBorder() { + this->cached_border.clear(); + for (const auto &block : this->border) + this->cached_border.append(block); +} + +void Layout::cacheBlockdata() { + this->cached_blockdata.clear(); + for (const auto &block : this->blockdata) + this->cached_blockdata.append(block); +} + +void Layout::cacheCollision() { + this->cached_collision.clear(); + for (const auto &block : this->blockdata) + this->cached_collision.append(block); +} + +bool Layout::layoutBlockChanged(int i, const Blockdata &cache) { + if (cache.length() <= i) + return true; + if (this->blockdata.length() <= i) + return true; + + return this->blockdata.at(i) != cache.at(i); +} + +uint16_t Layout::getBorderMetatileId(int x, int y) { + int i = y * getBorderWidth() + x; + return this->border[i].metatileId(); +} + +void Layout::setBorderMetatileId(int x, int y, uint16_t metatileId, bool enableScriptCallback) { + int i = y * getBorderWidth() + x; + if (i < this->border.size()) { + uint16_t prevMetatileId = this->border[i].metatileId(); + this->border[i].setMetatileId(metatileId); + if (prevMetatileId != metatileId && enableScriptCallback) { + Scripting::cb_BorderMetatileChanged(x, y, prevMetatileId, metatileId); + } + } +} + +void Layout::setBorderBlockData(Blockdata newBlockdata, bool enableScriptCallback) { + int width = getBorderWidth(); + int size = qMin(newBlockdata.size(), this->border.size()); + for (int i = 0; i < size; i++) { + Block prevBlock = this->border.at(i); + Block newBlock = newBlockdata.at(i); + if (prevBlock != newBlock) { + this->border.replace(i, newBlock); + if (enableScriptCallback) + Scripting::cb_BorderMetatileChanged(i % width, i / width, prevBlock.metatileId(), newBlock.metatileId()); + } + } +} + +void Layout::setDimensions(int newWidth, int newHeight, bool setNewBlockdata, bool enableScriptCallback) { + if (setNewBlockdata) { + setNewDimensionsBlockdata(newWidth, newHeight); + } + + int oldWidth = this->width; + int oldHeight = this->height; + this->width = newWidth; + this->height = newHeight; + + if (enableScriptCallback && (oldWidth != newWidth || oldHeight != newHeight)) { + Scripting::cb_MapResized(oldWidth, oldHeight, newWidth, newHeight); + } + + emit layoutChanged(this); + emit layoutDimensionsChanged(QSize(getWidth(), getHeight())); +} + +void Layout::setBorderDimensions(int newWidth, int newHeight, bool setNewBlockdata, bool enableScriptCallback) { + if (setNewBlockdata) { + setNewBorderDimensionsBlockdata(newWidth, newHeight); + } + + int oldWidth = this->border_width; + int oldHeight = this->border_height; + this->border_width = newWidth; + this->border_height = newHeight; + + if (enableScriptCallback && (oldWidth != newWidth || oldHeight != newHeight)) { + Scripting::cb_BorderResized(oldWidth, oldHeight, newWidth, newHeight); + } + + emit layoutChanged(this); +} + +void Layout::setNewDimensionsBlockdata(int newWidth, int newHeight) { + int oldWidth = getWidth(); + int oldHeight = getHeight(); + + Blockdata newBlockdata; + + for (int y = 0; y < newHeight; y++) + for (int x = 0; x < newWidth; x++) { + if (x < oldWidth && y < oldHeight) { + int index = y * oldWidth + x; + newBlockdata.append(this->blockdata.value(index)); + } else { + newBlockdata.append(0); + } + } + + this->blockdata = newBlockdata; +} + +void Layout::setNewBorderDimensionsBlockdata(int newWidth, int newHeight) { + int oldWidth = getBorderWidth(); + int oldHeight = getBorderHeight(); + + Blockdata newBlockdata; + + for (int y = 0; y < newHeight; y++) + for (int x = 0; x < newWidth; x++) { + if (x < oldWidth && y < oldHeight) { + int index = y * oldWidth + x; + newBlockdata.append(this->border.value(index)); + } else { + newBlockdata.append(0); + } + } + + this->border = newBlockdata; +} + +void Layout::_floodFillCollisionElevation(int x, int y, uint16_t collision, uint16_t elevation) { + QList todo; + todo.append(QPoint(x, y)); + while (todo.length()) { + QPoint point = todo.takeAt(0); + x = point.x(); + y = point.y(); + Block block; + if (!getBlock(x, y, &block)) { + continue; + } + + uint old_coll = block.collision(); + uint old_elev = block.elevation(); + if (old_coll == collision && old_elev == elevation) { + continue; + } + + block.setCollision(collision); + block.setElevation(elevation); + setBlock(x, y, block, true); + if (getBlock(x + 1, y, &block) && block.collision() == old_coll && block.elevation() == old_elev) { + todo.append(QPoint(x + 1, y)); + } + if (getBlock(x - 1, y, &block) && block.collision() == old_coll && block.elevation()== old_elev) { + todo.append(QPoint(x - 1, y)); + } + if (getBlock(x, y + 1, &block) && block.collision() == old_coll && block.elevation() == old_elev) { + todo.append(QPoint(x, y + 1)); + } + if (getBlock(x, y - 1, &block) && block.collision() == old_coll && block.elevation() == old_elev) { + todo.append(QPoint(x, y - 1)); + } + } +} + +void Layout::floodFillCollisionElevation(int x, int y, uint16_t collision, uint16_t elevation) { + Block block; + if (getBlock(x, y, &block) && (block.collision() != collision || block.elevation() != elevation)) { + _floodFillCollisionElevation(x, y, collision, elevation); + } +} + +void Layout::magicFillCollisionElevation(int initialX, int initialY, uint16_t collision, uint16_t elevation) { + Block block; + if (getBlock(initialX, initialY, &block) && (block.collision() != collision || block.elevation() != elevation)) { + uint old_coll = block.collision(); + uint old_elev = block.elevation(); + + for (int y = 0; y < getHeight(); y++) { + for (int x = 0; x < getWidth(); x++) { + if (getBlock(x, y, &block) && block.collision() == old_coll && block.elevation() == old_elev) { + block.setCollision(collision); + block.setElevation(elevation); + setBlock(x, y, block, true); + } + } + } + } +} + +QPixmap Layout::render(bool ignoreCache, Layout *fromLayout, QRect bounds) { + bool changed_any = false; + int width_ = getWidth(); + int height_ = getHeight(); + if (image.isNull() || image.width() != width_ * 16 || image.height() != height_ * 16) { + image = QImage(width_ * 16, height_ * 16, QImage::Format_RGBA8888); + changed_any = true; + } + if (this->blockdata.isEmpty() || !width_ || !height_) { + pixmap = pixmap.fromImage(image); + return pixmap; + } + + QPainter painter(&image); + for (int i = 0; i < this->blockdata.length(); i++) { + if (!ignoreCache && !layoutBlockChanged(i, this->cached_blockdata)) { + continue; + } + changed_any = true; + int map_y = width_ ? i / width_ : 0; + int map_x = width_ ? i % width_ : 0; + if (bounds.isValid() && !bounds.contains(map_x, map_y)) { + continue; + } + QPoint metatile_origin = QPoint(map_x * 16, map_y * 16); + Block block = this->blockdata.at(i); + QImage metatile_image = getMetatileImage( + block.metatileId(), + fromLayout ? fromLayout->tileset_primary : this->tileset_primary, + fromLayout ? fromLayout->tileset_secondary : this->tileset_secondary, + metatileLayerOrder, + metatileLayerOpacity + ); + painter.drawImage(metatile_origin, metatile_image); + } + painter.end(); + if (changed_any) { + cacheBlockdata(); + pixmap = pixmap.fromImage(image); + } + + return pixmap; +} + +QPixmap Layout::renderCollision(bool ignoreCache) { + bool changed_any = false; + int width_ = getWidth(); + int height_ = getHeight(); + if (collision_image.isNull() || collision_image.width() != width_ * 16 || collision_image.height() != height_ * 16) { + collision_image = QImage(width_ * 16, height_ * 16, QImage::Format_RGBA8888); + changed_any = true; + } + if (this->blockdata.isEmpty() || !width_ || !height_) { + collision_pixmap = collision_pixmap.fromImage(collision_image); + return collision_pixmap; + } + QPainter painter(&collision_image); + for (int i = 0; i < this->blockdata.length(); i++) { + if (!ignoreCache && !layoutBlockChanged(i, this->cached_collision)) { + continue; + } + changed_any = true; + Block block = this->blockdata.at(i); + QImage collision_metatile_image = getCollisionMetatileImage(block); + int map_y = width_ ? i / width_ : 0; + int map_x = width_ ? i % width_ : 0; + QPoint metatile_origin = QPoint(map_x * 16, map_y * 16); + painter.drawImage(metatile_origin, collision_metatile_image); + } + painter.end(); + cacheCollision(); + if (changed_any) { + collision_pixmap = collision_pixmap.fromImage(collision_image); + } + return collision_pixmap; +} + +QPixmap Layout::renderBorder(bool ignoreCache) { + bool changed_any = false, border_resized = false; + int width_ = getBorderWidth(); + int height_ = getBorderHeight(); + if (this->border_image.isNull()) { + this->border_image = QImage(width_ * 16, height_ * 16, QImage::Format_RGBA8888); + changed_any = true; + } + if (this->border_image.width() != width_ * 16 || this->border_image.height() != height_ * 16) { + this->border_image = QImage(width_ * 16, height_ * 16, QImage::Format_RGBA8888); + border_resized = true; + } + if (this->border.isEmpty()) { + this->border_pixmap = this->border_pixmap.fromImage(this->border_image); + return this->border_pixmap; + } + QPainter painter(&this->border_image); + for (int i = 0; i < this->border.length(); i++) { + if (!ignoreCache && (!border_resized && !layoutBlockChanged(i, this->cached_border))) { + continue; + } + + changed_any = true; + Block block = this->border.at(i); + uint16_t metatileId = block.metatileId(); + QImage metatile_image = getMetatileImage(metatileId, this->tileset_primary, this->tileset_secondary, metatileLayerOrder, metatileLayerOpacity); + int map_y = width_ ? i / width_ : 0; + int map_x = width_ ? i % width_ : 0; + painter.drawImage(QPoint(map_x * 16, map_y * 16), metatile_image); + } + painter.end(); + if (changed_any) { + cacheBorder(); + this->border_pixmap = this->border_pixmap.fromImage(this->border_image); + } + return this->border_pixmap; +} + +QPixmap Layout::getLayoutItemPixmap() { + return this->layoutItem ? this->layoutItem->pixmap() : QPixmap(); +} + +bool Layout::hasUnsavedChanges() const { + return !this->editHistory.isClean(); +} diff --git a/src/core/mapparser.cpp b/src/core/mapparser.cpp index 986c2c242..3d4258bd8 100644 --- a/src/core/mapparser.cpp +++ b/src/core/mapparser.cpp @@ -7,7 +7,7 @@ MapParser::MapParser() { } -MapLayout *MapParser::parse(QString filepath, bool *error, Project *project) +Layout *MapParser::parse(QString filepath, bool *error, Project *project) { QFile file(filepath); if (!file.open(QIODevice::ReadOnly)) { @@ -69,7 +69,7 @@ MapLayout *MapParser::parse(QString filepath, bool *error, Project *project) } } - MapLayout *mapLayout = new MapLayout(); + Layout *mapLayout = new Layout(); mapLayout->width = mapWidth; mapLayout->height = mapHeight; mapLayout->border_width = (borderWidth == 0) ? DEFAULT_BORDER_WIDTH : borderWidth; diff --git a/src/core/regionmap.cpp b/src/core/regionmap.cpp index 7dbd4c5f1..94650ccd9 100644 --- a/src/core/regionmap.cpp +++ b/src/core/regionmap.cpp @@ -19,8 +19,7 @@ using std::make_shared; RegionMap::RegionMap(Project *project) : section_prefix(projectConfig.getIdentifier(ProjectIdentifier::define_map_section_prefix)), - default_map_section(section_prefix + projectConfig.getIdentifier(ProjectIdentifier::define_map_section_empty)), - count_map_section(section_prefix + projectConfig.getIdentifier(ProjectIdentifier::define_map_section_count)) + default_map_section(project->getEmptyMapsecName()) { this->project = project; } @@ -157,7 +156,7 @@ bool RegionMap::loadLayout(poryjson::Json layoutJson) { for (int x = 0; x < this->layout_width; x++) { int bin_index = x + y * this->layout_width; uint8_t square_section_id = mapBinData.at(bin_index); - QString square_section_name = project->mapSectionValueToName.value(square_section_id); + QString square_section_name = project->mapSectionIdNames.value(square_section_id, this->default_map_section); LayoutSquare square; square.map_section = square_section_name; @@ -401,7 +400,7 @@ void RegionMap::saveLayout() { for (int m = 0; m < this->layout_height; m++) { for (int n = 0; n < this->layout_width; n++) { int i = n + this->layout_width * m; - data.append(this->project->mapSectionNameToValue.value(this->layouts["main"][i].map_section)); + data.append(this->project->mapSectionIdNames.indexOf(this->layouts["main"][i].map_section)); } } QFile bfile(fullPath(this->layout_path)); @@ -760,18 +759,15 @@ bool RegionMap::squareInLayout(int x, int y) { } MapSectionEntry RegionMap::getEntry(QString section) { - if (this->region_map_entries->contains(section)) - return this->region_map_entries->operator[](section); - else - return MapSectionEntry(); + return this->region_map_entries->value(section, MapSectionEntry()); } void RegionMap::setEntry(QString section, MapSectionEntry entry) { - this->region_map_entries->operator[](section) = entry; + this->region_map_entries->insert(section, entry); } void RegionMap::removeEntry(QString section) { - this->region_map_entries->erase(section); + this->region_map_entries->remove(section); } QString RegionMap::palPath() { @@ -788,27 +784,6 @@ int RegionMap::getMapSquareIndex(int x, int y) { return ((index < tilemap.length()) && (index >= 0)) ? index : 0; } -// For turning a MAPSEC_NAME into a unique identifier sMapName-style variable. -// CAPS_WITH_UNDERSCORE to CamelCase -QString RegionMap::fixCase(QString caps) { - bool big = true; - QString camel; - - static const QRegularExpression re_braced("({.*})"); - for (auto ch : caps.remove(re_braced).remove(this->section_prefix)) { - if (ch == '_' || ch == ' ') { - big = true; - continue; - } - if (big) { - camel += ch.toUpper(); - big = false; - } - else camel += ch.toLower(); - } - return camel; -} - QString RegionMap::fullPath(QString local) { return this->project->root + "/" + local; } diff --git a/src/core/regionmapeditcommands.cpp b/src/core/regionmapeditcommands.cpp index e718d596e..7a12cbc6c 100644 --- a/src/core/regionmapeditcommands.cpp +++ b/src/core/regionmapeditcommands.cpp @@ -90,7 +90,7 @@ bool EditLayout::mergeWith(const QUndoCommand *command) { /// -ResizeLayout::ResizeLayout(RegionMap *map, int oldWidth, int oldHeight, int newWidth, int newHeight, +ResizeRMLayout::ResizeRMLayout(RegionMap *map, int oldWidth, int oldHeight, int newWidth, int newHeight, QMap> oldLayouts, QMap> newLayouts, QUndoCommand *parent) : QUndoCommand(parent) { setText("Change Layout Dimensions"); @@ -104,7 +104,7 @@ ResizeLayout::ResizeLayout(RegionMap *map, int oldWidth, int oldHeight, int newW this->newLayouts = newLayouts; } -void ResizeLayout::redo() { +void ResizeRMLayout::redo() { QUndoCommand::redo(); if (!map) return; @@ -113,7 +113,7 @@ void ResizeLayout::redo() { map->setAllLayouts(this->newLayouts); } -void ResizeLayout::undo() { +void ResizeRMLayout::undo() { if (!map) return; map->setLayoutDimensions(oldWidth, oldHeight, false); @@ -122,8 +122,8 @@ void ResizeLayout::undo() { QUndoCommand::undo(); } -bool ResizeLayout::mergeWith(const QUndoCommand *command) { - const ResizeLayout *other = static_cast(command); +bool ResizeRMLayout::mergeWith(const QUndoCommand *command) { + const ResizeRMLayout *other = static_cast(command); if (this->map != other->map) return false; @@ -260,7 +260,7 @@ void ResizeTilemap::undo() { /// -ClearEntries::ClearEntries(RegionMap *map, tsl::ordered_map entries, QUndoCommand *parent) +ClearEntries::ClearEntries(RegionMap *map, QMap entries, QUndoCommand *parent) : QUndoCommand(parent) { setText("Clear Entries"); diff --git a/src/editor.cpp b/src/editor.cpp index 3929d1e36..86cedd5b7 100644 --- a/src/editor.cpp +++ b/src/editor.cpp @@ -4,7 +4,7 @@ #include "log.h" #include "connectionslistitem.h" #include "currentselectedmetatilespixmapitem.h" -#include "mapsceneeventfilter.h" +#include "eventfilters.h" #include "metatile.h" #include "montabwidget.h" #include "editcommands.h" @@ -75,10 +75,14 @@ void Editor::saveProject() { } void Editor::save() { - if (project && map) { + if (this->project && this->map) { saveUiFields(); - project->saveMap(map); - project->saveAllDataStructures(); + this->project->saveMap(this->map); + this->project->saveAllDataStructures(); + } + else if (this->project && this->layout) { + this->project->saveLayout(this->layout); + this->project->saveAllDataStructures(); } } @@ -102,66 +106,103 @@ void Editor::closeProject() { delete this->project; } -void Editor::setEditingMap() { - current_view = map_item; - if (map_item) { - map_item->paintingMode = MapPixmapItem::PaintMode::Metatiles; - map_item->draw(); - map_item->setVisible(true); - } - if (collision_item) { - collision_item->setVisible(false); - } - if (events_group) { - events_group->setVisible(false); +bool Editor::getEditingLayout() { + return this->editMode == EditMode::Metatiles || this->editMode == EditMode::Collision; +} + +void Editor::setEditorView() { + // based on editMode + if (!map_item || !collision_item) return; + if (!this->layout) return; + + map_item->setVisible(true); // is map item ever not visible + collision_item->setVisible(false); + + switch (this->editMode) { + case EditMode::Metatiles: + case EditMode::Connections: + case EditMode::Events: + current_view = map_item; + break; + case EditMode::Collision: + current_view = collision_item; + break; + default: + current_view = nullptr; + return; } + + map_item->draw(); + collision_item->draw(); + + current_view->setVisible(true); + updateBorderVisibility(); - this->cursorMapTileRect->stopSingleTileMode(); + this->cursorMapTileRect->setSingleTileMode(); this->cursorMapTileRect->setActive(true); - setMapEditingButtonsEnabled(true); + switch (this->editMode) { + case EditMode::Metatiles: + case EditMode::Collision: + map_item->setEditsEnabled(true); + this->editGroup.setActiveStack(&this->layout->editHistory); + break; + case EditMode::Connections: + this->cursorMapTileRect->setActive(false); + map_item->setEditsEnabled(false); + case EditMode::Events: + if (this->map) { + this->editGroup.setActiveStack(&this->map->editHistory); + } + break; + case EditMode::Header: + case EditMode::Encounters: + default: + this->editGroup.setActiveStack(nullptr); + break; + } + + if (this->events_group) { + this->events_group->setVisible(this->editMode == EditMode::Events); + } + setMapEditingButtonsEnabled(this->editMode != EditMode::Events); +} + +void Editor::setEditingMetatiles() { + this->editMode = EditMode::Metatiles; + + setEditorView(); } void Editor::setEditingCollision() { - current_view = collision_item; - if (collision_item) { - collision_item->draw(); - collision_item->setVisible(true); - } - if (map_item) { - map_item->paintingMode = MapPixmapItem::PaintMode::Metatiles; - map_item->draw(); - map_item->setVisible(true); - } - if (events_group) { - events_group->setVisible(false); - } - updateBorderVisibility(); - this->cursorMapTileRect->setSingleTileMode(); - this->cursorMapTileRect->setActive(true); + this->editMode = EditMode::Collision; - setMapEditingButtonsEnabled(true); + setEditorView(); +} + +void Editor::setEditingHeader() { + this->editMode = EditMode::Header; + + setEditorView(); } void Editor::setEditingObjects() { - current_view = map_item; - if (events_group) { - events_group->setVisible(true); - } - if (map_item) { - map_item->paintingMode = MapPixmapItem::PaintMode::EventObjects; - map_item->draw(); - map_item->setVisible(true); - } - if (collision_item) { - collision_item->setVisible(false); - } - updateBorderVisibility(); - this->cursorMapTileRect->setSingleTileMode(); - this->cursorMapTileRect->setActive(false); + this->editMode = EditMode::Events; + + setEditorView(); updateWarpEventWarnings(); +} - setMapEditingButtonsEnabled(false); +void Editor::setEditingConnections() { + this->editMode = EditMode::Connections; + + setEditorView(); +} + +void Editor::setEditingEncounters() { + this->editMode = EditMode::Encounters; + + setEditorView(); } void Editor::setMapEditingButtonsEnabled(bool enabled) { @@ -170,7 +211,7 @@ void Editor::setMapEditingButtonsEnabled(bool enabled) { this->ui->pushButton_ChangeDimensions->setEnabled(enabled); // If the fill button is pressed, unpress it and select the pointer. if (!enabled && (this->ui->toolButton_Fill->isChecked() || this->ui->toolButton_Dropper->isChecked())) { - this->map_edit_mode = "select"; + this->mapEditAction = EditAction::Select; this->settings->mapCursor = QCursor(); this->cursorMapTileRect->setSingleTileMode(); this->ui->toolButton_Fill->setChecked(false); @@ -180,24 +221,6 @@ void Editor::setMapEditingButtonsEnabled(bool enabled) { this->ui->checkBox_smartPaths->setEnabled(enabled); } -void Editor::setEditingConnections() { - current_view = map_item; - if (map_item) { - map_item->paintingMode = MapPixmapItem::PaintMode::Disabled; - map_item->draw(); - map_item->setVisible(true); - } - if (collision_item) { - collision_item->setVisible(false); - } - if (events_group) { - events_group->setVisible(false); - } - updateBorderVisibility(); - this->cursorMapTileRect->setSingleTileMode(); - this->cursorMapTileRect->setActive(false); -} - void Editor::clearWildMonTables() { QStackedWidget *stack = ui->stackedWidget_WildMons; const QSignalBlocker blocker(stack); @@ -788,6 +811,9 @@ void Editor::displayConnection(MapConnection *connection) { connect(listItem, &ConnectionsListItem::openMapClicked, this, &Editor::openConnectedMap); connect(pixmapItem, &ConnectionPixmapItem::connectionItemDoubleClicked, this, &Editor::openConnectedMap); + // Pressing the delete key on a selected connection's pixmap deletes it + connect(pixmapItem, &ConnectionPixmapItem::deleteRequested, this, &Editor::removeConnection); + // Sync the selection highlight between the list UI and the pixmap connect(pixmapItem, &ConnectionPixmapItem::selectionChanged, [=](bool selected) { listItem->setSelected(selected); @@ -846,11 +872,6 @@ void Editor::removeConnection(MapConnection *connection) { this->map->editHistory.push(new MapConnectionRemove(this->map, connection)); } -void Editor::removeSelectedConnection() { - if (selected_connection_item) - removeConnection(selected_connection_item->connection); -} - void Editor::removeConnectionPixmap(MapConnection *connection) { if (!connection) return; @@ -1059,8 +1080,8 @@ void Editor::onHoveredMovementPermissionCleared() { } QString Editor::getMetatileDisplayMessage(uint16_t metatileId) { - Metatile *metatile = Tileset::getMetatile(metatileId, map->layout->tileset_primary, map->layout->tileset_secondary); - QString label = Tileset::getMetatileLabel(metatileId, map->layout->tileset_primary, map->layout->tileset_secondary); + Metatile *metatile = Tileset::getMetatile(metatileId, this->layout->tileset_primary, this->layout->tileset_secondary); + QString label = Tileset::getMetatileLabel(metatileId, this->layout->tileset_primary, this->layout->tileset_secondary); QString message = QString("Metatile: %1").arg(Metatile::getMetatileIdString(metatileId)); if (label.size()) message += QString(" \"%1\"").arg(label); @@ -1146,46 +1167,46 @@ void Editor::setCursorRectVisible(bool visible) { void Editor::onHoveredMapMetatileChanged(const QPoint &pos) { int x = pos.x(); int y = pos.y(); - if (!map->isWithinBounds(x, y)) + if (!layout->isWithinBounds(x, y)) return; this->updateCursorRectPos(x, y); - if (map_item->paintingMode == MapPixmapItem::PaintMode::Metatiles) { - int blockIndex = y * map->getWidth() + x; - int metatileId = map->layout->blockdata.at(blockIndex).metatileId(); + if (this->getEditingLayout()) { + int blockIndex = y * layout->getWidth() + x; + int metatileId = layout->blockdata.at(blockIndex).metatileId(); this->ui->statusBar->showMessage(QString("X: %1, Y: %2, %3, Scale = %4x") .arg(x) .arg(y) .arg(getMetatileDisplayMessage(metatileId)) .arg(QString::number(zoomLevels[this->scaleIndex], 'g', 2))); } - else if (map_item->paintingMode == MapPixmapItem::PaintMode::EventObjects) { + else if (this->editMode == EditMode::Events) { this->ui->statusBar->showMessage(QString("X: %1, Y: %2, Scale = %3x") .arg(x) .arg(y) .arg(QString::number(zoomLevels[this->scaleIndex], 'g', 2))); } + Scripting::cb_BlockHoverChanged(x, y); } void Editor::onHoveredMapMetatileCleared() { this->setCursorRectVisible(false); - if (map_item->paintingMode == MapPixmapItem::PaintMode::Metatiles - || map_item->paintingMode == MapPixmapItem::PaintMode::EventObjects) { + if (!map_item->getEditsEnabled()) { this->ui->statusBar->clearMessage(); } Scripting::cb_BlockHoverCleared(); } void Editor::onHoveredMapMovementPermissionChanged(int x, int y) { - if (!map->isWithinBounds(x, y)) + if (!layout->isWithinBounds(x, y)) return; this->updateCursorRectPos(x, y); - if (map_item->paintingMode == MapPixmapItem::PaintMode::Metatiles) { - int blockIndex = y * map->getWidth() + x; - uint16_t collision = map->layout->blockdata.at(blockIndex).collision(); - uint16_t elevation = map->layout->blockdata.at(blockIndex).elevation(); + if (this->getEditingLayout()) { + int blockIndex = y * layout->getWidth() + x; + uint16_t collision = layout->blockdata.at(blockIndex).collision(); + uint16_t elevation = layout->blockdata.at(blockIndex).elevation(); QString message = QString("X: %1, Y: %2, %3") .arg(x) .arg(y) @@ -1197,7 +1218,7 @@ void Editor::onHoveredMapMovementPermissionChanged(int x, int y) { void Editor::onHoveredMapMovementPermissionCleared() { this->setCursorRectVisible(false); - if (map_item->paintingMode == MapPixmapItem::PaintMode::Metatiles) { + if (this->getEditingLayout()) { this->ui->statusBar->clearMessage(); } Scripting::cb_BlockHoverCleared(); @@ -1219,11 +1240,7 @@ QString Editor::getMovementPermissionText(uint16_t collision, uint16_t elevation return message; } -bool Editor::setMap(QString map_name) { - if (map_name.isEmpty()) { - return false; - } - +void Editor::unsetMap() { // disconnect previous map's signals so they are not firing // multiple times if set again in the future if (map) { @@ -1232,47 +1249,90 @@ bool Editor::setMap(QString map_name) { for (auto connection : map->getConnections()) disconnectMapConnection(connection); } + clearMapConnections(); - if (project) { - Map *loadedMap = project->loadMap(map_name); - if (!loadedMap) { - return false; - } + this->map = nullptr; +} - map = loadedMap; +bool Editor::setMap(QString map_name) { + if (!project || map_name.isEmpty()) { + return false; + } - editGroup.addStack(&map->editHistory); - editGroup.setActiveStack(&map->editHistory); - selected_events->clear(); - if (!displayMap()) { - return false; - } - map_ruler->setMapDimensions(QSize(map->getWidth(), map->getHeight())); - connect(map, &Map::mapDimensionsChanged, map_ruler, &MapRuler::setMapDimensions); - connect(map, &Map::openScriptRequested, this, &Editor::openScript); - connect(map, &Map::connectionAdded, this, &Editor::displayConnection); - connect(map, &Map::connectionRemoved, this, &Editor::removeConnectionPixmap); - updateSelectedEvents(); + unsetMap(); + + Map *loadedMap = project->loadMap(map_name); + if (!loadedMap) { + return false; + } + + this->map = loadedMap; + + setLayout(map->layout->id); + + editGroup.addStack(&map->editHistory); + editGroup.setActiveStack(&map->editHistory); + + selected_events->clear(); + if (!displayMap()) { + return false; } + displayWildMonTables(); + + connect(map, &Map::openScriptRequested, this, &Editor::openScript); + connect(map, &Map::connectionAdded, this, &Editor::displayConnection); + connect(map, &Map::connectionRemoved, this, &Editor::removeConnectionPixmap); + updateSelectedEvents(); return true; } -void Editor::onMapStartPaint(QGraphicsSceneMouseEvent *event, MapPixmapItem *item) { - if (item->paintingMode != MapPixmapItem::PaintMode::Metatiles) { +bool Editor::setLayout(QString layoutId) { + if (!project || layoutId.isEmpty()) { + return false; + } + + this->layout = this->project->loadLayout(layoutId); + + if (!displayLayout()) { + return false; + } + + editGroup.addStack(&layout->editHistory); + + map_ruler->setMapDimensions(QSize(this->layout->getWidth(), this->layout->getHeight())); + connect(this->layout, &Layout::layoutDimensionsChanged, map_ruler, &MapRuler::setMapDimensions); + + ui->comboBox_PrimaryTileset->blockSignals(true); + ui->comboBox_SecondaryTileset->blockSignals(true); + ui->comboBox_PrimaryTileset->setCurrentText(this->layout->tileset_primary_label); + ui->comboBox_SecondaryTileset->setCurrentText(this->layout->tileset_secondary_label); + ui->comboBox_PrimaryTileset->blockSignals(false); + ui->comboBox_SecondaryTileset->blockSignals(false); + + const QSignalBlocker b0(this->ui->comboBox_LayoutSelector); + int index = this->ui->comboBox_LayoutSelector->findText(layoutId); + if (index < 0) index = 0; + this->ui->comboBox_LayoutSelector->setCurrentIndex(index); + + return true; +} + +void Editor::onMapStartPaint(QGraphicsSceneMouseEvent *event, LayoutPixmapItem *) { + if (!this->getEditingLayout()) { return; } QPoint pos = Metatile::coordFromPixmapCoord(event->pos()); - if (event->buttons() & Qt::RightButton && (map_edit_mode == "paint" || map_edit_mode == "fill")) { + if (event->buttons() & Qt::RightButton && (mapEditAction == EditAction::Paint || mapEditAction == EditAction::Fill)) { this->cursorMapTileRect->initRightClickSelectionAnchor(pos.x(), pos.y()); } else { this->cursorMapTileRect->initAnchor(pos.x(), pos.y()); } } -void Editor::onMapEndPaint(QGraphicsSceneMouseEvent *, MapPixmapItem *item) { - if (!(item->paintingMode == MapPixmapItem::PaintMode::Metatiles)) { +void Editor::onMapEndPaint(QGraphicsSceneMouseEvent *, LayoutPixmapItem *) { + if (!this->getEditingLayout()) { return; } this->cursorMapTileRect->stopRightClickSelectionAnchor(); @@ -1305,16 +1365,16 @@ void Editor::setStraightPathCursorMode(QGraphicsSceneMouseEvent *event) { } } -void Editor::mouseEvent_map(QGraphicsSceneMouseEvent *event, MapPixmapItem *item) { +void Editor::mouseEvent_map(QGraphicsSceneMouseEvent *event, LayoutPixmapItem *item) { // TODO: add event tab object painting tool buttons stuff here - if (item->paintingMode == MapPixmapItem::PaintMode::Disabled) { + if (!item->getEditsEnabled()) { return; } QPoint pos = Metatile::coordFromPixmapCoord(event->pos()); - if (item->paintingMode == MapPixmapItem::PaintMode::Metatiles) { - if (map_edit_mode == "paint") { + if (this->getEditingLayout()) { + if (mapEditAction == EditAction::Paint) { if (event->buttons() & Qt::RightButton) { item->updateMetatileSelection(event); } else if (event->buttons() & Qt::MiddleButton) { @@ -1336,9 +1396,9 @@ void Editor::mouseEvent_map(QGraphicsSceneMouseEvent *event, MapPixmapItem *item } item->paint(event); } - } else if (map_edit_mode == "select") { + } else if (mapEditAction == EditAction::Select) { item->select(event); - } else if (map_edit_mode == "fill") { + } else if (mapEditAction == EditAction::Fill) { if (event->buttons() & Qt::RightButton) { item->updateMetatileSelection(event); } else if (event->modifiers() & Qt::ControlModifier) { @@ -1346,13 +1406,13 @@ void Editor::mouseEvent_map(QGraphicsSceneMouseEvent *event, MapPixmapItem *item } else { item->floodFill(event); } - } else if (map_edit_mode == "pick") { + } else if (mapEditAction == EditAction::Pick) { if (event->buttons() & Qt::RightButton) { item->updateMetatileSelection(event); } else { item->pick(event); } - } else if (map_edit_mode == "shift") { + } else if (mapEditAction == EditAction::Shift) { this->setStraightPathCursorMode(event); if (this->cursorMapTileRect->getStraightPathMode()) { item->lockNondominantAxis(event); @@ -1360,11 +1420,11 @@ void Editor::mouseEvent_map(QGraphicsSceneMouseEvent *event, MapPixmapItem *item } item->shift(event); } - } else if (item->paintingMode == MapPixmapItem::PaintMode::EventObjects) { - if (obj_edit_mode == "paint" && event->type() == QEvent::GraphicsSceneMousePress) { + } else if (this->editMode == EditMode::Events) { + if (objectEditAction == EditAction::Paint && event->type() == QEvent::GraphicsSceneMousePress) { // Right-clicking while in paint mode will change mode to select. if (event->buttons() & Qt::RightButton) { - this->obj_edit_mode = "select"; + this->objectEditAction = EditAction::Select; this->settings->mapCursor = QCursor(); this->cursorMapTileRect->setSingleTileMode(); this->ui->toolButton_Paint->setChecked(false); @@ -1386,9 +1446,9 @@ void Editor::mouseEvent_map(QGraphicsSceneMouseEvent *event, MapPixmapItem *item } } } - } else if (obj_edit_mode == "select") { + } else if (objectEditAction == EditAction::Select) { // do nothing here, at least for now - } else if (obj_edit_mode == "shift" && item->map) { + } else if (objectEditAction == EditAction::Shift) { static QPoint selection_origin; static unsigned actionId = 0; @@ -1404,8 +1464,8 @@ void Editor::mouseEvent_map(QGraphicsSceneMouseEvent *event, MapPixmapItem *item QList selectedEvents; - for (DraggablePixmapItem *item : getObjects()) { - selectedEvents.append(item->event); + for (DraggablePixmapItem *pixmapItem : getObjects()) { + selectedEvents.append(pixmapItem->event); } selection_origin = QPoint(pos.x(), pos.y()); @@ -1418,13 +1478,13 @@ void Editor::mouseEvent_map(QGraphicsSceneMouseEvent *event, MapPixmapItem *item } void Editor::mouseEvent_collision(QGraphicsSceneMouseEvent *event, CollisionPixmapItem *item) { - if (item->paintingMode != MapPixmapItem::PaintMode::Metatiles) { + if (!item->getEditsEnabled()) { return; } QPoint pos = Metatile::coordFromPixmapCoord(event->pos()); - if (map_edit_mode == "paint") { + if (mapEditAction == EditAction::Paint) { if (event->buttons() & Qt::RightButton) { item->updateMovementPermissionSelection(event); } else if (event->buttons() & Qt::MiddleButton) { @@ -1441,9 +1501,9 @@ void Editor::mouseEvent_collision(QGraphicsSceneMouseEvent *event, CollisionPixm } item->paint(event); } - } else if (map_edit_mode == "select") { + } else if (mapEditAction == EditAction::Select) { item->select(event); - } else if (map_edit_mode == "fill") { + } else if (mapEditAction == EditAction::Fill) { if (event->buttons() & Qt::RightButton) { item->pick(event); } else if (event->modifiers() & Qt::ControlModifier) { @@ -1451,9 +1511,9 @@ void Editor::mouseEvent_collision(QGraphicsSceneMouseEvent *event, CollisionPixm } else { item->floodFill(event); } - } else if (map_edit_mode == "pick") { + } else if (mapEditAction == EditAction::Pick) { item->pick(event); - } else if (map_edit_mode == "shift") { + } else if (mapEditAction == EditAction::Shift) { this->setStraightPathCursorMode(event); if (this->cursorMapTileRect->getStraightPathMode()) { item->lockNondominantAxis(event); @@ -1490,6 +1550,23 @@ void Editor::clearMap() { } bool Editor::displayMap() { + if (!this->map) + return false; + + displayMapEvents(); + displayMapConnections(); + maskNonVisibleConnectionTiles(); + + if (events_group) { + events_group->setVisible(false); + } + return true; +} + +bool Editor::displayLayout() { + if (!this->layout) + return false; + if (!scene) { scene = new QGraphicsScene; MapSceneEventFilter *filter = new MapSceneEventFilter(scene); @@ -1498,18 +1575,15 @@ bool Editor::displayMap() { scene->installEventFilter(this->map_ruler); } + clearConnectionMask(); displayMetatileSelector(); - displayMovementPermissionSelector(); displayMapMetatiles(); + displayMovementPermissionSelector(); displayMapMovementPermissions(); displayBorderMetatiles(); displayCurrentMetatilesSelection(); - displayMapEvents(); - displayMapConnections(); displayMapBorder(); displayMapGrid(); - displayWildMonTables(); - maskNonVisibleConnectionTiles(); this->map_ruler->setZValue(1000); scene->addItem(this->map_ruler); @@ -1520,9 +1594,7 @@ bool Editor::displayMap() { if (collision_item) { collision_item->setVisible(false); } - if (events_group) { - events_group->setVisible(false); - } + return true; } @@ -1538,7 +1610,7 @@ void Editor::displayMetatileSelector() { scene_metatiles = new QGraphicsScene; if (!metatile_selector_item) { - metatile_selector_item = new MetatileSelector(8, map); + metatile_selector_item = new MetatileSelector(8, this->layout); connect(metatile_selector_item, &MetatileSelector::hoveredMetatileSelectionChanged, this, &Editor::onHoveredMetatileSelectionChanged); connect(metatile_selector_item, &MetatileSelector::hoveredMetatileSelectionCleared, @@ -1547,14 +1619,14 @@ void Editor::displayMetatileSelector() { this, &Editor::onSelectedMetatilesChanged); metatile_selector_item->select(0); } else { - metatile_selector_item->setMap(map); + metatile_selector_item->setLayout(this->layout); if (metatile_selector_item->primaryTileset - && metatile_selector_item->primaryTileset != map->layout->tileset_primary) - emit tilesetUpdated(map->layout->tileset_primary->name); + && metatile_selector_item->primaryTileset != this->layout->tileset_primary) + emit tilesetUpdated(this->layout->tileset_primary->name); if (metatile_selector_item->secondaryTileset - && metatile_selector_item->secondaryTileset != map->layout->tileset_secondary) - emit tilesetUpdated(map->layout->tileset_secondary->name); - metatile_selector_item->setTilesets(map->layout->tileset_primary, map->layout->tileset_secondary); + && metatile_selector_item->secondaryTileset != this->layout->tileset_secondary) + emit tilesetUpdated(this->layout->tileset_secondary->name); + metatile_selector_item->setTilesets(this->layout->tileset_primary, this->layout->tileset_secondary); } scene_metatiles->addItem(metatile_selector_item); @@ -1571,12 +1643,12 @@ void Editor::clearMapMetatiles() { void Editor::displayMapMetatiles() { clearMapMetatiles(); - map_item = new MapPixmapItem(map, this->metatile_selector_item, this->settings); - connect(map_item, &MapPixmapItem::mouseEvent, this, &Editor::mouseEvent_map); - connect(map_item, &MapPixmapItem::startPaint, this, &Editor::onMapStartPaint); - connect(map_item, &MapPixmapItem::endPaint, this, &Editor::onMapEndPaint); - connect(map_item, &MapPixmapItem::hoveredMapMetatileChanged, this, &Editor::onHoveredMapMetatileChanged); - connect(map_item, &MapPixmapItem::hoveredMapMetatileCleared, this, &Editor::onHoveredMapMetatileCleared); + map_item = new LayoutPixmapItem(this->layout, this->metatile_selector_item, this->settings); + connect(map_item, &LayoutPixmapItem::mouseEvent, this, &Editor::mouseEvent_map); + connect(map_item, &LayoutPixmapItem::startPaint, this, &Editor::onMapStartPaint); + connect(map_item, &LayoutPixmapItem::endPaint, this, &Editor::onMapEndPaint); + connect(map_item, &LayoutPixmapItem::hoveredMapMetatileChanged, this, &Editor::onHoveredMapMetatileChanged); + connect(map_item, &LayoutPixmapItem::hoveredMapMetatileCleared, this, &Editor::onHoveredMapMetatileCleared); map_item->draw(true); scene->addItem(map_item); @@ -1601,7 +1673,7 @@ void Editor::clearMapMovementPermissions() { void Editor::displayMapMovementPermissions() { clearMapMovementPermissions(); - collision_item = new CollisionPixmapItem(map, ui->spinBox_SelectedCollision, ui->spinBox_SelectedElevation, + collision_item = new CollisionPixmapItem(this->layout, ui->spinBox_SelectedCollision, ui->spinBox_SelectedElevation, this->metatile_selector_item, this->settings, &this->collisionOpacity); connect(collision_item, &CollisionPixmapItem::mouseEvent, this, &Editor::mouseEvent_collision); connect(collision_item, &CollisionPixmapItem::hoveredMapMovementPermissionChanged, @@ -1625,7 +1697,7 @@ void Editor::displayBorderMetatiles() { clearBorderMetatiles(); scene_selected_border_metatiles = new QGraphicsScene; - selected_border_metatiles_item = new BorderMetatilesPixmapItem(map, this->metatile_selector_item); + selected_border_metatiles_item = new BorderMetatilesPixmapItem(this->layout, this->metatile_selector_item); selected_border_metatiles_item->draw(); scene_selected_border_metatiles->addItem(selected_border_metatiles_item); @@ -1650,14 +1722,14 @@ void Editor::displayCurrentMetatilesSelection() { clearCurrentMetatilesSelection(); scene_current_metatile_selection = new QGraphicsScene; - current_metatile_selection_item = new CurrentSelectedMetatilesPixmapItem(map, this->metatile_selector_item); + current_metatile_selection_item = new CurrentSelectedMetatilesPixmapItem(this->layout, this->metatile_selector_item); current_metatile_selection_item->draw(); scene_current_metatile_selection->addItem(current_metatile_selection_item); } void Editor::redrawCurrentMetatilesSelection() { if (current_metatile_selection_item) { - current_metatile_selection_item->setMap(map); + current_metatile_selection_item->setLayout(this->layout); current_metatile_selection_item->draw(); emit currentMetatilesSelectionChanged(); } @@ -1783,8 +1855,8 @@ void Editor::maskNonVisibleConnectionTiles() { mask.addRect( -BORDER_DISTANCE * 16, -BORDER_DISTANCE * 16, - (map->getWidth() + BORDER_DISTANCE * 2) * 16, - (map->getHeight() + BORDER_DISTANCE * 2) * 16 + (layout->getWidth() + BORDER_DISTANCE * 2) * 16, + (layout->getHeight() + BORDER_DISTANCE * 2) * 16 ); // Mask the tiles with the current theme's background color. @@ -1807,13 +1879,13 @@ void Editor::clearMapBorder() { void Editor::displayMapBorder() { clearMapBorder(); - int borderWidth = map->getBorderWidth(); - int borderHeight = map->getBorderHeight(); + int borderWidth = this->layout->getBorderWidth(); + int borderHeight = this->layout->getBorderHeight(); int borderHorzDist = getBorderDrawDistance(borderWidth); int borderVertDist = getBorderDrawDistance(borderHeight); - QPixmap pixmap = map->renderBorder(); - for (int y = -borderVertDist; y < map->getHeight() + borderVertDist; y += borderHeight) - for (int x = -borderHorzDist; x < map->getWidth() + borderHorzDist; x += borderWidth) { + QPixmap pixmap = this->layout->renderBorder(); + for (int y = -borderVertDist; y < this->layout->getHeight() + borderVertDist; y += borderHeight) + for (int x = -borderHorzDist; x < this->layout->getWidth() + borderHorzDist; x += borderWidth) { QGraphicsPixmapItem *item = new QGraphicsPixmapItem(pixmap); item->setX(x * 16); item->setY(y * 16); @@ -1824,7 +1896,7 @@ void Editor::displayMapBorder() { } void Editor::updateMapBorder() { - QPixmap pixmap = this->map->renderBorder(true); + QPixmap pixmap = this->layout->renderBorder(true); for (auto item : this->borderItems) { item->setPixmap(pixmap); } @@ -1875,8 +1947,8 @@ void Editor::displayMapGrid() { // elements of the scripting API, so they're painted manually in MapView::drawForeground. this->mapGrid = new QGraphicsItemGroup(); - const int pixelMapWidth = map->getWidth() * 16; - const int pixelMapHeight = map->getHeight() * 16; + 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. @@ -1913,21 +1985,21 @@ void Editor::updateMapGrid() { void Editor::updatePrimaryTileset(QString tilesetLabel, bool forceLoad) { - if (map->layout->tileset_primary_label != tilesetLabel || forceLoad) + if (this->layout->tileset_primary_label != tilesetLabel || forceLoad) { - map->layout->tileset_primary_label = tilesetLabel; - map->layout->tileset_primary = project->getTileset(tilesetLabel, forceLoad); - map->clearBorderCache(); + this->layout->tileset_primary_label = tilesetLabel; + this->layout->tileset_primary = project->getTileset(tilesetLabel, forceLoad); + layout->clearBorderCache(); } } void Editor::updateSecondaryTileset(QString tilesetLabel, bool forceLoad) { - if (map->layout->tileset_secondary_label != tilesetLabel || forceLoad) + if (this->layout->tileset_secondary_label != tilesetLabel || forceLoad) { - map->layout->tileset_secondary_label = tilesetLabel; - map->layout->tileset_secondary = project->getTileset(tilesetLabel, forceLoad); - map->clearBorderCache(); + this->layout->tileset_secondary_label = tilesetLabel; + this->layout->tileset_secondary = project->getTileset(tilesetLabel, forceLoad); + layout->clearBorderCache(); } } @@ -1973,7 +2045,7 @@ void Editor::updateCustomMapHeaderValues(QTableWidget *table) Tileset* Editor::getCurrentMapPrimaryTileset() { - QString tilesetLabel = map->layout->tileset_primary_label; + QString tilesetLabel = this->layout->tileset_primary_label; return project->getTileset(tilesetLabel); } @@ -2008,12 +2080,12 @@ void Editor::redrawObject(DraggablePixmapItem *item) { void Editor::updateWarpEventWarning(Event *event) { if (porymapConfig.warpBehaviorWarningDisabled) return; - if (!project || !map || !event || event->getEventType() != Event::Type::Warp) + if (!project || !map || !map->layout || !event || event->getEventType() != Event::Type::Warp) return; Block block; Metatile * metatile = nullptr; WarpEvent * warpEvent = static_cast(event); - if (map->getBlock(warpEvent->getX(), warpEvent->getY(), &block)) { + if (map->layout->getBlock(warpEvent->getX(), warpEvent->getY(), &block)) { metatile = Tileset::getMetatile(block.metatileId(), map->layout->tileset_primary, map->layout->tileset_secondary); } // metatile may be null if the warp is in the map border. Display the warning in this case @@ -2093,7 +2165,7 @@ void Editor::selectedEventIndexChanged(int index, Event::Group eventGroup) { } void Editor::duplicateSelectedEvents() { - if (!selected_events || !selected_events->length() || !map || !current_view || map_item->paintingMode != MapPixmapItem::PaintMode::EventObjects) + if (!selected_events || !selected_events->length() || !map || !current_view || this->getEditingLayout()) return; QList selectedEvents; @@ -2150,6 +2222,50 @@ bool Editor::eventLimitReached(Event::Type event_type) { return false; } +void Editor::deleteSelectedEvents() { + if (!this->selected_events || this->selected_events->length() == 0 || !this->map || this->editMode != EditMode::Events) + return; + + DraggablePixmapItem *nextSelectedEvent = nullptr; + QList selectedEvents; + int numDeleted = 0; + for (DraggablePixmapItem *item : *this->selected_events) { + Event::Group event_group = item->event->getEventGroup(); + if (event_group != Event::Group::Heal) { + numDeleted++; + item->event->setPixmapItem(item); + selectedEvents.append(item->event); + } + else { // don't allow deletion of heal locations + logWarn(QString("Cannot delete event of type '%1'").arg(Event::eventTypeToString(item->event->getEventType()))); + } + } + if (numDeleted) { + // Get the index for the event that should be selected after this event has been deleted. + // Select event at next smallest index when deleting a single event. + // If deleting multiple events, just let editor work out next selected. + if (numDeleted == 1) { + Event::Group event_group = selectedEvents[0]->getEventGroup(); + int index = this->map->events.value(event_group).indexOf(selectedEvents[0]); + if (index != this->map->events.value(event_group).size() - 1) + index++; + else + index--; + Event *event = nullptr; + if (index >= 0) + event = this->map->events.value(event_group).at(index); + for (QGraphicsItem *child : this->events_group->childItems()) { + DraggablePixmapItem *event_item = static_cast(child); + if (event_item->event == event) { + nextSelectedEvent = event_item; + break; + } + } + } + this->map->editHistory.push(new EventDelete(this, this->map, selectedEvents, nextSelectedEvent ? nextSelectedEvent->event : nullptr)); + } +} + void Editor::openMapScripts() const { openInTextEditor(map->getScriptsFilePath()); } @@ -2232,11 +2348,11 @@ bool Editor::startDetachedProcess(const QString &command, const QString &working // is clicking on the background instead of an event. void Editor::objectsView_onMousePress(QMouseEvent *event) { // make sure we are in object editing mode - if (map_item && map_item->paintingMode != MapPixmapItem::PaintMode::EventObjects) { + if (map_item && this->editMode != EditMode::Events) { return; } - if (this->obj_edit_mode == "paint" && event->buttons() & Qt::RightButton) { - this->obj_edit_mode = "select"; + if (this->objectEditAction == EditAction::Paint && event->buttons() & Qt::RightButton) { + this->objectEditAction = EditAction::Select; this->settings->mapCursor = QCursor(); this->cursorMapTileRect->setSingleTileMode(); this->ui->toolButton_Paint->setChecked(false); diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index fadf3d60a..d9e65eb30 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -18,7 +18,10 @@ #include "prefab.h" #include "montabwidget.h" #include "imageexport.h" +#include "maplistmodels.h" +#include "eventfilters.h" #include "newmapconnectiondialog.h" +#include "config.h" #include "filedialog.h" #include @@ -85,9 +88,6 @@ MainWindow::~MainWindow() { delete label_MapRulerStatus; delete editor; - delete mapListProxyModel; - delete mapGroupItemsList; - delete mapListModel; delete ui; } @@ -119,7 +119,7 @@ void MainWindow::initWindow() { this->initExtraSignals(); this->initEditor(); this->initMiscHeapObjects(); - this->initMapSortOrder(); + this->initMapList(); this->initShortcuts(); this->restoreWindowState(); @@ -153,11 +153,6 @@ void MainWindow::initExtraShortcuts() { shortcutDuplicate_Events->setObjectName("shortcutDuplicate_Events"); shortcutDuplicate_Events->setWhatsThis("Duplicate Selected Event(s)"); - auto *shortcutDelete_Object = new Shortcut( - {QKeySequence("Del"), QKeySequence("Backspace")}, this, SLOT(onDeleteKeyPressed())); - shortcutDelete_Object->setObjectName("shortcutDelete_Object"); - shortcutDelete_Object->setWhatsThis("Delete Selected Item(s)"); - auto *shortcutToggle_Border = new Shortcut(QKeySequence(), ui->checkBox_ToggleBorder, SLOT(toggle())); shortcutToggle_Border->setObjectName("shortcutToggle_Border"); shortcutToggle_Border->setWhatsThis("Toggle Border"); @@ -166,11 +161,15 @@ void MainWindow::initExtraShortcuts() { shortcutToggle_Smart_Paths->setObjectName("shortcutToggle_Smart_Paths"); shortcutToggle_Smart_Paths->setWhatsThis("Toggle Smart Paths"); - auto *shortcutExpand_All = new Shortcut(QKeySequence(), this, SLOT(on_toolButton_ExpandAll_clicked())); + auto *shortcutHide_Show = new Shortcut(QKeySequence(), this, SLOT(mapListShortcut_ToggleEmptyFolders())); + shortcutHide_Show->setObjectName("shortcutHide_Show"); + shortcutHide_Show->setWhatsThis("Map List: Hide/Show Empty Folders"); + + auto *shortcutExpand_All = new Shortcut(QKeySequence(), this, SLOT(mapListShortcut_ExpandAll())); shortcutExpand_All->setObjectName("shortcutExpand_All"); shortcutExpand_All->setWhatsThis("Map List: Expand all folders"); - auto *shortcutCollapse_All = new Shortcut(QKeySequence(), this, SLOT(on_toolButton_CollapseAll_clicked())); + auto *shortcutCollapse_All = new Shortcut(QKeySequence(), this, SLOT(mapListShortcut_CollapseAll())); shortcutCollapse_All->setObjectName("shortcutCollapse_All"); shortcutCollapse_All->setWhatsThis("Map List: Collapse all folders"); @@ -212,31 +211,33 @@ void MainWindow::applyUserShortcuts() { shortcut->setKeys(shortcutsConfig.userShortcuts(shortcut)); } -static const QMap mainTabNames = { - {MainTab::Map, "Map"}, - {MainTab::Events, "Events"}, - {MainTab::Header, "Header"}, - {MainTab::Connections, "Connections"}, - {MainTab::WildPokemon, "Wild Pokemon"}, -}; - void MainWindow::initCustomUI() { + static const QMap mainTabNames = { + {MainTab::Map, "Map"}, + {MainTab::Events, "Events"}, + {MainTab::Header, "Header"}, + {MainTab::Connections, "Connections"}, + {MainTab::WildPokemon, "Wild Pokemon"}, + }; + + static const QMap mainTabIcons = { + {MainTab::Map, QIcon(QStringLiteral(":/icons/minimap.ico"))}, + {MainTab::Events, QIcon(QStringLiteral(":/icons/viewsprites.ico"))}, + {MainTab::Header, QIcon(QStringLiteral(":/icons/application_form_edit.ico"))}, + {MainTab::Connections, QIcon(QStringLiteral(":/icons/connections.ico"))}, + {MainTab::WildPokemon, QIcon(QStringLiteral(":/icons/tall_grass.ico"))}, + }; + // Set up the tab bar while (ui->mainTabBar->count()) ui->mainTabBar->removeTab(0); - for (int i = 0; i < mainTabNames.count(); i++) + for (int i = 0; i < mainTabNames.count(); i++) { ui->mainTabBar->addTab(mainTabNames.value(i)); - - ui->mainTabBar->setTabIcon(MainTab::Map, QIcon(QStringLiteral(":/icons/map.ico"))); - ui->mainTabBar->setTabIcon(MainTab::WildPokemon, QIcon(QStringLiteral(":/icons/tall_grass.ico"))); + ui->mainTabBar->setTabIcon(i, mainTabIcons.value(i)); + } } void MainWindow::initExtraSignals() { - // Right-clicking on items in the map list tree view brings up a context menu. - ui->mapList->setContextMenuPolicy(Qt::CustomContextMenu); - connect(ui->mapList, &QTreeView::customContextMenuRequested, - this, &MainWindow::onOpenMapListContextMenu); - // other signals connect(ui->newEventToolButton, &NewEventToolButton::newEventAdded, this, &MainWindow::addNewEvent); connect(ui->tabWidget_EventType, &QTabWidget::currentChanged, this, &MainWindow::eventTabChanged); @@ -313,6 +314,7 @@ 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_deleteObject, &QAbstractButton::clicked, this->editor, &Editor::deleteSelectedEvents); this->loadUserSettings(); @@ -340,7 +342,7 @@ void MainWindow::initEditor() { ui->menuEdit->addAction(showHistory); // Toggle an asterisk in the window title when the undo state is changed - connect(&editor->editGroup, &QUndoGroup::cleanChanged, this, &MainWindow::showWindowTitle); + connect(&editor->editGroup, &QUndoGroup::indexChanged, this, &MainWindow::updateWindowTitle); // selecting objects from the spinners connect(this->ui->spinner_ObjectID, QOverload::of(&QSpinBox::valueChanged), [this](int value) { @@ -361,101 +363,119 @@ void MainWindow::initEditor() { } void MainWindow::initMiscHeapObjects() { - mapIcon = QIcon(QStringLiteral(":/icons/map.ico")); + ui->tabWidget_EventType->clear(); +} - mapListModel = new QStandardItemModel; - mapGroupItemsList = new QList; - mapListProxyModel = new FilterChildrenProxyModel; +void MainWindow::initMapList() { + ui->mapListContainer->setCurrentIndex(porymapConfig.mapListTab); - mapListProxyModel->setSourceModel(mapListModel); - ui->mapList->setModel(mapListProxyModel); + WheelFilter *wheelFilter = new WheelFilter(this); + ui->mainTabBar->installEventFilter(wheelFilter); + ui->mapListContainer->tabBar()->installEventFilter(wheelFilter); - ui->tabWidget_EventType->clear(); -} + // Create buttons for adding and removing items from the mapList + QFrame *buttonFrame = new QFrame(this->ui->mapListContainer); + buttonFrame->setFrameShape(QFrame::NoFrame); + + QHBoxLayout *layout = new QHBoxLayout(buttonFrame); + layout->setSpacing(0); + layout->setContentsMargins(0, 0, 0, 0); + + // Create add map/layout button + QPushButton *buttonAdd = new QPushButton(QIcon(":/icons/add.ico"), ""); + connect(buttonAdd, &QPushButton::clicked, this, &MainWindow::on_action_NewMap_triggered); + layout->addWidget(buttonAdd); + + /* TODO: Remove button disabled, no current support for deleting maps/layouts + // Create remove map/layout button + QPushButton *buttonRemove = new QPushButton(QIcon(":/icons/delete.ico"), ""); + connect(buttonRemove, &QPushButton::clicked, this, &MainWindow::deleteCurrentMapOrLayout); + layout->addWidget(buttonRemove); + */ + + ui->mapListContainer->setCornerWidget(buttonFrame, Qt::TopRightCorner); -void MainWindow::initMapSortOrder() { - QMenu *mapSortOrderMenu = new QMenu(this); - QActionGroup *mapSortOrderActionGroup = new QActionGroup(ui->toolButton_MapSortOrder); + // Connect tool bars to lists + ui->mapListToolBar_Groups->setList(ui->mapList); + ui->mapListToolBar_Areas->setList(ui->areaList); + ui->mapListToolBar_Layouts->setList(ui->layoutList); - mapSortOrderMenu->addAction(ui->actionSort_by_Group); - mapSortOrderMenu->addAction(ui->actionSort_by_Area); - mapSortOrderMenu->addAction(ui->actionSort_by_Layout); - ui->toolButton_MapSortOrder->setMenu(mapSortOrderMenu); + // Left-clicking on items in the map list opens the corresponding map/layout. + connect(ui->mapList, &QAbstractItemView::activated, this, &MainWindow::openMapListItem); + connect(ui->areaList, &QAbstractItemView::activated, this, &MainWindow::openMapListItem); + connect(ui->layoutList, &QAbstractItemView::activated, this, &MainWindow::openMapListItem); - mapSortOrderActionGroup->addAction(ui->actionSort_by_Group); - mapSortOrderActionGroup->addAction(ui->actionSort_by_Area); - mapSortOrderActionGroup->addAction(ui->actionSort_by_Layout); + // Right-clicking on items in the map list brings up a context menu. + connect(ui->mapList, &QTreeView::customContextMenuRequested, this, &MainWindow::onOpenMapListContextMenu); + connect(ui->areaList, &QTreeView::customContextMenuRequested, this, &MainWindow::onOpenMapListContextMenu); + connect(ui->layoutList, &QTreeView::customContextMenuRequested, this, &MainWindow::onOpenMapListContextMenu); - connect(mapSortOrderActionGroup, &QActionGroup::triggered, this, &MainWindow::mapSortOrder_changed); + // Only the groups list allows reorganizing folder contents, editing folder names, etc. + ui->mapListToolBar_Areas->setEditsAllowedButtonVisible(false); + ui->mapListToolBar_Layouts->setEditsAllowedButtonVisible(false); - QAction* sortOrder = ui->toolButton_MapSortOrder->menu()->actions()[porymapConfig.mapSortOrder]; - ui->toolButton_MapSortOrder->setIcon(sortOrder->icon()); - sortOrder->setChecked(true); + // When map list search filter is cleared we want the current map/layout in the editor to be visible in the list. + connect(ui->mapListToolBar_Groups, &MapListToolBar::filterCleared, this, &MainWindow::scrollMapListToCurrentMap); + connect(ui->mapListToolBar_Areas, &MapListToolBar::filterCleared, this, &MainWindow::scrollMapListToCurrentMap); + connect(ui->mapListToolBar_Layouts, &MapListToolBar::filterCleared, this, &MainWindow::scrollMapListToCurrentLayout); + + // Connect the "add folder" button in each of the map lists + connect(ui->mapListToolBar_Groups, &MapListToolBar::addFolderClicked, this, &MainWindow::mapListAddGroup); + connect(ui->mapListToolBar_Areas, &MapListToolBar::addFolderClicked, this, &MainWindow::mapListAddArea); + connect(ui->mapListToolBar_Layouts, &MapListToolBar::addFolderClicked, this, &MainWindow::mapListAddLayout); + + connect(ui->mapListContainer, &QTabWidget::currentChanged, this, &MainWindow::saveMapListTab); } -void MainWindow::showWindowTitle() { +void MainWindow::updateWindowTitle() { + if (!editor || !editor->project) { + setWindowTitle(QCoreApplication::applicationName()); + return; + } + + const QString projectName = editor->project->getProjectTitle(); + if (!editor->layout) { + setWindowTitle(projectName); + return; + } + if (editor->map) { setWindowTitle(QString("%1%2 - %3") .arg(editor->map->hasUnsavedChanges() ? "* " : "") .arg(editor->map->name) - .arg(editor->project->getProjectTitle()) + .arg(projectName) + ); + } else { + setWindowTitle(QString("%1%2 - %3") + .arg(editor->layout->hasUnsavedChanges() ? "* " : "") + .arg(editor->layout->name) + .arg(projectName) ); } + + // For some reason (perhaps on Qt < 6?) we had to clear the icon first here or mainTabBar wouldn't display correctly. + ui->mainTabBar->setTabIcon(MainTab::Map, QIcon()); + + QPixmap pixmap = editor->layout->pixmap; + if (!pixmap.isNull()) { + ui->mainTabBar->setTabIcon(MainTab::Map, QIcon(pixmap)); + } else { + ui->mainTabBar->setTabIcon(MainTab::Map, QIcon(QStringLiteral(":/icons/map.ico"))); + } } void MainWindow::markMapEdited() { - if (editor) markMapEdited(editor->map); + if (editor) markSpecificMapEdited(editor->map); } -void MainWindow::markMapEdited(Map* map) { +void MainWindow::markSpecificMapEdited(Map* map) { if (!map) return; map->hasUnsavedDataChanges = true; - updateMapListIcon(map->name); if (editor && editor->map == map) - showWindowTitle(); -} - -void MainWindow::mapSortOrder_changed(QAction *action) -{ - QList items = ui->toolButton_MapSortOrder->menu()->actions(); - int i = 0; - for (; i < items.count(); i++) - { - if (items[i] == action) - { - break; - } - } - - if (i != porymapConfig.mapSortOrder) - { - ui->toolButton_MapSortOrder->setIcon(action->icon()); - porymapConfig.mapSortOrder = static_cast(i); - if (isProjectOpen()) - { - sortMapList(); - applyMapListFilter(ui->lineEdit_filterBox->text()); - } - } -} - -void MainWindow::on_lineEdit_filterBox_textChanged(const QString &arg1) -{ - this->applyMapListFilter(arg1); -} - -void MainWindow::applyMapListFilter(QString filterText) -{ - mapListProxyModel->setFilterRegularExpression(QRegularExpression(filterText, QRegularExpression::CaseInsensitiveOption)); - if (filterText.isEmpty()) { - ui->mapList->collapseAll(); - } else { - ui->mapList->expandToDepth(0); - } - ui->mapList->setExpanded(mapListProxyModel->mapFromSource(mapListIndexes.value(editor->map->name)), true); - ui->mapList->scrollTo(mapListProxyModel->mapFromSource(mapListIndexes.value(editor->map->name)), QAbstractItemView::PositionAtCenter); + updateWindowTitle(); + updateMapList(); } void MainWindow::loadUserSettings() { @@ -573,16 +593,11 @@ bool MainWindow::openProject(QString dir, bool initial) { Scripting::init(this); // Create the project - auto project = new Project(this); + auto project = new Project(editor); project->set_root(dir); - QObject::connect(project, &Project::reloadProject, this, &MainWindow::on_action_Reload_Project_triggered); - QObject::connect(project, &Project::mapCacheCleared, this, &MainWindow::onMapCacheCleared); - QObject::connect(project, &Project::mapLoaded, this, &MainWindow::onMapLoaded); - QObject::connect(project, &Project::uncheckMonitorFilesAction, [this]() { - porymapConfig.monitorFiles = false; - if (this->preferenceEditor) - this->preferenceEditor->updateFields(); - }); + connect(project, &Project::fileChanged, this, &MainWindow::showFileWatcherWarning); + connect(project, &Project::mapLoaded, this, &MainWindow::onMapLoaded); + connect(project, &Project::mapSectionIdNamesChanged, this, &MainWindow::refreshLocationsComboBox); this->editor->setProject(project); // Make sure project looks reasonable before attempting to load it @@ -603,7 +618,7 @@ bool MainWindow::openProject(QString dir, bool initial) { // Only create the config files once the project has opened successfully in case the user selected an invalid directory this->editor->project->saveConfig(); - showWindowTitle(); + updateWindowTitle(); this->statusBar()->showMessage(QString("Opened %1").arg(projectString)); porymapConfig.projectManuallyClosed = false; @@ -614,7 +629,7 @@ bool MainWindow::openProject(QString dir, bool initial) { editor->metatile_selector_item, ui->scrollAreaWidgetContents_Prefabs, ui->label_prefabHelp, - editor->map); + editor->layout); Scripting::cb_ProjectOpened(dir); setWindowDisabled(false); return true; @@ -662,22 +677,28 @@ bool MainWindow::isProjectOpen() { } bool MainWindow::setInitialMap() { - QStringList names; - if (editor && editor->project) - names = editor->project->mapNames; - - // Try to set most recently-opened map, if it's still in the list. - QString recentMap = userConfig.recentMap; - if (!recentMap.isEmpty() && names.contains(recentMap) && setMap(recentMap, true)) - return true; + const QString recent = userConfig.recentMapOrLayout; + if (editor->project->mapNames.contains(recent)) { + // User recently had a map open that still exists. + if (setMap(recent)) + return true; + } else if (editor->project->mapLayoutsTable.contains(recent)) { + // User recently had a layout open that still exists. + if (setLayout(recent)) + return true; + } - // Failing that, try loading maps in the map list sequentially. - for (auto name : names) { - if (name != recentMap && setMap(name, true)) + // Failed to open recent map/layout, or no recent map/layout. Try opening maps then layouts sequentially. + for (const auto &name : editor->project->mapNames) { + if (name != recent && setMap(name)) + return true; + } + for (const auto &id : editor->project->mapLayoutsTable) { + if (id != recent && setLayout(id)) return true; } - logError("Failed to load any maps."); + logError("Failed to load any maps or layouts."); return false; } @@ -729,13 +750,53 @@ void MainWindow::openSubWindow(QWidget * window) { } } +void MainWindow::showFileWatcherWarning(QString filepath) { + if (!porymapConfig.monitorFiles || !isProjectOpen()) + return; + + Project *project = this->editor->project; + if (project->modifiedFileTimestamps.contains(filepath)) { + if (QDateTime::currentMSecsSinceEpoch() < project->modifiedFileTimestamps[filepath]) { + return; + } + project->modifiedFileTimestamps.remove(filepath); + } + + static bool showing = false; + if (showing) return; + + QMessageBox notice(this); + notice.setText("File Changed"); + notice.setInformativeText(QString("The file %1 has changed on disk. Would you like to reload the project?") + .arg(filepath.remove(project->root + "/"))); + notice.setStandardButtons(QMessageBox::No | QMessageBox::Yes); + notice.setDefaultButton(QMessageBox::No); + notice.setIcon(QMessageBox::Question); + + QCheckBox showAgainCheck("Do not ask again."); + notice.setCheckBox(&showAgainCheck); + + showing = true; + int choice = notice.exec(); + if (choice == QMessageBox::Yes) { + on_action_Reload_Project_triggered(); + } else if (choice == QMessageBox::No) { + if (showAgainCheck.isChecked()) { + porymapConfig.monitorFiles = false; + if (this->preferenceEditor) + this->preferenceEditor->updateFields(); + } + } + showing = false; +} + QString MainWindow::getExistingDirectory(QString dir) { return FileDialog::getExistingDirectory(this, "Open Directory", dir, QFileDialog::ShowDirsOnly); } void MainWindow::on_action_Open_Project_triggered() { - QString dir = getExistingDirectory(!userConfig.recentMap.isEmpty() ? userConfig.recentMap : "."); + QString dir = getExistingDirectory(!projectConfig.projectDir.isEmpty() ? userConfig.projectDir : "."); if (!dir.isEmpty()) openProject(dir); } @@ -758,9 +819,14 @@ void MainWindow::on_action_Close_Project_triggered() { porymapConfig.projectManuallyClosed = true; } +void MainWindow::unsetMap() { + this->editor->unsetMap(); + setLayoutOnlyMode(true); +} + // setMap, but with a visible error message in case of failure. // Use when the user is specifically requesting a map to open. -bool MainWindow::userSetMap(QString map_name, bool scrollTreeView) { +bool MainWindow::userSetMap(QString map_name) { if (editor->map && editor->map->name == map_name) return true; // Already set @@ -771,7 +837,7 @@ bool MainWindow::userSetMap(QString map_name, bool scrollTreeView) { return false; } - if (!setMap(map_name, scrollTreeView)) { + if (!setMap(map_name)) { QMessageBox msgBox(this); QString errorMsg = QString("There was an error opening map %1. Please see %2 for full error details.\n\n%3") .arg(map_name) @@ -783,60 +849,105 @@ bool MainWindow::userSetMap(QString map_name, bool scrollTreeView) { return true; } -bool MainWindow::setMap(QString map_name, bool scrollTreeView) { - logInfo(QString("Setting map to '%1'").arg(map_name)); +bool MainWindow::setMap(QString map_name) { if (map_name.isEmpty() || map_name == DYNAMIC_MAP_NAME) { + logInfo(QString("Cannot set map to '%1'").arg(map_name)); return false; } + logInfo(QString("Setting map to '%1'").arg(map_name)); + if (!editor || !editor->setMap(map_name)) { logWarn(QString("Failed to set map to '%1'").arg(map_name)); return false; } - if (editor->map != nullptr && !editor->map->name.isNull()) { - ui->mapList->setExpanded(mapListProxyModel->mapFromSource(mapListIndexes.value(editor->map->name)), false); - } - + setLayoutOnlyMode(false); this->lastSelectedEvent.clear(); + refreshMapScene(); displayMapProperties(); + updateWindowTitle(); + updateMapList(); + resetMapListFilters(); + + connect(editor->map, &Map::modified, this, &MainWindow::markMapEdited, Qt::UniqueConnection); + + connect(editor->layout, &Layout::layoutChanged, this, &MainWindow::onLayoutChanged, Qt::UniqueConnection); + connect(editor->layout, &Layout::needsRedrawing, this, &MainWindow::redrawMapScene, Qt::UniqueConnection); + + userConfig.recentMapOrLayout = map_name; + + Scripting::cb_MapOpened(map_name); + prefab.updatePrefabUi(editor->layout); + updateTilesetEditor(); + return true; +} + +// These parts of the UI only make sense when editing maps. +// When editing in layout-only mode they are disabled. +void MainWindow::setLayoutOnlyMode(bool layoutOnly) { + bool mapEditingEnabled = !layoutOnly; + this->ui->mainTabBar->setTabEnabled(MainTab::Events, mapEditingEnabled); + this->ui->mainTabBar->setTabEnabled(MainTab::Header, mapEditingEnabled); + this->ui->mainTabBar->setTabEnabled(MainTab::Connections, mapEditingEnabled); + this->ui->mainTabBar->setTabEnabled(MainTab::WildPokemon, mapEditingEnabled); - if (scrollTreeView) { - // Make sure we clear the filter first so we actually have a scroll target - mapListProxyModel->setFilterRegularExpression(QString()); - ui->mapList->setCurrentIndex(mapListProxyModel->mapFromSource(mapListIndexes.value(map_name))); - ui->mapList->scrollTo(ui->mapList->currentIndex(), QAbstractItemView::PositionAtCenter); + this->ui->comboBox_LayoutSelector->setEnabled(mapEditingEnabled); +} + +// setLayout, but with a visible error message in case of failure. +// Use when the user is specifically requesting a layout to open. +bool MainWindow::userSetLayout(QString layoutId) { + if (!setLayout(layoutId)) { + QMessageBox msgBox(this); + QString errorMsg = QString("There was an error opening layout %1. Please see %2 for full error details.\n\n%3") + .arg(layoutId) + .arg(getLogPath()) + .arg(getMostRecentError()); + msgBox.critical(nullptr, "Error Opening Layout", errorMsg); + return false; } + return true; +} - ui->mapList->setExpanded(mapListProxyModel->mapFromSource(mapListIndexes.value(map_name)), true); +bool MainWindow::setLayout(QString layoutId) { + if (this->editor->map) + logInfo("Switching to a layout-only editing mode. Disabling map-related edits."); - showWindowTitle(); + unsetMap(); - connect(editor->map, &Map::mapNeedsRedrawing, this, &MainWindow::onMapNeedsRedrawing); + // Prefer logging the name of the layout as displayed in the map list. + const QString layoutName = this->editor->project ? this->editor->project->layoutIdsToNames.value(layoutId, layoutId) : layoutId; + logInfo(QString("Setting layout to '%1'").arg(layoutName)); - // Swap the "currently-open" icon from the old map to the new map - if (!userConfig.recentMap.isEmpty() && userConfig.recentMap != map_name) - updateMapListIcon(userConfig.recentMap); - userConfig.recentMap = map_name; - updateMapListIcon(userConfig.recentMap); + if (!this->editor->setLayout(layoutId)) { + return false; + } + + layoutTreeModel->setLayout(layoutId); + + refreshMapScene(); + updateWindowTitle(); + updateMapList(); + resetMapListFilters(); + + connect(editor->layout, &Layout::needsRedrawing, this, &MainWindow::redrawMapScene, Qt::UniqueConnection); - Scripting::cb_MapOpened(map_name); - prefab.updatePrefabUi(editor->map); updateTilesetEditor(); + + userConfig.recentMapOrLayout = layoutId; + return true; } -void MainWindow::redrawMapScene() -{ - if (!editor->displayMap()) - return; - - this->refreshMapScene(); +void MainWindow::redrawMapScene() { + editor->displayMap(); + editor->displayLayout(); + refreshMapScene(); } -void MainWindow::refreshMapScene() -{ +void MainWindow::refreshMapScene() { on_mainTabBar_tabBarClicked(ui->mainTabBar->currentIndex()); ui->graphicsView_Map->setScene(editor->scene); @@ -872,7 +983,7 @@ void MainWindow::openWarpMap(QString map_name, int event_id, Event::Group event_ } // Open the destination map. - if (!userSetMap(map_name, true)) + if (!userSetMap(map_name)) return; // Select the target event. @@ -946,6 +1057,16 @@ void MainWindow::displayMapProperties() { ui->tableWidget_CustomHeaderFields->blockSignals(false); } +void MainWindow::on_comboBox_LayoutSelector_currentTextChanged(const QString &text) { + if (editor && editor->project && editor->map) { + if (editor->project->mapLayouts.contains(text)) { + editor->map->setLayout(editor->project->loadLayout(text)); + setMap(editor->map->name); + markMapEdited(); + } + } +} + void MainWindow::on_comboBox_Song_currentTextChanged(const QString &song) { if (editor && editor->map) { @@ -1040,7 +1161,6 @@ bool MainWindow::setProjectUI() { // Block signals to the comboboxes while they are being modified const QSignalBlocker blocker1(ui->comboBox_Song); - const QSignalBlocker blocker2(ui->comboBox_Location); const QSignalBlocker blocker3(ui->comboBox_PrimaryTileset); const QSignalBlocker blocker4(ui->comboBox_SecondaryTileset); const QSignalBlocker blocker5(ui->comboBox_Weather); @@ -1048,12 +1168,11 @@ bool MainWindow::setProjectUI() { const QSignalBlocker blocker7(ui->comboBox_Type); const QSignalBlocker blocker8(ui->comboBox_DiveMap); const QSignalBlocker blocker9(ui->comboBox_EmergeMap); + const QSignalBlocker blocker10(ui->comboBox_LayoutSelector); // Set up project comboboxes ui->comboBox_Song->clear(); ui->comboBox_Song->addItems(project->songNames); - ui->comboBox_Location->clear(); - ui->comboBox_Location->addItems(project->mapSectionValueToName.values()); ui->comboBox_PrimaryTileset->clear(); ui->comboBox_PrimaryTileset->addItems(project->primaryTilesetLabels); ui->comboBox_SecondaryTileset->clear(); @@ -1064,6 +1183,8 @@ bool MainWindow::setProjectUI() { ui->comboBox_BattleScene->addItems(project->mapBattleScenes); ui->comboBox_Type->clear(); ui->comboBox_Type->addItems(project->mapTypes); + ui->comboBox_LayoutSelector->clear(); + ui->comboBox_LayoutSelector->addItems(project->mapLayoutsTable); ui->comboBox_DiveMap->clear(); ui->comboBox_DiveMap->addItems(project->mapNames); ui->comboBox_DiveMap->setClearButtonEnabled(true); @@ -1072,8 +1193,7 @@ bool MainWindow::setProjectUI() { ui->comboBox_EmergeMap->addItems(project->mapNames); ui->comboBox_EmergeMap->setClearButtonEnabled(true); ui->comboBox_EmergeMap->setFocusedScrollingEnabled(false); - - sortMapList(); + refreshLocationsComboBox(); // Show/hide parts of the UI that are dependent on the user's project settings @@ -1101,9 +1221,39 @@ bool MainWindow::setProjectUI() { ui->spinBox_SelectedElevation->setMaximum(Block::getMaxElevation()); ui->spinBox_SelectedCollision->setMaximum(Block::getMaxCollision()); + // map models + this->mapGroupModel = new MapGroupModel(editor->project); + this->groupListProxyModel = new FilterChildrenProxyModel(); + groupListProxyModel->setSourceModel(this->mapGroupModel); + ui->mapList->setModel(groupListProxyModel); + + this->ui->mapList->setItemDelegateForColumn(0, new GroupNameDelegate(this->editor->project, this)); + connect(this->mapGroupModel, &MapGroupModel::dragMoveCompleted, this->ui->mapList, &MapTree::removeSelected); + + this->mapAreaModel = new MapAreaModel(editor->project); + this->areaListProxyModel = new FilterChildrenProxyModel(); + areaListProxyModel->setSourceModel(this->mapAreaModel); + ui->areaList->setModel(areaListProxyModel); + + this->layoutTreeModel = new LayoutTreeModel(editor->project); + this->layoutListProxyModel = new FilterChildrenProxyModel(); + this->layoutListProxyModel->setSourceModel(this->layoutTreeModel); + ui->layoutList->setModel(layoutListProxyModel); + return true; } +void MainWindow::refreshLocationsComboBox() { + QStringList locations = this->editor->project->mapSectionIdNames; + locations.sort(); + + const QSignalBlocker b(ui->comboBox_Location); + ui->comboBox_Location->clear(); + ui->comboBox_Location->addItems(locations); + if (this->editor->map) + ui->comboBox_Location->setCurrentText(this->editor->map->location); +} + void MainWindow::clearProjectUI() { // Block signals to the comboboxes while they are being modified const QSignalBlocker blocker1(ui->comboBox_Song); @@ -1115,7 +1265,7 @@ void MainWindow::clearProjectUI() { const QSignalBlocker blocker7(ui->comboBox_Type); const QSignalBlocker blocker8(ui->comboBox_DiveMap); const QSignalBlocker blocker9(ui->comboBox_EmergeMap); - const QSignalBlocker blockerA(ui->lineEdit_filterBox); + const QSignalBlocker blockerA(ui->comboBox_LayoutSelector); ui->comboBox_Song->clear(); ui->comboBox_Location->clear(); @@ -1126,189 +1276,318 @@ void MainWindow::clearProjectUI() { ui->comboBox_Type->clear(); ui->comboBox_DiveMap->clear(); ui->comboBox_EmergeMap->clear(); - ui->lineEdit_filterBox->clear(); + ui->comboBox_LayoutSelector->clear(); - // Clear map list - mapListModel->clear(); - mapListIndexes.clear(); - mapGroupItemsList->clear(); + // Clear map models + delete this->mapGroupModel; + delete this->groupListProxyModel; + delete this->mapAreaModel; + delete this->areaListProxyModel; + delete this->layoutTreeModel; + delete this->layoutListProxyModel; + resetMapListFilters(); Event::clearIcons(); } -void MainWindow::sortMapList() { - Project *project = editor->project; +void MainWindow::scrollMapList(MapTree *list, QString itemName) { + if (!list || itemName.isEmpty()) + return; + auto model = static_cast(list->model()); + auto sourceModel = static_cast(model->sourceModel()); + QModelIndex sourceIndex = sourceModel->indexOf(itemName); + if (!sourceIndex.isValid()) + return; + QModelIndex index = model->mapFromSource(sourceIndex); + if (!index.isValid()) + return; - QIcon mapFolderIcon; - mapFolderIcon.addFile(QStringLiteral(":/icons/folder_closed_map.ico"), QSize(), QIcon::Normal, QIcon::Off); - mapFolderIcon.addFile(QStringLiteral(":/icons/folder_map.ico"), QSize(), QIcon::Normal, QIcon::On); - - QIcon folderIcon; - folderIcon.addFile(QStringLiteral(":/icons/folder_closed.ico"), QSize(), QIcon::Normal, QIcon::Off); - //folderIcon.addFile(QStringLiteral(":/icons/folder.ico"), QSize(), QIcon::Normal, QIcon::On); - - ui->mapList->setUpdatesEnabled(false); - mapListModel->clear(); - mapListIndexes.clear(); - mapGroupItemsList->clear(); - QStandardItem *root = mapListModel->invisibleRootItem(); - - switch (porymapConfig.mapSortOrder) - { - case MapSortOrder::Group: - for (int i = 0; i < project->groupNames.length(); i++) { - QString group_name = project->groupNames.value(i); - QStandardItem *group = new QStandardItem; - group->setText(group_name); - group->setIcon(mapFolderIcon); - group->setEditable(false); - group->setData(group_name, Qt::UserRole); - group->setData("map_group", MapListUserRoles::TypeRole); - group->setData(i, MapListUserRoles::GroupRole); - root->appendRow(group); - mapGroupItemsList->append(group); - QStringList names = project->groupedMapNames.value(i); - for (int j = 0; j < names.length(); j++) { - QString map_name = names.value(j); - QStandardItem *map = createMapItem(map_name, i, j); - group->appendRow(map); - mapListIndexes.insert(map_name, map->index()); - } - } - break; - case MapSortOrder::Area: - { - QMap mapsecToGroupNum; - int row = 0; - for (auto mapsec_value : project->mapSectionValueToName.keys()) { - QString mapsec_name = project->mapSectionValueToName.value(mapsec_value); - QStandardItem *mapsec = new QStandardItem; - mapsec->setText(mapsec_name); - mapsec->setIcon(folderIcon); - mapsec->setEditable(false); - mapsec->setData(mapsec_name, Qt::UserRole); - mapsec->setData("map_sec", MapListUserRoles::TypeRole); - root->appendRow(mapsec); - mapGroupItemsList->append(mapsec); - mapsecToGroupNum.insert(mapsec_name, row++); - } - for (int i = 0; i < project->groupNames.length(); i++) { - QStringList names = project->groupedMapNames.value(i); - for (int j = 0; j < names.length(); j++) { - QString map_name = names.value(j); - QStandardItem *map = createMapItem(map_name, i, j); - QString location = project->readMapLocation(map_name); - QStandardItem *mapsecItem = mapGroupItemsList->at(mapsecToGroupNum[location]); - mapsecItem->setIcon(mapFolderIcon); - mapsecItem->appendRow(map); - mapListIndexes.insert(map_name, map->index()); - } - } - break; - } - case MapSortOrder::Layout: - { - QMap layoutIndices; - for (int i = 0; i < project->mapLayoutsTable.length(); i++) { - QString layoutId = project->mapLayoutsTable.value(i); - MapLayout *layout = project->mapLayouts.value(layoutId); - QStandardItem *layoutItem = new QStandardItem; - layoutItem->setText(layout->name); - layoutItem->setIcon(folderIcon); - layoutItem->setEditable(false); - layoutItem->setData(layout->name, Qt::UserRole); - layoutItem->setData("map_layout", MapListUserRoles::TypeRole); - layoutItem->setData(layout->id, MapListUserRoles::TypeRole2); - layoutItem->setData(i, MapListUserRoles::GroupRole); - root->appendRow(layoutItem); - mapGroupItemsList->append(layoutItem); - layoutIndices[layoutId] = i; - } - for (int i = 0; i < project->groupNames.length(); i++) { - QStringList names = project->groupedMapNames.value(i); - for (int j = 0; j < names.length(); j++) { - QString map_name = names.value(j); - QStandardItem *map = createMapItem(map_name, i, j); - QString layoutId = project->readMapLayoutId(map_name); - QStandardItem *layoutItem = mapGroupItemsList->at(layoutIndices.value(layoutId)); - layoutItem->setIcon(mapFolderIcon); - layoutItem->appendRow(map); - mapListIndexes.insert(map_name, map->index()); - } - } - break; + list->setCurrentIndex(index); + list->setExpanded(index, true); + list->scrollTo(index, QAbstractItemView::PositionAtCenter); +} + +void MainWindow::scrollMapListToCurrentMap(MapTree *list) { + if (this->editor->map) { + scrollMapList(list, this->editor->map->name); + } +} + +void MainWindow::scrollMapListToCurrentLayout(MapTree *list) { + if (this->editor->layout) { + scrollMapList(list, this->editor->layout->id); + } +} + +void MainWindow::onOpenMapListContextMenu(const QPoint &point) { + // Get selected item from list + auto list = getCurrentMapList(); + if (!list) return; + auto model = static_cast(list->model()); + QModelIndex index = model->mapToSource(list->indexAt(point)); + if (!index.isValid()) return; + auto sourceModel = static_cast(model->sourceModel()); + QStandardItem *selectedItem = sourceModel->itemFromIndex(index); + const QString itemType = selectedItem->data(MapListUserRoles::TypeRole).toString(); + const QString itemName = selectedItem->data(Qt::UserRole).toString(); + + QMenu menu(this); + QAction* addToFolderAction = nullptr; + QAction* deleteFolderAction = nullptr; + QAction* openItemAction = nullptr; + if (itemType == "map_name") { + // Right-clicking on a map. + openItemAction = menu.addAction("Open Map"); + //menu.addSeparator(); + //connect(menu.addAction("Delete Map"), &QAction::triggered, [this, index] { deleteMapListItem(index); }); // TODO: No support for deleting maps + } else if (itemType == "map_group") { + // Right-clicking on a map group folder + addToFolderAction = menu.addAction("Add New Map to Group"); + menu.addSeparator(); + deleteFolderAction = menu.addAction("Delete Map Group"); + } else if (itemType == "map_section") { + // Right-clicking on a MAPSEC folder + addToFolderAction = menu.addAction("Add New Map to Area"); + menu.addSeparator(); + deleteFolderAction = menu.addAction("Delete Area"); + if (itemName == this->editor->project->getEmptyMapsecName()) + deleteFolderAction->setEnabled(false); // Disallow deleting the default name + } else if (itemType == "map_layout") { + // Right-clicking on a map layout + openItemAction = menu.addAction("Open Layout"); + addToFolderAction = menu.addAction("Add New Map with Layout"); + //menu.addSeparator(); + //deleteFolderAction = menu.addAction("Delete Layout"); // TODO: No support for deleting layouts + } + + if (addToFolderAction) { + connect(addToFolderAction, &QAction::triggered, [this, itemName] { + openNewMapPopupWindow(); + this->newMapPrompt->init(ui->mapListContainer->currentIndex(), itemName); + }); + } + if (deleteFolderAction) { + connect(deleteFolderAction, &QAction::triggered, [sourceModel, index] { + sourceModel->removeItemAt(index); + }); + if (selectedItem->hasChildren()){ + // TODO: No support for deleting maps, so you may only delete folders if they don't contain any maps. + deleteFolderAction->setEnabled(false); } } + if (openItemAction) { + connect(openItemAction, &QAction::triggered, [this, index] { openMapListItem(index); }); + } - ui->mapList->setUpdatesEnabled(true); - ui->mapList->repaint(); - updateMapList(); + if (menu.actions().length() != 0) + menu.exec(QCursor::pos()); } -QStandardItem* MainWindow::createMapItem(QString mapName, int groupNum, int inGroupNum) { - QStandardItem *map = new QStandardItem; - map->setText(QString("[%1.%2] ").arg(groupNum).arg(inGroupNum, 2, 10, QLatin1Char('0')) + mapName); - map->setIcon(mapIcon); - map->setEditable(false); - map->setData(mapName, Qt::UserRole); - map->setData("map_name", MapListUserRoles::TypeRole); - return map; -} +void MainWindow::mapListAddGroup() { + QDialog dialog(this, Qt::WindowTitleHint | Qt::WindowCloseButtonHint); + dialog.setWindowModality(Qt::ApplicationModal); + QDialogButtonBox newItemButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, &dialog); + connect(&newItemButtonBox, &QDialogButtonBox::rejected, &dialog, &QDialog::reject); -void MainWindow::onOpenMapListContextMenu(const QPoint &point) -{ - QModelIndex index = mapListProxyModel->mapToSource(ui->mapList->indexAt(point)); - if (!index.isValid()) { - return; - } + QLineEdit *newNameEdit = new QLineEdit(&dialog); + newNameEdit->setClearButtonEnabled(true); - QStandardItem *selectedItem = mapListModel->itemFromIndex(index); - QVariant itemType = selectedItem->data(MapListUserRoles::TypeRole); - if (!itemType.isValid()) { - return; - } + static const QRegularExpression re_validChars("[A-Za-z_]+[\\w]*"); + newNameEdit->setValidator(new QRegularExpressionValidator(re_validChars, newNameEdit)); - // Build custom context menu depending on which type of item was selected (map group, map name, etc.) - if (itemType == "map_group") { - int groupNum = selectedItem->data(MapListUserRoles::GroupRole).toInt(); - QMenu* menu = new QMenu(this); - QActionGroup* actions = new QActionGroup(menu); - actions->addAction(menu->addAction("Add New Map to Group"))->setData(groupNum); - connect(actions, &QActionGroup::triggered, this, &MainWindow::onAddNewMapToGroupClick); - menu->exec(QCursor::pos()); - } else if (itemType == "map_sec") { - QString secName = selectedItem->data(Qt::UserRole).toString(); - QMenu* menu = new QMenu(this); - QActionGroup* actions = new QActionGroup(menu); - actions->addAction(menu->addAction("Add New Map to Area"))->setData(secName); - connect(actions, &QActionGroup::triggered, this, &MainWindow::onAddNewMapToAreaClick); - menu->exec(QCursor::pos()); - } else if (itemType == "map_layout") { - QString layoutId = selectedItem->data(MapListUserRoles::TypeRole2).toString(); - QMenu* menu = new QMenu(this); - QActionGroup* actions = new QActionGroup(menu); - actions->addAction(menu->addAction("Add New Map with Layout"))->setData(layoutId); - connect(actions, &QActionGroup::triggered, this, &MainWindow::onAddNewMapToLayoutClick); - menu->exec(QCursor::pos()); + QLabel *errorMessageLabel = new QLabel(&dialog); + errorMessageLabel->setVisible(false); + errorMessageLabel->setStyleSheet("QLabel { background-color: rgba(255, 0, 0, 25%) }"); + + connect(&newItemButtonBox, &QDialogButtonBox::accepted, [&](){ + const QString mapGroupName = newNameEdit->text(); + if (this->editor->project->groupNames.contains(mapGroupName)) { + errorMessageLabel->setText(QString("A map group with the name '%1' already exists").arg(mapGroupName)); + errorMessageLabel->setVisible(true); + } else { + dialog.accept(); + } + }); + + QFormLayout form(&dialog); + + form.addRow("New Group Name", newNameEdit); + form.addRow("", errorMessageLabel); + form.addRow(&newItemButtonBox); + + if (dialog.exec() == QDialog::Accepted) { + QString newFieldName = newNameEdit->text(); + if (newFieldName.isEmpty()) return; + this->mapGroupModel->insertGroupItem(newFieldName); } } -void MainWindow::onAddNewMapToGroupClick(QAction* triggeredAction) -{ - openNewMapPopupWindow(); - this->newMapPrompt->init(MapSortOrder::Group, triggeredAction->data()); -} +// TODO: Pull this all out into a custom window. Connect that to an action in the main menu as well. +// (or, re-use the new map dialog with some tweaks) +// TODO: This needs to take the same default settings you would get for a new map (tilesets, dimensions, etc.) +// and initialize it with the same fill settings (default metatile/collision/elevation, default border) +void MainWindow::mapListAddLayout() { + if (!editor || !editor->project) return; -void MainWindow::onAddNewMapToAreaClick(QAction* triggeredAction) -{ - openNewMapPopupWindow(); - this->newMapPrompt->init(MapSortOrder::Area, triggeredAction->data()); + QDialog dialog(this, Qt::WindowTitleHint | Qt::WindowCloseButtonHint); + dialog.setWindowModality(Qt::ApplicationModal); + QDialogButtonBox newItemButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, &dialog); + connect(&newItemButtonBox, &QDialogButtonBox::rejected, &dialog, &QDialog::reject); + + QLineEdit *newNameEdit = new QLineEdit(&dialog); + newNameEdit->setClearButtonEnabled(true); + + static const QRegularExpression re_validChars("[A-Za-z_]+[\\w]*"); + newNameEdit->setValidator(new QRegularExpressionValidator(re_validChars, newNameEdit)); + + // TODO: Support arbitrary LAYOUT_ ID names (Note from GriffinR: This is already handled in an unopened PR) + QLabel *newId = new QLabel("LAYOUT_", &dialog); + connect(newNameEdit, &QLineEdit::textChanged, [&](QString text){ + newId->setText(Layout::layoutConstantFromName(text.remove("_Layout"))); + }); + + NoScrollComboBox *useExistingCombo = new NoScrollComboBox(&dialog); + useExistingCombo->addItems(this->editor->project->mapLayoutsTable); + useExistingCombo->setEnabled(false); + + QCheckBox *useExistingCheck = new QCheckBox(&dialog); + + QLabel *errorMessageLabel = new QLabel(&dialog); + errorMessageLabel->setVisible(false); + errorMessageLabel->setStyleSheet("QLabel { background-color: rgba(255, 0, 0, 25%) }"); + + QComboBox *primaryCombo = new QComboBox(&dialog); + primaryCombo->addItems(this->editor->project->primaryTilesetLabels); + QComboBox *secondaryCombo = new QComboBox(&dialog); + secondaryCombo->addItems(this->editor->project->secondaryTilesetLabels); + + QSpinBox *widthSpin = new QSpinBox(&dialog); + QSpinBox *heightSpin = new QSpinBox(&dialog); + + widthSpin->setMinimum(1); + heightSpin->setMinimum(1); + widthSpin->setMaximum(this->editor->project->getMaxMapWidth()); + heightSpin->setMaximum(this->editor->project->getMaxMapHeight()); + + connect(useExistingCheck, &QCheckBox::stateChanged, [&](int state){ + bool useExisting = (state == Qt::Checked); + useExistingCombo->setEnabled(useExisting); + primaryCombo->setEnabled(!useExisting); + secondaryCombo->setEnabled(!useExisting); + widthSpin->setEnabled(!useExisting); + heightSpin->setEnabled(!useExisting); + }); + + QFormLayout form(&dialog); + form.addRow("New Layout Name", newNameEdit); + form.addRow("New Layout ID", newId); + form.addRow("Copy Existing Layout", useExistingCheck); + form.addRow("", useExistingCombo); + form.addRow("Primary Tileset", primaryCombo); + form.addRow("Secondary Tileset", secondaryCombo); + form.addRow("Layout Width", widthSpin); + form.addRow("Layout Height", heightSpin); + form.addRow("", errorMessageLabel); + + connect(&newItemButtonBox, &QDialogButtonBox::accepted, [&](){ + // verify some things + QString errorMessage; + QString tryLayoutName = newNameEdit->text(); + // name not empty + if (tryLayoutName.isEmpty()) { + errorMessage = "Name cannot be empty"; + } + // unique layout name & id + else if (this->editor->project->mapLayoutsTable.contains(newId->text()) + || this->editor->project->layoutIdsToNames.find(tryLayoutName) != this->editor->project->layoutIdsToNames.end()) { + errorMessage = "Layout Name / ID is not unique"; + } + // from id is existing value + else if (useExistingCheck->isChecked()) { + if (!this->editor->project->mapLayoutsTable.contains(useExistingCombo->currentText())) { + errorMessage = "Existing layout ID is not valid"; + } + } + + if (!errorMessage.isEmpty()) { + // show error + errorMessageLabel->setText(errorMessage); + errorMessageLabel->setVisible(true); + } + else { + dialog.accept(); + } + }); + + form.addRow(&newItemButtonBox); + + if (dialog.exec() == QDialog::Accepted) { + Layout::SimpleSettings layoutSettings; + QString layoutName = newNameEdit->text(); + layoutSettings.name = layoutName; + layoutSettings.id = Layout::layoutConstantFromName(layoutName.remove("_Layout")); + if (useExistingCheck->isChecked()) { + layoutSettings.from_id = useExistingCombo->currentText(); + } else { + layoutSettings.width = widthSpin->value(); + layoutSettings.height = heightSpin->value(); + layoutSettings.tileset_primary_label = primaryCombo->currentText(); + layoutSettings.tileset_secondary_label = secondaryCombo->currentText(); + } + Layout *newLayout = this->editor->project->createNewLayout(layoutSettings); + this->layoutTreeModel->insertLayoutItem(newLayout->id); + setLayout(newLayout->id); + } } -void MainWindow::onAddNewMapToLayoutClick(QAction* triggeredAction) -{ - openNewMapPopupWindow(); - this->newMapPrompt->init(MapSortOrder::Layout, triggeredAction->data()); +void MainWindow::mapListAddArea() { + QDialog dialog(this, Qt::WindowTitleHint | Qt::WindowCloseButtonHint); + dialog.setWindowModality(Qt::ApplicationModal); + QDialogButtonBox newItemButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, &dialog); + connect(&newItemButtonBox, &QDialogButtonBox::rejected, &dialog, &QDialog::reject); + + const QString prefix = projectConfig.getIdentifier(ProjectIdentifier::define_map_section_prefix); + QLineEdit *newNameEdit = new QLineEdit(&dialog); + QLineEdit *newNameDisplay = new QLineEdit(&dialog); + newNameDisplay->setText(prefix); + newNameDisplay->setEnabled(false); + connect(newNameEdit, &QLineEdit::textEdited, [newNameDisplay, prefix] (const QString &text) { + // As the user types a name, update the label to show the name with the prefix. + newNameDisplay->setText(prefix + text); + }); + + QLabel *errorMessageLabel = new QLabel(&dialog); + errorMessageLabel->setVisible(false); + errorMessageLabel->setStyleSheet("QLabel { background-color: rgba(255, 0, 0, 25%) }"); + + static const QRegularExpression re_validChars("[A-Za-z_]+[\\w]*"); + newNameEdit->setValidator(new QRegularExpressionValidator(re_validChars, newNameEdit)); + + connect(&newItemButtonBox, &QDialogButtonBox::accepted, [&](){ + const QString newAreaName = newNameDisplay->text(); + if (this->editor->project->mapSectionIdNames.contains(newAreaName)){ + errorMessageLabel->setText(QString("An area with the name '%1' already exists").arg(newAreaName)); + errorMessageLabel->setVisible(true); + } else { + dialog.accept(); + } + }); + + QLabel *newNameEditLabel = new QLabel("New Area Name", &dialog); + QLabel *newNameDisplayLabel = new QLabel("Constant Name", &dialog); + newNameDisplayLabel->setEnabled(false); + + QFormLayout form(&dialog); + + form.addRow(newNameEditLabel, newNameEdit); + form.addRow(newNameDisplayLabel, newNameDisplay); + form.addRow("", errorMessageLabel); + form.addRow(&newItemButtonBox); + + if (dialog.exec() == QDialog::Accepted) { + if (newNameEdit->text().isEmpty()) return; + this->mapAreaModel->insertAreaItem(newNameDisplay->text()); + } } void MainWindow::onNewMapCreated() { @@ -1322,29 +1601,36 @@ void MainWindow::onNewMapCreated() { logInfo(QString("Created a new map named %1.").arg(newMapName)); + // TODO: Creating a new map shouldn't be automatically saved editor->project->saveMap(newMap); editor->project->saveAllDataStructures(); - QStandardItem* groupItem = mapGroupItemsList->at(newMapGroup); - int numMapsInGroup = groupItem->rowCount(); - - QStandardItem *newMapItem = createMapItem(newMapName, newMapGroup, numMapsInGroup); - groupItem->appendRow(newMapItem); - mapListIndexes.insert(newMapName, newMapItem->index()); - - sortMapList(); - setMap(newMapName, true); + // Add new Map / Layout to the mapList models + this->mapGroupModel->insertMapItem(newMapName, editor->project->groupNames[newMapGroup]); + this->mapAreaModel->insertMapItem(newMapName, newMap->location, newMapGroup); + this->layoutTreeModel->insertMapItem(newMapName, newMap->layout->id); // Refresh any combo box that displays map names and persists between maps // (other combo boxes like for warp destinations are repopulated when the map changes). - int index = this->editor->project->mapNames.indexOf(newMapName); - if (index >= 0) { - const QSignalBlocker blocker1(ui->comboBox_DiveMap); - const QSignalBlocker blocker2(ui->comboBox_EmergeMap); - ui->comboBox_DiveMap->insertItem(index, newMapName); - ui->comboBox_EmergeMap->insertItem(index, newMapName); + int mapIndex = this->editor->project->mapNames.indexOf(newMapName); + if (mapIndex >= 0) { + const QSignalBlocker b_DiveMap(ui->comboBox_DiveMap); + const QSignalBlocker b_EmergeMap(ui->comboBox_EmergeMap); + ui->comboBox_DiveMap->insertItem(mapIndex, newMapName); + ui->comboBox_EmergeMap->insertItem(mapIndex, newMapName); + } + + // Refresh layout combo box (if a new one was created) + if (!existingLayout) { + int layoutIndex = this->editor->project->mapLayoutsTable.indexOf(newMap->layout->id); + if (layoutIndex >= 0) { + const QSignalBlocker b_Layouts(ui->comboBox_LayoutSelector); + ui->comboBox_LayoutSelector->insertItem(layoutIndex, newMap->layout->id); + } } + setMap(newMapName); + if (newMap->needsHealLocation) { addNewEvent(Event::Type::HealLocation); editor->project->saveHealLocations(newMap); @@ -1370,6 +1656,7 @@ void MainWindow::openNewMapPopupWindow() { void MainWindow::on_action_NewMap_triggered() { openNewMapPopupWindow(); + this->newMapPrompt->initUi(); this->newMapPrompt->init(); } @@ -1491,7 +1778,7 @@ void MainWindow::on_actionNew_Tileset_triggered() { void MainWindow::updateTilesetEditor() { if (this->tilesetEditor) { this->tilesetEditor->update( - this->editor->map, + this->editor->layout, editor->ui->comboBox_PrimaryTileset->currentText(), editor->ui->comboBox_SecondaryTileset->currentText() ); @@ -1546,62 +1833,68 @@ void MainWindow::currentMetatilesSelectionChanged() { scrollMetatileSelectorToSelection(); } -void MainWindow::on_mapList_activated(const QModelIndex &index) -{ - QVariant data = index.data(Qt::UserRole); - if (index.data(MapListUserRoles::TypeRole) == "map_name" && !data.isNull()) - userSetMap(data.toString()); +void MainWindow::saveMapListTab(int index) { + porymapConfig.mapListTab = index; } -void MainWindow::updateMapListIcon(const QString &mapName) { - if (!editor->project || !editor->project->mapCache.contains(mapName)) +void MainWindow::openMapListItem(const QModelIndex &index) { + if (!index.isValid()) return; - QStandardItem *item = mapListModel->itemFromIndex(mapListIndexes.value(mapName)); - if (!item) + QVariant data = index.data(Qt::UserRole); + if (data.isNull()) return; - static const QIcon mapEditedIcon = QIcon(QStringLiteral(":/icons/map_edited.ico")); - static const QIcon mapOpenedIcon = QIcon(QStringLiteral(":/icons/map_opened.ico")); + // Normally when a new map/layout is opened the search filters are cleared and the lists will scroll to display that map/layout in the list. + // We don't want to do this when the user interacts with a list directly, so we temporarily prevent changes to the search filter. + auto toolbar = getCurrentMapListToolBar(); + if (toolbar) toolbar->setFilterLocked(true); - if (editor->map && editor->map->name == mapName) { - item->setIcon(mapOpenedIcon); - } else if (editor->project->mapCache.value(mapName)->hasUnsavedChanges()) { - item->setIcon(mapEditedIcon); - } else { - item->setIcon(mapIcon); + QString type = index.data(MapListUserRoles::TypeRole).toString(); + if (type == "map_name") { + userSetMap(data.toString()); + } else if (type == "map_layout") { + userSetLayout(data.toString()); } + + if (toolbar) toolbar->setFilterLocked(false); } void MainWindow::updateMapList() { - QList list; - list.append(QModelIndex()); - while (list.length()) { - QModelIndex parent = list.takeFirst(); - for (int i = 0; i < mapListModel->rowCount(parent); i++) { - QModelIndex index = mapListModel->index(i, 0, parent); - if (mapListModel->hasChildren(index)) { - list.append(index); - } - QVariant data = index.data(Qt::UserRole); - if (!data.isNull()) { - updateMapListIcon(data.toString()); - } - } + if (this->editor->map) { + this->mapGroupModel->setMap(this->editor->map->name); + this->groupListProxyModel->layoutChanged(); + this->mapAreaModel->setMap(this->editor->map->name); + this->areaListProxyModel->layoutChanged(); + } else { + this->mapGroupModel->setMap(QString()); + this->groupListProxyModel->layoutChanged(); + this->ui->mapList->clearSelection(); + this->mapAreaModel->setMap(QString()); + this->areaListProxyModel->layoutChanged(); + this->ui->areaList->clearSelection(); + } + + if (this->editor->layout) { + this->layoutTreeModel->setLayout(this->editor->layout->id); + this->layoutListProxyModel->layoutChanged(); + } else { + this->layoutTreeModel->setLayout(QString()); + this->layoutListProxyModel->layoutChanged(); + this->ui->layoutList->clearSelection(); } } void MainWindow::on_action_Save_Project_triggered() { editor->saveProject(); + updateWindowTitle(); updateMapList(); - showWindowTitle(); } void MainWindow::on_action_Save_triggered() { editor->save(); - if (editor->map) - updateMapListIcon(editor->map->name); - showWindowTitle(); + updateWindowTitle(); + updateMapList(); } void MainWindow::duplicate() { @@ -1656,7 +1949,7 @@ void MainWindow::copy() { case MainTab::Map: { // copy the map image - QPixmap pixmap = editor->map ? editor->map->render(true) : QPixmap(); + QPixmap pixmap = editor->layout ? editor->layout->render(true) : QPixmap(); setClipboardData(pixmap.toImage()); logInfo("Copied current map image to clipboard"); break; @@ -1727,7 +2020,7 @@ void MainWindow::setClipboardData(QImage image) { } void MainWindow::paste() { - if (!editor || !editor->project || !editor->map) return; + if (!editor || !editor->project || !(editor->map || editor->layout)) return; QClipboard *clipboard = QGuiApplication::clipboard(); QString clipboardText(clipboard->text()); @@ -1832,17 +2125,17 @@ void MainWindow::on_mapViewTab_tabBarClicked(int index) Scripting::cb_MapViewTabChanged(oldIndex, index); if (index == MapViewTab::Metatiles) { - editor->setEditingMap(); + editor->setEditingMetatiles(); } else if (index == MapViewTab::Collision) { editor->setEditingCollision(); } else if (index == MapViewTab::Prefabs) { - editor->setEditingMap(); + editor->setEditingMetatiles(); if (projectConfig.prefabFilepath.isEmpty() && !projectConfig.prefabImportPrompted) { // User hasn't set up prefabs and hasn't been prompted before. // Ask if they'd like to import the default prefabs file. if (prefab.tryImportDefaultPrefabs(this, projectConfig.baseGameVersion)) - prefab.updatePrefabUi(this->editor->map); - } + prefab.updatePrefabUi(this->editor->layout); + } } editor->setCursorRectVisible(false); } @@ -1866,16 +2159,18 @@ void MainWindow::on_mainTabBar_tabBarClicked(int index) if (index == MainTab::Map) { ui->stackedWidget_MapEvents->setCurrentIndex(0); on_mapViewTab_tabBarClicked(ui->mapViewTab->currentIndex()); - clickToolButtonFromEditMode(editor->map_edit_mode); + clickToolButtonFromEditAction(editor->mapEditAction); } else if (index == MainTab::Events) { ui->stackedWidget_MapEvents->setCurrentIndex(1); editor->setEditingObjects(); - clickToolButtonFromEditMode(editor->obj_edit_mode); + clickToolButtonFromEditAction(editor->objectEditAction); } else if (index == MainTab::Connections) { editor->setEditingConnections(); - // Stop the Dive/Emerge combo boxes from getting the initial focus - ui->graphicsView_Connections->setFocus(); + } else if (index == MainTab::WildPokemon) { + editor->setEditingEncounters(); } + + if (!editor->map) return; if (index != MainTab::WildPokemon) { if (editor->project && editor->project->wildEncountersLoaded) editor->saveEncounterTabData(); @@ -2314,66 +2609,12 @@ void MainWindow::on_horizontalSlider_CollisionTransparency_valueChanged(int valu this->editor->collision_item->draw(true); } -void MainWindow::onDeleteKeyPressed() { - auto tab = ui->mainTabBar->currentIndex(); - if (tab == MainTab::Events) { - on_toolButton_deleteObject_clicked(); - } else if (tab == MainTab::Connections) { - if (editor) editor->removeSelectedConnection(); - } -} - -void MainWindow::on_toolButton_deleteObject_clicked() { - if (editor && editor->selected_events) { - if (editor->selected_events->length()) { - DraggablePixmapItem *nextSelectedEvent = nullptr; - QList selectedEvents; - int numDeleted = 0; - for (DraggablePixmapItem *item : *editor->selected_events) { - Event::Group event_group = item->event->getEventGroup(); - if (event_group != Event::Group::Heal) { - numDeleted++; - item->event->setPixmapItem(item); - selectedEvents.append(item->event); - } - else { // don't allow deletion of heal locations - logWarn(QString("Cannot delete event of type '%1'").arg(Event::eventTypeToString(item->event->getEventType()))); - } - } - if (numDeleted) { - // Get the index for the event that should be selected after this event has been deleted. - // Select event at next smallest index when deleting a single event. - // If deleting multiple events, just let editor work out next selected. - if (numDeleted == 1) { - Event::Group event_group = selectedEvents[0]->getEventGroup(); - int index = editor->map->events.value(event_group).indexOf(selectedEvents[0]); - if (index != editor->map->events.value(event_group).size() - 1) - index++; - else - index--; - Event *event = nullptr; - if (index >= 0) - event = editor->map->events.value(event_group).at(index); - for (QGraphicsItem *child : editor->events_group->childItems()) { - DraggablePixmapItem *event_item = static_cast(child); - if (event_item->event == event) { - nextSelectedEvent = event_item; - break; - } - } - } - editor->map->editHistory.push(new EventDelete(editor, editor->map, selectedEvents, nextSelectedEvent ? nextSelectedEvent->event : nullptr)); - } - } - } -} - void MainWindow::on_toolButton_Paint_clicked() { if (ui->mainTabBar->currentIndex() == MainTab::Map) - editor->map_edit_mode = "paint"; + editor->mapEditAction = Editor::EditAction::Paint; else - editor->obj_edit_mode = "paint"; + editor->objectEditAction = Editor::EditAction::Paint; editor->settings->mapCursor = QCursor(QPixmap(":/icons/pencil_cursor.ico"), 10, 10); @@ -2392,9 +2633,9 @@ void MainWindow::on_toolButton_Paint_clicked() void MainWindow::on_toolButton_Select_clicked() { if (ui->mainTabBar->currentIndex() == MainTab::Map) - editor->map_edit_mode = "select"; + editor->mapEditAction = Editor::EditAction::Select; else - editor->obj_edit_mode = "select"; + editor->objectEditAction = Editor::EditAction::Select; editor->settings->mapCursor = QCursor(); editor->cursorMapTileRect->setSingleTileMode(); @@ -2411,9 +2652,9 @@ void MainWindow::on_toolButton_Select_clicked() void MainWindow::on_toolButton_Fill_clicked() { if (ui->mainTabBar->currentIndex() == MainTab::Map) - editor->map_edit_mode = "fill"; + editor->mapEditAction = Editor::EditAction::Fill; else - editor->obj_edit_mode = "fill"; + editor->objectEditAction = Editor::EditAction::Fill; editor->settings->mapCursor = QCursor(QPixmap(":/icons/fill_color_cursor.ico"), 10, 10); editor->cursorMapTileRect->setSingleTileMode(); @@ -2430,9 +2671,9 @@ void MainWindow::on_toolButton_Fill_clicked() void MainWindow::on_toolButton_Dropper_clicked() { if (ui->mainTabBar->currentIndex() == MainTab::Map) - editor->map_edit_mode = "pick"; + editor->mapEditAction = Editor::EditAction::Pick; else - editor->obj_edit_mode = "pick"; + editor->objectEditAction = Editor::EditAction::Pick; editor->settings->mapCursor = QCursor(QPixmap(":/icons/pipette_cursor.ico"), 10, 10); editor->cursorMapTileRect->setSingleTileMode(); @@ -2449,9 +2690,9 @@ void MainWindow::on_toolButton_Dropper_clicked() void MainWindow::on_toolButton_Move_clicked() { if (ui->mainTabBar->currentIndex() == MainTab::Map) - editor->map_edit_mode = "move"; + editor->mapEditAction = Editor::EditAction::Move; else - editor->obj_edit_mode = "move"; + editor->objectEditAction = Editor::EditAction::Move; editor->settings->mapCursor = QCursor(QPixmap(":/icons/move.ico"), 7, 7); editor->cursorMapTileRect->setSingleTileMode(); @@ -2468,9 +2709,9 @@ void MainWindow::on_toolButton_Move_clicked() void MainWindow::on_toolButton_Shift_clicked() { if (ui->mainTabBar->currentIndex() == MainTab::Map) - editor->map_edit_mode = "shift"; + editor->mapEditAction = Editor::EditAction::Shift; else - editor->obj_edit_mode = "shift"; + editor->objectEditAction = Editor::EditAction::Shift; editor->settings->mapCursor = QCursor(QPixmap(":/icons/shift_cursor.ico"), 10, 10); editor->cursorMapTileRect->setSingleTileMode(); @@ -2485,37 +2726,37 @@ void MainWindow::on_toolButton_Shift_clicked() } void MainWindow::checkToolButtons() { - QString edit_mode; + Editor::EditAction editAction; if (ui->mainTabBar->currentIndex() == MainTab::Map) { - edit_mode = editor->map_edit_mode; + editAction = editor->mapEditAction; } else { - edit_mode = editor->obj_edit_mode; - if (edit_mode == "select" && editor->map_ruler) + editAction = editor->objectEditAction; + if (editAction == Editor::EditAction::Select && editor->map_ruler) editor->map_ruler->setEnabled(true); else if (editor->map_ruler) editor->map_ruler->setEnabled(false); } - ui->toolButton_Paint->setChecked(edit_mode == "paint"); - ui->toolButton_Select->setChecked(edit_mode == "select"); - ui->toolButton_Fill->setChecked(edit_mode == "fill"); - ui->toolButton_Dropper->setChecked(edit_mode == "pick"); - ui->toolButton_Move->setChecked(edit_mode == "move"); - ui->toolButton_Shift->setChecked(edit_mode == "shift"); + ui->toolButton_Paint->setChecked(editAction == Editor::EditAction::Paint); + ui->toolButton_Select->setChecked(editAction == Editor::EditAction::Select); + ui->toolButton_Fill->setChecked(editAction == Editor::EditAction::Fill); + ui->toolButton_Dropper->setChecked(editAction == Editor::EditAction::Pick); + ui->toolButton_Move->setChecked(editAction == Editor::EditAction::Move); + ui->toolButton_Shift->setChecked(editAction == Editor::EditAction::Shift); } -void MainWindow::clickToolButtonFromEditMode(QString editMode) { - if (editMode == "paint") { +void MainWindow::clickToolButtonFromEditAction(Editor::EditAction editAction) { + if (editAction == Editor::EditAction::Paint) { on_toolButton_Paint_clicked(); - } else if (editMode == "select") { + } else if (editAction == Editor::EditAction::Select) { on_toolButton_Select_clicked(); - } else if (editMode == "fill") { + } else if (editAction == Editor::EditAction::Fill) { on_toolButton_Fill_clicked(); - } else if (editMode == "pick") { + } else if (editAction == Editor::EditAction::Pick) { on_toolButton_Dropper_clicked(); - } else if (editMode == "move") { + } else if (editAction == Editor::EditAction::Move) { on_toolButton_Move_clicked(); - } else if (editMode == "shift") { + } else if (editAction == Editor::EditAction::Shift) { on_toolButton_Shift_clicked(); } } @@ -2523,34 +2764,30 @@ void MainWindow::clickToolButtonFromEditMode(QString editMode) { void MainWindow::onOpenConnectedMap(MapConnection *connection) { if (!connection) return; - if (userSetMap(connection->targetMapName(), true)) + if (userSetMap(connection->targetMapName())) editor->setSelectedConnection(connection->findMirror()); } -void MainWindow::onMapNeedsRedrawing() { - redrawMapScene(); -} - -void MainWindow::onMapCacheCleared() { - editor->map = nullptr; +void MainWindow::onLayoutChanged(Layout *) { + updateMapList(); } void MainWindow::onMapLoaded(Map *map) { - connect(map, &Map::modified, [this, map] { this->markMapEdited(map); }); + connect(map, &Map::modified, [this, map] { this->markSpecificMapEdited(map); }); } void MainWindow::onTilesetsSaved(QString primaryTilesetLabel, QString secondaryTilesetLabel) { // If saved tilesets are currently in-use, update them and redraw // Otherwise overwrite the cache for the saved tileset bool updated = false; - if (primaryTilesetLabel == this->editor->map->layout->tileset_primary_label) { + if (primaryTilesetLabel == this->editor->layout->tileset_primary_label) { this->editor->updatePrimaryTileset(primaryTilesetLabel, true); Scripting::cb_TilesetUpdated(primaryTilesetLabel); updated = true; } else { this->editor->project->getTileset(primaryTilesetLabel, true); } - if (secondaryTilesetLabel == this->editor->map->layout->tileset_secondary_label) { + if (secondaryTilesetLabel == this->editor->layout->tileset_secondary_label) { this->editor->updateSecondaryTileset(secondaryTilesetLabel, true); Scripting::cb_TilesetUpdated(secondaryTilesetLabel); updated = true; @@ -2583,6 +2820,17 @@ void MainWindow::on_action_Export_Map_Image_triggered() { } void MainWindow::on_actionExport_Stitched_Map_Image_triggered() { + if (!this->editor->map) { + QMessageBox warning(this); + warning.setText("Notice"); + warning.setInformativeText("Map stitch images are not possible without a map selected."); + warning.setStandardButtons(QMessageBox::Ok); + warning.setDefaultButton(QMessageBox::Cancel); + warning.setIcon(QMessageBox::Warning); + + warning.exec(); + return; + } showExportMapImageWindow(ImageExporterMode::Stitch); } @@ -2603,7 +2851,7 @@ void MainWindow::importMapFromAdvanceMap1_92() MapParser parser; bool error = false; - MapLayout *mapLayout = parser.parse(filepath, &error, editor->project); + Layout *mapLayout = parser.parse(filepath, &error, editor->project); if (error) { QMessageBox msgBox(this); msgBox.setText("Failed to import map from Advance Map 1.92 .map file."); @@ -2666,13 +2914,13 @@ void MainWindow::on_pushButton_ConfigureEncountersJSON_clicked() { void MainWindow::on_button_OpenDiveMap_clicked() { const QString mapName = ui->comboBox_DiveMap->currentText(); if (editor->project->mapNames.contains(mapName)) - userSetMap(mapName, true); + userSetMap(mapName); } void MainWindow::on_button_OpenEmergeMap_clicked() { const QString mapName = ui->comboBox_EmergeMap->currentText(); if (editor->project->mapNames.contains(mapName)) - userSetMap(mapName, true); + userSetMap(mapName); } void MainWindow::on_comboBox_DiveMap_currentTextChanged(const QString &mapName) { @@ -2688,30 +2936,31 @@ void MainWindow::on_comboBox_EmergeMap_currentTextChanged(const QString &mapName void MainWindow::on_comboBox_PrimaryTileset_currentTextChanged(const QString &tilesetLabel) { - if (editor->project->primaryTilesetLabels.contains(tilesetLabel) && editor->map) { + if (editor->project->primaryTilesetLabels.contains(tilesetLabel) && editor->layout) { editor->updatePrimaryTileset(tilesetLabel); redrawMapScene(); on_horizontalSlider_MetatileZoom_valueChanged(ui->horizontalSlider_MetatileZoom->value()); updateTilesetEditor(); - prefab.updatePrefabUi(editor->map); + prefab.updatePrefabUi(editor->layout); markMapEdited(); } } void MainWindow::on_comboBox_SecondaryTileset_currentTextChanged(const QString &tilesetLabel) { - if (editor->project->secondaryTilesetLabels.contains(tilesetLabel) && editor->map) { + if (editor->project->secondaryTilesetLabels.contains(tilesetLabel) && editor->layout) { editor->updateSecondaryTileset(tilesetLabel); redrawMapScene(); on_horizontalSlider_MetatileZoom_valueChanged(ui->horizontalSlider_MetatileZoom->value()); updateTilesetEditor(); - prefab.updatePrefabUi(editor->map); + prefab.updatePrefabUi(editor->layout); markMapEdited(); } } -void MainWindow::on_pushButton_ChangeDimensions_clicked() -{ +void MainWindow::on_pushButton_ChangeDimensions_clicked() { + if (!editor || !editor->layout) return; + QDialog dialog(this, Qt::WindowTitleHint | Qt::WindowCloseButtonHint); dialog.setWindowTitle("Change Map Dimensions"); dialog.setWindowModality(Qt::NonModal); @@ -2730,10 +2979,10 @@ void MainWindow::on_pushButton_ChangeDimensions_clicked() heightSpinBox->setMaximum(editor->project->getMaxMapHeight()); bwidthSpinBox->setMaximum(MAX_BORDER_WIDTH); bheightSpinBox->setMaximum(MAX_BORDER_HEIGHT); - widthSpinBox->setValue(editor->map->getWidth()); - heightSpinBox->setValue(editor->map->getHeight()); - bwidthSpinBox->setValue(editor->map->getBorderWidth()); - bheightSpinBox->setValue(editor->map->getBorderHeight()); + widthSpinBox->setValue(editor->layout->getWidth()); + heightSpinBox->setValue(editor->layout->getHeight()); + bwidthSpinBox->setValue(editor->layout->getBorderWidth()); + bheightSpinBox->setValue(editor->layout->getBorderHeight()); if (projectConfig.useCustomBorderSize) { form.addRow(new QLabel("Map Width"), widthSpinBox); form.addRow(new QLabel("Map Height"), heightSpinBox); @@ -2761,8 +3010,8 @@ void MainWindow::on_pushButton_ChangeDimensions_clicked() dialog.accept(); } else { QString errorText = QString("Error: The specified width and height are too large.\n" - "The maximum map width and height is the following: (width + 15) * (height + 14) <= %1\n" - "The specified map width and height was: (%2 + 15) * (%3 + 14) = %4") + "The maximum layout width and height is the following: (width + 15) * (height + 14) <= %1\n" + "The specified layout width and height was: (%2 + 15) * (%3 + 14) = %4") .arg(maxMetatiles) .arg(widthSpinBox->value()) .arg(heightSpinBox->value()) @@ -2776,21 +3025,21 @@ void MainWindow::on_pushButton_ChangeDimensions_clicked() form.addRow(errorLabel); if (dialog.exec() == QDialog::Accepted) { - Map *map = editor->map; - Blockdata oldMetatiles = map->layout->blockdata; - Blockdata oldBorder = map->layout->border; - QSize oldMapDimensions(map->getWidth(), map->getHeight()); - QSize oldBorderDimensions(map->getBorderWidth(), map->getBorderHeight()); + Layout *layout = editor->layout; + Blockdata oldMetatiles = layout->blockdata; + Blockdata oldBorder = layout->border; + QSize oldMapDimensions(layout->getWidth(), layout->getHeight()); + QSize oldBorderDimensions(layout->getBorderWidth(), layout->getBorderHeight()); QSize newMapDimensions(widthSpinBox->value(), heightSpinBox->value()); QSize newBorderDimensions(bwidthSpinBox->value(), bheightSpinBox->value()); if (oldMapDimensions != newMapDimensions || oldBorderDimensions != newBorderDimensions) { - editor->map->setDimensions(newMapDimensions.width(), newMapDimensions.height(), true, true); - editor->map->setBorderDimensions(newBorderDimensions.width(), newBorderDimensions.height(), true, true); - editor->map->editHistory.push(new ResizeMap(map, + layout->setDimensions(newMapDimensions.width(), newMapDimensions.height(), true, true); + layout->setBorderDimensions(newBorderDimensions.width(), newBorderDimensions.height(), true, true); + editor->layout->editHistory.push(new ResizeLayout(layout, oldMapDimensions, newMapDimensions, - oldMetatiles, map->layout->blockdata, + oldMetatiles, layout->blockdata, oldBorderDimensions, newBorderDimensions, - oldBorder, map->layout->border + oldBorder, layout->border )); } } @@ -2830,22 +3079,47 @@ void MainWindow::on_actionTileset_Editor_triggered() } void MainWindow::initTilesetEditor() { - this->tilesetEditor = new TilesetEditor(this->editor->project, this->editor->map, this); + this->tilesetEditor = new TilesetEditor(this->editor->project, this->editor->layout, this); connect(this->tilesetEditor, &TilesetEditor::tilesetsSaved, this, &MainWindow::onTilesetsSaved); } -void MainWindow::on_toolButton_ExpandAll_clicked() -{ - if (ui->mapList) { - ui->mapList->expandToDepth(0); +MapListToolBar* MainWindow::getCurrentMapListToolBar() { + switch (ui->mapListContainer->currentIndex()) { + case MapListTab::Groups: return ui->mapListToolBar_Groups; + case MapListTab::Areas: return ui->mapListToolBar_Areas; + case MapListTab::Layouts: return ui->mapListToolBar_Layouts; + default: return nullptr; } } -void MainWindow::on_toolButton_CollapseAll_clicked() -{ - if (ui->mapList) { - ui->mapList->collapseAll(); - } +MapTree* MainWindow::getCurrentMapList() { + auto toolbar = getCurrentMapListToolBar(); + if (toolbar) + return toolbar->list(); + return nullptr; +} + +// Clear the search filters on all the map lists. +// When the search filter is cleared the map lists will (if possible) display the currently-selected map/layout. +void MainWindow::resetMapListFilters() { + ui->mapListToolBar_Groups->clearFilter(); + ui->mapListToolBar_Areas->clearFilter(); + ui->mapListToolBar_Layouts->clearFilter(); +} + +void MainWindow::mapListShortcut_ExpandAll() { + auto toolbar = getCurrentMapListToolBar(); + if (toolbar) toolbar->expandList(); +} + +void MainWindow::mapListShortcut_CollapseAll() { + auto toolbar = getCurrentMapListToolBar(); + if (toolbar) toolbar->collapseList(); +} + +void MainWindow::mapListShortcut_ToggleEmptyFolders() { + auto toolbar = getCurrentMapListToolBar(); + if (toolbar) toolbar->toggleEmptyFolders(); } void MainWindow::on_actionAbout_Porymap_triggered() @@ -2948,7 +3222,7 @@ void MainWindow::reloadScriptEngine() { // Lying to the scripts here, simulating a project reload Scripting::cb_ProjectOpened(projectConfig.projectDir); if (editor && editor->map) - Scripting::cb_MapOpened(editor->map->name); + Scripting::cb_MapOpened(editor->map->name); // TODO: API should have equivalent for layout } void MainWindow::on_pushButton_AddCustomHeaderField_clicked() @@ -3034,7 +3308,7 @@ void MainWindow::on_actionRegion_Map_Editor_triggered() { } void MainWindow::on_pushButton_CreatePrefab_clicked() { - PrefabCreationDialog dialog(this, this->editor->metatile_selector_item, this->editor->map); + PrefabCreationDialog dialog(this, this->editor->metatile_selector_item, this->editor->layout); dialog.setWindowTitle("Create Prefab"); dialog.setWindowModality(Qt::NonModal); if (dialog.exec() == QDialog::Accepted) { @@ -3137,16 +3411,7 @@ bool MainWindow::closeProject() { if (!isProjectOpen()) return true; - // Check loaded maps for unsaved changes - bool unsavedChanges = false; - for (auto map : editor->project->mapCache.values()) { - if (map && map->hasUnsavedChanges()) { - unsavedChanges = true; - break; - } - } - - if (unsavedChanges) { + if (this->editor->project->hasUnsavedChanges()) { QMessageBox::StandardButton result = QMessageBox::question( this, "porymap", "The project has been modified, save changes?", QMessageBox::No | QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Yes); @@ -3159,10 +3424,10 @@ bool MainWindow::closeProject() { return false; } } - clearProjectUI(); editor->closeProject(); + clearProjectUI(); setWindowDisabled(true); - setWindowTitle(QCoreApplication::applicationName()); + updateWindowTitle(); return true; } diff --git a/src/project.cpp b/src/project.cpp index 9964b9e38..88e3d9d1d 100644 --- a/src/project.cpp +++ b/src/project.cpp @@ -35,10 +35,10 @@ int Project::max_map_data_size = 10240; // 0x2800 int Project::default_map_size = 20; int Project::max_object_events = 64; -Project::Project(QWidget *parent) : +Project::Project(QObject *parent) : QObject(parent) { - initSignals(); + QObject::connect(&this->fileWatcher, &QFileSystemWatcher::fileChanged, this, &Project::fileChanged); } Project::~Project() @@ -49,45 +49,6 @@ Project::~Project() clearEventGraphics(); } -void Project::initSignals() { - // detect changes to specific filepaths being monitored - QObject::connect(&fileWatcher, &QFileSystemWatcher::fileChanged, [this](QString changed){ - if (!porymapConfig.monitorFiles) return; - if (modifiedFileTimestamps.contains(changed)) { - if (QDateTime::currentMSecsSinceEpoch() < modifiedFileTimestamps[changed]) { - return; - } - modifiedFileTimestamps.remove(changed); - } - - static bool showing = false; - if (showing) return; - - QMessageBox notice(this->parentWidget()); - notice.setText("File Changed"); - notice.setInformativeText(QString("The file %1 has changed on disk. Would you like to reload the project?") - .arg(changed.remove(this->root + "/"))); - notice.setStandardButtons(QMessageBox::No | QMessageBox::Yes); - notice.setDefaultButton(QMessageBox::No); - notice.setIcon(QMessageBox::Question); - - QCheckBox showAgainCheck("Do not ask again."); - notice.setCheckBox(&showAgainCheck); - - showing = true; - int choice = notice.exec(); - if (choice == QMessageBox::Yes) { - emit reloadProject(); - } else if (choice == QMessageBox::No) { - if (showAgainCheck.isChecked()) { - porymapConfig.monitorFiles = false; - emit uncheckMonitorFilesAction(); - } - } - showing = false; - }); -} - void Project::set_root(QString dir) { this->root = dir; FileDialog::setDirectory(dir); @@ -163,7 +124,6 @@ void Project::clearMapCache() { delete map; } mapCache.clear(); - emit mapCacheCleared(); } void Project::clearTilesetCache() { @@ -429,15 +389,87 @@ QString Project::readMapLocation(QString map_name) { return ParseUtil::jsonToQString(mapObj["region_map_section"]); } -bool Project::loadLayout(MapLayout *layout) { - // Force these to run even if one fails - bool loadedTilesets = loadLayoutTilesets(layout); - bool loadedBlockdata = loadBlockdata(layout); - bool loadedBorder = loadLayoutBorder(layout); +Layout *Project::createNewLayout(Layout::SimpleSettings &layoutSettings) { + QString basePath = projectConfig.getFilePath(ProjectFilePath::data_layouts_folders); + Layout *layout; + + // Handle the case where we are copying from an existing layout first. + if (!layoutSettings.from_id.isEmpty()) { + // load from layout + loadLayout(mapLayouts[layoutSettings.from_id]); + + layout = mapLayouts[layoutSettings.from_id]->copy(); + layout->name = layoutSettings.name; + layout->id = layoutSettings.id; + layout->border_path = QString("%1%2/border.bin").arg(basePath, layoutSettings.name); + layout->blockdata_path = QString("%1%2/map.bin").arg(basePath, layoutSettings.name); + } + else { + layout = new Layout; + + layout->name = layoutSettings.name; + layout->id = layoutSettings.id; + layout->width = layoutSettings.width; + layout->height = layoutSettings.height; + layout->border_width = DEFAULT_BORDER_WIDTH; + layout->border_height = DEFAULT_BORDER_HEIGHT; + layout->tileset_primary_label = layoutSettings.tileset_primary_label; + layout->tileset_secondary_label = layoutSettings.tileset_secondary_label; + layout->border_path = QString("%1%2/border.bin").arg(basePath, layoutSettings.name); + layout->blockdata_path = QString("%1%2/map.bin").arg(basePath, layoutSettings.name); + + setNewLayoutBlockdata(layout); + setNewLayoutBorder(layout); + } + + // Create a new directory for the layout + QString newLayoutDir = QString(root + "/%1%2").arg(projectConfig.getFilePath(ProjectFilePath::data_layouts_folders), layout->name); + if (!QDir::root().mkdir(newLayoutDir)) { + logError(QString("Error: failed to create directory for new layout: '%1'").arg(newLayoutDir)); + delete layout; + return nullptr; + } + + mapLayouts.insert(layout->id, layout); + mapLayoutsMaster.insert(layout->id, layout->copy()); + mapLayoutsTable.append(layout->id); + mapLayoutsTableMaster.append(layout->id); + layoutIdsToNames.insert(layout->id, layout->name); + + saveLayout(layout); + + this->loadLayout(layout); + + return layout; +} + +bool Project::loadLayout(Layout *layout) { + if (!layout->loaded) { + // Force these to run even if one fails + bool loadedTilesets = loadLayoutTilesets(layout); + bool loadedBlockdata = loadBlockdata(layout); + bool loadedBorder = loadLayoutBorder(layout); - return loadedTilesets - && loadedBlockdata - && loadedBorder; + if (loadedTilesets && loadedBlockdata && loadedBorder) { + layout->loaded = true; + return true; + } else { + return false; + } + } + return true; +} + +Layout *Project::loadLayout(QString layoutId) { + if (mapLayouts.contains(layoutId)) { + Layout *layout = mapLayouts[layoutId]; + if (loadLayout(layout)) { + return layout; + } + } + + logError(QString("Error: Failed to load layout '%1'").arg(layoutId)); + return nullptr; } bool Project::loadMapLayout(Map* map) { @@ -462,7 +494,10 @@ bool Project::loadMapLayout(Map* map) { void Project::clearMapLayouts() { qDeleteAll(mapLayouts); mapLayouts.clear(); + qDeleteAll(mapLayoutsMaster); + mapLayoutsMaster.clear(); mapLayoutsTable.clear(); + layoutIdsToNames.clear(); } bool Project::readMapLayouts() { @@ -510,7 +545,7 @@ bool Project::readMapLayouts() { logError(QString("Layout %1 is missing field(s) in %2.").arg(i).arg(layoutsFilepath)); return false; } - MapLayout *layout = new MapLayout(); + Layout *layout = new Layout(); layout->id = ParseUtil::jsonToQString(layoutObj["id"]); if (layout->id.isEmpty()) { logError(QString("Missing 'id' value on layout %1 in %2").arg(i).arg(layoutsFilepath)); @@ -586,14 +621,12 @@ bool Project::readMapLayouts() { return false; } mapLayouts.insert(layout->id, layout); + mapLayoutsMaster.insert(layout->id, layout->copy()); mapLayoutsTable.append(layout->id); + mapLayoutsTableMaster.append(layout->id); + layoutIdsToNames.insert(layout->id, layout->name); } - // Deep copy - mapLayoutsMaster = mapLayouts; - mapLayoutsMaster.detach(); - mapLayoutsTableMaster = mapLayoutsTable; - mapLayoutsTableMaster.detach(); return true; } @@ -610,7 +643,7 @@ void Project::saveMapLayouts() { OrderedJson::array layoutsArr; for (QString layoutId : mapLayoutsTableMaster) { - MapLayout *layout = mapLayouts.value(layoutId); + Layout *layout = mapLayoutsMaster.value(layoutId); OrderedJson::object layoutObj; layoutObj["id"] = layout->id; layoutObj["name"] = layout->name; @@ -675,6 +708,47 @@ void Project::saveMapGroups() { mapGroupsFile.close(); } +void Project::saveRegionMapSections() { + const QString filepath = QString("%1/%2").arg(this->root).arg(projectConfig.getFilePath(ProjectFilePath::json_region_map_entries)); + QFile file(filepath); + if (!file.open(QIODevice::WriteOnly)) { + logError(QString("Could not open '%1' for writing").arg(filepath)); + return; + } + + const QString emptyMapsecName = getEmptyMapsecName(); + OrderedJson::array mapSectionArray; + for (const auto &idName : this->mapSectionIdNames) { + // The 'empty' map section (MAPSEC_NONE) isn't normally present in the region map sections data file. + // We append this name to mapSectionIdNames ourselves if it isn't present, in which case we don't want to output data for it here. + if (!this->saveEmptyMapsec && idName == emptyMapsecName) + continue; + + OrderedJson::object mapSectionObj; + mapSectionObj["id"] = idName; + + if (this->regionMapEntries.contains(idName)) { + MapSectionEntry entry = this->regionMapEntries.value(idName); + mapSectionObj["name"] = entry.name; + mapSectionObj["x"] = entry.x; + mapSectionObj["y"] = entry.y; + mapSectionObj["width"] = entry.width; + mapSectionObj["height"] = entry.height; + } + + mapSectionArray.append(mapSectionObj); + } + + OrderedJson::object object; + object["map_sections"] = mapSectionArray; + + ignoreWatchedFileTemporarily(filepath); + OrderedJson json(object); + OrderedJsonDoc jsonDoc(&json); + jsonDoc.dump(&file); + file.close(); +} + void Project::saveWildMonData() { if (!this->wildEncountersLoaded) return; @@ -1062,7 +1136,7 @@ void Project::saveTilesetPalettes(Tileset *tileset) { } } -bool Project::loadLayoutTilesets(MapLayout *layout) { +bool Project::loadLayoutTilesets(Layout *layout) { layout->tileset_primary = getTileset(layout->tileset_primary_label); if (!layout->tileset_primary) { QString defaultTileset = this->getDefaultPrimaryTilesetLabel(); @@ -1130,11 +1204,11 @@ Tileset* Project::loadTileset(QString label, Tileset *tileset) { return tileset; } -bool Project::loadBlockdata(MapLayout *layout) { +bool Project::loadBlockdata(Layout *layout) { QString path = QString("%1/%2").arg(root).arg(layout->blockdata_path); layout->blockdata = readBlockdata(path); layout->lastCommitBlocks.blocks = layout->blockdata; - layout->lastCommitBlocks.mapDimensions = QSize(layout->getWidth(), layout->getHeight()); + layout->lastCommitBlocks.layoutDimensions = QSize(layout->getWidth(), layout->getHeight()); if (layout->blockdata.count() != layout->getWidth() * layout->getHeight()) { logWarn(QString("Layout blockdata length %1 does not match dimensions %2x%3 (should be %4). Resizing blockdata.") @@ -1147,19 +1221,19 @@ bool Project::loadBlockdata(MapLayout *layout) { return true; } -void Project::setNewMapBlockdata(Map *map) { - map->layout->blockdata.clear(); - int width = map->getWidth(); - int height = map->getHeight(); +void Project::setNewLayoutBlockdata(Layout *layout) { + layout->blockdata.clear(); + int width = layout->getWidth(); + int height = layout->getHeight(); Block block(projectConfig.defaultMetatileId, projectConfig.defaultCollision, projectConfig.defaultElevation); for (int i = 0; i < width * height; i++) { - map->layout->blockdata.append(block); + layout->blockdata.append(block); } - map->layout->lastCommitBlocks.blocks = map->layout->blockdata; - map->layout->lastCommitBlocks.mapDimensions = QSize(width, height); + layout->lastCommitBlocks.blocks = layout->blockdata; + layout->lastCommitBlocks.layoutDimensions = QSize(width, height); } -bool Project::loadLayoutBorder(MapLayout *layout) { +bool Project::loadLayoutBorder(Layout *layout) { QString path = QString("%1/%2").arg(root).arg(layout->border_path); layout->border = readBlockdata(path); layout->lastCommitBlocks.border = layout->border; @@ -1175,36 +1249,36 @@ bool Project::loadLayoutBorder(MapLayout *layout) { return true; } -void Project::setNewMapBorder(Map *map) { - map->layout->border.clear(); - int width = map->getBorderWidth(); - int height = map->getBorderHeight(); +void Project::setNewLayoutBorder(Layout *layout) { + layout->border.clear(); + int width = layout->getBorderWidth(); + int height = layout->getBorderHeight(); if (projectConfig.newMapBorderMetatileIds.length() != width * height) { // Border size doesn't match the number of default border metatiles. // Fill the border with empty metatiles. for (int i = 0; i < width * height; i++) { - map->layout->border.append(0); + layout->border.append(0); } } else { // Fill the border with the default metatiles from the config. for (int i = 0; i < width * height; i++) { - map->layout->border.append(projectConfig.newMapBorderMetatileIds.at(i)); + layout->border.append(projectConfig.newMapBorderMetatileIds.at(i)); } } - map->layout->lastCommitBlocks.border = map->layout->border; - map->layout->lastCommitBlocks.borderDimensions = QSize(width, height); + layout->lastCommitBlocks.border = layout->border; + layout->lastCommitBlocks.borderDimensions = QSize(width, height); } -void Project::saveLayoutBorder(Map *map) { - QString path = QString("%1/%2").arg(root).arg(map->layout->border_path); - writeBlockdata(path, map->layout->border); +void Project::saveLayoutBorder(Layout *layout) { + QString path = QString("%1/%2").arg(root).arg(layout->border_path); + writeBlockdata(path, layout->border); } -void Project::saveLayoutBlockdata(Map* map) { - QString path = QString("%1/%2").arg(root).arg(map->layout->blockdata_path); - writeBlockdata(path, map->layout->blockdata); +void Project::saveLayoutBlockdata(Layout *layout) { + QString path = QString("%1/%2").arg(root).arg(layout->blockdata_path); + writeBlockdata(path, layout->blockdata); } void Project::writeBlockdata(QString path, const Blockdata &blockdata) { @@ -1359,36 +1433,46 @@ void Project::saveMap(Map *map) { jsonDoc.dump(&mapFile); mapFile.close(); - saveLayoutBorder(map); - saveLayoutBlockdata(map); + saveLayout(map->layout); saveHealLocations(map); - // Update global data structures with current map data. - updateMapLayout(map); - map->isPersistedToFile = true; map->hasUnsavedDataChanges = false; map->editHistory.setClean(); } -void Project::updateMapLayout(Map* map) { - if (!mapLayoutsTableMaster.contains(map->layoutId)) { - mapLayoutsTableMaster.append(map->layoutId); +void Project::saveLayout(Layout *layout) { + // + saveLayoutBorder(layout); + saveLayoutBlockdata(layout); + + // Update global data structures with current map data. + updateLayout(layout); + + layout->editHistory.setClean(); +} + +void Project::updateLayout(Layout *layout) { + if (!mapLayoutsTableMaster.contains(layout->id)) { + mapLayoutsTableMaster.append(layout->id); } - // Deep copy - MapLayout *layout = mapLayouts.value(map->layoutId); - MapLayout *newLayout = new MapLayout(); - *newLayout = *layout; - mapLayoutsMaster.insert(map->layoutId, newLayout); + if (mapLayoutsMaster.contains(layout->id)) { + mapLayoutsMaster[layout->id]->copyFrom(layout); + } + else { + mapLayoutsMaster.insert(layout->id, layout->copy()); + } } void Project::saveAllDataStructures() { saveMapLayouts(); saveMapGroups(); + saveRegionMapSections(); saveMapConstantsHeader(); saveWildMonData(); saveConfig(); + this->hasUnsavedDataChanges = false; } void Project::saveConfig() { @@ -1872,11 +1956,12 @@ Map* Project::addNewMapToGroup(QString mapName, int groupNum, Map *newMap, bool if (!existingLayout) { this->mapLayouts.insert(newMap->layoutId, newMap->layout); this->mapLayoutsTable.append(newMap->layoutId); + this->layoutIdsToNames.insert(newMap->layout->id, newMap->layout->name); if (!importedMap) { - setNewMapBlockdata(newMap); + setNewLayoutBlockdata(newMap->layout); } if (newMap->layout->border.isEmpty()) { - setNewMapBorder(newMap); + setNewLayoutBorder(newMap->layout); } } @@ -2166,24 +2251,99 @@ bool Project::readFieldmapMasks() { } bool Project::readRegionMapSections() { - this->mapSectionNameToValue.clear(); - this->mapSectionValueToName.clear(); - - const QStringList regexList = {QString("\\b%1").arg(projectConfig.getIdentifier(ProjectIdentifier::define_map_section_prefix))}; - QString filename = projectConfig.getFilePath(ProjectFilePath::constants_region_map_sections); - fileWatcher.addPath(root + "/" + filename); - this->mapSectionNameToValue = parser.readCDefinesByRegex(filename, regexList); - if (this->mapSectionNameToValue.isEmpty()) { - logError(QString("Failed to read region map sections from %1.").arg(filename)); + this->mapSectionIdNames.clear(); + this->regionMapEntries.clear(); + this->saveEmptyMapsec = false; + const QString defaultName = getEmptyMapsecName(); + const QString requiredPrefix = projectConfig.getIdentifier(ProjectIdentifier::define_map_section_prefix); + + QJsonDocument doc; + const QString filepath = QString("%1/%2").arg(this->root).arg(projectConfig.getFilePath(ProjectFilePath::json_region_map_entries)); + if (!parser.tryParseJsonFile(&doc, filepath)) { + logError(QString("Failed to read region map sections from '%1'").arg(filepath)); return false; } + fileWatcher.addPath(filepath); + + QJsonArray mapSections = doc.object()["map_sections"].toArray(); + for (int i = 0; i < mapSections.size(); i++) { + QJsonObject mapSectionObj = mapSections.at(i).toObject(); + + // For each map section, "id" is the only required field. This is the field we use to display the location names in various drop-downs. + const QString idField = "id"; + if (!mapSectionObj.contains(idField)) { + logWarn(QString("Ignoring data for map section %1. Missing required field \"%2\"").arg(i).arg(idField)); + continue; + } + const QString idName = ParseUtil::jsonToQString(mapSectionObj[idField]); + if (!idName.startsWith(requiredPrefix)) { + logWarn(QString("Ignoring data for map section '%1'. IDs must start with the prefix '%2'").arg(idName).arg(requiredPrefix)); + continue; + } + + this->mapSectionIdNames.append(idName); + if (idName == defaultName) { + // If the user has data for the 'empty' MAPSEC we need to know to output it later, + // because we will otherwise add a dummy entry for this value. + this->saveEmptyMapsec = true; + } + + // Map sections may have additional data indicating their position on the region map. + // If they have this data, we can add them to the region map entry list. + bool hasRegionMapData = true; + static const QSet regionMapFieldNames = { "name", "x", "y", "width", "height" }; + for (auto fieldName : regionMapFieldNames) { + if (!mapSectionObj.contains(fieldName)) { + hasRegionMapData = false; + break; + } + } + if (!hasRegionMapData) + continue; - for (QString defineName : this->mapSectionNameToValue.keys()) { - this->mapSectionValueToName.insert(this->mapSectionNameToValue[defineName], defineName); + MapSectionEntry entry; + entry.name = ParseUtil::jsonToQString(mapSectionObj["name"]); + entry.x = ParseUtil::jsonToInt(mapSectionObj["x"]); + entry.y = ParseUtil::jsonToInt(mapSectionObj["y"]); + entry.width = ParseUtil::jsonToInt(mapSectionObj["width"]); + entry.height = ParseUtil::jsonToInt(mapSectionObj["height"]); + entry.valid = true; + this->regionMapEntries[idName] = entry; } + + // Make sure the default name is present in the list. + if (!this->mapSectionIdNames.contains(defaultName)) { + this->mapSectionIdNames.append(defaultName); + } + return true; } +QString Project::getEmptyMapsecName() { + return projectConfig.getIdentifier(ProjectIdentifier::define_map_section_prefix) + projectConfig.getIdentifier(ProjectIdentifier::define_map_section_empty); +} + +// This function assumes a valid and unique name +void Project::addNewMapsec(const QString &name) { + if (!this->mapSectionIdNames.isEmpty() && this->mapSectionIdNames.last() == getEmptyMapsecName()) { + // If the default map section name (MAPSEC_NONE) is last in the list we'll keep it last in the list. + this->mapSectionIdNames.insert(this->mapSectionIdNames.length() - 1, name); + } else { + this->mapSectionIdNames.append(name); + } + this->hasUnsavedDataChanges = true; + emit mapSectionIdNamesChanged(); +} + +void Project::removeMapsec(const QString &name) { + if (!this->mapSectionIdNames.contains(name) || name == getEmptyMapsecName()) + return; + + this->mapSectionIdNames.removeOne(name); + this->hasUnsavedDataChanges = true; + emit mapSectionIdNamesChanged(); +} + // Read the constants to preserve any "unused" heal locations when writing the file later bool Project::readHealLocationConstants() { this->healLocationNameToValue.clear(); @@ -2866,3 +3026,23 @@ void Project::applyParsedLimits() { projectConfig.collisionSheetHeight = qMin(projectConfig.collisionSheetHeight, Block::getMaxElevation() + 1); projectConfig.collisionSheetWidth = qMin(projectConfig.collisionSheetWidth, Block::getMaxCollision() + 1); } + +bool Project::hasUnsavedChanges() { + if (this->hasUnsavedDataChanges) + return true; + + // Check layouts for unsaved changes + for (auto i = this->mapLayouts.constBegin(); i != this->mapLayouts.constEnd(); i++) { + auto layout = i.value(); + if (layout && layout->hasUnsavedChanges()) + return true; + } + + // Check loaded maps for unsaved changes + for (auto i = this->mapCache.constBegin(); i != this->mapCache.constEnd(); i++) { + auto map = i.value(); + if (map && map->hasUnsavedChanges()) + return true; + } + return false; +} diff --git a/src/scriptapi/apimap.cpp b/src/scriptapi/apimap.cpp index 7d110de46..98478f94b 100644 --- a/src/scriptapi/apimap.cpp +++ b/src/scriptapi/apimap.cpp @@ -24,7 +24,7 @@ void MainWindow::tryRedrawMapArea(bool forceRedraw) { this->editor->updateMapBorder(); this->editor->updateMapConnections(); if (this->tilesetEditor) - this->tilesetEditor->updateTilesets(this->editor->map->layout->tileset_primary_label, this->editor->map->layout->tileset_secondary_label); + this->tilesetEditor->updateTilesets(this->editor->layout->tileset_primary_label, this->editor->layout->tileset_secondary_label); if (this->editor->metatile_selector_item) this->editor->metatile_selector_item->draw(); if (this->editor->selected_border_metatiles_item) @@ -42,13 +42,13 @@ void MainWindow::tryRedrawMapArea(bool forceRedraw) { void MainWindow::tryCommitMapChanges(bool commitChanges) { if (commitChanges) { - Map *map = this->editor->map; - if (map) { - map->editHistory.push(new ScriptEditMap(map, - map->layout->lastCommitBlocks.mapDimensions, QSize(map->getWidth(), map->getHeight()), - map->layout->lastCommitBlocks.blocks, map->layout->blockdata, - map->layout->lastCommitBlocks.borderDimensions, QSize(map->getBorderWidth(), map->getBorderHeight()), - map->layout->lastCommitBlocks.border, map->layout->border + Layout *layout = this->editor->layout; + if (layout) { + layout->editHistory.push(new ScriptEditLayout(layout, + layout->lastCommitBlocks.layoutDimensions, QSize(layout->getWidth(), layout->getHeight()), + layout->lastCommitBlocks.blocks, layout->blockdata, + layout->lastCommitBlocks.borderDimensions, QSize(layout->getBorderWidth(), layout->getBorderHeight()), + layout->lastCommitBlocks.border, layout->border )); } } @@ -59,27 +59,27 @@ void MainWindow::tryCommitMapChanges(bool commitChanges) { //===================== QJSValue MainWindow::getBlock(int x, int y) { - if (!this->editor || !this->editor->map) + if (!this->editor || !this->editor->layout) return QJSValue(); Block block; - if (!this->editor->map->getBlock(x, y, &block)) { + if (!this->editor->layout->getBlock(x, y, &block)) { return Scripting::fromBlock(Block()); } return Scripting::fromBlock(block); } void MainWindow::setBlock(int x, int y, int metatileId, int collision, int elevation, bool forceRedraw, bool commitChanges) { - if (!this->editor || !this->editor->map) + if (!this->editor || !this->editor->layout) return; - this->editor->map->setBlock(x, y, Block(metatileId, collision, elevation)); + this->editor->layout->setBlock(x, y, Block(metatileId, collision, elevation)); this->tryCommitMapChanges(commitChanges); this->tryRedrawMapArea(forceRedraw); } void MainWindow::setBlock(int x, int y, int rawValue, bool forceRedraw, bool commitChanges) { - if (!this->editor || !this->editor->map) + if (!this->editor || !this->editor->layout) return; - this->editor->map->setBlock(x, y, Block(static_cast(rawValue))); + this->editor->layout->setBlock(x, y, Block(static_cast(rawValue))); this->tryCommitMapChanges(commitChanges); this->tryRedrawMapArea(forceRedraw); } @@ -93,73 +93,73 @@ void MainWindow::setBlocksFromSelection(int x, int y, bool forceRedraw, bool com } int MainWindow::getMetatileId(int x, int y) { - if (!this->editor || !this->editor->map) + if (!this->editor || !this->editor->layout) return 0; Block block; - if (!this->editor->map->getBlock(x, y, &block)) { + if (!this->editor->layout->getBlock(x, y, &block)) { return 0; } return block.metatileId(); } void MainWindow::setMetatileId(int x, int y, int metatileId, bool forceRedraw, bool commitChanges) { - if (!this->editor || !this->editor->map) + if (!this->editor || !this->editor->layout) return; Block block; - if (!this->editor->map->getBlock(x, y, &block)) { + if (!this->editor->layout->getBlock(x, y, &block)) { return; } - this->editor->map->setBlock(x, y, Block(metatileId, block.collision(), block.elevation())); + this->editor->layout->setBlock(x, y, Block(metatileId, block.collision(), block.elevation())); this->tryCommitMapChanges(commitChanges); this->tryRedrawMapArea(forceRedraw); } int MainWindow::getCollision(int x, int y) { - if (!this->editor || !this->editor->map) + if (!this->editor || !this->editor->layout) return 0; Block block; - if (!this->editor->map->getBlock(x, y, &block)) { + if (!this->editor->layout->getBlock(x, y, &block)) { return 0; } return block.collision(); } void MainWindow::setCollision(int x, int y, int collision, bool forceRedraw, bool commitChanges) { - if (!this->editor || !this->editor->map) + if (!this->editor || !this->editor->layout) return; Block block; - if (!this->editor->map->getBlock(x, y, &block)) { + if (!this->editor->layout->getBlock(x, y, &block)) { return; } - this->editor->map->setBlock(x, y, Block(block.metatileId(), collision, block.elevation())); + this->editor->layout->setBlock(x, y, Block(block.metatileId(), collision, block.elevation())); this->tryCommitMapChanges(commitChanges); this->tryRedrawMapArea(forceRedraw); } int MainWindow::getElevation(int x, int y) { - if (!this->editor || !this->editor->map) + if (!this->editor || !this->editor->layout) return 0; Block block; - if (!this->editor->map->getBlock(x, y, &block)) { + if (!this->editor->layout->getBlock(x, y, &block)) { return 0; } return block.elevation(); } void MainWindow::setElevation(int x, int y, int elevation, bool forceRedraw, bool commitChanges) { - if (!this->editor || !this->editor->map) + if (!this->editor || !this->editor->layout) return; Block block; - if (!this->editor->map->getBlock(x, y, &block)) { + if (!this->editor->layout->getBlock(x, y, &block)) { return; } - this->editor->map->setBlock(x, y, Block(block.metatileId(), block.collision(), elevation)); + this->editor->layout->setBlock(x, y, Block(block.metatileId(), block.collision(), elevation)); this->tryCommitMapChanges(commitChanges); this->tryRedrawMapArea(forceRedraw); } void MainWindow::bucketFill(int x, int y, int metatileId, bool forceRedraw, bool commitChanges) { - if (!this->editor || !this->editor->map) + if (!this->editor || !this->editor->layout) return; this->editor->map_item->floodFill(x, y, metatileId, true); this->tryCommitMapChanges(commitChanges); @@ -167,7 +167,7 @@ void MainWindow::bucketFill(int x, int y, int metatileId, bool forceRedraw, bool } void MainWindow::bucketFillFromSelection(int x, int y, bool forceRedraw, bool commitChanges) { - if (!this->editor || !this->editor->map) + if (!this->editor || !this->editor->layout) return; this->editor->map_item->floodFill(x, y, true); this->tryCommitMapChanges(commitChanges); @@ -175,7 +175,7 @@ void MainWindow::bucketFillFromSelection(int x, int y, bool forceRedraw, bool co } void MainWindow::magicFill(int x, int y, int metatileId, bool forceRedraw, bool commitChanges) { - if (!this->editor || !this->editor->map) + if (!this->editor || !this->editor->layout) return; this->editor->map_item->magicFill(x, y, metatileId, true); this->tryCommitMapChanges(commitChanges); @@ -183,7 +183,7 @@ void MainWindow::magicFill(int x, int y, int metatileId, bool forceRedraw, bool } void MainWindow::magicFillFromSelection(int x, int y, bool forceRedraw, bool commitChanges) { - if (!this->editor || !this->editor->map) + if (!this->editor || !this->editor->layout) return; this->editor->map_item->magicFill(x, y, true); this->tryCommitMapChanges(commitChanges); @@ -191,7 +191,7 @@ void MainWindow::magicFillFromSelection(int x, int y, bool forceRedraw, bool com } void MainWindow::shift(int xDelta, int yDelta, bool forceRedraw, bool commitChanges) { - if (!this->editor || !this->editor->map) + if (!this->editor || !this->editor->layout) return; this->editor->map_item->shift(xDelta, yDelta, true); this->tryCommitMapChanges(commitChanges); @@ -207,51 +207,51 @@ void MainWindow::commit() { } QJSValue MainWindow::getDimensions() { - if (!this->editor || !this->editor->map) + if (!this->editor || !this->editor->layout) return QJSValue(); - return Scripting::dimensions(this->editor->map->getWidth(), this->editor->map->getHeight()); + return Scripting::dimensions(this->editor->layout->getWidth(), this->editor->layout->getHeight()); } int MainWindow::getWidth() { - if (!this->editor || !this->editor->map) + if (!this->editor || !this->editor->layout) return 0; - return this->editor->map->getWidth(); + return this->editor->layout->getWidth(); } int MainWindow::getHeight() { - if (!this->editor || !this->editor->map) + if (!this->editor || !this->editor->layout) return 0; - return this->editor->map->getHeight(); + return this->editor->layout->getHeight(); } void MainWindow::setDimensions(int width, int height) { - if (!this->editor || !this->editor->map) + if (!this->editor || !this->editor->layout) return; if (!Project::mapDimensionsValid(width, height)) return; - this->editor->map->setDimensions(width, height); + this->editor->layout->setDimensions(width, height); this->tryCommitMapChanges(true); - this->onMapNeedsRedrawing(); + this->redrawMapScene(); } void MainWindow::setWidth(int width) { - if (!this->editor || !this->editor->map) + if (!this->editor || !this->editor->layout) return; - if (!Project::mapDimensionsValid(width, this->editor->map->getHeight())) + if (!Project::mapDimensionsValid(width, this->editor->layout->getHeight())) return; - this->editor->map->setDimensions(width, this->editor->map->getHeight()); + this->editor->layout->setDimensions(width, this->editor->layout->getHeight()); this->tryCommitMapChanges(true); - this->onMapNeedsRedrawing(); + this->redrawMapScene(); } void MainWindow::setHeight(int height) { - if (!this->editor || !this->editor->map) + if (!this->editor || !this->editor->layout) return; - if (!Project::mapDimensionsValid(this->editor->map->getWidth(), height)) + if (!Project::mapDimensionsValid(this->editor->layout->getWidth(), height)) return; - this->editor->map->setDimensions(this->editor->map->getWidth(), height); + this->editor->layout->setDimensions(this->editor->layout->getWidth(), height); this->tryCommitMapChanges(true); - this->onMapNeedsRedrawing(); + this->redrawMapScene(); } //===================== @@ -259,69 +259,69 @@ void MainWindow::setHeight(int height) { //===================== int MainWindow::getBorderMetatileId(int x, int y) { - if (!this->editor || !this->editor->map) + if (!this->editor || !this->editor->layout) return 0; - if (!this->editor->map->isWithinBorderBounds(x, y)) + if (!this->editor->layout->isWithinBorderBounds(x, y)) return 0; - return this->editor->map->getBorderMetatileId(x, y); + return this->editor->layout->getBorderMetatileId(x, y); } void MainWindow::setBorderMetatileId(int x, int y, int metatileId, bool forceRedraw, bool commitChanges) { - if (!this->editor || !this->editor->map) + if (!this->editor || !this->editor->layout) return; - if (!this->editor->map->isWithinBorderBounds(x, y)) + if (!this->editor->layout->isWithinBorderBounds(x, y)) return; - this->editor->map->setBorderMetatileId(x, y, metatileId); + this->editor->layout->setBorderMetatileId(x, y, metatileId); this->tryCommitMapChanges(commitChanges); this->tryRedrawMapArea(forceRedraw); } QJSValue MainWindow::getBorderDimensions() { - if (!this->editor || !this->editor->map) + if (!this->editor || !this->editor->layout) return QJSValue(); - return Scripting::dimensions(this->editor->map->getBorderWidth(), this->editor->map->getBorderHeight()); + return Scripting::dimensions(this->editor->layout->getBorderWidth(), this->editor->layout->getBorderHeight()); } int MainWindow::getBorderWidth() { - if (!this->editor || !this->editor->map) + if (!this->editor || !this->editor->layout) return 0; - return this->editor->map->getBorderWidth(); + return this->editor->layout->getBorderWidth(); } int MainWindow::getBorderHeight() { - if (!this->editor || !this->editor->map) + if (!this->editor || !this->editor->layout) return 0; - return this->editor->map->getBorderHeight(); + return this->editor->layout->getBorderHeight(); } void MainWindow::setBorderDimensions(int width, int height) { - if (!this->editor || !this->editor->map || !projectConfig.useCustomBorderSize) + if (!this->editor || !this->editor->layout || !projectConfig.useCustomBorderSize) return; if (width < 1 || height < 1 || width > MAX_BORDER_WIDTH || height > MAX_BORDER_HEIGHT) return; - this->editor->map->setBorderDimensions(width, height); + this->editor->layout->setBorderDimensions(width, height); this->tryCommitMapChanges(true); - this->onMapNeedsRedrawing(); + this->redrawMapScene(); } void MainWindow::setBorderWidth(int width) { - if (!this->editor || !this->editor->map || !projectConfig.useCustomBorderSize) + if (!this->editor || !this->editor->layout || !projectConfig.useCustomBorderSize) return; if (width < 1 || width > MAX_BORDER_WIDTH) return; - this->editor->map->setBorderDimensions(width, this->editor->map->getBorderHeight()); + this->editor->layout->setBorderDimensions(width, this->editor->layout->getBorderHeight()); this->tryCommitMapChanges(true); - this->onMapNeedsRedrawing(); + this->redrawMapScene(); } void MainWindow::setBorderHeight(int height) { - if (!this->editor || !this->editor->map || !projectConfig.useCustomBorderSize) + if (!this->editor || !this->editor->layout || !projectConfig.useCustomBorderSize) return; if (height < 1 || height > MAX_BORDER_HEIGHT) return; - this->editor->map->setBorderDimensions(this->editor->map->getBorderWidth(), height); + this->editor->layout->setBorderDimensions(this->editor->layout->getBorderWidth(), height); this->tryCommitMapChanges(true); - this->onMapNeedsRedrawing(); + this->redrawMapScene(); } //====================== @@ -330,7 +330,7 @@ void MainWindow::setBorderHeight(int height) { void MainWindow::refreshAfterPaletteChange(Tileset *tileset) { if (this->tilesetEditor) { - this->tilesetEditor->updateTilesets(this->editor->map->layout->tileset_primary_label, this->editor->map->layout->tileset_secondary_label); + this->tilesetEditor->updateTilesets(this->editor->layout->tileset_primary_label, this->editor->layout->tileset_secondary_label); } this->editor->metatile_selector_item->draw(); this->editor->selected_border_metatiles_item->draw(); @@ -341,7 +341,7 @@ void MainWindow::refreshAfterPaletteChange(Tileset *tileset) { } void MainWindow::setTilesetPalette(Tileset *tileset, int paletteIndex, QList> colors) { - if (!this->editor || !this->editor->map || !this->editor->map->layout) + if (!this->editor || !this->editor->layout) return; if (paletteIndex >= tileset->palettes.size()) return; @@ -357,42 +357,42 @@ void MainWindow::setTilesetPalette(Tileset *tileset, int paletteIndex, QList> colors, bool forceRedraw) { - if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_primary) + if (!this->editor || !this->editor->layout || !this->editor->layout->tileset_primary) return; - this->setTilesetPalette(this->editor->map->layout->tileset_primary, paletteIndex, colors); + this->setTilesetPalette(this->editor->layout->tileset_primary, paletteIndex, colors); if (forceRedraw) { - this->refreshAfterPaletteChange(this->editor->map->layout->tileset_primary); + this->refreshAfterPaletteChange(this->editor->layout->tileset_primary); } } void MainWindow::setPrimaryTilesetPalettes(QList>> palettes, bool forceRedraw) { - if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_primary) + if (!this->editor || !this->editor->layout || !this->editor->layout->tileset_primary) return; for (int i = 0; i < palettes.size(); i++) { - this->setTilesetPalette(this->editor->map->layout->tileset_primary, i, palettes[i]); + this->setTilesetPalette(this->editor->layout->tileset_primary, i, palettes[i]); } if (forceRedraw) { - this->refreshAfterPaletteChange(this->editor->map->layout->tileset_primary); + this->refreshAfterPaletteChange(this->editor->layout->tileset_primary); } } void MainWindow::setSecondaryTilesetPalette(int paletteIndex, QList> colors, bool forceRedraw) { - if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_secondary) + if (!this->editor || !this->editor->layout || !this->editor->layout->tileset_secondary) return; - this->setTilesetPalette(this->editor->map->layout->tileset_secondary, paletteIndex, colors); + this->setTilesetPalette(this->editor->layout->tileset_secondary, paletteIndex, colors); if (forceRedraw) { - this->refreshAfterPaletteChange(this->editor->map->layout->tileset_secondary); + this->refreshAfterPaletteChange(this->editor->layout->tileset_secondary); } } void MainWindow::setSecondaryTilesetPalettes(QList>> palettes, bool forceRedraw) { - if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_secondary) + if (!this->editor || !this->editor->layout || !this->editor->layout->tileset_secondary) return; for (int i = 0; i < palettes.size(); i++) { - this->setTilesetPalette(this->editor->map->layout->tileset_secondary, i, palettes[i]); + this->setTilesetPalette(this->editor->layout->tileset_secondary, i, palettes[i]); } if (forceRedraw) { - this->refreshAfterPaletteChange(this->editor->map->layout->tileset_secondary); + this->refreshAfterPaletteChange(this->editor->layout->tileset_secondary); } } @@ -420,27 +420,27 @@ QJSValue MainWindow::getTilesetPalettes(const QList> &palettes) { } QJSValue MainWindow::getPrimaryTilesetPalette(int paletteIndex) { - if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_primary) + if (!this->editor || !this->editor->layout || !this->editor->layout->tileset_primary) return QJSValue(); - return this->getTilesetPalette(this->editor->map->layout->tileset_primary->palettes, paletteIndex); + return this->getTilesetPalette(this->editor->layout->tileset_primary->palettes, paletteIndex); } QJSValue MainWindow::getPrimaryTilesetPalettes() { - if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_primary) + if (!this->editor || !this->editor->layout || !this->editor->layout->tileset_primary) return QJSValue(); - return this->getTilesetPalettes(this->editor->map->layout->tileset_primary->palettes); + return this->getTilesetPalettes(this->editor->layout->tileset_primary->palettes); } QJSValue MainWindow::getSecondaryTilesetPalette(int paletteIndex) { - if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_secondary) + if (!this->editor || !this->editor->layout || !this->editor->layout->tileset_secondary) return QJSValue(); - return this->getTilesetPalette(this->editor->map->layout->tileset_secondary->palettes, paletteIndex); + return this->getTilesetPalette(this->editor->layout->tileset_secondary->palettes, paletteIndex); } QJSValue MainWindow::getSecondaryTilesetPalettes() { - if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_secondary) + if (!this->editor || !this->editor->layout || !this->editor->layout->tileset_secondary) return QJSValue(); - return this->getTilesetPalettes(this->editor->map->layout->tileset_secondary->palettes); + return this->getTilesetPalettes(this->editor->layout->tileset_secondary->palettes); } void MainWindow::refreshAfterPalettePreviewChange() { @@ -452,7 +452,7 @@ void MainWindow::refreshAfterPalettePreviewChange() { } void MainWindow::setTilesetPalettePreview(Tileset *tileset, int paletteIndex, QList> colors) { - if (!this->editor || !this->editor->map || !this->editor->map->layout) + if (!this->editor || !this->editor->layout) return; if (paletteIndex >= tileset->palettePreviews.size()) return; @@ -467,19 +467,19 @@ void MainWindow::setTilesetPalettePreview(Tileset *tileset, int paletteIndex, QL } void MainWindow::setPrimaryTilesetPalettePreview(int paletteIndex, QList> colors, bool forceRedraw) { - if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_primary) + if (!this->editor || !this->editor->layout || !this->editor->layout->tileset_primary) return; - this->setTilesetPalettePreview(this->editor->map->layout->tileset_primary, paletteIndex, colors); + this->setTilesetPalettePreview(this->editor->layout->tileset_primary, paletteIndex, colors); if (forceRedraw) { this->refreshAfterPalettePreviewChange(); } } void MainWindow::setPrimaryTilesetPalettesPreview(QList>> palettes, bool forceRedraw) { - if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_primary) + if (!this->editor || !this->editor->layout || !this->editor->layout->tileset_primary) return; for (int i = 0; i < palettes.size(); i++) { - this->setTilesetPalettePreview(this->editor->map->layout->tileset_primary, i, palettes[i]); + this->setTilesetPalettePreview(this->editor->layout->tileset_primary, i, palettes[i]); } if (forceRedraw) { this->refreshAfterPalettePreviewChange(); @@ -487,19 +487,19 @@ void MainWindow::setPrimaryTilesetPalettesPreview(QList>> palet } void MainWindow::setSecondaryTilesetPalettePreview(int paletteIndex, QList> colors, bool forceRedraw) { - if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_secondary) + if (!this->editor || !this->editor->layout || !this->editor->layout->tileset_secondary) return; - this->setTilesetPalettePreview(this->editor->map->layout->tileset_secondary, paletteIndex, colors); + this->setTilesetPalettePreview(this->editor->layout->tileset_secondary, paletteIndex, colors); if (forceRedraw) { this->refreshAfterPalettePreviewChange(); } } void MainWindow::setSecondaryTilesetPalettesPreview(QList>> palettes, bool forceRedraw) { - if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_secondary) + if (!this->editor || !this->editor->layout || !this->editor->layout->tileset_secondary) return; for (int i = 0; i < palettes.size(); i++) { - this->setTilesetPalettePreview(this->editor->map->layout->tileset_secondary, i, palettes[i]); + this->setTilesetPalettePreview(this->editor->layout->tileset_secondary, i, palettes[i]); } if (forceRedraw) { this->refreshAfterPalettePreviewChange(); @@ -507,63 +507,63 @@ void MainWindow::setSecondaryTilesetPalettesPreview(QList>> pal } QJSValue MainWindow::getPrimaryTilesetPalettePreview(int paletteIndex) { - if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_primary) + if (!this->editor || !this->editor->layout || !this->editor->layout->tileset_primary) return QJSValue(); - return this->getTilesetPalette(this->editor->map->layout->tileset_primary->palettePreviews, paletteIndex); + return this->getTilesetPalette(this->editor->layout->tileset_primary->palettePreviews, paletteIndex); } QJSValue MainWindow::getPrimaryTilesetPalettesPreview() { - if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_primary) + if (!this->editor || !this->editor->layout || !this->editor->layout->tileset_primary) return QJSValue(); - return this->getTilesetPalettes(this->editor->map->layout->tileset_primary->palettePreviews); + return this->getTilesetPalettes(this->editor->layout->tileset_primary->palettePreviews); } QJSValue MainWindow::getSecondaryTilesetPalettePreview(int paletteIndex) { - if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_secondary) + if (!this->editor || !this->editor->layout || !this->editor->layout->tileset_secondary) return QJSValue(); - return this->getTilesetPalette(this->editor->map->layout->tileset_secondary->palettePreviews, paletteIndex); + return this->getTilesetPalette(this->editor->layout->tileset_secondary->palettePreviews, paletteIndex); } QJSValue MainWindow::getSecondaryTilesetPalettesPreview() { - if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_secondary) + if (!this->editor || !this->editor->layout || !this->editor->layout->tileset_secondary) return QJSValue(); - return this->getTilesetPalettes(this->editor->map->layout->tileset_secondary->palettePreviews); + return this->getTilesetPalettes(this->editor->layout->tileset_secondary->palettePreviews); } int MainWindow::getNumPrimaryTilesetMetatiles() { - if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_primary) + if (!this->editor || !this->editor->layout || !this->editor->layout->tileset_primary) return 0; - return this->editor->map->layout->tileset_primary->numMetatiles(); + return this->editor->layout->tileset_primary->numMetatiles(); } int MainWindow::getNumSecondaryTilesetMetatiles() { - if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_secondary) + if (!this->editor || !this->editor->layout || !this->editor->layout->tileset_secondary) return 0; - return this->editor->map->layout->tileset_secondary->numMetatiles(); + return this->editor->layout->tileset_secondary->numMetatiles(); } int MainWindow::getNumPrimaryTilesetTiles() { - if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_primary) + if (!this->editor || !this->editor->layout || !this->editor->layout->tileset_primary) return 0; - return this->editor->map->layout->tileset_primary->tiles.length(); + return this->editor->layout->tileset_primary->tiles.length(); } int MainWindow::getNumSecondaryTilesetTiles() { - if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_secondary) + if (!this->editor || !this->editor->layout || !this->editor->layout->tileset_secondary) return 0; - return this->editor->map->layout->tileset_secondary->tiles.length(); + return this->editor->layout->tileset_secondary->tiles.length(); } QString MainWindow::getPrimaryTileset() { - if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_primary) + if (!this->editor || !this->editor->layout || !this->editor->layout->tileset_primary) return QString(); - return this->editor->map->layout->tileset_primary->name; + return this->editor->layout->tileset_primary->name; } QString MainWindow::getSecondaryTileset() { - if (!this->editor || !this->editor->map || !this->editor->map->layout || !this->editor->map->layout->tileset_secondary) + if (!this->editor || !this->editor->layout || !this->editor->layout->tileset_secondary) return QString(); - return this->editor->map->layout->tileset_secondary->name; + return this->editor->layout->tileset_secondary->name; } void MainWindow::setPrimaryTileset(QString tileset) { @@ -575,13 +575,13 @@ void MainWindow::setSecondaryTileset(QString tileset) { } void MainWindow::saveMetatilesByMetatileId(int metatileId) { - Tileset * tileset = Tileset::getMetatileTileset(metatileId, this->editor->map->layout->tileset_primary, this->editor->map->layout->tileset_secondary); + Tileset * tileset = Tileset::getMetatileTileset(metatileId, this->editor->layout->tileset_primary, this->editor->layout->tileset_secondary); if (this->editor->project && tileset) this->editor->project->saveTilesetMetatiles(tileset); } void MainWindow::saveMetatileAttributesByMetatileId(int metatileId) { - Tileset * tileset = Tileset::getMetatileTileset(metatileId, this->editor->map->layout->tileset_primary, this->editor->map->layout->tileset_secondary); + Tileset * tileset = Tileset::getMetatileTileset(metatileId, this->editor->layout->tileset_primary, this->editor->layout->tileset_secondary); if (this->editor->project && tileset) this->editor->project->saveTilesetMetatileAttributes(tileset); @@ -597,19 +597,19 @@ void MainWindow::saveMetatileAttributesByMetatileId(int metatileId) { } Metatile * MainWindow::getMetatile(int metatileId) { - if (!this->editor || !this->editor->map || !this->editor->map->layout) + if (!this->editor || !this->editor->layout) return nullptr; - return Tileset::getMetatile(metatileId, this->editor->map->layout->tileset_primary, this->editor->map->layout->tileset_secondary); + return Tileset::getMetatile(metatileId, this->editor->layout->tileset_primary, this->editor->layout->tileset_secondary); } QString MainWindow::getMetatileLabel(int metatileId) { - if (!this->editor || !this->editor->map || !this->editor->map->layout) + if (!this->editor || !this->editor->layout) return QString(); - return Tileset::getMetatileLabel(metatileId, this->editor->map->layout->tileset_primary, this->editor->map->layout->tileset_secondary); + return Tileset::getMetatileLabel(metatileId, this->editor->layout->tileset_primary, this->editor->layout->tileset_secondary); } void MainWindow::setMetatileLabel(int metatileId, QString label) { - if (!this->editor || !this->editor->map || !this->editor->map->layout) + if (!this->editor || !this->editor->layout) return; // If the Tileset Editor is opened on this metatile we need to update the text box @@ -618,13 +618,13 @@ void MainWindow::setMetatileLabel(int metatileId, QString label) { return; } - if (!Tileset::setMetatileLabel(metatileId, label, this->editor->map->layout->tileset_primary, this->editor->map->layout->tileset_secondary)) { + if (!Tileset::setMetatileLabel(metatileId, label, this->editor->layout->tileset_primary, this->editor->layout->tileset_secondary)) { logError("Failed to set metatile label. Must be a valid metatile id and a label containing only letters, numbers, and underscores."); return; } if (this->editor->project) - this->editor->project->saveTilesetMetatileLabels(this->editor->map->layout->tileset_primary, this->editor->map->layout->tileset_secondary); + this->editor->project->saveTilesetMetatileLabels(this->editor->layout->tileset_primary, this->editor->layout->tileset_secondary); } int MainWindow::getMetatileLayerType(int metatileId) { @@ -794,9 +794,9 @@ void MainWindow::setMetatileTile(int metatileId, int tileIndex, QJSValue tileObj } QJSValue MainWindow::getTilePixels(int tileId) { - if (tileId < 0 || !this->editor || !this->editor->project || !this->editor->map || !this->editor->map->layout) + if (tileId < 0 || !this->editor || !this->editor->project || !this->editor->map || !this->editor->layout) return QJSValue(); - QImage tileImage = getTileImage(tileId, this->editor->map->layout->tileset_primary, this->editor->map->layout->tileset_secondary); + QImage tileImage = getTileImage(tileId, this->editor->layout->tileset_primary, this->editor->layout->tileset_secondary); if (tileImage.isNull() || tileImage.sizeInBytes() < 64) return QJSValue(); const uchar * pixels = tileImage.constBits(); @@ -836,7 +836,7 @@ QString MainWindow::getLocation() { void MainWindow::setLocation(QString location) { if (!this->ui || !this->editor || !this->editor->project) return; - if (!this->editor->project->mapSectionNameToValue.contains(location)) { + if (!this->editor->project->mapSectionIdNames.contains(location)) { logError(QString("Unknown location '%1'").arg(location)); return; } diff --git a/src/scriptapi/apioverlay.cpp b/src/scriptapi/apioverlay.cpp index bee473ff4..b12f5f095 100644 --- a/src/scriptapi/apioverlay.cpp +++ b/src/scriptapi/apioverlay.cpp @@ -254,23 +254,21 @@ void MapView::addImage(int x, int y, QString filepath, int layer, bool useCache) } void MapView::createImage(int x, int y, QString filepath, int width, int height, int xOffset, int yOffset, qreal hScale, qreal vScale, int paletteId, bool setTransparency, int layer, bool useCache) { - if (!this->editor || !this->editor->map || !this->editor->map->layout - || !this->editor->map->layout->tileset_primary || !this->editor->map->layout->tileset_secondary) + if (!this->editor || !this->editor->layout || !this->editor->layout->tileset_primary || !this->editor->layout->tileset_secondary) return; QList palette; if (paletteId != -1) - palette = Tileset::getPalette(paletteId, this->editor->map->layout->tileset_primary, this->editor->map->layout->tileset_secondary); + palette = Tileset::getPalette(paletteId, this->editor->layout->tileset_primary, this->editor->layout->tileset_secondary); if (this->getOverlay(layer)->addImage(x, y, filepath, useCache, width, height, xOffset, yOffset, hScale, vScale, palette, setTransparency)) this->scene()->update(); } void MapView::addTileImage(int x, int y, int tileId, bool xflip, bool yflip, int paletteId, bool setTransparency, int layer) { - if (!this->editor || !this->editor->map || !this->editor->map->layout - || !this->editor->map->layout->tileset_primary || !this->editor->map->layout->tileset_secondary) + if (!this->editor || !this->editor->layout || !this->editor->layout->tileset_primary || !this->editor->layout->tileset_secondary) return; QImage image = getPalettedTileImage(tileId, - this->editor->map->layout->tileset_primary, - this->editor->map->layout->tileset_secondary, + this->editor->layout->tileset_primary, + this->editor->layout->tileset_secondary, paletteId) .mirrored(xflip, yflip); if (setTransparency) @@ -285,14 +283,13 @@ void MapView::addTileImage(int x, int y, QJSValue tileObj, bool setTransparency, } void MapView::addMetatileImage(int x, int y, int metatileId, bool setTransparency, int layer) { - if (!this->editor || !this->editor->map || !this->editor->map->layout - || !this->editor->map->layout->tileset_primary || !this->editor->map->layout->tileset_secondary) + if (!this->editor || !this->editor->layout || !this->editor->layout->tileset_primary || !this->editor->layout->tileset_secondary) return; QImage image = getMetatileImage(static_cast(metatileId), - this->editor->map->layout->tileset_primary, - this->editor->map->layout->tileset_secondary, - this->editor->map->metatileLayerOrder, - this->editor->map->metatileLayerOpacity); + this->editor->layout->tileset_primary, + this->editor->layout->tileset_secondary, + this->editor->layout->metatileLayerOrder, + this->editor->layout->metatileLayerOpacity); if (setTransparency) image.setColor(0, qRgba(0, 0, 0, 0)); if (this->getOverlay(layer)->addImage(x, y, image)) diff --git a/src/scriptapi/apiutility.cpp b/src/scriptapi/apiutility.cpp index a4aeae966..e5cebc541 100644 --- a/src/scriptapi/apiutility.cpp +++ b/src/scriptapi/apiutility.cpp @@ -201,13 +201,13 @@ QList ScriptUtility::getCustomScripts() { } QList ScriptUtility::getMetatileLayerOrder() { - if (!window || !window->editor || !window->editor->map) + if (!window || !window->editor || !window->editor->layout) return QList(); - return window->editor->map->metatileLayerOrder; + return window->editor->layout->metatileLayerOrder; } void ScriptUtility::setMetatileLayerOrder(QList order) { - if (!window || !window->editor || !window->editor->map) + if (!window || !window->editor || !window->editor->layout) return; const int numLayers = 3; @@ -226,20 +226,20 @@ void ScriptUtility::setMetatileLayerOrder(QList order) { } if (invalid) return; - window->editor->map->metatileLayerOrder = order; + window->editor->layout->metatileLayerOrder = order; window->refreshAfterPalettePreviewChange(); } QList ScriptUtility::getMetatileLayerOpacity() { - if (!window || !window->editor || !window->editor->map) + if (!window || !window->editor || !window->editor->layout) return QList(); - return window->editor->map->metatileLayerOpacity; + return window->editor->layout->metatileLayerOpacity; } void ScriptUtility::setMetatileLayerOpacity(QList order) { - if (!window || !window->editor || !window->editor->map) + if (!window || !window->editor || !window->editor->layout) return; - window->editor->map->metatileLayerOpacity = order; + window->editor->layout->metatileLayerOpacity = order; window->refreshAfterPalettePreviewChange(); } @@ -282,7 +282,7 @@ QList ScriptUtility::getSongNames() { QList ScriptUtility::getLocationNames() { if (!window || !window->editor || !window->editor->project) return QList(); - return window->editor->project->mapSectionNameToValue.keys(); + return window->editor->project->mapSectionIdNames; } QList ScriptUtility::getWeatherNames() { diff --git a/src/ui/bordermetatilespixmapitem.cpp b/src/ui/bordermetatilespixmapitem.cpp index 8f8bc1348..6c5425586 100644 --- a/src/ui/bordermetatilespixmapitem.cpp +++ b/src/ui/bordermetatilespixmapitem.cpp @@ -7,30 +7,30 @@ void BorderMetatilesPixmapItem::mousePressEvent(QGraphicsSceneMouseEvent *event) { MetatileSelection selection = this->metatileSelector->getMetatileSelection(); QPoint pos = Metatile::coordFromPixmapCoord(event->pos()); - int width = map->getBorderWidth(); - int height = map->getBorderHeight(); + int width = layout->getBorderWidth(); + int height = layout->getBorderHeight(); - Blockdata oldBorder = map->layout->border; + Blockdata oldBorder = layout->border; for (int i = 0; i < selection.dimensions.x() && (i + pos.x()) < width; i++) { for (int j = 0; j < selection.dimensions.y() && (j + pos.y()) < height; j++) { MetatileSelectionItem item = selection.metatileItems.at(j * selection.dimensions.x() + i); - map->setBorderMetatileId(pos.x() + i, pos.y() + j, item.metatileId, true); + layout->setBorderMetatileId(pos.x() + i, pos.y() + j, item.metatileId, true); } } - if (map->layout->border != oldBorder) { - map->editHistory.push(new PaintBorder(map, oldBorder, map->layout->border, 0)); + if (layout->border != oldBorder) { + layout->editHistory.push(new PaintBorder(layout, oldBorder, layout->border, 0)); } emit borderMetatilesChanged(); } void BorderMetatilesPixmapItem::draw() { - map->setBorderItem(this); + layout->setBorderItem(this); - int width = map->getBorderWidth(); - int height = map->getBorderHeight(); + int width = layout->getBorderWidth(); + int height = layout->getBorderHeight(); QImage image(16 * width, 16 * height, QImage::Format_RGBA8888); QPainter painter(&image); @@ -39,11 +39,11 @@ void BorderMetatilesPixmapItem::draw() { int x = i * 16; int y = j * 16; QImage metatile_image = getMetatileImage( - map->getBorderMetatileId(i, j), - map->layout->tileset_primary, - map->layout->tileset_secondary, - map->metatileLayerOrder, - map->metatileLayerOpacity); + layout->getBorderMetatileId(i, j), + layout->tileset_primary, + layout->tileset_secondary, + layout->metatileLayerOrder, + layout->metatileLayerOpacity); QPoint metatile_origin = QPoint(x, y); painter.drawImage(metatile_origin, metatile_image); } @@ -57,7 +57,7 @@ void BorderMetatilesPixmapItem::draw() { void BorderMetatilesPixmapItem::hoverUpdate(const QPointF &pixmapPos) { QPoint pos = Metatile::coordFromPixmapCoord(pixmapPos); - uint16_t metatileId = this->map->getBorderMetatileId(pos.x(), pos.y()); + uint16_t metatileId = this->layout->getBorderMetatileId(pos.x(), pos.y()); emit this->hoveredBorderMetatileSelectionChanged(metatileId); } diff --git a/src/ui/collisionpixmapitem.cpp b/src/ui/collisionpixmapitem.cpp index 08829fd0f..e0587b081 100644 --- a/src/ui/collisionpixmapitem.cpp +++ b/src/ui/collisionpixmapitem.cpp @@ -8,7 +8,7 @@ void CollisionPixmapItem::hoverMoveEvent(QGraphicsSceneHoverEvent *event) { this->previousPos = pos; emit this->hoveredMapMovementPermissionChanged(pos.x(), pos.y()); } - if (this->settings->betterCursors && this->paintingMode == MapPixmapItem::PaintMode::Metatiles) { + if (this->settings->betterCursors && this->getEditsEnabled()) { setCursor(this->settings->mapCursor); } } @@ -21,7 +21,7 @@ void CollisionPixmapItem::hoverEnterEvent(QGraphicsSceneHoverEvent * event) { void CollisionPixmapItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *) { emit this->hoveredMapMovementPermissionCleared(); - if (this->settings->betterCursors && this->paintingMode == MapPixmapItem::PaintMode::Metatiles){ + if (this->settings->betterCursors && this->getEditsEnabled()){ unsetCursor(); } this->has_mouse = false; @@ -49,9 +49,9 @@ void CollisionPixmapItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { } void CollisionPixmapItem::draw(bool ignoreCache) { - if (map) { - map->setCollisionItem(this); - setPixmap(map->renderCollision(ignoreCache)); + if (this->layout) { + this->layout->setCollisionItem(this); + setPixmap(this->layout->renderCollision(ignoreCache)); setOpacity(*this->opacity); } } @@ -59,8 +59,8 @@ void CollisionPixmapItem::draw(bool ignoreCache) { void CollisionPixmapItem::paint(QGraphicsSceneMouseEvent *event) { if (event->type() == QEvent::GraphicsSceneMouseRelease) { actionId_++; - } else if (map) { - Blockdata oldCollision = map->layout->blockdata; + } else if (this->layout) { + Blockdata oldCollision = this->layout->blockdata; QPoint pos = Metatile::coordFromPixmapCoord(event->pos()); @@ -70,18 +70,18 @@ void CollisionPixmapItem::paint(QGraphicsSceneMouseEvent *event) { pos = this->adjustCoords(pos); } else { this->prevStraightPathState = false; - this->lockedAxis = MapPixmapItem::Axis::None; + this->lockedAxis = LayoutPixmapItem::Axis::None; } Block block; - if (map->getBlock(pos.x(), pos.y(), &block)) { + if (this->layout->getBlock(pos.x(), pos.y(), &block)) { block.setCollision(this->selectedCollision->value()); block.setElevation(this->selectedElevation->value()); - map->setBlock(pos.x(), pos.y(), block, true); + this->layout->setBlock(pos.x(), pos.y(), block, true); } - if (map->layout->blockdata != oldCollision) { - map->editHistory.push(new PaintCollision(map, oldCollision, map->layout->blockdata, actionId_)); + if (this->layout->blockdata != oldCollision) { + this->layout->editHistory.push(new PaintCollision(this->layout, oldCollision, this->layout->blockdata, actionId_)); } } } @@ -89,16 +89,16 @@ void CollisionPixmapItem::paint(QGraphicsSceneMouseEvent *event) { void CollisionPixmapItem::floodFill(QGraphicsSceneMouseEvent *event) { if (event->type() == QEvent::GraphicsSceneMouseRelease) { this->actionId_++; - } else if (map) { - Blockdata oldCollision = map->layout->blockdata; + } else if (this->layout) { + Blockdata oldCollision = this->layout->blockdata; QPoint pos = Metatile::coordFromPixmapCoord(event->pos()); uint16_t collision = this->selectedCollision->value(); uint16_t elevation = this->selectedElevation->value(); - map->floodFillCollisionElevation(pos.x(), pos.y(), collision, elevation); + this->layout->floodFillCollisionElevation(pos.x(), pos.y(), collision, elevation); - if (map->layout->blockdata != oldCollision) { - map->editHistory.push(new BucketFillCollision(map, oldCollision, map->layout->blockdata)); + if (this->layout->blockdata != oldCollision) { + this->layout->editHistory.push(new BucketFillCollision(this->layout, oldCollision, this->layout->blockdata)); } } } @@ -106,15 +106,15 @@ void CollisionPixmapItem::floodFill(QGraphicsSceneMouseEvent *event) { void CollisionPixmapItem::magicFill(QGraphicsSceneMouseEvent *event) { if (event->type() == QEvent::GraphicsSceneMouseRelease) { this->actionId_++; - } else if (map) { - Blockdata oldCollision = map->layout->blockdata; + } else if (this->layout) { + Blockdata oldCollision = this->layout->blockdata; QPoint pos = Metatile::coordFromPixmapCoord(event->pos()); uint16_t collision = this->selectedCollision->value(); uint16_t elevation = this->selectedElevation->value(); - map->magicFillCollisionElevation(pos.x(), pos.y(), collision, elevation); + this->layout->magicFillCollisionElevation(pos.x(), pos.y(), collision, elevation); - if (map->layout->blockdata != oldCollision) { - map->editHistory.push(new MagicFillCollision(map, oldCollision, map->layout->blockdata)); + if (this->layout->blockdata != oldCollision) { + this->layout->editHistory.push(new MagicFillCollision(this->layout, oldCollision, this->layout->blockdata)); } } } @@ -129,15 +129,15 @@ void CollisionPixmapItem::updateMovementPermissionSelection(QGraphicsSceneMouseE // Snap point to within map bounds. if (pos.x() < 0) pos.setX(0); - if (pos.x() >= map->getWidth()) pos.setX(map->getWidth() - 1); + if (pos.x() >= this->layout->getWidth()) pos.setX(this->layout->getWidth() - 1); if (pos.y() < 0) pos.setY(0); - if (pos.y() >= map->getHeight()) pos.setY(map->getHeight() - 1); + if (pos.y() >= this->layout->getHeight()) pos.setY(this->layout->getHeight() - 1); this->updateSelection(pos); } void CollisionPixmapItem::updateSelection(QPoint pos) { Block block; - if (map->getBlock(pos.x(), pos.y(), &block)) { + if (this->layout->getBlock(pos.x(), pos.y(), &block)) { this->selectedCollision->setValue(block.collision()); this->selectedElevation->setValue(block.elevation()); } diff --git a/src/ui/connectionpixmapitem.cpp b/src/ui/connectionpixmapitem.cpp index f84120128..35e07a15c 100644 --- a/src/ui/connectionpixmapitem.cpp +++ b/src/ui/connectionpixmapitem.cpp @@ -9,6 +9,7 @@ ConnectionPixmapItem::ConnectionPixmapItem(MapConnection* connection, int x, int connection(connection) { this->setEditable(true); + setFlag(ItemIsFocusable, true); this->basePixmap = pixmap(); this->setOrigin(x, y); } @@ -110,17 +111,20 @@ bool ConnectionPixmapItem::getEditable() { } void ConnectionPixmapItem::setSelected(bool selected) { + if (selected && !hasFocus()) { + setFocus(Qt::OtherFocusReason); + } + if (this->selected == selected) return; this->selected = selected; + this->render(); emit selectionChanged(selected); } void ConnectionPixmapItem::mousePressEvent(QGraphicsSceneMouseEvent *) { - if (!this->getEditable()) - return; - this->setSelected(true); + setFocus(Qt::MouseFocusReason); } void ConnectionPixmapItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { @@ -131,3 +135,18 @@ void ConnectionPixmapItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { void ConnectionPixmapItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *) { emit connectionItemDoubleClicked(this->connection); } + +void ConnectionPixmapItem::keyPressEvent(QKeyEvent* event) { + if (event->key() == Qt::Key_Delete || event->key() == Qt::Key_Backspace) { + emit deleteRequested(this->connection); + } else { + QGraphicsPixmapItem::keyPressEvent(event); + } +} + +void ConnectionPixmapItem::focusInEvent(QFocusEvent* event) { + if (!this->getEditable()) + return; + this->setSelected(true); + QGraphicsPixmapItem::focusInEvent(event); +} diff --git a/src/ui/connectionslistitem.cpp b/src/ui/connectionslistitem.cpp index a5b8759a3..ccdf7e6c0 100644 --- a/src/ui/connectionslistitem.cpp +++ b/src/ui/connectionslistitem.cpp @@ -10,6 +10,7 @@ ConnectionsListItem::ConnectionsListItem(QWidget *parent, MapConnection * connec ui(new Ui::ConnectionsListItem) { ui->setupUi(this); + setFocusPolicy(Qt::StrongFocus); const QSignalBlocker blocker1(ui->comboBox_Direction); const QSignalBlocker blocker2(ui->comboBox_Map); @@ -101,3 +102,16 @@ void ConnectionsListItem::on_button_Delete_clicked() { void ConnectionsListItem::on_button_OpenMap_clicked() { emit openMapClicked(this->connection); } + +void ConnectionsListItem::focusInEvent(QFocusEvent* event) { + this->setSelected(true); + QFrame::focusInEvent(event); +} + +void ConnectionsListItem::keyPressEvent(QKeyEvent* event) { + if (event->key() == Qt::Key_Delete || event->key() == Qt::Key_Backspace) { + on_button_Delete_clicked(); + } else { + QFrame::keyPressEvent(event); + } +} diff --git a/src/ui/currentselectedmetatilespixmapitem.cpp b/src/ui/currentselectedmetatilespixmapitem.cpp index 0967e5588..e8b16f49a 100644 --- a/src/ui/currentselectedmetatilespixmapitem.cpp +++ b/src/ui/currentselectedmetatilespixmapitem.cpp @@ -2,7 +2,7 @@ #include "imageproviders.h" #include -QPixmap drawMetatileSelection(MetatileSelection selection, Map *map) { +QPixmap drawMetatileSelection(MetatileSelection selection, Layout *layout) { int width = selection.dimensions.x() * 16; int height = selection.dimensions.y() * 16; QImage image(width, height, QImage::Format_RGBA8888); @@ -19,10 +19,10 @@ QPixmap drawMetatileSelection(MetatileSelection selection, Map *map) { if (item.enabled) { QImage metatile_image = getMetatileImage( item.metatileId, - map->layout->tileset_primary, - map->layout->tileset_secondary, - map->metatileLayerOrder, - map->metatileLayerOpacity); + layout->tileset_primary, + layout->tileset_secondary, + layout->metatileLayerOrder, + layout->metatileLayerOpacity); painter.drawImage(metatile_origin, metatile_image); } } @@ -34,5 +34,5 @@ QPixmap drawMetatileSelection(MetatileSelection selection, Map *map) { void CurrentSelectedMetatilesPixmapItem::draw() { MetatileSelection selection = metatileSelector->getMetatileSelection(); - setPixmap(drawMetatileSelection(selection, this->map)); + setPixmap(drawMetatileSelection(selection, this->layout)); } diff --git a/src/ui/eventfilters.cpp b/src/ui/eventfilters.cpp new file mode 100644 index 000000000..1e7b2b804 --- /dev/null +++ b/src/ui/eventfilters.cpp @@ -0,0 +1,26 @@ +#include "eventfilters.h" + +#include + + + +bool WheelFilter::eventFilter(QObject *, QEvent *event) { + if (event->type() == QEvent::Wheel) { + return true; + } + return false; +} + + + +bool MapSceneEventFilter::eventFilter(QObject*, QEvent *event) { + if (event->type() == QEvent::GraphicsSceneWheel) { + QGraphicsSceneWheelEvent *wheelEvent = static_cast(event); + if (wheelEvent->modifiers() & Qt::ControlModifier) { + emit wheelZoom(wheelEvent->delta() > 0 ? 1 : -1); + event->accept(); + return true; + } + } + return false; +} diff --git a/src/ui/filterchildrenproxymodel.cpp b/src/ui/filterchildrenproxymodel.cpp index a08c150c7..99464ae60 100644 --- a/src/ui/filterchildrenproxymodel.cpp +++ b/src/ui/filterchildrenproxymodel.cpp @@ -8,6 +8,15 @@ FilterChildrenProxyModel::FilterChildrenProxyModel(QObject *parent) : bool FilterChildrenProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const { + if (this->hideEmpty && source_parent.row() < 0) // want to hide children + { + QModelIndex source_index = sourceModel()->index(source_row, this->filterKeyColumn(), source_parent) ; + if(source_index.isValid()) + { + if (!sourceModel()->hasChildren(source_index)) + return false; + } + } // custom behaviour : if(filterRegularExpression().pattern().isEmpty() == false) { diff --git a/src/ui/graphicsview.cpp b/src/ui/graphicsview.cpp index fa04c0f72..a9761139a 100644 --- a/src/ui/graphicsview.cpp +++ b/src/ui/graphicsview.cpp @@ -24,6 +24,14 @@ void GraphicsView::moveEvent(QMoveEvent *event) { label_MapRulerStatus->move(mapToGlobal(QPoint(6, 6))); } +void MapView::keyPressEvent(QKeyEvent *event) { + if (editor && (event->key() == Qt::Key_Delete || event->key() == Qt::Key_Backspace)) { + editor->deleteSelectedEvents(); + } else { + QGraphicsView::keyPressEvent(event); + } +} + void MapView::drawForeground(QPainter *painter, const QRectF&) { for (auto i = this->overlayMap.constBegin(); i != this->overlayMap.constEnd(); i++) { i.value()->renderItems(painter); diff --git a/src/ui/mappixmapitem.cpp b/src/ui/layoutpixmapitem.cpp similarity index 66% rename from src/ui/mappixmapitem.cpp rename to src/ui/layoutpixmapitem.cpp index 173403cf0..f53cf275a 100644 --- a/src/ui/mappixmapitem.cpp +++ b/src/ui/layoutpixmapitem.cpp @@ -1,4 +1,4 @@ -#include "mappixmapitem.h" +#include "layoutpixmapitem.h" #include "metatile.h" #include "log.h" #include "scripting.h" @@ -7,8 +7,8 @@ #define SWAP(a, b) do { if (a != b) { a ^= b; b ^= a; a ^= b; } } while (0) -void MapPixmapItem::paint(QGraphicsSceneMouseEvent *event) { - if (map) { +void LayoutPixmapItem::paint(QGraphicsSceneMouseEvent *event) { + if (layout) { if (event->type() == QEvent::GraphicsSceneMouseRelease) { actionId_++; } else { @@ -20,7 +20,7 @@ void MapPixmapItem::paint(QGraphicsSceneMouseEvent *event) { pos = this->adjustCoords(pos); } else { this->prevStraightPathState = false; - this->lockedAxis = MapPixmapItem::Axis::None; + this->lockedAxis = LayoutPixmapItem::Axis::None; } // Paint onto the map. @@ -43,8 +43,8 @@ void MapPixmapItem::paint(QGraphicsSceneMouseEvent *event) { } } -void MapPixmapItem::shift(QGraphicsSceneMouseEvent *event) { - if (map) { +void LayoutPixmapItem::shift(QGraphicsSceneMouseEvent *event) { + if (layout) { if (event->type() == QEvent::GraphicsSceneMouseRelease) { actionId_++; } else { @@ -56,7 +56,7 @@ void MapPixmapItem::shift(QGraphicsSceneMouseEvent *event) { pos = this->adjustCoords(pos); } else { this->prevStraightPathState = false; - this->lockedAxis = MapPixmapItem::Axis::None; + this->lockedAxis = LayoutPixmapItem::Axis::None; } if (event->type() == QEvent::GraphicsSceneMousePress) { @@ -76,32 +76,32 @@ void MapPixmapItem::shift(QGraphicsSceneMouseEvent *event) { } } -void MapPixmapItem::shift(int xDelta, int yDelta, bool fromScriptCall) { - Blockdata oldMetatiles = map->layout->blockdata; +void LayoutPixmapItem::shift(int xDelta, int yDelta, bool fromScriptCall) { + Blockdata oldMetatiles = this->layout->blockdata; - for (int i = 0; i < map->getWidth(); i++) - for (int j = 0; j < map->getHeight(); j++) { + for (int i = 0; i < this->layout->getWidth(); i++) + for (int j = 0; j < this->layout->getHeight(); j++) { int destX = i + xDelta; int destY = j + yDelta; if (destX < 0) - do { destX += map->getWidth(); } while (destX < 0); + do { destX += this->layout->getWidth(); } while (destX < 0); if (destY < 0) - do { destY += map->getHeight(); } while (destY < 0); - destX %= map->getWidth(); - destY %= map->getHeight(); + do { destY += this->layout->getHeight(); } while (destY < 0); + destX %= this->layout->getWidth(); + destY %= this->layout->getHeight(); - int blockIndex = j * map->getWidth() + i; + int blockIndex = j * this->layout->getWidth() + i; Block srcBlock = oldMetatiles.at(blockIndex); - map->setBlock(destX, destY, srcBlock); + this->layout->setBlock(destX, destY, srcBlock); } - if (!fromScriptCall && map->layout->blockdata != oldMetatiles) { - map->editHistory.push(new ShiftMetatiles(map, oldMetatiles, map->layout->blockdata, actionId_)); + if (!fromScriptCall && this->layout->blockdata != oldMetatiles) { + this->layout->editHistory.push(new ShiftMetatiles(this->layout, oldMetatiles, this->layout->blockdata, actionId_)); Scripting::cb_MapShifted(xDelta, yDelta); } } -void MapPixmapItem::paintNormal(int x, int y, bool fromScriptCall) { +void LayoutPixmapItem::paintNormal(int x, int y, bool fromScriptCall) { MetatileSelection selection = this->metatileSelector->getMetatileSelection(); int initialX = fromScriptCall ? x : this->paint_tile_initial_x; int initialY = fromScriptCall ? y : this->paint_tile_initial_y; @@ -117,14 +117,14 @@ void MapPixmapItem::paintNormal(int x, int y, bool fromScriptCall) { y = initialY + (yDiff / selection.dimensions.y()) * selection.dimensions.y(); // for edit history - Blockdata oldMetatiles = !fromScriptCall ? map->layout->blockdata : Blockdata(); + Blockdata oldMetatiles = !fromScriptCall ? this->layout->blockdata : Blockdata(); - for (int i = 0; i < selection.dimensions.x() && i + x < map->getWidth(); i++) - for (int j = 0; j < selection.dimensions.y() && j + y < map->getHeight(); j++) { + for (int i = 0; i < selection.dimensions.x() && i + x < this->layout->getWidth(); i++) + for (int j = 0; j < selection.dimensions.y() && j + y < this->layout->getHeight(); j++) { int actualX = i + x; int actualY = j + y; Block block; - if (map->getBlock(actualX, actualY, &block)) { + if (this->layout->getBlock(actualX, actualY, &block)) { int index = j * selection.dimensions.x() + i; MetatileSelectionItem item = selection.metatileItems.at(index); if (!item.enabled) @@ -135,19 +135,19 @@ void MapPixmapItem::paintNormal(int x, int y, bool fromScriptCall) { block.setCollision(collisionItem.collision); block.setElevation(collisionItem.elevation); } - map->setBlock(actualX, actualY, block, !fromScriptCall); + this->layout->setBlock(actualX, actualY, block, !fromScriptCall); } } - if (!fromScriptCall && map->layout->blockdata != oldMetatiles) { - map->editHistory.push(new PaintMetatile(map, oldMetatiles, map->layout->blockdata, actionId_)); + if (!fromScriptCall && this->layout->blockdata != oldMetatiles) { + this->layout->editHistory.push(new PaintMetatile(this->layout, oldMetatiles, this->layout->blockdata, actionId_)); } } // These are tile offsets from the top-left tile in the 3x3 smart path selection. // Each entry is for one possibility from the marching squares value for a tile. // (Marching Squares: https://en.wikipedia.org/wiki/Marching_squares) -QList MapPixmapItem::smartPathTable = QList({ +QList LayoutPixmapItem::smartPathTable = QList({ 4, // 0000 4, // 0001 4, // 0010 @@ -189,7 +189,7 @@ bool isValidSmartPathSelection(MetatileSelection selection) { return true; } -void MapPixmapItem::paintSmartPath(int x, int y, bool fromScriptCall) { +void LayoutPixmapItem::paintSmartPath(int x, int y, bool fromScriptCall) { MetatileSelection selection = this->metatileSelector->getMetatileSelection(); if (!isValidSmartPathSelection(selection)) return; @@ -206,30 +206,30 @@ void MapPixmapItem::paintSmartPath(int x, int y, bool fromScriptCall) { } // for edit history - Blockdata oldMetatiles = !fromScriptCall ? map->layout->blockdata : Blockdata(); + Blockdata oldMetatiles = !fromScriptCall ? this->layout->blockdata : Blockdata(); // Fill the region with the open tile. for (int i = 0; i <= 1; i++) for (int j = 0; j <= 1; j++) { - if (!map->isWithinBounds(x + i, y + j)) + if (!this->layout->isWithinBounds(x + i, y + j)) continue; int actualX = i + x; int actualY = j + y; Block block; - if (map->getBlock(actualX, actualY, &block)) { + if (this->layout->getBlock(actualX, actualY, &block)) { block.setMetatileId(openMetatileId); if (setCollisions) { block.setCollision(openCollision); block.setElevation(openElevation); } - map->setBlock(actualX, actualY, block, !fromScriptCall); + this->layout->setBlock(actualX, actualY, block, !fromScriptCall); } } // Go back and resolve the edge tiles for (int i = -1; i <= 2; i++) for (int j = -1; j <= 2; j++) { - if (!map->isWithinBounds(x + i, y + j)) + if (!this->layout->isWithinBounds(x + i, y + j)) continue; // Ignore the corners, which can't possible be affected by the smart path. if ((i == -1 && j == -1) || (i == 2 && j == -1) || @@ -240,7 +240,7 @@ void MapPixmapItem::paintSmartPath(int x, int y, bool fromScriptCall) { int actualX = i + x; int actualY = j + y; Block block; - if (!map->getBlock(actualX, actualY, &block) || !isSmartPathTile(selection.metatileItems, block.metatileId())) { + if (!this->layout->getBlock(actualX, actualY, &block) || !isSmartPathTile(selection.metatileItems, block.metatileId())) { continue; } @@ -251,13 +251,13 @@ void MapPixmapItem::paintSmartPath(int x, int y, bool fromScriptCall) { Block left; // Get marching squares value, to determine which tile to use. - if (map->getBlock(actualX, actualY - 1, &top) && isSmartPathTile(selection.metatileItems, top.metatileId())) + if (this->layout->getBlock(actualX, actualY - 1, &top) && isSmartPathTile(selection.metatileItems, top.metatileId())) id += 1; - if (map->getBlock(actualX + 1, actualY, &right) && isSmartPathTile(selection.metatileItems, right.metatileId())) + if (this->layout->getBlock(actualX + 1, actualY, &right) && isSmartPathTile(selection.metatileItems, right.metatileId())) id += 2; - if (map->getBlock(actualX, actualY + 1, &bottom) && isSmartPathTile(selection.metatileItems, bottom.metatileId())) + if (this->layout->getBlock(actualX, actualY + 1, &bottom) && isSmartPathTile(selection.metatileItems, bottom.metatileId())) id += 4; - if (map->getBlock(actualX - 1, actualY, &left) && isSmartPathTile(selection.metatileItems, left.metatileId())) + if (this->layout->getBlock(actualX - 1, actualY, &left) && isSmartPathTile(selection.metatileItems, left.metatileId())) id += 8; block.setMetatileId(selection.metatileItems.at(smartPathTable[id]).metatileId); @@ -266,19 +266,19 @@ void MapPixmapItem::paintSmartPath(int x, int y, bool fromScriptCall) { block.setCollision(collisionItem.collision); block.setElevation(collisionItem.elevation); } - map->setBlock(actualX, actualY, block, !fromScriptCall); + this->layout->setBlock(actualX, actualY, block, !fromScriptCall); } - if (!fromScriptCall && map->layout->blockdata != oldMetatiles) { - map->editHistory.push(new PaintMetatile(map, oldMetatiles, map->layout->blockdata, actionId_)); + if (!fromScriptCall && this->layout->blockdata != oldMetatiles) { + this->layout->editHistory.push(new PaintMetatile(this->layout, oldMetatiles, this->layout->blockdata, actionId_)); } } -void MapPixmapItem::lockNondominantAxis(QGraphicsSceneMouseEvent *event) { +void LayoutPixmapItem::lockNondominantAxis(QGraphicsSceneMouseEvent *event) { /* Return if an axis is already locked, or if the mouse has been released. The mouse release check is necessary - * because MapPixmapItem::mouseReleaseEvent seems to get called before this function, which would unlock the axis + * because LayoutPixmapItem::mouseReleaseEvent seems to get called before this function, which would unlock the axis * and then get immediately re-locked here until the next ctrl-click. */ - if (this->lockedAxis != MapPixmapItem::Axis::None || event->type() == QEvent::GraphicsSceneMouseRelease) + if (this->lockedAxis != LayoutPixmapItem::Axis::None || event->type() == QEvent::GraphicsSceneMouseRelease) return; QPoint pos = Metatile::coordFromPixmapCoord(event->pos()); @@ -293,31 +293,31 @@ void MapPixmapItem::lockNondominantAxis(QGraphicsSceneMouseEvent *event) { int yDiff = pos.y() - this->straight_path_initial_y; if (xDiff || yDiff) { if (abs(xDiff) < abs(yDiff)) { - this->lockedAxis = MapPixmapItem::Axis::X; + this->lockedAxis = LayoutPixmapItem::Axis::X; } else { - this->lockedAxis = MapPixmapItem::Axis::Y; + this->lockedAxis = LayoutPixmapItem::Axis::Y; } } } // Adjust the cooresponding coordinate when it is locked -QPoint MapPixmapItem::adjustCoords(QPoint pos) { - if (this->lockedAxis == MapPixmapItem::Axis::X) { +QPoint LayoutPixmapItem::adjustCoords(QPoint pos) { + if (this->lockedAxis == LayoutPixmapItem::Axis::X) { pos.setX(this->straight_path_initial_x); - } else if (this->lockedAxis == MapPixmapItem::Axis::Y) { + } else if (this->lockedAxis == LayoutPixmapItem::Axis::Y) { pos.setY(this->straight_path_initial_y); } return pos; } -void MapPixmapItem::updateMetatileSelection(QGraphicsSceneMouseEvent *event) { +void LayoutPixmapItem::updateMetatileSelection(QGraphicsSceneMouseEvent *event) { QPoint pos = Metatile::coordFromPixmapCoord(event->pos()); - // Snap point to within map bounds. + // Snap point to within layout bounds. if (pos.x() < 0) pos.setX(0); - if (pos.x() >= map->getWidth()) pos.setX(map->getWidth() - 1); + if (pos.x() >= this->layout->getWidth()) pos.setX(this->layout->getWidth() - 1); if (pos.y() < 0) pos.setY(0); - if (pos.y() >= map->getHeight()) pos.setY(map->getHeight() - 1); + if (pos.y() >= this->layout->getHeight()) pos.setY(this->layout->getHeight() - 1); // Update/apply copied metatiles. if (event->type() == QEvent::GraphicsSceneMousePress) { @@ -325,7 +325,7 @@ void MapPixmapItem::updateMetatileSelection(QGraphicsSceneMouseEvent *event) { selection.clear(); selection.append(QPoint(pos.x(), pos.y())); Block block; - if (map->getBlock(pos.x(), pos.y(), &block)) { + if (this->layout->getBlock(pos.x(), pos.y(), &block)) { this->metatileSelector->selectFromMap(block.metatileId(), block.collision(), block.elevation()); } } else if (event->type() == QEvent::GraphicsSceneMouseMove) { @@ -348,11 +348,11 @@ void MapPixmapItem::updateMetatileSelection(QGraphicsSceneMouseEvent *event) { int x = point.x(); int y = point.y(); Block block; - if (map->getBlock(x, y, &block)) { + if (this->layout->getBlock(x, y, &block)) { metatiles.append(block.metatileId()); } - int blockIndex = y * map->getWidth() + x; - block = map->layout->blockdata.at(blockIndex); + int blockIndex = y * this->layout->getWidth() + x; + block = this->layout->blockdata.at(blockIndex); auto collision = block.collision(); auto elevation = block.elevation(); collisions.append(QPair(collision, elevation)); @@ -362,8 +362,8 @@ void MapPixmapItem::updateMetatileSelection(QGraphicsSceneMouseEvent *event) { } } -void MapPixmapItem::floodFill(QGraphicsSceneMouseEvent *event) { - if (map) { +void LayoutPixmapItem::floodFill(QGraphicsSceneMouseEvent *event) { + if (this->layout) { if (event->type() == QEvent::GraphicsSceneMouseRelease) { actionId_++; } else { @@ -371,7 +371,7 @@ void MapPixmapItem::floodFill(QGraphicsSceneMouseEvent *event) { Block block; MetatileSelection selection = this->metatileSelector->getMetatileSelection(); int metatileId = selection.metatileItems.first().metatileId; - if (selection.metatileItems.count() > 1 || (map->getBlock(pos.x(), pos.y(), &block) && block.metatileId() != metatileId)) { + if (selection.metatileItems.count() > 1 || (this->layout->getBlock(pos.x(), pos.y(), &block) && block.metatileId() != metatileId)) { bool smartPathsEnabled = event->modifiers() & Qt::ShiftModifier; if ((this->settings->smartPathsEnabled || smartPathsEnabled) && selection.dimensions.x() == 3 && selection.dimensions.y() == 3) this->floodFillSmartPath(pos.x(), pos.y()); @@ -382,8 +382,8 @@ void MapPixmapItem::floodFill(QGraphicsSceneMouseEvent *event) { } } -void MapPixmapItem::magicFill(QGraphicsSceneMouseEvent *event) { - if (map) { +void LayoutPixmapItem::magicFill(QGraphicsSceneMouseEvent *event) { + if (this->layout) { if (event->type() == QEvent::GraphicsSceneMouseRelease) { actionId_++; } else { @@ -393,18 +393,18 @@ void MapPixmapItem::magicFill(QGraphicsSceneMouseEvent *event) { } } -void MapPixmapItem::magicFill(int x, int y, uint16_t metatileId, bool fromScriptCall) { +void LayoutPixmapItem::magicFill(int x, int y, uint16_t metatileId, bool fromScriptCall) { QPoint selectionDimensions(1, 1); QList selectedMetatiles = QList({MetatileSelectionItem{ true, metatileId }}); this->magicFill(x, y, selectionDimensions, selectedMetatiles, QList(), fromScriptCall); } -void MapPixmapItem::magicFill(int x, int y, bool fromScriptCall) { +void LayoutPixmapItem::magicFill(int x, int y, bool fromScriptCall) { MetatileSelection selection = this->metatileSelector->getMetatileSelection(); this->magicFill(x, y, selection.dimensions, selection.metatileItems, selection.collisionItems, fromScriptCall); } -void MapPixmapItem::magicFill( +void LayoutPixmapItem::magicFill( int initialX, int initialY, QPoint selectionDimensions, @@ -412,18 +412,18 @@ void MapPixmapItem::magicFill( QList selectedCollisions, bool fromScriptCall) { Block block; - if (map->getBlock(initialX, initialY, &block)) { + if (this->layout->getBlock(initialX, initialY, &block)) { if (selectedMetatiles.length() == 1 && selectedMetatiles.at(0).metatileId == block.metatileId()) { return; } - Blockdata oldMetatiles = !fromScriptCall ? map->layout->blockdata : Blockdata(); + Blockdata oldMetatiles = !fromScriptCall ? this->layout->blockdata : Blockdata(); bool setCollisions = selectedCollisions.length() == selectedMetatiles.length(); uint16_t metatileId = block.metatileId(); - for (int y = 0; y < map->getHeight(); y++) { - for (int x = 0; x < map->getWidth(); x++) { - if (map->getBlock(x, y, &block) && block.metatileId() == metatileId) { + for (int y = 0; y < this->layout->getHeight(); y++) { + for (int x = 0; x < this->layout->getWidth(); x++) { + if (this->layout->getBlock(x, y, &block) && block.metatileId() == metatileId) { int xDiff = x - initialX; int yDiff = y - initialY; int i = xDiff % selectionDimensions.x(); @@ -438,30 +438,30 @@ void MapPixmapItem::magicFill( block.setCollision(item.collision); block.setElevation(item.elevation); } - map->setBlock(x, y, block, !fromScriptCall); + this->layout->setBlock(x, y, block, !fromScriptCall); } } } } - if (!fromScriptCall && map->layout->blockdata != oldMetatiles) { - map->editHistory.push(new MagicFillMetatile(map, oldMetatiles, map->layout->blockdata, actionId_)); + if (!fromScriptCall && this->layout->blockdata != oldMetatiles) { + this->layout->editHistory.push(new MagicFillMetatile(this->layout, oldMetatiles, this->layout->blockdata, actionId_)); } } } -void MapPixmapItem::floodFill(int initialX, int initialY, bool fromScriptCall) { +void LayoutPixmapItem::floodFill(int initialX, int initialY, bool fromScriptCall) { MetatileSelection selection = this->metatileSelector->getMetatileSelection(); this->floodFill(initialX, initialY, selection.dimensions, selection.metatileItems, selection.collisionItems, fromScriptCall); } -void MapPixmapItem::floodFill(int initialX, int initialY, uint16_t metatileId, bool fromScriptCall) { +void LayoutPixmapItem::floodFill(int initialX, int initialY, uint16_t metatileId, bool fromScriptCall) { QPoint selectionDimensions(1, 1); QList selectedMetatiles = QList({MetatileSelectionItem{true, metatileId}}); this->floodFill(initialX, initialY, selectionDimensions, selectedMetatiles, QList(), fromScriptCall); } -void MapPixmapItem::floodFill( +void LayoutPixmapItem::floodFill( int initialX, int initialY, QPoint selectionDimensions, @@ -469,7 +469,7 @@ void MapPixmapItem::floodFill( QList selectedCollisions, bool fromScriptCall) { bool setCollisions = selectedCollisions.length() == selectedMetatiles.length(); - Blockdata oldMetatiles = !fromScriptCall ? map->layout->blockdata : Blockdata(); + Blockdata oldMetatiles = !fromScriptCall ? this->layout->blockdata : Blockdata(); QSet visited; QList todo; @@ -479,11 +479,11 @@ void MapPixmapItem::floodFill( int x = point.x(); int y = point.y(); Block block; - if (!map->getBlock(x, y, &block)) { + if (!this->layout->getBlock(x, y, &block)) { continue; } - visited.insert(x + y * map->getWidth()); + visited.insert(x + y * this->layout->getWidth()); int xDiff = x - initialX; int yDiff = y - initialY; int i = xDiff % selectionDimensions.x(); @@ -500,32 +500,32 @@ void MapPixmapItem::floodFill( block.setCollision(item.collision); block.setElevation(item.elevation); } - map->setBlock(x, y, block, !fromScriptCall); + this->layout->setBlock(x, y, block, !fromScriptCall); } - if (!visited.contains(x + 1 + y * map->getWidth()) && map->getBlock(x + 1, y, &block) && block.metatileId() == old_metatileId) { + if (!visited.contains(x + 1 + y * this->layout->getWidth()) && this->layout->getBlock(x + 1, y, &block) && block.metatileId() == old_metatileId) { todo.append(QPoint(x + 1, y)); - visited.insert(x + 1 + y * map->getWidth()); + visited.insert(x + 1 + y * this->layout->getWidth()); } - if (!visited.contains(x - 1 + y * map->getWidth()) && map->getBlock(x - 1, y, &block) && block.metatileId() == old_metatileId) { + if (!visited.contains(x - 1 + y * this->layout->getWidth()) && this->layout->getBlock(x - 1, y, &block) && block.metatileId() == old_metatileId) { todo.append(QPoint(x - 1, y)); - visited.insert(x - 1 + y * map->getWidth()); + visited.insert(x - 1 + y * this->layout->getWidth()); } - if (!visited.contains(x + (y + 1) * map->getWidth()) && map->getBlock(x, y + 1, &block) && block.metatileId() == old_metatileId) { + if (!visited.contains(x + (y + 1) * this->layout->getWidth()) && this->layout->getBlock(x, y + 1, &block) && block.metatileId() == old_metatileId) { todo.append(QPoint(x, y + 1)); - visited.insert(x + (y + 1) * map->getWidth()); + visited.insert(x + (y + 1) * this->layout->getWidth()); } - if (!visited.contains(x + (y - 1) * map->getWidth()) && map->getBlock(x, y - 1, &block) && block.metatileId() == old_metatileId) { + if (!visited.contains(x + (y - 1) * this->layout->getWidth()) && this->layout->getBlock(x, y - 1, &block) && block.metatileId() == old_metatileId) { todo.append(QPoint(x, y - 1)); - visited.insert(x + (y - 1) * map->getWidth()); + visited.insert(x + (y - 1) * this->layout->getWidth()); } } - if (!fromScriptCall && map->layout->blockdata != oldMetatiles) { - map->editHistory.push(new BucketFillMetatile(map, oldMetatiles, map->layout->blockdata, actionId_)); + if (!fromScriptCall && this->layout->blockdata != oldMetatiles) { + this->layout->editHistory.push(new BucketFillMetatile(this->layout, oldMetatiles, this->layout->blockdata, actionId_)); } } -void MapPixmapItem::floodFillSmartPath(int initialX, int initialY, bool fromScriptCall) { +void LayoutPixmapItem::floodFillSmartPath(int initialX, int initialY, bool fromScriptCall) { MetatileSelection selection = this->metatileSelector->getMetatileSelection(); if (!isValidSmartPathSelection(selection)) return; @@ -542,7 +542,7 @@ void MapPixmapItem::floodFillSmartPath(int initialX, int initialY, bool fromScri setCollisions = true; } - Blockdata oldMetatiles = !fromScriptCall ? map->layout->blockdata : Blockdata(); + Blockdata oldMetatiles = !fromScriptCall ? this->layout->blockdata : Blockdata(); // Flood fill the region with the open tile. QList todo; @@ -552,7 +552,7 @@ void MapPixmapItem::floodFillSmartPath(int initialX, int initialY, bool fromScri int x = point.x(); int y = point.y(); Block block; - if (!map->getBlock(x, y, &block)) { + if (!this->layout->getBlock(x, y, &block)) { continue; } @@ -566,17 +566,17 @@ void MapPixmapItem::floodFillSmartPath(int initialX, int initialY, bool fromScri block.setCollision(openCollision); block.setElevation(openElevation); } - map->setBlock(x, y, block, !fromScriptCall); - if (map->getBlock(x + 1, y, &block) && block.metatileId() == old_metatileId) { + this->layout->setBlock(x, y, block, !fromScriptCall); + if (this->layout->getBlock(x + 1, y, &block) && block.metatileId() == old_metatileId) { todo.append(QPoint(x + 1, y)); } - if (map->getBlock(x - 1, y, &block) && block.metatileId() == old_metatileId) { + if (this->layout->getBlock(x - 1, y, &block) && block.metatileId() == old_metatileId) { todo.append(QPoint(x - 1, y)); } - if (map->getBlock(x, y + 1, &block) && block.metatileId() == old_metatileId) { + if (this->layout->getBlock(x, y + 1, &block) && block.metatileId() == old_metatileId) { todo.append(QPoint(x, y + 1)); } - if (map->getBlock(x, y - 1, &block) && block.metatileId() == old_metatileId) { + if (this->layout->getBlock(x, y - 1, &block) && block.metatileId() == old_metatileId) { todo.append(QPoint(x, y - 1)); } } @@ -590,11 +590,11 @@ void MapPixmapItem::floodFillSmartPath(int initialX, int initialY, bool fromScri int x = point.x(); int y = point.y(); Block block; - if (!map->getBlock(x, y, &block)) { + if (!this->layout->getBlock(x, y, &block)) { continue; } - visited.insert(x + y * map->getWidth()); + visited.insert(x + y * this->layout->getWidth()); int id = 0; Block top; Block right; @@ -602,13 +602,13 @@ void MapPixmapItem::floodFillSmartPath(int initialX, int initialY, bool fromScri Block left; // Get marching squares value, to determine which tile to use. - if (map->getBlock(x, y - 1, &top) && isSmartPathTile(selection.metatileItems, top.metatileId())) + if (this->layout->getBlock(x, y - 1, &top) && isSmartPathTile(selection.metatileItems, top.metatileId())) id += 1; - if (map->getBlock(x + 1, y, &right) && isSmartPathTile(selection.metatileItems, right.metatileId())) + if (this->layout->getBlock(x + 1, y, &right) && isSmartPathTile(selection.metatileItems, right.metatileId())) id += 2; - if (map->getBlock(x, y + 1, &bottom) && isSmartPathTile(selection.metatileItems, bottom.metatileId())) + if (this->layout->getBlock(x, y + 1, &bottom) && isSmartPathTile(selection.metatileItems, bottom.metatileId())) id += 4; - if (map->getBlock(x - 1, y, &left) && isSmartPathTile(selection.metatileItems, left.metatileId())) + if (this->layout->getBlock(x - 1, y, &left) && isSmartPathTile(selection.metatileItems, left.metatileId())) id += 8; block.setMetatileId(selection.metatileItems.at(smartPathTable[id]).metatileId); @@ -617,41 +617,41 @@ void MapPixmapItem::floodFillSmartPath(int initialX, int initialY, bool fromScri block.setCollision(item.collision); block.setElevation(item.elevation); } - map->setBlock(x, y, block, !fromScriptCall); + this->layout->setBlock(x, y, block, !fromScriptCall); // Visit neighbors if they are smart-path tiles, and don't revisit any. - if (!visited.contains(x + 1 + y * map->getWidth()) && map->getBlock(x + 1, y, &block) && isSmartPathTile(selection.metatileItems, block.metatileId())) { + if (!visited.contains(x + 1 + y * this->layout->getWidth()) && this->layout->getBlock(x + 1, y, &block) && isSmartPathTile(selection.metatileItems, block.metatileId())) { todo.append(QPoint(x + 1, y)); - visited.insert(x + 1 + y * map->getWidth()); + visited.insert(x + 1 + y * this->layout->getWidth()); } - if (!visited.contains(x - 1 + y * map->getWidth()) && map->getBlock(x - 1, y, &block) && isSmartPathTile(selection.metatileItems, block.metatileId())) { + if (!visited.contains(x - 1 + y * this->layout->getWidth()) && this->layout->getBlock(x - 1, y, &block) && isSmartPathTile(selection.metatileItems, block.metatileId())) { todo.append(QPoint(x - 1, y)); - visited.insert(x - 1 + y * map->getWidth()); + visited.insert(x - 1 + y * this->layout->getWidth()); } - if (!visited.contains(x + (y + 1) * map->getWidth()) && map->getBlock(x, y + 1, &block) && isSmartPathTile(selection.metatileItems, block.metatileId())) { + if (!visited.contains(x + (y + 1) * this->layout->getWidth()) && this->layout->getBlock(x, y + 1, &block) && isSmartPathTile(selection.metatileItems, block.metatileId())) { todo.append(QPoint(x, y + 1)); - visited.insert(x + (y + 1) * map->getWidth()); + visited.insert(x + (y + 1) * this->layout->getWidth()); } - if (!visited.contains(x + (y - 1) * map->getWidth()) && map->getBlock(x, y - 1, &block) && isSmartPathTile(selection.metatileItems, block.metatileId())) { + if (!visited.contains(x + (y - 1) * this->layout->getWidth()) && this->layout->getBlock(x, y - 1, &block) && isSmartPathTile(selection.metatileItems, block.metatileId())) { todo.append(QPoint(x, y - 1)); - visited.insert(x + (y - 1) * map->getWidth()); + visited.insert(x + (y - 1) * this->layout->getWidth()); } } - if (!fromScriptCall && map->layout->blockdata != oldMetatiles) { - map->editHistory.push(new BucketFillMetatile(map, oldMetatiles, map->layout->blockdata, actionId_)); + if (!fromScriptCall && this->layout->blockdata != oldMetatiles) { + this->layout->editHistory.push(new BucketFillMetatile(this->layout, oldMetatiles, this->layout->blockdata, actionId_)); } } -void MapPixmapItem::pick(QGraphicsSceneMouseEvent *event) { +void LayoutPixmapItem::pick(QGraphicsSceneMouseEvent *event) { QPoint pos = Metatile::coordFromPixmapCoord(event->pos()); Block block; - if (map->getBlock(pos.x(), pos.y(), &block)) { + if (this->layout->getBlock(pos.x(), pos.y(), &block)) { this->metatileSelector->selectFromMap(block.metatileId(), block.collision(), block.elevation()); } } -void MapPixmapItem::select(QGraphicsSceneMouseEvent *event) { +void LayoutPixmapItem::select(QGraphicsSceneMouseEvent *event) { QPoint pos = Metatile::coordFromPixmapCoord(event->pos()); if (event->type() == QEvent::GraphicsSceneMousePress) { selection_origin = QPoint(pos.x(), pos.y()); @@ -681,43 +681,47 @@ void MapPixmapItem::select(QGraphicsSceneMouseEvent *event) { } } -void MapPixmapItem::draw(bool ignoreCache) { - if (map) { - map->setMapItem(this); - setPixmap(map->render(ignoreCache)); +void LayoutPixmapItem::draw(bool ignoreCache) { + if (this->layout) { + layout->setLayoutItem(this); + setPixmap(this->layout->render(ignoreCache)); } } -void MapPixmapItem::hoverMoveEvent(QGraphicsSceneHoverEvent *event) { +void LayoutPixmapItem::hoverMoveEvent(QGraphicsSceneHoverEvent *event) { QPoint pos = Metatile::coordFromPixmapCoord(event->pos()); if (pos != this->metatilePos) { this->metatilePos = pos; emit this->hoveredMapMetatileChanged(pos); } - if (this->settings->betterCursors && this->paintingMode != MapPixmapItem::PaintMode::Disabled) { + if (this->settings->betterCursors && this->editsEnabled) { setCursor(this->settings->mapCursor); } } -void MapPixmapItem::hoverEnterEvent(QGraphicsSceneHoverEvent * event) { + +void LayoutPixmapItem::hoverEnterEvent(QGraphicsSceneHoverEvent * event) { this->has_mouse = true; QPoint pos = Metatile::coordFromPixmapCoord(event->pos()); emit this->hoveredMapMetatileChanged(pos); } -void MapPixmapItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *) { + +void LayoutPixmapItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *) { emit this->hoveredMapMetatileCleared(); - if (this->settings->betterCursors && this->paintingMode != MapPixmapItem::PaintMode::Disabled) { + if (this->settings->betterCursors && this->editsEnabled) { unsetCursor(); } this->has_mouse = false; } -void MapPixmapItem::mousePressEvent(QGraphicsSceneMouseEvent *event) { + +void LayoutPixmapItem::mousePressEvent(QGraphicsSceneMouseEvent *event) { QPoint pos = Metatile::coordFromPixmapCoord(event->pos()); this->paint_tile_initial_x = this->straight_path_initial_x = pos.x(); this->paint_tile_initial_y = this->straight_path_initial_y = pos.y(); emit startPaint(event, this); emit mouseEvent(event, this); } -void MapPixmapItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { + +void LayoutPixmapItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { QPoint pos = Metatile::coordFromPixmapCoord(event->pos()); if (pos != this->metatilePos) { this->metatilePos = pos; @@ -725,8 +729,9 @@ void MapPixmapItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { } emit mouseEvent(event, this); } -void MapPixmapItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { - this->lockedAxis = MapPixmapItem::Axis::None; + +void LayoutPixmapItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { + this->lockedAxis = LayoutPixmapItem::Axis::None; emit endPaint(event, this); emit mouseEvent(event, this); } diff --git a/src/ui/mapimageexporter.cpp b/src/ui/mapimageexporter.cpp index 3a4a46dbc..a7a266295 100644 --- a/src/ui/mapimageexporter.cpp +++ b/src/ui/mapimageexporter.cpp @@ -43,6 +43,7 @@ MapImageExporter::MapImageExporter(QWidget *parent_, Editor *editor_, ImageExpor this->setAttribute(Qt::WA_DeleteOnClose); ui->setupUi(this); this->map = editor_->map; + this->layout = editor_->layout; this->editor = editor_; this->mode = mode; this->setWindowTitle(getTitle(this->mode)); @@ -50,9 +51,11 @@ MapImageExporter::MapImageExporter(QWidget *parent_, Editor *editor_, ImageExpor this->ui->groupBox_Connections->setVisible(this->mode != ImageExporterMode::Stitch); this->ui->groupBox_Timelapse->setVisible(this->mode == ImageExporterMode::Timelapse); - this->ui->comboBox_MapSelection->addItems(editor->project->mapNames); - this->ui->comboBox_MapSelection->setCurrentText(map->name); - this->ui->comboBox_MapSelection->setEnabled(false);// TODO: allow selecting map from drop-down + if (this->map) { + this->ui->comboBox_MapSelection->addItems(editor->project->mapNames); + this->ui->comboBox_MapSelection->setCurrentText(map->name); + this->ui->comboBox_MapSelection->setEnabled(false);// TODO: allow selecting map from drop-down + } connect(ui->pushButton_Save, &QPushButton::pressed, this, &MapImageExporter::saveImage); connect(ui->pushButton_Cancel, &QPushButton::pressed, this, &MapImageExporter::close); @@ -87,13 +90,13 @@ void MapImageExporter::saveImage() { switch (this->mode) { case ImageExporterMode::Normal: - defaultFilename = map->name; + defaultFilename = this->map? this->map->name : this->layout->name; break; case ImageExporterMode::Stitch: - defaultFilename = QString("Stitch_From_%1").arg(map->name); + defaultFilename = QString("Stitch_From_%1").arg(this->map? this->map->name : this->layout->name); break; case ImageExporterMode::Timelapse: - defaultFilename = QString("Timelapse_%1").arg(map->name); + defaultFilename = QString("Timelapse_%1").arg(this->map? this->map->name : this->layout->name); break; } @@ -111,86 +114,100 @@ void MapImageExporter::saveImage() { this->preview.save(filepath); break; case ImageExporterMode::Timelapse: - QProgressDialog progress("Building map timelapse...", "Cancel", 0, 1, this); - progress.setAutoClose(true); - progress.setWindowModality(Qt::WindowModal); - progress.setModal(true); - progress.setMaximum(1); - progress.setValue(0); - - int maxWidth = this->map->getWidth() * 16; - int maxHeight = this->map->getHeight() * 16; - if (this->settings.showBorder) { - maxWidth += 2 * STITCH_MODE_BORDER_DISTANCE * 16; - maxHeight += 2 * STITCH_MODE_BORDER_DISTANCE * 16; - } - // Rewind to the specified start of the map edit history. - int i = 0; - while (this->map->editHistory.canUndo()) { - progress.setValue(i); - this->map->editHistory.undo(); - int width = this->map->getWidth() * 16; - int height = this->map->getHeight() * 16; - if (this->settings.showBorder) { - width += 2 * STITCH_MODE_BORDER_DISTANCE * 16; - height += 2 * STITCH_MODE_BORDER_DISTANCE * 16; - } - if (width > maxWidth) { - maxWidth = width; - } - if (height > maxHeight) { - maxHeight = height; - } - i++; - } - QGifImage timelapseImg(QSize(maxWidth, maxHeight)); + // Timelapse will play in order of layout changes then map changes (events) + // TODO: potentially update in the future? + QGifImage timelapseImg; timelapseImg.setDefaultDelay(this->settings.timelapseDelayMs); timelapseImg.setDefaultTransparentColor(QColor(0, 0, 0)); - // Draw each frame, skpping the specified number of map edits in - // the undo history. - progress.setMaximum(i); - while (i > 0) { - if (progress.wasCanceled()) { - progress.close(); - while (i > 0 && this->map->editHistory.canRedo()) { - i--; - this->map->editHistory.redo(); - } - return; - } - while (this->map->editHistory.canRedo() && - !historyItemAppliesToFrame(this->map->editHistory.command(this->map->editHistory.index()))) { - i--; - this->map->editHistory.redo(); + + // lambda to avoid redundancy + auto generateTimelapseFromHistory = [this, &timelapseImg](QString progressText, QUndoStack &historyStack){ + QProgressDialog progress(progressText, "Cancel", 0, 1, this); + progress.setAutoClose(true); + progress.setWindowModality(Qt::WindowModal); + progress.setModal(true); + progress.setMaximum(1); + progress.setValue(0); + + int maxWidth = this->layout->getWidth() * 16; + int maxHeight = this->layout->getHeight() * 16; + if (this->settings.showBorder) { + maxWidth += 2 * STITCH_MODE_BORDER_DISTANCE * 16; + maxHeight += 2 * STITCH_MODE_BORDER_DISTANCE * 16; } - progress.setValue(progress.maximum() - i); - QPixmap pixmap = this->getFormattedMapPixmap(this->map); - if (pixmap.width() < maxWidth || pixmap.height() < maxHeight) { - QPixmap pixmap2 = QPixmap(maxWidth, maxHeight); - QPainter painter(&pixmap2); - pixmap2.fill(QColor(0, 0, 0)); - painter.drawPixmap(0, 0, pixmap.width(), pixmap.height(), pixmap); - painter.end(); - pixmap = pixmap2; + // Rewind to the specified start of the map edit history. + int i = 0; + while (historyStack.canUndo()) { + progress.setValue(i); + historyStack.undo(); + int width = this->layout->getWidth() * 16; + int height = this->layout->getHeight() * 16; + if (this->settings.showBorder) { + width += 2 * STITCH_MODE_BORDER_DISTANCE * 16; + height += 2 * STITCH_MODE_BORDER_DISTANCE * 16; + } + if (width > maxWidth) { + maxWidth = width; + } + if (height > maxHeight) { + maxHeight = height; + } + i++; } - timelapseImg.addFrame(pixmap.toImage()); - for (int j = 0; j < this->settings.timelapseSkipAmount; j++) { - if (i > 0) { + + // Draw each frame, skpping the specified number of map edits in + // the undo history. + progress.setMaximum(i); + while (i > 0) { + if (progress.wasCanceled()) { + progress.close(); + while (i > 0 && historyStack.canRedo()) { + i--; + historyStack.redo(); + } + return; + } + while (historyStack.canRedo() && + !historyItemAppliesToFrame(historyStack.command(historyStack.index()))) { i--; - this->map->editHistory.redo(); - while (this->map->editHistory.canRedo() && - !historyItemAppliesToFrame(this->map->editHistory.command(this->map->editHistory.index()))) { + historyStack.redo(); + } + progress.setValue(progress.maximum() - i); + QPixmap pixmap = this->getFormattedMapPixmap(this->map); + if (pixmap.width() < maxWidth || pixmap.height() < maxHeight) { + QPixmap pixmap2 = QPixmap(maxWidth, maxHeight); + QPainter painter(&pixmap2); + pixmap2.fill(QColor(0, 0, 0)); + painter.drawPixmap(0, 0, pixmap.width(), pixmap.height(), pixmap); + painter.end(); + pixmap = pixmap2; + } + timelapseImg.addFrame(pixmap.toImage()); + for (int j = 0; j < this->settings.timelapseSkipAmount; j++) { + if (i > 0) { i--; - this->map->editHistory.redo(); + historyStack.redo(); + while (historyStack.canRedo() && + !historyItemAppliesToFrame(historyStack.command(historyStack.index()))) { + i--; + historyStack.redo(); + } } } } - } - // The latest map state is the last animated frame. - QPixmap pixmap = this->getFormattedMapPixmap(this->map); - timelapseImg.addFrame(pixmap.toImage()); + // The latest map state is the last animated frame. + QPixmap pixmap = this->getFormattedMapPixmap(this->map); + timelapseImg.addFrame(pixmap.toImage()); + progress.close(); + }; + + if (this->layout) + generateTimelapseFromHistory("Building layout timelapse...", this->layout->editHistory); + + if (this->map) + generateTimelapseFromHistory("Building map timelapse...", this->map->editHistory); + timelapseImg.save(filepath); - progress.close(); break; } this->close(); @@ -206,8 +223,8 @@ bool MapImageExporter::historyItemAppliesToFrame(const QUndoCommand *command) { case CommandId::ID_BucketFillMetatile: case CommandId::ID_MagicFillMetatile: case CommandId::ID_ShiftMetatiles: - case CommandId::ID_ResizeMap: - case CommandId::ID_ScriptEditMap: + case CommandId::ID_ResizeLayout: + case CommandId::ID_ScriptEditLayout: return true; case CommandId::ID_PaintCollision: case CommandId::ID_BucketFillCollision: @@ -403,18 +420,28 @@ void MapImageExporter::scalePreview() { } } +// THIS QPixmap MapImageExporter::getFormattedMapPixmap(Map *map, bool ignoreBorder) { QPixmap pixmap; + Layout *layout; + // draw background layer / base image - map->render(true); - pixmap = map->pixmap; + if (!this->map) { + layout = this->layout; + layout->render(true); + pixmap = layout->pixmap; + } else { + layout = map->layout; + map->layout->render(true); + pixmap = map->layout->pixmap; + } if (this->settings.showCollision) { QPainter collisionPainter(&pixmap); - map->renderCollision(true); + layout->renderCollision(true); collisionPainter.setOpacity(editor->collisionOpacity); - collisionPainter.drawPixmap(0, 0, map->collision_pixmap); + collisionPainter.drawPixmap(0, 0, layout->collision_pixmap); collisionPainter.end(); } @@ -423,16 +450,16 @@ QPixmap MapImageExporter::getFormattedMapPixmap(Map *map, bool ignoreBorder) { int borderHeight = 0, borderWidth = 0; if (!ignoreBorder && this->settings.showBorder) { int borderDistance = this->mode ? STITCH_MODE_BORDER_DISTANCE : BORDER_DISTANCE; - map->renderBorder(); - int borderHorzDist = editor->getBorderDrawDistance(map->getBorderWidth()); - int borderVertDist = editor->getBorderDrawDistance(map->getBorderHeight()); + layout->renderBorder(); + int borderHorzDist = editor->getBorderDrawDistance(layout->getBorderWidth()); + int borderVertDist = editor->getBorderDrawDistance(layout->getBorderHeight()); borderWidth = borderDistance * 16; borderHeight = borderDistance * 16; - QPixmap newPixmap = QPixmap(map->pixmap.width() + borderWidth * 2, map->pixmap.height() + borderHeight * 2); + QPixmap newPixmap = QPixmap(layout->pixmap.width() + borderWidth * 2, layout->pixmap.height() + borderHeight * 2); QPainter borderPainter(&newPixmap); - for (int y = borderDistance - borderVertDist; y < map->getHeight() + borderVertDist * 2; y += map->getBorderHeight()) { - for (int x = borderDistance - borderHorzDist; x < map->getWidth() + borderHorzDist * 2; x += map->getBorderWidth()) { - borderPainter.drawPixmap(x * 16, y * 16, map->layout->border_pixmap); + for (int y = borderDistance - borderVertDist; y < layout->getHeight() + borderVertDist * 2; y += layout->getBorderHeight()) { + for (int x = borderDistance - borderHorzDist; x < layout->getWidth() + borderHorzDist * 2; x += layout->getBorderWidth()) { + borderPainter.drawPixmap(x * 16, y * 16, layout->border_pixmap); } } borderPainter.drawImage(borderWidth, borderHeight, pixmap.toImage()); @@ -440,6 +467,10 @@ QPixmap MapImageExporter::getFormattedMapPixmap(Map *map, bool ignoreBorder) { pixmap = newPixmap; } + if (!this->map) { + return pixmap; + } + 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); diff --git a/src/ui/maplistmodels.cpp b/src/ui/maplistmodels.cpp new file mode 100644 index 000000000..a270736af --- /dev/null +++ b/src/ui/maplistmodels.cpp @@ -0,0 +1,692 @@ +#include "maplistmodels.h" + +#include +#include + +#include "project.h" +#include "filterchildrenproxymodel.h" + + + +void MapTree::removeSelected() { + while (!this->selectedIndexes().isEmpty()) { + QModelIndex i = this->selectedIndexes().takeLast(); + this->model()->removeRow(i.row(), i.parent()); + } +} + +void MapTree::keyPressEvent(QKeyEvent *event) { + if (event->key() == Qt::Key_Delete || event->key() == Qt::Key_Backspace) { + // Delete selected items in the tree + auto selectionModel = this->selectionModel(); + if (!selectionModel->hasSelection()) + return; + + auto model = static_cast(this->model()); + auto sourceModel = static_cast(model->sourceModel()); + + QModelIndexList selectedIndexes = selectionModel->selectedRows(); + QList persistentIndexes; + for (const auto &index : selectedIndexes) { + persistentIndexes.append(model->mapToSource(index)); + } + for (const auto &index : persistentIndexes) { + sourceModel->removeItemAt(index); + } + } else { + QWidget::keyPressEvent(event); + } +} + +void MapListModel::removeItemAt(const QModelIndex &index) { + QStandardItem *item = this->getItem(index)->child(index.row(), index.column()); + if (!item) + return; + + const QString type = item->data(MapListUserRoles::TypeRole).toString(); + if (type == "map_name") { + // TODO: No support for deleting maps + } else { + // TODO: Because there's no support for deleting maps we can only delete empty folders + if (!item->hasChildren()) { + this->removeItem(item); + } + } +} + + + +QWidget *GroupNameDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &, const QModelIndex &) const { + QLineEdit *editor = new QLineEdit(parent); + static const QRegularExpression expression("[A-Za-z_]+[\\w]*"); + editor->setPlaceholderText("gMapGroup_"); + editor->setValidator(new QRegularExpressionValidator(expression, parent)); + editor->setFrame(false); + return editor; +} + +void GroupNameDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const { + QString groupName = index.data(Qt::UserRole).toString(); + QLineEdit *le = static_cast(editor); + le->setText(groupName); +} + +void GroupNameDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const { + QLineEdit *le = static_cast(editor); + QString groupName = le->text(); + model->setData(index, groupName, Qt::UserRole); +} + +void GroupNameDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &) const { + editor->setGeometry(option.rect); +} + + + +MapGroupModel::MapGroupModel(Project *project, QObject *parent) : MapListModel(parent) { + this->project = project; + this->root = this->invisibleRootItem(); + + initialize(); +} + +Qt::DropActions MapGroupModel::supportedDropActions() const { + return Qt::MoveAction; +} + +QStringList MapGroupModel::mimeTypes() const { + QStringList types; + types << "application/porymap.mapgroupmodel.map" + << "application/porymap.mapgroupmodel.group" + << "application/porymap.mapgroupmodel.source.row" + << "application/porymap.mapgroupmodel.source.column"; + return types; +} + +QMimeData *MapGroupModel::mimeData(const QModelIndexList &indexes) const { + QMimeData *mimeData = QStandardItemModel::mimeData(indexes); + QByteArray encodedData; + + QDataStream stream(&encodedData, QIODevice::WriteOnly); + + // if dropping a selection containing a group(s) and map(s), clear all selection but first group. + for (const QModelIndex &index : indexes) { + if (index.isValid() && data(index, MapListUserRoles::TypeRole).toString() == "map_group") { + QString groupName = data(index, Qt::UserRole).toString(); + stream << groupName; + mimeData->setData("application/porymap.mapgroupmodel.group", encodedData); + mimeData->setData("application/porymap.mapgroupmodel.source.row", QByteArray::number(index.row())); + return mimeData; + } + } + + for (const QModelIndex &index : indexes) { + if (index.isValid()) { + QString mapName = data(index, Qt::UserRole).toString(); + stream << mapName; + } + } + + mimeData->setData("application/porymap.mapgroupmodel.map", encodedData); + return mimeData; +} + +bool MapGroupModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int, const QModelIndex &parentIndex) { + if (action == Qt::IgnoreAction) + return true; + + if (!parentIndex.isValid() && !data->hasFormat("application/porymap.mapgroupmodel.group")) + return false; + + int firstRow = 0; + + if (row != -1) { + firstRow = row; + } + else if (parentIndex.isValid()) { + firstRow = rowCount(parentIndex); + } + + if (data->hasFormat("application/porymap.mapgroupmodel.group")) { + if (parentIndex.row() != -1 || parentIndex.column() != -1) { + return false; + } + QByteArray encodedData = data->data("application/porymap.mapgroupmodel.group"); + QDataStream stream(&encodedData, QIODevice::ReadOnly); + QString groupName; + + while (!stream.atEnd()) { + stream >> groupName; + } + + this->insertRow(row, parentIndex); + + // copy children to new node + int sourceRow = data->data("application/porymap.mapgroupmodel.source.row").toInt(); + QModelIndex originIndex = this->index(sourceRow, 0); + QModelIndexList children; + QStringList mapsToMove; + for (int i = 0; i < this->rowCount(originIndex); ++i ) { + children << this->index( i, 0, originIndex); + mapsToMove << this->index( i, 0 , originIndex).data(Qt::UserRole).toString(); + } + + QModelIndex groupIndex = index(row, 0, parentIndex); + QStandardItem *groupItem = this->itemFromIndex(groupIndex); + createGroupItem(groupName, row, groupItem); + + for (QString mapName : mapsToMove) { + QStandardItem *mapItem = createMapItem(mapName); + groupItem->appendRow(mapItem); + } + } + else if (data->hasFormat("application/porymap.mapgroupmodel.map")) { + QByteArray encodedData = data->data("application/porymap.mapgroupmodel.map"); + QDataStream stream(&encodedData, QIODevice::ReadOnly); + QStringList droppedMaps; + int rowCount = 0; + + while (!stream.atEnd()) { + QString mapName; + stream >> mapName; + droppedMaps << mapName; + rowCount++; + } + + QStandardItem *groupItem = this->itemFromIndex(parentIndex); + if (groupItem->hasChildren()) { + this->insertRows(firstRow, rowCount, parentIndex); + for (QString mapName : droppedMaps) { + QModelIndex mapIndex = index(firstRow, 0, parentIndex); + QStandardItem *mapItem = this->itemFromIndex(mapIndex); + createMapItem(mapName, mapItem); + firstRow++; + } + } + // for whatever reason insertRows doesn't work as I expected with childless items + // so just append all the new maps instead + else { + for (QString mapName : droppedMaps) { + QStandardItem *mapItem = createMapItem(mapName); + groupItem->appendRow(mapItem); + firstRow++; + } + } + + } + + emit dragMoveCompleted(); + updateProject(); + + return true; +} + +void MapGroupModel::updateProject() { + if (!this->project) return; + + QStringList groupNames; + QMap mapGroups; + QList groupedMapNames; + QStringList mapNames; + + for (int g = 0; g < this->root->rowCount(); g++) { + QStandardItem *groupItem = this->item(g); + QString groupName = groupItem->data(Qt::UserRole).toString(); + groupNames.append(groupName); + mapGroups[groupName] = g; + QStringList mapsInGroup; + for (int m = 0; m < groupItem->rowCount(); m++) { + QStandardItem *mapItem = groupItem->child(m); + if (!mapItem) { + logError("An error occured while trying to apply updates to map group structure."); + return; + } + QString mapName = mapItem->data(Qt::UserRole).toString(); + mapsInGroup.append(mapName); + mapNames.append(mapName); + } + groupedMapNames.append(mapsInGroup); + } + + this->project->groupNames = groupNames; + this->project->mapGroups = mapGroups; + this->project->groupedMapNames = groupedMapNames; + this->project->mapNames = mapNames; + this->project->hasUnsavedDataChanges = true; +} + +QStandardItem *MapGroupModel::createGroupItem(QString groupName, int groupIndex, QStandardItem *group) { + if (!group) group = new QStandardItem; + group->setText(groupName); + group->setData(groupName, Qt::UserRole); + group->setData("map_group", MapListUserRoles::TypeRole); + group->setData(groupIndex, MapListUserRoles::GroupRole); + group->setFlags(Qt::ItemIsEnabled | Qt::ItemIsDropEnabled | Qt::ItemIsEditable | Qt::ItemIsDragEnabled | Qt::ItemIsSelectable); + this->groupItems.insert(groupName, group); + return group; +} + +QStandardItem *MapGroupModel::createMapItem(QString mapName, QStandardItem *map) { + if (!map) map = new QStandardItem; + map->setData(mapName, Qt::UserRole); + map->setData("map_name", MapListUserRoles::TypeRole); + map->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled); + this->mapItems[mapName] = map; + return map; +} + +QStandardItem *MapGroupModel::insertGroupItem(QString groupName) { + QStandardItem *group = createGroupItem(groupName, this->groupItems.size()); + this->root->appendRow(group); + this->updateProject(); + return group; +} + +void MapGroupModel::removeItem(QStandardItem *item) { + this->removeRow(item->row()); + this->updateProject(); +} + +QStandardItem *MapGroupModel::insertMapItem(QString mapName, QString groupName) { + QStandardItem *group = this->groupItems[groupName]; + if (!group) { + group = insertGroupItem(groupName); + } + QStandardItem *map = createMapItem(mapName); + group->appendRow(map); + return map; +} + +void MapGroupModel::initialize() { + this->groupItems.clear(); + this->mapItems.clear(); + for (int i = 0; i < this->project->groupNames.length(); i++) { + QString group_name = this->project->groupNames.value(i); + QStandardItem *group = createGroupItem(group_name, i); + root->appendRow(group); + QStringList names = this->project->groupedMapNames.value(i); + for (int j = 0; j < names.length(); j++) { + QString map_name = names.value(j); + QStandardItem *map = createMapItem(map_name); + group->appendRow(map); + } + } +} + +QStandardItem *MapGroupModel::getItem(const QModelIndex &index) const { + if (index.isValid()) { + QStandardItem *item = static_cast(index.internalPointer()); + if (item) + return item; + } + return this->root; +} + +QModelIndex MapGroupModel::indexOf(QString mapName) const { + if (this->mapItems.contains(mapName)) { + return this->mapItems[mapName]->index(); + } + return QModelIndex(); +} + +QVariant MapGroupModel::data(const QModelIndex &index, int role) const { + if (!index.isValid()) return QVariant(); + + int row = index.row(); + int col = index.column(); + + if (role == Qt::DecorationRole) { + static QIcon mapGrayIcon = QIcon(QStringLiteral(":/icons/map_grayed.ico")); + static QIcon mapIcon = QIcon(QStringLiteral(":/icons/map.ico")); + static QIcon mapEditedIcon = QIcon(QStringLiteral(":/icons/map_edited.ico")); + static QIcon mapOpenedIcon = QIcon(QStringLiteral(":/icons/map_opened.ico")); + + static QIcon mapFolderIcon; + static QIcon folderIcon; + static bool loaded = false; + if (!loaded) { + mapFolderIcon.addFile(QStringLiteral(":/icons/folder_closed_map.ico"), QSize(), QIcon::Normal, QIcon::Off); + mapFolderIcon.addFile(QStringLiteral(":/icons/folder_map.ico"), QSize(), QIcon::Normal, QIcon::On); + folderIcon.addFile(QStringLiteral(":/icons/folder_closed.ico"), QSize(), QIcon::Normal, QIcon::Off); + folderIcon.addFile(QStringLiteral(":/icons/folder.ico"), QSize(), QIcon::Normal, QIcon::On); + loaded = true; + } + + QStandardItem *item = this->getItem(index)->child(row, col); + QString type = item->data(MapListUserRoles::TypeRole).toString(); + + if (type == "map_group") { + if (!item->hasChildren()) { + return folderIcon; + } + return mapFolderIcon; + } else if (type == "map_name") { + QString mapName = item->data(Qt::UserRole).toString(); + if (mapName == this->openMap) { + return mapOpenedIcon; + } + else if (this->project->mapCache.contains(mapName)) { + if (this->project->mapCache.value(mapName)->hasUnsavedChanges()) { + return mapEditedIcon; + } + else { + return mapIcon; + } + } + return mapGrayIcon; + } + } + else if (role == Qt::DisplayRole) { + QStandardItem *item = this->getItem(index)->child(row, col); + QString type = item->data(MapListUserRoles::TypeRole).toString(); + + if (type == "map_name") { + return QString("[%1.%2] ").arg(this->getItem(index)->row()).arg(row, 2, 10, QLatin1Char('0')) + item->data(Qt::UserRole).toString(); + } + else if (type == "map_group") { + return item->data(Qt::UserRole).toString(); + } + } + + return QStandardItemModel::data(index, role); +} + +bool MapGroupModel::setData(const QModelIndex &index, const QVariant &value, int role) { + if (role == Qt::UserRole && data(index, MapListUserRoles::TypeRole).toString() == "map_group") { + // verify uniqueness of new group name + if (this->project->groupNames.contains(value.toString())) { + return false; + } + } + if (QStandardItemModel::setData(index, value, role)) { + this->updateProject(); + } + return true; +} + + + +MapAreaModel::MapAreaModel(Project *project, QObject *parent) : MapListModel(parent) { + this->project = project; + this->root = this->invisibleRootItem(); + + initialize(); +} + +QStandardItem *MapAreaModel::createAreaItem(QString mapsecName) { + QStandardItem *area = new QStandardItem; + area->setText(mapsecName); + area->setEditable(false); + area->setData(mapsecName, Qt::UserRole); + area->setData("map_section", MapListUserRoles::TypeRole); + // group->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled); + this->areaItems.insert(mapsecName, area); + return area; +} + +QStandardItem *MapAreaModel::createMapItem(QString mapName, int, int) { + QStandardItem *map = new QStandardItem; + map->setText(mapName); + map->setEditable(false); + map->setData(mapName, Qt::UserRole); + map->setData("map_name", MapListUserRoles::TypeRole); + // map->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled); + this->mapItems.insert(mapName, map); + return map; +} + +QStandardItem *MapAreaModel::insertAreaItem(QString areaName) { + this->project->addNewMapsec(areaName); + QStandardItem *item = createAreaItem(areaName); + this->root->appendRow(item); + this->sort(0, Qt::AscendingOrder); + return item; +} + +QStandardItem *MapAreaModel::insertMapItem(QString mapName, QString areaName, int groupIndex) { + QStandardItem *area = this->areaItems[areaName]; + if (!area) { + return nullptr; + } + int mapIndex = area->rowCount(); + QStandardItem *map = createMapItem(mapName, groupIndex, mapIndex); + area->appendRow(map); + return map; +} + +void MapAreaModel::removeItem(QStandardItem *item) { + this->project->removeMapsec(item->data(Qt::UserRole).toString()); + this->removeRow(item->row()); +} + +void MapAreaModel::initialize() { + this->areaItems.clear(); + this->mapItems.clear(); + + for (const auto &idName : this->project->mapSectionIdNames) { + this->root->appendRow(createAreaItem(idName)); + } + + for (int i = 0; i < this->project->groupNames.length(); i++) { + QStringList names = this->project->groupedMapNames.value(i); + for (int j = 0; j < names.length(); j++) { + QString mapName = names.value(j); + QStandardItem *map = createMapItem(mapName, i, j); + QString mapsecName = this->project->readMapLocation(mapName); + if (this->areaItems.contains(mapsecName)) { + this->areaItems[mapsecName]->appendRow(map); + } + } + } + this->sort(0, Qt::AscendingOrder); +} + +QStandardItem *MapAreaModel::getItem(const QModelIndex &index) const { + if (index.isValid()) { + QStandardItem *item = static_cast(index.internalPointer()); + if (item) + return item; + } + return this->root; +} + +QModelIndex MapAreaModel::indexOf(QString mapName) const { + if (this->mapItems.contains(mapName)) { + return this->mapItems[mapName]->index(); + } + return QModelIndex(); +} + +QVariant MapAreaModel::data(const QModelIndex &index, int role) const { + if (!index.isValid()) return QVariant(); + + int row = index.row(); + int col = index.column(); + + if (role == Qt::DecorationRole) { + static QIcon mapGrayIcon = QIcon(QStringLiteral(":/icons/map_grayed.ico")); + static QIcon mapIcon = QIcon(QStringLiteral(":/icons/map.ico")); + static QIcon mapEditedIcon = QIcon(QStringLiteral(":/icons/map_edited.ico")); + static QIcon mapOpenedIcon = QIcon(QStringLiteral(":/icons/map_opened.ico")); + + static QIcon mapFolderIcon; + static QIcon folderIcon; + static bool loaded = false; + if (!loaded) { + mapFolderIcon.addFile(QStringLiteral(":/icons/folder_closed_map.ico"), QSize(), QIcon::Normal, QIcon::Off); + mapFolderIcon.addFile(QStringLiteral(":/icons/folder_map.ico"), QSize(), QIcon::Normal, QIcon::On); + folderIcon.addFile(QStringLiteral(":/icons/folder_closed.ico"), QSize(), QIcon::Normal, QIcon::Off); + folderIcon.addFile(QStringLiteral(":/icons/folder.ico"), QSize(), QIcon::Normal, QIcon::On); + loaded = true; + } + + QStandardItem *item = this->getItem(index)->child(row, col); + QString type = item->data(MapListUserRoles::TypeRole).toString(); + + if (type == "map_section") { + if (item->hasChildren()) { + return mapFolderIcon; + } + return folderIcon; + } else if (type == "map_name") { + QString mapName = item->data(Qt::UserRole).toString(); + if (mapName == this->openMap) { + return mapOpenedIcon; + } + else if (this->project->mapCache.contains(mapName)) { + if (this->project->mapCache.value(mapName)->hasUnsavedChanges()) { + return mapEditedIcon; + } + else { + return mapIcon; + } + } + return mapGrayIcon; + } + } + else if (role == Qt::DisplayRole) { + QStandardItem *item = this->getItem(index)->child(row, col); + QString type = item->data(MapListUserRoles::TypeRole).toString(); + + if (type == "map_section") { + return item->data(Qt::UserRole).toString(); + } + } + + return QStandardItemModel::data(index, role); +} + + + +LayoutTreeModel::LayoutTreeModel(Project *project, QObject *parent) : MapListModel(parent) { + this->project = project; + this->root = this->invisibleRootItem(); + + initialize(); +} + +QStandardItem *LayoutTreeModel::createLayoutItem(QString layoutId) { + QStandardItem *layout = new QStandardItem; + layout->setText(this->project->layoutIdsToNames[layoutId]); + layout->setEditable(false); + layout->setData(layoutId, Qt::UserRole); + layout->setData("map_layout", MapListUserRoles::TypeRole); + // // group->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled); + this->layoutItems.insert(layoutId, layout); + return layout; +} + +QStandardItem *LayoutTreeModel::createMapItem(QString mapName) { + QStandardItem *map = new QStandardItem; + map->setText(mapName); + map->setEditable(false); + map->setData(mapName, Qt::UserRole); + map->setData("map_name", MapListUserRoles::TypeRole); + map->setFlags(Qt::NoItemFlags | Qt::ItemNeverHasChildren); + this->mapItems.insert(mapName, map); + return map; +} + +QStandardItem *LayoutTreeModel::insertLayoutItem(QString layoutId) { + QStandardItem *layoutItem = this->createLayoutItem(layoutId); + this->root->appendRow(layoutItem); + this->sort(0, Qt::AscendingOrder); + return layoutItem; +} + +QStandardItem *LayoutTreeModel::insertMapItem(QString mapName, QString layoutId) { + QStandardItem *layout = nullptr; + if (this->layoutItems.contains(layoutId)) { + layout = this->layoutItems[layoutId]; + } + else { + layout = createLayoutItem(layoutId); + this->root->appendRow(layout); + } + if (!layout) { + return nullptr; + } + QStandardItem *map = createMapItem(mapName); + layout->appendRow(map); + return map; +} + +void LayoutTreeModel::removeItem(QStandardItem *) { + // TODO: Deleting layouts not supported +} + + +void LayoutTreeModel::initialize() { + this->layoutItems.clear(); + this->mapItems.clear(); + for (int i = 0; i < this->project->mapLayoutsTable.length(); i++) { + QString layoutId = project->mapLayoutsTable.value(i); + QStandardItem *layoutItem = createLayoutItem(layoutId); + this->root->appendRow(layoutItem); + } + + for (auto mapList : this->project->groupedMapNames) { + for (auto mapName : mapList) { + QString layoutId = project->readMapLayoutId(mapName); + QStandardItem *map = createMapItem(mapName); + this->layoutItems[layoutId]->appendRow(map); + } + } + this->sort(0, Qt::AscendingOrder); +} + +QStandardItem *LayoutTreeModel::getItem(const QModelIndex &index) const { + if (index.isValid()) { + QStandardItem *item = static_cast(index.internalPointer()); + if (item) + return item; + } + return this->root; +} + +QModelIndex LayoutTreeModel::indexOf(QString layoutName) const { + if (this->layoutItems.contains(layoutName)) { + return this->layoutItems[layoutName]->index(); + } + return QModelIndex(); +} + +QVariant LayoutTreeModel::data(const QModelIndex &index, int role) const { + if (!index.isValid()) return QVariant(); + + int row = index.row(); + int col = index.column(); + + if (role == Qt::DecorationRole) { + static QIcon mapGrayIcon = QIcon(QStringLiteral(":/icons/map_grayed.ico")); + static QIcon mapIcon = QIcon(QStringLiteral(":/icons/map.ico")); + static QIcon mapEditedIcon = QIcon(QStringLiteral(":/icons/map_edited.ico")); + static QIcon mapOpenedIcon = QIcon(QStringLiteral(":/icons/map_opened.ico")); + + QStandardItem *item = this->getItem(index)->child(row, col); + QString type = item->data(MapListUserRoles::TypeRole).toString(); + + if (type == "map_layout") { + QString layoutId = item->data(Qt::UserRole).toString(); + if (layoutId == this->openLayout) { + return mapOpenedIcon; + } + else if (this->project->mapLayouts.contains(layoutId)) { + if (this->project->mapLayouts.value(layoutId)->hasUnsavedChanges()) { + return mapEditedIcon; + } + else if (!this->project->mapLayouts[layoutId]->loaded) { + return mapGrayIcon; + } + } + return mapIcon; + } + else if (type == "map_name") { + return QVariant(); + } + + return QVariant(); + } + + return QStandardItemModel::data(index, role); +} diff --git a/src/ui/maplisttoolbar.cpp b/src/ui/maplisttoolbar.cpp new file mode 100644 index 000000000..d03aa8384 --- /dev/null +++ b/src/ui/maplisttoolbar.cpp @@ -0,0 +1,141 @@ +#include "maplisttoolbar.h" +#include "ui_maplisttoolbar.h" +#include "editor.h" + +#include + +/* + TODO: The button states for each tool bar (just the two toggleable buttons, hide empty folders and allow editing) + should be saved in the config. This will be cleaner/easier once the config is JSON, so holding off on that for now. +*/ + +MapListToolBar::MapListToolBar(QWidget *parent) + : QFrame(parent) + , ui(new Ui::MapListToolBar) +{ + ui->setupUi(this); + + ui->button_ToggleEmptyFolders->setChecked(!m_emptyFoldersVisible); + ui->button_ToggleEdit->setChecked(m_editsAllowed); + + connect(ui->button_AddFolder, &QAbstractButton::clicked, this, &MapListToolBar::addFolderClicked); // TODO: Tool tip + connect(ui->button_ExpandAll, &QAbstractButton::clicked, this, &MapListToolBar::expandList); + connect(ui->button_CollapseAll, &QAbstractButton::clicked, this, &MapListToolBar::collapseList); + connect(ui->button_ToggleEdit, &QAbstractButton::clicked, this, &MapListToolBar::toggleEditsAllowed); + connect(ui->lineEdit_filterBox, &QLineEdit::textChanged, this, &MapListToolBar::applyFilter); + connect(ui->button_ToggleEmptyFolders, &QAbstractButton::clicked, [this] { + toggleEmptyFolders(); + + // Display message to let user know what just happened (if there are no empty folders visible it's not obvious). + const QString message = QString("%1 empty folders!").arg(m_emptyFoldersVisible ? "Showing" : "Hiding"); + QToolTip::showText(ui->button_ToggleEmptyFolders->mapToGlobal(QPoint(0, 0)), message); + }); +} + +MapListToolBar::~MapListToolBar() +{ + delete ui; +} + +void MapListToolBar::setList(MapTree *list) { + m_list = list; + + // Sync list with current settings + setEditsAllowed(m_editsAllowed); + setEmptyFoldersVisible(m_emptyFoldersVisible); +} + +void MapListToolBar::setEditsAllowedButtonVisible(bool visible) { + ui->button_ToggleEdit->setVisible(visible); +} + +void MapListToolBar::toggleEditsAllowed() { + if (m_list) { + m_list->clearSelection(); + } + setEditsAllowed(!m_editsAllowed); +} + +void MapListToolBar::setEditsAllowed(bool allowed) { + m_editsAllowed = allowed; + + const QSignalBlocker b(ui->button_ToggleEdit); + ui->button_ToggleEdit->setChecked(allowed); + + if (!m_list) + return; + + if (allowed) { + m_list->setSelectionMode(QAbstractItemView::ExtendedSelection); + m_list->setDragEnabled(true); + m_list->setAcceptDrops(true); + m_list->setDropIndicatorShown(true); + m_list->setDragDropMode(QAbstractItemView::InternalMove); + m_list->setEditTriggers(QAbstractItemView::DoubleClicked | QAbstractItemView::EditKeyPressed); + } else { + m_list->setSelectionMode(QAbstractItemView::NoSelection); + m_list->setDragEnabled(false); + m_list->setAcceptDrops(false); + m_list->setDropIndicatorShown(false); + m_list->setDragDropMode(QAbstractItemView::NoDragDrop); + m_list->setEditTriggers(QAbstractItemView::NoEditTriggers); + } +} + +void MapListToolBar::toggleEmptyFolders() { + setEmptyFoldersVisible(!m_emptyFoldersVisible); +} + +void MapListToolBar::setEmptyFoldersVisible(bool visible) { + m_emptyFoldersVisible = visible; + + if (m_list) { + auto model = static_cast(m_list->model()); + if (model) { + model->setHideEmpty(!visible); + model->setFilterRegularExpression(ui->lineEdit_filterBox->text()); + } + } + + // Update tool tip to reflect what will happen if the button is pressed. + const QString toolTip = QString("%1 empty folders in the list.").arg(visible ? "Hide" : "Show"); + ui->button_ToggleEmptyFolders->setToolTip(toolTip); + + const QSignalBlocker b(ui->button_ToggleEmptyFolders); + ui->button_ToggleEmptyFolders->setChecked(!visible); +} + +void MapListToolBar::expandList() { + if (m_list) + m_list->expandToDepth(0); +} + +void MapListToolBar::collapseList() { + if (m_list) { + m_list->collapseAll(); + } +} + +void MapListToolBar::applyFilter(const QString &filterText) { + if (m_filterLocked) + return; + + const QSignalBlocker b(ui->lineEdit_filterBox); + ui->lineEdit_filterBox->setText(filterText); + + if (m_list) { + auto model = static_cast(m_list->model()); + if (model) model->setFilterRegularExpression(QRegularExpression(filterText, QRegularExpression::CaseInsensitiveOption)); + + if (filterText.isEmpty()) { + m_list->collapseAll(); + emit filterCleared(m_list); + } else { + m_list->expandToDepth(0); + } + } +} + +void MapListToolBar::clearFilter() { + applyFilter(""); +} diff --git a/src/ui/mapsceneeventfilter.cpp b/src/ui/mapsceneeventfilter.cpp deleted file mode 100644 index f8ae14cb5..000000000 --- a/src/ui/mapsceneeventfilter.cpp +++ /dev/null @@ -1,23 +0,0 @@ -#include "mapsceneeventfilter.h" -#include -#include - -MapSceneEventFilter::MapSceneEventFilter(QObject *parent) : QObject(parent) -{ - -} - -bool MapSceneEventFilter::eventFilter(QObject*, QEvent *event) -{ - if (event->type() == QEvent::GraphicsSceneWheel) - { - QGraphicsSceneWheelEvent *wheelEvent = static_cast(event); - if (wheelEvent->modifiers() & Qt::ControlModifier) - { - emit wheelZoom(wheelEvent->delta() > 0 ? 1 : -1); - event->accept(); - return true; - } - } - return false; -} diff --git a/src/ui/metatileselector.cpp b/src/ui/metatileselector.cpp index eca6462df..1214e2894 100644 --- a/src/ui/metatileselector.cpp +++ b/src/ui/metatileselector.cpp @@ -28,7 +28,7 @@ void MetatileSelector::draw() { if (i >= primaryLength) { tile += Project::getNumMetatilesPrimary() - primaryLength; } - QImage metatile_image = getMetatileImage(tile, this->primaryTileset, this->secondaryTileset, map->metatileLayerOrder, map->metatileLayerOpacity); + QImage metatile_image = getMetatileImage(tile, this->primaryTileset, this->secondaryTileset, layout->metatileLayerOrder, layout->metatileLayerOpacity); int map_y = i / this->numMetatilesWide; int map_x = i % this->numMetatilesWide; QPoint metatile_origin = QPoint(map_x * 16, map_y * 16); @@ -226,6 +226,6 @@ QPoint MetatileSelector::getMetatileIdCoordsOnWidget(uint16_t metatileId) { return pos; } -void MetatileSelector::setMap(Map *map) { - this->map = map; +void MetatileSelector::setLayout(Layout *layout) { + this->layout = layout; } diff --git a/src/ui/montabwidget.cpp b/src/ui/montabwidget.cpp index 74c52dc5d..7b196c75e 100644 --- a/src/ui/montabwidget.cpp +++ b/src/ui/montabwidget.cpp @@ -3,6 +3,7 @@ #include "editor.h" #include "encountertablemodel.h" #include "encountertabledelegates.h" +#include "eventfilters.h" @@ -11,20 +12,13 @@ static WildMonInfo encounterClipboard; MonTabWidget::MonTabWidget(Editor *editor, QWidget *parent) : QTabWidget(parent) { this->editor = editor; populate(); - this->tabBar()->installEventFilter(this); + this->tabBar()->installEventFilter(new WheelFilter(this)); } MonTabWidget::~MonTabWidget() { } -bool MonTabWidget::eventFilter(QObject *, QEvent *event) { - if (event->type() == QEvent::Wheel) { - return true; - } - return false; -} - void MonTabWidget::populate() { EncounterFields fields = editor->project->wildMonFields; activeTabs.resize(fields.size()); diff --git a/src/ui/newmappopup.cpp b/src/ui/newmappopup.cpp index be6499e62..def485c2a 100644 --- a/src/ui/newmappopup.cpp +++ b/src/ui/newmappopup.cpp @@ -8,6 +8,8 @@ #include #include +// TODO: Convert to modal dialog (among other things, this means we wouldn't need to worry about changes to the map list while this is open) + struct NewMapPopup::Settings NewMapPopup::settings = {}; NewMapPopup::NewMapPopup(QWidget *parent, Project *project) : @@ -27,14 +29,18 @@ NewMapPopup::~NewMapPopup() delete ui; } -void NewMapPopup::init() { +void NewMapPopup::initUi() { // Populate combo boxes ui->comboBox_NewMap_Primary_Tileset->addItems(project->primaryTilesetLabels); ui->comboBox_NewMap_Secondary_Tileset->addItems(project->secondaryTilesetLabels); ui->comboBox_NewMap_Group->addItems(project->groupNames); ui->comboBox_NewMap_Song->addItems(project->songNames); ui->comboBox_NewMap_Type->addItems(project->mapTypes); - ui->comboBox_NewMap_Location->addItems(project->mapSectionNameToValue.keys()); + ui->comboBox_NewMap_Location->addItems(project->mapSectionIdNames); + + const QSignalBlocker b(ui->comboBox_Layout); + ui->comboBox_Layout->addItems(project->mapLayoutsTable); + this->layoutId = project->mapLayoutsTable.first(); // Set spin box limits ui->spinBox_NewMap_Width->setMinimum(1); @@ -67,6 +73,10 @@ void NewMapPopup::init() { ui->spinBox_NewMap_Floor_Number->setVisible(hasFloorNumber); ui->label_NewMap_Floor_Number->setVisible(hasFloorNumber); + this->updateGeometry(); +} + +void NewMapPopup::init() { // Restore previous settings ui->lineEdit_NewMap_Name->setText(project->getNewMapName()); ui->comboBox_NewMap_Group->setTextItem(settings.group); @@ -94,29 +104,32 @@ void NewMapPopup::init() { } // Creating new map by right-clicking in the map list -void NewMapPopup::init(MapSortOrder type, QVariant data) { - switch (type) +void NewMapPopup::init(int tabIndex, QString fieldName) { + initUi(); + switch (tabIndex) { - case MapSortOrder::Group: - settings.group = project->groupNames.at(data.toInt()); + case MapListTab::Groups: + settings.group = fieldName; break; - case MapSortOrder::Area: - settings.location = data.toString(); + case MapListTab::Areas: + settings.location = fieldName; break; - case MapSortOrder::Layout: - useLayout(data.toString()); + case MapListTab::Layouts: + this->ui->checkBox_UseExistingLayout->setCheckState(Qt::Checked); + useLayout(fieldName); break; } init(); } // Creating new map from AdvanceMap import -void NewMapPopup::init(MapLayout *mapLayout) { +void NewMapPopup::init(Layout *mapLayout) { + initUi(); this->importedMap = true; useLayoutSettings(mapLayout); this->map = new Map(); - this->map->layout = new MapLayout(); + this->map->layout = new Layout(); this->map->layout->blockdata = mapLayout->blockdata; if (!mapLayout->border.isEmpty()) { @@ -175,7 +188,7 @@ void NewMapPopup::setDefaultSettings(Project *project) { settings.primaryTilesetLabel = project->getDefaultPrimaryTilesetLabel(); settings.secondaryTilesetLabel = project->getDefaultSecondaryTilesetLabel(); settings.type = project->mapTypes.value(0, "0"); - settings.location = project->mapSectionNameToValue.keys().value(0, "0"); + settings.location = project->mapSectionIdNames.value(0, "0"); settings.song = project->defaultSong; settings.canFlyTo = false; settings.showLocationName = true; @@ -204,28 +217,60 @@ void NewMapPopup::saveSettings() { settings.floorNumber = ui->spinBox_NewMap_Floor_Number->value(); } -void NewMapPopup::useLayoutSettings(MapLayout *layout) { +void NewMapPopup::useLayoutSettings(Layout *layout) { if (!layout) return; + settings.width = layout->width; + ui->spinBox_NewMap_Width->setValue(layout->width); + settings.height = layout->height; + ui->spinBox_NewMap_Height->setValue(layout->height); + settings.borderWidth = layout->border_width; + ui->spinBox_NewMap_BorderWidth->setValue(layout->border_width); + settings.borderHeight = layout->border_height; + ui->spinBox_NewMap_BorderWidth->setValue(layout->border_height); + settings.primaryTilesetLabel = layout->tileset_primary_label; + ui->comboBox_NewMap_Primary_Tileset->setCurrentIndex(ui->comboBox_NewMap_Primary_Tileset->findText(layout->tileset_primary_label)); + settings.secondaryTilesetLabel = layout->tileset_secondary_label; + ui->comboBox_NewMap_Secondary_Tileset->setCurrentIndex(ui->comboBox_NewMap_Secondary_Tileset->findText(layout->tileset_secondary_label)); } void NewMapPopup::useLayout(QString layoutId) { this->existingLayout = true; this->layoutId = layoutId; + + this->ui->comboBox_Layout->setCurrentIndex(this->ui->comboBox_Layout->findText(layoutId)); + useLayoutSettings(project->mapLayouts.value(this->layoutId)); +} + +void NewMapPopup::on_checkBox_UseExistingLayout_stateChanged(int state) { + bool layoutEditsEnabled = (state == Qt::Unchecked); + + this->ui->comboBox_Layout->setEnabled(!layoutEditsEnabled); + + this->ui->spinBox_NewMap_Width->setEnabled(layoutEditsEnabled); + this->ui->spinBox_NewMap_Height->setEnabled(layoutEditsEnabled); + this->ui->spinBox_NewMap_BorderWidth->setEnabled(layoutEditsEnabled); + this->ui->spinBox_NewMap_BorderWidth->setEnabled(layoutEditsEnabled); + this->ui->comboBox_NewMap_Primary_Tileset->setEnabled(layoutEditsEnabled); + this->ui->comboBox_NewMap_Secondary_Tileset->setEnabled(layoutEditsEnabled); - // Dimensions and tilesets can't be changed for new maps using an existing layout - ui->spinBox_NewMap_Width->setDisabled(true); - ui->spinBox_NewMap_Height->setDisabled(true); - ui->spinBox_NewMap_BorderWidth->setDisabled(true); - ui->spinBox_NewMap_BorderHeight->setDisabled(true); - ui->comboBox_NewMap_Primary_Tileset->setDisabled(true); - ui->comboBox_NewMap_Secondary_Tileset->setDisabled(true); + if (!layoutEditsEnabled) { + useLayout(this->layoutId);//this->ui->comboBox_Layout->currentText()); + } else { + this->existingLayout = false; + } +} + +void NewMapPopup::on_comboBox_Layout_currentTextChanged(const QString &text) { + if (this->project->mapLayoutsTable.contains(text)) { + useLayout(text); + } } void NewMapPopup::on_lineEdit_NewMap_Name_textChanged(const QString &text) { @@ -242,7 +287,7 @@ void NewMapPopup::on_pushButton_NewMap_Accept_clicked() { return; } Map *newMap = new Map; - MapLayout *layout; + Layout *layout; // If map name is not unique, use default value. Also use only valid characters. // After stripping invalid characters, strip any leading digits. @@ -267,8 +312,8 @@ void NewMapPopup::on_pushButton_NewMap_Accept_clicked() { layout = this->project->mapLayouts.value(this->layoutId); newMap->needsLayoutDir = false; } else { - layout = new MapLayout; - layout->id = MapLayout::layoutConstantFromName(newMapName); + layout = new Layout; + layout->id = Layout::layoutConstantFromName(newMapName); layout->name = QString("%1_Layout").arg(newMap->name); layout->width = this->ui->spinBox_NewMap_Width->value(); layout->height = this->ui->spinBox_NewMap_Height->value(); diff --git a/src/ui/prefab.cpp b/src/ui/prefab.cpp index 594e811ae..106421787 100644 --- a/src/ui/prefab.cpp +++ b/src/ui/prefab.cpp @@ -158,32 +158,32 @@ QList Prefab::getPrefabsForTilesets(QString primaryTileset, QString return filteredPrefabs; } -void Prefab::initPrefabUI(MetatileSelector *selector, QWidget *prefabWidget, QLabel *emptyPrefabLabel, Map *map) { +void Prefab::initPrefabUI(MetatileSelector *selector, QWidget *prefabWidget, QLabel *emptyPrefabLabel, Layout *layout) { this->selector = selector; this->prefabWidget = prefabWidget; this->emptyPrefabLabel = emptyPrefabLabel; this->loadPrefabs(); - this->updatePrefabUi(map); + this->updatePrefabUi(layout); } // This function recreates the UI state for the prefab tab. // We completely delete all the prefab widgets, and recreate new widgets // from the relevant list of prefab items. // This is not very efficient, but it gets the job done. -void Prefab::updatePrefabUi(Map *map) { +void Prefab::updatePrefabUi(Layout *layout) { if (!this->selector) return; // Cleanup the PrefabFrame to have a clean slate. - auto layout = this->prefabWidget->layout(); - while (layout && layout->count() > 1) { - auto child = layout->takeAt(1); + auto uiLayout = this->prefabWidget->layout(); + while (uiLayout && uiLayout->count() > 1) { + auto child = uiLayout->takeAt(1); if (child->widget()) { delete child->widget(); } delete child; } - QList prefabs = this->getPrefabsForTilesets(map->layout->tileset_primary_label, map->layout->tileset_secondary_label); + QList prefabs = this->getPrefabsForTilesets(layout->tileset_primary_label, layout->tileset_secondary_label); if (prefabs.isEmpty()) { emptyPrefabLabel->setVisible(true); return; @@ -202,7 +202,7 @@ void Prefab::updatePrefabUi(Map *map) { frame->ui->label_Name->setText(item.name); auto scene = new QGraphicsScene; - scene->addPixmap(drawMetatileSelection(item.selection, map)); + scene->addPixmap(drawMetatileSelection(item.selection, layout)); scene->setSceneRect(scene->itemsBoundingRect()); frame->ui->graphicsView_Prefab->setScene(scene); frame->ui->graphicsView_Prefab->setFixedSize(scene->itemsBoundingRect().width() + 2, @@ -216,7 +216,7 @@ void Prefab::updatePrefabUi(Map *map) { }); // Clicking the delete button removes it from the list of known prefabs and updates the UI. - QObject::connect(frame->ui->pushButton_DeleteItem, &QPushButton::clicked, [this, item, map](){ + QObject::connect(frame->ui->pushButton_DeleteItem, &QPushButton::clicked, [this, item, layout](){ for (int i = 0; i < this->items.size(); i++) { if (this->items[i].id == item.id) { QMessageBox msgBox; @@ -234,7 +234,7 @@ void Prefab::updatePrefabUi(Map *map) { if (msgBox.clickedButton() == deleteButton) { this->items.removeAt(i); this->savePrefabs(); - this->updatePrefabUi(map); + this->updatePrefabUi(layout); } break; } @@ -246,7 +246,7 @@ void Prefab::updatePrefabUi(Map *map) { prefabWidget->layout()->addItem(verticalSpacer); } -void Prefab::addPrefab(MetatileSelection selection, Map *map, QString name) { +void Prefab::addPrefab(MetatileSelection selection, Layout *layout, QString name) { // First, determine which tilesets are actually used in this new prefab, // based on the metatile ids. bool usesPrimaryTileset = false; @@ -264,12 +264,12 @@ void Prefab::addPrefab(MetatileSelection selection, Map *map, QString name) { this->items.append(PrefabItem{ QUuid::createUuid(), name, - usesPrimaryTileset ? map->layout->tileset_primary_label : "", - usesSecondaryTileset ? map->layout->tileset_secondary_label: "", + usesPrimaryTileset ? layout->tileset_primary_label : "", + usesSecondaryTileset ? layout->tileset_secondary_label: "", selection }); this->savePrefabs(); - this->updatePrefabUi(map); + this->updatePrefabUi(layout); } bool Prefab::tryImportDefaultPrefabs(QWidget * parent, BaseGameVersion version, QString filepath) { diff --git a/src/ui/prefabcreationdialog.cpp b/src/ui/prefabcreationdialog.cpp index 976a53a15..a7b3029c2 100644 --- a/src/ui/prefabcreationdialog.cpp +++ b/src/ui/prefabcreationdialog.cpp @@ -6,16 +6,16 @@ #include -PrefabCreationDialog::PrefabCreationDialog(QWidget *parent, MetatileSelector *metatileSelector, Map *map) : +PrefabCreationDialog::PrefabCreationDialog(QWidget *parent, MetatileSelector *metatileSelector, Layout *layout) : QDialog(parent), ui(new Ui::PrefabCreationDialog) { ui->setupUi(this); - this->map = map; + this->layout = layout; this->selection = metatileSelector->getMetatileSelection(); QGraphicsScene *scene = new QGraphicsScene; - QGraphicsPixmapItem *pixmapItem = scene->addPixmap(drawMetatileSelection(this->selection, map)); + QGraphicsPixmapItem *pixmapItem = scene->addPixmap(drawMetatileSelection(this->selection, layout)); scene->setSceneRect(scene->itemsBoundingRect()); this->ui->graphicsView_Prefab->setScene(scene); this->ui->graphicsView_Prefab->setFixedSize(scene->itemsBoundingRect().width() + 2, @@ -35,7 +35,7 @@ PrefabCreationDialog::PrefabCreationDialog(QWidget *parent, MetatileSelector *me if (this->selection.hasCollision) { this->selection.collisionItems[index].enabled = toggledState; } - pixmapItem->setPixmap(drawMetatileSelection(this->selection, map)); + pixmapItem->setPixmap(drawMetatileSelection(this->selection, layout)); }); } @@ -45,5 +45,5 @@ PrefabCreationDialog::~PrefabCreationDialog() } void PrefabCreationDialog::savePrefab() { - prefab.addPrefab(this->selection, this->map, this->ui->lineEdit_PrefabName->text()); + prefab.addPrefab(this->selection, this->layout, this->ui->lineEdit_PrefabName->text()); } diff --git a/src/ui/regionmapeditor.cpp b/src/ui/regionmapeditor.cpp index b2a585368..c6485d519 100644 --- a/src/ui/regionmapeditor.cpp +++ b/src/ui/regionmapeditor.cpp @@ -29,7 +29,6 @@ RegionMapEditor::RegionMapEditor(QWidget *parent, Project *project) : this->ui->setupUi(this); this->project = project; this->configFilepath = QString("%1/%2").arg(this->project->root).arg(projectConfig.getFilePath(ProjectFilePath::json_region_porymap_cfg)); - this->mapSectionFilepath = QString("%1/%2").arg(this->project->root).arg(projectConfig.getFilePath(ProjectFilePath::json_region_map_entries)); this->initShortcuts(); this->restoreWindowState(); } @@ -110,67 +109,13 @@ void RegionMapEditor::applyUserShortcuts() { } bool RegionMapEditor::loadRegionMapEntries() { - this->region_map_entries.clear(); - - ParseUtil parser; - QJsonDocument sectionsDoc; - if (!parser.tryParseJsonFile(§ionsDoc, this->mapSectionFilepath)) { - logError(QString("Failed to read map data from %1").arg(this->mapSectionFilepath)); - return false; - } - - // for some unknown reason, the OrderedJson class would not parse this properly - // perhaps updating nlohmann/json here would fix it, but that also requires using C++17 - QJsonObject object = sectionsDoc.object(); - - for (auto entryRef : object["map_sections"].toArray()) { - QJsonObject entryObject = entryRef.toObject(); - QString entryMapSection = ParseUtil::jsonToQString(entryObject["map_section"]); - MapSectionEntry entry; - entry.name = ParseUtil::jsonToQString(entryObject["name"]); - entry.x = ParseUtil::jsonToInt(entryObject["x"]); - entry.y = ParseUtil::jsonToInt(entryObject["y"]); - entry.width = ParseUtil::jsonToInt(entryObject["width"]); - entry.height = ParseUtil::jsonToInt(entryObject["height"]); - entry.valid = true; - this->region_map_entries[entryMapSection] = entry; - } - + this->region_map_entries = this->project->regionMapEntries; return true; } bool RegionMapEditor::saveRegionMapEntries() { - QFile sectionsFile(this->mapSectionFilepath); - if (!sectionsFile.open(QIODevice::WriteOnly)) { - logError(QString("Could not open %1 for writing").arg(this->mapSectionFilepath)); - return false; - } - - OrderedJson::object object; - OrderedJson::array mapSectionArray; - - for (auto pair : this->region_map_entries) { - QString section = pair.first; - MapSectionEntry entry = pair.second; - - OrderedJson::object entryObject; - entryObject["map_section"] = section; - entryObject["name"] = entry.name; - entryObject["x"] = entry.x; - entryObject["y"] = entry.y; - entryObject["width"] = entry.width; - entryObject["height"] = entry.height; - - mapSectionArray.append(entryObject); - } - - object["map_sections"] = mapSectionArray; - - OrderedJson sectionsJson(object); - OrderedJsonDoc jsonDoc(§ionsJson); - jsonDoc.dump(§ionsFile); - sectionsFile.close(); - + this->project->regionMapEntries = this->region_map_entries; + this->project->saveRegionMapSections(); return true; } @@ -708,7 +653,7 @@ void RegionMapEditor::displayRegionMapLayoutOptions() { this->ui->comboBox_RM_ConnectedMap->blockSignals(true); this->ui->comboBox_RM_ConnectedMap->clear(); - this->ui->comboBox_RM_ConnectedMap->addItems(this->project->mapSectionValueToName.values()); + this->ui->comboBox_RM_ConnectedMap->addItems(this->project->mapSectionIdNames); this->ui->comboBox_RM_ConnectedMap->blockSignals(false); this->ui->frame_RM_Options->setEnabled(true); @@ -775,7 +720,7 @@ void RegionMapEditor::displayRegionMapEntryOptions() { if (!this->region_map->layoutEnabled()) return; this->ui->comboBox_RM_Entry_MapSection->clear(); - this->ui->comboBox_RM_Entry_MapSection->addItems(this->project->mapSectionValueToName.values()); + this->ui->comboBox_RM_Entry_MapSection->addItems(this->project->mapSectionIdNames); this->ui->spinBox_RM_Entry_x->setMaximum(128); this->ui->spinBox_RM_Entry_y->setMaximum(128); this->ui->spinBox_RM_Entry_width->setMinimum(1); @@ -787,17 +732,13 @@ void RegionMapEditor::displayRegionMapEntryOptions() { void RegionMapEditor::updateRegionMapEntryOptions(QString section) { if (!this->region_map->layoutEnabled()) return; - bool isSpecialSection = (section == this->region_map->default_map_section - || section == this->region_map->count_map_section); - - bool enabled = (!isSpecialSection && this->region_map_entries.contains(section)); - + bool enabled = (section != this->region_map->default_map_section) && this->region_map_entries.contains(section); this->ui->lineEdit_RM_MapName->setEnabled(enabled); this->ui->spinBox_RM_Entry_x->setEnabled(enabled); this->ui->spinBox_RM_Entry_y->setEnabled(enabled); this->ui->spinBox_RM_Entry_width->setEnabled(enabled); this->ui->spinBox_RM_Entry_height->setEnabled(enabled); - this->ui->pushButton_entryActivate->setEnabled(!isSpecialSection); + this->ui->pushButton_entryActivate->setEnabled(section != this->region_map->default_map_section); this->ui->pushButton_entryActivate->setText(enabled ? "Remove" : "Add"); this->ui->lineEdit_RM_MapName->blockSignals(true); @@ -902,14 +843,8 @@ void RegionMapEditor::onRegionMapEntryDragged(int new_x, int new_y) { } void RegionMapEditor::onRegionMapLayoutSelectedTileChanged(int index) { - QString message = QString(); this->currIndex = index; this->region_map_layout_item->highlightedTile = index; - if (this->region_map->squareHasMap(index)) { - message = QString("\t %1").arg(this->project->mapSecToMapHoverName.value( - this->region_map->squareMapSection(index))).remove("{NAME_END}"); - } - this->ui->statusbar->showMessage(message); updateRegionMapLayoutOptions(index); this->region_map_layout_item->draw(); @@ -922,8 +857,7 @@ void RegionMapEditor::onRegionMapLayoutHoveredTileChanged(int index) { if (x >= 0 && y >= 0) { message = QString("(%1, %2)").arg(x).arg(y); if (this->region_map->squareHasMap(index)) { - message += QString("\t %1").arg(this->project->mapSecToMapHoverName.value( - this->region_map->squareMapSection(index))).remove("{NAME_END}"); + message += QString("\t %1").arg(this->region_map->squareMapSection(index)); } } this->ui->statusbar->showMessage(message); @@ -1094,7 +1028,7 @@ void RegionMapEditor::on_spinBox_RM_LayoutWidth_valueChanged(int value) { int newHeight = this->region_map->layoutHeight(); QMap> newLayouts = this->region_map->getAllLayouts(); - ResizeLayout *commit = new ResizeLayout(this->region_map, oldWidth, oldHeight, newWidth, newHeight, oldLayouts, newLayouts); + ResizeRMLayout *commit = new ResizeRMLayout(this->region_map, oldWidth, oldHeight, newWidth, newHeight, oldLayouts, newLayouts); this->region_map->editHistory.push(commit); } } @@ -1111,7 +1045,7 @@ void RegionMapEditor::on_spinBox_RM_LayoutHeight_valueChanged(int value) { int newHeight = this->region_map->layoutHeight(); QMap> newLayouts = this->region_map->getAllLayouts(); - ResizeLayout *commit = new ResizeLayout(this->region_map, oldWidth, oldHeight, newWidth, newHeight, oldLayouts, newLayouts); + ResizeRMLayout *commit = new ResizeRMLayout(this->region_map, oldWidth, oldHeight, newWidth, newHeight, oldLayouts, newLayouts); this->region_map->editHistory.push(commit); } } @@ -1203,10 +1137,10 @@ void RegionMapEditor::on_action_Swap_triggered() { QFormLayout form(&popup); QComboBox *oldSecBox = new QComboBox(); - oldSecBox->addItems(this->project->mapSectionValueToName.values()); + oldSecBox->addItems(this->project->mapSectionIdNames); form.addRow(new QLabel("Map Section 1:"), oldSecBox); QComboBox *newSecBox = new QComboBox(); - newSecBox->addItems(this->project->mapSectionValueToName.values()); + newSecBox->addItems(this->project->mapSectionIdNames); form.addRow(new QLabel("Map Section 2:"), newSecBox); QDialogButtonBox buttonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, &popup); @@ -1242,10 +1176,10 @@ void RegionMapEditor::on_action_Replace_triggered() { QFormLayout form(&popup); QComboBox *oldSecBox = new QComboBox(); - oldSecBox->addItems(this->project->mapSectionValueToName.values()); + oldSecBox->addItems(this->project->mapSectionIdNames); form.addRow(new QLabel("Old Map Section:"), oldSecBox); QComboBox *newSecBox = new QComboBox(); - newSecBox->addItems(this->project->mapSectionValueToName.values()); + newSecBox->addItems(this->project->mapSectionIdNames); form.addRow(new QLabel("New Map Section:"), newSecBox); QDialogButtonBox buttonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, &popup); diff --git a/src/ui/tileseteditor.cpp b/src/ui/tileseteditor.cpp index 2d76882e5..e709bed1b 100644 --- a/src/ui/tileseteditor.cpp +++ b/src/ui/tileseteditor.cpp @@ -13,15 +13,15 @@ #include #include -TilesetEditor::TilesetEditor(Project *project, Map *map, QWidget *parent) : +TilesetEditor::TilesetEditor(Project *project, Layout *layout, QWidget *parent) : QMainWindow(parent), ui(new Ui::TilesetEditor), project(project), - map(map), + layout(layout), hasUnsavedChanges(false) { this->setAttribute(Qt::WA_DeleteOnClose); - this->setTilesets(this->map->layout->tileset_primary_label, this->map->layout->tileset_secondary_label); + this->setTilesets(this->layout->tileset_primary_label, this->layout->tileset_secondary_label); this->initUi(); } @@ -43,14 +43,14 @@ TilesetEditor::~TilesetEditor() this->metatileHistory.clear(); } -void TilesetEditor::update(Map *map, QString primaryTilesetLabel, QString secondaryTilesetLabel) { - this->updateMap(map); +void TilesetEditor::update(Layout *layout, QString primaryTilesetLabel, QString secondaryTilesetLabel) { + this->updateLayout(layout); this->updateTilesets(primaryTilesetLabel, secondaryTilesetLabel); } -void TilesetEditor::updateMap(Map *map) { - this->map = map; - this->metatileSelector->map = map; +void TilesetEditor::updateLayout(Layout *layout) { + this->layout = layout; + this->metatileSelector->layout = layout; } void TilesetEditor::updateTilesets(QString primaryTilesetLabel, QString secondaryTilesetLabel) { @@ -180,7 +180,7 @@ void TilesetEditor::setMetatileLabelValidator() { void TilesetEditor::initMetatileSelector() { - this->metatileSelector = new TilesetEditorMetatileSelector(this->primaryTileset, this->secondaryTileset, this->map); + this->metatileSelector = new TilesetEditorMetatileSelector(this->primaryTileset, this->secondaryTileset, this->layout); connect(this->metatileSelector, &TilesetEditorMetatileSelector::hoveredMetatileChanged, this, &TilesetEditor::onHoveredMetatileChanged); connect(this->metatileSelector, &TilesetEditorMetatileSelector::hoveredMetatileCleared, @@ -387,7 +387,7 @@ void TilesetEditor::onSelectedMetatileChanged(uint16_t metatileId) { // The Tileset Editor (if open) needs to reflect these changes when the metatile is next displayed. if (this->metatileReloadQueue.contains(metatileId)) { this->metatileReloadQueue.remove(metatileId); - Metatile *updatedMetatile = Tileset::getMetatile(metatileId, this->map->layout->tileset_primary, this->map->layout->tileset_secondary); + Metatile *updatedMetatile = Tileset::getMetatile(metatileId, this->layout->tileset_primary, this->layout->tileset_secondary); if (updatedMetatile) *this->metatile = *updatedMetatile; } diff --git a/src/ui/tileseteditormetatileselector.cpp b/src/ui/tileseteditormetatileselector.cpp index a16a35f8c..778fde9c9 100644 --- a/src/ui/tileseteditormetatileselector.cpp +++ b/src/ui/tileseteditormetatileselector.cpp @@ -3,11 +3,11 @@ #include "project.h" #include -TilesetEditorMetatileSelector::TilesetEditorMetatileSelector(Tileset *primaryTileset, Tileset *secondaryTileset, Map *map) +TilesetEditorMetatileSelector::TilesetEditorMetatileSelector(Tileset *primaryTileset, Tileset *secondaryTileset, Layout *layout) : SelectablePixmapItem(32, 32, 1, 1) { this->setTilesets(primaryTileset, secondaryTileset, false); this->numMetatilesWide = 8; - this->map = map; + this->layout = layout; setAcceptHoverEvents(true); this->usedMetatiles.resize(Project::getNumMetatilesTotal()); } @@ -54,8 +54,8 @@ QImage TilesetEditorMetatileSelector::buildImage(int metatileIdStart, int numMet metatileId, this->primaryTileset, this->secondaryTileset, - map->metatileLayerOrder, - map->metatileLayerOpacity, + this->layout->metatileLayerOrder, + this->layout->metatileLayerOpacity, true) .scaled(32, 32); int map_y = i / this->numMetatilesWide;