diff --git a/src/runner/general_settings.cpp b/src/runner/general_settings.cpp index 50dd8dbbc8..9f8ceeb8ad 100644 --- a/src/runner/general_settings.cpp +++ b/src/runner/general_settings.cpp @@ -35,6 +35,31 @@ namespace ensure_ignored_conflict_properties_shape(obj); return obj; } + + DashboardSortOrder parse_dashboard_sort_order(const json::JsonObject& obj, DashboardSortOrder fallback) + { + if (json::has(obj, L"dashboard_sort_order", json::JsonValueType::Number)) + { + const auto raw_value = static_cast(obj.GetNamedNumber(L"dashboard_sort_order", static_cast(static_cast(fallback)))); + return raw_value == static_cast(DashboardSortOrder::ByStatus) ? DashboardSortOrder::ByStatus : DashboardSortOrder::Alphabetical; + } + + if (json::has(obj, L"dashboard_sort_order", json::JsonValueType::String)) + { + const auto raw = obj.GetNamedString(L"dashboard_sort_order"); + if (raw == L"ByStatus") + { + return DashboardSortOrder::ByStatus; + } + + if (raw == L"Alphabetical") + { + return DashboardSortOrder::Alphabetical; + } + } + + return fallback; + } } // TODO: would be nice to get rid of these globals, since they're basically cached json settings @@ -46,6 +71,7 @@ static bool download_updates_automatically = true; static bool show_whats_new_after_updates = true; static bool enable_experimentation = true; static bool enable_warnings_elevated_apps = true; +static DashboardSortOrder dashboard_sort_order = DashboardSortOrder::Alphabetical; static json::JsonObject ignored_conflict_properties = create_default_ignored_conflict_properties(); json::JsonObject GeneralSettings::to_json() @@ -75,6 +101,7 @@ json::JsonObject GeneralSettings::to_json() result.SetNamedValue(L"download_updates_automatically", json::value(downloadUpdatesAutomatically)); result.SetNamedValue(L"show_whats_new_after_updates", json::value(showWhatsNewAfterUpdates)); result.SetNamedValue(L"enable_experimentation", json::value(enableExperimentation)); + result.SetNamedValue(L"dashboard_sort_order", json::value(static_cast(dashboardSortOrder))); result.SetNamedValue(L"is_admin", json::value(isAdmin)); result.SetNamedValue(L"enable_warnings_elevated_apps", json::value(enableWarningsElevatedApps)); result.SetNamedValue(L"theme", json::value(theme)); @@ -99,6 +126,7 @@ json::JsonObject load_general_settings() show_whats_new_after_updates = loaded.GetNamedBoolean(L"show_whats_new_after_updates", true); enable_experimentation = loaded.GetNamedBoolean(L"enable_experimentation", true); enable_warnings_elevated_apps = loaded.GetNamedBoolean(L"enable_warnings_elevated_apps", true); + dashboard_sort_order = parse_dashboard_sort_order(loaded, dashboard_sort_order); if (json::has(loaded, L"ignored_conflict_properties", json::JsonValueType::Object)) { @@ -128,6 +156,7 @@ GeneralSettings get_general_settings() .downloadUpdatesAutomatically = download_updates_automatically && is_user_admin, .showWhatsNewAfterUpdates = show_whats_new_after_updates, .enableExperimentation = enable_experimentation, + .dashboardSortOrder = dashboard_sort_order, .theme = settings_theme, .systemTheme = WindowsColors::is_dark_mode() ? L"dark" : L"light", .powerToysVersion = get_product_version(), @@ -159,6 +188,7 @@ void apply_general_settings(const json::JsonObject& general_configs, bool save) show_whats_new_after_updates = general_configs.GetNamedBoolean(L"show_whats_new_after_updates", true); enable_experimentation = general_configs.GetNamedBoolean(L"enable_experimentation", true); + dashboard_sort_order = parse_dashboard_sort_order(general_configs, dashboard_sort_order); // apply_general_settings is called by the runner's WinMain, so we can just force the run at startup gpo rule here. auto gpo_run_as_startup = powertoys_gpo::getConfiguredRunAtStartupValue(); diff --git a/src/runner/general_settings.h b/src/runner/general_settings.h index 38fbd5789a..b4f7638846 100644 --- a/src/runner/general_settings.h +++ b/src/runner/general_settings.h @@ -2,6 +2,12 @@ #include +enum class DashboardSortOrder +{ + Alphabetical = 0, + ByStatus = 1, +}; + struct GeneralSettings { bool isStartupEnabled; @@ -16,6 +22,7 @@ struct GeneralSettings bool downloadUpdatesAutomatically; bool showWhatsNewAfterUpdates; bool enableExperimentation; + DashboardSortOrder dashboardSortOrder; std::wstring theme; std::wstring systemTheme; std::wstring powerToysVersion; diff --git a/src/settings-ui/Settings.UI.Library/GeneralSettings.cs b/src/settings-ui/Settings.UI.Library/GeneralSettings.cs index 24ff4584fe..0f380aca78 100644 --- a/src/settings-ui/Settings.UI.Library/GeneralSettings.cs +++ b/src/settings-ui/Settings.UI.Library/GeneralSettings.cs @@ -13,6 +13,12 @@ using Settings.UI.Library.Attributes; namespace Microsoft.PowerToys.Settings.UI.Library { + public enum DashboardSortOrder + { + Alphabetical, + ByStatus, + } + public class GeneralSettings : ISettingsConfig { // Gets or sets a value indicating whether run powertoys on start-up. @@ -76,6 +82,9 @@ namespace Microsoft.PowerToys.Settings.UI.Library [JsonPropertyName("enable_experimentation")] public bool EnableExperimentation { get; set; } + [JsonPropertyName("dashboard_sort_order")] + public DashboardSortOrder DashboardSortOrder { get; set; } + [JsonPropertyName("ignored_conflict_properties")] public ShortcutConflictProperties IgnoredConflictProperties { get; set; } @@ -89,6 +98,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library ShowNewUpdatesToastNotification = true; AutoDownloadUpdates = false; EnableExperimentation = true; + DashboardSortOrder = DashboardSortOrder.Alphabetical; Theme = "system"; SystemTheme = "light"; try diff --git a/src/settings-ui/Settings.UI/Converters/EnumToBooleanConverter.cs b/src/settings-ui/Settings.UI/Converters/EnumToBooleanConverter.cs new file mode 100644 index 0000000000..27689435cc --- /dev/null +++ b/src/settings-ui/Settings.UI/Converters/EnumToBooleanConverter.cs @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using Microsoft.UI.Xaml.Data; + +namespace Microsoft.PowerToys.Settings.UI.Converters +{ + public partial class EnumToBooleanConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, string language) + { + if (value == null || parameter == null) + { + return false; + } + + // Get the enum value as string + var enumString = value.ToString(); + var parameterString = parameter.ToString(); + + return enumString.Equals(parameterString, StringComparison.OrdinalIgnoreCase); + } + + public object ConvertBack(object value, Type targetType, object parameter, string language) + { + throw new NotImplementedException(); + } + } +} diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Views/DashboardPage.xaml b/src/settings-ui/Settings.UI/SettingsXAML/Views/DashboardPage.xaml index 545122a56b..643811d2fc 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Views/DashboardPage.xaml +++ b/src/settings-ui/Settings.UI/SettingsXAML/Views/DashboardPage.xaml @@ -19,6 +19,7 @@ x:Key="ModuleItemTemplateSelector" ActivationTemplate="{StaticResource ModuleItemActivationTemplate}" ShortcutTemplate="{StaticResource ModuleItemShortcutTemplate}" /> + @@ -276,9 +277,36 @@ Padding="0" VerticalAlignment="Top" DividerVisibility="Collapsed"> + + + + diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Views/DashboardPage.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/Views/DashboardPage.xaml.cs index a06e5838a4..0d7273f924 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Views/DashboardPage.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/Views/DashboardPage.xaml.cs @@ -66,5 +66,15 @@ namespace Microsoft.PowerToys.Settings.UI.Views App.GetOobeWindow().Activate(); } + + private void SortAlphabetical_Click(object sender, RoutedEventArgs e) + { + ViewModel.DashboardSortOrder = DashboardSortOrder.Alphabetical; + } + + private void SortByStatus_Click(object sender, RoutedEventArgs e) + { + ViewModel.DashboardSortOrder = DashboardSortOrder.ByStatus; + } } } diff --git a/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw b/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw index 95ce0b5f19..5f22c7e4dc 100644 --- a/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw +++ b/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw @@ -2999,7 +2999,7 @@ Right-click to remove the key combination, thereby deactivating the shortcut.Crosshairs fixed length (px) px = pixels - + Crosshairs orientation @@ -4462,6 +4462,18 @@ Activate by holding the key for the character you want to add an accent to, then Home + + Sort utilities + + + Alphabetically + + + By status + + + Sort utilities + Preview @@ -4770,7 +4782,7 @@ Copy a zoomed screen with Ctrl+C or save it by typing Ctrl+S. Crop the copy or s Smooth zoomed image - + Specify the initial level of magnification when zooming in diff --git a/src/settings-ui/Settings.UI/ViewModels/DashboardViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/DashboardViewModel.cs index 344eaa183f..15a50b5dbd 100644 --- a/src/settings-ui/Settings.UI/ViewModels/DashboardViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/DashboardViewModel.cs @@ -62,6 +62,23 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels } } + private DashboardSortOrder _dashboardSortOrder = DashboardSortOrder.Alphabetical; + + public DashboardSortOrder DashboardSortOrder + { + get => generalSettingsConfig.DashboardSortOrder; + set + { + if (Set(ref _dashboardSortOrder, value)) + { + generalSettingsConfig.DashboardSortOrder = value; + OutGoingGeneralSettings outgoing = new OutGoingGeneralSettings(generalSettingsConfig); + SendConfigMSG(outgoing.ToString()); + RefreshModuleList(); + } + } + } + private ISettingsRepository _settingsRepository; private GeneralSettings generalSettingsConfig; private Windows.ApplicationModel.Resources.ResourceLoader resourceLoader = Helpers.ResourceLoaderInstance.ResourceLoader; @@ -73,14 +90,13 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels generalSettingsConfig = settingsRepository.SettingsConfig; generalSettingsConfig.AddEnabledModuleChangeNotification(ModuleEnabledChangedOnSettingsPage); + // Initialize dashboard sort order from settings + _dashboardSortOrder = generalSettingsConfig.DashboardSortOrder; + // set the callback functions value to handle outgoing IPC message. SendConfigMSG = ipcMSGCallBackFunc; - foreach (ModuleType moduleType in Enum.GetValues()) - { - AddDashboardListItem(moduleType); - } - + RefreshModuleList(); GetShortcutModules(); } @@ -113,21 +129,39 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels GlobalHotkeyConflictManager.Instance?.RequestAllConflicts(); } - private void AddDashboardListItem(ModuleType moduleType) + private void RefreshModuleList() { - GpoRuleConfigured gpo = ModuleHelper.GetModuleGpoConfiguration(moduleType); - var newItem = new DashboardListItem() + AllModules.Clear(); + + var moduleItems = new List(); + + foreach (ModuleType moduleType in Enum.GetValues()) { - Tag = moduleType, - Label = resourceLoader.GetString(ModuleHelper.GetModuleLabelResourceName(moduleType)), - IsEnabled = gpo == GpoRuleConfigured.Enabled || (gpo != GpoRuleConfigured.Disabled && ModuleHelper.GetIsModuleEnabled(generalSettingsConfig, moduleType)), - IsLocked = gpo == GpoRuleConfigured.Enabled || gpo == GpoRuleConfigured.Disabled, - Icon = ModuleHelper.GetModuleTypeFluentIconName(moduleType), - DashboardModuleItems = GetModuleItems(moduleType), + GpoRuleConfigured gpo = ModuleHelper.GetModuleGpoConfiguration(moduleType); + var newItem = new DashboardListItem() + { + Tag = moduleType, + Label = resourceLoader.GetString(ModuleHelper.GetModuleLabelResourceName(moduleType)), + IsEnabled = gpo == GpoRuleConfigured.Enabled || (gpo != GpoRuleConfigured.Disabled && ModuleHelper.GetIsModuleEnabled(generalSettingsConfig, moduleType)), + IsLocked = gpo == GpoRuleConfigured.Enabled || gpo == GpoRuleConfigured.Disabled, + Icon = ModuleHelper.GetModuleTypeFluentIconName(moduleType), + DashboardModuleItems = GetModuleItems(moduleType), + }; + newItem.EnabledChangedCallback = EnabledChangedOnUI; + moduleItems.Add(newItem); + } + + // Sort based on current sort order + var sortedItems = DashboardSortOrder switch + { + DashboardSortOrder.ByStatus => moduleItems.OrderByDescending(x => x.IsEnabled).ThenBy(x => x.Label), + _ => moduleItems.OrderBy(x => x.Label), // Default alphabetical }; - AllModules.Add(newItem); - newItem.EnabledChangedCallback = EnabledChangedOnUI; + foreach (var item in sortedItems) + { + AllModules.Add(item); + } } private void EnabledChangedOnUI(DashboardListItem dashboardListItem) @@ -149,6 +183,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels { try { + RefreshModuleList(); GetShortcutModules(); OnPropertyChanged(nameof(ShortcutModules));