using System; using System.Drawing; using System.Linq; using System.Windows.Forms; using PolyTrader.Core.Modularity; namespace PolyTraderSharp.Ui { /// /// WinForms-Umsetzung des gemeinsamen Fenster-Menüs. Liegt bewusst in der App und nicht mehr /// im Core: der Core ist seit der Linux-Portierung toolkit-neutral (net8.0) und darf keine /// WinForms-Typen mehr kennen. Die Avalonia-Shell bekommt später ihr eigenes Gegenstück. /// /// Baut das gemeinsame Fenster-Menü, das auf JEDEM PolyTrader-Fenster erscheint und das Wechseln /// zwischen allen Fenstern (Launcher + Core + Module) erlaubt. Da es nur den Core-Contract /// nutzt, funktioniert es auch aus Modul-Fenstern (die die App nicht kennen). /// /// Die Fenster werden NICHT mehr in einem Untermenü „Fenster" versteckt, sondern als eigenständige /// Einträge (mit Icon) direkt nebeneinander in der obersten Menüleiste aufgelistet. Das aktuelle /// Fenster ist fett + angehakt, weitere offene Fenster sind angehakt. /// Rechts liegt eine kontextabhängige Aktion: NUR auf dem Launcher (Hauptprozess) „Beenden" /// (fährt die gesamte App über die Sicherheitsabfrage herunter); auf allen anderen Fenstern /// „Fenster schließen" (schließt nur dieses Fenster – ohne Einfluss auf laufende Module). /// Die Leiste wird bei jeder Statusänderung (Fenster geöffnet/geschlossen) neu aufgebaut. /// public static class WindowMenu { /// /// Verdrahtet eine Designer- mit der Fensterliste: füllt sie sofort und /// baut sie bei jeder Offen-Status-Änderung neu auf. Die Registrierung wird sauber gelöst, sobald /// die Menüleiste (mit ihrem Fenster) entsorgt wird – kein Event-Leak über die Host-Lebensdauer. /// public static void Wire(MenuStrip menu, IModuleUiHost host, string? currentViewId) { void Refresh() { if (menu.IsDisposed) return; if (menu.IsHandleCreated && menu.InvokeRequired) { try { menu.BeginInvoke((Action)(() => Populate(menu, host, currentViewId))); } catch { /* Fenster wird gerade geschlossen */ } return; } Populate(menu, host, currentViewId); } Populate(menu, host, currentViewId); host.OpenStateChanged += Refresh; menu.Disposed += (_, _) => host.OpenStateChanged -= Refresh; } /// Baut die Menüleiste komplett neu auf (Launcher, alle Views nebeneinander, Beenden rechts). public static void Populate(MenuStrip menu, IModuleUiHost host, string? currentViewId) { menu.Items.Clear(); var launcher = new ToolStripMenuItem("Launcher") { Checked = currentViewId == null }; if (currentViewId == null) launcher.Font = new Font(launcher.Font, FontStyle.Bold); launcher.Click += (_, _) => host.ActivateMain(); menu.Items.Add(launcher); foreach (var view in host.Views.OrderBy(v => v.Order).ThenBy(v => v.Title)) { bool isCurrent = view.Id == currentViewId; var image = ViewIcons.Resolve(view.IconKey); var item = new ToolStripMenuItem(view.Title) { Image = image, ImageScaling = ToolStripItemImageScaling.SizeToFit, DisplayStyle = image != null ? ToolStripItemDisplayStyle.ImageAndText : ToolStripItemDisplayStyle.Text, Checked = isCurrent || host.IsOpen(view.Id) }; if (isCurrent) item.Font = new Font(item.Font, FontStyle.Bold); string id = view.Id; item.Click += (_, _) => host.OpenView(id); menu.Items.Add(item); } // Kontextabhängige rechte Aktion: nur der Launcher darf die App beenden (Sicherheitsabfrage); // jedes andere Fenster bietet nur „Fenster schließen" (kein App-Shutdown, Module laufen weiter). if (currentViewId == null) { var exit = new ToolStripMenuItem("Beenden") { Alignment = ToolStripItemAlignment.Right }; exit.Click += (_, _) => host.RequestShutdown(); menu.Items.Add(exit); } else { var close = new ToolStripMenuItem("Fenster schließen") { Alignment = ToolStripItemAlignment.Right }; close.Click += (_, _) => menu.FindForm()?.Close(); menu.Items.Add(close); } } } }