diff --git a/AuraClick/App.xaml b/AuraClick/App.xaml index 41325f1c..4eb0b26a 100644 --- a/AuraClick/App.xaml +++ b/AuraClick/App.xaml @@ -8,6 +8,7 @@ + diff --git a/AuraClick/AuraClick.csproj b/AuraClick/AuraClick.csproj index 29cef165..13be7c23 100644 --- a/AuraClick/AuraClick.csproj +++ b/AuraClick/AuraClick.csproj @@ -32,6 +32,7 @@ + all @@ -39,7 +40,7 @@ - + diff --git a/AuraClick/Controls/IsEnabledTextBlock/IsEnabledTextBlock.cs b/AuraClick/Controls/IsEnabledTextBlock/IsEnabledTextBlock.cs deleted file mode 100644 index 1cbe7f74..00000000 --- a/AuraClick/Controls/IsEnabledTextBlock/IsEnabledTextBlock.cs +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright (C) 2026 Ryan Luu -// -// This file is part of Aura Click. -// -// Aura Click is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published -// by the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Aura Click is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Aura Click. If not, see . - -using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Controls; -using System.ComponentModel; - -// To learn more about WinUI, the WinUI project structure, -// and more about our project templates, see: http://aka.ms/winui-project-info. - -namespace AuraClick.Controls; - -/// -/// A custom control that displays text and changes its visual state based on its enabled state. -/// -[TemplateVisualState(Name = "Normal", GroupName = "CommonStates")] -[TemplateVisualState(Name = "Disabled", GroupName = "CommonStates")] -public partial class IsEnabledTextBlock : Control -{ - /// - /// Identifies the dependency property. - /// - public static readonly DependencyProperty TextProperty = DependencyProperty.Register( - "Text", - typeof(string), - typeof(IsEnabledTextBlock), - null); - - /// - /// Initializes a new instance of the class. - /// - public IsEnabledTextBlock() - { - DefaultStyleKey = typeof(IsEnabledTextBlock); - } - - /// - /// Gets or sets the text content of the control. - /// - [Localizable(true)] - public string Text - { - get => (string)GetValue(TextProperty); - set => SetValue(TextProperty, value); - } - - /// - /// Applies the control template and sets up the initial visual state. - /// - protected override void OnApplyTemplate() - { - IsEnabledChanged -= IsEnabledTextBlock_IsEnabledChanged; - SetEnabledState(); - IsEnabledChanged += IsEnabledTextBlock_IsEnabledChanged; - base.OnApplyTemplate(); - } - - /// - /// Handles the IsEnabledChanged event and updates the visual state. - /// - /// The source of the event. - /// The event data. - private void IsEnabledTextBlock_IsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e) - { - SetEnabledState(); - } - - /// - /// Sets the visual state of the control based on its enabled state. - /// - private void SetEnabledState() - { - _ = VisualStateManager.GoToState(this, IsEnabled ? "Normal" : "Disabled", true); - } -} \ No newline at end of file diff --git a/AuraClick/Controls/IsEnabledTextBlock/IsEnabledTextBlock.xaml b/AuraClick/Controls/IsEnabledTextBlock/IsEnabledTextBlock.xaml deleted file mode 100644 index 0b3c3cd5..00000000 --- a/AuraClick/Controls/IsEnabledTextBlock/IsEnabledTextBlock.xaml +++ /dev/null @@ -1,35 +0,0 @@ - - - - \ No newline at end of file diff --git a/AuraClick/Helpers/HotkeyManager.cs b/AuraClick/Helpers/HotkeyManager.cs new file mode 100644 index 00000000..2e4be8d5 --- /dev/null +++ b/AuraClick/Helpers/HotkeyManager.cs @@ -0,0 +1,98 @@ +// Copyright (C) 2026 Ryan Luu +// +// This file is part of Aura Click. +// +// Aura Click is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Aura Click is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Aura Click. If not, see . + +using DevWinUI; +using Windows.System; +using Windows.Win32; +using Windows.Win32.Foundation; +using Windows.Win32.UI.Input.KeyboardAndMouse; +using WinRT.Interop; + +namespace AuraClick.Helpers; + +internal static class HotkeyManager +{ + public static bool RegisterHotkey(int hotkeyId, IEnumerable? keys) + { + if (!TryGetHotkey(keys, out var modifiers, out var virtualKey)) + { + return false; + } + + var hWnd = new HWND(WindowNative.GetWindowHandle(App.MainWindow)); + UnregisterHotkey(hotkeyId); + return PInvoke.RegisterHotKey(hWnd, hotkeyId, ToWin32Modifiers(modifiers) | HOT_KEY_MODIFIERS.MOD_NOREPEAT, (uint)virtualKey); + } + + public static bool UnregisterHotkey(int hotkeyId) + => PInvoke.UnregisterHotKey(new HWND(WindowNative.GetWindowHandle(App.MainWindow)), hotkeyId); + + private static bool TryGetHotkey(IEnumerable? keys, out VirtualKeyModifiers modifiers, out VirtualKey virtualKey) + { + modifiers = VirtualKeyModifiers.None; + virtualKey = VirtualKey.None; + + if (keys is null) + { + return false; + } + + foreach (var key in keys) + { + if (key is not KeyVisualInfo keyInfo || keyInfo.Key is not VirtualKey keyCode) + { + continue; + } + + if (TryGetModifier(keyCode, out var modifier)) + { + modifiers |= modifier; + } + else + { + virtualKey = keyCode; + } + } + + return virtualKey != VirtualKey.None; + } + + private static bool TryGetModifier(VirtualKey keyCode, out VirtualKeyModifiers modifier) + { + modifier = keyCode switch + { + VirtualKey.Control or VirtualKey.LeftControl or VirtualKey.RightControl => VirtualKeyModifiers.Control, + VirtualKey.Shift or VirtualKey.LeftShift or VirtualKey.RightShift => VirtualKeyModifiers.Shift, + VirtualKey.Menu or VirtualKey.LeftMenu or VirtualKey.RightMenu => VirtualKeyModifiers.Menu, + VirtualKey.LeftWindows or VirtualKey.RightWindows => VirtualKeyModifiers.Windows, + _ => VirtualKeyModifiers.None, + }; + + return modifier != VirtualKeyModifiers.None; + } + + private static HOT_KEY_MODIFIERS ToWin32Modifiers(VirtualKeyModifiers modifiers) + { + HOT_KEY_MODIFIERS result = 0; + if ((modifiers & VirtualKeyModifiers.Control) != 0) result |= HOT_KEY_MODIFIERS.MOD_CONTROL; + if ((modifiers & VirtualKeyModifiers.Menu) != 0) result |= HOT_KEY_MODIFIERS.MOD_ALT; + if ((modifiers & VirtualKeyModifiers.Shift) != 0) result |= HOT_KEY_MODIFIERS.MOD_SHIFT; + if ((modifiers & VirtualKeyModifiers.Windows) != 0) result |= HOT_KEY_MODIFIERS.MOD_WIN; + return result; + } + +} diff --git a/AuraClick/MainWindow.xaml b/AuraClick/MainWindow.xaml index 1a80fe04..6fa61e55 100644 --- a/AuraClick/MainWindow.xaml +++ b/AuraClick/MainWindow.xaml @@ -8,9 +8,9 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:windowex="using:WinUIEx" Width="400" - Height="340" + Height="360" MinWidth="400" - MinHeight="340" + MinHeight="360" mc:Ignorable="d"> diff --git a/AuraClick/NativeMethods.txt b/AuraClick/NativeMethods.txt index 44852877..c5975d20 100644 --- a/AuraClick/NativeMethods.txt +++ b/AuraClick/NativeMethods.txt @@ -1,10 +1,10 @@ RegisterHotKey UnregisterHotKey -CallWindowProc SendInput SetProcessInformation GetCurrentProcess SetPriorityClass PROCESS_POWER_THROTTLING_CURRENT_VERSION PROCESS_POWER_THROTTLING_EXECUTION_SPEED -PROCESS_POWER_THROTTLING_STATE \ No newline at end of file +PROCESS_POWER_THROTTLING_STATE +WM_HOTKEY \ No newline at end of file diff --git a/AuraClick/Pages/MainPage.xaml b/AuraClick/Pages/MainPage.xaml index a7290fb8..6ac3eb54 100644 --- a/AuraClick/Pages/MainPage.xaml +++ b/AuraClick/Pages/MainPage.xaml @@ -5,9 +5,9 @@ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:animatedvisuals="using:Microsoft.UI.Xaml.Controls.AnimatedVisuals" - xmlns:controls="using:AuraClick.Controls" xmlns:converters="using:CommunityToolkit.WinUI.Converters" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:dev="using:DevWinUI" xmlns:helpers="using:AuraClick.Helpers" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:ui="using:CommunityToolkit.WinUI" @@ -43,7 +43,7 @@ - @@ -110,11 +110,15 @@ Grid.Row="2" Grid.Column="0" Spacing="{StaticResource StackPanelSpacing}"> - -