Settings-Dialoge in echten Rhino-Fenstern (Eto.Form + WebView)
Statt Overlay-im-Panel oeffnet sich der Settings-Dialog jetzt als echtes Rhino-Fenster (verschiebbar, resizable, mehrere parallel). Infrastruktur in panel_base.py: - load_inline akzeptiert jetzt `params` (dict) und injiziert sie als window.PANEL_PARAMS — Satelliten-Apps lesen ihren initialen State daraus. - Neue Funktion open_satellite_window(mode, params, title, size, on_save, on_cancel): erstellt Eto.Forms.Form mit eingebetteter WebView, eigenem Inline-Bridge fuer SAVE/CANCEL-Messages, ruft Callbacks auf und schliesst das Fenster. Backend rhinopanel.py: - Neue Message-Handler OPEN_GESCHOSS_SETTINGS und OPEN_EBENEN_SETTINGS. - _open_geschoss_settings: oeffnet das Satelliten-Fenster mit dem Geschoss als Payload. on_save: replace im doc.Strings z-Liste + _apply(save_z=True). - _open_ebenen_settings: gleich, aber fuer Ebene + hatchPatterns. Neue React-Entries: - GeschossSettingsApp.jsx: wrappt GeschossSettingsDialog, liest window.PANEL_PARAMS, schickt SAVE/CANCEL direkt via document.title- Bridge. - EbenenSettingsApp.jsx: gleich fuer EbenenSettingsDialog. main.jsx-Switch erweitert um 'geschoss_settings' und 'ebenen_settings'. GeschossManager und EbenenManager: - Inline-Dialog-State und -Rendering entfernt. - onSettings ruft jetzt openGeschossSettings(z) / openEbenenSettings(e) in der Bridge auf → Backend oeffnet das Satelliten-Fenster. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
+100
-3
@@ -198,8 +198,12 @@ class BaseBridge(object):
|
||||
|
||||
# --- HTML laden -------------------------------------------------------------
|
||||
|
||||
def load_inline(wv, mode):
|
||||
"""Laedt dist/index.html inline und injiziert window.PANEL_MODE."""
|
||||
def load_inline(wv, mode, params=None):
|
||||
"""Laedt dist/index.html inline und injiziert window.PANEL_MODE.
|
||||
|
||||
`params` (optional dict): wird als `window.PANEL_PARAMS` injiziert. Wird
|
||||
von Satelliten-Fenstern (z.B. Settings-Dialoge) verwendet um initial-
|
||||
State an die React-App zu uebergeben."""
|
||||
if not os.path.exists(_DIST):
|
||||
print("[{}] dist nicht gefunden".format(mode.upper()))
|
||||
return
|
||||
@@ -207,7 +211,14 @@ def load_inline(wv, mode):
|
||||
with open(_DIST, "rb") as f:
|
||||
html = f.read().decode("utf-8")
|
||||
|
||||
mode_script = '<script>window.PANEL_MODE="{}";</script>'.format(mode)
|
||||
parts = ['<script>window.PANEL_MODE="{}";'.format(mode)]
|
||||
if params is not None:
|
||||
try:
|
||||
parts.append('window.PANEL_PARAMS=' + json.dumps(params, ensure_ascii=False) + ';')
|
||||
except Exception as ex:
|
||||
print("[{}] PANEL_PARAMS serialize: {}".format(mode.upper(), ex))
|
||||
parts.append('</script>')
|
||||
mode_script = ''.join(parts)
|
||||
if "</head>" in html:
|
||||
html = html.replace("</head>", mode_script + "</head>")
|
||||
else:
|
||||
@@ -267,6 +278,92 @@ def attach_webview(panel, bridge, mode):
|
||||
Rhino.RhinoApp.Idle += on_idle
|
||||
|
||||
|
||||
# --- 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):
|
||||
"""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.
|
||||
|
||||
Returns die Form-Instance (User kann sie speichern um sie spaeter
|
||||
programmatisch zu schliessen)."""
|
||||
|
||||
form = forms.Form()
|
||||
if title is None: title = mode.replace('_', ' ').title()
|
||||
form.Title = title
|
||||
try:
|
||||
form.ClientSize = drawing.Size(int(size[0]), int(size[1]))
|
||||
except Exception: pass
|
||||
form.Resizable = True
|
||||
form.Topmost = False
|
||||
|
||||
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()
|
||||
except Exception: pass
|
||||
try: form.Close()
|
||||
except Exception: pass
|
||||
|
||||
bridge = _SatelliteBridge()
|
||||
bridge.set_webview(wv)
|
||||
|
||||
def on_title_(s, e):
|
||||
title_str = e.Title or ""
|
||||
if not title_str.startswith("RHINOMSG::"):
|
||||
return
|
||||
try:
|
||||
bridge.handle_raw(title_str[10:])
|
||||
except Exception as ex:
|
||||
print("[{}] Message-Fehler: {}".format(mode.upper(), ex))
|
||||
finally:
|
||||
try:
|
||||
wv.ExecuteScript("document.title='{}';".format(mode.upper()))
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def on_loaded(s, e):
|
||||
try: wv.ExecuteScript("window.RHINO_MODE=true;")
|
||||
except Exception: pass
|
||||
|
||||
wv.DocumentTitleChanged += on_title_
|
||||
wv.DocumentLoaded += on_loaded
|
||||
|
||||
form.Content = wv
|
||||
form.Show()
|
||||
# HTML nach Show() laden — sonst ist die WebView eventuell noch nicht
|
||||
# gerendert und die JS-Bridge initialisiert sich seltsam.
|
||||
try:
|
||||
load_inline(wv, mode, params=params)
|
||||
except Exception as ex:
|
||||
print("[{}] Inline-Fehler: {}".format(mode.upper(), ex))
|
||||
return form
|
||||
|
||||
|
||||
# --- Dynamic .NET Type ------------------------------------------------------
|
||||
|
||||
def create_dockable_type(guid_str, type_name, assembly_name):
|
||||
|
||||
Reference in New Issue
Block a user