Improved keyboad shortcuts.
This commit is contained in:
@@ -37,11 +37,14 @@ public partial class BasePage : Page, IRecipient<LanguageChanged>
|
||||
/// Unregister message recipients for this page. Override to unregister specific message types.
|
||||
/// </summary>
|
||||
protected virtual void UnregisterRecipients() { }
|
||||
|
||||
public virtual CoreBaseViewModel? AssociatedViewModel => null;
|
||||
}
|
||||
|
||||
public abstract class BasePage<T> : BasePage where T : CoreBaseViewModel
|
||||
{
|
||||
public T ViewModel { get; } = WinoApplication.Current.Services.GetService<T>() ?? throw new ArgumentException($"Can't resolve '{typeof(T)}' as view model.");
|
||||
public override CoreBaseViewModel AssociatedViewModel => ViewModel;
|
||||
|
||||
protected BasePage()
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Media.Animation;
|
||||
using Wino.Core.Domain.Enums;
|
||||
using Wino.Mail.ViewModels.Data;
|
||||
using Wino.Messaging.Client.Navigation;
|
||||
|
||||
namespace Wino.Helpers;
|
||||
|
||||
public static class BreadcrumbNavigationHelper
|
||||
{
|
||||
public static bool Navigate(
|
||||
Frame frame,
|
||||
ObservableCollection<BreadcrumbNavigationItemViewModel> pageHistory,
|
||||
BreadcrumbNavigationRequested message,
|
||||
Func<WinoPage, Type> getPageType)
|
||||
{
|
||||
var pageType = getPageType(message.PageType);
|
||||
|
||||
if (pageType == null)
|
||||
return false;
|
||||
|
||||
frame.Navigate(pageType, message.Parameter, new SlideNavigationTransitionInfo
|
||||
{
|
||||
Effect = SlideNavigationTransitionEffect.FromRight
|
||||
});
|
||||
|
||||
SetActiveItem(pageHistory, null);
|
||||
pageHistory.Add(new BreadcrumbNavigationItemViewModel(
|
||||
message,
|
||||
isActive: true,
|
||||
stepNumber: pageHistory.Count + 1,
|
||||
backStackDepth: frame.BackStack.Count + 1));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool GoBack(
|
||||
Frame frame,
|
||||
ObservableCollection<BreadcrumbNavigationItemViewModel> pageHistory,
|
||||
NavigationTransitionEffect slideEffect)
|
||||
{
|
||||
if (!frame.CanGoBack || pageHistory.Count == 0)
|
||||
return false;
|
||||
|
||||
pageHistory.RemoveAt(pageHistory.Count - 1);
|
||||
frame.GoBack(new SlideNavigationTransitionInfo
|
||||
{
|
||||
Effect = slideEffect == NavigationTransitionEffect.FromLeft
|
||||
? SlideNavigationTransitionEffect.FromLeft
|
||||
: SlideNavigationTransitionEffect.FromRight
|
||||
});
|
||||
|
||||
SetActiveItem(pageHistory, pageHistory.Count > 0 ? pageHistory[^1] : null);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool NavigateTo(
|
||||
Frame frame,
|
||||
ObservableCollection<BreadcrumbNavigationItemViewModel> pageHistory,
|
||||
int targetIndex)
|
||||
{
|
||||
if (targetIndex < 0 || targetIndex >= pageHistory.Count)
|
||||
return false;
|
||||
|
||||
var activeIndex = GetActiveIndex(pageHistory);
|
||||
if (activeIndex <= 0 || targetIndex >= activeIndex)
|
||||
return false;
|
||||
|
||||
var targetItem = pageHistory[targetIndex];
|
||||
|
||||
while (frame.BackStack.Count > targetItem.BackStackDepth)
|
||||
{
|
||||
frame.BackStack.RemoveAt(frame.BackStack.Count - 1);
|
||||
}
|
||||
|
||||
if (!frame.CanGoBack)
|
||||
return false;
|
||||
|
||||
frame.GoBack(new SlideNavigationTransitionInfo
|
||||
{
|
||||
Effect = SlideNavigationTransitionEffect.FromLeft
|
||||
});
|
||||
|
||||
while (pageHistory.Count > targetIndex + 1)
|
||||
{
|
||||
pageHistory.RemoveAt(pageHistory.Count - 1);
|
||||
}
|
||||
|
||||
SetActiveItem(pageHistory, targetItem);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static int GetActiveIndex(ObservableCollection<BreadcrumbNavigationItemViewModel> pageHistory)
|
||||
{
|
||||
for (var i = 0; i < pageHistory.Count; i++)
|
||||
{
|
||||
if (pageHistory[i].IsActive)
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
private static void SetActiveItem(
|
||||
ObservableCollection<BreadcrumbNavigationItemViewModel> pageHistory,
|
||||
BreadcrumbNavigationItemViewModel? activeItem)
|
||||
{
|
||||
foreach (var item in pageHistory)
|
||||
{
|
||||
item.IsActive = ReferenceEquals(item, activeItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using CommunityToolkit.WinUI;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Controls.Primitives;
|
||||
@@ -14,6 +15,7 @@ using Wino.Core.Domain;
|
||||
using Wino.Core.Domain.Entities.Mail;
|
||||
using Wino.Core.Domain.Enums;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
using Wino.Core.Domain.Models;
|
||||
using Wino.Core.Domain.Models.Folders;
|
||||
using Wino.Core.Domain.Models.MailItem;
|
||||
using Wino.Core.Domain.Models.Navigation;
|
||||
@@ -43,6 +45,7 @@ public sealed partial class MailAppShell : MailAppShellAbstract,
|
||||
public MailAppShell() : base()
|
||||
{
|
||||
InitializeComponent();
|
||||
PreviewKeyDown += OnPreviewKeyDown;
|
||||
}
|
||||
|
||||
protected override void OnNavigatedFrom(NavigationEventArgs e)
|
||||
@@ -315,6 +318,100 @@ public sealed partial class MailAppShell : MailAppShellAbstract,
|
||||
}
|
||||
}
|
||||
|
||||
private async void OnPreviewKeyDown(object sender, KeyRoutedEventArgs e)
|
||||
{
|
||||
if (e.KeyStatus.RepeatCount > 1 || ShouldIgnoreShortcut())
|
||||
return;
|
||||
|
||||
var key = NormalizeKey(e.Key);
|
||||
if (string.IsNullOrEmpty(key))
|
||||
return;
|
||||
|
||||
var shortcutService = WinoApplication.Current.Services.GetRequiredService<IKeyboardShortcutService>();
|
||||
var shortcut = await shortcutService.GetShortcutForKeyAsync(WinoApplicationMode.Mail, key, GetCurrentModifierKeys());
|
||||
|
||||
if (shortcut == null)
|
||||
return;
|
||||
|
||||
var details = new KeyboardShortcutTriggerDetails
|
||||
{
|
||||
ShortcutId = shortcut.Id,
|
||||
Mode = shortcut.Mode,
|
||||
Action = shortcut.Action,
|
||||
Key = shortcut.Key,
|
||||
ModifierKeys = shortcut.ModifierKeys,
|
||||
Sender = sender,
|
||||
Origin = FocusManager.GetFocusedElement(XamlRoot)
|
||||
};
|
||||
|
||||
await ViewModel.KeyboardShortcutHook(details);
|
||||
|
||||
if (InnerShellFrame.Content is BasePage activePage && activePage.AssociatedViewModel != null)
|
||||
{
|
||||
await activePage.AssociatedViewModel.KeyboardShortcutHook(details);
|
||||
}
|
||||
|
||||
if (details.Handled)
|
||||
{
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
|
||||
private bool ShouldIgnoreShortcut()
|
||||
{
|
||||
var focusedElement = FocusManager.GetFocusedElement(XamlRoot);
|
||||
|
||||
if (focusedElement is TextBox or AutoSuggestBox or PasswordBox or RichEditBox or ComboBox)
|
||||
return true;
|
||||
|
||||
if (focusedElement is FrameworkElement frameworkElement)
|
||||
{
|
||||
var typeName = frameworkElement.GetType().Name;
|
||||
if (typeName.Contains("WebView", StringComparison.OrdinalIgnoreCase))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
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))
|
||||
{
|
||||
modifiers |= ModifierKeys.Windows;
|
||||
}
|
||||
|
||||
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()
|
||||
};
|
||||
}
|
||||
|
||||
protected override void RegisterRecipients()
|
||||
{
|
||||
base.RegisterRecipients();
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using System;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Input;
|
||||
using Wino.Core.Domain.Enums;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
using Wino.Core.Domain.Models;
|
||||
using Wino.Mail.Views.Abstract;
|
||||
using Wino.Messaging.Client.Calendar;
|
||||
|
||||
@@ -17,14 +23,13 @@ public sealed partial class CalendarAppShell : CalendarAppShellAbstract,
|
||||
public CalendarAppShell()
|
||||
{
|
||||
InitializeComponent();
|
||||
PreviewKeyDown += OnPreviewKeyDown;
|
||||
|
||||
// Window.Current.SetTitleBar(DragArea);
|
||||
ManageCalendarDisplayType(ViewModel.StatePersistenceService.CalendarDisplayType);
|
||||
}
|
||||
|
||||
private void ManageCalendarDisplayType(Core.Domain.Enums.CalendarDisplayType displayType)
|
||||
{
|
||||
// Go to different states based on the display type.
|
||||
if (displayType == Core.Domain.Enums.CalendarDisplayType.Month)
|
||||
{
|
||||
VisualStateManager.GoToState(this, STATE_VerticalCalendar, false);
|
||||
@@ -44,12 +49,6 @@ public sealed partial class CalendarAppShell : CalendarAppShellAbstract,
|
||||
ManageCalendarDisplayType(message.NewDisplayType);
|
||||
}
|
||||
|
||||
//private void ShellFrameContentNavigated(object sender, Microsoft.UI.Xaml.Navigation.NavigationEventArgs e)
|
||||
// => RealAppBar.ShellFrameContent = (e.Content as BasePage).ShellContent;
|
||||
|
||||
//private void AppBarBackButtonClicked(Core.UWP.Controls.WinoAppTitleBar sender, RoutedEventArgs args)
|
||||
// => ViewModel.NavigationService.GoBack();
|
||||
|
||||
protected override void RegisterRecipients()
|
||||
{
|
||||
base.RegisterRecipients();
|
||||
@@ -63,4 +62,98 @@ public sealed partial class CalendarAppShell : CalendarAppShellAbstract,
|
||||
|
||||
WeakReferenceMessenger.Default.Unregister<CalendarDisplayTypeChangedMessage>(this);
|
||||
}
|
||||
|
||||
private async void OnPreviewKeyDown(object sender, KeyRoutedEventArgs e)
|
||||
{
|
||||
if (e.KeyStatus.RepeatCount > 1 || ShouldIgnoreShortcut())
|
||||
return;
|
||||
|
||||
var key = NormalizeKey(e.Key);
|
||||
if (string.IsNullOrEmpty(key))
|
||||
return;
|
||||
|
||||
var shortcutService = WinoApplication.Current.Services.GetRequiredService<IKeyboardShortcutService>();
|
||||
var shortcut = await shortcutService.GetShortcutForKeyAsync(WinoApplicationMode.Calendar, key, GetCurrentModifierKeys());
|
||||
|
||||
if (shortcut == null)
|
||||
return;
|
||||
|
||||
var details = new KeyboardShortcutTriggerDetails
|
||||
{
|
||||
ShortcutId = shortcut.Id,
|
||||
Mode = shortcut.Mode,
|
||||
Action = shortcut.Action,
|
||||
Key = shortcut.Key,
|
||||
ModifierKeys = shortcut.ModifierKeys,
|
||||
Sender = sender,
|
||||
Origin = FocusManager.GetFocusedElement(XamlRoot)
|
||||
};
|
||||
|
||||
await ViewModel.KeyboardShortcutHook(details);
|
||||
|
||||
if (InnerShellFrame.Content is BasePage activePage && activePage.AssociatedViewModel != null)
|
||||
{
|
||||
await activePage.AssociatedViewModel.KeyboardShortcutHook(details);
|
||||
}
|
||||
|
||||
if (details.Handled)
|
||||
{
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
|
||||
private bool ShouldIgnoreShortcut()
|
||||
{
|
||||
var focusedElement = FocusManager.GetFocusedElement(XamlRoot);
|
||||
|
||||
if (focusedElement is TextBox or AutoSuggestBox or PasswordBox or RichEditBox or ComboBox)
|
||||
return true;
|
||||
|
||||
if (focusedElement is FrameworkElement frameworkElement)
|
||||
{
|
||||
var typeName = frameworkElement.GetType().Name;
|
||||
if (typeName.Contains("WebView", StringComparison.OrdinalIgnoreCase))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
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))
|
||||
{
|
||||
modifiers |= ModifierKeys.Windows;
|
||||
}
|
||||
|
||||
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()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -470,8 +470,8 @@
|
||||
<Border
|
||||
x:Name="AttachmentsPane"
|
||||
Margin="0,8,0,0"
|
||||
AllowDrop="True"
|
||||
Padding="16"
|
||||
AllowDrop="True"
|
||||
Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
|
||||
CornerRadius="{StaticResource ControlCornerRadius}"
|
||||
DragLeave="AttachmentsPane_DragLeave"
|
||||
@@ -487,7 +487,10 @@
|
||||
Style="{StaticResource TransparentActionButtonStyle}">
|
||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||
<coreControls:WinoFontIcon FontSize="14" Icon="AttachmentNew" />
|
||||
<TextBlock FontSize="18" FontWeight="SemiBold" Text="+" />
|
||||
<TextBlock
|
||||
FontSize="18"
|
||||
FontWeight="SemiBold"
|
||||
Text="+" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
|
||||
@@ -560,7 +563,20 @@
|
||||
<TextBlock Style="{StaticResource BodyStrongTextBlockStyle}" Text="{x:Bind domain:Translator.CalendarEventCompose_Notes}" />
|
||||
</StackPanel>
|
||||
<!-- Notes Editor -->
|
||||
<mailControls:EditorTabbedCommandBarControl CommandTarget="{x:Bind NotesEditor}" />
|
||||
<mailControls:EditorTabbedCommandBarControl CommandTarget="{x:Bind NotesEditor}">
|
||||
<mailControls:EditorTabbedCommandBarControl.PaneCustomContent>
|
||||
<toolkit:TabbedCommandBarItem
|
||||
CommandAlignment="Right"
|
||||
IsDynamicOverflowEnabled="True"
|
||||
OverflowButtonAlignment="Left">
|
||||
<AppBarButton Click="ToggleNotesEditorThemeClicked" ToolTipService.ToolTip="{x:Bind GetEditorThemeToolTip(NotesEditor.IsEditorDarkMode), Mode=OneWay}">
|
||||
<AppBarButton.Icon>
|
||||
<coreControls:WinoFontIcon Icon="{x:Bind GetEditorThemeIcon(NotesEditor.IsEditorDarkMode), Mode=OneWay}" />
|
||||
</AppBarButton.Icon>
|
||||
</AppBarButton>
|
||||
</toolkit:TabbedCommandBarItem>
|
||||
</mailControls:EditorTabbedCommandBarControl.PaneCustomContent>
|
||||
</mailControls:EditorTabbedCommandBarControl>
|
||||
<mailControls:WebViewEditorControl
|
||||
x:Name="NotesEditor"
|
||||
MinHeight="500"
|
||||
|
||||
@@ -15,6 +15,7 @@ using Windows.Storage;
|
||||
using Wino.Core.Domain;
|
||||
using Wino.Messaging.Client.Shell;
|
||||
using Wino.Calendar.ViewModels.Data;
|
||||
using Wino.Mail.WinUI.Controls;
|
||||
using Wino.Mail.WinUI.Views.Abstract;
|
||||
|
||||
namespace Wino.Calendar.Views;
|
||||
@@ -29,6 +30,15 @@ public sealed partial class CalendarEventComposePage : CalendarEventComposePageA
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public WinoIconGlyph GetEditorThemeIcon(bool isDarkMode) => isDarkMode ? WinoIconGlyph.LightEditor : WinoIconGlyph.DarkEditor;
|
||||
|
||||
public string GetEditorThemeToolTip(bool isDarkMode) => isDarkMode ? Translator.Composer_LightTheme : Translator.Composer_DarkTheme;
|
||||
|
||||
private void ToggleNotesEditorThemeClicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
NotesEditor.ToggleEditorTheme();
|
||||
}
|
||||
|
||||
protected override async void OnNavigatedTo(NavigationEventArgs e)
|
||||
{
|
||||
base.OnNavigatedTo(e);
|
||||
|
||||
@@ -145,14 +145,13 @@
|
||||
Visibility="{x:Bind ViewModel.IsDraftBusy, Mode=OneWay}">
|
||||
<ProgressRing IsActive="True" />
|
||||
</AppBarButton>
|
||||
<AppBarToggleButton
|
||||
x:Name="EditorThemeToggleButton"
|
||||
IsChecked="{x:Bind WebViewEditor.IsEditorDarkMode, Mode=TwoWay}"
|
||||
ToolTipService.ToolTip="Toggle editor dark mode">
|
||||
<AppBarToggleButton.Icon>
|
||||
<coreControls:WinoFontIcon Icon="DarkEditor" />
|
||||
</AppBarToggleButton.Icon>
|
||||
</AppBarToggleButton>
|
||||
<AppBarButton
|
||||
Click="ToggleEditorThemeClicked"
|
||||
ToolTipService.ToolTip="{x:Bind GetEditorThemeToolTip(WebViewEditor.IsEditorDarkMode), Mode=OneWay}">
|
||||
<AppBarButton.Icon>
|
||||
<coreControls:WinoFontIcon Icon="{x:Bind GetEditorThemeIcon(WebViewEditor.IsEditorDarkMode), Mode=OneWay}" />
|
||||
</AppBarButton.Icon>
|
||||
</AppBarButton>
|
||||
<AppBarButton Command="{x:Bind ViewModel.DiscardCommand}" Label="{x:Bind domain:Translator.Buttons_Discard}">
|
||||
<AppBarButton.Icon>
|
||||
<coreControls:WinoFontIcon Icon="Delete" />
|
||||
|
||||
@@ -22,6 +22,7 @@ using Wino.Core.Domain.Entities.Shared;
|
||||
using Wino.Core.Domain.Models.Reader;
|
||||
using Wino.Mail.ViewModels.Data;
|
||||
using Wino.Mail.ViewModels.Messages;
|
||||
using Wino.Mail.WinUI.Controls;
|
||||
using Wino.Mail.WinUI.Extensions;
|
||||
using Wino.Messaging.Client.Mails;
|
||||
using Wino.Messaging.Client.Shell;
|
||||
@@ -43,6 +44,15 @@ public sealed partial class ComposePage : ComposePageAbstract,
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public WinoIconGlyph GetEditorThemeIcon(bool isDarkMode) => isDarkMode ? WinoIconGlyph.LightEditor : WinoIconGlyph.DarkEditor;
|
||||
|
||||
public string GetEditorThemeToolTip(bool isDarkMode) => isDarkMode ? Translator.Composer_LightTheme : Translator.Composer_DarkTheme;
|
||||
|
||||
private void ToggleEditorThemeClicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
WebViewEditor.ToggleEditorTheme();
|
||||
}
|
||||
|
||||
private async void GlobalFocusManagerGotFocus(object? sender, FocusManagerGotFocusEventArgs e)
|
||||
{
|
||||
// In order to delegate cursor to the inner editor for WebView2.
|
||||
|
||||
@@ -52,7 +52,6 @@ public sealed partial class MailListPage : MailListPageAbstract,
|
||||
|
||||
private IStatePersistanceService StatePersistenceService { get; } = WinoApplication.Current.Services.GetService<IStatePersistanceService>() ?? throw new Exception($"Can't resolve {nameof(KeyPressService)}");
|
||||
private IKeyPressService KeyPressService { get; } = WinoApplication.Current.Services.GetService<IKeyPressService>() ?? throw new Exception($"Can't resolve {nameof(KeyPressService)}");
|
||||
private IKeyboardShortcutService KeyboardShortcutService { get; } = WinoApplication.Current.Services.GetService<IKeyboardShortcutService>() ?? throw new Exception($"Can't resolve {nameof(IKeyboardShortcutService)}");
|
||||
public MailListPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
@@ -671,19 +670,7 @@ public sealed partial class MailListPage : MailListPageAbstract,
|
||||
}
|
||||
else
|
||||
{
|
||||
// Check keyboard shortcuts from service.
|
||||
ModifierKeys modifiers = args.Modifiers.ToDomainModifierKeys();
|
||||
|
||||
var operation = await KeyboardShortcutService.GetMailOperationForKeyAsync(args.Key.ToString(), modifiers);
|
||||
|
||||
if (operation != null)
|
||||
{
|
||||
ViewModel.ExecuteMailOperationCommand.Execute(operation);
|
||||
}
|
||||
else
|
||||
{
|
||||
args.Handled = false;
|
||||
}
|
||||
args.Handled = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,9 +3,9 @@ using System.Linq;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Microsoft.UI.Xaml.Media.Animation;
|
||||
using Microsoft.UI.Xaml.Navigation;
|
||||
using MoreLinq;
|
||||
using Wino.Core.Domain;
|
||||
using Wino.Core.Domain.Enums;
|
||||
using Wino.Helpers;
|
||||
using Wino.Mail.ViewModels.Data;
|
||||
using Wino.Mail.WinUI.Views.Abstract;
|
||||
using Wino.Messaging.Client.Navigation;
|
||||
@@ -42,7 +42,7 @@ public sealed partial class ManageAccountsPage : ManageAccountsPageAbstract,
|
||||
AccountPagesFrame.Navigated += AccountPagesFrameNavigated;
|
||||
|
||||
var initialRequest = new BreadcrumbNavigationRequested(Translator.MenuManageAccounts, WinoPage.AccountManagementPage);
|
||||
PageHistory.Add(new BreadcrumbNavigationItemViewModel(initialRequest, true));
|
||||
PageHistory.Add(new BreadcrumbNavigationItemViewModel(initialRequest, true, backStackDepth: AccountPagesFrame.BackStack.Count + 1));
|
||||
|
||||
var accountManagementPageType = ViewModel.NavigationService.GetPageType(WinoPage.AccountManagementPage);
|
||||
|
||||
@@ -69,15 +69,7 @@ public sealed partial class ManageAccountsPage : ManageAccountsPageAbstract,
|
||||
|
||||
void IRecipient<BreadcrumbNavigationRequested>.Receive(BreadcrumbNavigationRequested message)
|
||||
{
|
||||
var pageType = ViewModel.NavigationService.GetPageType(message.PageType);
|
||||
|
||||
if (pageType == null) return;
|
||||
|
||||
AccountPagesFrame.Navigate(pageType, message.Parameter, new SlideNavigationTransitionInfo() { Effect = Microsoft.UI.Xaml.Media.Animation.SlideNavigationTransitionEffect.FromRight });
|
||||
|
||||
PageHistory.ForEach(a => a.IsActive = false);
|
||||
|
||||
PageHistory.Add(new BreadcrumbNavigationItemViewModel(message, true));
|
||||
BreadcrumbNavigationHelper.Navigate(AccountPagesFrame, PageHistory, message, ViewModel.NavigationService.GetPageType);
|
||||
UpdateWindowTitle();
|
||||
}
|
||||
|
||||
@@ -89,40 +81,20 @@ public sealed partial class ManageAccountsPage : ManageAccountsPageAbstract,
|
||||
|
||||
private void GoBackFrame(Core.Domain.Enums.NavigationTransitionEffect slideEffect)
|
||||
{
|
||||
if (AccountPagesFrame.CanGoBack)
|
||||
{
|
||||
PageHistory.RemoveAt(PageHistory.Count - 1);
|
||||
if (!BreadcrumbNavigationHelper.GoBack(AccountPagesFrame, PageHistory, slideEffect))
|
||||
return;
|
||||
|
||||
var winuiEffect = slideEffect switch
|
||||
{
|
||||
Core.Domain.Enums.NavigationTransitionEffect.FromLeft => Microsoft.UI.Xaml.Media.Animation.SlideNavigationTransitionEffect.FromLeft,
|
||||
_ => Microsoft.UI.Xaml.Media.Animation.SlideNavigationTransitionEffect.FromRight,
|
||||
};
|
||||
|
||||
AccountPagesFrame.GoBack(new SlideNavigationTransitionInfo() { Effect = winuiEffect });
|
||||
|
||||
// Set the new last item as active
|
||||
if (PageHistory.Count > 0)
|
||||
{
|
||||
PageHistory.ForEach(a => a.IsActive = false);
|
||||
PageHistory[PageHistory.Count - 1].IsActive = true;
|
||||
}
|
||||
|
||||
// Update back button visibility after navigation
|
||||
ViewModel.StatePersistenceService.IsManageAccountsNavigating = AccountPagesFrame.CanGoBack;
|
||||
UpdateWindowTitle();
|
||||
}
|
||||
ViewModel.StatePersistenceService.IsManageAccountsNavigating = AccountPagesFrame.CanGoBack;
|
||||
UpdateWindowTitle();
|
||||
}
|
||||
|
||||
private void BreadItemClicked(Microsoft.UI.Xaml.Controls.BreadcrumbBar sender, Microsoft.UI.Xaml.Controls.BreadcrumbBarItemClickedEventArgs args)
|
||||
{
|
||||
var clickedPageHistory = PageHistory[args.Index];
|
||||
if (!BreadcrumbNavigationHelper.NavigateTo(AccountPagesFrame, PageHistory, args.Index))
|
||||
return;
|
||||
|
||||
// Trigger GoBack repeatedly until we reach the clicked breadcrumb item
|
||||
while (PageHistory.FirstOrDefault(a => a.IsActive) != clickedPageHistory)
|
||||
{
|
||||
ViewModel.NavigationService.GoBack();
|
||||
}
|
||||
ViewModel.StatePersistenceService.IsManageAccountsNavigating = AccountPagesFrame.CanGoBack;
|
||||
UpdateWindowTitle();
|
||||
}
|
||||
|
||||
public void Receive(BackBreadcrumNavigationRequested message)
|
||||
|
||||
@@ -221,6 +221,17 @@
|
||||
<FontIcon Foreground="{ThemeResource AccentTextFillColorPrimaryBrush}" Glyph="" />
|
||||
</controls:SettingsCard.HeaderIcon>
|
||||
</controls:SettingsCard>
|
||||
|
||||
<controls:SettingsCard
|
||||
Click="SettingOptionClicked"
|
||||
Description="{x:Bind domain:Translator.Settings_KeyboardShortcuts_Description}"
|
||||
Header="{x:Bind domain:Translator.Settings_KeyboardShortcuts_Title}"
|
||||
IsClickEnabled="True"
|
||||
Tag="{x:Bind enums:WinoPage.KeyboardShortcutsPage}">
|
||||
<controls:SettingsCard.HeaderIcon>
|
||||
<FontIcon Foreground="{ThemeResource AccentTextFillColorPrimaryBrush}" Glyph="" />
|
||||
</controls:SettingsCard.HeaderIcon>
|
||||
</controls:SettingsCard>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
</abstract:SettingOptionsPageAbstract>
|
||||
|
||||
@@ -52,19 +52,28 @@
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<StackPanel Grid.Column="0">
|
||||
<TextBlock
|
||||
Margin="0,0,0,4"
|
||||
Style="{ThemeResource BodyStrongTextBlockStyle}"
|
||||
Text="{x:Bind MailOperationDisplayName}" />
|
||||
Text="{x:Bind ActionDisplayName}" />
|
||||
<TextBlock
|
||||
Opacity="0.8"
|
||||
Style="{ThemeResource CaptionTextBlockStyle}"
|
||||
Text="{x:Bind DisplayName}" />
|
||||
</StackPanel>
|
||||
|
||||
<TextBlock
|
||||
Grid.Column="1"
|
||||
Margin="8,0"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Style="{ThemeResource CaptionTextBlockStyle}"
|
||||
Text="{x:Bind ModeDisplayName}" />
|
||||
|
||||
<Button
|
||||
Grid.Column="2"
|
||||
Margin="4,0"
|
||||
@@ -73,7 +82,7 @@
|
||||
Content=""
|
||||
FontFamily="Segoe MDL2 Assets"
|
||||
Style="{ThemeResource SubtleButtonStyle}"
|
||||
ToolTipService.ToolTip="Edit" />
|
||||
ToolTipService.ToolTip="{x:Bind domain:Translator.Buttons_Edit}" />
|
||||
|
||||
<Button
|
||||
Grid.Column="3"
|
||||
@@ -83,7 +92,7 @@
|
||||
Content=""
|
||||
FontFamily="Segoe MDL2 Assets"
|
||||
Style="{ThemeResource SubtleButtonStyle}"
|
||||
ToolTipService.ToolTip="Delete" />
|
||||
ToolTipService.ToolTip="{x:Bind domain:Translator.Buttons_Delete}" />
|
||||
</Grid>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
|
||||
@@ -3,9 +3,9 @@ using System.Linq;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Microsoft.UI.Xaml.Media.Animation;
|
||||
using Microsoft.UI.Xaml.Navigation;
|
||||
using MoreLinq;
|
||||
using Wino.Core.Domain;
|
||||
using Wino.Core.Domain.Enums;
|
||||
using Wino.Helpers;
|
||||
using Wino.Mail.ViewModels.Data;
|
||||
using Wino.Messaging.Client.Navigation;
|
||||
using Wino.Views.Abstract;
|
||||
@@ -35,7 +35,7 @@ public sealed partial class SettingsPage : SettingsPageAbstract,
|
||||
SettingsFrame.Navigate(typeof(SettingOptionsPage), null, new SuppressNavigationTransitionInfo());
|
||||
|
||||
var initialRequest = new BreadcrumbNavigationRequested(Translator.MenuSettings, WinoPage.SettingOptionsPage);
|
||||
PageHistory.Add(new BreadcrumbNavigationItemViewModel(initialRequest, true));
|
||||
PageHistory.Add(new BreadcrumbNavigationItemViewModel(initialRequest, true, backStackDepth: SettingsFrame.BackStack.Count + 1));
|
||||
|
||||
if (e.Parameter is WinoPage parameterPage)
|
||||
{
|
||||
@@ -99,15 +99,7 @@ public sealed partial class SettingsPage : SettingsPageAbstract,
|
||||
|
||||
void IRecipient<BreadcrumbNavigationRequested>.Receive(BreadcrumbNavigationRequested message)
|
||||
{
|
||||
var pageType = ViewModel.NavigationService.GetPageType(message.PageType);
|
||||
|
||||
if (pageType == null) return;
|
||||
|
||||
SettingsFrame.Navigate(pageType, message.Parameter, new SlideNavigationTransitionInfo() { Effect = SlideNavigationTransitionEffect.FromRight });
|
||||
|
||||
PageHistory.ForEach(a => a.IsActive = false);
|
||||
|
||||
PageHistory.Add(new BreadcrumbNavigationItemViewModel(message, true));
|
||||
BreadcrumbNavigationHelper.Navigate(SettingsFrame, PageHistory, message, ViewModel.NavigationService.GetPageType);
|
||||
UpdateWindowTitle();
|
||||
}
|
||||
|
||||
@@ -119,40 +111,20 @@ public sealed partial class SettingsPage : SettingsPageAbstract,
|
||||
|
||||
private void GoBackFrame(Core.Domain.Enums.NavigationTransitionEffect slideEffect)
|
||||
{
|
||||
if (SettingsFrame.CanGoBack)
|
||||
{
|
||||
PageHistory.RemoveAt(PageHistory.Count - 1);
|
||||
if (!BreadcrumbNavigationHelper.GoBack(SettingsFrame, PageHistory, slideEffect))
|
||||
return;
|
||||
|
||||
var winuiEffect = slideEffect switch
|
||||
{
|
||||
Core.Domain.Enums.NavigationTransitionEffect.FromLeft => Microsoft.UI.Xaml.Media.Animation.SlideNavigationTransitionEffect.FromLeft,
|
||||
_ => Microsoft.UI.Xaml.Media.Animation.SlideNavigationTransitionEffect.FromRight,
|
||||
};
|
||||
|
||||
SettingsFrame.GoBack(new SlideNavigationTransitionInfo() { Effect = winuiEffect });
|
||||
|
||||
// Set the new last item as active
|
||||
if (PageHistory.Count > 0)
|
||||
{
|
||||
PageHistory.ForEach(a => a.IsActive = false);
|
||||
PageHistory[PageHistory.Count - 1].IsActive = true;
|
||||
}
|
||||
|
||||
// Update back button visibility after navigation
|
||||
ViewModel.StatePersistenceService.IsSettingsNavigating = SettingsFrame.CanGoBack;
|
||||
UpdateWindowTitle();
|
||||
}
|
||||
ViewModel.StatePersistenceService.IsSettingsNavigating = SettingsFrame.CanGoBack;
|
||||
UpdateWindowTitle();
|
||||
}
|
||||
|
||||
private void BreadItemClicked(Microsoft.UI.Xaml.Controls.BreadcrumbBar sender, Microsoft.UI.Xaml.Controls.BreadcrumbBarItemClickedEventArgs args)
|
||||
{
|
||||
var clickedPageHistory = PageHistory[args.Index];
|
||||
if (!BreadcrumbNavigationHelper.NavigateTo(SettingsFrame, PageHistory, args.Index))
|
||||
return;
|
||||
|
||||
// Trigger GoBack repeatedly until we reach the clicked breadcrumb item
|
||||
while (PageHistory.FirstOrDefault(a => a.IsActive) != clickedPageHistory)
|
||||
{
|
||||
ViewModel.NavigationService.GoBack();
|
||||
}
|
||||
ViewModel.StatePersistenceService.IsSettingsNavigating = SettingsFrame.CanGoBack;
|
||||
UpdateWindowTitle();
|
||||
}
|
||||
|
||||
public void Receive(BackBreadcrumNavigationRequested message)
|
||||
|
||||
@@ -3,8 +3,8 @@ using System.Linq;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Microsoft.UI.Xaml.Media.Animation;
|
||||
using Microsoft.UI.Xaml.Navigation;
|
||||
using MoreLinq;
|
||||
using Wino.Core.Domain.Enums;
|
||||
using Wino.Helpers;
|
||||
using Wino.Mail.ViewModels.Data;
|
||||
using Wino.Mail.WinUI.Views.Abstract;
|
||||
using Wino.Messaging.Client.Navigation;
|
||||
@@ -45,16 +45,7 @@ public sealed partial class WelcomeHostPage : WelcomeHostPageAbstract,
|
||||
|
||||
public void Receive(BreadcrumbNavigationRequested message)
|
||||
{
|
||||
var pageType = ViewModel.NavigationService.GetPageType(message.PageType);
|
||||
if (pageType == null) return;
|
||||
|
||||
WizardFrame.Navigate(pageType, message.Parameter, new SlideNavigationTransitionInfo
|
||||
{
|
||||
Effect = SlideNavigationTransitionEffect.FromRight
|
||||
});
|
||||
|
||||
PageHistory.ForEach(a => a.IsActive = false);
|
||||
PageHistory.Add(new BreadcrumbNavigationItemViewModel(message, isActive: true, stepNumber: PageHistory.Count + 1));
|
||||
BreadcrumbNavigationHelper.Navigate(WizardFrame, PageHistory, message, ViewModel.NavigationService.GetPageType);
|
||||
}
|
||||
|
||||
public void Receive(BackBreadcrumNavigationRequested message)
|
||||
@@ -64,33 +55,11 @@ public sealed partial class WelcomeHostPage : WelcomeHostPageAbstract,
|
||||
|
||||
private void GoBackFrame()
|
||||
{
|
||||
if (!WizardFrame.CanGoBack) return;
|
||||
|
||||
PageHistory.RemoveAt(PageHistory.Count - 1);
|
||||
WizardFrame.GoBack(new SlideNavigationTransitionInfo
|
||||
{
|
||||
Effect = SlideNavigationTransitionEffect.FromLeft
|
||||
});
|
||||
|
||||
if (PageHistory.Count > 0)
|
||||
{
|
||||
PageHistory.ForEach(a => a.IsActive = false);
|
||||
PageHistory[PageHistory.Count - 1].IsActive = true;
|
||||
}
|
||||
BreadcrumbNavigationHelper.GoBack(WizardFrame, PageHistory, NavigationTransitionEffect.FromLeft);
|
||||
}
|
||||
|
||||
private void BreadItemClicked(Microsoft.UI.Xaml.Controls.BreadcrumbBar sender, Microsoft.UI.Xaml.Controls.BreadcrumbBarItemClickedEventArgs args)
|
||||
{
|
||||
var clickedItem = PageHistory[args.Index];
|
||||
var currentActive = PageHistory.FirstOrDefault(a => a.IsActive);
|
||||
|
||||
// Only allow navigating backwards (clicking items before current)
|
||||
if (currentActive == null || args.Index >= PageHistory.IndexOf(currentActive))
|
||||
return;
|
||||
|
||||
while (PageHistory.FirstOrDefault(a => a.IsActive) != clickedItem && WizardFrame.CanGoBack)
|
||||
{
|
||||
GoBackFrame();
|
||||
}
|
||||
BreadcrumbNavigationHelper.NavigateTo(WizardFrame, PageHistory, args.Index);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user