Try implementing HotkeyControl, but with a modal window

This commit is contained in:
Yusyuriv 2024-04-19 09:19:08 +06:00
parent 15fe1cd643
commit aaf770afa2
No known key found for this signature in database
GPG key ID: A91C52E6F73148E0
5 changed files with 456 additions and 2 deletions

View file

@ -0,0 +1,77 @@
<UserControl
x:Class="Flow.Launcher.HotkeyControl2"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Button
Width="Auto"
FontSize="13"
FontWeight="Bold"
Foreground="{DynamicResource Color01B}"
Click="GetNewHotkey">
<Button.Template>
<ControlTemplate TargetType="Button">
<Border
x:Name="ButtonBorder"
Padding="5,0,5,0"
Background="{DynamicResource ButtonBackgroundColor}"
BorderBrush="{DynamicResource ButtonInsideBorder}"
BorderThickness="1"
CornerRadius="5">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
</Border>
<ControlTemplate.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="True" />
<Condition Property="IsPressed" Value="True" />
</MultiTrigger.Conditions>
<Setter TargetName="ButtonBorder" Property="Background"
Value="{DynamicResource ButtonMousePressed}" />
<Setter TargetName="ButtonBorder" Property="BorderBrush"
Value="{DynamicResource ButtonMousePressedInsideBorder}" />
</MultiTrigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="True" />
</MultiTrigger.Conditions>
<Setter TargetName="ButtonBorder" Property="Background"
Value="{DynamicResource ButtonMouseOver}" />
</MultiTrigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsPressed" Value="True" />
</MultiTrigger.Conditions>
<Setter TargetName="ButtonBorder" Property="Background"
Value="{DynamicResource ButtonMousePressed}" />
<Setter TargetName="ButtonBorder" Property="BorderBrush"
Value="{DynamicResource CustomContextHover}" />
</MultiTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Button.Template>
<Button.Content>
<ItemsControl x:Name="HotkeyList">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border
Margin="2,5,2,5"
Padding="10,5,10,5"
Background="{DynamicResource AccentButtonBackground}"
BorderThickness="1"
CornerRadius="5">
<TextBlock Text="{Binding}" />
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Button.Content>
</Button>
</UserControl>

View file

@ -0,0 +1,207 @@
#nullable enable
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Input;
using Flow.Launcher.Core.Resource;
using Flow.Launcher.Helper;
using Flow.Launcher.Infrastructure.Hotkey;
namespace Flow.Launcher
{
public partial class HotkeyControl2
{
public string WindowTitle { get; set; } = string.Empty;
public static readonly DependencyProperty WindowTitleProperty = DependencyProperty.Register(
nameof(WindowTitle),
typeof(string),
typeof(HotkeyControl2),
new PropertyMetadata(default(string))
);
/// <summary>
/// Designed for Preview Hotkey and KeyGesture.
/// </summary>
public static readonly DependencyProperty ValidateKeyGestureProperty = DependencyProperty.Register(
nameof(ValidateKeyGesture),
typeof(bool),
typeof(HotkeyControl2),
new PropertyMetadata(default(bool))
);
public bool ValidateKeyGesture
{
get { return (bool)GetValue(ValidateKeyGestureProperty); }
set { SetValue(ValidateKeyGestureProperty, value); }
}
public static readonly DependencyProperty DefaultHotkeyProperty = DependencyProperty.Register(
nameof(DefaultHotkey),
typeof(string),
typeof(HotkeyControl2),
new PropertyMetadata(default(string))
);
public string DefaultHotkey
{
get { return (string)GetValue(DefaultHotkeyProperty); }
set { SetValue(DefaultHotkeyProperty, value); }
}
private static void OnHotkeyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is not HotkeyControl2 hotkeyControl)
{
return;
}
hotkeyControl.SetKeysToDisplay(new HotkeyModel(hotkeyControl.Hotkey));
hotkeyControl.CurrentHotkey = new HotkeyModel(hotkeyControl.Hotkey);
}
public static readonly DependencyProperty ChangeHotkeyProperty = DependencyProperty.Register(
nameof(ChangeHotkey),
typeof(ICommand),
typeof(HotkeyControl2),
new PropertyMetadata(default(ICommand))
);
public ICommand? ChangeHotkey
{
get { return (ICommand)GetValue(ChangeHotkeyProperty); }
set { SetValue(ChangeHotkeyProperty, value); }
}
public static readonly DependencyProperty HotkeyProperty = DependencyProperty.Register(
nameof(Hotkey),
typeof(string),
typeof(HotkeyControl2),
new FrameworkPropertyMetadata("", FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnHotkeyChanged)
);
public string Hotkey
{
get { return (string)GetValue(HotkeyProperty); }
set { SetValue(HotkeyProperty, value); }
}
public HotkeyControl2()
{
InitializeComponent();
HotkeyList.ItemsSource = KeysToDisplay;
SetKeysToDisplay(CurrentHotkey);
}
private static bool CheckHotkeyAvailability(HotkeyModel hotkey, bool validateKeyGesture) =>
hotkey.Validate(validateKeyGesture) && HotKeyMapper.CheckAvailability(hotkey);
public string EmptyHotkey => InternationalizationManager.Instance.GetTranslation("none");
public ObservableCollection<string> KeysToDisplay { get; set; } = new();
public HotkeyModel CurrentHotkey { get; private set; } = new(false, false, false, false, Key.None);
public void GetNewHotkey(object sender, RoutedEventArgs e)
{
if (!string.IsNullOrEmpty(Hotkey))
{
HotKeyMapper.RemoveHotkey(Hotkey);
}
var owner = Window.GetWindow(this);
var width = owner?.ActualWidth ?? 0;
var height = owner?.ActualHeight ?? 0;
var w = new HotkeyControl2Dialog
{
Title = WindowTitle,
Owner = owner,
Width = width,
Height = height
};
w.ShowDialog();
switch (w.ResultType)
{
case HotkeyControl2Dialog.EResultType.Cancel:
return;
case HotkeyControl2Dialog.EResultType.Save:
SetHotkey(w.ResultValue);
break;
case HotkeyControl2Dialog.EResultType.Reset:
ResetToDefault();
break;
case HotkeyControl2Dialog.EResultType.Delete:
Delete();
break;
}
}
private void ResetToDefault()
{
if (!string.IsNullOrEmpty(Hotkey))
HotKeyMapper.RemoveHotkey(Hotkey);
Hotkey = DefaultHotkey;
CurrentHotkey = new HotkeyModel(Hotkey);
SetKeysToDisplay(CurrentHotkey);
SetHotkey(CurrentHotkey);
}
private void SetHotkey(HotkeyModel keyModel, bool triggerValidate = true)
{
if (triggerValidate)
{
bool hotkeyAvailable = CheckHotkeyAvailability(keyModel, ValidateKeyGesture);
if (!hotkeyAvailable)
{
return;
}
Hotkey = keyModel.ToString();
SetKeysToDisplay(CurrentHotkey);
ChangeHotkey?.Execute(keyModel);
}
else
{
Hotkey = keyModel.ToString();
ChangeHotkey?.Execute(keyModel);
}
}
public void Delete()
{
if (!string.IsNullOrEmpty(Hotkey))
HotKeyMapper.RemoveHotkey(Hotkey);
Hotkey = "";
SetKeysToDisplay(new HotkeyModel(false, false, false, false, Key.None));
}
private void SetKeysToDisplay(HotkeyModel? hotkey)
{
KeysToDisplay.Clear();
if (hotkey == null || hotkey == default(HotkeyModel))
{
KeysToDisplay.Add(EmptyHotkey);
return;
}
foreach (var key in hotkey.Value.EnumerateDisplayKeys()!)
{
KeysToDisplay.Add(key);
}
}
public void SetHotkey(string? keyStr, bool triggerValidate = true)
{
SetHotkey(new HotkeyModel(keyStr), triggerValidate);
}
}
}

View file

@ -0,0 +1,76 @@
<Window x:Class="Flow.Launcher.HotkeyControl2Dialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
WindowStartupLocation="CenterOwner"
Width="400"
Height="300"
MinWidth="400"
MinHeight="300"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
Icon="Images\app.ico"
ResizeMode="NoResize"
ShowInTaskbar="False"
Background="Transparent"
AllowsTransparency="True"
WindowStyle="None"
PreviewKeyDown="OnPreviewKeyDown">
<WindowChrome.WindowChrome>
<WindowChrome
CaptionHeight="0"
GlassFrameThickness="0" />
</WindowChrome.WindowChrome>
<Border Background="#AA000000" CornerRadius="8">
<Border Background="DarkGray" Width="400" Height="300" HorizontalAlignment="Center" VerticalAlignment="Center"
CornerRadius="8">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid Grid.Row="0" Grid.Column="0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="{Binding Title}" />
<StackPanel Grid.Row="1" VerticalAlignment="Center" HorizontalAlignment="Center">
<ItemsControl ItemsSource="{Binding KeysToDisplay}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Background="Red" CornerRadius="6" Padding="8" Margin="0 0 10 0">
<TextBlock Text="{Binding}" />
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</Grid>
<Border Grid.Row="0" Grid.Column="1" Background="Gray" CornerRadius="0 8 0 0">
<StackPanel>
<Button Content="Reset" Click="Reset" />
<Button Content="Delete" Click="Delete" />
</StackPanel>
</Border>
<Border Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" Background="Black" CornerRadius="0 0 8 8">
<StackPanel HorizontalAlignment="Center" Orientation="Horizontal" Margin="10">
<Button Content="Save" Margin="0 0 10 0" Click="Save" />
<Button Content="Cancel" Click="Cancel" />
</StackPanel>
</Border>
</Grid>
</Border>
</Border>
</Window>

View file

@ -0,0 +1,93 @@
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Input;
using Flow.Launcher.Core.Resource;
using Flow.Launcher.Infrastructure.Hotkey;
using Flow.Launcher.Plugin;
namespace Flow.Launcher;
public partial class HotkeyControl2Dialog : Window
{
public string Hotkey { get; set; } = string.Empty;
public HotkeyModel CurrentHotkey { get; set; } = new(false, false, false, false, Key.None);
public ObservableCollection<string> KeysToDisplay { get; set; } = new();
public enum EResultType
{
Cancel,
Save,
Reset,
Delete
}
public EResultType ResultType { get; private set; } = EResultType.Cancel;
public string ResultValue { get; private set; } = string.Empty;
public string EmptyHotkey => InternationalizationManager.Instance.GetTranslation("none");
public HotkeyControl2Dialog()
{
InitializeComponent();
}
public void Reset(object sender, RoutedEventArgs routedEventArgs)
{
ResultType = EResultType.Reset;
Close();
}
public void Delete(object sender, RoutedEventArgs routedEventArgs)
{
ResultType = EResultType.Delete;
Close();
}
public void Cancel(object sender, RoutedEventArgs routedEventArgs)
{
ResultType = EResultType.Cancel;
Close();
}
public void Save(object sender, RoutedEventArgs routedEventArgs)
{
ResultType = EResultType.Save;
ResultValue = string.Join("+", KeysToDisplay);
Close();
}
private void OnPreviewKeyDown(object sender, KeyEventArgs e)
{
e.Handled = true;
//when alt is pressed, the real key should be e.SystemKey
Key key = e.Key == Key.System ? e.SystemKey : e.Key;
SpecialKeyState specialKeyState = GlobalHotkey.CheckModifiers();
var hotkeyModel = new HotkeyModel(
specialKeyState.AltPressed,
specialKeyState.ShiftPressed,
specialKeyState.WinPressed,
specialKeyState.CtrlPressed,
key);
CurrentHotkey = hotkeyModel;
SetKeysToDisplay(CurrentHotkey);
}
private void SetKeysToDisplay(HotkeyModel? hotkey)
{
KeysToDisplay.Clear();
if (hotkey == null || hotkey == default(HotkeyModel))
{
KeysToDisplay.Add(EmptyHotkey);
return;
}
foreach (var key in hotkey.Value.EnumerateDisplayKeys()!)
{
KeysToDisplay.Add(key);
}
}
}

View file

@ -2641,7 +2641,7 @@
Title="{DynamicResource flowlauncherHotkey}"
Icon="&#xeda7;"
Sub="{DynamicResource flowlauncherHotkeyToolTip}">
<flowlauncher:HotkeyControl
<flowlauncher:HotkeyControl2
ChangeHotkey="{Binding SetTogglingHotkeyCommand}"
DefaultHotkey="Alt+Space"
Hotkey="{Binding Settings.Hotkey}"
@ -2654,9 +2654,10 @@
Title="{DynamicResource previewHotkey}"
Icon="&#xe8a1;"
Sub="{DynamicResource previewHotkeyToolTip}">
<flowlauncher:HotkeyControl
<flowlauncher:HotkeyControl2
DefaultHotkey="F1"
Hotkey="{Binding Settings.PreviewHotkey}"
WindowTitle="Preview hotkey"
ValidateKeyGesture="False" />
</cc:Card>
</StackPanel>