Improved keyboad shortcuts.
This commit is contained in:
@@ -21,22 +21,40 @@
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- Mail Operation -->
|
||||
<StackPanel Grid.Row="1" Margin="0,0,0,20">
|
||||
<TextBlock
|
||||
Margin="0,0,0,8"
|
||||
Style="{ThemeResource BodyStrongTextBlockStyle}"
|
||||
Text="{x:Bind domain:Translator.KeyboardShortcuts_Mode}" />
|
||||
<StackPanel Orientation="Horizontal" Spacing="16">
|
||||
<RadioButton
|
||||
Content="{x:Bind domain:Translator.KeyboardShortcuts_ModeMail}"
|
||||
GroupName="ShortcutMode"
|
||||
IsChecked="{x:Bind IsMailModeSelected, Mode=TwoWay}" />
|
||||
<RadioButton
|
||||
Content="{x:Bind domain:Translator.KeyboardShortcuts_ModeCalendar}"
|
||||
GroupName="ShortcutMode"
|
||||
IsChecked="{x:Bind IsCalendarModeSelected, Mode=TwoWay}" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Action -->
|
||||
<StackPanel Grid.Row="0" Margin="0,0,0,20">
|
||||
<TextBlock
|
||||
Margin="0,0,0,4"
|
||||
Style="{ThemeResource BodyStrongTextBlockStyle}"
|
||||
Text="{x:Bind domain:Translator.KeyboardShortcuts_MailoperationAction}" />
|
||||
Text="{x:Bind domain:Translator.KeyboardShortcuts_Action}" />
|
||||
<ComboBox
|
||||
x:Name="MailOperationComboBox"
|
||||
x:Name="ActionComboBox"
|
||||
HorizontalAlignment="Stretch"
|
||||
ItemsSource="{x:Bind AvailableMailOperations}"
|
||||
SelectedItem="{x:Bind SelectedMailOperation, Mode=TwoWay}">
|
||||
ItemsSource="{x:Bind AvailableActions}"
|
||||
SelectedItem="{x:Bind SelectedAction, Mode=TwoWay}">
|
||||
<ComboBox.ItemTemplate>
|
||||
|
||||
<DataTemplate x:DataType="data:MailOperationViewModel">
|
||||
<DataTemplate x:DataType="data:KeyboardShortcutActionViewModel">
|
||||
<TextBlock Text="{x:Bind DisplayName}" />
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
@@ -44,7 +62,7 @@
|
||||
</StackPanel>
|
||||
|
||||
<!-- Key Input -->
|
||||
<StackPanel Grid.Row="1" Margin="0,0,0,20">
|
||||
<StackPanel Grid.Row="2" Margin="0,0,0,20">
|
||||
<TextBlock
|
||||
Margin="0,0,0,4"
|
||||
Style="{ThemeResource BodyStrongTextBlockStyle}"
|
||||
@@ -61,31 +79,10 @@
|
||||
Text="{x:Bind domain:Translator.KeyboardShortcuts_FocusArea}" />
|
||||
</StackPanel>
|
||||
|
||||
<!-- Modifiers -->
|
||||
<StackPanel Grid.Row="2" Margin="0,0,0,20">
|
||||
<TextBlock
|
||||
Margin="0,0,0,8"
|
||||
Style="{ThemeResource BodyStrongTextBlockStyle}"
|
||||
Text="{x:Bind domain:Translator.KeyboardShortcuts_Modifiers}" />
|
||||
<Border
|
||||
Padding="16,12"
|
||||
Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
|
||||
BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="6">
|
||||
<StackPanel Orientation="Horizontal" Spacing="16">
|
||||
<CheckBox Content="Ctrl" IsChecked="{x:Bind IsControlPressed, Mode=TwoWay}" />
|
||||
<CheckBox Content="Alt" IsChecked="{x:Bind IsAltPressed, Mode=TwoWay}" />
|
||||
<CheckBox Content="Shift" IsChecked="{x:Bind IsShiftPressed, Mode=TwoWay}" />
|
||||
<CheckBox Content="Win" IsChecked="{x:Bind IsWindowsPressed, Mode=TwoWay}" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Error Message -->
|
||||
<Border
|
||||
x:Name="ErrorBorder"
|
||||
Grid.Row="3"
|
||||
Grid.Row="4"
|
||||
Padding="12,8"
|
||||
Background="{ThemeResource SystemFillColorCriticalBackgroundBrush}"
|
||||
CornerRadius="4"
|
||||
|
||||
@@ -4,6 +4,7 @@ using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Input;
|
||||
using Wino.Core.Domain.Entities.Shared;
|
||||
using Wino.Core.Domain.Enums;
|
||||
using Wino.Core.Domain;
|
||||
using Wino.Core.Domain.Models;
|
||||
using Wino.Core.ViewModels.Data;
|
||||
|
||||
@@ -13,35 +14,51 @@ public sealed partial class KeyboardShortcutDialog : ContentDialog
|
||||
{
|
||||
public KeyboardShortcutDialogResult Result { get; private set; } = KeyboardShortcutDialogResult.Canceled();
|
||||
|
||||
public List<MailOperationViewModel> AvailableMailOperations { get; }
|
||||
public List<KeyboardShortcutActionViewModel> AvailableActions { get; private set; } = [];
|
||||
|
||||
public MailOperationViewModel SelectedMailOperation { get; set; }
|
||||
public bool IsControlPressed { get; set; }
|
||||
public bool IsAltPressed { get; set; }
|
||||
public bool IsShiftPressed { get; set; }
|
||||
public bool IsWindowsPressed { get; set; }
|
||||
public KeyboardShortcutActionViewModel SelectedAction { get; set; } = null!;
|
||||
public WinoApplicationMode SelectedMode { get; set; } = WinoApplicationMode.Mail;
|
||||
public bool IsMailModeSelected
|
||||
{
|
||||
get => SelectedMode == WinoApplicationMode.Mail;
|
||||
set
|
||||
{
|
||||
if (!value || SelectedMode == WinoApplicationMode.Mail) return;
|
||||
SelectedMode = WinoApplicationMode.Mail;
|
||||
RefreshAvailableActions();
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsCalendarModeSelected
|
||||
{
|
||||
get => SelectedMode == WinoApplicationMode.Calendar;
|
||||
set
|
||||
{
|
||||
if (!value || SelectedMode == WinoApplicationMode.Calendar) return;
|
||||
SelectedMode = WinoApplicationMode.Calendar;
|
||||
RefreshAvailableActions();
|
||||
}
|
||||
}
|
||||
|
||||
private ModifierKeys _modifierKeys;
|
||||
private string _key = string.Empty;
|
||||
|
||||
public KeyboardShortcutDialog()
|
||||
{
|
||||
InitializeComponent();
|
||||
AvailableMailOperations = GetAvailableMailOperations();
|
||||
SelectedMailOperation = AvailableMailOperations.FirstOrDefault()!;
|
||||
RefreshAvailableActions();
|
||||
}
|
||||
|
||||
public KeyboardShortcutDialog(KeyboardShortcut existingShortcut) : this()
|
||||
{
|
||||
if (existingShortcut != null)
|
||||
{
|
||||
KeyInputTextBox.Text = existingShortcut.Key;
|
||||
SelectedMailOperation = AvailableMailOperations.FirstOrDefault(x => x.Operation == existingShortcut.MailOperation)!;
|
||||
|
||||
var modifiers = existingShortcut.ModifierKeys;
|
||||
IsControlPressed = modifiers.HasFlag(ModifierKeys.Control);
|
||||
IsAltPressed = modifiers.HasFlag(ModifierKeys.Alt);
|
||||
IsShiftPressed = modifiers.HasFlag(ModifierKeys.Shift);
|
||||
IsWindowsPressed = modifiers.HasFlag(ModifierKeys.Windows);
|
||||
|
||||
Title = "Edit Keyboard Shortcut";
|
||||
SelectedMode = existingShortcut.Mode;
|
||||
_modifierKeys = existingShortcut.ModifierKeys;
|
||||
_key = existingShortcut.Key;
|
||||
RefreshAvailableActions(existingShortcut.Action);
|
||||
KeyInputTextBox.Text = BuildDisplayString(_key, _modifierKeys);
|
||||
Title = Translator.KeyboardShortcuts_EditTitle;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,61 +68,47 @@ public sealed partial class KeyboardShortcutDialog : ContentDialog
|
||||
ErrorBorder.Visibility = Microsoft.UI.Xaml.Visibility.Collapsed;
|
||||
|
||||
// Validate input
|
||||
if (string.IsNullOrWhiteSpace(KeyInputTextBox.Text))
|
||||
if (string.IsNullOrWhiteSpace(_key))
|
||||
{
|
||||
ShowError("Please enter a key for the shortcut.");
|
||||
ShowError(Translator.KeyboardShortcuts_EnterKey);
|
||||
args.Cancel = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (SelectedMailOperation == null || SelectedMailOperation.Operation == MailOperation.None)
|
||||
if (SelectedAction == null || SelectedAction.Action == KeyboardShortcutAction.None)
|
||||
{
|
||||
ShowError("Please select a mail operation for the shortcut.");
|
||||
ShowError(Translator.KeyboardShortcuts_SelectOperation);
|
||||
args.Cancel = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// Get modifier keys
|
||||
var modifierKeys = GetSelectedModifierKeys();
|
||||
|
||||
// Create successful result
|
||||
Result = KeyboardShortcutDialogResult.Success(KeyInputTextBox.Text, modifierKeys, SelectedMailOperation.Operation);
|
||||
Result = KeyboardShortcutDialogResult.Success(SelectedMode, _key, _modifierKeys, SelectedAction.Action);
|
||||
}
|
||||
|
||||
private void KeyInputTextBox_PreviewKeyDown(object sender, KeyRoutedEventArgs e)
|
||||
{
|
||||
// Clear error when user starts typing
|
||||
ErrorBorder.Visibility = Microsoft.UI.Xaml.Visibility.Collapsed;
|
||||
|
||||
var key = e.Key.ToString();
|
||||
_modifierKeys = GetCurrentModifierKeys();
|
||||
var key = NormalizeKey(e.Key);
|
||||
|
||||
// Update modifier states based on current key press
|
||||
IsControlPressed = Microsoft.UI.Input.InputKeyboardSource.GetKeyStateForCurrentThread(Windows.System.VirtualKey.Control).HasFlag(Windows.UI.Core.CoreVirtualKeyStates.Down);
|
||||
IsAltPressed = Microsoft.UI.Input.InputKeyboardSource.GetKeyStateForCurrentThread(Windows.System.VirtualKey.Menu).HasFlag(Windows.UI.Core.CoreVirtualKeyStates.Down);
|
||||
IsShiftPressed = Microsoft.UI.Input.InputKeyboardSource.GetKeyStateForCurrentThread(Windows.System.VirtualKey.Shift).HasFlag(Windows.UI.Core.CoreVirtualKeyStates.Down);
|
||||
IsWindowsPressed = Microsoft.UI.Input.InputKeyboardSource.GetKeyStateForCurrentThread(Windows.System.VirtualKey.LeftWindows).HasFlag(Windows.UI.Core.CoreVirtualKeyStates.Down) ||
|
||||
Microsoft.UI.Input.InputKeyboardSource.GetKeyStateForCurrentThread(Windows.System.VirtualKey.RightWindows).HasFlag(Windows.UI.Core.CoreVirtualKeyStates.Down);
|
||||
|
||||
// Set the key (ignore modifier keys themselves)
|
||||
if (key != "Control" && key != "Menu" && key != "Shift" && key != "LeftWindows" && key != "RightWindows")
|
||||
if (!string.IsNullOrEmpty(key))
|
||||
{
|
||||
KeyInputTextBox.Text = key;
|
||||
_key = key;
|
||||
}
|
||||
|
||||
// Prevent the key from being processed further
|
||||
// e.Handled = true;
|
||||
KeyInputTextBox.Text = string.IsNullOrEmpty(_key)
|
||||
? BuildDisplayString(string.Empty, _modifierKeys)
|
||||
: BuildDisplayString(_key, _modifierKeys);
|
||||
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
private ModifierKeys GetSelectedModifierKeys()
|
||||
private void RefreshAvailableActions(KeyboardShortcutAction selectedAction = KeyboardShortcutAction.None)
|
||||
{
|
||||
var modifiers = ModifierKeys.None;
|
||||
|
||||
if (IsControlPressed) modifiers |= ModifierKeys.Control;
|
||||
if (IsAltPressed) modifiers |= ModifierKeys.Alt;
|
||||
if (IsShiftPressed) modifiers |= ModifierKeys.Shift;
|
||||
if (IsWindowsPressed) modifiers |= ModifierKeys.Windows;
|
||||
|
||||
return modifiers;
|
||||
AvailableActions = GetAvailableActions(SelectedMode);
|
||||
SelectedAction = AvailableActions.FirstOrDefault(x => x.Action == selectedAction) ?? AvailableActions.FirstOrDefault()!;
|
||||
Bindings.Update();
|
||||
}
|
||||
|
||||
private void ShowError(string message)
|
||||
@@ -114,32 +117,91 @@ public sealed partial class KeyboardShortcutDialog : ContentDialog
|
||||
ErrorBorder.Visibility = Microsoft.UI.Xaml.Visibility.Visible;
|
||||
}
|
||||
|
||||
private static List<MailOperationViewModel> GetAvailableMailOperations()
|
||||
private static List<KeyboardShortcutActionViewModel> GetAvailableActions(WinoApplicationMode mode)
|
||||
{
|
||||
var operations = new List<MailOperationViewModel>();
|
||||
|
||||
// Add commonly used mail operations that make sense for keyboard shortcuts
|
||||
var validOperations = new[]
|
||||
KeyboardShortcutAction[] actions = mode switch
|
||||
{
|
||||
MailOperation.Archive,
|
||||
MailOperation.UnArchive,
|
||||
MailOperation.SoftDelete,
|
||||
MailOperation.Move,
|
||||
MailOperation.MoveToJunk,
|
||||
MailOperation.SetFlag,
|
||||
MailOperation.ClearFlag,
|
||||
MailOperation.MarkAsRead,
|
||||
MailOperation.MarkAsUnread,
|
||||
MailOperation.Reply,
|
||||
MailOperation.ReplyAll,
|
||||
MailOperation.Forward
|
||||
WinoApplicationMode.Mail =>
|
||||
[
|
||||
KeyboardShortcutAction.NewMail,
|
||||
KeyboardShortcutAction.ToggleReadUnread,
|
||||
KeyboardShortcutAction.ToggleFlag,
|
||||
KeyboardShortcutAction.ToggleArchive,
|
||||
KeyboardShortcutAction.Delete,
|
||||
KeyboardShortcutAction.Move,
|
||||
KeyboardShortcutAction.Reply,
|
||||
KeyboardShortcutAction.ReplyAll,
|
||||
KeyboardShortcutAction.Send
|
||||
],
|
||||
WinoApplicationMode.Calendar =>
|
||||
[
|
||||
KeyboardShortcutAction.NewEvent,
|
||||
KeyboardShortcutAction.Delete
|
||||
],
|
||||
_ => []
|
||||
};
|
||||
|
||||
foreach (var operation in validOperations)
|
||||
return actions
|
||||
.Select(action => new KeyboardShortcutActionViewModel(mode, action))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private static ModifierKeys GetCurrentModifierKeys()
|
||||
{
|
||||
var modifiers = ModifierKeys.None;
|
||||
|
||||
if (Microsoft.UI.Input.InputKeyboardSource.GetKeyStateForCurrentThread(Windows.System.VirtualKey.Control).HasFlag(Windows.UI.Core.CoreVirtualKeyStates.Down))
|
||||
modifiers |= ModifierKeys.Control;
|
||||
|
||||
if (Microsoft.UI.Input.InputKeyboardSource.GetKeyStateForCurrentThread(Windows.System.VirtualKey.Menu).HasFlag(Windows.UI.Core.CoreVirtualKeyStates.Down))
|
||||
modifiers |= ModifierKeys.Alt;
|
||||
|
||||
if (Microsoft.UI.Input.InputKeyboardSource.GetKeyStateForCurrentThread(Windows.System.VirtualKey.Shift).HasFlag(Windows.UI.Core.CoreVirtualKeyStates.Down))
|
||||
modifiers |= ModifierKeys.Shift;
|
||||
|
||||
if (Microsoft.UI.Input.InputKeyboardSource.GetKeyStateForCurrentThread(Windows.System.VirtualKey.LeftWindows).HasFlag(Windows.UI.Core.CoreVirtualKeyStates.Down) ||
|
||||
Microsoft.UI.Input.InputKeyboardSource.GetKeyStateForCurrentThread(Windows.System.VirtualKey.RightWindows).HasFlag(Windows.UI.Core.CoreVirtualKeyStates.Down))
|
||||
{
|
||||
operations.Add(new MailOperationViewModel(operation));
|
||||
modifiers |= ModifierKeys.Windows;
|
||||
}
|
||||
|
||||
return operations.OrderBy(x => x.DisplayName).ToList();
|
||||
return modifiers;
|
||||
}
|
||||
|
||||
private static string NormalizeKey(Windows.System.VirtualKey key)
|
||||
{
|
||||
return key switch
|
||||
{
|
||||
Windows.System.VirtualKey.Control or
|
||||
Windows.System.VirtualKey.LeftControl or
|
||||
Windows.System.VirtualKey.RightControl or
|
||||
Windows.System.VirtualKey.Menu or
|
||||
Windows.System.VirtualKey.LeftMenu or
|
||||
Windows.System.VirtualKey.RightMenu or
|
||||
Windows.System.VirtualKey.Shift or
|
||||
Windows.System.VirtualKey.LeftShift or
|
||||
Windows.System.VirtualKey.RightShift or
|
||||
Windows.System.VirtualKey.LeftWindows or
|
||||
Windows.System.VirtualKey.RightWindows => string.Empty,
|
||||
_ => key.ToString()
|
||||
};
|
||||
}
|
||||
|
||||
private static string BuildDisplayString(string key, ModifierKeys modifierKeys)
|
||||
{
|
||||
var parts = new List<string>();
|
||||
|
||||
if (modifierKeys.HasFlag(ModifierKeys.Control))
|
||||
parts.Add("Ctrl");
|
||||
if (modifierKeys.HasFlag(ModifierKeys.Alt))
|
||||
parts.Add("Alt");
|
||||
if (modifierKeys.HasFlag(ModifierKeys.Shift))
|
||||
parts.Add("Shift");
|
||||
if (modifierKeys.HasFlag(ModifierKeys.Windows))
|
||||
parts.Add("Win");
|
||||
if (!string.IsNullOrEmpty(key))
|
||||
parts.Add(key);
|
||||
|
||||
return string.Join("+", parts);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user