- 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>
80 lines
2.8 KiB
C#
80 lines
2.8 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using System.Windows.Forms;
|
||
using PolyTrader.Core.Modularity;
|
||
|
||
namespace PolyTraderSharp.Ui
|
||
{
|
||
/// <summary>
|
||
/// 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).
|
||
/// </summary>
|
||
public class ShellUiHost : IModuleUiHost
|
||
{
|
||
private readonly List<ModuleView> _views = new();
|
||
private readonly Dictionary<string, Form> _open = new();
|
||
private Form? _mainWindow;
|
||
|
||
/// <summary>Wird ausgelöst, wenn sich der Offen-Status irgendeiner View ändert.</summary>
|
||
public event Action? OpenStateChanged;
|
||
|
||
public IReadOnlyList<ModuleView> Views => _views;
|
||
|
||
public void RegisterView(ModuleView view) => _views.Add(view);
|
||
|
||
/// <summary>Setzt das Hauptfenster (Launcher) – Ziel für <see cref="ActivateMain"/>.</summary>
|
||
public void SetMainWindow(Form main) => _mainWindow = main;
|
||
|
||
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;
|
||
|
||
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;
|
||
}
|
||
|
||
var form = view.CreateForm();
|
||
if (string.IsNullOrEmpty(form.Text) || form.Text == form.Name)
|
||
form.Text = view.Title;
|
||
|
||
// 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();
|
||
}
|
||
}
|
||
}
|