UI Slice 5: Menueleiste (Fenster nebeneinander), sicheres Beenden, Modul-Aktivierung

- Fenster-Menue: WindowMenu fuellt die oberste MenuStrip mit Top-Level-Eintraegen
  nebeneinander (mit Icon) statt Untermenue "Fenster"; ModuleView.Icon zentral in der
  App zugewiesen (AssignMenuIcons). Kein miFenster mehr im Launcher-Designer.
- Sicheres Beenden: ShutdownConfirmDialog (10s-Timer sperrt "Jetzt beenden", Abbrechen
  jederzeit) via IModuleUiHost.RequestShutdown(). Nur der Launcher (Hauptprozess) bietet
  "Beenden"; andere Fenster nur "Fenster schliessen" (kein App-Shutdown, Module laufen
  weiter). Launcher-Schliessen-X routet ueber dieselbe Abfrage.
- Modul-Aktivierung (restart-basiert): ServerSettings.DisabledModules, in Program.Main
  vor der DI-Registrierung gefiltert -> deaktivierte Module werden nicht geladen.
  Dashboard-Tab "Module" zeigt Status + schaltet um (Hinweis: greift nach Neustart).
  Optionaler IPolyTraderModule.GetActivationBlocker fuer "nicht aktivierbar"-Info.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Richard
2026-07-23 18:46:09 +02:00
co-authored by Claude Opus 4.8
parent 886de3a85b
commit 039bc240f8
13 changed files with 685 additions and 61 deletions
+38 -6
View File
@@ -17,6 +17,13 @@ namespace PolyTraderSharp.Ui
private readonly List<ModuleView> _views = new();
private readonly Dictionary<string, Form> _open = new();
private Form? _mainWindow;
private bool _shutdownDialogOpen;
/// <summary>
/// True, sobald das Herunterfahren über die Sicherheitsabfrage bestätigt wurde. Der Launcher
/// wertet das in FormClosing aus, um beim Schließen-X denselben Dialog zu erzwingen.
/// </summary>
public bool ShutdownConfirmed { get; private set; }
/// <summary>Wird ausgelöst, wenn sich der Offen-Status irgendeiner View ändert.</summary>
public event Action? OpenStateChanged;
@@ -29,15 +36,14 @@ namespace PolyTraderSharp.Ui
public void SetMainWindow(Form main) => _mainWindow = main;
/// <summary>
/// Fügt einem Fenster das gemeinsame Fenster"-Menü hinzu (oberste Leiste). Als letztes
/// hinzugefügtes Top-Control belegt der MenuStrip die oberste Zeile über etwaigen ToolStrips.
/// Fügt einem Fenster die gemeinsame Fenster-Menüleiste hinzu (oberste Leiste, alle Fenster
/// nebeneinander mit Icon). Als letztes hinzugefügtes Top-Control belegt der MenuStrip die
/// oberste Zeile über etwaigen ToolStrips.
/// </summary>
private void AttachWindowMenu(Form form, string currentViewId)
{
var menu = new MenuStrip { Dock = DockStyle.Top };
var fenster = new ToolStripMenuItem("Fenster");
menu.Items.Add(fenster);
PolyTrader.Core.Modularity.WindowMenu.Wire(fenster, this, currentViewId);
var menu = new MenuStrip { Dock = DockStyle.Top, ImageScalingSize = new System.Drawing.Size(24, 24) };
PolyTrader.Core.Modularity.WindowMenu.Wire(menu, this, currentViewId);
form.Controls.Add(menu);
form.MainMenuStrip = menu;
}
@@ -54,6 +60,32 @@ namespace PolyTraderSharp.Ui
public bool IsOpen(string viewId) =>
_open.TryGetValue(viewId, out var form) && !form.IsDisposed;
/// <summary>
/// Zeigt die Sicherheitsabfrage (Timer-gesperrter Beenden-Button, Abbrechen jederzeit). Bei
/// Bestätigung wird die Message-Loop beendet das geordnete Herunterfahren der Module läuft
/// anschließend in Program.Main über <c>AppHost.StopAsync()</c> (nach <c>Application.Run</c>).
/// Aus jedem Fenster aufrufbar; der Dialog erscheint zentriert über dem Hauptfenster.
/// </summary>
public void RequestShutdown()
{
if (ShutdownConfirmed || _shutdownDialogOpen) return;
_shutdownDialogOpen = true;
try
{
using var dlg = new ShutdownConfirmDialog();
var owner = _mainWindow != null && !_mainWindow.IsDisposed ? _mainWindow : null;
var result = owner != null ? dlg.ShowDialog(owner) : dlg.ShowDialog();
if (result != DialogResult.OK) return;
ShutdownConfirmed = true;
Application.Exit(); // beendet die Message-Loop; StopAsync() fährt Module danach sauber herunter
}
finally
{
_shutdownDialogOpen = false;
}
}
public void OpenView(string viewId)
{
var view = _views.FirstOrDefault(v => v.Id == viewId);