Calendar rendering implementation.

This commit is contained in:
Burak Kaan Köse
2026-03-23 14:56:36 +01:00
parent 8586d0ef54
commit 1adba271e2
32 changed files with 11146 additions and 846 deletions
@@ -4,16 +4,224 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:abstract="using:Wino.Calendar.Views.Abstract"
xmlns:calendarControls="using:Wino.Calendar.Controls"
xmlns:collections="using:CommunityToolkit.Mvvm.Collections"
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"
mc:Ignorable="d">
<Page.Resources>
<CollectionViewSource
x:Name="GroupedCalendarEnumerableViewSource"
IsSourceGrouped="True"
Source="{x:Bind ViewModel.AccountCalendarStateService.GroupedCalendars, Mode=OneWay}" />
</Page.Resources>
<Grid>
<calendarControls:CalendarPeriodControl
x:Name="CalendarSurface"
IsEnabled="{x:Bind ViewModel.IsCalendarEnabled, Mode=OneWay}"
CalendarItems="{x:Bind ViewModel.CalendarItems, Mode=OneWay}"
CalendarSettings="{x:Bind ViewModel.CurrentSettings, Mode=OneWay}"
VisibleRange="{x:Bind ViewModel.CurrentVisibleRange, Mode=OneWay}" />
<Border
Margin="4,0,7,7"
BorderBrush="{StaticResource CardStrokeColorDefaultBrush}"
BorderThickness="1"
CornerRadius="7">
<calendarControls:CalendarPeriodControl
x:Name="CalendarSurface"
CalendarItems="{x:Bind ViewModel.CalendarItems, Mode=OneWay}"
CalendarSettings="{x:Bind ViewModel.CurrentSettings, Mode=OneWay}"
EmptySlotTapped="CalendarSurfaceEmptySlotTapped"
IsEnabled="{x:Bind ViewModel.IsCalendarEnabled, Mode=OneWay}"
VisibleRange="{x:Bind ViewModel.CurrentVisibleRange, Mode=OneWay}" />
</Border>
<Canvas x:Name="CalendarOverlayCanvas" IsHitTestVisible="False">
<Grid
x:Name="TeachingTipPositionerGrid"
Background="Transparent"
IsHitTestVisible="False"
Visibility="Visible" />
<Popup
x:Name="QuickEventPopupDialog"
ActualPlacementChanged="PopupPlacementChanged"
Closed="QuickEventPopupClosed"
DesiredPlacement="{x:Bind helpers:XamlHelpers.GetPlaccementModeForCalendarType(ViewModel.StatePersistanceService.CalendarDisplayType), Mode=OneWay}"
IsLightDismissEnabled="True"
IsOpen="{x:Bind ViewModel.IsQuickEventDialogOpen, Mode=TwoWay}"
PlacementTarget="{x:Bind TeachingTipPositionerGrid}">
<Popup.ChildTransitions>
<TransitionCollection>
<PopupThemeTransition />
</TransitionCollection>
</Popup.ChildTransitions>
<Grid
MinWidth="440"
MaxWidth="520"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
BorderBrush="{x:Bind helpers:XamlHelpers.GetSolidColorBrushFromHex(ViewModel.SelectedQuickEventAccountCalendar.BackgroundColorHex), Mode=OneWay, TargetNullValue='LightGray'}"
BorderThickness="1"
CornerRadius="8"
RowSpacing="12">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Button
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch"
Background="{x:Bind helpers:XamlHelpers.GetSolidColorBrushFromHex(ViewModel.SelectedQuickEventAccountCalendar.BackgroundColorHex), Mode=OneWay, TargetNullValue='LightGray'}"
CornerRadius="8,8,0,0">
<Button.Content>
<Grid
Height="36"
Padding="12,0"
ColumnSpacing="10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock
VerticalAlignment="Center"
Foreground="{x:Bind helpers:XamlHelpers.GetReadableTextColor(ViewModel.SelectedQuickEventAccountCalendar.BackgroundColorHex), Mode=OneWay}"
Text="{x:Bind ViewModel.SelectedQuickEventAccountCalendar.Account.Name, Mode=OneWay}" />
<TextBlock
Grid.Column="1"
VerticalAlignment="Center"
FontWeight="SemiBold"
Foreground="{x:Bind helpers:XamlHelpers.GetReadableTextColor(ViewModel.SelectedQuickEventAccountCalendar.BackgroundColorHex), Mode=OneWay}"
Text="{x:Bind ViewModel.SelectedQuickEventAccountCalendarName, Mode=OneWay}"
TextTrimming="CharacterEllipsis" />
<FontIcon
Grid.Column="2"
VerticalAlignment="Center"
Foreground="{x:Bind helpers:XamlHelpers.GetReadableTextColor(ViewModel.SelectedQuickEventAccountCalendar.BackgroundColorHex), Mode=OneWay}"
Glyph="&#xE70D;" />
</Grid>
</Button.Content>
<Button.Flyout>
<Flyout x:Name="QuickEventAccountSelectorFlyout" Placement="Bottom">
<ListView
MaxHeight="300"
ItemsSource="{x:Bind GroupedCalendarEnumerableViewSource.View, Mode=OneWay}"
SelectedItem="{x:Bind ViewModel.SelectedQuickEventAccountCalendar, Mode=TwoWay}"
SelectionChanged="QuickEventAccountSelectorSelectionChanged">
<ListView.ItemTemplate>
<DataTemplate x:DataType="data:AccountCalendarViewModel">
<StackPanel
Margin="0,0,16,0"
Orientation="Horizontal"
Spacing="8">
<Ellipse
Width="14"
Height="14"
Fill="{x:Bind helpers:XamlHelpers.GetSolidColorBrushFromHex(BackgroundColorHex), Mode=OneWay}" />
<TextBlock VerticalAlignment="Center" Text="{x:Bind Name, Mode=OneWay}" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate x:DataType="collections:IReadOnlyObservableGroup">
<TextBlock FontWeight="SemiBold" Text="{x:Bind Key.ToString()}" />
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</ListView.GroupStyle>
</ListView>
</Flyout>
</Button.Flyout>
</Button>
<Grid
Grid.Row="1"
Padding="12"
RowSpacing="16">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid ColumnSpacing="12">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBox
FontSize="16"
PlaceholderText="{x:Bind domain:Translator.QuickEventDialog_EventName}"
Text="{x:Bind ViewModel.EventName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<CheckBox
Grid.Column="1"
VerticalAlignment="Center"
Content="{x:Bind domain:Translator.QuickEventDialog_IsAllDay}"
IsChecked="{x:Bind ViewModel.IsAllDay, Mode=TwoWay}" />
</Grid>
<Grid Grid.Row="1" ColumnSpacing="12">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ComboBox
IsEditable="True"
IsEnabled="{x:Bind helpers:XamlHelpers.ReverseBoolConverter(ViewModel.IsAllDay), Mode=OneWay}"
ItemsSource="{x:Bind ViewModel.HourSelectionStrings, Mode=OneWay}"
SelectedItem="{x:Bind ViewModel.SelectedStartTimeString, Mode=TwoWay}"
TextSubmitted="StartTimeDurationSubmitted" />
<TextBlock
Grid.Column="1"
VerticalAlignment="Center"
Text="-" />
<ComboBox
Grid.Column="2"
IsEditable="True"
IsEnabled="{x:Bind helpers:XamlHelpers.ReverseBoolConverter(ViewModel.IsAllDay), Mode=OneWay}"
ItemsSource="{x:Bind ViewModel.HourSelectionStrings, Mode=OneWay}"
SelectedItem="{x:Bind ViewModel.SelectedEndTimeString, Mode=TwoWay}"
TextSubmitted="EndTimeDurationSubmitted" />
</Grid>
<TextBox
Grid.Row="2"
PlaceholderText="{x:Bind domain:Translator.QuickEventDialog_Location}"
Text="{x:Bind ViewModel.Location, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<Grid Grid.Row="3" ColumnSpacing="12">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<!-- Command="{x:Bind ViewModel.MoreDetailsCommand}" -->
<Button HorizontalAlignment="Stretch" Content="{x:Bind domain:Translator.QuickEventDialogMoreDetailsButtonText}" />
<Button
Grid.Column="1"
HorizontalAlignment="Stretch"
Command="{x:Bind ViewModel.SaveQuickEventCommand}"
Content="{x:Bind domain:Translator.Buttons_Save}"
Style="{ThemeResource AccentButtonStyle}" />
</Grid>
</Grid>
</Grid>
</Popup>
</Canvas>
</Grid>
</abstract:CalendarPageAbstract>
@@ -1,6 +1,11 @@
using System;
using CommunityToolkit.Mvvm.Messaging;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Controls.Primitives;
using Microsoft.UI.Xaml.Navigation;
using Windows.Foundation;
using Wino.Calendar.Controls;
using Wino.Calendar.Views.Abstract;
using Wino.Core.Domain.Models.Calendar;
using Wino.Messaging.Client.Calendar;
@@ -9,6 +14,8 @@ namespace Wino.Calendar.Views;
public sealed partial class CalendarPage : CalendarPageAbstract
{
private const int PopupDialogOffset = 12;
public CalendarPage()
{
InitializeComponent();
@@ -38,4 +45,70 @@ public sealed partial class CalendarPage : CalendarPageAbstract
var request = new CalendarDisplayRequest(ViewModel.StatePersistanceService.CalendarDisplayType, anchorDate);
WeakReferenceMessenger.Default.Send(new LoadCalendarMessage(request));
}
private void CalendarSurfaceEmptySlotTapped(object sender, CalendarEmptySlotTappedEventArgs e)
{
if (ViewModel.DisplayDetailsCalendarItemViewModel != null)
{
ViewModel.DisplayDetailsCalendarItemViewModel = null;
return;
}
ViewModel.SelectedQuickEventDate = e.ClickedDate;
var transform = CalendarSurface.TransformToVisual(CalendarOverlayCanvas);
var canvasPoint = transform.TransformPoint(e.PositionerPoint);
TeachingTipPositionerGrid.Width = e.CellSize.Width;
TeachingTipPositionerGrid.Height = e.CellSize.Height;
Canvas.SetLeft(TeachingTipPositionerGrid, canvasPoint.X);
Canvas.SetTop(TeachingTipPositionerGrid, canvasPoint.Y);
var startTime = e.ClickedDate.TimeOfDay;
var endTime = startTime.Add(TimeSpan.FromMinutes(30));
ViewModel.SelectQuickEventTimeRange(startTime, endTime);
QuickEventPopupDialog.IsOpen = true;
}
private void QuickEventAccountSelectorSelectionChanged(object sender, SelectionChangedEventArgs e)
=> QuickEventAccountSelectorFlyout.Hide();
private void QuickEventPopupClosed(object sender, object e)
{
}
private void PopupPlacementChanged(object sender, object e)
{
if (sender is not Popup popup)
{
return;
}
popup.HorizontalOffset = 0;
popup.VerticalOffset = 0;
switch (popup.ActualPlacement)
{
case PopupPlacementMode.Top:
popup.VerticalOffset = PopupDialogOffset * -1;
break;
case PopupPlacementMode.Bottom:
popup.VerticalOffset = PopupDialogOffset;
break;
case PopupPlacementMode.Left:
popup.HorizontalOffset = PopupDialogOffset * -1;
break;
case PopupPlacementMode.Right:
popup.HorizontalOffset = PopupDialogOffset;
break;
}
}
private void StartTimeDurationSubmitted(ComboBox sender, ComboBoxTextSubmittedEventArgs args)
=> ViewModel.SelectedStartTimeString = args.Text;
private void EndTimeDurationSubmitted(ComboBox sender, ComboBoxTextSubmittedEventArgs args)
=> ViewModel.SelectedEndTimeString = args.Text;
}
@@ -175,10 +175,36 @@
<controls:SettingsCard.HeaderIcon>
<PathIcon Data="F1 M 4.921875 18.75 C 4.433594 18.75 3.966471 18.650717 3.520508 18.452148 C 3.074544 18.25358 2.683919 17.986654 2.348633 17.651367 C 2.013346 17.31608 1.746419 16.925455 1.547852 16.479492 C 1.349284 16.033529 1.25 15.566406 1.25 15.078125 L 1.25 4.921875 C 1.25 4.433594 1.349284 3.966473 1.547852 3.520508 C 1.746419 3.074545 2.013346 2.68392 2.348633 2.348633 C 2.683919 2.013348 3.074544 1.74642 3.520508 1.547852 C 3.966471 1.349285 4.433594 1.25 4.921875 1.25 L 15.078125 1.25 C 15.566406 1.25 16.033527 1.349285 16.479492 1.547852 C 16.925455 1.74642 17.31608 2.013348 17.651367 2.348633 C 17.986652 2.68392 18.25358 3.074545 18.452148 3.520508 C 18.650715 3.966473 18.75 4.433594 18.75 4.921875 L 18.75 15.078125 C 18.75 15.566406 18.650715 16.033529 18.452148 16.479492 C 18.25358 16.925455 17.986652 17.31608 17.651367 17.651367 C 17.31608 17.986654 16.925455 18.25358 16.479492 18.452148 C 16.033527 18.650717 15.566406 18.75 15.078125 18.75 Z M 2.5 10 L 17.5 10 L 17.5 4.951172 C 17.5 4.625651 17.433268 4.314779 17.299805 4.018555 C 17.16634 3.722332 16.987305 3.461914 16.762695 3.237305 C 16.538086 3.012695 16.277668 2.83366 15.981445 2.700195 C 15.685221 2.566732 15.374349 2.5 15.048828 2.5 L 4.951172 2.5 C 4.625651 2.5 4.314778 2.566732 4.018555 2.700195 C 3.722331 2.83366 3.461914 3.012695 3.237305 3.237305 C 3.012695 3.461914 2.833659 3.722332 2.700195 4.018555 C 2.566732 4.314779 2.5 4.625651 2.5 4.951172 Z " />
</controls:SettingsCard.HeaderIcon>
<ToggleSwitch
IsOn="{x:Bind ViewModel.Is24HourHeaders, Mode=TwoWay}"
OffContent="12h"
OnContent="24h" />
<StackPanel Spacing="8">
<ToggleSwitch
IsOn="{x:Bind ViewModel.Is24HourHeaders, Mode=TwoWay}"
OffContent="12h"
OnContent="24h" />
<TextBlock
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Text="{x:Bind ViewModel.TimedHourLabelPreview, Mode=OneWay}"
TextWrapping="WrapWholeWords" />
</StackPanel>
</controls:SettingsCard>
<controls:SettingsCard
Description="{x:Bind domain:Translator.CalendarSettings_TimedDayHeaderFormat_Description}"
Header="{x:Bind domain:Translator.CalendarSettings_TimedDayHeaderFormat_Header}">
<controls:SettingsCard.HeaderIcon>
<FontIcon Glyph="&#xE823;" />
</controls:SettingsCard.HeaderIcon>
<StackPanel Spacing="8">
<TextBox
PlaceholderText="ddd dd"
Text="{x:Bind ViewModel.TimedDayHeaderDateFormat, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<ComboBox
ItemsSource="{x:Bind ViewModel.TimedDayHeaderFormatPresets, Mode=OneWay}"
SelectedIndex="{x:Bind ViewModel.SelectedTimedDayHeaderFormatPresetIndex, Mode=TwoWay}" />
<TextBlock
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Text="{x:Bind ViewModel.TimedDayHeaderFormatPreview, Mode=OneWay}"
TextWrapping="WrapWholeWords" />
</StackPanel>
</controls:SettingsCard>
</controls:SettingsExpander.Items>
</controls:SettingsExpander>
@@ -289,7 +315,7 @@
<StateTrigger IsActive="{x:Bind ViewModel.Is24HourHeaders, Mode=OneWay}" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="WorkStartStartPicker.ClockIdentifier" Value="24HourClock" />
<Setter Target="WorkHourStartPicker.ClockIdentifier" Value="24HourClock" />
<Setter Target="WorkEndStartPicker.ClockIdentifier" Value="24HourClock" />
</VisualState.Setters>
</VisualState>