From e232ef760c5604983068b0213e71e08f6a425c23 Mon Sep 17 00:00:00 2001 From: GriffinR Date: Sun, 17 Aug 2025 20:34:32 -0400 Subject: [PATCH] Add sorting options to the Locations and Layouts list --- include/config.h | 2 ++ include/mainwindow.h | 5 +++-- include/project.h | 11 +++++++++-- src/config.cpp | 8 ++++++++ src/core/regionmap.cpp | 6 +++--- src/mainwindow.cpp | 37 +++++++++++++++++++++++++++++------- src/scriptapi/apiutility.cpp | 2 +- src/ui/mapheaderform.cpp | 2 +- src/ui/maplistmodels.cpp | 4 ++-- src/ui/regionmapeditor.cpp | 12 ++++++------ 10 files changed, 65 insertions(+), 24 deletions(-) diff --git a/include/config.h b/include/config.h index 1f2a1a14..12bd1811 100644 --- a/include/config.h +++ b/include/config.h @@ -97,6 +97,8 @@ public: int mapListTab; bool mapListEditGroupsEnabled; QMap mapListHideEmptyEnabled; + bool mapListLayoutsSorted; + bool mapListLocationsSorted; bool prettyCursors; bool mirrorConnectingMaps; bool showDiveEmergeMaps; diff --git a/include/mainwindow.h b/include/mainwindow.h index d15bde31..c747eb09 100644 --- a/include/mainwindow.h +++ b/include/mainwindow.h @@ -393,10 +393,10 @@ private: void openDuplicateMapOrLayoutDialog(); void openNewMapGroupDialog(); void openNewLocationDialog(); - void scrollMapList(MapTree *list, const QString &itemName); + void scrollMapList(MapTree *list, const QString &itemName, bool expandItem = true); void scrollMapListToCurrentMap(MapTree *list); void scrollMapListToCurrentLayout(MapTree *list); - void scrollCurrentMapListToItem(const QString &itemName); + void scrollCurrentMapListToItem(const QString &itemName, bool expandItem = true); void showFileWatcherWarning(); bool openProject(QString dir, bool initial = false); bool closeProject(); @@ -410,6 +410,7 @@ private: void rebuildMapList_Locations(); void rebuildMapList_Layouts(); + void setMapListSorted(MapTree *list, bool sort); void updateMapList(); void openMapListItem(const QModelIndex &index); void onMapListTabChanged(int index); diff --git a/include/project.h b/include/project.h index 0ea25a4a..c377362b 100644 --- a/include/project.h +++ b/include/project.h @@ -52,8 +52,6 @@ public: QStringList bgEventFacingDirections; QStringList trainerTypes; QStringList globalScriptLabels; - QStringList mapSectionIdNamesSaveOrder; - QStringList mapSectionIdNames; QMap encounterTypeToName; QMap terrainTypeToName; QMap> metatileLabelsMap; @@ -83,6 +81,7 @@ public: Map* loadMap(const QString &mapName); const QStringList& layoutIds() const { return this->alphabeticalLayoutIds; } + const QStringList& layoutIdsOrdered() const { return this->orderedLayoutIds; } bool isKnownLayout(const QString &layoutId) const { return this->mapLayouts.contains(layoutId); } bool isLoadedLayout(const QString &layoutId) const { return this->loadedLayoutIds.contains(layoutId); } bool isUnsavedLayout(const QString &layoutId) const; @@ -93,6 +92,12 @@ public: Layout* getLayout(const QString &layoutId) const { return this->mapLayouts.value(layoutId); } Layout* loadLayout(const QString &layoutId); + const QStringList& locationNames() const { return this->mapSectionIdNames; } + const QStringList& locationNamesOrdered() const { return this->mapSectionIdNamesSaveOrder; } + + QString getLocationName(int locationValue) const { return this->mapSectionIdNamesSaveOrder.value(locationValue, getEmptyMapsecName()); } + int getLocationValue(const QString &locationName) const { return this->mapSectionIdNamesSaveOrder.indexOf(locationName); } + void clearMaps(); void clearTilesetCache(); void clearMapLayouts(); @@ -284,6 +289,8 @@ private: QStringList orderedLayoutIdsMaster; QHash mapLayouts; QHash mapLayoutsMaster; + QStringList mapSectionIdNamesSaveOrder; + QStringList mapSectionIdNames; // Fields for preserving top-level JSON data that Porymap isn't expecting. QJsonObject customLayoutsData; diff --git a/src/config.cpp b/src/config.cpp index 4322e4cd..7f7014d8 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -327,6 +327,8 @@ void PorymapConfig::reset() { this->mapListTab = 0; this->mapListEditGroupsEnabled = false; this->mapListHideEmptyEnabled.clear(); + this->mapListLayoutsSorted = true; + this->mapListLocationsSorted = true; this->prettyCursors = true; this->mirrorConnectingMaps = true; this->showDiveEmergeMaps = false; @@ -397,6 +399,10 @@ void PorymapConfig::parseConfigKeyValue(QString key, QString value) { return; } this->mapListHideEmptyEnabled.insert(tab, getConfigBool(key, value)); + } else if (key == "map_list_layouts_sorted") { + this->mapListLayoutsSorted = getConfigBool(key, value); + } else if (key == "map_list_locations_sorted") { + this->mapListLocationsSorted = getConfigBool(key, value); } else if (key == "main_window_geometry") { this->mainWindowGeometry = bytesFromString(value); } else if (key == "main_window_state") { @@ -579,6 +585,8 @@ QMap PorymapConfig::getKeyValueMap() { for (auto i = this->mapListHideEmptyEnabled.constBegin(); i != this->mapListHideEmptyEnabled.constEnd(); i++) { map.insert(QStringLiteral("map_list_hide_empty_enabled/") + QString::number(i.key()), i.value() ? "1" : "0"); } + map.insert("map_list_layouts_sorted", this->mapListLayoutsSorted ? "1" : "0"); + map.insert("map_list_locations_sorted", this->mapListLocationsSorted ? "1" : "0"); map.insert("main_window_geometry", stringFromByteArray(this->mainWindowGeometry)); map.insert("main_window_state", stringFromByteArray(this->mainWindowState)); map.insert("map_splitter_state", stringFromByteArray(this->mapSplitterState)); diff --git a/src/core/regionmap.cpp b/src/core/regionmap.cpp index 587be33c..a16d3b54 100644 --- a/src/core/regionmap.cpp +++ b/src/core/regionmap.cpp @@ -156,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->mapSectionIdNames.value(square_section_id, this->default_map_section); + QString square_section_name = project->getLocationName(square_section_id); LayoutSquare square; square.map_section = square_section_name; @@ -397,11 +397,11 @@ void RegionMap::saveLayout() { case LayoutFormat::Binary: { QByteArray data; - int defaultValue = this->project->mapSectionIdNames.indexOf(this->default_map_section); + int defaultValue = this->project->getLocationValue(this->default_map_section); 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; - int mapSectionValue = this->project->mapSectionIdNames.indexOf(this->layouts["main"][i].map_section); + int mapSectionValue = this->project->getLocationValue(this->layouts["main"][i].map_section); if (mapSectionValue < 0){ mapSectionValue = defaultValue; } diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 3519827b..ee1050d1 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -1440,16 +1440,15 @@ bool MainWindow::setProjectUI() { this->locationListProxyModel = new FilterChildrenProxyModel(); this->locationListProxyModel->setSourceModel(this->mapLocationModel); this->locationListProxyModel->setHideEmpty(porymapConfig.mapListHideEmptyEnabled[MapListTab::Locations]); - ui->locationList->setModel(locationListProxyModel); - ui->locationList->sortByColumn(0, Qt::SortOrder::AscendingOrder); + setMapListSorted(ui->locationList, porymapConfig.mapListLocationsSorted); this->layoutTreeModel = new LayoutTreeModel(editor->project); this->layoutListProxyModel = new FilterChildrenProxyModel(); this->layoutListProxyModel->setSourceModel(this->layoutTreeModel); this->layoutListProxyModel->setHideEmpty(porymapConfig.mapListHideEmptyEnabled[MapListTab::Layouts]); ui->layoutList->setModel(layoutListProxyModel); - ui->layoutList->sortByColumn(0, Qt::SortOrder::AscendingOrder); + setMapListSorted(ui->layoutList, porymapConfig.mapListLayoutsSorted); ui->mapCustomAttributesFrame->table()->setRestrictedKeys(project->getTopLevelMapFields()); @@ -1503,7 +1502,7 @@ void MainWindow::clearProjectUI() { resetMapNavigation(); } -void MainWindow::scrollMapList(MapTree *list, const QString &itemName) { +void MainWindow::scrollMapList(MapTree *list, const QString &itemName, bool expandItem) { if (!list || itemName.isEmpty()) return; auto model = static_cast(list->model()); @@ -1516,7 +1515,7 @@ void MainWindow::scrollMapList(MapTree *list, const QString &itemName) { return; list->setCurrentIndex(index); - list->setExpanded(index, true); + if (expandItem) list->setExpanded(index, true); list->scrollTo(index, QAbstractItemView::PositionAtCenter); } @@ -1537,13 +1536,13 @@ void MainWindow::scrollMapListToCurrentLayout(MapTree *list) { // - The map list was in the middle of a search // - A map/layout is being opened by interacting with the list (in which case `lockMapListAutoScroll` is true) // - The item is not in the list (e.g. a layout ID for the Groups list) -void MainWindow::scrollCurrentMapListToItem(const QString &itemName) { +void MainWindow::scrollCurrentMapListToItem(const QString &itemName, bool expandItem) { if (this->lockMapListAutoScroll) return; auto toolbar = getCurrentMapListToolBar(); if (toolbar && toolbar->filterText().isEmpty()) { - scrollMapList(toolbar->list(), itemName); + scrollMapList(toolbar->list(), itemName, expandItem); } } @@ -1565,6 +1564,7 @@ void MainWindow::onOpenMapListContextMenu(const QPoint &point) { QAction* openItemAction = nullptr; QAction* copyListNameAction = nullptr; QAction* copyToolTipAction = nullptr; + QAction* sortFoldersAction = nullptr; if (itemType == "map_name") { // Right-clicking on a map. @@ -1596,6 +1596,8 @@ void MainWindow::onOpenMapListContextMenu(const QPoint &point) { deleteFolderAction = menu.addAction("Delete Location"); if (itemName == this->editor->project->getEmptyMapsecName()) deleteFolderAction->setEnabled(false); // Disallow deleting the default name + menu.addSeparator(); + sortFoldersAction = menu.addAction(list->isSortingEnabled() ? "Sort List by Value" : "Sort List Alphabetically"); } else if (itemType == "map_layout") { // Right-clicking on a map layout openItemAction = menu.addAction("Open Layout"); @@ -1612,6 +1614,8 @@ void MainWindow::onOpenMapListContextMenu(const QPoint &point) { addToFolderAction = menu.addAction("Add New Map with Layout"); //menu.addSeparator(); //deleteFolderAction = menu.addAction("Delete Layout"); // TODO: No support for deleting layouts + menu.addSeparator(); + sortFoldersAction = menu.addAction(list->isSortingEnabled() ? "Sort List by Value" : "Sort List Alphabetically"); } if (addToFolderAction) { @@ -1646,6 +1650,12 @@ void MainWindow::onOpenMapListContextMenu(const QPoint &point) { setClipboardData(selectedItem->toolTip()); }); } + if (sortFoldersAction) { + connect(sortFoldersAction, &QAction::triggered, [this, list, itemName] { + setMapListSorted(list, !list->isSortingEnabled()); + scrollCurrentMapListToItem(itemName, false); + }); + } if (menu.actions().length() != 0) menu.exec(QCursor::pos()); @@ -1895,6 +1905,19 @@ void MainWindow::rebuildMapList_Layouts() { ui->mapListToolBar_Layouts->refreshFilter(); } +void MainWindow::setMapListSorted(MapTree *list, bool sort) { + if (sort == list->isSortingEnabled()) + return; + list->setSortingEnabled(sort); + list->sortByColumn(sort ? 0 : -1, Qt::SortOrder::AscendingOrder); + + if (list == ui->locationList) { + porymapConfig.mapListLocationsSorted = sort; + } else if (list == ui->layoutList) { + porymapConfig.mapListLayoutsSorted = sort; + } +} + QString MainWindow::getActiveItemName() { if (this->editor->map) return this->editor->map->name(); if (this->editor->layout) return this->editor->layout->id; diff --git a/src/scriptapi/apiutility.cpp b/src/scriptapi/apiutility.cpp index 8f205de2..e18d7110 100644 --- a/src/scriptapi/apiutility.cpp +++ b/src/scriptapi/apiutility.cpp @@ -288,7 +288,7 @@ QList ScriptUtility::getSongNames() { QList ScriptUtility::getLocationNames() { if (!window || !window->editor || !window->editor->project) return QList(); - return window->editor->project->mapSectionIdNames; + return window->editor->project->locationNames(); } QList ScriptUtility::getWeatherNames() { diff --git a/src/ui/mapheaderform.cpp b/src/ui/mapheaderform.cpp index 991fe1c5..921aec82 100644 --- a/src/ui/mapheaderform.cpp +++ b/src/ui/mapheaderform.cpp @@ -67,7 +67,7 @@ void MapHeaderForm::setProject(Project * project, bool allowChanges) { const QSignalBlocker b_Locations(ui->comboBox_Location); ui->comboBox_Location->clear(); - ui->comboBox_Location->addItems(m_project->mapSectionIdNames); + ui->comboBox_Location->addItems(m_project->locationNames()); // Hide config-specific settings diff --git a/src/ui/maplistmodels.cpp b/src/ui/maplistmodels.cpp index 3bd95d66..ba4e9a79 100644 --- a/src/ui/maplistmodels.cpp +++ b/src/ui/maplistmodels.cpp @@ -442,7 +442,7 @@ bool MapGroupModel::setData(const QModelIndex &index, const QVariant &value, int MapLocationModel::MapLocationModel(Project *project, QObject *parent) : MapListModel(project, parent) { this->folderTypeName = "map_section"; - for (const auto &idName : this->project->mapSectionIdNames) { + for (const auto &idName : this->project->locationNamesOrdered()) { insertMapFolderItem(idName); } for (const auto &mapName : this->project->mapNames()) { @@ -466,7 +466,7 @@ QStandardItem *MapLocationModel::createMapFolderItem(const QString &folderName, LayoutTreeModel::LayoutTreeModel(Project *project, QObject *parent) : MapListModel(project, parent) { this->folderTypeName = "map_layout"; - for (const auto &layoutId : this->project->layoutIds()) { + for (const auto &layoutId : this->project->layoutIdsOrdered()) { insertMapFolderItem(layoutId); } for (const auto &mapName : this->project->mapNames()) { diff --git a/src/ui/regionmapeditor.cpp b/src/ui/regionmapeditor.cpp index 0415e0f0..61514661 100644 --- a/src/ui/regionmapeditor.cpp +++ b/src/ui/regionmapeditor.cpp @@ -648,7 +648,7 @@ void RegionMapEditor::displayRegionMapLayoutOptions() { const QSignalBlocker b(ui->comboBox_RM_ConnectedMap); this->ui->comboBox_RM_ConnectedMap->clear(); - this->ui->comboBox_RM_ConnectedMap->addItems(this->project->mapSectionIdNames); + this->ui->comboBox_RM_ConnectedMap->addItems(this->project->locationNames()); this->ui->frame_RM_Options->setEnabled(true); @@ -711,7 +711,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->mapSectionIdNames); + this->ui->comboBox_RM_Entry_MapSection->addItems(this->project->locationNames()); this->ui->spinBox_RM_Entry_x->setMaximum(128); this->ui->spinBox_RM_Entry_y->setMaximum(128); this->ui->spinBox_RM_Entry_width->setMinimum(1); @@ -1103,10 +1103,10 @@ void RegionMapEditor::on_action_Swap_triggered() { QFormLayout form(&popup); QComboBox *oldSecBox = new QComboBox(); - oldSecBox->addItems(this->project->mapSectionIdNames); + oldSecBox->addItems(this->project->locationNames()); form.addRow(new QLabel("Map Section 1:"), oldSecBox); QComboBox *newSecBox = new QComboBox(); - newSecBox->addItems(this->project->mapSectionIdNames); + newSecBox->addItems(this->project->locationNames()); form.addRow(new QLabel("Map Section 2:"), newSecBox); QDialogButtonBox buttonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, &popup); @@ -1142,10 +1142,10 @@ void RegionMapEditor::on_action_Replace_triggered() { QFormLayout form(&popup); QComboBox *oldSecBox = new QComboBox(); - oldSecBox->addItems(this->project->mapSectionIdNames); + oldSecBox->addItems(this->project->locationNames()); form.addRow(new QLabel("Old Map Section:"), oldSecBox); QComboBox *newSecBox = new QComboBox(); - newSecBox->addItems(this->project->mapSectionIdNames); + newSecBox->addItems(this->project->locationNames()); form.addRow(new QLabel("New Map Section:"), newSecBox); QDialogButtonBox buttonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, &popup);