using System; using System.Collections.Generic; using System.Linq; using System.Windows.Forms; using PolyTrader.Core.Modularity; namespace PolyTraderSharp.Ui { /// /// Verwaltet die registrierten Fenster-Views: öffnet sie als eigenständige Forms, /// hält je View höchstens eine Instanz offen und holt ein bereits offenes Fenster /// wieder in den Vordergrund. Meldet Änderungen am Offen-Status (für die /// Button-Markierung im Launcher). /// public class ShellUiHost : IModuleUiHost { private readonly List _views = new(); private readonly Dictionary _open = new(); private Form? _mainWindow; private bool _shutdownDialogOpen; /// /// 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. /// public bool ShutdownConfirmed { get; private set; } /// Wird ausgelöst, wenn sich der Offen-Status irgendeiner View ändert. public event Action? OpenStateChanged; public IReadOnlyList Views => _views; public void RegisterView(ModuleView view) => _views.Add(view); /// Setzt das Hauptfenster (Launcher) – Ziel für . public void SetMainWindow(Form main) => _mainWindow = main; /// /// 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. /// private void AttachWindowMenu(Form form, string currentViewId) { var menu = new MenuStrip { Dock = DockStyle.Top, ImageScalingSize = new System.Drawing.Size(24, 24) }; WindowMenu.Wire(menu, this, currentViewId); form.Controls.Add(menu); form.MainMenuStrip = menu; } public void ActivateMain() { if (_mainWindow == null || _mainWindow.IsDisposed) return; if (_mainWindow.WindowState == FormWindowState.Minimized) _mainWindow.WindowState = FormWindowState.Maximized; _mainWindow.BringToFront(); _mainWindow.Activate(); } public bool IsOpen(string viewId) => _open.TryGetValue(viewId, out var form) && !form.IsDisposed; /// /// 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 AppHost.StopAsync() (nach Application.Run). /// Aus jedem Fenster aufrufbar; der Dialog erscheint zentriert über dem Hauptfenster. /// 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); if (view != null) OpenView(view); } public void OpenView(ModuleView view) { if (_open.TryGetValue(view.Id, out var existing) && !existing.IsDisposed) { if (existing.WindowState == FormWindowState.Minimized) existing.WindowState = FormWindowState.Normal; existing.BringToFront(); existing.Activate(); return; } // CreateView liefert bewusst object (toolkit-neutraler Core-Contract). Die WinForms-Shell // kennt ihr Toolkit und castet hier; ein falscher Typ ist ein Programmierfehler und soll // laut knallen, nicht still ein leeres Fenster erzeugen. var form = (Form)view.CreateView(); if (string.IsNullOrEmpty(form.Text) || form.Text == form.Name) form.Text = view.Title; // Gemeinsames „Fenster"-Menü zentral in jedes Fenster injizieren (identische Shell-Chrome // auf allen Fenstern; die inhaltlichen Controls bleiben designerbasiert). Nur, wenn das // Fenster nicht bereits ein eigenes Menü mitbringt (z. B. der Launcher). if (form.MainMenuStrip == null) AttachWindowMenu(form, view.Id); // Alle Fenster maximiert (Vorgabe): Full-HD-Ziel, skaliert auf größere Auflösungen. form.StartPosition = FormStartPosition.CenterScreen; form.WindowState = FormWindowState.Maximized; _open[view.Id] = form; form.FormClosed += (_, _) => { _open.Remove(view.Id); OpenStateChanged?.Invoke(); }; form.Show(); OpenStateChanged?.Invoke(); } } }