Initial feature for drag / drop calendar events.

This commit is contained in:
Burak Kaan Köse
2026-04-08 23:46:02 +02:00
parent a3c35dfae5
commit 3dc4ac03ec
30 changed files with 621 additions and 4 deletions
+2
View File
@@ -14,6 +14,7 @@
<!-- Reading Page Date/Name Group Header Background -->
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#ecf0f1</SolidColorBrush>
<SolidColorBrush x:Key="CalendarDefaultHourBackgroundBrush">#B2FCFCFC</SolidColorBrush>
<SolidColorBrush x:Key="CalendarHoverHourBackgroundBrush">#260078D4</SolidColorBrush>
<SolidColorBrush x:Key="CalendarWorkHourBackgroundBrush">#D9ECEFF1</SolidColorBrush>
<SolidColorBrush x:Key="CalendarSelectedHourBackgroundBrush">#4D0078D4</SolidColorBrush>
@@ -27,6 +28,7 @@
<ResourceDictionary x:Name="Dark">
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#2C2C2C</SolidColorBrush>
<SolidColorBrush x:Key="CalendarDefaultHourBackgroundBrush">#662C2C2C</SolidColorBrush>
<SolidColorBrush x:Key="CalendarHoverHourBackgroundBrush">#33399BFF</SolidColorBrush>
<SolidColorBrush x:Key="CalendarWorkHourBackgroundBrush">#992C2C2C</SolidColorBrush>
<SolidColorBrush x:Key="CalendarSelectedHourBackgroundBrush">#66399BFF</SolidColorBrush>
+2
View File
@@ -13,6 +13,7 @@
<ResourceDictionary x:Name="Light">
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#b2dffc</SolidColorBrush>
<SolidColorBrush x:Key="CalendarDefaultHourBackgroundBrush">#33B2DFFC</SolidColorBrush>
<SolidColorBrush x:Key="CalendarHoverHourBackgroundBrush">#59B2DFFC</SolidColorBrush>
<SolidColorBrush x:Key="CalendarWorkHourBackgroundBrush">#66B2DFFC</SolidColorBrush>
<SolidColorBrush x:Key="CalendarSelectedHourBackgroundBrush">#4D0078D4</SolidColorBrush>
@@ -21,6 +22,7 @@
<ResourceDictionary x:Name="Dark">
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#b2dffc</SolidColorBrush>
<SolidColorBrush x:Key="CalendarDefaultHourBackgroundBrush">#33B2DFFC</SolidColorBrush>
<SolidColorBrush x:Key="CalendarHoverHourBackgroundBrush">#59B2DFFC</SolidColorBrush>
<SolidColorBrush x:Key="CalendarWorkHourBackgroundBrush">#66B2DFFC</SolidColorBrush>
<SolidColorBrush x:Key="CalendarSelectedHourBackgroundBrush">#66399BFF</SolidColorBrush>
+2
View File
@@ -23,6 +23,7 @@
<Color x:Key="MainCustomThemeColor">#D9FFFFFF</Color>
<SolidColorBrush x:Key="CalendarDefaultHourBackgroundBrush" Color="{StaticResource MainCustomThemeColor}" Opacity="0.55" />
<SolidColorBrush x:Key="CalendarHoverHourBackgroundBrush" Color="{StaticResource MainCustomThemeColor}" Opacity="0.7" />
<SolidColorBrush x:Key="CalendarWorkHourBackgroundBrush" Color="{StaticResource MainCustomThemeColor}" Opacity="0.85" />
<SolidColorBrush x:Key="CalendarSelectedHourBackgroundBrush">#4D0078D4</SolidColorBrush>
@@ -37,6 +38,7 @@
<Color x:Key="MainCustomThemeColor">#E61F1F1F</Color>
<SolidColorBrush x:Key="CalendarDefaultHourBackgroundBrush" Color="{StaticResource MainCustomThemeColor}" Opacity="0.55" />
<SolidColorBrush x:Key="CalendarHoverHourBackgroundBrush" Color="{StaticResource MainCustomThemeColor}" Opacity="0.7" />
<SolidColorBrush x:Key="CalendarWorkHourBackgroundBrush" Color="{StaticResource MainCustomThemeColor}" Opacity="0.85" />
<SolidColorBrush x:Key="CalendarSelectedHourBackgroundBrush">#66399BFF</SolidColorBrush>
+2
View File
@@ -14,12 +14,14 @@
<!-- Reading Page Date/Name Group Header Background -->
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#ecf0f1</SolidColorBrush>
<SolidColorBrush x:Key="CalendarDefaultHourBackgroundBrush">#F7F9FA</SolidColorBrush>
<SolidColorBrush x:Key="CalendarHoverHourBackgroundBrush">#260078D4</SolidColorBrush>
<SolidColorBrush x:Key="CalendarWorkHourBackgroundBrush">#DFE4EA</SolidColorBrush>
<SolidColorBrush x:Key="CalendarSelectedHourBackgroundBrush">#4D0078D4</SolidColorBrush>
</ResourceDictionary>
<ResourceDictionary x:Name="Dark">
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#1f1f1f</SolidColorBrush>
<SolidColorBrush x:Key="CalendarDefaultHourBackgroundBrush">#1F1F1F</SolidColorBrush>
<SolidColorBrush x:Key="CalendarHoverHourBackgroundBrush">#33399BFF</SolidColorBrush>
<SolidColorBrush x:Key="CalendarWorkHourBackgroundBrush">#262626</SolidColorBrush>
<SolidColorBrush x:Key="CalendarSelectedHourBackgroundBrush">#66399BFF</SolidColorBrush>
</ResourceDictionary>
+2
View File
@@ -13,12 +13,14 @@
<ResourceDictionary x:Name="Light">
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#A800D608</SolidColorBrush>
<SolidColorBrush x:Key="CalendarDefaultHourBackgroundBrush">#2200D608</SolidColorBrush>
<SolidColorBrush x:Key="CalendarHoverHourBackgroundBrush">#4D00D608</SolidColorBrush>
<SolidColorBrush x:Key="CalendarWorkHourBackgroundBrush">#4D00D608</SolidColorBrush>
<SolidColorBrush x:Key="CalendarSelectedHourBackgroundBrush">#4D0078D4</SolidColorBrush>
</ResourceDictionary>
<ResourceDictionary x:Name="Dark">
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#59001C01</SolidColorBrush>
<SolidColorBrush x:Key="CalendarDefaultHourBackgroundBrush">#22001C01</SolidColorBrush>
<SolidColorBrush x:Key="CalendarHoverHourBackgroundBrush">#6600D608</SolidColorBrush>
<SolidColorBrush x:Key="CalendarWorkHourBackgroundBrush">#59001C01</SolidColorBrush>
<SolidColorBrush x:Key="CalendarSelectedHourBackgroundBrush">#66399BFF</SolidColorBrush>
</ResourceDictionary>
+2
View File
@@ -13,6 +13,7 @@
<ResourceDictionary x:Name="Light">
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#dcfad8</SolidColorBrush>
<SolidColorBrush x:Key="CalendarDefaultHourBackgroundBrush">#26DCFAD8</SolidColorBrush>
<SolidColorBrush x:Key="CalendarHoverHourBackgroundBrush">#59DCFAD8</SolidColorBrush>
<SolidColorBrush x:Key="CalendarWorkHourBackgroundBrush">#59DCFAD8</SolidColorBrush>
<SolidColorBrush x:Key="CalendarSelectedHourBackgroundBrush">#4D0078D4</SolidColorBrush>
<SolidColorBrush x:Key="CalendarSeperatorBrush">#576574</SolidColorBrush>
@@ -22,6 +23,7 @@
<ResourceDictionary x:Name="Dark">
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#dcfad8</SolidColorBrush>
<SolidColorBrush x:Key="CalendarDefaultHourBackgroundBrush">#26576574</SolidColorBrush>
<SolidColorBrush x:Key="CalendarHoverHourBackgroundBrush">#59576574</SolidColorBrush>
<SolidColorBrush x:Key="CalendarWorkHourBackgroundBrush">#59576574</SolidColorBrush>
<SolidColorBrush x:Key="CalendarSelectedHourBackgroundBrush">#66399BFF</SolidColorBrush>
</ResourceDictionary>
+2
View File
@@ -14,6 +14,7 @@
<!-- Brushes -->
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#fdcb6e</SolidColorBrush>
<SolidColorBrush x:Key="CalendarDefaultHourBackgroundBrush">#33FDCB6E</SolidColorBrush>
<SolidColorBrush x:Key="CalendarHoverHourBackgroundBrush">#59FDCB6E</SolidColorBrush>
<SolidColorBrush x:Key="CalendarWorkHourBackgroundBrush">#66FDCB6E</SolidColorBrush>
<SolidColorBrush x:Key="CalendarSelectedHourBackgroundBrush">#4D0078D4</SolidColorBrush>
</ResourceDictionary>
@@ -21,6 +22,7 @@
<!-- Brushes -->
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#5413191F</SolidColorBrush>
<SolidColorBrush x:Key="CalendarDefaultHourBackgroundBrush">#2213191F</SolidColorBrush>
<SolidColorBrush x:Key="CalendarHoverHourBackgroundBrush">#4D13191F</SolidColorBrush>
<SolidColorBrush x:Key="CalendarWorkHourBackgroundBrush">#5413191F</SolidColorBrush>
<SolidColorBrush x:Key="CalendarSelectedHourBackgroundBrush">#66399BFF</SolidColorBrush>
</ResourceDictionary>
+2
View File
@@ -14,6 +14,7 @@
<!-- Brushes -->
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#b0c6dd</SolidColorBrush>
<SolidColorBrush x:Key="CalendarDefaultHourBackgroundBrush">#33B0C6DD</SolidColorBrush>
<SolidColorBrush x:Key="CalendarHoverHourBackgroundBrush">#59B0C6DD</SolidColorBrush>
<SolidColorBrush x:Key="CalendarWorkHourBackgroundBrush">#66B0C6DD</SolidColorBrush>
<SolidColorBrush x:Key="CalendarSelectedHourBackgroundBrush">#4D0078D4</SolidColorBrush>
</ResourceDictionary>
@@ -21,6 +22,7 @@
<!-- Brushes -->
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#b0c6dd</SolidColorBrush>
<SolidColorBrush x:Key="CalendarDefaultHourBackgroundBrush">#33B0C6DD</SolidColorBrush>
<SolidColorBrush x:Key="CalendarHoverHourBackgroundBrush">#59B0C6DD</SolidColorBrush>
<SolidColorBrush x:Key="CalendarWorkHourBackgroundBrush">#66B0C6DD</SolidColorBrush>
<SolidColorBrush x:Key="CalendarSelectedHourBackgroundBrush">#66399BFF</SolidColorBrush>
</ResourceDictionary>
@@ -0,0 +1,13 @@
using Wino.Calendar.ViewModels.Data;
namespace Wino.Calendar.Controls;
internal sealed class CalendarDragPackage
{
public CalendarDragPackage(CalendarItemViewModel calendarItemViewModel)
{
CalendarItemViewModel = calendarItemViewModel;
}
public CalendarItemViewModel CalendarItemViewModel { get; }
}
@@ -6,6 +6,7 @@ using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Media;
using Windows.ApplicationModel.DataTransfer;
using Wino.Calendar.ViewModels.Data;
using Wino.Calendar.ViewModels.Messages;
using Wino.Core.Domain;
@@ -56,6 +57,8 @@ public sealed partial class CalendarItemControl : UserControl
private void UpdateVisualStates()
{
CanDrag = CalendarItem?.CanDragDrop == true;
if (CalendarItem == null) return;
if (CalendarItem.IsAllDayEvent)
@@ -80,7 +83,25 @@ public sealed partial class CalendarItemControl : UserControl
}
}
private void ControlDragStarting(UIElement sender, DragStartingEventArgs args) => IsDragging = true;
private void ControlDragStarting(UIElement sender, DragStartingEventArgs args)
{
if (CalendarItem?.CanDragDrop != true)
{
args.Cancel = true;
IsDragging = false;
return;
}
args.AllowedOperations = DataPackageOperation.Move;
var dragPackage = new CalendarDragPackage(CalendarItem);
args.Data.Properties.Add(nameof(CalendarDragPackage), dragPackage);
args.Data.SetText(CalendarItem.DisplayTitle);
args.Data.Properties.Title = CalendarItem.DisplayTitle;
args.DragUI.SetContentFromDataPackage();
IsDragging = true;
}
private void ControlDropped(UIElement sender, DropCompletedEventArgs args) => IsDragging = false;
@@ -0,0 +1,28 @@
using System;
using Wino.Calendar.ViewModels.Data;
namespace Wino.Calendar.Controls;
public enum CalendarDropTargetKind
{
TimedSlot,
TimedAllDay,
MonthCell
}
public sealed class CalendarItemDroppedEventArgs : EventArgs
{
public CalendarItemDroppedEventArgs(
CalendarItemViewModel calendarItemViewModel,
DateTime targetStart,
CalendarDropTargetKind targetKind)
{
CalendarItemViewModel = calendarItemViewModel;
TargetStart = targetStart;
TargetKind = targetKind;
}
public CalendarItemViewModel CalendarItemViewModel { get; }
public DateTime TargetStart { get; }
public CalendarDropTargetKind TargetKind { get; }
}
@@ -103,8 +103,14 @@
x:Name="TimedAllDayHost"
Grid.Row="1"
Grid.Column="1"
AllowDrop="True"
DragLeave="CalendarDropTargetDragLeave"
DragOver="TimedAllDayHostDragOver"
Drop="TimedAllDayHostDrop"
Height="{x:Bind TimedAllDayHeight, Mode=OneWay}"
Background="{ThemeResource LayerFillColorDefaultBrush}"
PointerExited="CalendarDropTargetPointerExited"
PointerMoved="TimedAllDayHostPointerMoved"
Visibility="{x:Bind HasTimedAllDayItems, Mode=OneWay}">
<skia:SKXamlCanvas x:Name="TimedAllDayCanvas" PaintSurface="TimedAllDayCanvasPaintSurface" />
<Canvas x:Name="TimedAllDayItemsCanvas" />
@@ -134,7 +140,13 @@
<Grid
x:Name="TimedViewport"
Grid.Column="1"
Height="{x:Bind TimelineHeight, Mode=OneWay}">
AllowDrop="True"
DragLeave="CalendarDropTargetDragLeave"
DragOver="TimedViewportDragOver"
Drop="TimedViewportDrop"
Height="{x:Bind TimelineHeight, Mode=OneWay}"
PointerExited="CalendarDropTargetPointerExited"
PointerMoved="TimedViewportPointerMoved">
<skia:SKXamlCanvas x:Name="TimedStructureCanvas" PaintSurface="TimedStructureCanvasPaintSurface" />
<Border
x:Name="TimedInteractionLayer"
@@ -169,7 +181,13 @@
<Grid
x:Name="MonthViewport"
Grid.Row="1"
AllowDrop="True"
DragLeave="CalendarDropTargetDragLeave"
DragOver="MonthViewportDragOver"
Drop="MonthViewportDrop"
HorizontalAlignment="Stretch"
PointerExited="CalendarDropTargetPointerExited"
PointerMoved="MonthViewportPointerMoved"
VerticalAlignment="Stretch">
<skia:SKXamlCanvas x:Name="MonthStructureCanvas" PaintSurface="MonthStructureCanvasPaintSurface" />
<Border
@@ -19,6 +19,7 @@ using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Media;
using SkiaSharp;
using SkiaSharp.Views.Windows;
using Windows.ApplicationModel.DataTransfer;
using Windows.Foundation;
using Windows.UI;
using Wino.Calendar.ViewModels.Data;
@@ -57,6 +58,8 @@ public sealed partial class CalendarPeriodControl : UserControl, INotifyProperty
private bool _hasPresentedState;
private bool _refreshPending = true;
private bool _refreshScheduled;
private CalendarDropTargetInfo? _hoverTarget;
private CalendarDragPackage? _activeDragPackage;
private CalendarDisplayType _lastDisplayMode = CalendarDisplayType.Month;
private DateOnly _lastDisplayDate = DateOnly.FromDateTime(DateTime.Today);
private DayOfWeek _lastFirstDayOfWeek = DayOfWeek.Monday;
@@ -83,6 +86,9 @@ public sealed partial class CalendarPeriodControl : UserControl, INotifyProperty
[GeneratedDependencyProperty]
public partial Brush? SelectedSlotBackground { get; set; }
[GeneratedDependencyProperty]
public partial Brush? HoverSlotBackground { get; set; }
[GeneratedDependencyProperty]
public partial DateTime? SelectedDateTime { get; set; }
@@ -98,6 +104,7 @@ public sealed partial class CalendarPeriodControl : UserControl, INotifyProperty
public event PropertyChangedEventHandler? PropertyChanged;
public event EventHandler<CalendarEmptySlotTappedEventArgs>? EmptySlotTapped;
public event EventHandler<CalendarItemDroppedEventArgs>? CalendarItemDropped;
private ObservableCollection<HeaderTextLayout> TimedHeaderTextsCollection { get; } = [];
private ObservableCollection<HeaderTextLayout> MonthHeaderTextsCollection { get; } = [];
@@ -182,6 +189,7 @@ public sealed partial class CalendarPeriodControl : UserControl, INotifyProperty
partial void OnCalendarSettingsChanged(CalendarSettings? newValue) => RequestRefresh();
partial void OnTimedHeaderDateFormatChanged(string? newValue) => RequestRefresh();
partial void OnSelectedSlotBackgroundChanged(Brush? newValue) => InvalidateStructureCanvases();
partial void OnHoverSlotBackgroundChanged(Brush? newValue) => InvalidateStructureCanvases();
partial void OnSelectedDateTimeChanged(DateTime? newValue) => InvalidateStructureCanvases();
partial void OnCalendarItemsChanged(IReadOnlyList<CalendarItemViewModel>? newValue)
@@ -263,6 +271,7 @@ public sealed partial class CalendarPeriodControl : UserControl, INotifyProperty
private void InvalidateStructureCanvases()
{
TimedAllDayCanvas.Invalidate();
TimedStructureCanvas.Invalidate();
MonthStructureCanvas.Invalidate();
}
@@ -511,6 +520,7 @@ public sealed partial class CalendarPeriodControl : UserControl, INotifyProperty
private void TimedAllDayCanvasPaintSurface(object? sender, SKPaintSurfaceEventArgs e)
{
using var borderPaint = CreateLinePaint();
using var hoverFillPaint = CreateFillPaint(HoverSlotBackground ?? new SolidColorBrush(Colors.Transparent));
var canvas = e.Surface.Canvas;
canvas.Clear(SKColors.Transparent);
@@ -525,6 +535,12 @@ public sealed partial class CalendarPeriodControl : UserControl, INotifyProperty
var height = e.Info.Height;
var dayWidth = (float)(_timedLayout.DayWidth * scaleX);
var hoveredTimedAllDayRect = GetHoveredTimedAllDayRect(dayWidth, height);
if (hoveredTimedAllDayRect.HasValue && hoverFillPaint.Color.Alpha > 0)
{
canvas.DrawRect(hoveredTimedAllDayRect.Value, hoverFillPaint);
}
for (var index = 1; index < _timedLayout.VisibleDates.Count; index++)
{
var x = dayWidth * index;
@@ -541,6 +557,7 @@ public sealed partial class CalendarPeriodControl : UserControl, INotifyProperty
using var defaultFillPaint = CreateFillPaint(DefaultHourBackground ?? new SolidColorBrush(Colors.Transparent));
using var workFillPaint = CreateFillPaint(WorkHourBackground ?? new SolidColorBrush(Colors.Transparent));
using var selectedFillPaint = CreateFillPaint(SelectedSlotBackground ?? new SolidColorBrush(Colors.Transparent));
using var hoverFillPaint = CreateFillPaint(HoverSlotBackground ?? new SolidColorBrush(Colors.Transparent));
var canvas = e.Surface.Canvas;
canvas.Clear(SKColors.Transparent);
@@ -579,6 +596,12 @@ public sealed partial class CalendarPeriodControl : UserControl, INotifyProperty
}
}
var hoveredTimedSlotRect = GetHoveredTimedSlotRect(dayWidth, intervalHeight, intervalCount);
if (hoveredTimedSlotRect.HasValue && hoverFillPaint.Color.Alpha > 0)
{
canvas.DrawRect(hoveredTimedSlotRect.Value, hoverFillPaint);
}
var selectedTimedSlotRect = GetSelectedTimedSlotRect(dayWidth, intervalHeight, intervalCount);
if (selectedTimedSlotRect.HasValue && selectedFillPaint.Color.Alpha > 0)
{
@@ -609,6 +632,7 @@ public sealed partial class CalendarPeriodControl : UserControl, INotifyProperty
IsAntialias = true
};
using var selectedPaint = CreateFillPaint(SelectedSlotBackground ?? new SolidColorBrush(Colors.Transparent));
using var hoverPaint = CreateFillPaint(HoverSlotBackground ?? new SolidColorBrush(Colors.Transparent));
var canvas = e.Surface.Canvas;
canvas.Clear(SKColors.Transparent);
@@ -632,6 +656,12 @@ public sealed partial class CalendarPeriodControl : UserControl, INotifyProperty
canvas.DrawRect((float)cell.Bounds.X, (float)cell.Bounds.Y, (float)cell.Bounds.Width, (float)cell.Bounds.Height, todayPaint);
}
var hoveredMonthCellRect = GetHoveredMonthCellRect();
if (hoveredMonthCellRect.HasValue && hoverPaint.Color.Alpha > 0)
{
canvas.DrawRect(hoveredMonthCellRect.Value, hoverPaint);
}
var selectedMonthCellRect = GetSelectedMonthCellRect();
if (selectedMonthCellRect.HasValue && selectedPaint.Color.Alpha > 0)
{
@@ -814,6 +844,220 @@ public sealed partial class CalendarPeriodControl : UserControl, INotifyProperty
new Size(cell.Bounds.Width, cell.Bounds.Height)));
}
private void TimedViewportPointerMoved(object sender, PointerRoutedEventArgs e)
=> SetHoverTarget(ResolveTimedDropTarget(e.GetCurrentPoint(TimedViewport).Position, _activeDragPackage?.CalendarItemViewModel));
private void TimedAllDayHostPointerMoved(object sender, PointerRoutedEventArgs e)
=> SetHoverTarget(ResolveTimedAllDayDropTarget(e.GetCurrentPoint(TimedAllDayHost).Position, _activeDragPackage?.CalendarItemViewModel));
private void MonthViewportPointerMoved(object sender, PointerRoutedEventArgs e)
=> SetHoverTarget(ResolveMonthDropTarget(e.GetCurrentPoint(MonthViewport).Position, _activeDragPackage?.CalendarItemViewModel));
private void CalendarDropTargetPointerExited(object sender, PointerRoutedEventArgs e)
{
if (_activeDragPackage == null)
{
SetHoverTarget(null);
}
}
private void TimedViewportDragOver(object sender, DragEventArgs e)
{
if (!TryGetDragPackage(e, out var dragPackage))
{
return;
}
var hoverTarget = ResolveTimedDropTarget(e.GetPosition(TimedViewport), dragPackage.CalendarItemViewModel);
UpdateDragOverState(e, dragPackage, hoverTarget);
}
private void TimedAllDayHostDragOver(object sender, DragEventArgs e)
{
if (!TryGetDragPackage(e, out var dragPackage))
{
return;
}
var hoverTarget = ResolveTimedAllDayDropTarget(e.GetPosition(TimedAllDayHost), dragPackage.CalendarItemViewModel);
UpdateDragOverState(e, dragPackage, hoverTarget);
}
private void MonthViewportDragOver(object sender, DragEventArgs e)
{
if (!TryGetDragPackage(e, out var dragPackage))
{
return;
}
var hoverTarget = ResolveMonthDropTarget(e.GetPosition(MonthViewport), dragPackage.CalendarItemViewModel);
UpdateDragOverState(e, dragPackage, hoverTarget);
}
private void TimedViewportDrop(object sender, DragEventArgs e)
=> HandleDrop(e, ResolveTimedDropTarget(e.GetPosition(TimedViewport), _activeDragPackage?.CalendarItemViewModel));
private void TimedAllDayHostDrop(object sender, DragEventArgs e)
=> HandleDrop(e, ResolveTimedAllDayDropTarget(e.GetPosition(TimedAllDayHost), _activeDragPackage?.CalendarItemViewModel));
private void MonthViewportDrop(object sender, DragEventArgs e)
=> HandleDrop(e, ResolveMonthDropTarget(e.GetPosition(MonthViewport), _activeDragPackage?.CalendarItemViewModel));
private void CalendarDropTargetDragLeave(object sender, DragEventArgs e)
{
_activeDragPackage = null;
SetHoverTarget(null);
}
private bool TryGetDragPackage(DragEventArgs e, out CalendarDragPackage dragPackage)
{
dragPackage = null;
if (!e.DataView.Properties.ContainsKey(nameof(CalendarDragPackage)))
{
e.AcceptedOperation = DataPackageOperation.None;
_activeDragPackage = null;
SetHoverTarget(null);
return false;
}
dragPackage = e.DataView.Properties[nameof(CalendarDragPackage)] as CalendarDragPackage;
if (dragPackage?.CalendarItemViewModel?.CanDragDrop != true)
{
e.AcceptedOperation = DataPackageOperation.None;
_activeDragPackage = null;
SetHoverTarget(null);
return false;
}
return true;
}
private void UpdateDragOverState(DragEventArgs e, CalendarDragPackage dragPackage, CalendarDropTargetInfo? hoverTarget)
{
_activeDragPackage = dragPackage;
SetHoverTarget(hoverTarget);
if (hoverTarget.HasValue)
{
e.AcceptedOperation = DataPackageOperation.Move;
}
else
{
e.AcceptedOperation = DataPackageOperation.None;
}
}
private void HandleDrop(DragEventArgs e, CalendarDropTargetInfo? hoverTarget)
{
try
{
if (_activeDragPackage?.CalendarItemViewModel?.CanDragDrop != true || !hoverTarget.HasValue)
{
e.AcceptedOperation = DataPackageOperation.None;
return;
}
e.AcceptedOperation = DataPackageOperation.Move;
CalendarItemDropped?.Invoke(
this,
new CalendarItemDroppedEventArgs(
_activeDragPackage.CalendarItemViewModel,
hoverTarget.Value.TargetStart,
hoverTarget.Value.Kind));
}
finally
{
_activeDragPackage = null;
SetHoverTarget(null);
}
}
private CalendarDropTargetInfo? ResolveTimedDropTarget(Point position, CalendarItemViewModel? draggedItem)
{
if (draggedItem?.IsAllDayEvent == true ||
_timedLayout.VisibleDates.Count == 0 ||
_timedLayout.DayWidth <= 0)
{
return null;
}
var dayIndex = Math.Clamp((int)(position.X / _timedLayout.DayWidth), 0, _timedLayout.VisibleDates.Count - 1);
var intervalHeight = GetTimedSelectionIntervalHeight();
var slotIndex = Math.Clamp((int)(position.Y / intervalHeight), 0, (int)((24d * 60d / TimedSelectionIntervalMinutes) - 1));
var date = _timedLayout.VisibleDates[dayIndex];
var slotStart = TimeSpan.FromMinutes(slotIndex * TimedSelectionIntervalMinutes);
return new CalendarDropTargetInfo(
CalendarDropTargetKind.TimedSlot,
date,
dayIndex,
slotIndex,
date.ToDateTime(TimeOnly.MinValue).Add(slotStart));
}
private CalendarDropTargetInfo? ResolveTimedAllDayDropTarget(Point position, CalendarItemViewModel? draggedItem)
{
if (draggedItem is { IsAllDayEvent: false } ||
_timedLayout.VisibleDates.Count == 0 ||
_timedLayout.DayWidth <= 0 ||
TimedAllDayHeight <= 0)
{
return null;
}
var dayIndex = Math.Clamp((int)(position.X / _timedLayout.DayWidth), 0, _timedLayout.VisibleDates.Count - 1);
var date = _timedLayout.VisibleDates[dayIndex];
return new CalendarDropTargetInfo(
CalendarDropTargetKind.TimedAllDay,
date,
dayIndex,
-1,
date.ToDateTime(TimeOnly.MinValue));
}
private CalendarDropTargetInfo? ResolveMonthDropTarget(Point position, CalendarItemViewModel? draggedItem)
{
if (_monthLayout.Cells.Count == 0 || _monthLayout.CellWidth <= 0 || _monthLayout.CellHeight <= 0)
{
return null;
}
var column = Math.Clamp((int)(position.X / _monthLayout.CellWidth), 0, MonthCalendarLayoutCalculator.ColumnCount - 1);
var row = Math.Clamp((int)(position.Y / _monthLayout.CellHeight), 0, MonthCalendarLayoutCalculator.RowCount - 1);
var cellIndex = Math.Clamp((row * MonthCalendarLayoutCalculator.ColumnCount) + column, 0, _monthLayout.Cells.Count - 1);
var cell = _monthLayout.Cells[cellIndex];
var targetStart = cell.Date.ToDateTime(TimeOnly.MinValue);
if (draggedItem is { IsAllDayEvent: false })
{
targetStart = targetStart.Add(draggedItem.StartDate.TimeOfDay);
}
return new CalendarDropTargetInfo(
CalendarDropTargetKind.MonthCell,
cell.Date,
-1,
-1,
targetStart);
}
private void SetHoverTarget(CalendarDropTargetInfo? hoverTarget)
{
if (_hoverTarget == hoverTarget)
{
return;
}
_hoverTarget = hoverTarget;
InvalidateStructureCanvases();
}
private SKRect? GetSelectedTimedSlotRect(float dayWidth, float intervalHeight, int intervalCount)
{
if (SelectedDateTime is not DateTime selectedDateTime || _timedLayout.VisibleDates.Count == 0)
@@ -835,6 +1079,30 @@ public sealed partial class CalendarPeriodControl : UserControl, INotifyProperty
return new SKRect(x, y, x + dayWidth, y + intervalHeight);
}
private SKRect? GetHoveredTimedSlotRect(float dayWidth, float intervalHeight, int intervalCount)
{
if (_hoverTarget is not { Kind: CalendarDropTargetKind.TimedSlot } hoverTarget)
{
return null;
}
var slotIndex = Math.Clamp(hoverTarget.SlotIndex, 0, intervalCount - 1);
var x = hoverTarget.DayIndex * dayWidth;
var y = slotIndex * intervalHeight;
return new SKRect(x, y, x + dayWidth, y + intervalHeight);
}
private SKRect? GetHoveredTimedAllDayRect(float dayWidth, float height)
{
if (_hoverTarget is not { Kind: CalendarDropTargetKind.TimedAllDay } hoverTarget)
{
return null;
}
var x = hoverTarget.DayIndex * dayWidth;
return new SKRect(x, 0, x + dayWidth, height);
}
private SKRect? GetSelectedMonthCellRect()
{
if (SelectedDateTime is not DateTime selectedDateTime)
@@ -860,6 +1128,30 @@ public sealed partial class CalendarPeriodControl : UserControl, INotifyProperty
return null;
}
private SKRect? GetHoveredMonthCellRect()
{
if (_hoverTarget is not { Kind: CalendarDropTargetKind.MonthCell } hoverTarget)
{
return null;
}
foreach (var cell in _monthLayout.Cells)
{
if (cell.Date != hoverTarget.Date)
{
continue;
}
return new SKRect(
(float)cell.Bounds.X,
(float)cell.Bounds.Y,
(float)(cell.Bounds.X + cell.Bounds.Width),
(float)(cell.Bounds.Y + cell.Bounds.Height));
}
return null;
}
private int FindVisibleDateIndex(DateOnly date)
{
for (var index = 0; index < _timedLayout.VisibleDates.Count; index++)
@@ -1229,6 +1521,13 @@ public sealed partial class CalendarPeriodControl : UserControl, INotifyProperty
private readonly record struct CalendarTransitionInfo(CalendarTransitionKind Kind, int Direction);
private readonly record struct CalendarDropTargetInfo(
CalendarDropTargetKind Kind,
DateOnly Date,
int DayIndex,
int SlotIndex,
DateTime TargetStart);
private enum CalendarTransitionKind
{
None,
@@ -41,8 +41,10 @@
x:Name="CalendarSurface"
CalendarItems="{x:Bind ViewModel.CalendarItems, Mode=OneWay}"
CalendarSettings="{x:Bind ViewModel.CurrentSettings, Mode=OneWay}"
CalendarItemDropped="CalendarSurfaceCalendarItemDropped"
DefaultHourBackground="{ThemeResource CalendarDefaultHourBackgroundBrush}"
EmptySlotTapped="CalendarSurfaceEmptySlotTapped"
HoverSlotBackground="{ThemeResource CalendarHoverHourBackgroundBrush}"
IsEnabled="{x:Bind ViewModel.IsCalendarEnabled, Mode=OneWay}"
SelectedDateTime="{x:Bind ViewModel.SelectedQuickEventDate, Mode=OneWay}"
SelectedSlotBackground="{ThemeResource CalendarSelectedHourBackgroundBrush}"
@@ -173,6 +173,9 @@ public sealed partial class CalendarPage : CalendarPageAbstract, ITitleBarSearch
_suppressSelectionResetOnPopupClose = false;
}
private async void CalendarSurfaceCalendarItemDropped(object sender, CalendarItemDroppedEventArgs e)
=> await ViewModel.MoveCalendarItemAsync(e.CalendarItemViewModel, e.TargetStart);
private void QuickEventAccountSelectorSelectionChanged(object sender, SelectionChangedEventArgs e)
=> QuickEventAccountSelectorFlyout.Hide();