From 1731e5088ea0a6d203e49845d2c3d38380f29b9f Mon Sep 17 00:00:00 2001 From: Sebastian Hedtrich Date: Wed, 19 Aug 2026 11:23:54 +0200 Subject: [PATCH] Anpassung am Sitzplan --- .../Views/Groups/SeatingPlanTabView.axaml | 53 ++++++++++------- .../Views/Groups/SeatingPlanTabView.axaml.cs | 59 ++++++++++++++++++- 2 files changed, 91 insertions(+), 21 deletions(-) diff --git a/LehrerApp.Desktop/Views/Groups/SeatingPlanTabView.axaml b/LehrerApp.Desktop/Views/Groups/SeatingPlanTabView.axaml index 386549a..d88aba3 100644 --- a/LehrerApp.Desktop/Views/Groups/SeatingPlanTabView.axaml +++ b/LehrerApp.Desktop/Views/Groups/SeatingPlanTabView.axaml @@ -60,8 +60,8 @@ HorizontalAlignment="Center"/> - - + + @@ -73,8 +73,13 @@ - + @@ -113,37 +118,45 @@ + + - - - - + + + + + + + - - - - + - - - + + + diff --git a/LehrerApp.Desktop/Views/Groups/SeatingPlanTabView.axaml.cs b/LehrerApp.Desktop/Views/Groups/SeatingPlanTabView.axaml.cs index 01960b5..fa687a5 100644 --- a/LehrerApp.Desktop/Views/Groups/SeatingPlanTabView.axaml.cs +++ b/LehrerApp.Desktop/Views/Groups/SeatingPlanTabView.axaml.cs @@ -10,6 +10,9 @@ namespace LehrerApp.Desktop.Views.Groups; public partial class SeatingPlanTabView : UserControl { + private const double AutoScrollEdgeSize = 72; + private const double AutoScrollStep = 14; + private object? _dragCandidate; private PointerPressedEventArgs? _dragTrigger; private Avalonia.Point _pressPosition; @@ -18,8 +21,15 @@ public partial class SeatingPlanTabView : UserControl private SeatCellViewModel? _pendingDropTarget; private bool _pendingClearSeat; private DateTime _ignoreTapUntil; + private readonly DispatcherTimer _autoScrollTimer; + private Avalonia.Vector _autoScrollDirection; - public SeatingPlanTabView() => InitializeComponent(); + public SeatingPlanTabView() + { + InitializeComponent(); + _autoScrollTimer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(30) }; + _autoScrollTimer.Tick += OnAutoScrollTick; + } protected override void OnDataContextChanged(EventArgs e) { @@ -69,6 +79,7 @@ public partial class SeatingPlanTabView : UserControl _draggedStudent = null; _pendingDropTarget = null; _pendingClearSeat = false; + StopAutoScroll(); _ignoreTapUntil = DateTime.UtcNow.AddMilliseconds(250); // Never mutate an ItemsControl while Avalonia is still processing its native @@ -107,6 +118,7 @@ public partial class SeatingPlanTabView : UserControl private void OnSeatDrop(object? sender, DragEventArgs e) { + StopAutoScroll(); if (sender is not Border { DataContext: SeatCellViewModel target }) return; target.IsDropTarget = false; if (!CanDropOn(target)) @@ -128,6 +140,7 @@ public partial class SeatingPlanTabView : UserControl private void OnUnassignedDrop(object? sender, DragEventArgs e) { + StopAutoScroll(); if (_draggedSeat is not null) { _pendingDropTarget = null; @@ -136,6 +149,50 @@ public partial class SeatingPlanTabView : UserControl } } + private void OnRoomDragOver(object? sender, DragEventArgs e) + { + if (_draggedSeat is null && _draggedStudent is null) + { + StopAutoScroll(); + return; + } + + var position = e.GetPosition(RoomScrollViewer); + _autoScrollDirection = new Avalonia.Vector( + GetAutoScrollDirection(position.X, RoomScrollViewer.Bounds.Width), + GetAutoScrollDirection(position.Y, RoomScrollViewer.Bounds.Height)); + + if (_autoScrollDirection == default) + StopAutoScroll(); + else if (!_autoScrollTimer.IsEnabled) + _autoScrollTimer.Start(); + } + + private void OnRoomDragLeave(object? sender, DragEventArgs e) => StopAutoScroll(); + + private void OnAutoScrollTick(object? sender, EventArgs e) + { + var maxX = Math.Max(0, RoomScrollViewer.Extent.Width - RoomScrollViewer.Viewport.Width); + var maxY = Math.Max(0, RoomScrollViewer.Extent.Height - RoomScrollViewer.Viewport.Height); + var offset = RoomScrollViewer.Offset; + RoomScrollViewer.Offset = new Avalonia.Vector( + Math.Clamp(offset.X + _autoScrollDirection.X * AutoScrollStep, 0, maxX), + Math.Clamp(offset.Y + _autoScrollDirection.Y * AutoScrollStep, 0, maxY)); + } + + private static double GetAutoScrollDirection(double position, double viewportSize) + { + if (position < AutoScrollEdgeSize) return -1; + if (position > viewportSize - AutoScrollEdgeSize) return 1; + return 0; + } + + private void StopAutoScroll() + { + _autoScrollDirection = default; + _autoScrollTimer.Stop(); + } + private async void OnSeatTapped(object? sender, TappedEventArgs e) { if (DateTime.UtcNow < _ignoreTapUntil || sender is not Border { DataContext: SeatCellViewModel seat }