Better creation of context menu items for calendar events.

This commit is contained in:
Burak Kaan Köse
2026-04-09 01:01:28 +02:00
parent 832a4b0348
commit aaf0b7d069
4 changed files with 98 additions and 60 deletions
@@ -1,56 +1,30 @@
using System.Collections.Generic; using System.Collections.Generic;
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Messaging; using CommunityToolkit.Mvvm.Messaging;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Controls;
using Wino.Calendar.ViewModels.Messages;
using Wino.Calendar.ViewModels.Data; using Wino.Calendar.ViewModels.Data;
using Wino.Calendar.ViewModels.Messages;
using Wino.Core.Domain; using Wino.Core.Domain;
using Wino.Core.Domain.Enums; using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Interfaces;
using Wino.Core.Domain.Models.Calendar; using Wino.Core.Domain.Models.Calendar;
using Wino.Mail.WinUI;
using Wino.Mail.WinUI.Controls; using Wino.Mail.WinUI.Controls;
namespace Wino.Calendar.Controls; namespace Wino.Calendar.Controls;
public partial class CalendarItemCommandBarFlyout : CommandBarFlyout public partial class CalendarItemCommandBarFlyout : CommandBarFlyout
{ {
private readonly ICalendarContextMenuItemService _contextMenuItemService; private readonly RelayCommand<CalendarContextMenuAction> _executeActionCommand;
public static readonly DependencyProperty ItemProperty = DependencyProperty.Register(nameof(Item), typeof(CalendarItemViewModel), typeof(CalendarItemCommandBarFlyout), new PropertyMetadata(null, new PropertyChangedCallback(OnItemChanged)));
public CalendarItemViewModel Item
{
get { return (CalendarItemViewModel)GetValue(ItemProperty); }
set { SetValue(ItemProperty, value); }
}
public CalendarItemCommandBarFlyout() public CalendarItemCommandBarFlyout()
{ {
_contextMenuItemService = WinoApplication.Current.Services.GetRequiredService<ICalendarContextMenuItemService>(); _executeActionCommand = new RelayCommand<CalendarContextMenuAction>(ExecuteAction);
Opening += FlyoutOpening;
} }
private static void OnItemChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) public CalendarItemViewModel? Item { get; set; }
public void SetMenuItems(IReadOnlyList<CalendarContextMenuItem> menuItems)
{ {
if (d is CalendarItemCommandBarFlyout flyout) ClearMenuItems();
{
flyout.UpdateMenuItems();
}
}
private void FlyoutOpening(object sender, object e) => UpdateMenuItems();
private void UpdateMenuItems()
{
PrimaryCommands.Clear();
SecondaryCommands.Clear();
if (Item?.CalendarItem == null)
return;
var menuItems = _contextMenuItemService.GetContextMenuItems(Item.CalendarItem);
foreach (var menuItem in menuItems) foreach (var menuItem in menuItems)
{ {
@@ -63,12 +37,20 @@ public partial class CalendarItemCommandBarFlyout : CommandBarFlyout
} }
} }
public void ClearMenuItems()
{
PrimaryCommands.Clear();
SecondaryCommands.Clear();
}
private AppBarButton BuildAppBarButton(CalendarContextMenuItem menuItem) private AppBarButton BuildAppBarButton(CalendarContextMenuItem menuItem)
{ {
var button = new AppBarButton var button = new AppBarButton
{ {
Label = GetActionLabel(menuItem.Action), Label = GetActionLabel(menuItem.Action),
IsEnabled = menuItem.IsEnabled, IsEnabled = menuItem.IsEnabled,
Command = _executeActionCommand,
CommandParameter = menuItem.Action,
Icon = new WinoFontIcon Icon = new WinoFontIcon
{ {
Icon = GetActionIcon(menuItem.Action), Icon = GetActionIcon(menuItem.Action),
@@ -82,10 +64,6 @@ public partial class CalendarItemCommandBarFlyout : CommandBarFlyout
PopulateMenuFlyoutItems(flyout.Items, menuItem.Children); PopulateMenuFlyoutItems(flyout.Items, menuItem.Children);
button.Flyout = flyout; button.Flyout = flyout;
} }
else
{
button.Click += (_, _) => ExecuteAction(menuItem.Action);
}
return button; return button;
} }
@@ -110,10 +88,11 @@ public partial class CalendarItemCommandBarFlyout : CommandBarFlyout
var flyoutItem = new MenuFlyoutItem var flyoutItem = new MenuFlyoutItem
{ {
Text = GetActionLabel(menuItem.Action), Text = GetActionLabel(menuItem.Action),
IsEnabled = menuItem.IsEnabled IsEnabled = menuItem.IsEnabled,
Command = _executeActionCommand,
CommandParameter = menuItem.Action
}; };
flyoutItem.Click += (_, _) => ExecuteAction(menuItem.Action);
items.Add(flyoutItem); items.Add(flyoutItem);
} }
} }
@@ -121,7 +100,8 @@ public partial class CalendarItemCommandBarFlyout : CommandBarFlyout
private void ExecuteAction(CalendarContextMenuAction action) private void ExecuteAction(CalendarContextMenuAction action)
{ {
if (Item == null) // We don't want to trigger any action or hide the flyout if it's a sub menu item.
if (Item == null || (action.ShowAs == null && action.ResponseStatus == null && action.TargetType == null))
return; return;
WeakReferenceMessenger.Default.Send(new CalendarItemContextActionRequestedMessage(Item, action)); WeakReferenceMessenger.Default.Send(new CalendarItemContextActionRequestedMessage(Item, action));
@@ -31,9 +31,10 @@
<Grid.ContextFlyout> <Grid.ContextFlyout>
<local:CalendarItemCommandBarFlyout <local:CalendarItemCommandBarFlyout
AlwaysExpanded="True" AlwaysExpanded="True"
Item="{x:Bind CalendarItem, Mode=OneWay}" Closed="CalendarItemCommandBarFlyout_Closed"
Opening="CalendarItemCommandBarFlyout_Opening"
Placement="BottomEdgeAlignedLeft" Placement="BottomEdgeAlignedLeft"
ShowMode="Transient" /> ShowMode="Auto" />
</Grid.ContextFlyout> </Grid.ContextFlyout>
@@ -80,8 +81,8 @@
<muxc:ProgressRing <muxc:ProgressRing
x:Name="BusyRing" x:Name="BusyRing"
Grid.Column="2" Grid.Column="2"
Width="14" Width="10"
Height="14" Height="10"
Margin="0,2,2,0" Margin="0,2,2,0"
HorizontalAlignment="Right" HorizontalAlignment="Right"
VerticalAlignment="Top" VerticalAlignment="Top"
@@ -111,6 +112,7 @@
</StackPanel> </StackPanel>
<VisualStateManager.VisualStateGroups> <VisualStateManager.VisualStateGroups>
<!-- TODO: Selection of items are disabled temporarily. -->
<VisualStateGroup x:Name="SelectionStates"> <VisualStateGroup x:Name="SelectionStates">
<VisualState x:Name="NonSelected" /> <VisualState x:Name="NonSelected" />
<VisualState x:Name="Selected"> <VisualState x:Name="Selected">
@@ -1,19 +1,20 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using CommunityToolkit.Mvvm.Messaging; using CommunityToolkit.Mvvm.Messaging;
using CommunityToolkit.WinUI; using Microsoft.Extensions.DependencyInjection;
using Itenso.TimePeriod;
using Microsoft.UI.Xaml; using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Input; using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Media;
using Windows.ApplicationModel.DataTransfer; using Windows.ApplicationModel.DataTransfer;
using Wino.Calendar.ViewModels.Data; using Wino.Calendar.ViewModels.Data;
using Wino.Calendar.ViewModels.Messages; using Wino.Calendar.ViewModels.Messages;
using Wino.Core.Domain; using Wino.Core.Domain.Interfaces;
using Wino.Mail.WinUI;
namespace Wino.Calendar.Controls; namespace Wino.Calendar.Controls;
public sealed partial class CalendarItemControl : UserControl public sealed partial class CalendarItemControl : UserControl
{ {
private readonly ICalendarContextMenuItemService _contextMenuItemService;
// Single tap has a delay to report double taps properly. // Single tap has a delay to report double taps properly.
private bool isSingleTap = false; private bool isSingleTap = false;
@@ -44,6 +45,7 @@ public sealed partial class CalendarItemControl : UserControl
public CalendarItemControl() public CalendarItemControl()
{ {
_contextMenuItemService = WinoApplication.Current.Services.GetRequiredService<ICalendarContextMenuItemService>();
InitializeComponent(); InitializeComponent();
} }
@@ -141,4 +143,33 @@ public sealed partial class CalendarItemControl : UserControl
WeakReferenceMessenger.Default.Send(new CalendarItemRightTappedMessage(CalendarItem)); WeakReferenceMessenger.Default.Send(new CalendarItemRightTappedMessage(CalendarItem));
} }
private void CalendarItemCommandBarFlyout_Opening(object sender, object e)
{
if (sender is not CalendarItemCommandBarFlyout flyout)
{
return;
}
flyout.Item = CalendarItem;
if (CalendarItem?.CalendarItem == null)
{
flyout.ClearMenuItems();
return;
}
flyout.SetMenuItems(_contextMenuItemService.GetContextMenuItems(CalendarItem.CalendarItem));
}
private void CalendarItemCommandBarFlyout_Closed(object sender, object e)
{
if (sender is not CalendarItemCommandBarFlyout flyout)
{
return;
}
flyout.ClearMenuItems();
flyout.Item = null;
}
} }
@@ -32,6 +32,7 @@ public sealed partial class CalendarPage : CalendarPageAbstract, ITitleBarSearch
private CancellationTokenSource? _searchCancellationTokenSource; private CancellationTokenSource? _searchCancellationTokenSource;
private long _calendarTypeSelectorChangedToken; private long _calendarTypeSelectorChangedToken;
private bool _suppressSelectionResetOnPopupClose; private bool _suppressSelectionResetOnPopupClose;
private bool _hasAttachedNavigationLifetimeEvents;
public ObservableCollection<TitleBarSearchSuggestion> SearchSuggestions { get; } = []; public ObservableCollection<TitleBarSearchSuggestion> SearchSuggestions { get; } = [];
@@ -42,13 +43,6 @@ public sealed partial class CalendarPage : CalendarPageAbstract, ITitleBarSearch
public CalendarPage() public CalendarPage()
{ {
InitializeComponent(); InitializeComponent();
_calendarTypeSelectorChangedToken = CalendarToolbar.RegisterSelectedTypeChanged(CalendarTypeSelectorSelectedTypeChanged);
CalendarToolbar.PreviousDateRequested += CalendarToolbarPreviousDateRequested;
CalendarToolbar.NextDateRequested += CalendarToolbarNextDateRequested;
ViewModel.PropertyChanged += ViewModelPropertyChanged;
CalendarShellClient.PropertyChanged += CalendarShellClientPropertyChanged;
CalendarShellClient.StatePersistenceService.StatePropertyChanged += CalendarStatePersistenceServiceChanged;
Unloaded += CalendarPageUnloaded;
RefreshCalendarToolbar(); RefreshCalendarToolbar();
} }
@@ -61,6 +55,8 @@ public sealed partial class CalendarPage : CalendarPageAbstract, ITitleBarSearch
protected override void OnNavigatedTo(NavigationEventArgs e) protected override void OnNavigatedTo(NavigationEventArgs e)
{ {
base.OnNavigatedTo(e); base.OnNavigatedTo(e);
Bindings.Update();
AttachNavigationLifetimeEvents();
RefreshCalendarToolbar(); RefreshCalendarToolbar();
if (e.NavigationMode == NavigationMode.Back && ViewModel.RestoreVisibleState()) if (e.NavigationMode == NavigationMode.Back && ViewModel.RestoreVisibleState())
@@ -79,6 +75,12 @@ public sealed partial class CalendarPage : CalendarPageAbstract, ITitleBarSearch
WeakReferenceMessenger.Default.Send(new LoadCalendarMessage(request)); WeakReferenceMessenger.Default.Send(new LoadCalendarMessage(request));
} }
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
base.OnNavigatedFrom(e);
DetachNavigationLifetimeEvents();
}
public async Task OnTitleBarSearchTextChangedAsync() public async Task OnTitleBarSearchTextChangedAsync()
{ {
_searchCancellationTokenSource?.Cancel(); _searchCancellationTokenSource?.Cancel();
@@ -272,9 +274,31 @@ public sealed partial class CalendarPage : CalendarPageAbstract, ITitleBarSearch
} }
} }
private void CalendarPageUnloaded(object sender, RoutedEventArgs e) private void AttachNavigationLifetimeEvents()
{ {
if (_hasAttachedNavigationLifetimeEvents)
{
return;
}
_calendarTypeSelectorChangedToken = CalendarToolbar.RegisterSelectedTypeChanged(CalendarTypeSelectorSelectedTypeChanged);
CalendarToolbar.PreviousDateRequested += CalendarToolbarPreviousDateRequested;
CalendarToolbar.NextDateRequested += CalendarToolbarNextDateRequested;
ViewModel.PropertyChanged += ViewModelPropertyChanged;
CalendarShellClient.PropertyChanged += CalendarShellClientPropertyChanged;
CalendarShellClient.StatePersistenceService.StatePropertyChanged += CalendarStatePersistenceServiceChanged;
_hasAttachedNavigationLifetimeEvents = true;
}
private void DetachNavigationLifetimeEvents()
{
if (!_hasAttachedNavigationLifetimeEvents)
{
return;
}
CloseQuickEventPopup(clearSelection: true); CloseQuickEventPopup(clearSelection: true);
Bindings.StopTracking();
CalendarToolbar.UnregisterSelectedTypeChanged(_calendarTypeSelectorChangedToken); CalendarToolbar.UnregisterSelectedTypeChanged(_calendarTypeSelectorChangedToken);
CalendarToolbar.PreviousDateRequested -= CalendarToolbarPreviousDateRequested; CalendarToolbar.PreviousDateRequested -= CalendarToolbarPreviousDateRequested;
CalendarToolbar.NextDateRequested -= CalendarToolbarNextDateRequested; CalendarToolbar.NextDateRequested -= CalendarToolbarNextDateRequested;
@@ -283,7 +307,8 @@ public sealed partial class CalendarPage : CalendarPageAbstract, ITitleBarSearch
CalendarShellClient.StatePersistenceService.StatePropertyChanged -= CalendarStatePersistenceServiceChanged; CalendarShellClient.StatePersistenceService.StatePropertyChanged -= CalendarStatePersistenceServiceChanged;
_searchCancellationTokenSource?.Cancel(); _searchCancellationTokenSource?.Cancel();
_searchCancellationTokenSource?.Dispose(); _searchCancellationTokenSource?.Dispose();
Unloaded -= CalendarPageUnloaded; _searchCancellationTokenSource = null;
_hasAttachedNavigationLifetimeEvents = false;
} }
private async void SaveQuickEventClicked(object sender, RoutedEventArgs e) private async void SaveQuickEventClicked(object sender, RoutedEventArgs e)