diff --git a/rhino/oberleiste.py b/rhino/oberleiste.py
index 2932d43..c397735 100644
--- a/rhino/oberleiste.py
+++ b/rhino/oberleiste.py
@@ -915,11 +915,10 @@ class OberleisteBridge(panel_base.BaseBridge):
print("[OBERLEISTE] notify overrides:", ex)
elif t == "OPEN_OVERRIDES_PANEL":
try:
- import System
- import Rhino.UI as RhinoUI
- RhinoUI.Panels.OpenPanel(System.Guid(OVERRIDES_PANEL_GUID_STR))
+ import overrides_panel
+ overrides_panel.open_as_window()
except Exception as ex:
- print("[OBERLEISTE] OpenPanel Overrides:", ex)
+ print("[OBERLEISTE] open_as_window Overrides:", ex)
# --- Command-Line Integration -----------------------------------
elif t == "RUN_COMMAND":
diff --git a/rhino/overrides_panel.py b/rhino/overrides_panel.py
index f7c7f0b..5abae85 100644
--- a/rhino/overrides_panel.py
+++ b/rhino/overrides_panel.py
@@ -211,16 +211,37 @@ class OverridesBridge(panel_base.BaseBridge):
self._send_state()
-def _bridge_factory():
+def _ensure_listeners_once():
+ """Overrides-Listener nur EINMAL global installieren (statt bei jedem
+ open_as_window)."""
+ if sc.sticky.get("overrides_listeners_installed"):
+ return
+ try:
+ overrides.install_listeners()
+ sc.sticky["overrides_listeners_installed"] = True
+ except Exception as ex:
+ print("[OVERRIDES] install_listeners:", ex)
+
+
+def open_as_window():
+ """Oeffnet OVERRIDES als echtes Rhino-Fenster (Eto.Form + WebView).
+ Wird vom Oberleiste-Bridge bei OPEN_OVERRIDES_PANEL gerufen.
+
+ Pro Fenster eine eigene OverridesBridge-Instanz. Die letzte Instanz
+ landet in sticky["overrides_bridge"] — andere Panels (Oberleiste) die
+ Cross-Updates an Overrides senden, treffen das aktive Fenster."""
+ _ensure_listeners_once()
b = OverridesBridge()
- try: overrides.install_listeners()
- except Exception as ex: print("[OVERRIDES] install_listeners:", ex)
- # Bridge im sticky ablegen, damit andere Panels (z.B. Oberleiste) sie
- # bei Cross-Panel-Updates erreichen koennen.
sc.sticky["overrides_bridge"] = b
- return b
+ panel_base.open_satellite_window(
+ "overrides",
+ title="OVERRIDES",
+ size=(760, 580),
+ bridge=b)
-panel_base.register_and_open("overrides", "OVERRIDES", PANEL_GUID_STR, _bridge_factory,
- icon_spec=("tune", "#b5621e"),
- min_size=(720, 560))
+# OVERRIDES laeuft jetzt als Satelliten-Fenster (geoeffnet vom Oberleiste-
+# Gear-Button), nicht mehr als gedocktes Panel. Listener werden lazy beim
+# ersten open_as_window installiert. Falls jemand das alte Panel via
+# Workspace-Layout noch geoeffnet hat, wird es nicht mehr registriert →
+# Rhino zeigt es leer / nicht mehr an.
diff --git a/rhino/panel_base.py b/rhino/panel_base.py
index 7e21191..93b431b 100644
--- a/rhino/panel_base.py
+++ b/rhino/panel_base.py
@@ -281,16 +281,20 @@ def attach_webview(panel, bridge, mode):
# --- Satelliten-Fenster (echtes Rhino-Fenster mit eingebetteter WebView) ----
def open_satellite_window(mode, params=None, title=None, size=(420, 560),
- on_save=None, on_cancel=None):
+ on_save=None, on_cancel=None, bridge=None):
"""Oeffnet ein echtes Rhino-Fenster (Eto.Form) mit eingebetteter WebView.
Die WebView laedt die React-App mit dem gegebenen `mode` und `params`.
- Die React-App sendet via Bridge `SAVE`/`CANCEL`-Messages. Wir rufen
- dann die jeweilige Callback-Funktion auf (mit dem Save-Payload) und
- schliessen das Fenster.
+ Zwei Bridge-Modi:
+ - **Default (kein `bridge`-Arg):** inline SAVE/CANCEL-Bridge. React
+ sendet SAVE/CANCEL → on_save/on_cancel-Callback → Fenster zu. Fuer
+ einfache One-Shot-Dialoge (Settings etc.).
+ - **`bridge` uebergeben:** eine vollwertige BaseBridge-Subklasse (z.B.
+ OverridesBridge). Das Fenster nutzt die wie ein normales Panel,
+ mit allen Mess-Typen die der Bridge handlet. Save/Cancel sind dort
+ nicht standard; Fenster bleibt offen bis User es manuell schliesst.
- Returns die Form-Instance (User kann sie speichern um sie spaeter
- programmatisch zu schliessen)."""
+ Returns die Form-Instance."""
form = forms.Form()
if title is None: title = mode.replace('_', ' ').title()
@@ -303,33 +307,31 @@ def open_satellite_window(mode, params=None, title=None, size=(420, 560),
wv = forms.WebView()
- # Inline-Bridge fuer Satelliten-Fenster: handle SAVE/CANCEL, schliesse
- # bei beiden das Fenster.
- class _SatelliteBridge(BaseBridge):
- def __init__(self):
- BaseBridge.__init__(self, mode)
- def handle(self, data):
- t = data.get("type", "")
- p = data.get("payload") or {}
- if t == "READY":
- # React liest PANEL_PARAMS direkt vom window-Object — wir
- # muessen also nichts mehr aktiv senden.
- pass
- elif t == "SAVE":
- if on_save is not None:
- try: on_save(p)
- except Exception as ex:
- print("[{}] on_save: {}".format(mode.upper(), ex))
- try: form.Close()
- except Exception: pass
- elif t == "CANCEL":
- if on_cancel is not None:
- try: on_cancel()
+ if bridge is None:
+ # Inline-Bridge fuer einfache Settings-Dialoge: SAVE/CANCEL, schliesse
+ # bei beiden das Fenster.
+ class _SatelliteBridge(BaseBridge):
+ def __init__(self):
+ BaseBridge.__init__(self, mode)
+ def handle(self, data):
+ t = data.get("type", "")
+ p = data.get("payload") or {}
+ if t == "READY":
+ pass
+ elif t == "SAVE":
+ if on_save is not None:
+ try: on_save(p)
+ except Exception as ex:
+ print("[{}] on_save: {}".format(mode.upper(), ex))
+ try: form.Close()
except Exception: pass
- try: form.Close()
- except Exception: pass
-
- bridge = _SatelliteBridge()
+ elif t == "CANCEL":
+ if on_cancel is not None:
+ try: on_cancel()
+ except Exception: pass
+ try: form.Close()
+ except Exception: pass
+ bridge = _SatelliteBridge()
bridge.set_webview(wv)
def on_title_(s, e):
diff --git a/src/OberleisteApp.jsx b/src/OberleisteApp.jsx
index 45b05e9..dd8eb22 100644
--- a/src/OberleisteApp.jsx
+++ b/src/OberleisteApp.jsx
@@ -374,6 +374,11 @@ export default function OberleisteApp() {
+