feat: Dunkelmodus, Fenstergröße merken, Papierkorb für Löschvorgänge (12.4, 14.3, 14.6)

Dunkelmodus über neuen Einstellungen-Tab "Darstellung" (Systemvorgabe/Hell/Dunkel),
Fenstergröße/Maximiert-Status wird über Sitzungen hinweg gemerkt (bewusst ohne
Fensterposition), und ein generischer Snapshot-basierter Papierkorb (30 Tage) für
Sitzpläne, Noten, Notenschlüssel-Vorlagen, Aufgaben und Zeiteinträge. Details und
bewusste Scope-Entscheidungen (Spaltenbreiten zurückgestellt, Farb-Audit für
Dunkelmodus offen, welche Entitäten der Papierkorb abdeckt) in TODO.md.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-20 16:26:10 +02:00
co-authored by Claude Sonnet 5
parent d9d007cd4b
commit 331033db5c
20 changed files with 1023 additions and 12 deletions
@@ -0,0 +1,54 @@
using System.Text.Json;
namespace LehrerApp.Desktop.Services;
/// <summary>Systemvorgabe folgt <see cref="Avalonia.Styling.ThemeVariant.Default"/> (bisheriges,
/// unverändertes Verhalten); Hell/Dunkel erzwingen die jeweilige Variante unabhängig vom
/// Betriebssystem.</summary>
public enum AppTheme { System, Light, Dark }
internal sealed class AppearanceSettingsConfig
{
public AppTheme Theme { get; set; } = AppTheme.System;
}
/// <summary>Merkt sich die gewählte Darstellung (12.4) über Sitzungen hinweg.</summary>
public sealed class AppearanceSettingsService
{
private readonly string _configPath;
public AppearanceSettingsService(string appDataPath) =>
_configPath = Path.Combine(appDataPath, "appearancesettings.json");
public AppTheme Load()
{
try
{
if (File.Exists(_configPath))
return JsonSerializer.Deserialize<AppearanceSettingsConfig>(File.ReadAllText(_configPath))
?.Theme ?? AppTheme.System;
}
catch { /* beschädigte Konfiguration -> Systemvorgabe */ }
return AppTheme.System;
}
public void Save(AppTheme theme) =>
File.WriteAllText(_configPath, JsonSerializer.Serialize(new AppearanceSettingsConfig { Theme = theme }));
}
// ComboBox-Anzeige: deutsche Beschriftung statt des rohen Enum-Namens (etabliertes Muster, siehe
// z.B. NiveauDisplay/GradeCategoryDisplay).
public static class AppThemeDisplay
{
public static string Label(AppTheme theme) => theme switch
{
AppTheme.Light => "Hell",
AppTheme.Dark => "Dunkel",
_ => "Systemvorgabe",
};
public static string[] Options { get; } = Enum.GetValues<AppTheme>().Select(Label).ToArray();
public static AppTheme FromLabel(string? label) =>
Enum.GetValues<AppTheme>().FirstOrDefault(t => Label(t) == label, AppTheme.System);
}
@@ -0,0 +1,41 @@
using System.Text.Json;
namespace LehrerApp.Desktop.Services;
public sealed class WindowSettings
{
public double Width { get; set; } = 1280;
public double Height { get; set; } = 800;
public bool IsMaximized { get; set; }
}
/// <summary>
/// Merkt sich Fenstergröße und Maximiert-Status über Sitzungen hinweg (14.6). Bewusst OHNE
/// Fensterposition: ein Laptop, der mal mit, mal ohne externen Monitor läuft, könnte sonst dazu
/// führen, dass das Fenster bei der nächsten Sitzung außerhalb jedes sichtbaren Bildschirms
/// landet — die Größe allein ist der Teil, der täglich beim Korrigieren am Abend stört.
/// </summary>
public sealed class WindowSettingsService
{
private readonly string _configPath;
public WindowSettingsService(string appDataPath) =>
_configPath = Path.Combine(appDataPath, "windowsettings.json");
public WindowSettings Load()
{
try
{
if (File.Exists(_configPath))
{
var loaded = JsonSerializer.Deserialize<WindowSettings>(File.ReadAllText(_configPath));
if (loaded is { Width: >= 200, Height: >= 150 }) return loaded;
}
}
catch { /* beschädigte Konfiguration -> Standardgröße */ }
return new WindowSettings();
}
public void Save(WindowSettings settings) =>
File.WriteAllText(_configPath, JsonSerializer.Serialize(settings));
}