Improved keyboad shortcuts.

This commit is contained in:
Burak Kaan Köse
2026-03-08 13:21:42 +01:00
parent c1568d33e6
commit 15400d4096
35 changed files with 979 additions and 336 deletions
+3
View File
@@ -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);
}
}
}
+97
View File
@@ -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);
+7 -8
View File
@@ -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="&#xE787;" />
</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="&#xE765;" />
</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="&#xE70F;"
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="&#xE74D;"
FontFamily="Segoe MDL2 Assets"
Style="{ThemeResource SubtleButtonStyle}"
ToolTipService.ToolTip="Delete" />
ToolTipService.ToolTip="{x:Bind domain:Translator.Buttons_Delete}" />
</Grid>
</Border>
</DataTemplate>
+11 -39
View File
@@ -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)
+4 -35
View File
@@ -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);
}
}