Refactoring, commenting and fixing some little lefrover bugs

This commit is contained in:
Aaron Junker
2025-07-28 16:12:36 +02:00
parent 56f056e492
commit 2f89281178
14 changed files with 402 additions and 298 deletions

View File

@@ -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"));

View File

@@ -9,21 +9,28 @@ namespace ShortcutGuide.Helpers
{
public static class DisplayHelper
{
private enum MonitorFromWindowDwFlags
{
MONITOR_DEFAULTTONEAREST = 2,
}
/// <summary>
/// Returns the display work area for the monitor that contains the specified window.
/// </summary>
/// <param name="hwnd">The window handle</param>
/// <returns>A <see cref="Rect"/> element containing the display area</returns>
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;
}
/// <summary>
/// The index of the monitor that contains the specified window. -1 indicates that no monitor was found (yet).
/// </summary>
private static int _foundMonitorIndex = -1;
/// <summary>
/// The index of the monitor in the enumeration. This is used to find the correct monitor in the list of monitors.
/// </summary>
private static int _monitorIndex;
private static bool MonitorEnumProc(nint hMonitor, nint hdcMonitor, ref NativeMethods.RECT lprcMonitor, nint dwData)

View File

@@ -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)
{

View File

@@ -15,18 +15,34 @@ using YamlDotNet.Serialization;
namespace ShortcutGuide.Helpers
{
/// <summary>
/// Helps to interpret the manifest files for the Shortcut Guide.
/// </summary>
public class ManifestInterpreter
{
// Todo: Get language from settings or environment variable, default to "en-US"
/// <summary>
/// Gets the language used for the manifest files.
/// </summary>
public static string Language => "en-US";
/// <summary>
/// Returns the shortcuts for a specific application.
/// </summary>
/// <remarks>
/// The method should only be called if the application is known to have a shortcuts file.
/// </remarks>
/// <param name="applicationName">The manifest id.</param>
/// <returns>The deserialized shortcuts file.</returns>
/// <exception cref="FileNotFoundException">The requested file was not found.</exception>
public static ShortcutFile GetShortcutsOfApplication(string applicationName)
{
string path = GetPathOfInterpretations();
string path = PathOfManifestFiles;
IEnumerable<string> files = Directory.EnumerateFiles(path, applicationName + ".*.yml") ??
throw new FileNotFoundException($"The file for the application '{applicationName}' was not found in '{path}'.");
IEnumerable<string> filesEnumerable = files as string[] ?? files.ToArray();
IEnumerable<string> 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)
/// <summary>
/// Deserializes the content of a YAML file to a <see cref="ShortcutFile"/>.
/// </summary>
/// <param name="content">The content of the YAML file.</param>
/// <returns>A deserialized <see cref="ShortcutFile"/> object.</returns>
private static ShortcutFile YamlToShortcutList(string content)
{
Deserializer deserializer = new();
return deserializer.Deserialize<ShortcutFile>(content);
}
public static string GetPathOfInterpretations()
{
return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Microsoft", "WinGet", "KeyboardShortcuts");
}
/// <summary>
/// Gets the path to the directory where the manifest files are stored.
/// </summary>
public static string PathOfManifestFiles => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Microsoft", "WinGet", "KeyboardShortcuts");
/// <summary>
/// Retrieves the index YAML file that contains the list of all applications and their shortcuts.
/// </summary>
/// <returns>A deserialized <see cref="IndexFile"/> object.</returns>
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<IndexFile>(content);
}
/// <summary>
/// Retrieves all application IDs that should be displayed, based on the foreground window and background processes.
/// </summary>
/// <returns>An array of all application IDs.</returns>
public static string[] GetAllCurrentApplicationIds()
{
nint handle = NativeMethods.GetForegroundWindow();

View File

@@ -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
{
/// <summary>
/// Populates the PowerToys shortcuts in the manifest files.
/// </summary>
internal sealed partial class PowerToysShortcutsPopulator
{
/// <summary>
/// Populates the PowerToys shortcuts in the manifest files.
/// </summary>
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 = "# <Populate start>";
const string populateEndString = "# <Populate end>";
content = PopulateRegex().Replace(content, populateStartString + Environment.NewLine);
content = new(PopulateRegex().Replace(content.ToString(), populateStartString + Environment.NewLine));
ISettingsUtils settingsUtils = new SettingsUtils();
EnabledModules enabledModules = SettingsRepository<GeneralSettings>.GetInstance(settingsUtils).SettingsConfig.Enabled;
if (enabledModules.AdvancedPaste)
{
AdvancedPasteProperties advancedPasteProperties = SettingsRepository<AdvancedPasteSettings>.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<AlwaysOnTopSettings>.GetInstance(settingsUtils).SettingsConfig.Properties.Hotkey, SettingsResourceLoader.GetString("AlwaysOnTop/ModuleTitle"), content, SettingsResourceLoader.GetString("AlwaysOnTop_ShortDescription"));
content.Append(HotkeySettingsToYaml(SettingsRepository<AlwaysOnTopSettings>.GetInstance(settingsUtils).SettingsConfig.Properties.Hotkey, SettingsResourceLoader.GetString("AlwaysOnTop/ModuleTitle"), SettingsResourceLoader.GetString("AlwaysOnTop_ShortDescription")));
}
if (enabledModules.ColorPicker)
{
content = HotkeySettingsToYaml(SettingsRepository<ColorPickerSettings>.GetInstance(settingsUtils).SettingsConfig.Properties.ActivationShortcut, SettingsResourceLoader.GetString("ColorPicker/ModuleTitle"), content, SettingsResourceLoader.GetString("ColorPicker_ShortDescription"));
content.Append(HotkeySettingsToYaml(SettingsRepository<ColorPickerSettings>.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<CropAndLockSettings>.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<FancyZonesSettings>.GetInstance(settingsUtils).SettingsConfig.Properties.FancyzonesEditorHotkey, SettingsResourceLoader.GetString("FancyZones/ModuleTitle"), content, SettingsResourceLoader.GetString("FancyZones_OpenEditor"));
content.Append(HotkeySettingsToYaml(SettingsRepository<FancyZonesSettings>.GetInstance(settingsUtils).SettingsConfig.Properties.FancyzonesEditorHotkey, SettingsResourceLoader.GetString("FancyZones/ModuleTitle"), SettingsResourceLoader.GetString("FancyZones_OpenEditor")));
}
if (enabledModules.MouseHighlighter)
{
content = HotkeySettingsToYaml(SettingsRepository<MouseHighlighterSettings>.GetInstance(settingsUtils).SettingsConfig.Properties.ActivationShortcut, SettingsResourceLoader.GetString("MouseUtils_MouseHighlighter/Header"), content, SettingsResourceLoader.GetString("MouseHighlighter_ShortDescription"));
content.Append(HotkeySettingsToYaml(SettingsRepository<MouseHighlighterSettings>.GetInstance(settingsUtils).SettingsConfig.Properties.ActivationShortcut, SettingsResourceLoader.GetString("MouseUtils_MouseHighlighter/Header"), SettingsResourceLoader.GetString("MouseHighlighter_ShortDescription")));
}
if (enabledModules.MouseJump)
{
content = HotkeySettingsToYaml(SettingsRepository<MouseJumpSettings>.GetInstance(settingsUtils).SettingsConfig.Properties.ActivationShortcut, SettingsResourceLoader.GetString("MouseUtils_MouseJump/Header"), content, SettingsResourceLoader.GetString("MouseJump_ShortDescription"));
content.Append(HotkeySettingsToYaml(SettingsRepository<MouseJumpSettings>.GetInstance(settingsUtils).SettingsConfig.Properties.ActivationShortcut, SettingsResourceLoader.GetString("MouseUtils_MouseJump/Header"), SettingsResourceLoader.GetString("MouseJump_ShortDescription")));
}
if (enabledModules.MousePointerCrosshairs)
{
content = HotkeySettingsToYaml(SettingsRepository<MousePointerCrosshairsSettings>.GetInstance(settingsUtils).SettingsConfig.Properties.ActivationShortcut, SettingsResourceLoader.GetString("MouseUtils_MousePointerCrosshairs/Header"), content, SettingsResourceLoader.GetString("MouseCrosshairs_ShortDescription"));
content.Append(HotkeySettingsToYaml(SettingsRepository<MousePointerCrosshairsSettings>.GetInstance(settingsUtils).SettingsConfig.Properties.ActivationShortcut, SettingsResourceLoader.GetString("MouseUtils_MousePointerCrosshairs/Header"), SettingsResourceLoader.GetString("MouseCrosshairs_ShortDescription")));
}
if (enabledModules.Peek)
{
content = HotkeySettingsToYaml(SettingsRepository<PeekSettings>.GetInstance(settingsUtils).SettingsConfig.Properties.ActivationShortcut, SettingsResourceLoader.GetString("Peek/ModuleTitle"), content);
content.Append(HotkeySettingsToYaml(SettingsRepository<PeekSettings>.GetInstance(settingsUtils).SettingsConfig.Properties.ActivationShortcut, SettingsResourceLoader.GetString("Peek/ModuleTitle")));
}
if (enabledModules.PowerLauncher)
{
content = HotkeySettingsToYaml(SettingsRepository<PowerLauncherSettings>.GetInstance(settingsUtils).SettingsConfig.Properties.OpenPowerLauncher, SettingsResourceLoader.GetString("PowerLauncher/ModuleTitle"), content);
content.Append(HotkeySettingsToYaml(SettingsRepository<PowerLauncherSettings>.GetInstance(settingsUtils).SettingsConfig.Properties.OpenPowerLauncher, SettingsResourceLoader.GetString("PowerLauncher/ModuleTitle")));
}
if (enabledModules.MeasureTool)
{
content = HotkeySettingsToYaml(SettingsRepository<MeasureToolSettings>.GetInstance(settingsUtils).SettingsConfig.Properties.ActivationShortcut, SettingsResourceLoader.GetString("MeasureTool/ModuleTitle"), content, SettingsResourceLoader.GetString("ScreenRuler_ShortDescription"));
content.Append(HotkeySettingsToYaml(SettingsRepository<MeasureToolSettings>.GetInstance(settingsUtils).SettingsConfig.Properties.ActivationShortcut, SettingsResourceLoader.GetString("MeasureTool/ModuleTitle"), SettingsResourceLoader.GetString("ScreenRuler_ShortDescription")));
}
if (enabledModules.ShortcutGuide)
{
content = HotkeySettingsToYaml(SettingsRepository<ShortcutGuideSettings>.GetInstance(settingsUtils).SettingsConfig.Properties.DefaultOpenShortcutGuide, SettingsResourceLoader.GetString("ShortcutGuide/ModuleTitle"), content, SettingsResourceLoader.GetString("ShortcutGuide_ShortDescription"));
content.Append(HotkeySettingsToYaml(SettingsRepository<ShortcutGuideSettings>.GetInstance(settingsUtils).SettingsConfig.Properties.DefaultOpenShortcutGuide, SettingsResourceLoader.GetString("ShortcutGuide/ModuleTitle"), SettingsResourceLoader.GetString("ShortcutGuide_ShortDescription")));
}
if (enabledModules.PowerOcr)
{
content = HotkeySettingsToYaml(SettingsRepository<PowerOcrSettings>.GetInstance(settingsUtils).SettingsConfig.Properties.ActivationShortcut, SettingsResourceLoader.GetString("TextExtractor/ModuleTitle"), content, SettingsResourceLoader.GetString("PowerOcr_ShortDescription"));
content.Append(HotkeySettingsToYaml(SettingsRepository<PowerOcrSettings>.GetInstance(settingsUtils).SettingsConfig.Properties.ActivationShortcut, SettingsResourceLoader.GetString("TextExtractor/ModuleTitle"), SettingsResourceLoader.GetString("PowerOcr_ShortDescription")));
}
if (enabledModules.Workspaces)
{
content = HotkeySettingsToYaml(SettingsRepository<WorkspacesSettings>.GetInstance(settingsUtils).SettingsConfig.Properties.Hotkey, SettingsResourceLoader.GetString("Workspaces/ModuleTitle"), content, SettingsResourceLoader.GetString("Workspaces_ShortDescription"));
content.Append(HotkeySettingsToYaml(SettingsRepository<WorkspacesSettings>.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)
/// <summary>
/// Converts the hotkey settings to a YAML format string for the manifest file.
/// </summary>
/// <param name="hotkeySettings">Object containing a hotkey from the settings.</param>
/// <param name="moduleName">The name of the PowerToys module.</param>
/// <param name="description">Description of the action.</param>
/// <returns>Yaml code for the manifest file.</returns>
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)
/// <inheritdoc cref="HotkeySettingsToYaml(HotkeySettings, string, string?)"/>
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(@"# <Populate start>[\s\S\n\r]*# <Populate end>")]

View File

@@ -8,8 +8,14 @@ namespace ShortcutGuide.Helpers
{
internal static class ResourceLoaderInstance
{
/// <summary>
/// Gets the resource loader for the Shortcut Guide module.
/// </summary>
internal static ResourceLoader ResourceLoader { get; private set; }
/// <summary>
/// Gets the resource loader for the Settings module.
/// </summary>
internal static ResourceLoader SettingsResourceLoader { get; private set; }
static ResourceLoaderInstance()

View File

@@ -7,8 +7,15 @@ using TasklistButton = ShortcutGuide.NativeMethods.TasklistButton;
namespace ShortcutGuide.Helpers
{
internal sealed class TasklistPositions
/// <summary>
/// Provides methods to retrieve the positions of taskbar buttons on the current monitor.
/// </summary>
internal static class TasklistPositions
{
/// <summary>
/// Retrieves the taskbar buttons for the current monitor.
/// </summary>
/// <returns>An array of the taskbar buttons.</returns>
public static TasklistButton[] GetButtons()
{
var monitor = NativeMethods.MonitorFromWindow(MainWindow.WindowHwnd, 0);

View File

@@ -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<StackPanel> 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 "<Copilot>":
shortcutStackPanel.Children.Add(new BitmapIcon() { UriSource = new("ms-appx:///Assets/ShortcutGuide/CopilotKey.png") });
break;
case "<Office>":
shortcutStackPanel.Children.Add(new BitmapIcon() { UriSource = new("ms-appx:///Assets/ShortcutGuide/OfficeKey.png"), Height = 20, Width = 20 });
break;
case "<Left>":
AddNewTextToStackPanel("←");
break;
case "<Right>":
AddNewTextToStackPanel("→");
break;
case "<Up>":
AddNewTextToStackPanel("↑");
break;
case "<Down>":
AddNewTextToStackPanel("↓");
break;
case "<TASKBAR1-9>":
AddNewTextToStackPanel("...");
break;
case "<Underlined letter>":
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 "<Arrow>":
TextBlock arrowTextBlock = new()
{
Text = "→",
Margin = new Thickness(3),
VerticalAlignment = VerticalAlignment.Center,
};
shortcutStackPanel.Children.Add(arrowTextBlock);
AnimateTextBlock(arrowTextBlock, "→↓←↑", 1000);
break;
case "<ArrowLR>":
TextBlock arrowLRTextBlock = new()
{
Text = "→",
Margin = new Thickness(3),
FontFamily = new("Courier New"),
VerticalAlignment = VerticalAlignment.Center,
};
shortcutStackPanel.Children.Add(arrowLRTextBlock);
AnimateTextBlock(arrowLRTextBlock, "→←", 1000);
break;
case "<ArrowUD>":
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);
}
}
/// <summary>
/// Transforms a key string into a visual representation in the stack panel.
/// </summary>
/// <param name="key">The string reprensentation of the key.</param>
/// <param name="shortcutStackPanel">The stackpanel to add the key to.</param>
private static void AddKeyToStackPanel(string key, StackPanel shortcutStackPanel)
{
switch (key)
{
case "<Copilot>":
shortcutStackPanel.Children.Add(new BitmapIcon() { UriSource = new("ms-appx:///Assets/ShortcutGuide/CopilotKey.png") });
break;
case "<Office>":
shortcutStackPanel.Children.Add(new BitmapIcon() { UriSource = new("ms-appx:///Assets/ShortcutGuide/OfficeKey.png"), Height = 20, Width = 20 });
break;
case "<Left>":
AddNewTextToStackPanel("←");
break;
case "<Right>":
AddNewTextToStackPanel("→");
break;
case "<Up>":
AddNewTextToStackPanel("↑");
break;
case "<Down>":
AddNewTextToStackPanel("↓");
break;
case "<TASKBAR1-9>":
AddNewTextToStackPanel("...");
break;
case "<Underlined letter>":
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 "<Arrow>":
TextBlock arrowTextBlock = new()
{
Text = "→",
Margin = new Thickness(3),
VerticalAlignment = VerticalAlignment.Center,
};
shortcutStackPanel.Children.Add(arrowTextBlock);
AnimateTextBlock(arrowTextBlock, "→↓←↑", 1000);
break;
case "<ArrowLR>":
TextBlock arrowLRTextBlock = new()
{
Text = "→",
Margin = new Thickness(3),
FontFamily = new("Courier New"),
VerticalAlignment = VerticalAlignment.Center,
};
shortcutStackPanel.Children.Add(arrowLRTextBlock);
AnimateTextBlock(arrowLRTextBlock, "→←", 1000);
break;
case "<ArrowUD>":
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 });
}
}
/// <summary>
/// Animates the text of a TextBlock by cycling through the characters of a given string.
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
/// <param name="animatedTextBlock">The textblock to animate.</param>
/// <param name="text">The characters to cycle through.</param>
/// <param name="delay">The delay to the next animation frame.</param>
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
}
}
/// <summary>
/// Animates the visibility of the stack panels one after another.
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
/// <param name="panels">The panels to animate.</param>
/// <param name="delay">The delay to the next animation frame.</param>
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
}
}
}

View File

@@ -7,14 +7,29 @@ using System.Collections.Generic;
namespace ShortcutGuide.Models
{
/// <summary>
/// Represents the parameters for the shortcut page in the Shortcut Guide module.
/// </summary>
internal struct ShortcutPageParameters
{
/// <summary>
/// Gets or sets the content of the search box.
/// </summary>
public static SearchFilterObservable SearchFilter = new();
/// <summary>
/// Gets or sets the pinned shortcuts for the Shortcut Guide.
/// </summary>
public static Dictionary<string, List<ShortcutEntry>> PinnedShortcuts = [];
/// <summary>
/// Gets or sets the name of the current page being displayed in the Shortcut Guide.
/// </summary>
public static string CurrentPageName = string.Empty;
/// <summary>
/// The height of the frame that displays the shortcuts.
/// </summary>
public static FrameHeightObservable FrameHeight = new();
internal sealed class SearchFilterObservable

View File

@@ -119,4 +119,9 @@ internal static partial class NativeMethods
public int Keynum;
}
public enum MonitorFromWindowDwFlags
{
MONITOR_DEFAULTTONEAREST = 2,
}
}

View File

@@ -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;
}

View File

@@ -33,7 +33,7 @@
</TeachingTip>
<TextBox Margin="0,0,6,0" Grid.Column="2" Grid.Row="0" Height="32" Width="300" HorizontalAlignment="Right" VerticalContentAlignment="Center" x:Uid="SearchBox" x:Name="SearchBox" HorizontalContentAlignment="Right" TextChanged="SearchBox_TextChanged">
<TextBox.KeyboardAccelerators>
<KeyboardAccelerator Modifiers="Control" Key="F" Invoked="SearchBoy_KeyboardAcceleratorInvoked" />
<KeyboardAccelerator Modifiers="Control" Key="F" Invoked="SearchBox_KeyboardAcceleratorInvoked" />
</TextBox.KeyboardAccelerators>
</TextBox>
<Button Margin="0,0,6,0" Grid.Column="3" Grid.Row="0" HorizontalContentAlignment="Center" x:Uid="SettingsButton" Click="SettingsButton_Clicked">

View File

@@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.IO;
using System.Text.Json;
using Common.UI;
using ManagedCommon;
using Microsoft.PowerToys.Settings.UI.Library;
using Microsoft.UI;
using Microsoft.UI.Windowing;
@@ -29,12 +30,12 @@ namespace ShortcutGuide
public sealed partial class MainWindow
{
private readonly string[] _currentApplicationIds;
/*private readonly bool _isInWindowsKeyMode;*/
private readonly bool _firstRun;
public static nint WindowHwnd { get; set; }
private AppWindow _appWindow;
private bool _setPosition;
public MainWindow()
{
@@ -42,21 +43,6 @@ namespace ShortcutGuide
InitializeComponent();
// Todo: Reimplement holding the Windows key down to show the guide.
/*
_isInWindowsKeyMode = (GetAsyncKeyState(0x5B) & 0x8000) != 0 || (GetAsyncKeyState(0x5C) & 0x8000) != 0;
if (_isInWindowsKeyMode)
{
Current.CoreWindow.KeyUp += (_, e) =>
{
if (e.VirtualKey is (VirtualKey)0x5B or (VirtualKey)0x5C)
{
Close();
}
};
}*/
Title = ResourceLoaderInstance.ResourceLoader.GetString("Title")!;
var hwnd = WinRT.Interop.WindowNative.GetWindowHandle(this);
@@ -72,7 +58,7 @@ namespace ShortcutGuide
this.SetIsMaximizable(false);
IsTitleBarVisible = false;
// Remove the caption style from the window style. Windows App SDK 1.6 added it, which made the title bar and borders appear for Measure Tool. This code removes it.
// Remove the caption style from the window style. Windows App SDK 1.6 added it, which made the title bar and borders appear. This code removes it.
var windowStyle = GetWindowLongW(hwnd, GWL_STYLE);
windowStyle &= ~WS_CAPTION;
_ = SetWindowLongW(hwnd, GWL_STYLE, windowStyle);
@@ -117,7 +103,8 @@ namespace ShortcutGuide
// Ignore, as the theme will be set by the system.
break;
default:
throw new InvalidDataException("Invalid theme value in settings.");
Logger.LogError("Invalid theme value in settings: " + shortcutGuideProperties.Theme.Value);
break;
}
_firstRun = shortcutGuideProperties.FirstRun.Value;
@@ -136,6 +123,7 @@ namespace ShortcutGuide
#endif
}
// The code below sets the position of the window to the center of the monitor, but only if it hasn't been set before.
if (!_setPosition)
{
var hwnd = WinRT.Interop.WindowNative.GetWindowHandle(this);
@@ -168,6 +156,7 @@ namespace ShortcutGuide
};
}
// Populate the window selector with the current application IDs if it is empty.
if (WindowSelector.Items.Count == 0)
{
foreach (var item in _currentApplicationIds)
@@ -217,8 +206,6 @@ namespace ShortcutGuide
ShortcutPageParameters.FrameHeight.OnFrameHeightChanged(ContentFrame.ActualHeight);
}
private bool _setPosition;
public void CloseButton_Clicked(object sender, RoutedEventArgs e)
{
ShortcutView.AnimationCancellationTokenSource.Cancel();
@@ -230,7 +217,7 @@ namespace ShortcutGuide
ShortcutPageParameters.SearchFilter.OnFilterChanged(SearchBox.Text);
}
private void SearchBoy_KeyboardAcceleratorInvoked(KeyboardAccelerator sender, KeyboardAcceleratorInvokedEventArgs args)
private void SearchBox_KeyboardAcceleratorInvoked(KeyboardAccelerator sender, KeyboardAcceleratorInvokedEventArgs args)
{
SearchBox.Focus(FocusState.Programmatic);
}
@@ -257,6 +244,9 @@ namespace ShortcutGuide
}
}
/// <summary>
/// Adds the welcome page to the window selector and opens it.
/// </summary>
private void CreateAndOpenWelcomePage()
{
WindowSelector.Items.Insert(0, new SelectorBarItem { Name = "<WELCOME>", Text = ResourceLoaderInstance.ResourceLoader.GetString("Welcome"), Icon = new FontIcon { Glyph = "\uE789" } });

View File

@@ -23,10 +23,15 @@ namespace ShortcutGuide
{
public sealed partial class ShortcutView : INotifyPropertyChanged
{
private readonly DispatcherTimer _taskbarUpdateTimer = new() { Interval = TimeSpan.FromMilliseconds(500) };
private readonly bool _showTaskbarShortcuts;
private readonly DispatcherTimer _taskbarIconsUpdateTimer = new() { Interval = TimeSpan.FromMilliseconds(500) };
private readonly ShortcutFile _shortcutList = ManifestInterpreter.GetShortcutsOfApplication(ShortcutPageParameters.CurrentPageName);
private bool _showTaskbarShortcuts;
private static CancellationTokenSource _animationCancellationTokenSource = new();
/// <summary>
/// Gets or sets a cancellation token source for animations in shortcut view.
/// When setting a new token source, the previous one is cancelled to stop ongoing animations.
/// </summary>
public static CancellationTokenSource AnimationCancellationTokenSource
{
get => _animationCancellationTokenSource;
@@ -37,42 +42,17 @@ namespace ShortcutGuide
}
}
private readonly ShortcutFile _shortcutList = ManifestInterpreter.GetShortcutsOfApplication(ShortcutPageParameters.CurrentPageName);
public ShortcutView()
{
InitializeComponent();
ShortcutView.AnimationCancellationTokenSource = new();
DataContext = this;
int i = -1;
// Stop any ongoing animations by cancelling the previous token source
AnimationCancellationTokenSource = new();
try
{
CategorySelector.Items.Add(new SelectorBarItem()
{
Text = ResourceLoaderInstance.ResourceLoader.GetString("Overview"),
Name = i.ToString(CultureInfo.InvariantCulture),
});
i++;
foreach (var category in _shortcutList.Shortcuts)
{
switch (category.SectionName)
{
case { } name when name.StartsWith("<TASKBAR1-9>", StringComparison.Ordinal):
_showTaskbarShortcuts = true;
break;
case { } name when name.StartsWith('<') && name.EndsWith('>'):
break;
default:
CategorySelector.Items.Add(new SelectorBarItem() { Text = category.SectionName, Name = i.ToString(CultureInfo.InvariantCulture) });
break;
}
i++;
}
PopulateCategorySelector();
CategorySelector.SelectedItem = CategorySelector.Items[0];
CategorySelector.SelectionChanged += CategorySelector_SelectionChanged;
@@ -93,8 +73,8 @@ namespace ShortcutGuide
if (_showTaskbarShortcuts)
{
TaskbarIndicators.Visibility = Visibility.Visible;
_taskbarUpdateTimer.Tick += UpdateTaskbarIndicators;
_taskbarUpdateTimer.Start();
_taskbarIconsUpdateTimer.Tick += UpdateTaskbarIndicators;
_taskbarIconsUpdateTimer.Start();
}
OpenOverview();
@@ -108,12 +88,46 @@ namespace ShortcutGuide
Unloaded += (_, _) =>
{
AnimationCancellationTokenSource = new();
_taskbarUpdateTimer.Tick -= UpdateTaskbarIndicators;
_taskbarUpdateTimer.Stop();
_taskbarIconsUpdateTimer.Tick -= UpdateTaskbarIndicators;
_taskbarIconsUpdateTimer.Stop();
};
}
/// <summary>
/// Populates the <see cref="CategorySelector"/> selector and sets <see cref="_showTaskbarShortcuts"/>.
/// </summary>
private void PopulateCategorySelector()
{
int i = -1;
CategorySelector.Items.Add(new SelectorBarItem()
{
Text = ResourceLoaderInstance.ResourceLoader.GetString("Overview"),
Name = i.ToString(CultureInfo.InvariantCulture),
});
i++;
foreach (var category in _shortcutList.Shortcuts)
{
switch (category.SectionName)
{
case { } name when name.StartsWith("<TASKBAR1-9>", StringComparison.Ordinal):
_showTaskbarShortcuts = true;
break;
case { } name when name.StartsWith('<') && name.EndsWith('>'):
break;
default:
CategorySelector.Items.Add(new SelectorBarItem() { Text = category.SectionName, Name = i.ToString(CultureInfo.InvariantCulture) });
break;
}
i++;
}
}
/// <summary>
/// Updates the taskbar indicators.
/// </summary>
private void UpdateTaskbarIndicators(object? sender, object? e)
{
NativeMethods.TasklistButton[] buttons = TasklistPositions.GetButtons();
@@ -159,11 +173,11 @@ namespace ShortcutGuide
((Rectangle)canvases[i].Children[0]).Height = buttons[i].Height / DpiHelper.GetDPIScaleForWindow(MainWindow.WindowHwnd.ToInt32());
((TextBlock)canvases[i].Children[1]).Width = buttons[i].Width / DpiHelper.GetDPIScaleForWindow(MainWindow.WindowHwnd.ToInt32());
((TextBlock)canvases[i].Children[1]).Height = buttons[i].Height / DpiHelper.GetDPIScaleForWindow(MainWindow.WindowHwnd.ToInt32());
continue;
}
else
{
canvases[i].Visibility = Visibility.Collapsed;
}
canvases[i].Visibility = Visibility.Collapsed;
}
}
@@ -202,15 +216,9 @@ namespace ShortcutGuide
PinnedListTitle.Visibility = Visibility.Visible;
ShortcutListElement.Visibility = Visibility.Collapsed;
foreach (var list in _shortcutList.Shortcuts)
foreach (var shortcut in _shortcutList.Shortcuts.SelectMany(list => list.Properties.Where(s => s.Recommended)))
{
foreach (var shortcut in list.Properties)
{
if (shortcut.Recommended)
{
RecommendedListElement.Items.Add((ShortcutTemplateDataObject)shortcut);
}
}
RecommendedListElement.Items.Add((ShortcutTemplateDataObject)shortcut);
}
if (RecommendedListElement.Items.Count == 0)
@@ -246,13 +254,13 @@ namespace ShortcutGuide
{
TaskbarLaunchShortcutsListElement.Items.Add((ShortcutTemplateDataObject)item);
}
return;
}
else
{
TaskbarLaunchShortcutsListElement.Visibility = Visibility.Collapsed;
TaskbarLaunchShortcutsTitle.Visibility = Visibility.Collapsed;
TaskbarLaunchShortcutsDescription.Visibility = Visibility.Collapsed;
}
TaskbarLaunchShortcutsListElement.Visibility = Visibility.Collapsed;
TaskbarLaunchShortcutsTitle.Visibility = Visibility.Collapsed;
TaskbarLaunchShortcutsDescription.Visibility = Visibility.Collapsed;
}
private string _searchFilter = string.Empty;
@@ -278,26 +286,16 @@ namespace ShortcutGuide
}
OverviewStackPanel.Visibility = Visibility.Collapsed;
foreach (var list in _shortcutList.Shortcuts)
foreach (var shortcut in _shortcutList.Shortcuts.SelectMany(list => list.Properties.Where(s => s.Name.Contains(filter, StringComparison.InvariantCultureIgnoreCase))))
{
foreach (var shortcut in list.Properties)
{
if (shortcut.Name.Contains(filter, StringComparison.InvariantCultureIgnoreCase))
{
ShortcutListElement.Items.Add((ShortcutTemplateDataObject)shortcut);
}
}
ShortcutListElement.Items.Add((ShortcutTemplateDataObject)shortcut);
}
}
else
{
foreach (var shortcut in _shortcutList.Shortcuts[int.Parse(CategorySelector.SelectedItem.Name, CultureInfo.InvariantCulture)].Properties)
foreach (var shortcut in _shortcutList.Shortcuts[int.Parse(CategorySelector.SelectedItem.Name, CultureInfo.InvariantCulture)].Properties.Where(s => s.Name.Contains(filter, StringComparison.InvariantCultureIgnoreCase)))
{
if (shortcut.Name.Contains(filter, StringComparison.InvariantCultureIgnoreCase))
{
ShortcutListElement.Items.Add((ShortcutTemplateDataObject)shortcut);
}
ShortcutListElement.Items.Add((ShortcutTemplateDataObject)shortcut);
}
}