/ src / settings-ui / Settings.UI / ViewModels / DashboardViewModel.cs
DashboardViewModel.cs
  1  // Copyright (c) Microsoft Corporation
  2  // The Microsoft Corporation licenses this file to you under the MIT license.
  3  // See the LICENSE file in the project root for more information.
  4  
  5  using System;
  6  using System.Collections.Generic;
  7  using System.Collections.ObjectModel;
  8  using System.IO.Abstractions;
  9  using System.Linq;
 10  using System.Threading.Tasks;
 11  using CommunityToolkit.WinUI.Controls;
 12  using global::PowerToys.GPOWrapper;
 13  using ManagedCommon;
 14  using Microsoft.PowerToys.Settings.UI.Controls;
 15  using Microsoft.PowerToys.Settings.UI.Helpers;
 16  using Microsoft.PowerToys.Settings.UI.Library;
 17  using Microsoft.PowerToys.Settings.UI.Library.Helpers;
 18  using Microsoft.PowerToys.Settings.UI.Library.HotkeyConflicts;
 19  using Microsoft.PowerToys.Settings.UI.Library.Interfaces;
 20  using Microsoft.PowerToys.Settings.UI.Library.Utilities;
 21  using Microsoft.PowerToys.Settings.UI.Services;
 22  using Microsoft.PowerToys.Settings.UI.Views;
 23  using Microsoft.UI.Dispatching;
 24  using Microsoft.UI.Xaml;
 25  using Microsoft.UI.Xaml.Controls;
 26  using Settings.UI.Library;
 27  
 28  namespace Microsoft.PowerToys.Settings.UI.ViewModels
 29  {
 30      public partial class DashboardViewModel : PageViewModelBase
 31      {
 32          private readonly object _sortLock = new object();
 33  
 34          protected override string ModuleName => "Dashboard";
 35  
 36          private DispatcherQueue dispatcher;
 37  
 38          public Func<string, int> SendConfigMSG { get; }
 39  
 40          public ObservableCollection<DashboardListItem> AllModules { get; set; } = new ObservableCollection<DashboardListItem>();
 41  
 42          public ObservableCollection<DashboardListItem> ShortcutModules { get; set; } = new ObservableCollection<DashboardListItem>();
 43  
 44          public ObservableCollection<DashboardListItem> ActionModules { get; set; } = new ObservableCollection<DashboardListItem>();
 45  
 46          public ObservableCollection<QuickAccessItem> QuickAccessItems => _quickAccessViewModel.Items;
 47  
 48          private readonly QuickAccessViewModel _quickAccessViewModel;
 49  
 50          // Master list of module items that is sorted and projected into AllModules.
 51          private List<DashboardListItem> _moduleItems = new List<DashboardListItem>();
 52  
 53          // Flag to prevent circular updates when a UI toggle triggers settings changes.
 54          private bool _isUpdatingFromUI;
 55  
 56          // Flag to prevent toggle operations during sorting to avoid race conditions.
 57          private bool _isSorting;
 58  
 59          private AllHotkeyConflictsData _allHotkeyConflictsData = new AllHotkeyConflictsData();
 60  
 61          public AllHotkeyConflictsData AllHotkeyConflictsData
 62          {
 63              get => _allHotkeyConflictsData;
 64              set
 65              {
 66                  if (Set(ref _allHotkeyConflictsData, value))
 67                  {
 68                      OnPropertyChanged();
 69                  }
 70              }
 71          }
 72  
 73          public string PowerToysVersion
 74          {
 75              get
 76              {
 77                  return Helper.GetProductVersion();
 78              }
 79          }
 80  
 81          private DashboardSortOrder _dashboardSortOrder = DashboardSortOrder.Alphabetical;
 82  
 83          public DashboardSortOrder DashboardSortOrder
 84          {
 85              get => generalSettingsConfig.DashboardSortOrder;
 86              set
 87              {
 88                  if (_dashboardSortOrder != value)
 89                  {
 90                      _dashboardSortOrder = value;
 91                      generalSettingsConfig.DashboardSortOrder = value;
 92                      OutGoingGeneralSettings outgoing = new OutGoingGeneralSettings(generalSettingsConfig);
 93  
 94                      SendConfigMSG(outgoing.ToString());
 95  
 96                      // Notify UI before sorting so menu updates its checked state
 97                      OnPropertyChanged(nameof(DashboardSortOrder));
 98  
 99                      SortModuleList();
100                  }
101              }
102          }
103  
104          private ISettingsRepository<GeneralSettings> _settingsRepository;
105          private GeneralSettings generalSettingsConfig;
106          private Windows.ApplicationModel.Resources.ResourceLoader resourceLoader = Helpers.ResourceLoaderInstance.ResourceLoader;
107  
108          public DashboardViewModel(ISettingsRepository<GeneralSettings> settingsRepository, Func<string, int> ipcMSGCallBackFunc)
109          {
110              dispatcher = DispatcherQueue.GetForCurrentThread();
111              _settingsRepository = settingsRepository;
112              generalSettingsConfig = settingsRepository.SettingsConfig;
113  
114              _settingsRepository.SettingsChanged += OnSettingsChanged;
115  
116              // Initialize dashboard sort order from settings
117              _dashboardSortOrder = generalSettingsConfig.DashboardSortOrder;
118  
119              // set the callback functions value to handle outgoing IPC message.
120              SendConfigMSG = ipcMSGCallBackFunc;
121  
122              _quickAccessViewModel = new QuickAccessViewModel(
123                  _settingsRepository,
124                  new Microsoft.PowerToys.Settings.UI.Controls.QuickAccessLauncher(App.IsElevated),
125                  moduleType => Helpers.ModuleGpoHelper.GetModuleGpoConfiguration(moduleType) == global::PowerToys.GPOWrapper.GpoRuleConfigured.Disabled,
126                  resourceLoader);
127  
128              BuildModuleList();
129              SortModuleList();
130              RefreshShortcutModules();
131          }
132  
133          private void OnSettingsChanged(GeneralSettings newSettings)
134          {
135              dispatcher.TryEnqueue(() =>
136              {
137                  generalSettingsConfig = newSettings;
138  
139                  // Update local field and notify UI if sort order changed
140                  if (_dashboardSortOrder != generalSettingsConfig.DashboardSortOrder)
141                  {
142                      _dashboardSortOrder = generalSettingsConfig.DashboardSortOrder;
143                      OnPropertyChanged(nameof(DashboardSortOrder));
144                  }
145  
146                  ModuleEnabledChangedOnSettingsPage();
147              });
148          }
149  
150          protected override void OnConflictsUpdated(object sender, AllHotkeyConflictsEventArgs e)
151          {
152              dispatcher.TryEnqueue(() =>
153              {
154                  var allConflictData = e.Conflicts;
155                  foreach (var inAppConflict in allConflictData.InAppConflicts)
156                  {
157                      var hotkey = inAppConflict.Hotkey;
158                      var hotkeySetting = new HotkeySettings(hotkey.Win, hotkey.Ctrl, hotkey.Alt, hotkey.Shift, hotkey.Key);
159                      inAppConflict.ConflictIgnored = HotkeyConflictIgnoreHelper.IsIgnoringConflicts(hotkeySetting);
160                  }
161  
162                  foreach (var systemConflict in allConflictData.SystemConflicts)
163                  {
164                      var hotkey = systemConflict.Hotkey;
165                      var hotkeySetting = new HotkeySettings(hotkey.Win, hotkey.Ctrl, hotkey.Alt, hotkey.Shift, hotkey.Key);
166                      systemConflict.ConflictIgnored = HotkeyConflictIgnoreHelper.IsIgnoringConflicts(hotkeySetting);
167                  }
168  
169                  AllHotkeyConflictsData = e.Conflicts ?? new AllHotkeyConflictsData();
170              });
171          }
172  
173          private void RequestConflictData()
174          {
175              // Request current conflicts data
176              GlobalHotkeyConflictManager.Instance?.RequestAllConflicts();
177          }
178  
179          /// <summary>
180          /// Builds the master list of module items. Called once during initialization.
181          /// Each module item contains its configuration, enabled state, and GPO lock status.
182          /// </summary>
183          private void BuildModuleList()
184          {
185              _moduleItems.Clear();
186  
187              foreach (ModuleType moduleType in Enum.GetValues<ModuleType>())
188              {
189                  if (moduleType == ModuleType.GeneralSettings)
190                  {
191                      continue;
192                  }
193  
194                  GpoRuleConfigured gpo = ModuleGpoHelper.GetModuleGpoConfiguration(moduleType);
195                  var newItem = new DashboardListItem()
196                  {
197                      Tag = moduleType,
198                      Label = resourceLoader.GetString(ModuleHelper.GetModuleLabelResourceName(moduleType)),
199                      IsEnabled = gpo == GpoRuleConfigured.Enabled || (gpo != GpoRuleConfigured.Disabled && ModuleHelper.GetIsModuleEnabled(generalSettingsConfig, moduleType)),
200                      IsLocked = gpo == GpoRuleConfigured.Enabled || gpo == GpoRuleConfigured.Disabled,
201                      Icon = ModuleHelper.GetModuleTypeFluentIconName(moduleType),
202                      IsNew = moduleType == ModuleType.CursorWrap || moduleType == ModuleType.PowerDisplay,
203                      DashboardModuleItems = GetModuleItems(moduleType),
204                      ClickCommand = new RelayCommand<object>(DashboardListItemClick),
205                  };
206                  newItem.EnabledChangedCallback = EnabledChangedOnUI;
207                  _moduleItems.Add(newItem);
208              }
209          }
210  
211          /// <summary>
212          /// Sorts the module list according to the current sort order and updates the AllModules collection.
213          /// On first call, populates AllModules. On subsequent calls, uses Move() to reorder items in-place
214          /// to avoid destroying and recreating UI elements.
215          /// Temporarily disables interaction on all items during sorting to prevent race conditions.
216          /// </summary>
217          private void SortModuleList()
218          {
219              if (_isSorting)
220              {
221                  return;
222              }
223  
224              lock (_sortLock)
225              {
226                  _isSorting = true;
227                  try
228                  {
229                      var sortedItems = (DashboardSortOrder switch
230                      {
231                          DashboardSortOrder.ByStatus => _moduleItems.OrderByDescending(x => x.IsEnabled).ThenBy(x => x.Label),
232                          _ => _moduleItems.OrderBy(x => x.Label), // Default alphabetical
233                      }).ToList();
234  
235                      // If AllModules is empty (first load), just populate it.
236                      if (AllModules.Count == 0)
237                      {
238                          foreach (var item in sortedItems)
239                          {
240                              AllModules.Add(item);
241                          }
242  
243                          return;
244                      }
245  
246                      // Otherwise, update the collection in place using Move to avoid UI glitches.
247                      for (int i = 0; i < sortedItems.Count; i++)
248                      {
249                          var currentItem = sortedItems[i];
250                          var currentIndex = AllModules.IndexOf(currentItem);
251  
252                          if (currentIndex != -1 && currentIndex != i)
253                          {
254                              AllModules.Move(currentIndex, i);
255                          }
256                      }
257                  }
258                  finally
259                  {
260                      // Use dispatcher to reset flag after UI updates complete
261                      dispatcher.TryEnqueue(DispatcherQueuePriority.Low, () =>
262                      {
263                          _isSorting = false;
264                      });
265                  }
266              }
267          }
268  
269          /// <summary>
270          /// Refreshes module enabled/locked states by re-reading GPO configuration. Only
271          /// updates properties that have actually changed to minimize UI notifications
272          /// then re-sorts the list according to the current sort order.
273          /// </summary>
274          private void RefreshModuleList()
275          {
276              foreach (var item in _moduleItems)
277              {
278                  GpoRuleConfigured gpo = ModuleGpoHelper.GetModuleGpoConfiguration(item.Tag);
279  
280                  // GPO can force-enable (Enabled) or force-disable (Disabled) a module.
281                  // If Enabled: module is on and the user cannot disable it.
282                  // If Disabled: module is off and the user cannot enable it.
283                  // Otherwise, the setting is unlocked and the user can enable/disable it.
284                  bool newEnabledState = gpo == GpoRuleConfigured.Enabled || (gpo != GpoRuleConfigured.Disabled && ModuleHelper.GetIsModuleEnabled(generalSettingsConfig, item.Tag));
285  
286                  // Lock the toggle when GPO is controlling the module.
287                  bool newLockedState = gpo == GpoRuleConfigured.Enabled || gpo == GpoRuleConfigured.Disabled;
288  
289                  // Only update if there's an actual change to minimize UI notifications.
290                  if (item.IsEnabled != newEnabledState)
291                  {
292                      item.UpdateStatus(newEnabledState);
293                  }
294  
295                  if (item.IsLocked != newLockedState)
296                  {
297                      item.IsLocked = newLockedState;
298                  }
299              }
300  
301              SortModuleList();
302          }
303  
304          /// <summary>
305          /// Callback invoked when a user toggles a module's enabled state in the UI.
306          /// Sets the _isUpdatingFromUI flag to prevent circular updates, then updates
307          /// settings, re-sorts if needed, and refreshes dependent collections.
308          /// </summary>
309          private void EnabledChangedOnUI(ModuleListItem item)
310          {
311              var dashboardListItem = (DashboardListItem)item;
312              var isEnabled = dashboardListItem.IsEnabled;
313  
314              // Ignore toggle operations during sorting to prevent race conditions.
315              // Revert the toggle state since UI already changed due to TwoWay binding.
316              if (_isSorting)
317              {
318                  dashboardListItem.UpdateStatus(!isEnabled);
319                  return;
320              }
321  
322              _isUpdatingFromUI = true;
323              try
324              {
325                  // Send optimized IPC message with only the module status update
326                  // Format: {"module_status": {"ModuleName": true/false}}
327                  string moduleKey = ModuleHelper.GetModuleKey(dashboardListItem.Tag);
328                  string moduleStatusJson = $"{{\"module_status\": {{\"{moduleKey}\": {isEnabled.ToString().ToLowerInvariant()}}}}}";
329                  SendConfigMSG(moduleStatusJson);
330  
331                  // Update local settings config to keep UI in sync
332                  ModuleHelper.SetIsModuleEnabled(generalSettingsConfig, dashboardListItem.Tag, isEnabled);
333  
334                  if (dashboardListItem.Tag == ModuleType.NewPlus && isEnabled == true)
335                  {
336                      var settingsUtils = SettingsUtils.Default;
337                      var settings = NewPlusViewModel.LoadSettings(settingsUtils);
338                      NewPlusViewModel.CopyTemplateExamples(settings.Properties.TemplateLocation.Value);
339                  }
340  
341                  // Re-sort only required if sorting by enabled status.
342                  if (DashboardSortOrder == DashboardSortOrder.ByStatus)
343                  {
344                      SortModuleList();
345                  }
346  
347                  // Always refresh shortcuts/actions to reflect enabled state changes.
348                  RefreshShortcutModules();
349  
350                  // Request updated conflicts after module state change.
351                  RequestConflictData();
352              }
353              finally
354              {
355                  _isUpdatingFromUI = false;
356              }
357          }
358  
359          /// <summary>
360          /// Callback invoked when module enabled state changes from other parts of the
361          /// settings UI. Ignores the notification if it was triggered by a UI toggle
362          /// we're already handling, to prevent circular updates.
363          /// </summary>
364          public void ModuleEnabledChangedOnSettingsPage()
365          {
366              // Ignore if this was triggered by a UI change that we're already handling.
367              if (_isUpdatingFromUI)
368              {
369                  return;
370              }
371  
372              try
373              {
374                  RefreshModuleList();
375                  RefreshShortcutModules();
376  
377                  OnPropertyChanged(nameof(ShortcutModules));
378  
379                  // Request updated conflicts after module state change.
380                  RequestConflictData();
381              }
382              catch (Exception ex)
383              {
384                  Logger.LogError($"Updating active/disabled modules list failed: {ex.Message}");
385              }
386          }
387  
388          /// <summary>
389          /// Rebuilds ShortcutModules and ActionModules collections by filtering AllModules
390          /// to only include enabled modules and their respective shortcut/action items.
391          /// </summary>
392          private void RefreshShortcutModules()
393          {
394              ShortcutModules.Clear();
395              ActionModules.Clear();
396  
397              foreach (var x in AllModules.Where(x => x.IsEnabled))
398              {
399                  var filteredItems = x.DashboardModuleItems
400                      .Where(m => m is DashboardModuleShortcutItem || m is DashboardModuleActivationItem)
401                      .ToList();
402  
403                  if (filteredItems.Count != 0)
404                  {
405                      var newItem = new DashboardListItem
406                      {
407                          Icon = x.Icon,
408                          IsLocked = x.IsLocked,
409                          Label = x.Label,
410                          Tag = x.Tag,
411                          IsEnabled = x.IsEnabled,
412                          DashboardModuleItems = new ObservableCollection<DashboardModuleItem>(filteredItems),
413                      };
414  
415                      ShortcutModules.Add(newItem);
416                      newItem.EnabledChangedCallback = x.EnabledChangedCallback;
417                  }
418              }
419  
420              foreach (var x in AllModules.Where(x => x.IsEnabled))
421              {
422                  var filteredItems = x.DashboardModuleItems
423                      .Where(m => m is DashboardModuleButtonItem)
424                      .ToList();
425  
426                  if (filteredItems.Count != 0)
427                  {
428                      var newItem = new DashboardListItem
429                      {
430                          Icon = x.Icon,
431                          IsLocked = x.IsLocked,
432                          Label = x.Label,
433                          Tag = x.Tag,
434                          IsEnabled = x.IsEnabled,
435                          DashboardModuleItems = new ObservableCollection<DashboardModuleItem>(filteredItems),
436                      };
437  
438                      ActionModules.Add(newItem);
439                      newItem.EnabledChangedCallback = x.EnabledChangedCallback;
440                  }
441              }
442          }
443  
444          private ObservableCollection<DashboardModuleItem> GetModuleItems(ModuleType moduleType)
445          {
446              return moduleType switch
447              {
448                  ModuleType.AdvancedPaste => GetModuleItemsAdvancedPaste(),
449                  ModuleType.AlwaysOnTop => GetModuleItemsAlwaysOnTop(),
450                  ModuleType.CmdPal => GetModuleItemsCmdPal(),
451                  ModuleType.ColorPicker => GetModuleItemsColorPicker(),
452                  ModuleType.CropAndLock => GetModuleItemsCropAndLock(),
453                  ModuleType.EnvironmentVariables => GetModuleItemsEnvironmentVariables(),
454                  ModuleType.FancyZones => GetModuleItemsFancyZones(),
455                  ModuleType.FindMyMouse => GetModuleItemsFindMyMouse(),
456                  ModuleType.Hosts => GetModuleItemsHosts(),
457                  ModuleType.LightSwitch => GetModuleItemsLightSwitch(),
458                  ModuleType.MouseHighlighter => GetModuleItemsMouseHighlighter(),
459                  ModuleType.MouseJump => GetModuleItemsMouseJump(),
460                  ModuleType.MousePointerCrosshairs => GetModuleItemsMousePointerCrosshairs(),
461                  ModuleType.Peek => GetModuleItemsPeek(),
462                  ModuleType.PowerDisplay => GetModuleItemsPowerDisplay(),
463                  ModuleType.PowerLauncher => GetModuleItemsPowerLauncher(),
464                  ModuleType.PowerAccent => GetModuleItemsPowerAccent(),
465                  ModuleType.Workspaces => GetModuleItemsWorkspaces(),
466                  ModuleType.RegistryPreview => GetModuleItemsRegistryPreview(),
467                  ModuleType.MeasureTool => GetModuleItemsMeasureTool(),
468                  ModuleType.ShortcutGuide => GetModuleItemsShortcutGuide(),
469                  ModuleType.PowerOCR => GetModuleItemsPowerOCR(),
470                  _ => new ObservableCollection<DashboardModuleItem>(), // never called, all values listed above
471              };
472          }
473  
474          private ObservableCollection<DashboardModuleItem> GetModuleItemsAlwaysOnTop()
475          {
476              ISettingsRepository<AlwaysOnTopSettings> moduleSettingsRepository = SettingsRepository<AlwaysOnTopSettings>.GetInstance(SettingsUtils.Default);
477              var list = new List<DashboardModuleItem>
478              {
479                  new DashboardModuleShortcutItem() { Label = resourceLoader.GetString("AlwaysOnTop_ShortDescription"), Shortcut = moduleSettingsRepository.SettingsConfig.Properties.Hotkey.Value.GetKeysList() },
480              };
481              return new ObservableCollection<DashboardModuleItem>(list);
482          }
483  
484          private ObservableCollection<DashboardModuleItem> GetModuleItemsCmdPal()
485          {
486              var hotkey = new CmdPalProperties().Hotkey;
487  
488              var list = new List<DashboardModuleItem>
489              {
490                  new DashboardModuleShortcutItem() { Label = resourceLoader.GetString("CmdPal_ActivationDescription"), Shortcut = hotkey.GetKeysList() },
491              };
492              return new ObservableCollection<DashboardModuleItem>(list);
493          }
494  
495          private ObservableCollection<DashboardModuleItem> GetModuleItemsColorPicker()
496          {
497              ISettingsRepository<ColorPickerSettings> moduleSettingsRepository = SettingsRepository<ColorPickerSettings>.GetInstance(SettingsUtils.Default);
498              var settings = moduleSettingsRepository.SettingsConfig;
499              var hotkey = settings.Properties.ActivationShortcut;
500              var list = new List<DashboardModuleItem>
501              {
502                  new DashboardModuleShortcutItem() { Label = resourceLoader.GetString("ColorPicker_ShortDescription"), Shortcut = hotkey.GetKeysList() },
503              };
504              return new ObservableCollection<DashboardModuleItem>(list);
505          }
506  
507          private ObservableCollection<DashboardModuleItem> GetModuleItemsLightSwitch()
508          {
509              ISettingsRepository<LightSwitchSettings> moduleSettingsRepository = SettingsRepository<LightSwitchSettings>.GetInstance(SettingsUtils.Default);
510              var settings = moduleSettingsRepository.SettingsConfig;
511              var list = new List<DashboardModuleItem>
512              {
513                  new DashboardModuleShortcutItem() { Label = resourceLoader.GetString("LightSwitch_ForceDarkMode"), Shortcut = settings.Properties.ToggleThemeHotkey.Value.GetKeysList() },
514              };
515              return new ObservableCollection<DashboardModuleItem>(list);
516          }
517  
518          private ObservableCollection<DashboardModuleItem> GetModuleItemsCropAndLock()
519          {
520              ISettingsRepository<CropAndLockSettings> moduleSettingsRepository = SettingsRepository<CropAndLockSettings>.GetInstance(SettingsUtils.Default);
521              var settings = moduleSettingsRepository.SettingsConfig;
522              var list = new List<DashboardModuleItem>
523              {
524                  new DashboardModuleShortcutItem() { Label = resourceLoader.GetString("CropAndLock_Thumbnail"), Shortcut = settings.Properties.ThumbnailHotkey.Value.GetKeysList() },
525                  new DashboardModuleShortcutItem() { Label = resourceLoader.GetString("CropAndLock_Reparent"), Shortcut = settings.Properties.ReparentHotkey.Value.GetKeysList() },
526                  new DashboardModuleShortcutItem() { Label = resourceLoader.GetString("CropAndLock_Screenshot"), Shortcut = settings.Properties.ScreenshotHotkey.Value.GetKeysList() },
527              };
528              return new ObservableCollection<DashboardModuleItem>(list);
529          }
530  
531          private ObservableCollection<DashboardModuleItem> GetModuleItemsEnvironmentVariables()
532          {
533              var list = new List<DashboardModuleItem>
534              {
535                  new DashboardModuleButtonItem() { ButtonTitle = resourceLoader.GetString("EnvironmentVariables_LaunchButtonControl/Header"), IsButtonDescriptionVisible = true, ButtonDescription = resourceLoader.GetString("EnvironmentVariables_LaunchButtonControl/Description"), ButtonGlyph = "ms-appx:///Assets/Settings/Icons/EnvironmentVariables.png", ButtonClickHandler = EnvironmentVariablesLaunchClicked },
536              };
537              return new ObservableCollection<DashboardModuleItem>(list);
538          }
539  
540          private ObservableCollection<DashboardModuleItem> GetModuleItemsFancyZones()
541          {
542              ISettingsRepository<FancyZonesSettings> moduleSettingsRepository = SettingsRepository<FancyZonesSettings>.GetInstance(SettingsUtils.Default);
543              var settings = moduleSettingsRepository.SettingsConfig;
544              string activationMode = $"{resourceLoader.GetString(settings.Properties.FancyzonesShiftDrag.Value ? "FancyZones_ActivationShiftDrag" : "FancyZones_ActivationNoShiftDrag")}.";
545  
546              var list = new List<DashboardModuleItem>
547              {
548                  new DashboardModuleActivationItem() { Label = resourceLoader.GetString("Activate_Zones"), Activation = activationMode },
549                  new DashboardModuleShortcutItem() { Label = resourceLoader.GetString("FancyZones_OpenEditor"), Shortcut = settings.Properties.FancyzonesEditorHotkey.Value.GetKeysList() },
550                  new DashboardModuleButtonItem() { ButtonTitle = resourceLoader.GetString("FancyZones_LaunchEditorButtonControl/Header"), IsButtonDescriptionVisible = true, ButtonDescription = resourceLoader.GetString("FancyZones_LaunchEditorButtonControl/Description"), ButtonGlyph = "ms-appx:///Assets/Settings/Icons/FancyZones.png", ButtonClickHandler = FancyZoneLaunchClicked },
551              };
552              return new ObservableCollection<DashboardModuleItem>(list);
553          }
554  
555          private ObservableCollection<DashboardModuleItem> GetModuleItemsFindMyMouse()
556          {
557              ISettingsRepository<FindMyMouseSettings> moduleSettingsRepository = SettingsRepository<FindMyMouseSettings>.GetInstance(SettingsUtils.Default);
558              string shortDescription = resourceLoader.GetString("FindMyMouse_ShortDescription");
559              var settings = moduleSettingsRepository.SettingsConfig;
560              var activationMethod = settings.Properties.ActivationMethod.Value;
561              var list = new List<DashboardModuleItem>();
562              if (activationMethod == 3)
563              {
564                  var hotkey = settings.Properties.ActivationShortcut;
565                  list.Add(new DashboardModuleShortcutItem() { Label = shortDescription, Shortcut = hotkey.GetKeysList() });
566              }
567              else
568              {
569                  string activation = string.Empty;
570                  switch (activationMethod)
571                  {
572                      case 2: activation = resourceLoader.GetString("MouseUtils_FindMyMouse_ActivationShakeMouse/Content"); break;
573                      case 1: activation = resourceLoader.GetString("MouseUtils_FindMyMouse_ActivationDoubleRightControlPress/Content"); break;
574                      case 0:
575                      default: activation = resourceLoader.GetString("MouseUtils_FindMyMouse_ActivationDoubleControlPress/Content"); break;
576                  }
577  
578                  list.Add(new DashboardModuleActivationItem() { Label = resourceLoader.GetString("Dashboard_Activation"), Activation = activation });
579              }
580  
581              return new ObservableCollection<DashboardModuleItem>(list);
582          }
583  
584          private ObservableCollection<DashboardModuleItem> GetModuleItemsHosts()
585          {
586              var list = new List<DashboardModuleItem>
587              {
588                  new DashboardModuleButtonItem() { ButtonTitle = resourceLoader.GetString("Hosts_LaunchButtonControl/Header"), IsButtonDescriptionVisible = true, ButtonDescription = resourceLoader.GetString("Hosts_LaunchButtonControl/Description"), ButtonGlyph = "ms-appx:///Assets/Settings/Icons/Hosts.png", ButtonClickHandler = HostLaunchClicked },
589              };
590              return new ObservableCollection<DashboardModuleItem>(list);
591          }
592  
593          private ObservableCollection<DashboardModuleItem> GetModuleItemsMouseHighlighter()
594          {
595              ISettingsRepository<MouseHighlighterSettings> moduleSettingsRepository = SettingsRepository<MouseHighlighterSettings>.GetInstance(SettingsUtils.Default);
596              var list = new List<DashboardModuleItem>
597              {
598                  new DashboardModuleShortcutItem() { Label = resourceLoader.GetString("MouseHighlighter_ShortDescription"), Shortcut = moduleSettingsRepository.SettingsConfig.Properties.ActivationShortcut.GetKeysList() },
599              };
600              return new ObservableCollection<DashboardModuleItem>(list);
601          }
602  
603          private ObservableCollection<DashboardModuleItem> GetModuleItemsMouseJump()
604          {
605              ISettingsRepository<MouseJumpSettings> moduleSettingsRepository = SettingsRepository<MouseJumpSettings>.GetInstance(SettingsUtils.Default);
606              var list = new List<DashboardModuleItem>
607              {
608                  new DashboardModuleShortcutItem() { Label = resourceLoader.GetString("MouseJump_ShortDescription"), Shortcut = moduleSettingsRepository.SettingsConfig.Properties.ActivationShortcut.GetKeysList() },
609              };
610              return new ObservableCollection<DashboardModuleItem>(list);
611          }
612  
613          private ObservableCollection<DashboardModuleItem> GetModuleItemsMousePointerCrosshairs()
614          {
615              ISettingsRepository<MousePointerCrosshairsSettings> moduleSettingsRepository = SettingsRepository<MousePointerCrosshairsSettings>.GetInstance(SettingsUtils.Default);
616              var list = new List<DashboardModuleItem>
617              {
618                  new DashboardModuleShortcutItem() { Label = resourceLoader.GetString("MouseCrosshairs_ShortDescription"), Shortcut = moduleSettingsRepository.SettingsConfig.Properties.ActivationShortcut.GetKeysList() },
619              };
620              return new ObservableCollection<DashboardModuleItem>(list);
621          }
622  
623          private ObservableCollection<DashboardModuleItem> GetModuleItemsAdvancedPaste()
624          {
625              ISettingsRepository<AdvancedPasteSettings> moduleSettingsRepository = SettingsRepository<AdvancedPasteSettings>.GetInstance(SettingsUtils.Default);
626              var list = new List<DashboardModuleItem>
627              {
628                  new DashboardModuleShortcutItem() { Label = resourceLoader.GetString("AdvancedPasteUI_Shortcut/Header"), Shortcut = moduleSettingsRepository.SettingsConfig.Properties.AdvancedPasteUIShortcut.GetKeysList() },
629                  new DashboardModuleShortcutItem() { Label = resourceLoader.GetString("PasteAsPlainText_Shortcut/Header"), Shortcut = moduleSettingsRepository.SettingsConfig.Properties.PasteAsPlainTextShortcut.GetKeysList() },
630              };
631  
632              if (moduleSettingsRepository.SettingsConfig.Properties.PasteAsMarkdownShortcut.GetKeysList().Count > 0)
633              {
634                  list.Add(new DashboardModuleShortcutItem() { Label = resourceLoader.GetString("PasteAsMarkdown_Shortcut/Header"), Shortcut = moduleSettingsRepository.SettingsConfig.Properties.PasteAsMarkdownShortcut.GetKeysList() });
635              }
636  
637              if (moduleSettingsRepository.SettingsConfig.Properties.PasteAsJsonShortcut.GetKeysList().Count > 0)
638              {
639                  list.Add(new DashboardModuleShortcutItem() { Label = resourceLoader.GetString("PasteAsJson_Shortcut/Header"), Shortcut = moduleSettingsRepository.SettingsConfig.Properties.PasteAsJsonShortcut.GetKeysList() });
640              }
641  
642              return new ObservableCollection<DashboardModuleItem>(list);
643          }
644  
645          private ObservableCollection<DashboardModuleItem> GetModuleItemsPeek()
646          {
647              ISettingsRepository<PeekSettings> moduleSettingsRepository = SettingsRepository<PeekSettings>.GetInstance(SettingsUtils.Default);
648              var list = new List<DashboardModuleItem>
649              {
650                  new DashboardModuleShortcutItem() { Label = resourceLoader.GetString("Peek_ShortDescription"), Shortcut = moduleSettingsRepository.SettingsConfig.Properties.ActivationShortcut.GetKeysList() },
651              };
652              return new ObservableCollection<DashboardModuleItem>(list);
653          }
654  
655          private ObservableCollection<DashboardModuleItem> GetModuleItemsPowerLauncher()
656          {
657              ISettingsRepository<PowerLauncherSettings> moduleSettingsRepository = SettingsRepository<PowerLauncherSettings>.GetInstance(SettingsUtils.Default);
658              var list = new List<DashboardModuleItem>
659              {
660                  new DashboardModuleShortcutItem() { Label = resourceLoader.GetString("Run_ShortDescription"), Shortcut = moduleSettingsRepository.SettingsConfig.Properties.OpenPowerLauncher.GetKeysList() },
661              };
662              return new ObservableCollection<DashboardModuleItem>(list);
663          }
664  
665          private ObservableCollection<DashboardModuleItem> GetModuleItemsPowerAccent()
666          {
667              var settingsUtils = SettingsUtils.Default;
668              PowerAccentSettings moduleSettings = settingsUtils.GetSettingsOrDefault<PowerAccentSettings>(PowerAccentSettings.ModuleName);
669              var activationMethod = moduleSettings.Properties.ActivationKey;
670              string activation = string.Empty;
671              switch (activationMethod)
672              {
673                  case Library.Enumerations.PowerAccentActivationKey.LeftRightArrow: activation = resourceLoader.GetString("QuickAccent_Activation_Key_Arrows/Content"); break;
674                  case Library.Enumerations.PowerAccentActivationKey.Space: activation = resourceLoader.GetString("QuickAccent_Activation_Key_Space/Content"); break;
675                  case Library.Enumerations.PowerAccentActivationKey.Both: activation = resourceLoader.GetString("QuickAccent_Activation_Key_Either/Content"); break;
676              }
677  
678              var list = new List<DashboardModuleItem>
679              {
680                  new DashboardModuleActivationItem() { Label = resourceLoader.GetString("Dashboard_Activation"), Activation = activation },
681              };
682              return new ObservableCollection<DashboardModuleItem>(list);
683          }
684  
685          private ObservableCollection<DashboardModuleItem> GetModuleItemsWorkspaces()
686          {
687              ISettingsRepository<WorkspacesSettings> moduleSettingsRepository = SettingsRepository<WorkspacesSettings>.GetInstance(SettingsUtils.Default);
688              var settings = moduleSettingsRepository.SettingsConfig;
689  
690              var list = new List<DashboardModuleItem>
691              {
692                  new DashboardModuleShortcutItem() { Label = resourceLoader.GetString("Workspaces_ShortDescription"), Shortcut = settings.Properties.Hotkey.Value.GetKeysList() },
693                  new DashboardModuleButtonItem() { ButtonTitle = resourceLoader.GetString("Workspaces_LaunchEditorButtonControl/Header"), IsButtonDescriptionVisible = true, ButtonDescription = resourceLoader.GetString("FancyZones_LaunchEditorButtonControl/Description"), ButtonGlyph = "ms-appx:///Assets/Settings/Icons/Workspaces.png", ButtonClickHandler = WorkspacesLaunchClicked },
694              };
695              return new ObservableCollection<DashboardModuleItem>(list);
696          }
697  
698          private ObservableCollection<DashboardModuleItem> GetModuleItemsRegistryPreview()
699          {
700              var list = new List<DashboardModuleItem>
701              {
702                  new DashboardModuleButtonItem() { ButtonTitle = resourceLoader.GetString("RegistryPreview_LaunchButtonControl/Header"), ButtonGlyph = "ms-appx:///Assets/Settings/Icons/RegistryPreview.png",  ButtonClickHandler = RegistryPreviewLaunchClicked },
703              };
704              return new ObservableCollection<DashboardModuleItem>(list);
705          }
706  
707          private ObservableCollection<DashboardModuleItem> GetModuleItemsMeasureTool()
708          {
709              ISettingsRepository<MeasureToolSettings> moduleSettingsRepository = SettingsRepository<MeasureToolSettings>.GetInstance(SettingsUtils.Default);
710              var list = new List<DashboardModuleItem>
711              {
712                  new DashboardModuleShortcutItem() { Label = resourceLoader.GetString("ScreenRuler_ShortDescription"), Shortcut = moduleSettingsRepository.SettingsConfig.Properties.ActivationShortcut.GetKeysList() },
713              };
714              return new ObservableCollection<DashboardModuleItem>(list);
715          }
716  
717          private ObservableCollection<DashboardModuleItem> GetModuleItemsShortcutGuide()
718          {
719              ISettingsRepository<ShortcutGuideSettings> moduleSettingsRepository = SettingsRepository<ShortcutGuideSettings>.GetInstance(SettingsUtils.Default);
720  
721              var shortcut = moduleSettingsRepository.SettingsConfig.Properties.UseLegacyPressWinKeyBehavior.Value
722                  ? new List<object> { 92 } // Right Windows key code
723                  : moduleSettingsRepository.SettingsConfig.Properties.OpenShortcutGuide.GetKeysList();
724  
725              var list = new List<DashboardModuleItem>
726              {
727                  new DashboardModuleShortcutItem() { Label = resourceLoader.GetString("ShortcutGuide_ShortDescription"), Shortcut = shortcut },
728              };
729              return new ObservableCollection<DashboardModuleItem>(list);
730          }
731  
732          private ObservableCollection<DashboardModuleItem> GetModuleItemsPowerOCR()
733          {
734              ISettingsRepository<PowerOcrSettings> moduleSettingsRepository = SettingsRepository<PowerOcrSettings>.GetInstance(SettingsUtils.Default);
735              var list = new List<DashboardModuleItem>
736              {
737                  new DashboardModuleShortcutItem() { Label = resourceLoader.GetString("PowerOcr_ShortDescription"), Shortcut = moduleSettingsRepository.SettingsConfig.Properties.ActivationShortcut.GetKeysList() },
738              };
739              return new ObservableCollection<DashboardModuleItem>(list);
740          }
741  
742          private ObservableCollection<DashboardModuleItem> GetModuleItemsPowerDisplay()
743          {
744              ISettingsRepository<PowerDisplaySettings> moduleSettingsRepository = SettingsRepository<PowerDisplaySettings>.GetInstance(SettingsUtils.Default);
745              var settings = moduleSettingsRepository.SettingsConfig;
746              var list = new List<DashboardModuleItem>
747              {
748                  new DashboardModuleShortcutItem() { Label = resourceLoader.GetString("PowerDisplay_ToggleWindow"), Shortcut = settings.Properties.ActivationShortcut.GetKeysList() },
749                  new DashboardModuleButtonItem() { ButtonTitle = resourceLoader.GetString("PowerDisplay_LaunchButtonControl/Header"), IsButtonDescriptionVisible = true, ButtonDescription = resourceLoader.GetString("PowerDisplay_LaunchButtonControl/Description"), ButtonGlyph = "ms-appx:///Assets/Settings/Icons/PowerDisplay.png", ButtonClickHandler = PowerDisplayLaunchClicked },
750              };
751              return new ObservableCollection<DashboardModuleItem>(list);
752          }
753  
754          internal void SWVersionButtonClicked()
755          {
756              NavigationService.Navigate(typeof(GeneralPage));
757          }
758  
759          private void EnvironmentVariablesLaunchClicked(object sender, RoutedEventArgs e)
760          {
761              var settingsUtils = SettingsUtils.Default;
762              var environmentVariablesViewModel = new EnvironmentVariablesViewModel(settingsUtils, SettingsRepository<GeneralSettings>.GetInstance(settingsUtils), SettingsRepository<EnvironmentVariablesSettings>.GetInstance(settingsUtils), ShellPage.SendDefaultIPCMessage, App.IsElevated);
763              environmentVariablesViewModel.Launch();
764          }
765  
766          private void HostLaunchClicked(object sender, RoutedEventArgs e)
767          {
768              var settingsUtils = SettingsUtils.Default;
769              var hostsViewModel = new HostsViewModel(settingsUtils, SettingsRepository<GeneralSettings>.GetInstance(settingsUtils), SettingsRepository<HostsSettings>.GetInstance(settingsUtils), ShellPage.SendDefaultIPCMessage, App.IsElevated);
770              hostsViewModel.Launch();
771          }
772  
773          private void FancyZoneLaunchClicked(object sender, RoutedEventArgs e)
774          {
775              // send message to launch the zones editor;
776              SendConfigMSG("{\"action\":{\"FancyZones\":{\"action_name\":\"ToggledFZEditor\", \"value\":\"\"}}}");
777          }
778  
779          private void WorkspacesLaunchClicked(object sender, RoutedEventArgs e)
780          {
781              // send message to launch the Workspaces editor;
782              SendConfigMSG("{\"action\":{\"Workspaces\":{\"action_name\":\"LaunchEditor\", \"value\":\"\"}}}");
783          }
784  
785          private void RegistryPreviewLaunchClicked(object sender, RoutedEventArgs e)
786          {
787              var actionName = "Launch";
788              SendConfigMSG("{\"action\":{\"RegistryPreview\":{\"action_name\":\"" + actionName + "\", \"value\":\"\"}}}");
789          }
790  
791          private void PowerDisplayLaunchClicked(object sender, RoutedEventArgs e)
792          {
793              var actionName = "Launch";
794              SendConfigMSG("{\"action\":{\"PowerDisplay\":{\"action_name\":\"" + actionName + "\", \"value\":\"\"}}}");
795          }
796  
797          internal void DashboardListItemClick(object sender)
798          {
799              if (sender is ModuleType moduleType)
800              {
801                  NavigationService.Navigate(ModuleGpoHelper.GetModulePageType(moduleType));
802              }
803          }
804  
805          public override void Dispose()
806          {
807              base.Dispose();
808              if (_settingsRepository != null)
809              {
810                  _settingsRepository.SettingsChanged -= OnSettingsChanged;
811              }
812  
813              GC.SuppressFinalize(this);
814          }
815      }
816  }