From 918fe6171d58872d5448fa3aa1e86b87330de742 Mon Sep 17 00:00:00 2001 From: PatTheMav Date: Fri, 18 Oct 2024 14:07:38 +0200 Subject: [PATCH] UI: Update order of profiles and scene collections in their menus The old method to update the profile menu iterated over the directory entries of the profile directory in the order provided by the operating system. The system calls used for this explicitly state that the order of items is "undefined" but seems to have followed a case-insensitive alphabetical order on Windows, an order which users have come to expect. The new code uses a std::map to store discovered profiles and scene collections, which is ordered by key and with std::string used for keys this means a lexicographical sorting of keys which is case-sensitive. To restore the old behavior, profiles and scene collections need to be added to their respective menus sorted by case-insensitive order, which has to be done manually before adding the items. --- UI/window-basic-main-profiles.cpp | 49 +++++++++++++++++---- UI/window-basic-main-scene-collections.cpp | 50 ++++++++++++++++++---- 2 files changed, 83 insertions(+), 16 deletions(-) diff --git a/UI/window-basic-main-profiles.cpp b/UI/window-basic-main-profiles.cpp index 4f539c3be..1a1acd6a8 100644 --- a/UI/window-basic-main-profiles.cpp +++ b/UI/window-basic-main-profiles.cpp @@ -43,6 +43,30 @@ extern void DuplicateCurrentCookieProfile(ConfigFile &config); extern void CheckExistingCookieId(); extern void DeleteCookies(); +// MARK: - Anonymous Namespace +namespace { +QList sortedProfiles{}; + +void updateSortedProfiles(const OBSProfileCache &profiles) +{ + const QLocale locale = QLocale::system(); + QList newList{}; + + for (auto [profileName, _] : profiles) { + QString entry = QString::fromStdString(profileName); + newList.append(entry); + } + + std::sort(newList.begin(), newList.end(), [&locale](const QString &lhs, const QString &rhs) -> bool { + int result = QString::localeAwareCompare(locale.toLower(lhs), locale.toLower(rhs)); + + return (result < 0); + }); + + sortedProfiles.swap(newList); +} +} // namespace + // MARK: - Main Profile Management Functions void OBSBasic::SetupNewProfile(const std::string &profileName, bool useWizard) @@ -277,18 +301,27 @@ void OBSBasic::RefreshProfiles(bool refreshCache) if (refreshCache) { RefreshProfileCache(); } + updateSortedProfiles(profiles); size_t numAddedProfiles = 0; - for (auto &[profileName, profile] : profiles) { - QAction *action = new QAction(QString().fromStdString(profileName), this); - action->setProperty("file_name", QString().fromStdString(profile.directoryName)); - connect(action, &QAction::triggered, this, &OBSBasic::ChangeProfile); - action->setCheckable(true); - action->setChecked(profileName == currentProfileName); + for (auto &name : sortedProfiles) { + const std::string profileName = name.toStdString(); + try { + OBSProfile &profile = profiles.at(profileName); - ui->profileMenu->addAction(action); + QAction *action = new QAction(QString().fromStdString(profileName), this); + action->setProperty("file_name", QString().fromStdString(profile.directoryName)); + connect(action, &QAction::triggered, this, &OBSBasic::ChangeProfile); + action->setCheckable(true); + action->setChecked(profileName == currentProfileName); - numAddedProfiles += 1; + ui->profileMenu->addAction(action); + + numAddedProfiles += 1; + } catch (const std::out_of_range &error) { + blog(LOG_ERROR, "No profile with name %s found in profile cache.\n%s", profileName.c_str(), + error.what()); + } } ui->actionRemoveProfile->setEnabled(numAddedProfiles > 1); diff --git a/UI/window-basic-main-scene-collections.cpp b/UI/window-basic-main-scene-collections.cpp index 84285d1c8..d3c145608 100644 --- a/UI/window-basic-main-scene-collections.cpp +++ b/UI/window-basic-main-scene-collections.cpp @@ -34,6 +34,30 @@ constexpr std::string_view OBSSceneCollectionPath = "/obs-studio/basic/scenes/"; +// MARK: - Anonymous Namespace +namespace { +QList sortedSceneCollections{}; + +void updateSortedSceneCollections(const OBSSceneCollectionCache &collections) +{ + const QLocale locale = QLocale::system(); + QList newList{}; + + for (auto [collectionName, _] : collections) { + QString entry = QString::fromStdString(collectionName); + newList.append(entry); + } + + std::sort(newList.begin(), newList.end(), [&locale](const QString &lhs, const QString &rhs) -> bool { + int result = QString::localeAwareCompare(locale.toLower(lhs), locale.toLower(rhs)); + + return (result < 0); + }); + + sortedSceneCollections.swap(newList); +} +} // namespace + // MARK: - Main Scene Collection Management Functions void OBSBasic::SetupNewSceneCollection(const std::string &collectionName) @@ -280,17 +304,27 @@ void OBSBasic::RefreshSceneCollections(bool refreshCache) RefreshSceneCollectionCache(); } + updateSortedSceneCollections(collections); + size_t numAddedCollections = 0; - for (auto &[collectionName, collection] : collections) { - QAction *action = new QAction(QString().fromStdString(collectionName), this); - action->setProperty("file_name", QString().fromStdString(collection.fileName)); - connect(action, &QAction::triggered, this, &OBSBasic::ChangeSceneCollection); - action->setCheckable(true); - action->setChecked(collectionName == currentCollectionName); + for (auto &name : sortedSceneCollections) { + const std::string collectionName = name.toStdString(); + try { + OBSSceneCollection &collection = collections.at(collectionName); - ui->sceneCollectionMenu->addAction(action); + QAction *action = new QAction(QString().fromStdString(collectionName), this); + action->setProperty("file_name", QString().fromStdString(collection.fileName)); + connect(action, &QAction::triggered, this, &OBSBasic::ChangeSceneCollection); + action->setCheckable(true); + action->setChecked(collectionName == currentCollectionName); - numAddedCollections += 1; + ui->sceneCollectionMenu->addAction(action); + + numAddedCollections += 1; + } catch (const std::out_of_range &error) { + blog(LOG_ERROR, "No scene collection with name %s found in scene collection cache.\n%s", + collectionName.c_str(), error.what()); + } } ui->actionRemoveSceneCollection->setEnabled(numAddedCollections > 1);