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 -------------------------------------------------------------
|
# --- HTML laden -------------------------------------------------------------
|
||||||
|
|
||||||
def load_inline(wv, mode):
|
def load_inline(wv, mode, params=None):
|
||||||
"""Laedt dist/index.html inline und injiziert window.PANEL_MODE."""
|
"""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):
|
if not os.path.exists(_DIST):
|
||||||
print("[{}] dist nicht gefunden".format(mode.upper()))
|
print("[{}] dist nicht gefunden".format(mode.upper()))
|
||||||
return
|
return
|
||||||
@@ -207,7 +211,14 @@ def load_inline(wv, mode):
|
|||||||
with open(_DIST, "rb") as f:
|
with open(_DIST, "rb") as f:
|
||||||
html = f.read().decode("utf-8")
|
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:
|
if "</head>" in html:
|
||||||
html = html.replace("</head>", mode_script + "</head>")
|
html = html.replace("</head>", mode_script + "</head>")
|
||||||
else:
|
else:
|
||||||
@@ -267,6 +278,92 @@ def attach_webview(panel, bridge, mode):
|
|||||||
Rhino.RhinoApp.Idle += on_idle
|
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 ------------------------------------------------------
|
# --- Dynamic .NET Type ------------------------------------------------------
|
||||||
|
|
||||||
def create_dockable_type(guid_str, type_name, assembly_name):
|
def create_dockable_type(guid_str, type_name, assembly_name):
|
||||||
|
|||||||
@@ -216,9 +216,89 @@ class EbenenBridge(panel_base.BaseBridge):
|
|||||||
elif t == "DELETE_PRESET":
|
elif t == "DELETE_PRESET":
|
||||||
self._delete_preset(p.get("name") or "")
|
self._delete_preset(p.get("name") or "")
|
||||||
self._send_combination()
|
self._send_combination()
|
||||||
|
elif t == "OPEN_GESCHOSS_SETTINGS":
|
||||||
|
self._open_geschoss_settings(p.get("geschoss") or {})
|
||||||
|
elif t == "OPEN_EBENEN_SETTINGS":
|
||||||
|
self._open_ebenen_settings(p.get("ebene") or {},
|
||||||
|
p.get("hatchPatterns") or [])
|
||||||
|
|
||||||
# ---- Helpers ----
|
# ---- Helpers ----
|
||||||
|
|
||||||
|
def _open_geschoss_settings(self, geschoss):
|
||||||
|
"""Oeffnet ein echtes Rhino-Fenster (Eto.Form mit WebView) mit dem
|
||||||
|
GeschossSettingsDialog. Save updated den Eintrag in doc.Strings +
|
||||||
|
triggert Cross-Panel-Sync."""
|
||||||
|
if not isinstance(geschoss, dict) or not geschoss.get("id"):
|
||||||
|
print("[EBENEN] open_geschoss_settings: kein Geschoss-Payload")
|
||||||
|
return
|
||||||
|
gid = geschoss["id"]
|
||||||
|
def on_save(updated):
|
||||||
|
doc = Rhino.RhinoDoc.ActiveDoc
|
||||||
|
if doc is None: return
|
||||||
|
z_raw = doc.Strings.GetValue("dossier_zeichnungsebenen")
|
||||||
|
if not z_raw:
|
||||||
|
print("[EBENEN] save_geschoss: kein z-Store"); return
|
||||||
|
try:
|
||||||
|
z_list = json.loads(z_raw)
|
||||||
|
except Exception as ex:
|
||||||
|
print("[EBENEN] save_geschoss JSON:", ex); return
|
||||||
|
replaced = False
|
||||||
|
for i, z in enumerate(z_list):
|
||||||
|
if isinstance(z, dict) and z.get("id") == gid:
|
||||||
|
z_list[i] = updated
|
||||||
|
replaced = True
|
||||||
|
break
|
||||||
|
if not replaced:
|
||||||
|
print("[EBENEN] save_geschoss: id {} nicht gefunden".format(gid))
|
||||||
|
return
|
||||||
|
# Build_layers + Save via _apply (durchlaeuft ohne save_e)
|
||||||
|
e_raw = doc.Strings.GetValue("dossier_ebenen")
|
||||||
|
try: e_list = json.loads(e_raw) if e_raw else []
|
||||||
|
except Exception: e_list = []
|
||||||
|
self._apply(z_list, e_list, save_z=True, save_e=False)
|
||||||
|
panel_base.open_satellite_window(
|
||||||
|
"geschoss_settings",
|
||||||
|
params=geschoss,
|
||||||
|
title="Zeichnungsebene: {}".format(geschoss.get("name", "")),
|
||||||
|
size=(380, 540),
|
||||||
|
on_save=on_save)
|
||||||
|
|
||||||
|
def _open_ebenen_settings(self, ebene, hatch_patterns):
|
||||||
|
"""Oeffnet ein echtes Rhino-Fenster mit dem EbenenSettingsDialog."""
|
||||||
|
if not isinstance(ebene, dict) or not ebene.get("code"):
|
||||||
|
print("[EBENEN] open_ebenen_settings: kein Ebene-Payload")
|
||||||
|
return
|
||||||
|
old_code = ebene["code"]
|
||||||
|
def on_save(updated):
|
||||||
|
doc = Rhino.RhinoDoc.ActiveDoc
|
||||||
|
if doc is None: return
|
||||||
|
e_raw = doc.Strings.GetValue("dossier_ebenen")
|
||||||
|
if not e_raw:
|
||||||
|
print("[EBENEN] save_ebene: kein e-Store"); return
|
||||||
|
try:
|
||||||
|
e_list = json.loads(e_raw)
|
||||||
|
except Exception as ex:
|
||||||
|
print("[EBENEN] save_ebene JSON:", ex); return
|
||||||
|
replaced = False
|
||||||
|
for i, e in enumerate(e_list):
|
||||||
|
if isinstance(e, dict) and e.get("code") == old_code:
|
||||||
|
e_list[i] = updated
|
||||||
|
replaced = True
|
||||||
|
break
|
||||||
|
if not replaced:
|
||||||
|
print("[EBENEN] save_ebene: code {} nicht gefunden".format(old_code))
|
||||||
|
return
|
||||||
|
z_raw = doc.Strings.GetValue("dossier_zeichnungsebenen")
|
||||||
|
try: z_list = json.loads(z_raw) if z_raw else []
|
||||||
|
except Exception: z_list = []
|
||||||
|
self._apply(z_list, e_list, save_z=False, save_e=True)
|
||||||
|
panel_base.open_satellite_window(
|
||||||
|
"ebenen_settings",
|
||||||
|
params={"ebene": ebene, "hatchPatterns": hatch_patterns},
|
||||||
|
title="Ebene: {}_{}".format(ebene.get("code", ""), ebene.get("name", "")),
|
||||||
|
size=(420, 600),
|
||||||
|
on_save=on_save)
|
||||||
|
|
||||||
def _apply(self, zeichnungsebenen, ebenen, save_z=True, save_e=True):
|
def _apply(self, zeichnungsebenen, ebenen, save_z=True, save_e=True):
|
||||||
print("[EBENEN] _apply START z={} e={} (save_z={} save_e={})".format(
|
print("[EBENEN] _apply START z={} e={} (save_z={} save_e={})".format(
|
||||||
len(zeichnungsebenen) if zeichnungsebenen else 0,
|
len(zeichnungsebenen) if zeichnungsebenen else 0,
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
import { useEffect } from 'react'
|
||||||
|
import EbenenSettingsDialog from './components/EbenenSettingsDialog'
|
||||||
|
import { notifyReady } from './lib/rhinoBridge'
|
||||||
|
|
||||||
|
function bridgeSend(type, payload = {}) {
|
||||||
|
if (!window.RHINO_MODE) { console.log('[Bridge] →', type, payload); return }
|
||||||
|
const json = JSON.stringify({ type, payload })
|
||||||
|
document.title = 'RHINOMSG::' + json
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function EbenenSettingsApp() {
|
||||||
|
const initial = (typeof window !== 'undefined' && window.PANEL_PARAMS) || {}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
notifyReady()
|
||||||
|
const blockContext = (ev) => ev.preventDefault()
|
||||||
|
document.addEventListener('contextmenu', blockContext)
|
||||||
|
return () => document.removeEventListener('contextmenu', blockContext)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const ebene = initial.ebene || initial
|
||||||
|
const hatchPatterns = initial.hatchPatterns || ['Solid']
|
||||||
|
|
||||||
|
if (!ebene || typeof ebene !== 'object' || !ebene.code) {
|
||||||
|
return <div style={{ padding: 20, color: 'var(--text-muted)' }}>Keine Daten</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{
|
||||||
|
width: '100vw', height: '100vh',
|
||||||
|
background: 'var(--bg-base)',
|
||||||
|
overflow: 'hidden',
|
||||||
|
}}>
|
||||||
|
<EbenenSettingsDialog
|
||||||
|
ebene={ebene}
|
||||||
|
hatchPatterns={hatchPatterns}
|
||||||
|
onSave={(updated) => bridgeSend('SAVE', updated)}
|
||||||
|
onClose={() => bridgeSend('CANCEL', {})}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import { useEffect } from 'react'
|
||||||
|
import GeschossSettingsDialog from './components/GeschossSettingsDialog'
|
||||||
|
import { onMessage, notifyReady } from './lib/rhinoBridge'
|
||||||
|
|
||||||
|
// Inline send fuer SAVE/CANCEL — schickt direkt an Python-Bridge des
|
||||||
|
// Satelliten-Fensters.
|
||||||
|
function bridgeSend(type, payload = {}) {
|
||||||
|
if (!window.RHINO_MODE) { console.log('[Bridge] →', type, payload); return }
|
||||||
|
const json = JSON.stringify({ type, payload })
|
||||||
|
document.title = 'RHINOMSG::' + json
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function GeschossSettingsApp() {
|
||||||
|
// PANEL_PARAMS wurden von Python beim Window-Open injiziert.
|
||||||
|
const initial = (typeof window !== 'undefined' && window.PANEL_PARAMS) || {}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
notifyReady()
|
||||||
|
// Native Browser-Context-Menu unterdruecken
|
||||||
|
const blockContext = (ev) => ev.preventDefault()
|
||||||
|
document.addEventListener('contextmenu', blockContext)
|
||||||
|
return () => document.removeEventListener('contextmenu', blockContext)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
// Wenn keine Daten da sind: leeres Frame
|
||||||
|
if (!initial || typeof initial !== 'object') {
|
||||||
|
return <div style={{ padding: 20, color: 'var(--text-muted)' }}>Keine Daten</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{
|
||||||
|
width: '100vw', height: '100vh',
|
||||||
|
background: 'var(--bg-base)',
|
||||||
|
overflow: 'hidden',
|
||||||
|
}}>
|
||||||
|
<GeschossSettingsDialog
|
||||||
|
geschoss={initial}
|
||||||
|
onSave={(updated) => bridgeSend('SAVE', updated)}
|
||||||
|
onClose={() => bridgeSend('CANCEL', {})}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -2,8 +2,7 @@ import { useState, useRef, useMemo, useEffect } from 'react'
|
|||||||
import Icon from './Icon'
|
import Icon from './Icon'
|
||||||
import ConfirmDeleteEbene from './ConfirmDeleteEbene'
|
import ConfirmDeleteEbene from './ConfirmDeleteEbene'
|
||||||
import ContextMenu from './ContextMenu'
|
import ContextMenu from './ContextMenu'
|
||||||
import EbenenSettingsDialog from './EbenenSettingsDialog'
|
import { setLayerStyle, deleteEbene, moveSelectionToEbene, openEbenenSettings } from '../lib/rhinoBridge'
|
||||||
import { setLayerStyle, deleteEbene, moveSelectionToEbene } from '../lib/rhinoBridge'
|
|
||||||
|
|
||||||
const MODES = [
|
const MODES = [
|
||||||
{ value: 'all', label: 'Alle anzeigen' },
|
{ value: 'all', label: 'Alle anzeigen' },
|
||||||
@@ -266,7 +265,8 @@ export default function EbenenManager({
|
|||||||
const [ctxMenu, setCtxMenu] = useState(null) // { x, y, code }
|
const [ctxMenu, setCtxMenu] = useState(null) // { x, y, code }
|
||||||
const [clipboard, setClipboard] = useState(null) // { color, lw }
|
const [clipboard, setClipboard] = useState(null) // { color, lw }
|
||||||
const [autoEdit, setAutoEdit] = useState(null) // { code, field, token }
|
const [autoEdit, setAutoEdit] = useState(null) // { code, field, token }
|
||||||
const [settingsCode, setSettingsCode] = useState(null) // code der Ebene deren Einstellungen offen sind
|
// Settings-Dialog laeuft jetzt in einem echten Rhino-Fenster (Satellite-
|
||||||
|
// Window via Eto.Form + WebView). State hier nicht mehr noetig.
|
||||||
|
|
||||||
// Bei Auto-Edit: in View scrollen
|
// Bei Auto-Edit: in View scrollen
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -409,7 +409,10 @@ export default function EbenenManager({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const ctxItems = (code) => [
|
const ctxItems = (code) => [
|
||||||
{ label: 'Ebeneneinstellungen…', icon: 'settings', onClick: () => setSettingsCode(code) },
|
{ label: 'Ebeneneinstellungen…', icon: 'settings', onClick: () => {
|
||||||
|
const target = ebenen.find(e => e.code === code)
|
||||||
|
if (target) openEbenenSettings(target, hatchPatterns)
|
||||||
|
} },
|
||||||
{ divider: true },
|
{ divider: true },
|
||||||
{ label: 'Selektion hierher übertragen', icon: 'move_down', onClick: () => moveSelectionToEbene(code) },
|
{ label: 'Selektion hierher übertragen', icon: 'move_down', onClick: () => moveSelectionToEbene(code) },
|
||||||
{ divider: true },
|
{ divider: true },
|
||||||
@@ -583,22 +586,6 @@ export default function EbenenManager({
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{settingsCode && (() => {
|
|
||||||
const target = ebenen.find(e => e.code === settingsCode)
|
|
||||||
if (!target) { setSettingsCode(null); return null }
|
|
||||||
return (
|
|
||||||
<EbenenSettingsDialog
|
|
||||||
ebene={target}
|
|
||||||
hatchPatterns={hatchPatterns}
|
|
||||||
onSave={(updated) => {
|
|
||||||
// Code-Wechsel handhaben (eindeutiger key)
|
|
||||||
onChange(ebenen.map(e => e.code === settingsCode ? updated : e))
|
|
||||||
setSettingsCode(null)
|
|
||||||
}}
|
|
||||||
onClose={() => setSettingsCode(null)}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
})()}
|
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import Icon from './Icon'
|
import Icon from './Icon'
|
||||||
import GeschossDialog from './GeschossDialog'
|
import GeschossDialog from './GeschossDialog'
|
||||||
import GeschossSettingsDialog from './GeschossSettingsDialog'
|
import { openGeschossSettings } from '../lib/rhinoBridge'
|
||||||
|
|
||||||
function GeschossBadge({ name }) {
|
function GeschossBadge({ name }) {
|
||||||
return <span className="chip chip-info">{name}</span>
|
return <span className="chip chip-info">{name}</span>
|
||||||
@@ -86,7 +86,6 @@ export default function GeschossManager({
|
|||||||
mode, onModeChange,
|
mode, onModeChange,
|
||||||
}) {
|
}) {
|
||||||
const [dialogOpen, setDialogOpen] = useState(false)
|
const [dialogOpen, setDialogOpen] = useState(false)
|
||||||
const [settingsFor, setSettingsFor] = useState(null) // Geschoss-Objekt oder null
|
|
||||||
|
|
||||||
const sorted = [...zeichnungsebenen].reverse()
|
const sorted = [...zeichnungsebenen].reverse()
|
||||||
const gesamthoehe = zeichnungsebenen
|
const gesamthoehe = zeichnungsebenen
|
||||||
@@ -162,7 +161,7 @@ export default function GeschossManager({
|
|||||||
mode={mode}
|
mode={mode}
|
||||||
onClick={() => onActiveChange(z.id)}
|
onClick={() => onActiveChange(z.id)}
|
||||||
onToggleVisible={() => toggleVisible(z.id)}
|
onToggleVisible={() => toggleVisible(z.id)}
|
||||||
onSettings={() => setSettingsFor(z)}
|
onSettings={() => openGeschossSettings(z)}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
@@ -176,16 +175,6 @@ export default function GeschossManager({
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{settingsFor && (
|
|
||||||
<GeschossSettingsDialog
|
|
||||||
geschoss={settingsFor}
|
|
||||||
onSave={(updated) => {
|
|
||||||
onChange(zeichnungsebenen.map(z => z.id === updated.id ? updated : z))
|
|
||||||
setSettingsFor(null)
|
|
||||||
}}
|
|
||||||
onClose={() => setSettingsFor(null)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -277,6 +277,15 @@ export function saveCombinationPreset(name, layers) { send('SAVE_PRESET', { na
|
|||||||
export function saveCurrentAsCombination(name) { send('SAVE_CURRENT_AS_PRESET', { name }) }
|
export function saveCurrentAsCombination(name) { send('SAVE_CURRENT_AS_PRESET', { name }) }
|
||||||
export function deleteCombinationPreset(name) { send('DELETE_PRESET', { name }) }
|
export function deleteCombinationPreset(name) { send('DELETE_PRESET', { name }) }
|
||||||
|
|
||||||
|
// Satelliten-Fenster oeffnen — Python oeffnet ein echtes Rhino-Fenster
|
||||||
|
// (Eto.Form mit eingebetteter WebView) mit dem Settings-Dialog.
|
||||||
|
export function openGeschossSettings(geschoss) {
|
||||||
|
send('OPEN_GESCHOSS_SETTINGS', { geschoss })
|
||||||
|
}
|
||||||
|
export function openEbenenSettings(ebene, hatchPatterns) {
|
||||||
|
send('OPEN_EBENEN_SETTINGS', { ebene, hatchPatterns })
|
||||||
|
}
|
||||||
|
|
||||||
export function applyVisibility(activeZ, zeichnungsebenen, activeCode, ebenen, zMode, eMode) {
|
export function applyVisibility(activeZ, zeichnungsebenen, activeCode, ebenen, zMode, eMode) {
|
||||||
// Split-Panels koennen mit null/[] fuer fremde Slice aufrufen — Backend
|
// Split-Panels koennen mit null/[] fuer fremde Slice aufrufen — Backend
|
||||||
// fuellt aus doc.Strings. Hier robust gegen alles Falsy.
|
// fuellt aus doc.Strings. Hier robust gegen alles Falsy.
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ import { createRoot } from 'react-dom/client'
|
|||||||
import './index.css'
|
import './index.css'
|
||||||
import App from './App.jsx'
|
import App from './App.jsx'
|
||||||
import ZeichnungsebenenApp from './ZeichnungsebenenApp.jsx'
|
import ZeichnungsebenenApp from './ZeichnungsebenenApp.jsx'
|
||||||
|
import GeschossSettingsApp from './GeschossSettingsApp.jsx'
|
||||||
|
import EbenenSettingsApp from './EbenenSettingsApp.jsx'
|
||||||
import GestaltungApp from './GestaltungApp.jsx'
|
import GestaltungApp from './GestaltungApp.jsx'
|
||||||
import AusschnitteApp from './AusschnitteApp.jsx'
|
import AusschnitteApp from './AusschnitteApp.jsx'
|
||||||
import MassstabApp from './MassstabApp.jsx'
|
import MassstabApp from './MassstabApp.jsx'
|
||||||
@@ -24,6 +26,8 @@ const RootApp = mode === 'gestaltung' ? GestaltungApp
|
|||||||
: mode === 'layouts' ? LayoutsApp
|
: mode === 'layouts' ? LayoutsApp
|
||||||
: mode === 'elemente' ? ElementeApp
|
: mode === 'elemente' ? ElementeApp
|
||||||
: mode === 'zeichnungsebenen' ? ZeichnungsebenenApp
|
: mode === 'zeichnungsebenen' ? ZeichnungsebenenApp
|
||||||
|
: mode === 'geschoss_settings' ? GeschossSettingsApp
|
||||||
|
: mode === 'ebenen_settings' ? EbenenSettingsApp
|
||||||
: App
|
: App
|
||||||
|
|
||||||
window.onerror = function (msg, src, line, col, err) {
|
window.onerror = function (msg, src, line, col, err) {
|
||||||
|
|||||||
Reference in New Issue
Block a user