Anpassung am Sitzplan
This commit is contained in:
@@ -60,8 +60,8 @@
|
|||||||
HorizontalAlignment="Center"/>
|
HorizontalAlignment="Center"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
<Grid RowDefinitions="Auto,*" IsVisible="{Binding HasSelectedPlan}" Margin="24">
|
<Grid RowDefinitions="Auto,*" ColumnDefinitions="*,220" IsVisible="{Binding HasSelectedPlan}" Margin="24">
|
||||||
<Grid Grid.Row="0" ColumnDefinitions="*,Auto" Margin="0,0,0,18">
|
<Grid Grid.Row="0" Grid.ColumnSpan="2" ColumnDefinitions="*,Auto" Margin="0,0,0,18">
|
||||||
<StackPanel Spacing="3">
|
<StackPanel Spacing="3">
|
||||||
<TextBlock Text="{Binding PlanTitle}" FontSize="22" FontWeight="SemiBold"/>
|
<TextBlock Text="{Binding PlanTitle}" FontSize="22" FontWeight="SemiBold"/>
|
||||||
<TextBlock Text="{Binding PlanSubtitle}" Opacity="0.65"/>
|
<TextBlock Text="{Binding PlanSubtitle}" Opacity="0.65"/>
|
||||||
@@ -73,8 +73,13 @@
|
|||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<ScrollViewer Grid.Row="1" HorizontalScrollBarVisibility="Auto"
|
<ScrollViewer x:Name="RoomScrollViewer" Grid.Row="1" Grid.Column="0"
|
||||||
VerticalScrollBarVisibility="Auto">
|
Margin="0,0,16,0"
|
||||||
|
HorizontalScrollBarVisibility="Auto"
|
||||||
|
VerticalScrollBarVisibility="Auto"
|
||||||
|
DragDrop.AllowDrop="True"
|
||||||
|
DragDrop.DragOver="OnRoomDragOver"
|
||||||
|
DragDrop.DragLeave="OnRoomDragLeave">
|
||||||
<StackPanel Spacing="14" HorizontalAlignment="Center">
|
<StackPanel Spacing="14" HorizontalAlignment="Center">
|
||||||
<Border Background="{DynamicResource SystemControlBackgroundChromeMediumLowBrush}"
|
<Border Background="{DynamicResource SystemControlBackgroundChromeMediumLowBrush}"
|
||||||
CornerRadius="6" Padding="36,8" HorizontalAlignment="Center">
|
CornerRadius="6" Padding="36,8" HorizontalAlignment="Center">
|
||||||
@@ -113,38 +118,46 @@
|
|||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</ItemsControl.ItemTemplate>
|
</ItemsControl.ItemTemplate>
|
||||||
</ItemsControl>
|
</ItemsControl>
|
||||||
|
</StackPanel>
|
||||||
|
</ScrollViewer>
|
||||||
|
|
||||||
<Border DragDrop.AllowDrop="True" DragDrop.DragOver="OnUnassignedDragOver"
|
<!-- Die Schülerliste hat einen eigenen Scrollbereich und bleibt dadurch auch bei
|
||||||
|
großen Räumen neben den aktuell sichtbaren Sitzplätzen erreichbar. -->
|
||||||
|
<Border Grid.Row="1" Grid.Column="1"
|
||||||
|
DragDrop.AllowDrop="True" DragDrop.DragOver="OnUnassignedDragOver"
|
||||||
DragDrop.Drop="OnUnassignedDrop"
|
DragDrop.Drop="OnUnassignedDrop"
|
||||||
BorderBrush="{DynamicResource SystemControlForegroundBaseLowBrush}"
|
BorderBrush="{DynamicResource SystemControlForegroundBaseLowBrush}"
|
||||||
BorderThickness="1" CornerRadius="7" Padding="12" Margin="5,8">
|
BorderThickness="1" CornerRadius="7" Padding="12">
|
||||||
<StackPanel Spacing="8">
|
<Grid RowDefinitions="Auto,Auto,*">
|
||||||
<TextBlock Text="NICHT ZUGEORDNET" FontSize="10" FontWeight="Bold" Opacity="0.5"/>
|
<TextBlock Grid.Row="0" Text="NICHT ZUGEORDNET" FontSize="10"
|
||||||
<TextBlock Text="Schüler hierher ziehen, um einen Platz zu leeren."
|
FontWeight="Bold" Opacity="0.5"/>
|
||||||
FontSize="11" Opacity="0.5"/>
|
<TextBlock Grid.Row="1" Margin="0,5,0,10"
|
||||||
|
Text="Schüler auf einen Platz ziehen. Belegte Plätze hierher ziehen, um sie zu leeren."
|
||||||
|
TextWrapping="Wrap" FontSize="11" Opacity="0.5"/>
|
||||||
|
<ScrollViewer Grid.Row="2" VerticalScrollBarVisibility="Auto"
|
||||||
|
HorizontalScrollBarVisibility="Disabled">
|
||||||
|
<StackPanel Spacing="6">
|
||||||
<ItemsControl ItemsSource="{Binding UnassignedStudents}">
|
<ItemsControl ItemsSource="{Binding UnassignedStudents}">
|
||||||
<ItemsControl.ItemsPanel>
|
|
||||||
<ItemsPanelTemplate><WrapPanel/></ItemsPanelTemplate>
|
|
||||||
</ItemsControl.ItemsPanel>
|
|
||||||
<ItemsControl.ItemTemplate>
|
<ItemsControl.ItemTemplate>
|
||||||
<DataTemplate x:DataType="vm:StudentSeatOption">
|
<DataTemplate x:DataType="vm:StudentSeatOption">
|
||||||
<Border Background="{DynamicResource SystemAccentColorLight2}" CornerRadius="5"
|
<Border Background="{DynamicResource SystemAccentColorLight2}" CornerRadius="5"
|
||||||
Padding="9,5" Margin="0,0,6,6"
|
Padding="9,7"
|
||||||
PointerPressed="OnDragSourcePressed"
|
PointerPressed="OnDragSourcePressed"
|
||||||
PointerMoved="OnDragSourceMoved"
|
PointerMoved="OnDragSourceMoved"
|
||||||
PointerReleased="OnDragSourceReleased">
|
PointerReleased="OnDragSourceReleased">
|
||||||
<TextBlock Text="{Binding DisplayName}" FontSize="12"/>
|
<TextBlock Text="{Binding DisplayName}" FontSize="12" TextTrimming="CharacterEllipsis"/>
|
||||||
</Border>
|
</Border>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</ItemsControl.ItemTemplate>
|
</ItemsControl.ItemTemplate>
|
||||||
</ItemsControl>
|
</ItemsControl>
|
||||||
<TextBlock Text="Alle Schüler sind zugeordnet." Classes="emptyhint"
|
<TextBlock Text="Alle Schüler sind zugeordnet." Classes="emptyhint"
|
||||||
|
TextWrapping="Wrap"
|
||||||
IsVisible="{Binding !UnassignedStudents.Count}"/>
|
IsVisible="{Binding !UnassignedStudents.Count}"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Border>
|
|
||||||
</StackPanel>
|
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
|||||||
@@ -10,6 +10,9 @@ namespace LehrerApp.Desktop.Views.Groups;
|
|||||||
|
|
||||||
public partial class SeatingPlanTabView : UserControl
|
public partial class SeatingPlanTabView : UserControl
|
||||||
{
|
{
|
||||||
|
private const double AutoScrollEdgeSize = 72;
|
||||||
|
private const double AutoScrollStep = 14;
|
||||||
|
|
||||||
private object? _dragCandidate;
|
private object? _dragCandidate;
|
||||||
private PointerPressedEventArgs? _dragTrigger;
|
private PointerPressedEventArgs? _dragTrigger;
|
||||||
private Avalonia.Point _pressPosition;
|
private Avalonia.Point _pressPosition;
|
||||||
@@ -18,8 +21,15 @@ public partial class SeatingPlanTabView : UserControl
|
|||||||
private SeatCellViewModel? _pendingDropTarget;
|
private SeatCellViewModel? _pendingDropTarget;
|
||||||
private bool _pendingClearSeat;
|
private bool _pendingClearSeat;
|
||||||
private DateTime _ignoreTapUntil;
|
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)
|
protected override void OnDataContextChanged(EventArgs e)
|
||||||
{
|
{
|
||||||
@@ -69,6 +79,7 @@ public partial class SeatingPlanTabView : UserControl
|
|||||||
_draggedStudent = null;
|
_draggedStudent = null;
|
||||||
_pendingDropTarget = null;
|
_pendingDropTarget = null;
|
||||||
_pendingClearSeat = false;
|
_pendingClearSeat = false;
|
||||||
|
StopAutoScroll();
|
||||||
_ignoreTapUntil = DateTime.UtcNow.AddMilliseconds(250);
|
_ignoreTapUntil = DateTime.UtcNow.AddMilliseconds(250);
|
||||||
|
|
||||||
// Never mutate an ItemsControl while Avalonia is still processing its native
|
// 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)
|
private void OnSeatDrop(object? sender, DragEventArgs e)
|
||||||
{
|
{
|
||||||
|
StopAutoScroll();
|
||||||
if (sender is not Border { DataContext: SeatCellViewModel target }) return;
|
if (sender is not Border { DataContext: SeatCellViewModel target }) return;
|
||||||
target.IsDropTarget = false;
|
target.IsDropTarget = false;
|
||||||
if (!CanDropOn(target))
|
if (!CanDropOn(target))
|
||||||
@@ -128,6 +140,7 @@ public partial class SeatingPlanTabView : UserControl
|
|||||||
|
|
||||||
private void OnUnassignedDrop(object? sender, DragEventArgs e)
|
private void OnUnassignedDrop(object? sender, DragEventArgs e)
|
||||||
{
|
{
|
||||||
|
StopAutoScroll();
|
||||||
if (_draggedSeat is not null)
|
if (_draggedSeat is not null)
|
||||||
{
|
{
|
||||||
_pendingDropTarget = 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)
|
private async void OnSeatTapped(object? sender, TappedEventArgs e)
|
||||||
{
|
{
|
||||||
if (DateTime.UtcNow < _ignoreTapUntil || sender is not Border { DataContext: SeatCellViewModel seat }
|
if (DateTime.UtcNow < _ignoreTapUntil || sender is not Border { DataContext: SeatCellViewModel seat }
|
||||||
|
|||||||
Reference in New Issue
Block a user