Files
LehrerApp/LehrerApp.Desktop/Views/MainWindow.axaml.cs
2026-08-29 00:45:19 +02:00

136 lines
4.5 KiB
C#

using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Threading;
using LehrerApp.Desktop.Services;
using LehrerApp.Desktop.ViewModels;
using LehrerApp.Sync;
namespace LehrerApp.Desktop.Views;
public partial class MainWindow : Window
{
private static readonly TimeSpan FinalSyncDelay = TimeSpan.FromMilliseconds(350);
private SyncEngine? _syncEngine;
private bool _finalSyncStarted;
private bool _closeAfterFinalSync;
public MainWindow()
{
InitializeComponent();
PointerMoved += (_, _) => NotifyActivity();
PointerPressed += (_, _) => NotifyActivity();
KeyDown += (_, _) => NotifyActivity();
KeyDown += OnWindowKeyDown;
}
private void OnWindowKeyDown(object? sender, KeyEventArgs e)
{
if (DataContext is not MainWindowViewModel vm) return;
var commandModifier = (e.KeyModifiers & (KeyModifiers.Control | KeyModifiers.Meta)) != 0;
if (commandModifier && e.Key == Key.K)
{
vm.OpenCommandPaletteCommand.Execute(null);
FocusCommandPalette();
e.Handled = true;
return;
}
if (!vm.IsCommandPaletteOpen) return;
if (e.Key == Key.Escape)
{
vm.CloseCommandPaletteCommand.Execute(null);
e.Handled = true;
}
else if (e.Key == Key.Enter)
{
vm.CommandPalette.ExecuteCommand.Execute(vm.CommandPalette.SelectedResult);
e.Handled = true;
}
else if (e.Key is Key.Down or Key.Up)
{
MoveCommandPaletteSelection(vm, e.Key == Key.Down ? 1 : -1);
e.Handled = true;
}
}
private void OnOpenCommandPaletteClick(object? sender, Avalonia.Interactivity.RoutedEventArgs e)
{
if (DataContext is MainWindowViewModel vm)
vm.OpenCommandPaletteCommand.Execute(null);
FocusCommandPalette();
}
private void FocusCommandPalette() => Dispatcher.UIThread.Post(() =>
{
if (this.FindControl<TextBox>("CommandPaletteSearchBox") is { } search)
{
search.Focus();
search.SelectAll();
}
});
private static void MoveCommandPaletteSelection(MainWindowViewModel vm, int delta)
{
var results = vm.CommandPalette.Results;
if (results.Count == 0) return;
var current = vm.CommandPalette.SelectedResult is { } selected ? results.IndexOf(selected) : -1;
vm.CommandPalette.SelectedResult = results[Math.Clamp(current + delta, 0, results.Count - 1)];
}
public void EnableFinalSync(SyncEngine syncEngine)
{
_syncEngine = syncEngine;
Closing += OnClosing;
}
/// <summary>Wendet die zuletzt gespeicherte Fenstergröße an und merkt sich die aktuelle beim
/// Schließen (14.6). Eigenständig von <see cref="EnableFinalSync"/> verdrahtet (mehrere
/// Closing-Handler sind unproblematisch) und immer aktiv, auch ohne konfigurierten
/// Sync-Server.</summary>
public void EnableWindowSizePersistence(WindowSettingsService settings)
{
var saved = settings.Load();
Width = saved.Width;
Height = saved.Height;
if (saved.IsMaximized) WindowState = WindowState.Maximized;
Closing += (_, _) =>
{
var current = settings.Load();
settings.Save(new WindowSettings
{
IsMaximized = WindowState == WindowState.Maximized,
// Im maximierten Zustand spiegeln Width/Height die Bildschirmgröße wider, nicht
// die zuletzt genutzte Normalgröße - dann den vorherigen Wert beibehalten statt
// ihn zu überschreiben.
Width = WindowState == WindowState.Normal ? Width : current.Width,
Height = WindowState == WindowState.Normal ? Height : current.Height,
});
};
}
private async void OnClosing(object? sender, WindowClosingEventArgs e)
{
if (_closeAfterFinalSync || _finalSyncStarted || _syncEngine is null)
return;
e.Cancel = true;
_finalSyncStarted = true;
await _syncEngine.SyncBeforeShutdownAsync(FinalSyncDelay);
_closeAfterFinalSync = true;
Close();
}
private void NotifyActivity()
{
if (DataContext is MainWindowViewModel vm) vm.AppLock.NotifyActivity();
}
private void OnLockScreenKeyDown(object? sender, KeyEventArgs e)
{
if (e.Key == Key.Enter && DataContext is MainWindowViewModel vm)
vm.AppLock.UnlockCommand.Execute(null);
}
}