UI Slice 1a: Shell-Fundament + Launcher-Buttons statisch + Fenster-Menue + maximiert

- IModuleUiHost (Core) um Navigation erweitert: Views/IsOpen/OpenView/ActivateMain/OpenStateChanged
  + optionales Icon je ModuleView. So kann JEDES Fenster (auch Modul-Fenster, die nur Core kennen)
  das gemeinsame Fenster-Menue bauen.
- WindowMenu (Core): baut das 'Fenster'-Dropdown (Launcher + alle Views + Beenden), haakt offene
  Fenster an, markiert das aktuelle fett; Neuaufbau beim Aufklappen.
- ShellUiHost: SetMainWindow/ActivateMain; oeffnet ALLE Fenster jetzt MAXIMIERT (Vorgabe).
- Launcher: alle Modul-/Core-Buttons STATISCH im Designer (btn_copytrading/-resolutionfarming/
  -supervisor ergaenzt), an View-IDs gebunden; fehlt eine View -> Button deaktiviert. Dynamischer
  Laufzeit-Anhang ENTFERNT -> kein doppelter Accounting-Button mehr; leerer btn_accounting_Click raus.
  Datei/Beenden -> Fenster-Menue (WindowMenu). Launcher startet maximiert (Sizable, MinSize 1280x720).

Build 0 Fehler, --smoke-ui alle 6 Views + Launcher gruen. Fenster-Menue auf den uebrigen Fenstern folgt (1b).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Richard
2026-07-20 18:30:30 +02:00
co-authored by Claude Opus 4.8
parent ca0d4ceed0
commit f54cd7423b
5 changed files with 178 additions and 69 deletions
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Windows.Forms;
namespace PolyTrader.Core.Modularity
@@ -23,6 +24,9 @@ namespace PolyTrader.Core.Modularity
/// <summary>Optionale Sortierreihenfolge.</summary>
public int Order { get; init; } = 0;
/// <summary>Optionales Icon (16x16) für Menü/Buttons. Wird von der Shell/den Menüs genutzt.</summary>
public System.Drawing.Image? Icon { get; init; }
/// <summary>Erzeugt das anzuzeigende Fenster (frische Instanz je Öffnung).</summary>
public Func<Form> CreateForm { get; init; } = () => new Form();
}
@@ -30,9 +34,26 @@ namespace PolyTrader.Core.Modularity
/// <summary>
/// Wird der Shell beim Start übergeben; Core und Module registrieren hier ihre Ansichten.
/// Die Shell (Launcher) öffnet die Fenster auf Anforderung und verwaltet den Offen-Status.
/// Über die Navigations-Mitglieder kann JEDES Fenster (auch Modul-Fenster, die nur den Core
/// kennen) das gemeinsame „Fenster"-Menü bauen (siehe <see cref="WindowMenu"/>).
/// </summary>
public interface IModuleUiHost
{
void RegisterView(ModuleView view);
/// <summary>Alle registrierten Ansichten (Core + Module).</summary>
IReadOnlyList<ModuleView> Views { get; }
/// <summary>Ist das Fenster dieser View gerade offen?</summary>
bool IsOpen(string viewId);
/// <summary>Öffnet die View bzw. holt ein bereits offenes Fenster in den Vordergrund (maximiert).</summary>
void OpenView(string viewId);
/// <summary>Holt das Hauptfenster (Launcher) in den Vordergrund.</summary>
void ActivateMain();
/// <summary>Feuert, wenn sich der Offen-Status irgendeiner View ändert.</summary>
event Action? OpenStateChanged;
}
}
@@ -0,0 +1,53 @@
using System;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
namespace PolyTrader.Core.Modularity
{
/// <summary>
/// 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
/// <see cref="IModuleUiHost"/> nutzt, funktioniert es auch aus Modul-Fenstern (die die App nicht kennen).
///
/// Der Menü-Container liegt im Designer jedes Fensters (ein <see cref="ToolStripMenuItem"/> „Fenster");
/// die dynamische Liste (Offen-Status ändert sich) wird hier beim Aufklappen frisch gefüllt:
/// offene Fenster sind angehakt, das aktuelle Fenster ist fett + angehakt markiert.
/// </summary>
public static class WindowMenu
{
/// <summary>Verdrahtet ein Designer-„Fenster"-Menüelement mit der Fensterliste (Neuaufbau beim Aufklappen).</summary>
public static void Wire(ToolStripMenuItem fensterMenu, IModuleUiHost host, string? currentViewId)
{
fensterMenu.DropDownOpening += (_, _) => Populate(fensterMenu, host, currentViewId);
Populate(fensterMenu, host, currentViewId);
}
public static void Populate(ToolStripMenuItem fensterMenu, IModuleUiHost host, string? currentViewId)
{
fensterMenu.DropDownItems.Clear();
var launcher = new ToolStripMenuItem("Launcher") { Checked = currentViewId == null };
if (currentViewId == null) launcher.Font = new Font(launcher.Font, FontStyle.Bold);
launcher.Click += (_, _) => host.ActivateMain();
fensterMenu.DropDownItems.Add(launcher);
fensterMenu.DropDownItems.Add(new ToolStripSeparator());
foreach (var view in host.Views.OrderBy(v => v.Order).ThenBy(v => v.Title))
{
bool isCurrent = view.Id == currentViewId;
var item = new ToolStripMenuItem(view.Title) { Image = view.Icon };
item.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);
fensterMenu.DropDownItems.Add(item);
}
fensterMenu.DropDownItems.Add(new ToolStripSeparator());
var exit = new ToolStripMenuItem("Beenden");
exit.Click += (_, _) => Application.Exit();
fensterMenu.DropDownItems.Add(exit);
}
}
}