diff --git a/src/modules/ShortcutGuide/ShortcutGuide.IndexYmlGenerator/IndexYmlGenerator.cs b/src/modules/ShortcutGuide/ShortcutGuide.IndexYmlGenerator/IndexYmlGenerator.cs index d8a1179733..1997bc1023 100644 --- a/src/modules/ShortcutGuide/ShortcutGuide.IndexYmlGenerator/IndexYmlGenerator.cs +++ b/src/modules/ShortcutGuide/ShortcutGuide.IndexYmlGenerator/IndexYmlGenerator.cs @@ -21,7 +21,7 @@ namespace ShortcutGuide.IndexYmlGenerator // Todo: Exception handling public static void CreateIndexYmlFile() { - string path = ManifestInterpreter.GetPathOfInterpretations(); + string path = ManifestInterpreter.PathOfManifestFiles; if (File.Exists(Path.Combine(path, "index.yml"))) { File.Delete(Path.Combine(path, "index.yml")); diff --git a/src/modules/ShortcutGuide/ShortcutGuide.Ui/Helpers/DisplayHelper.cs b/src/modules/ShortcutGuide/ShortcutGuide.Ui/Helpers/DisplayHelper.cs index cc968333cb..e638ff1c7e 100644 --- a/src/modules/ShortcutGuide/ShortcutGuide.Ui/Helpers/DisplayHelper.cs +++ b/src/modules/ShortcutGuide/ShortcutGuide.Ui/Helpers/DisplayHelper.cs @@ -9,21 +9,28 @@ namespace ShortcutGuide.Helpers { public static class DisplayHelper { - private enum MonitorFromWindowDwFlags - { - MONITOR_DEFAULTTONEAREST = 2, - } - + /// + /// Returns the display work area for the monitor that contains the specified window. + /// + /// The window handle + /// A element containing the display area public static Rect GetWorkAreaForDisplayWithWindow(nint hwnd) { _foundMonitorIndex = -1; _monitorIndex = 0; - var monitor = NativeMethods.MonitorFromWindow(hwnd, (int)MonitorFromWindowDwFlags.MONITOR_DEFAULTTONEAREST); + var monitor = NativeMethods.MonitorFromWindow(hwnd, (int)NativeMethods.MonitorFromWindowDwFlags.MONITOR_DEFAULTTONEAREST); NativeMethods.EnumDisplayMonitors(nint.Zero, nint.Zero, MonitorEnumProc, new NativeMethods.LPARAM(monitor)); return MonitorInfo.GetDisplayMonitors()[_foundMonitorIndex].RectWork; } + /// + /// The index of the monitor that contains the specified window. -1 indicates that no monitor was found (yet). + /// private static int _foundMonitorIndex = -1; + + /// + /// The index of the monitor in the enumeration. This is used to find the correct monitor in the list of monitors. + /// private static int _monitorIndex; private static bool MonitorEnumProc(nint hMonitor, nint hdcMonitor, ref NativeMethods.RECT lprcMonitor, nint dwData) diff --git a/src/modules/ShortcutGuide/ShortcutGuide.Ui/Helpers/DpiHelper.cs b/src/modules/ShortcutGuide/ShortcutGuide.Ui/Helpers/DpiHelper.cs index c63f449af1..78f5fa8b12 100644 --- a/src/modules/ShortcutGuide/ShortcutGuide.Ui/Helpers/DpiHelper.cs +++ b/src/modules/ShortcutGuide/ShortcutGuide.Ui/Helpers/DpiHelper.cs @@ -20,13 +20,13 @@ namespace ShortcutGuide.Helpers return (float)dpi / DEFAULT_DPI; } - public static long GetScreenDPIForWindow(int hwnd, ref int dpi) + private static long GetScreenDPIForWindow(int hwnd, ref int dpi) { var targetMonitor = NativeMethods.MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST); return GetScreenDPIForMonitor(targetMonitor.ToInt32(), ref dpi); } - public static long GetScreenDPIForMonitor(int targetMonitor, ref int dpi) + private static long GetScreenDPIForMonitor(int targetMonitor, ref int dpi) { if (targetMonitor != 0) { diff --git a/src/modules/ShortcutGuide/ShortcutGuide.Ui/Helpers/ManifestInterpreter.cs b/src/modules/ShortcutGuide/ShortcutGuide.Ui/Helpers/ManifestInterpreter.cs index 70497c6b07..e45ccb58d1 100644 --- a/src/modules/ShortcutGuide/ShortcutGuide.Ui/Helpers/ManifestInterpreter.cs +++ b/src/modules/ShortcutGuide/ShortcutGuide.Ui/Helpers/ManifestInterpreter.cs @@ -15,18 +15,34 @@ using YamlDotNet.Serialization; namespace ShortcutGuide.Helpers { + /// + /// Helps to interpret the manifest files for the Shortcut Guide. + /// public class ManifestInterpreter { // Todo: Get language from settings or environment variable, default to "en-US" + + /// + /// Gets the language used for the manifest files. + /// public static string Language => "en-US"; + /// + /// Returns the shortcuts for a specific application. + /// + /// + /// The method should only be called if the application is known to have a shortcuts file. + /// + /// The manifest id. + /// The deserialized shortcuts file. + /// The requested file was not found. public static ShortcutFile GetShortcutsOfApplication(string applicationName) { - string path = GetPathOfInterpretations(); + string path = PathOfManifestFiles; IEnumerable files = Directory.EnumerateFiles(path, applicationName + ".*.yml") ?? throw new FileNotFoundException($"The file for the application '{applicationName}' was not found in '{path}'."); - IEnumerable filesEnumerable = files as string[] ?? files.ToArray(); + IEnumerable filesEnumerable = files as string[] ?? [.. files]; return filesEnumerable.Any(f => f.EndsWith($".{Language}.yml", StringComparison.InvariantCulture)) ? YamlToShortcutList(File.ReadAllText(Path.Combine(path, applicationName + $".{Language}.yml"))) : filesEnumerable.Any(f => f.EndsWith(".en-US.yml", StringComparison.InvariantCulture)) @@ -34,25 +50,38 @@ namespace ShortcutGuide.Helpers : throw new FileNotFoundException($"The file for the application '{applicationName}' was not found in '{path}' with the language '{Language}' or 'en-US'."); } - public static ShortcutFile YamlToShortcutList(string content) + /// + /// Deserializes the content of a YAML file to a . + /// + /// The content of the YAML file. + /// A deserialized object. + private static ShortcutFile YamlToShortcutList(string content) { Deserializer deserializer = new(); return deserializer.Deserialize(content); } - public static string GetPathOfInterpretations() - { - return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Microsoft", "WinGet", "KeyboardShortcuts"); - } + /// + /// Gets the path to the directory where the manifest files are stored. + /// + public static string PathOfManifestFiles => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Microsoft", "WinGet", "KeyboardShortcuts"); + /// + /// Retrieves the index YAML file that contains the list of all applications and their shortcuts. + /// + /// A deserialized object. public static IndexFile GetIndexYamlFile() { - string path = GetPathOfInterpretations(); + string path = PathOfManifestFiles; string content = File.ReadAllText(Path.Combine(path, "index.yml")); Deserializer deserializer = new(); return deserializer.Deserialize(content); } + /// + /// Retrieves all application IDs that should be displayed, based on the foreground window and background processes. + /// + /// An array of all application IDs. public static string[] GetAllCurrentApplicationIds() { nint handle = NativeMethods.GetForegroundWindow(); diff --git a/src/modules/ShortcutGuide/ShortcutGuide.Ui/Helpers/PowerToysShortcutsPopulator.cs b/src/modules/ShortcutGuide/ShortcutGuide.Ui/Helpers/PowerToysShortcutsPopulator.cs index 64c1a0860e..b96bcfa8b9 100644 --- a/src/modules/ShortcutGuide/ShortcutGuide.Ui/Helpers/PowerToysShortcutsPopulator.cs +++ b/src/modules/ShortcutGuide/ShortcutGuide.Ui/Helpers/PowerToysShortcutsPopulator.cs @@ -5,132 +5,147 @@ using System; using System.Globalization; using System.IO; +using System.Text; using System.Text.RegularExpressions; using Microsoft.PowerToys.Settings.UI.Library; using static ShortcutGuide.Helpers.ResourceLoaderInstance; namespace ShortcutGuide.Helpers { + /// + /// Populates the PowerToys shortcuts in the manifest files. + /// internal sealed partial class PowerToysShortcutsPopulator { + /// + /// Populates the PowerToys shortcuts in the manifest files. + /// public static void Populate() { - string path = Path.Combine(ManifestInterpreter.GetPathOfInterpretations(), $"Microsoft.PowerToys.{ManifestInterpreter.Language}.yml"); + string path = Path.Combine(ManifestInterpreter.PathOfManifestFiles, $"Microsoft.PowerToys.{ManifestInterpreter.Language}.yml"); - string content = File.ReadAllText(path); + StringBuilder content = new(File.ReadAllText(path)); const string populateStartString = "# "; const string populateEndString = "# "; - content = PopulateRegex().Replace(content, populateStartString + Environment.NewLine); + content = new(PopulateRegex().Replace(content.ToString(), populateStartString + Environment.NewLine)); ISettingsUtils settingsUtils = new SettingsUtils(); EnabledModules enabledModules = SettingsRepository.GetInstance(settingsUtils).SettingsConfig.Enabled; if (enabledModules.AdvancedPaste) { AdvancedPasteProperties advancedPasteProperties = SettingsRepository.GetInstance(settingsUtils).SettingsConfig.Properties; - content = HotkeySettingsToYaml(advancedPasteProperties.AdvancedPasteUIShortcut, SettingsResourceLoader.GetString("AdvancedPaste/ModuleTitle"), content, SettingsResourceLoader.GetString("AdvancedPasteUI_Shortcut/Header")); - content = HotkeySettingsToYaml(advancedPasteProperties.PasteAsPlainTextShortcut, SettingsResourceLoader.GetString("AdvancedPaste/ModuleTitle"), content, SettingsResourceLoader.GetString("PasteAsPlainText_Shortcut/Header")); - content = HotkeySettingsToYaml(advancedPasteProperties.PasteAsMarkdownShortcut, SettingsResourceLoader.GetString("AdvancedPaste/ModuleTitle"), content, SettingsResourceLoader.GetString("PasteAsMarkdown_Shortcut/Header")); - content = HotkeySettingsToYaml(advancedPasteProperties.PasteAsJsonShortcut, SettingsResourceLoader.GetString("AdvancedPaste/ModuleTitle"), content, SettingsResourceLoader.GetString("PasteAsJson_Shortcut/Header")); + content.Append(HotkeySettingsToYaml(advancedPasteProperties.AdvancedPasteUIShortcut, SettingsResourceLoader.GetString("AdvancedPaste/ModuleTitle"), SettingsResourceLoader.GetString("AdvancedPasteUI_Shortcut/Header"))); + content.Append(HotkeySettingsToYaml(advancedPasteProperties.PasteAsPlainTextShortcut, SettingsResourceLoader.GetString("AdvancedPaste/ModuleTitle"), SettingsResourceLoader.GetString("PasteAsPlainText_Shortcut/Header"))); + content.Append(HotkeySettingsToYaml(advancedPasteProperties.PasteAsMarkdownShortcut, SettingsResourceLoader.GetString("AdvancedPaste/ModuleTitle"), SettingsResourceLoader.GetString("PasteAsMarkdown_Shortcut/Header"))); + content.Append(HotkeySettingsToYaml(advancedPasteProperties.PasteAsJsonShortcut, SettingsResourceLoader.GetString("AdvancedPaste/ModuleTitle"), SettingsResourceLoader.GetString("PasteAsJson_Shortcut/Header"))); if (advancedPasteProperties.AdditionalActions.ImageToText.IsShown) { - content = HotkeySettingsToYaml(advancedPasteProperties.AdditionalActions.ImageToText.Shortcut, SettingsResourceLoader.GetString("AdvancedPaste/ModuleTitle"), content, SettingsResourceLoader.GetString("ImageToText/Header")); + content.Append(HotkeySettingsToYaml(advancedPasteProperties.AdditionalActions.ImageToText.Shortcut, SettingsResourceLoader.GetString("AdvancedPaste/ModuleTitle"), SettingsResourceLoader.GetString("ImageToText/Header"))); } if (advancedPasteProperties.AdditionalActions.PasteAsFile.IsShown) { - content = HotkeySettingsToYaml(advancedPasteProperties.AdditionalActions.PasteAsFile.PasteAsTxtFile.Shortcut, SettingsResourceLoader.GetString("AdvancedPaste/ModuleTitle"), content, SettingsResourceLoader.GetString("PasteAsTxtFile/Header")); - content = HotkeySettingsToYaml(advancedPasteProperties.AdditionalActions.PasteAsFile.PasteAsPngFile.Shortcut, SettingsResourceLoader.GetString("AdvancedPaste/ModuleTitle"), content, SettingsResourceLoader.GetString("PasteAsPngFile/Header")); - content = HotkeySettingsToYaml(advancedPasteProperties.AdditionalActions.PasteAsFile.PasteAsHtmlFile.Shortcut, SettingsResourceLoader.GetString("AdvancedPaste/ModuleTitle"), content, SettingsResourceLoader.GetString("PasteAsHtmlFile/Header")); + content.Append(HotkeySettingsToYaml(advancedPasteProperties.AdditionalActions.PasteAsFile.PasteAsTxtFile.Shortcut, SettingsResourceLoader.GetString("AdvancedPaste/ModuleTitle"), SettingsResourceLoader.GetString("PasteAsTxtFile/Header"))); + content.Append(HotkeySettingsToYaml(advancedPasteProperties.AdditionalActions.PasteAsFile.PasteAsPngFile.Shortcut, SettingsResourceLoader.GetString("AdvancedPaste/ModuleTitle"), SettingsResourceLoader.GetString("PasteAsPngFile/Header"))); + content.Append(HotkeySettingsToYaml(advancedPasteProperties.AdditionalActions.PasteAsFile.PasteAsHtmlFile.Shortcut, SettingsResourceLoader.GetString("AdvancedPaste/ModuleTitle"), SettingsResourceLoader.GetString("PasteAsHtmlFile/Header"))); } if (advancedPasteProperties.AdditionalActions.Transcode.IsShown) { - content = HotkeySettingsToYaml(advancedPasteProperties.AdditionalActions.Transcode.TranscodeToMp3.Shortcut, SettingsResourceLoader.GetString("AdvancedPaste/ModuleTitle"), content, SettingsResourceLoader.GetString("TranscodeToMp3/Header")); - content = HotkeySettingsToYaml(advancedPasteProperties.AdditionalActions.Transcode.TranscodeToMp4.Shortcut, SettingsResourceLoader.GetString("AdvancedPaste/ModuleTitle"), content, SettingsResourceLoader.GetString("TranscodeToMp4/Header")); + content.Append(HotkeySettingsToYaml(advancedPasteProperties.AdditionalActions.Transcode.TranscodeToMp3.Shortcut, SettingsResourceLoader.GetString("AdvancedPaste/ModuleTitle"), SettingsResourceLoader.GetString("TranscodeToMp3/Header"))); + content.Append(HotkeySettingsToYaml(advancedPasteProperties.AdditionalActions.Transcode.TranscodeToMp4.Shortcut, SettingsResourceLoader.GetString("AdvancedPaste/ModuleTitle"), SettingsResourceLoader.GetString("TranscodeToMp4/Header"))); } } if (enabledModules.AlwaysOnTop) { - content = HotkeySettingsToYaml(SettingsRepository.GetInstance(settingsUtils).SettingsConfig.Properties.Hotkey, SettingsResourceLoader.GetString("AlwaysOnTop/ModuleTitle"), content, SettingsResourceLoader.GetString("AlwaysOnTop_ShortDescription")); + content.Append(HotkeySettingsToYaml(SettingsRepository.GetInstance(settingsUtils).SettingsConfig.Properties.Hotkey, SettingsResourceLoader.GetString("AlwaysOnTop/ModuleTitle"), SettingsResourceLoader.GetString("AlwaysOnTop_ShortDescription"))); } if (enabledModules.ColorPicker) { - content = HotkeySettingsToYaml(SettingsRepository.GetInstance(settingsUtils).SettingsConfig.Properties.ActivationShortcut, SettingsResourceLoader.GetString("ColorPicker/ModuleTitle"), content, SettingsResourceLoader.GetString("ColorPicker_ShortDescription")); + content.Append(HotkeySettingsToYaml(SettingsRepository.GetInstance(settingsUtils).SettingsConfig.Properties.ActivationShortcut, SettingsResourceLoader.GetString("ColorPicker/ModuleTitle"), SettingsResourceLoader.GetString("ColorPicker_ShortDescription"))); } if (enabledModules.CmdPal) { - content = HotkeySettingsToYaml(new CmdPalProperties().Hotkey, SettingsResourceLoader.GetString("CmdPal/ModuleTitle"), content); + content.Append(HotkeySettingsToYaml(new CmdPalProperties().Hotkey, SettingsResourceLoader.GetString("CmdPal/ModuleTitle"))); } if (enabledModules.CropAndLock) { CropAndLockProperties cropAndLockProperties = SettingsRepository.GetInstance(settingsUtils).SettingsConfig.Properties; - content = HotkeySettingsToYaml(cropAndLockProperties.ThumbnailHotkey, SettingsResourceLoader.GetString("CropAndLock/ModuleTitle"), content, SettingsResourceLoader.GetString("CropAndLock_Thumbnail")); - content = HotkeySettingsToYaml(cropAndLockProperties.ReparentHotkey, SettingsResourceLoader.GetString("CropAndLock/ModuleTitle"), content, SettingsResourceLoader.GetString("CropAndLock_Reparent")); + content.Append(HotkeySettingsToYaml(cropAndLockProperties.ThumbnailHotkey, SettingsResourceLoader.GetString("CropAndLock/ModuleTitle"), SettingsResourceLoader.GetString("CropAndLock_Thumbnail"))); + content.Append(HotkeySettingsToYaml(cropAndLockProperties.ReparentHotkey, SettingsResourceLoader.GetString("CropAndLock/ModuleTitle"), SettingsResourceLoader.GetString("CropAndLock_Reparent"))); } if (enabledModules.FancyZones) { - content = HotkeySettingsToYaml(SettingsRepository.GetInstance(settingsUtils).SettingsConfig.Properties.FancyzonesEditorHotkey, SettingsResourceLoader.GetString("FancyZones/ModuleTitle"), content, SettingsResourceLoader.GetString("FancyZones_OpenEditor")); + content.Append(HotkeySettingsToYaml(SettingsRepository.GetInstance(settingsUtils).SettingsConfig.Properties.FancyzonesEditorHotkey, SettingsResourceLoader.GetString("FancyZones/ModuleTitle"), SettingsResourceLoader.GetString("FancyZones_OpenEditor"))); } if (enabledModules.MouseHighlighter) { - content = HotkeySettingsToYaml(SettingsRepository.GetInstance(settingsUtils).SettingsConfig.Properties.ActivationShortcut, SettingsResourceLoader.GetString("MouseUtils_MouseHighlighter/Header"), content, SettingsResourceLoader.GetString("MouseHighlighter_ShortDescription")); + content.Append(HotkeySettingsToYaml(SettingsRepository.GetInstance(settingsUtils).SettingsConfig.Properties.ActivationShortcut, SettingsResourceLoader.GetString("MouseUtils_MouseHighlighter/Header"), SettingsResourceLoader.GetString("MouseHighlighter_ShortDescription"))); } if (enabledModules.MouseJump) { - content = HotkeySettingsToYaml(SettingsRepository.GetInstance(settingsUtils).SettingsConfig.Properties.ActivationShortcut, SettingsResourceLoader.GetString("MouseUtils_MouseJump/Header"), content, SettingsResourceLoader.GetString("MouseJump_ShortDescription")); + content.Append(HotkeySettingsToYaml(SettingsRepository.GetInstance(settingsUtils).SettingsConfig.Properties.ActivationShortcut, SettingsResourceLoader.GetString("MouseUtils_MouseJump/Header"), SettingsResourceLoader.GetString("MouseJump_ShortDescription"))); } if (enabledModules.MousePointerCrosshairs) { - content = HotkeySettingsToYaml(SettingsRepository.GetInstance(settingsUtils).SettingsConfig.Properties.ActivationShortcut, SettingsResourceLoader.GetString("MouseUtils_MousePointerCrosshairs/Header"), content, SettingsResourceLoader.GetString("MouseCrosshairs_ShortDescription")); + content.Append(HotkeySettingsToYaml(SettingsRepository.GetInstance(settingsUtils).SettingsConfig.Properties.ActivationShortcut, SettingsResourceLoader.GetString("MouseUtils_MousePointerCrosshairs/Header"), SettingsResourceLoader.GetString("MouseCrosshairs_ShortDescription"))); } if (enabledModules.Peek) { - content = HotkeySettingsToYaml(SettingsRepository.GetInstance(settingsUtils).SettingsConfig.Properties.ActivationShortcut, SettingsResourceLoader.GetString("Peek/ModuleTitle"), content); + content.Append(HotkeySettingsToYaml(SettingsRepository.GetInstance(settingsUtils).SettingsConfig.Properties.ActivationShortcut, SettingsResourceLoader.GetString("Peek/ModuleTitle"))); } if (enabledModules.PowerLauncher) { - content = HotkeySettingsToYaml(SettingsRepository.GetInstance(settingsUtils).SettingsConfig.Properties.OpenPowerLauncher, SettingsResourceLoader.GetString("PowerLauncher/ModuleTitle"), content); + content.Append(HotkeySettingsToYaml(SettingsRepository.GetInstance(settingsUtils).SettingsConfig.Properties.OpenPowerLauncher, SettingsResourceLoader.GetString("PowerLauncher/ModuleTitle"))); } if (enabledModules.MeasureTool) { - content = HotkeySettingsToYaml(SettingsRepository.GetInstance(settingsUtils).SettingsConfig.Properties.ActivationShortcut, SettingsResourceLoader.GetString("MeasureTool/ModuleTitle"), content, SettingsResourceLoader.GetString("ScreenRuler_ShortDescription")); + content.Append(HotkeySettingsToYaml(SettingsRepository.GetInstance(settingsUtils).SettingsConfig.Properties.ActivationShortcut, SettingsResourceLoader.GetString("MeasureTool/ModuleTitle"), SettingsResourceLoader.GetString("ScreenRuler_ShortDescription"))); } if (enabledModules.ShortcutGuide) { - content = HotkeySettingsToYaml(SettingsRepository.GetInstance(settingsUtils).SettingsConfig.Properties.DefaultOpenShortcutGuide, SettingsResourceLoader.GetString("ShortcutGuide/ModuleTitle"), content, SettingsResourceLoader.GetString("ShortcutGuide_ShortDescription")); + content.Append(HotkeySettingsToYaml(SettingsRepository.GetInstance(settingsUtils).SettingsConfig.Properties.DefaultOpenShortcutGuide, SettingsResourceLoader.GetString("ShortcutGuide/ModuleTitle"), SettingsResourceLoader.GetString("ShortcutGuide_ShortDescription"))); } if (enabledModules.PowerOcr) { - content = HotkeySettingsToYaml(SettingsRepository.GetInstance(settingsUtils).SettingsConfig.Properties.ActivationShortcut, SettingsResourceLoader.GetString("TextExtractor/ModuleTitle"), content, SettingsResourceLoader.GetString("PowerOcr_ShortDescription")); + content.Append(HotkeySettingsToYaml(SettingsRepository.GetInstance(settingsUtils).SettingsConfig.Properties.ActivationShortcut, SettingsResourceLoader.GetString("TextExtractor/ModuleTitle"), SettingsResourceLoader.GetString("PowerOcr_ShortDescription"))); } if (enabledModules.Workspaces) { - content = HotkeySettingsToYaml(SettingsRepository.GetInstance(settingsUtils).SettingsConfig.Properties.Hotkey, SettingsResourceLoader.GetString("Workspaces/ModuleTitle"), content, SettingsResourceLoader.GetString("Workspaces_ShortDescription")); + content.Append(HotkeySettingsToYaml(SettingsRepository.GetInstance(settingsUtils).SettingsConfig.Properties.Hotkey, SettingsResourceLoader.GetString("Workspaces/ModuleTitle"), SettingsResourceLoader.GetString("Workspaces_ShortDescription"))); } - content += populateEndString; + content.Append(populateEndString); - File.WriteAllText(path, content); + File.WriteAllText(path, content.ToString()); } - public static string HotkeySettingsToYaml(HotkeySettings hotkeySettings, string moduleName, string content, string? description = null) + /// + /// Converts the hotkey settings to a YAML format string for the manifest file. + /// + /// Object containing a hotkey from the settings. + /// The name of the PowerToys module. + /// Description of the action. + /// Yaml code for the manifest file. + private static string HotkeySettingsToYaml(HotkeySettings hotkeySettings, string moduleName, string? description = null) { + string content = string.Empty; content += " - Name: " + moduleName + Environment.NewLine; content += " Shortcut: " + Environment.NewLine; content += " - Win: " + hotkeySettings.Win.ToString() + Environment.NewLine; @@ -147,9 +162,10 @@ namespace ShortcutGuide.Helpers return content; } - public static string HotkeySettingsToYaml(KeyboardKeysProperty keyboardKeys, string moduleName, string content, string? description = null) + /// + private static string HotkeySettingsToYaml(KeyboardKeysProperty hotkeySettings, string moduleName, string? description = null) { - return HotkeySettingsToYaml(keyboardKeys.Value, moduleName, content, description); + return HotkeySettingsToYaml(hotkeySettings.Value, moduleName, description); } [GeneratedRegex(@"# [\s\S\n\r]*# ")] diff --git a/src/modules/ShortcutGuide/ShortcutGuide.Ui/Helpers/ResourceLoaderInstance.cs b/src/modules/ShortcutGuide/ShortcutGuide.Ui/Helpers/ResourceLoaderInstance.cs index 3441f76ef3..f4e18161e9 100644 --- a/src/modules/ShortcutGuide/ShortcutGuide.Ui/Helpers/ResourceLoaderInstance.cs +++ b/src/modules/ShortcutGuide/ShortcutGuide.Ui/Helpers/ResourceLoaderInstance.cs @@ -8,8 +8,14 @@ namespace ShortcutGuide.Helpers { internal static class ResourceLoaderInstance { + /// + /// Gets the resource loader for the Shortcut Guide module. + /// internal static ResourceLoader ResourceLoader { get; private set; } + /// + /// Gets the resource loader for the Settings module. + /// internal static ResourceLoader SettingsResourceLoader { get; private set; } static ResourceLoaderInstance() diff --git a/src/modules/ShortcutGuide/ShortcutGuide.Ui/Helpers/TasklistPositions.cs b/src/modules/ShortcutGuide/ShortcutGuide.Ui/Helpers/TasklistPositions.cs index 3056a0b28c..efbf444841 100644 --- a/src/modules/ShortcutGuide/ShortcutGuide.Ui/Helpers/TasklistPositions.cs +++ b/src/modules/ShortcutGuide/ShortcutGuide.Ui/Helpers/TasklistPositions.cs @@ -7,8 +7,15 @@ using TasklistButton = ShortcutGuide.NativeMethods.TasklistButton; namespace ShortcutGuide.Helpers { - internal sealed class TasklistPositions + /// + /// Provides methods to retrieve the positions of taskbar buttons on the current monitor. + /// + internal static class TasklistPositions { + /// + /// Retrieves the taskbar buttons for the current monitor. + /// + /// An array of the taskbar buttons. public static TasklistButton[] GetButtons() { var monitor = NativeMethods.MonitorFromWindow(MainWindow.WindowHwnd, 0); diff --git a/src/modules/ShortcutGuide/ShortcutGuide.Ui/Models/ShortcutEntry.cs b/src/modules/ShortcutGuide/ShortcutGuide.Ui/Models/ShortcutEntry.cs index 39b72caa75..7f268338e5 100644 --- a/src/modules/ShortcutGuide/ShortcutGuide.Ui/Models/ShortcutEntry.cs +++ b/src/modules/ShortcutGuide/ShortcutGuide.Ui/Models/ShortcutEntry.cs @@ -2,9 +2,11 @@ // 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 System.Collections.Generic; using System.Linq; using System.Text.Json.Serialization; +using System.Threading; using System.Threading.Tasks; using Microsoft.PowerToys.Settings.UI.Library.Utilities; using Microsoft.UI.Xaml; @@ -108,31 +110,11 @@ namespace ShortcutGuide.Models { List shortcutStackPanels = []; - async void AnimateTextBlock(TextBlock animatedTextBlock, string text, int delay = 500) - { - try - { - int index = 0; - - while (!ShortcutView.AnimationCancellationTokenSource.Token.IsCancellationRequested) - { - animatedTextBlock.Text = text[index].ToString(); - index = (index + 1) % text.Length; - await Task.Delay(delay); - } - } - catch - { - // ignored - } - } - for (int i = 0; i < shortcut.Shortcut.Length; i++) { ShortcutDescription shortcutEntry = shortcut.Shortcut[i]; - StackPanel shortcutStackPanel = new(); + StackPanel shortcutStackPanel = new() { Orientation = Orientation.Horizontal }; shortcutStackPanels.Add(shortcutStackPanel); - shortcutStackPanel.Orientation = Orientation.Horizontal; // If any entry is blank, we skip the whole shortcut if (shortcutEntry is { Ctrl: false, Alt: false, Shift: false, Win: false, Keys.Length: 0 }) @@ -184,94 +166,7 @@ namespace ShortcutGuide.Models foreach (string key in shortcutEntry.Keys) { - switch (key) - { - case "": - shortcutStackPanel.Children.Add(new BitmapIcon() { UriSource = new("ms-appx:///Assets/ShortcutGuide/CopilotKey.png") }); - break; - case "": - shortcutStackPanel.Children.Add(new BitmapIcon() { UriSource = new("ms-appx:///Assets/ShortcutGuide/OfficeKey.png"), Height = 20, Width = 20 }); - break; - case "": - AddNewTextToStackPanel("←"); - break; - case "": - AddNewTextToStackPanel("→"); - break; - case "": - AddNewTextToStackPanel("↑"); - break; - case "": - AddNewTextToStackPanel("↓"); - break; - case "": - AddNewTextToStackPanel("..."); - break; - case "": - TextBlock animatedTextBlock = new() - { - Text = "A", - Margin = new Thickness(3), - VerticalAlignment = VerticalAlignment.Center, - - // Use monospaced font to ensure the text doesn't move - FontFamily = new("Courier New"), - TextDecorations = TextDecorations.Underline, - }; - - shortcutStackPanel.Children.Add(animatedTextBlock); - - AnimateTextBlock(animatedTextBlock, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"); - break; - case "": - TextBlock arrowTextBlock = new() - { - Text = "→", - Margin = new Thickness(3), - VerticalAlignment = VerticalAlignment.Center, - }; - - shortcutStackPanel.Children.Add(arrowTextBlock); - - AnimateTextBlock(arrowTextBlock, "→↓←↑", 1000); - break; - case "": - TextBlock arrowLRTextBlock = new() - { - Text = "→", - Margin = new Thickness(3), - FontFamily = new("Courier New"), - VerticalAlignment = VerticalAlignment.Center, - }; - shortcutStackPanel.Children.Add(arrowLRTextBlock); - AnimateTextBlock(arrowLRTextBlock, "→←", 1000); - break; - case "": - TextBlock arrowUDTextBlock = new() - { - Text = "↑", - Margin = new Thickness(3), - FontFamily = new("Courier New"), - VerticalAlignment = VerticalAlignment.Center, - }; - shortcutStackPanel.Children.Add(arrowUDTextBlock); - AnimateTextBlock(arrowUDTextBlock, "↑↓", 1000); - break; - case { } name when name.StartsWith('<') && name.EndsWith('>'): - AddNewTextToStackPanel(name[1..^1]); - break; - case { } num when int.TryParse(num, out int parsedNum): - if (parsedNum == 0) - { - break; - } - - AddNewTextToStackPanel(Helper.GetKeyName((uint)parsedNum)); - break; - default: - AddNewTextToStackPanel(key); - break; - } + AddKeyToStackPanel(key, shortcutStackPanel); } continue; @@ -291,53 +186,190 @@ namespace ShortcutGuide.Models case <= 1: return new ShortcutTemplateDataObject(shortcut.Name, shortcut.Description ?? string.Empty, stackPanelToReturn, shortcut); default: + stackPanelToReturn = new StackPanel { - { - stackPanelToReturn = new StackPanel - { - Orientation = Orientation.Vertical, - }; + Orientation = Orientation.Vertical, + }; - foreach (StackPanel panel in shortcutStackPanels) - { - panel.Visibility = Visibility.Collapsed; - stackPanelToReturn.Children.Add(panel); - } - - shortcutStackPanels[0].Visibility = Visibility.Visible; - for (int i = 1; i < shortcutStackPanels.Count; i++) - { - shortcutStackPanels[i].Visibility = Visibility.Collapsed; - } - - async void AnimateStackPanels(StackPanel[] panels, int delay = 2000) - { - try - { - int index = 0; - while (!ShortcutView.AnimationCancellationTokenSource.Token.IsCancellationRequested) - { - foreach (StackPanel panel in panels) - { - panel.Visibility = Visibility.Collapsed; - } - - panels[index].Visibility = Visibility.Visible; - index = (index + 1) % panels.Length; - await Task.Delay(delay); - } - } - catch - { - // ignored - } - } - - AnimateStackPanels([.. shortcutStackPanels]); - } - - return new ShortcutTemplateDataObject(shortcut.Name, shortcut.Description ?? string.Empty, stackPanelToReturn, shortcut); + foreach (StackPanel panel in shortcutStackPanels) + { + panel.Visibility = Visibility.Collapsed; + stackPanelToReturn.Children.Add(panel); } + + shortcutStackPanels[0].Visibility = Visibility.Visible; + for (int i = 1; i < shortcutStackPanels.Count; i++) + { + shortcutStackPanels[i].Visibility = Visibility.Collapsed; + } + + AnimateStackPanels([.. shortcutStackPanels]); + + return new ShortcutTemplateDataObject(shortcut.Name, shortcut.Description ?? string.Empty, stackPanelToReturn, shortcut); + } + } + + /// + /// Transforms a key string into a visual representation in the stack panel. + /// + /// The string reprensentation of the key. + /// The stackpanel to add the key to. + private static void AddKeyToStackPanel(string key, StackPanel shortcutStackPanel) + { + switch (key) + { + case "": + shortcutStackPanel.Children.Add(new BitmapIcon() { UriSource = new("ms-appx:///Assets/ShortcutGuide/CopilotKey.png") }); + break; + case "": + shortcutStackPanel.Children.Add(new BitmapIcon() { UriSource = new("ms-appx:///Assets/ShortcutGuide/OfficeKey.png"), Height = 20, Width = 20 }); + break; + case "": + AddNewTextToStackPanel("←"); + break; + case "": + AddNewTextToStackPanel("→"); + break; + case "": + AddNewTextToStackPanel("↑"); + break; + case "": + AddNewTextToStackPanel("↓"); + break; + case "": + AddNewTextToStackPanel("..."); + break; + case "": + TextBlock animatedTextBlock = new() + { + Text = "A", + Margin = new Thickness(3), + VerticalAlignment = VerticalAlignment.Center, + + // Use monospaced font to ensure the text doesn't move + FontFamily = new("Courier New"), + TextDecorations = TextDecorations.Underline, + }; + + shortcutStackPanel.Children.Add(animatedTextBlock); + + AnimateTextBlock(animatedTextBlock, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"); + break; + case "": + TextBlock arrowTextBlock = new() + { + Text = "→", + Margin = new Thickness(3), + VerticalAlignment = VerticalAlignment.Center, + }; + + shortcutStackPanel.Children.Add(arrowTextBlock); + + AnimateTextBlock(arrowTextBlock, "→↓←↑", 1000); + break; + case "": + TextBlock arrowLRTextBlock = new() + { + Text = "→", + Margin = new Thickness(3), + FontFamily = new("Courier New"), + VerticalAlignment = VerticalAlignment.Center, + }; + shortcutStackPanel.Children.Add(arrowLRTextBlock); + AnimateTextBlock(arrowLRTextBlock, "→←", 1000); + break; + case "": + TextBlock arrowUDTextBlock = new() + { + Text = "↑", + Margin = new Thickness(3), + FontFamily = new("Courier New"), + VerticalAlignment = VerticalAlignment.Center, + }; + shortcutStackPanel.Children.Add(arrowUDTextBlock); + AnimateTextBlock(arrowUDTextBlock, "↑↓", 1000); + break; + case { } name when name.StartsWith('<') && name.EndsWith('>'): + AddNewTextToStackPanel(name[1..^1]); + break; + case { } num when int.TryParse(num, out int parsedNum): + if (parsedNum == 0) + { + break; + } + + AddNewTextToStackPanel(Helper.GetKeyName((uint)parsedNum)); + break; + default: + AddNewTextToStackPanel(key); + break; + } + + void AddNewTextToStackPanel(string text) + { + shortcutStackPanel.Children.Add(new TextBlock { Text = text, Margin = new Thickness(3), VerticalAlignment = VerticalAlignment.Center }); + } + } + + /// + /// Animates the text of a TextBlock by cycling through the characters of a given string. + /// + /// + /// This function runs asynchronously and will not block the UI thread. Exceptions that occur during the animation will be caught and ignored to prevent crashes. + /// + /// The textblock to animate. + /// The characters to cycle through. + /// The delay to the next animation frame. + private static async void AnimateTextBlock(TextBlock animatedTextBlock, string text, int delay = 500) + { + try + { + int index = 0; + CancellationToken cancellationToken = ShortcutView.AnimationCancellationTokenSource.Token; + + while (!cancellationToken.IsCancellationRequested) + { + animatedTextBlock.Text = text[index].ToString(); + index = (index + 1) % text.Length; + await Task.Delay(delay); + } + } + catch + { + // ignored + } + } + + /// + /// Animates the visibility of the stack panels one after another. + /// + /// + /// This function runs asynchronously and will not block the UI thread. Exceptions that occur during the animation will be caught and ignored to prevent crashes. + /// + /// The panels to animate. + /// The delay to the next animation frame. + private static async void AnimateStackPanels(StackPanel[] panels, int delay = 2000) + { + try + { + int index = 0; + CancellationToken cancellationToken = ShortcutView.AnimationCancellationTokenSource.Token; + + while (!cancellationToken.IsCancellationRequested) + { + foreach (StackPanel panel in panels) + { + panel.Visibility = Visibility.Collapsed; + } + + panels[index].Visibility = Visibility.Visible; + index = (index + 1) % panels.Length; + await Task.Delay(delay); + } + } + catch + { + // ignored } } } diff --git a/src/modules/ShortcutGuide/ShortcutGuide.Ui/Models/ShortcutPageParameters.cs b/src/modules/ShortcutGuide/ShortcutGuide.Ui/Models/ShortcutPageParameters.cs index f2faba001a..d88c4cda91 100644 --- a/src/modules/ShortcutGuide/ShortcutGuide.Ui/Models/ShortcutPageParameters.cs +++ b/src/modules/ShortcutGuide/ShortcutGuide.Ui/Models/ShortcutPageParameters.cs @@ -7,14 +7,29 @@ using System.Collections.Generic; namespace ShortcutGuide.Models { + /// + /// Represents the parameters for the shortcut page in the Shortcut Guide module. + /// internal struct ShortcutPageParameters { + /// + /// Gets or sets the content of the search box. + /// public static SearchFilterObservable SearchFilter = new(); + /// + /// Gets or sets the pinned shortcuts for the Shortcut Guide. + /// public static Dictionary> PinnedShortcuts = []; + /// + /// Gets or sets the name of the current page being displayed in the Shortcut Guide. + /// public static string CurrentPageName = string.Empty; + /// + /// The height of the frame that displays the shortcuts. + /// public static FrameHeightObservable FrameHeight = new(); internal sealed class SearchFilterObservable diff --git a/src/modules/ShortcutGuide/ShortcutGuide.Ui/NativeMethods.cs b/src/modules/ShortcutGuide/ShortcutGuide.Ui/NativeMethods.cs index 987912c464..e1d830dcc9 100644 --- a/src/modules/ShortcutGuide/ShortcutGuide.Ui/NativeMethods.cs +++ b/src/modules/ShortcutGuide/ShortcutGuide.Ui/NativeMethods.cs @@ -119,4 +119,9 @@ internal static partial class NativeMethods public int Keynum; } + + public enum MonitorFromWindowDwFlags + { + MONITOR_DEFAULTTONEAREST = 2, + } } diff --git a/src/modules/ShortcutGuide/ShortcutGuide.Ui/Program.cs b/src/modules/ShortcutGuide/ShortcutGuide.Ui/Program.cs index e2602e1f19..a8e4f61b0f 100644 --- a/src/modules/ShortcutGuide/ShortcutGuide.Ui/Program.cs +++ b/src/modules/ShortcutGuide/ShortcutGuide.Ui/Program.cs @@ -9,7 +9,6 @@ using System.Threading; using System.Windows; using ManagedCommon; using Microsoft.UI.Dispatching; -using Microsoft.UI.Xaml; using Microsoft.Windows.AppLifecycle; using ShortcutGuide.Helpers; using Application = Microsoft.UI.Xaml.Application; @@ -34,9 +33,9 @@ namespace ShortcutGuide return; } - if (!Directory.Exists(ManifestInterpreter.GetPathOfInterpretations())) + if (!Directory.Exists(ManifestInterpreter.PathOfManifestFiles)) { - Directory.CreateDirectory(ManifestInterpreter.GetPathOfInterpretations()); + Directory.CreateDirectory(ManifestInterpreter.PathOfManifestFiles); } if (NativeMethods.IsCurrentWindowExcludedFromShortcutGuide()) @@ -48,7 +47,7 @@ namespace ShortcutGuide // Todo: Handle error foreach (var file in InbuiltManifestFiles) { - File.Copy(Path.GetDirectoryName(Environment.ProcessPath) + "\\Assets\\ShortcutGuide\\" + file, ManifestInterpreter.GetPathOfInterpretations() + "\\" + file, true); + File.Copy(Path.GetDirectoryName(Environment.ProcessPath) + "\\Assets\\ShortcutGuide\\" + file, ManifestInterpreter.PathOfManifestFiles + "\\" + file, true); } Process indexGeneration = Process.Start(Path.GetDirectoryName(Environment.ProcessPath) + "\\PowerToys.ShortcutGuide.IndexYmlGenerator.exe"); @@ -56,7 +55,7 @@ namespace ShortcutGuide if (indexGeneration.ExitCode != 0) { Logger.LogError("Index generation failed with exit code: " + indexGeneration.ExitCode); - MessageBox.Show($"Shortcut Guide encountered an error while generating the index file. There is likely a corrupt shortcuts file in \"{ManifestInterpreter.GetPathOfInterpretations()}\". Try deleting this directory.", "Error displaying shortcuts", MessageBoxButton.OK, MessageBoxImage.Error); + MessageBox.Show($"Shortcut Guide encountered an error while generating the index file. There is likely a corrupt shortcuts file in \"{ManifestInterpreter.PathOfManifestFiles}\". Try deleting this directory.", "Error displaying shortcuts", MessageBoxButton.OK, MessageBoxImage.Error); return; } diff --git a/src/modules/ShortcutGuide/ShortcutGuide.Ui/ShortcutGuideXAML/MainWindow.xaml b/src/modules/ShortcutGuide/ShortcutGuide.Ui/ShortcutGuideXAML/MainWindow.xaml index 66323b6f4c..b527eba93f 100644 --- a/src/modules/ShortcutGuide/ShortcutGuide.Ui/ShortcutGuideXAML/MainWindow.xaml +++ b/src/modules/ShortcutGuide/ShortcutGuide.Ui/ShortcutGuideXAML/MainWindow.xaml @@ -33,7 +33,7 @@ - +