UI: Update order of profiles and scene collections in their menus
Description
Replicates the sorting of profiles and scene collection menu items provided by the Windows file system APIs with the new system.
Motivation and Context
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.
[!NOTE] The implementation relies on Qt's internal locale-aware sorting using the system locale, which is why the entries are not sorted "at the source" but before creating the menu entries. While the C++ STL provides some support for storing Unicode strings, it has no Unicode-aware built-in algorithms.
How Has This Been Tested?
Tested on macOS 15 with profiles and scene collections using the following names in lower- and uppercase:
A,B,CÆ,Å,Äてすと(hiragana)テスト(katakana)日本語(kanji)
The lowercase variants were sorted to come right after their uppercase variants, European special characters came after the generic latin characters, non-latin characters were sorted by their code points, with katakana appearing before hiragana and kanji.
Types of changes
- Tweak (non-breaking change to improve existing functionality)
Checklist:
- [x] My code has been run through clang-format.
- [x] I have read the contributing document.
- [x] My code is not on the master branch.
- [x] The code has been tested.
- [x] All commit messages are properly formatted and commits squashed where appropriate.
- [x] I have included updates to all appropriate documentation.
Also added code that should fix https://github.com/obsproject/obs-studio/issues/11414.
It seems more correct to me to set the profile name/collection name explicitly as a custom property on the QAction and not rely on its text property (which is the visible text of the action) as the latter can be changed/influenced by Qt after the fact.
It’s similar to the old behavior, but with uppercase letters on top and lowercase letters on the bottom.
https://github.com/user-attachments/assets/4021e204-f6d5-4739-8aa1-eb3569b2fe07
It’s similar to the old behavior, but with uppercase letters on top and lowercase letters on the bottom.
That's because the files/directories actually have different names on disk - one profile will be named "A" on disk, the other be will have been renamed to "a2" by libobs as a "safe" alternative (because it wants to allow case-sensitive distinctions between names).
And in any sorting "A2" will come after "a", but "a" will always come after "A".
@Warchamp7 This would require even more code just to replicate the quirks of the old code:
- I'd have to first create a list of the file/directory names
- Sort those
- Then iterate over all profiles/collections to find the corresponding profile/collection by its file/directory name for each directory name
- Create menu items for each found/matched entry
This would be a decent pile of code-stink in my opinion (I'd have argued that the original variant was best because it required no additional sorting and was stable across platforms), but I defer the final decision to you.
It also looks like that the order in which the upper/lowercase variants are created might influence the display order - in the given example "a" was created first before "A" (so "A" is actually named "A2" on disk) but if "A" had been created first you'd end up with "A" and "a2" and the order would be "A" followed by "a", so it was (ordered) chaos in OBS 30.0.2.
That's because the files/directories actually have different names on disk - one profile will be named "A" on disk, the other be will have been renamed to "a2" by
libobsas a "safe" alternative (because it wants to allow case-sensitive distinctions between names).And in any sorting "A2" will come after "a", but "a" will always come after "A".
I see, thank you for the explanation.