Outlook calendar/event syncing basics without delta. Bunch of UI updates for the calendar view.

This commit is contained in:
Burak Kaan Köse
2025-01-06 02:15:21 +01:00
parent a7674d436d
commit 125c277c88
46 changed files with 1104 additions and 356 deletions

View File

@@ -1,93 +0,0 @@
<UserControl
x:Class="Wino.Calendar.Controls.AllDayItemsControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:calendarHelpers="using:Wino.Calendar.Helpers"
xmlns:controls="using:Wino.Calendar.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:data="using:Wino.Calendar.ViewModels.Data"
xmlns:domain="using:Wino.Core.Domain"
xmlns:helpers="using:Wino.Helpers"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:selectors="using:Wino.Calendar.Selectors"
x:Name="AllDayControl"
d:DesignHeight="300"
d:DesignWidth="400"
mc:Ignorable="d">
<Grid>
<ItemsControl x:Name="EventItemsControl" ItemsSource="{x:Bind CalendarDayModel.EventsCollection.AllDayEvents, Mode=OneWay}">
<ItemsControl.ItemTemplateSelector>
<selectors:CustomAreaCalendarItemSelector>
<selectors:CustomAreaCalendarItemSelector.AllDayTemplate>
<DataTemplate x:DataType="data:CalendarItemViewModel">
<controls:CalendarItemControl
CalendarItem="{x:Bind}"
DisplayingDate="{Binding CalendarDayModel, ElementName=AllDayControl}"
IsCustomEventArea="True" />
</DataTemplate>
</selectors:CustomAreaCalendarItemSelector.AllDayTemplate>
<selectors:CustomAreaCalendarItemSelector.MultiDayTemplate>
<DataTemplate x:DataType="data:CalendarItemViewModel">
<controls:CalendarItemControl
CalendarItem="{x:Bind}"
DisplayingDate="{Binding CalendarDayModel, ElementName=AllDayControl}"
IsCustomEventArea="True" />
</DataTemplate>
</selectors:CustomAreaCalendarItemSelector.MultiDayTemplate>
</selectors:CustomAreaCalendarItemSelector>
</ItemsControl.ItemTemplateSelector>
<ItemsControl.ItemContainerTransitions>
<TransitionCollection>
<AddDeleteThemeTransition />
</TransitionCollection>
</ItemsControl.ItemContainerTransitions>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical" Spacing="2" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
<!--<controls:CalendarItemControl
x:Name="SingleAllDayEventHolder"
CalendarItem="{x:Bind calendarHelpers:CalendarXamlHelpers.GetFirstAllDayEvent(EventCollection), Mode=OneWay}"
Visibility="{x:Bind helpers:XamlHelpers.CountToVisibilityConverter(EventCollection.AllDayEvents.Count), Mode=OneWay}" />
<Button
x:Name="AllDayItemsSummaryButton"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Visibility="Collapsed">
<Button.Flyout>
<Flyout Placement="Bottom">
<ScrollViewer>
<ItemsControl ItemTemplate="{x:Bind RegularEventItemTemplate}" ItemsSource="{x:Bind EventCollection.AllDayEvents}" />
</ScrollViewer>
</Flyout>
</Button.Flyout>
</Button>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="ViewStates">
<VisualState x:Name="FullView" />
<VisualState x:Name="SummaryView">
<VisualState.Setters>
<Setter Target="SingleAllDayEventHolder.Visibility" Value="Collapsed" />
<Setter Target="AllDayItemsSummaryButton.Visibility" Value="Visible" />
<Setter Target="AllDayItemsSummaryButton.Content">
<Setter.Value>
<TextBlock>
<Run Text="{x:Bind EventCollection.AllDayEvents.Count, Mode=OneWay, TargetNullValue='0'}" /> <Run Text="{x:Bind domain:Translator.CalendarAllDayEventSummary}" />
</TextBlock>
</Setter.Value>
</Setter>
</VisualState.Setters>
<VisualState.StateTriggers>
<StateTrigger IsActive="{x:Bind helpers:XamlHelpers.IsMultiple(EventCollection.AllDayEvents.Count), Mode=OneWay, FallbackValue='False'}" />
</VisualState.StateTriggers>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>-->
</Grid>
</UserControl>

View File

@@ -1,27 +0,0 @@
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Wino.Core.Domain.Models.Calendar;
namespace Wino.Calendar.Controls
{
public sealed partial class AllDayItemsControl : UserControl
{
#region Dependency Properties
public static readonly DependencyProperty CalendarDayModelProperty = DependencyProperty.Register(nameof(CalendarDayModel), typeof(CalendarDayModel), typeof(AllDayItemsControl), new PropertyMetadata(null));
public CalendarDayModel CalendarDayModel
{
get { return (CalendarDayModel)GetValue(CalendarDayModelProperty); }
set { SetValue(CalendarDayModelProperty, value); }
}
#endregion
public AllDayItemsControl()
{
InitializeComponent();
}
}
}

View File

@@ -42,6 +42,7 @@
<Rectangle
x:Name="MainBorder"
Grid.ColumnSpan="2"
Canvas.ZIndex="2"
Stroke="{ThemeResource CalendarItemBorderBrush}"
StrokeThickness="0" />
@@ -61,18 +62,19 @@
<StackPanel
x:Name="AttributeStack"
Grid.Column="1"
Margin="0,4,4,0"
Margin="0,4,0,0"
HorizontalAlignment="Right"
VerticalAlignment="Top"
Orientation="Horizontal">
Orientation="Horizontal"
Spacing="6">
<controls:WinoFontIcon
FontSize="10"
FontSize="12"
Foreground="{x:Bind helpers:XamlHelpers.GetReadableTextColor(CalendarItem.AssignedCalendar.BackgroundColorHex), Mode=OneWay}"
Icon="CalendarEventRepeat"
Visibility="{x:Bind CalendarItem.IsRecurringEvent, Mode=OneWay}" />
<controls:WinoFontIcon
FontSize="16"
FontSize="12"
Foreground="{x:Bind helpers:XamlHelpers.GetReadableTextColor(CalendarItem.AssignedCalendar.BackgroundColorHex), Mode=OneWay}"
Icon="CalendarEventMuiltiDay"
Visibility="{x:Bind CalendarItem.IsMultiDayEvent, Mode=OneWay}" />
@@ -83,7 +85,8 @@
<VisualState x:Name="NonSelected" />
<VisualState x:Name="Selected">
<VisualState.Setters>
<Setter Target="MainBorder.StrokeThickness" Value="2" />
<Setter Target="MainBorder.StrokeThickness" Value="1" />
<Setter Target="MainBorder.Margin" Value="1" />
<Setter Target="MainBorder.Stroke" Value="{ThemeResource CalendarItemSelectedBorderBrush}" />
</VisualState.Setters>
<VisualState.StateTriggers>
@@ -105,20 +108,27 @@
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="EventDurationStates">
<!-- Regular event template in the panel. -->
<VisualState x:Name="RegularEvent" />
<!-- All-Day template for top area. -->
<VisualState x:Name="AllDayEvent">
<VisualState.Setters>
<Setter Target="AttributeStack.VerticalAlignment" Value="Center" />
<Setter Target="MainGrid.MinHeight" Value="30" />
<Setter Target="MainBorder.StrokeThickness" Value="0.5" />
<Setter Target="MainBorder.StrokeThickness" Value="0" />
</VisualState.Setters>
</VisualState>
<!-- Multi-Day template for top area. -->
<VisualState x:Name="CustomAreaMultiDayEvent">
<VisualState.Setters>
<Setter Target="MainBackground.Opacity" Value="1" />
<Setter Target="MainBorder.StrokeThickness" Value="0.5" />
<Setter Target="AttributeStack.Visibility" Value="Collapsed" />
<Setter Target="MainBorder.StrokeThickness" Value="0" />
<Setter Target="AttributeStack.Visibility" Value="Visible" />
<Setter Target="AttributeStack.Margin" Value="0,0,4,0" />
<Setter Target="AttributeStack.VerticalAlignment" Value="Center" />
<Setter Target="MainGrid.MinHeight" Value="30" />
<Setter Target="EventTitleTextblock.HorizontalAlignment" Value="Stretch" />
<Setter Target="EventTitleTextblock.HorizontalTextAlignment" Value="Left" />
@@ -126,11 +136,18 @@
</VisualState.Setters>
</VisualState>
<!--
Ghost rendering for multi-day events in the panel.
All-Multi day area template is CustomAreaMultiDayEvent
-->
<VisualState x:Name="MultiDayEvent">
<VisualState.Setters>
<Setter Target="MainGrid.CornerRadius" Value="0" />
<Setter Target="MainBackground.Opacity" Value="0.2" />
<Setter Target="MainGrid.IsHitTestVisible" Value="False" />
<Setter Target="MainBorder.StrokeThickness" Value="0.5" />
<Setter Target="MainBorder.StrokeThickness" Value="0" />
<Setter Target="AttributeStack.Visibility" Value="Collapsed" />
<Setter Target="EventTitleTextblock.Visibility" Value="Collapsed" />
</VisualState.Setters>

View File

@@ -1,8 +1,10 @@
using System.Diagnostics;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.Messaging;
using Itenso.TimePeriod;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Input;
using Wino.Calendar.ViewModels.Data;
using Wino.Calendar.ViewModels.Messages;
using Wino.Core.Domain;
@@ -12,7 +14,8 @@ namespace Wino.Calendar.Controls
{
public sealed partial class CalendarItemControl : UserControl
{
public bool IsAllDayMultiDayEvent { get; set; }
// Single tap has a delay to report double taps properly.
private bool isSingleTap = false;
public static readonly DependencyProperty CalendarItemProperty = DependencyProperty.Register(nameof(CalendarItem), typeof(CalendarItemViewModel), typeof(CalendarItemControl), new PropertyMetadata(null, new PropertyChangedCallback(OnCalendarItemChanged)));
public static readonly DependencyProperty IsDraggingProperty = DependencyProperty.Register(nameof(IsDragging), typeof(bool), typeof(CalendarItemControl), new PropertyMetadata(false));
@@ -163,21 +166,30 @@ namespace Wino.Calendar.Controls
private void ControlDropped(UIElement sender, DropCompletedEventArgs args) => IsDragging = false;
private void ControlTapped(object sender, Windows.UI.Xaml.Input.TappedRoutedEventArgs e)
private async void ControlTapped(object sender, TappedRoutedEventArgs e)
{
if (CalendarItem == null) return;
WeakReferenceMessenger.Default.Send(new CalendarItemTappedMessage(CalendarItem));
isSingleTap = true;
await Task.Delay(100);
if (isSingleTap)
{
WeakReferenceMessenger.Default.Send(new CalendarItemTappedMessage(CalendarItem, DisplayingDate));
}
}
private void ControlDoubleTapped(object sender, Windows.UI.Xaml.Input.DoubleTappedRoutedEventArgs e)
private void ControlDoubleTapped(object sender, DoubleTappedRoutedEventArgs e)
{
if (CalendarItem == null) return;
isSingleTap = false;
WeakReferenceMessenger.Default.Send(new CalendarItemDoubleTappedMessage(CalendarItem));
}
private void ControlRightTapped(object sender, Windows.UI.Xaml.Input.RightTappedRoutedEventArgs e)
private void ControlRightTapped(object sender, RightTappedRoutedEventArgs e)
{
if (CalendarItem == null) return;
@@ -188,10 +200,6 @@ namespace Wino.Calendar.Controls
{
if (CalendarItem == null) return;
if (!CalendarItem.IsSelected)
{
WeakReferenceMessenger.Default.Send(new CalendarItemTappedMessage(CalendarItem));
}
}
}
}

View File

@@ -24,6 +24,8 @@ namespace Wino.Calendar.Controls
// Hide navigation buttons
PreviousButton.Opacity = NextButton.Opacity = 0;
PreviousButton.IsHitTestVisible = NextButton.IsHitTestVisible = false;
var t = FindName("ScrollingHost");
}
public void GoPreviousFlip()
@@ -37,6 +39,5 @@ namespace Wino.Calendar.Controls
var nextPeer = new ButtonAutomationPeer(NextButton);
nextPeer.Invoke();
}
}
}

View File

@@ -19,7 +19,7 @@ namespace Wino.Calendar.Controls
private TextBlock HeaderDateDayText;
private TextBlock ColumnHeaderText;
private Border IsTodayBorder;
private AllDayItemsControl AllDayItemsControl;
private ItemsControl AllDayItemsControl;
public CalendarDayModel DayModel
{
@@ -41,7 +41,7 @@ namespace Wino.Calendar.Controls
HeaderDateDayText = GetTemplateChild(PART_HeaderDateDayText) as TextBlock;
ColumnHeaderText = GetTemplateChild(PART_ColumnHeaderText) as TextBlock;
IsTodayBorder = GetTemplateChild(PART_IsTodayBorder) as Border;
AllDayItemsControl = GetTemplateChild(PART_AllDayItemsControl) as AllDayItemsControl;
AllDayItemsControl = GetTemplateChild(PART_AllDayItemsControl) as ItemsControl;
UpdateValues();
}
@@ -61,7 +61,7 @@ namespace Wino.Calendar.Controls
HeaderDateDayText.Text = DayModel.RepresentingDate.Day.ToString();
ColumnHeaderText.Text = DayModel.RepresentingDate.ToString("dddd", DayModel.CalendarRenderOptions.CalendarSettings.CultureInfo);
AllDayItemsControl.CalendarDayModel = DayModel;
AllDayItemsControl.ItemsSource = DayModel.EventsCollection.AllDayEvents;
bool isToday = DayModel.RepresentingDate.Date == DateTime.Now.Date;

View File

@@ -1,6 +1,7 @@
using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Wino.Calendar.Args;
@@ -18,6 +19,8 @@ namespace Wino.Calendar.Controls
public event EventHandler<TimelineCellSelectedArgs> TimelineCellSelected;
public event EventHandler<TimelineCellUnselectedArgs> TimelineCellUnselected;
public event EventHandler ScrollPositionChanging;
#region Dependency Properties
public static readonly DependencyProperty DayRangesProperty = DependencyProperty.Register(nameof(DayRanges), typeof(ObservableCollection<DayRangeRenderModel>), typeof(WinoCalendarControl), new PropertyMetadata(null));
@@ -25,6 +28,7 @@ namespace Wino.Calendar.Controls
public static readonly DependencyProperty SelectedFlipViewDayRangeProperty = DependencyProperty.Register(nameof(SelectedFlipViewDayRange), typeof(DayRangeRenderModel), typeof(WinoCalendarControl), new PropertyMetadata(null));
public static readonly DependencyProperty ActiveCanvasProperty = DependencyProperty.Register(nameof(ActiveCanvas), typeof(WinoDayTimelineCanvas), typeof(WinoCalendarControl), new PropertyMetadata(null, new PropertyChangedCallback(OnActiveCanvasChanged)));
public static readonly DependencyProperty IsFlipIdleProperty = DependencyProperty.Register(nameof(IsFlipIdle), typeof(bool), typeof(WinoCalendarControl), new PropertyMetadata(true, new PropertyChangedCallback(OnIdleStateChanged)));
public static readonly DependencyProperty ActiveScrollViewerProperty = DependencyProperty.Register(nameof(ActiveScrollViewer), typeof(ScrollViewer), typeof(WinoCalendarControl), new PropertyMetadata(null, new PropertyChangedCallback(OnActiveVerticalScrollViewerChanged)));
public DayRangeRenderModel SelectedFlipViewDayRange
{
@@ -32,6 +36,12 @@ namespace Wino.Calendar.Controls
set { SetValue(SelectedFlipViewDayRangeProperty, value); }
}
public ScrollViewer ActiveScrollViewer
{
get { return (ScrollViewer)GetValue(ActiveScrollViewerProperty); }
set { SetValue(ActiveScrollViewerProperty, value); }
}
public WinoDayTimelineCanvas ActiveCanvas
{
get { return (WinoDayTimelineCanvas)GetValue(ActiveCanvasProperty); }
@@ -79,6 +89,22 @@ namespace Wino.Calendar.Controls
}
}
private static void OnActiveVerticalScrollViewerChanged(DependencyObject calendar, DependencyPropertyChangedEventArgs e)
{
if (calendar is WinoCalendarControl calendarControl)
{
if (e.OldValue is ScrollViewer oldScrollViewer)
{
calendarControl.DeregisterScrollChanges(oldScrollViewer);
}
if (e.NewValue is ScrollViewer newScrollViewer)
{
calendarControl.RegisterScrollChanges(newScrollViewer);
}
}
}
private static void OnActiveCanvasChanged(DependencyObject calendar, DependencyPropertyChangedEventArgs e)
{
if (calendar is WinoCalendarControl calendarControl)
@@ -128,6 +154,23 @@ namespace Wino.Calendar.Controls
canvas.TimelineCellUnselected += ActiveTimelineCellUnselected;
}
private void RegisterScrollChanges(ScrollViewer scrollViewer)
{
if (scrollViewer == null) return;
scrollViewer.ViewChanging += ScrollViewChanging;
}
private void DeregisterScrollChanges(ScrollViewer scrollViewer)
{
if (scrollViewer == null) return;
scrollViewer.ViewChanging -= ScrollViewChanging;
}
private void ScrollViewChanging(object sender, ScrollViewerViewChangingEventArgs e)
=> ScrollPositionChanging?.Invoke(this, EventArgs.Empty);
private void CalendarSizeChanged(object sender, SizeChangedEventArgs e)
{
if (ActiveCanvas == null) return;
@@ -159,6 +202,22 @@ namespace Wino.Calendar.Controls
public void NavigateToDay(DateTime dateTime) => InternalFlipView.NavigateToDay(dateTime);
public async void NavigateToHour(TimeSpan timeSpan)
{
if (ActiveScrollViewer == null) return;
// Total height of the FlipViewItem is the same as vertical ScrollViewer to position day headers.
await Task.Yield();
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.High, () =>
{
double hourHeght = 60;
double totalHeight = ActiveScrollViewer.ScrollableHeight;
double scrollPosition = timeSpan.TotalHours * hourHeght;
ActiveScrollViewer.ChangeView(null, scrollPosition, null, disableAnimation: false);
});
}
public void ResetTimelineSelection()
{
if (ActiveCanvas == null) return;
@@ -180,6 +239,13 @@ namespace Wino.Calendar.Controls
InternalFlipView.GoPreviousFlip();
}
public void UnselectActiveTimelineCell()
{
if (ActiveCanvas == null) return;
ActiveCanvas.SelectedDateTime = null;
}
public CalendarItemControl GetCalendarItemControl(CalendarItemViewModel calendarItemViewModel)
{
if (ActiveCanvas == null) return null;

View File

@@ -14,13 +14,29 @@ namespace Wino.Calendar.Controls
{
public static readonly DependencyProperty IsIdleProperty = DependencyProperty.Register(nameof(IsIdle), typeof(bool), typeof(WinoCalendarFlipView), new PropertyMetadata(true));
public static readonly DependencyProperty ActiveCanvasProperty = DependencyProperty.Register(nameof(ActiveCanvas), typeof(WinoDayTimelineCanvas), typeof(WinoCalendarFlipView), new PropertyMetadata(null));
public static readonly DependencyProperty ActiveVerticalScrollViewerProperty = DependencyProperty.Register(nameof(ActiveVerticalScrollViewer), typeof(ScrollViewer), typeof(WinoCalendarFlipView), new PropertyMetadata(null));
/// <summary>
/// Gets or sets the active canvas that is currently displayed in the flip view.
/// Each day-range of flip view item has a canvas that displays the day timeline.
/// </summary>
public WinoDayTimelineCanvas ActiveCanvas
{
get { return (WinoDayTimelineCanvas)GetValue(ActiveCanvasProperty); }
set { SetValue(ActiveCanvasProperty, value); }
}
/// <summary>
/// Gets or sets the scroll viewer that is currently active in the flip view.
/// It's the vertical scroll that scrolls the timeline only, not the header part that belongs
/// to parent FlipView control.
/// </summary>
public ScrollViewer ActiveVerticalScrollViewer
{
get { return (ScrollViewer)GetValue(ActiveVerticalScrollViewerProperty); }
set { SetValue(ActiveVerticalScrollViewerProperty, value); }
}
public bool IsIdle
{
get { return (bool)GetValue(IsIdleProperty); }
@@ -46,6 +62,7 @@ namespace Wino.Calendar.Controls
if (d is WinoCalendarFlipView flipView)
{
flipView.UpdateActiveCanvas();
flipView.UpdateActiveScrollViewer();
}
}
@@ -62,22 +79,58 @@ namespace Wino.Calendar.Controls
IsIdle = e.Action == NotifyCollectionChangedAction.Reset || e.Action == NotifyCollectionChangedAction.Replace;
}
public async void UpdateActiveCanvas()
private async Task<FlipViewItem> GetCurrentFlipViewItem()
{
// TODO: Refactor this mechanism by listening to PrepareContainerForItemOverride and Loaded events together.
while (ContainerFromIndex(SelectedIndex) == null)
{
await Task.Delay(100);
}
return ContainerFromIndex(SelectedIndex) as FlipViewItem;
}
private void UpdateActiveScrollViewer()
{
if (SelectedIndex < 0)
ActiveVerticalScrollViewer = null;
else
{
GetCurrentFlipViewItem().ContinueWith(task =>
{
if (task.IsCompletedSuccessfully)
{
var flipViewItem = task.Result;
_ = Dispatcher.TryRunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
ActiveVerticalScrollViewer = flipViewItem.FindDescendant<ScrollViewer>();
});
}
});
}
}
public void UpdateActiveCanvas()
{
if (SelectedIndex < 0)
ActiveCanvas = null;
else
{
// TODO: Refactor this mechanism by listening to PrepareContainerForItemOverride and Loaded events together.
while (ContainerFromIndex(SelectedIndex) == null)
GetCurrentFlipViewItem().ContinueWith(task =>
{
await Task.Delay(100);
}
if (task.IsCompletedSuccessfully)
{
var flipViewItem = task.Result;
if (ContainerFromIndex(SelectedIndex) is FlipViewItem flipViewItem)
{
ActiveCanvas = flipViewItem.FindDescendant<WinoDayTimelineCanvas>();
}
_ = Dispatcher.TryRunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
ActiveCanvas = flipViewItem.FindDescendant<WinoDayTimelineCanvas>();
});
}
});
}
}
@@ -127,12 +180,6 @@ namespace Wino.Calendar.Controls
});
}
public void NavigateHour(TimeSpan hourTimeSpan)
{
// Total height of the FlipViewItem is the same as vertical ScrollViewer to position day headers.
// Find the day range that contains the hour.
}
private ObservableRangeCollection<DayRangeRenderModel> GetItemsSource()
=> ItemsSource as ObservableRangeCollection<DayRangeRenderModel>;
}

View File

@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using CommunityToolkit.WinUI;
using Itenso.TimePeriod;
@@ -79,7 +78,7 @@ namespace Wino.Calendar.Controls
var periodRelation = child.Period.GetRelation(Period);
Debug.WriteLine($"Render relation of {child.Title} ({child.Period.Start} - {child.Period.End}) is {periodRelation} with {Period.Start.Day}");
// Debug.WriteLine($"Render relation of {child.Title} ({child.Period.Start} - {child.Period.End}) is {periodRelation} with {Period.Start.Day}");
if (!child.IsMultiDayEvent)
{
@@ -169,6 +168,15 @@ namespace Wino.Calendar.Controls
if (childWidth < 0) childWidth = 1;
// Regular events must have 2px margin
if (!child.IsMultiDayEvent && !child.IsAllDayEvent)
{
childLeft += 2;
childTop += 2;
childHeight -= 2;
childWidth -= 2;
}
var arrangementRect = new Rect(childLeft + EventItemMargin.Left, childTop + EventItemMargin.Top, Math.Max(childWidth - extraRightMargin, 1), childHeight);
// Make sure measured size will fit in the arranged box.
@@ -179,6 +187,7 @@ namespace Wino.Calendar.Controls
//Debug.WriteLine($"{child.Title}, Measured: {measureSize}, Arranged: {arrangementRect}");
}
return finalSize;
}

View File

@@ -145,8 +145,8 @@ namespace Wino.Calendar.Controls
}
else
{
TimelineCellSelected?.Invoke(this, new TimelineCellSelectedArgs(clickedDateTime, touchPoint, positionerPoint, cellSize));
SelectedDateTime = clickedDateTime;
TimelineCellSelected?.Invoke(this, new TimelineCellSelectedArgs(clickedDateTime, touchPoint, positionerPoint, cellSize));
}
Debug.WriteLine($"Clicked: {clickedDateTime}");

View File

@@ -1,6 +1,11 @@
using System.Linq;
using Windows.UI.Xaml.Controls.Primitives;
using Wino.Calendar.ViewModels.Data;
using Wino.Core.Domain;
using Wino.Core.Domain.Collections;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Models.Calendar;
using Wino.Helpers;
namespace Wino.Calendar.Helpers
{
@@ -8,5 +13,37 @@ namespace Wino.Calendar.Helpers
{
public static CalendarItemViewModel GetFirstAllDayEvent(CalendarEventCollection collection)
=> (CalendarItemViewModel)collection.AllDayEvents.FirstOrDefault();
public static string GetDetailsPopupDurationString(CalendarItemViewModel calendarItemViewModel, CalendarSettings settings)
{
if (calendarItemViewModel == null || settings == null) return string.Empty;
// Single event in a day.
if (!calendarItemViewModel.IsAllDayEvent && !calendarItemViewModel.IsMultiDayEvent)
{
return $"{calendarItemViewModel.Period.Start.ToString("d", settings.CultureInfo)} {settings.GetTimeString(calendarItemViewModel.Period.Duration)}";
}
else if (calendarItemViewModel.IsMultiDayEvent)
{
return $"{calendarItemViewModel.Period.Start.ToString("d", settings.CultureInfo)} - {calendarItemViewModel.Period.End.ToString("d", settings.CultureInfo)}";
}
else
{
// All day event.
return $"{calendarItemViewModel.Period.Start.ToString("d", settings.CultureInfo)} ({Translator.CalendarItemAllDay})";
}
}
public static PopupPlacementMode GetDesiredPlacementModeForEventsDetailsPopup(
CalendarItemViewModel calendarItemViewModel,
CalendarDisplayType calendarDisplayType)
{
if (calendarItemViewModel == null) return PopupPlacementMode.Auto;
// All and/or multi day events always go to the top of the screen.
if (calendarItemViewModel.IsAllDayEvent || calendarItemViewModel.IsMultiDayEvent) return PopupPlacementMode.Bottom;
return XamlHelpers.GetPlaccementModeForCalendarType(calendarDisplayType);
}
}
}

View File

@@ -20,7 +20,7 @@
<Identity
Name="58272BurakKSE.WinoCalendar"
Publisher="CN=51FBDAF3-E212-4149-89A2-A2636B3BC911"
Version="1.0.10.0" />
Version="1.0.13.0" />
<mp:PhoneIdentity PhoneProductId="f047b7dd-96ec-4d54-a862-9321e271e449" PhonePublisherId="00000000-0000-0000-0000-000000000000"/>

View File

@@ -18,11 +18,6 @@ namespace Wino.Calendar.Services
public event EventHandler<GroupedAccountCalendarViewModel> CollectiveAccountGroupSelectionStateChanged;
public event EventHandler<AccountCalendarViewModel> AccountCalendarSelectionStateChanged;
[ObservableProperty]
public ObservableCollection<CalendarItemViewModel> _selectedItems = new ObservableCollection<CalendarItemViewModel>();
public bool HasMultipleSelectedItems => SelectedItems.Count > 1;
[ObservableProperty]
private ReadOnlyObservableCollection<GroupedAccountCalendarViewModel> groupedAccountCalendars;
@@ -52,13 +47,6 @@ namespace Wino.Calendar.Services
public AccountCalendarStateService()
{
GroupedAccountCalendars = new ReadOnlyObservableCollection<GroupedAccountCalendarViewModel>(_internalGroupedAccountCalendars);
SelectedItems.CollectionChanged += SelectedCalendarItemsUpdated;
}
private void SelectedCalendarItemsUpdated(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
OnPropertyChanged(nameof(HasMultipleSelectedItems));
}
private void SingleGroupCalendarCollectiveStateChanged(object sender, EventArgs e)

View File

@@ -9,6 +9,7 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:models="using:Wino.Core.Domain.Models.Calendar"
xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
xmlns:selectors="using:Wino.Calendar.Selectors"
xmlns:toolkitControls="using:CommunityToolkit.WinUI.Controls">
@@ -90,8 +91,6 @@
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<!-- Rendering left hour headers. -->
<ItemsControl ItemTemplate="{StaticResource DayCalendarHourHeaderTemplate}" ItemsSource="{x:Bind DayHeaders}" />
@@ -138,6 +137,7 @@
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch"
ActiveCanvas="{x:Bind ActiveCanvas, Mode=TwoWay}"
ActiveVerticalScrollViewer="{x:Bind ActiveScrollViewer, Mode=TwoWay}"
Background="Transparent"
IsIdle="{x:Bind IsFlipIdle, Mode=TwoWay}"
IsTabStop="False"
@@ -220,11 +220,42 @@
<StackPanel Grid.Column="1" HorizontalAlignment="Right" />
<!-- All-Multi Day Events -->
<controls:AllDayItemsControl
<ItemsControl
x:Name="PART_AllDayItemsControl"
Grid.Row="1"
Grid.ColumnSpan="2"
Margin="0,6" />
Margin="0,6">
<ItemsControl.ItemTemplateSelector>
<selectors:CustomAreaCalendarItemSelector>
<selectors:CustomAreaCalendarItemSelector.AllDayTemplate>
<DataTemplate x:DataType="data:CalendarItemViewModel">
<controls:CalendarItemControl
CalendarItem="{x:Bind}"
DisplayingDate="{Binding DataContext, ElementName=PART_AllDayItemsControl}"
IsCustomEventArea="True" />
</DataTemplate>
</selectors:CustomAreaCalendarItemSelector.AllDayTemplate>
<selectors:CustomAreaCalendarItemSelector.MultiDayTemplate>
<DataTemplate x:DataType="data:CalendarItemViewModel">
<controls:CalendarItemControl
CalendarItem="{x:Bind}"
DisplayingDate="{Binding DataContext, ElementName=PART_AllDayItemsControl}"
IsCustomEventArea="True" />
</DataTemplate>
</selectors:CustomAreaCalendarItemSelector.MultiDayTemplate>
</selectors:CustomAreaCalendarItemSelector>
</ItemsControl.ItemTemplateSelector>
<ItemsControl.ItemContainerTransitions>
<TransitionCollection>
<AddDeleteThemeTransition />
</TransitionCollection>
</ItemsControl.ItemContainerTransitions>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical" Spacing="2" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</Grid>
<VisualStateManager.VisualStateGroups>

View File

@@ -150,7 +150,11 @@
Grid.RowSpan="2"
Grid.ColumnSpan="2"
Background="{ThemeResource WinoApplicationBackgroundColor}"
IsHitTestVisible="False" />
IsHitTestVisible="False">
<Grid.BackgroundTransition>
<BrushTransition />
</Grid.BackgroundTransition>
</Grid>
<SplitView
x:Name="MainSplitView"
@@ -236,7 +240,7 @@
CornerRadius="3">
<TextBlock
FontSize="14"
Foreground="{x:Bind helpers:XamlHelpers.GetSolidColorBrushFromHex(TextColorHex), Mode=OneWay}"
Foreground="{x:Bind helpers:XamlHelpers.GetReadableTextColor(BackgroundColorHex), Mode=OneWay}"
Text="{x:Bind Name, Mode=OneWay}"
TextWrapping="Wrap" />
</Border>

File diff suppressed because one or more lines are too long

View File

@@ -4,7 +4,6 @@ using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Navigation;
using Wino.Calendar.Args;
using Wino.Calendar.ViewModels.Messages;
using Wino.Calendar.Views.Abstract;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Models.Calendar;
@@ -14,9 +13,9 @@ namespace Wino.Calendar.Views
{
public sealed partial class CalendarPage : CalendarPageAbstract,
IRecipient<ScrollToDateMessage>,
IRecipient<ScrollToHourMessage>,
IRecipient<GoNextDateRequestedMessage>,
IRecipient<GoPreviousDateRequestedMessage>,
IRecipient<CalendarItemRightTappedMessage>
IRecipient<GoPreviousDateRequestedMessage>
{
private const int PopupDialogOffset = 12;
@@ -24,12 +23,26 @@ namespace Wino.Calendar.Views
{
InitializeComponent();
NavigationCacheMode = NavigationCacheMode.Enabled;
ViewModel.DetailsShowCalendarItemChanged += CalendarItemDetailContextChanged;
}
private void CalendarItemDetailContextChanged(object sender, EventArgs e)
{
if (ViewModel.DisplayDetailsCalendarItemViewModel != null)
{
var control = CalendarControl.GetCalendarItemControl(ViewModel.DisplayDetailsCalendarItemViewModel);
if (control != null)
{
EventDetailsPopup.PlacementTarget = control;
}
}
}
public void Receive(ScrollToHourMessage message) => CalendarControl.NavigateToHour(message.TimeSpan);
public void Receive(ScrollToDateMessage message) => CalendarControl.NavigateToDay(message.Date);
public void Receive(GoNextDateRequestedMessage message) => CalendarControl.GoNextRange();
public void Receive(GoPreviousDateRequestedMessage message) => CalendarControl.GoPreviousRange();
protected override void OnNavigatedTo(NavigationEventArgs e)
@@ -53,6 +66,17 @@ namespace Wino.Calendar.Views
private void CellSelected(object sender, TimelineCellSelectedArgs e)
{
// Dismiss event details if exists and cancel the selection.
// This is to prevent the event details from being displayed when the user clicks somewhere else.
if (EventDetailsPopup.IsOpen)
{
CalendarControl.UnselectActiveTimelineCell();
ViewModel.DisplayDetailsCalendarItemViewModel = null;
return;
}
ViewModel.SelectedQuickEventDate = e.ClickedDate;
TeachingTipPositionerGrid.Width = e.CellSize.Width;
@@ -115,15 +139,23 @@ namespace Wino.Calendar.Views
}
public void Receive(CalendarItemRightTappedMessage message)
{
}
private void StartTimeDurationSubmitted(ComboBox sender, ComboBoxTextSubmittedEventArgs args)
=> ViewModel.SelectedStartTimeString = args.Text;
private void EndTimeDurationSubmitted(ComboBox sender, ComboBoxTextSubmittedEventArgs args)
=> ViewModel.SelectedEndTimeString = args.Text;
private void EventDetailsPopupClosed(object sender, object e)
{
ViewModel.DisplayDetailsCalendarItemViewModel = null;
}
private void CalendarScrolling(object sender, EventArgs e)
{
// In case of scrolling, we must dismiss the event details dialog.
ViewModel.DisplayDetailsCalendarItemViewModel = null;
}
}
}

File diff suppressed because one or more lines are too long

View File

@@ -137,9 +137,6 @@
</Compile>
<Compile Include="Args\TimelineCellSelectedArgs.cs" />
<Compile Include="Args\TimelineCellUnselectedArgs.cs" />
<Compile Include="Controls\AllDayItemsControl.xaml.cs">
<DependentUpon>AllDayItemsControl.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\CalendarItemControl.xaml.cs">
<DependentUpon>CalendarItemControl.xaml</DependentUpon>
</Compile>
@@ -252,10 +249,6 @@
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</ApplicationDefinition>
<Page Include="Controls\AllDayItemsControl.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Controls\CalendarItemControl.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>