diff --git a/rhino/dimensionen.py b/rhino/dimensionen.py index 9fbb009..bf6b610 100644 --- a/rhino/dimensionen.py +++ b/rhino/dimensionen.py @@ -20,7 +20,6 @@ if _HERE not in sys.path: sys.path.insert(0, _HERE) import panel_base -import mass_style PANEL_GUID_STR = "9e3c8c5d-6d4a-4f3e-b3c5-d4e5f6071a2c" @@ -343,34 +342,6 @@ class DimensionenBridge(panel_base.BaseBridge): elif t == "SET_CIRCLE_RADIUS":self._set_circle_radius(p) elif t == "SET_LINE_LENGTH": self._set_line_length(p) elif t == "SET_RECTANGLE": self._set_rectangle(p) - elif t == "MASS_STYLE_SET_ACTIVE": - mass_style.set_active_id(Rhino.RhinoDoc.ActiveDoc, p.get("id")) - self._send_state(force=True) - self._broadcast_raum_regen() - elif t == "MASS_STYLE_SAVE": - mass_style.save_preset(Rhino.RhinoDoc.ActiveDoc, p.get("preset") or {}) - self._send_state(force=True) - self._broadcast_raum_regen() - elif t == "MASS_STYLE_DELETE": - mass_style.delete_preset(Rhino.RhinoDoc.ActiveDoc, p.get("id")) - self._send_state(force=True) - self._broadcast_raum_regen() - - def _broadcast_raum_regen(self): - """Beim Preset-Wechsel: alle Raeume regen damit die Stempel-Flaechen - in der neuen Default-Rundung erscheinen. Eingehaengt in elemente.""" - try: - import elemente - doc = Rhino.RhinoDoc.ActiveDoc - if doc is None: return - for obj in doc.Objects: - try: - m = elemente._read_meta(obj) - if m and m.get("type") == "raum_outline": - elemente._queue_regen(m["id"]) - except Exception: pass - except Exception as ex: - print("[DIMENSIONEN] mass_style raum-regen:", ex) # --- State-Snapshot ----------------------------------------------------- @@ -405,8 +376,6 @@ class DimensionenBridge(panel_base.BaseBridge): "refPoint": self._ref, "coordSystem": self._coord_sys, "planeName": "CPlane" if self._coord_sys == "cplane" else "Welt", - "massStyles": mass_style.list_presets(doc), - "massStyleActive": mass_style.get_active_id(doc), } shape = _detect_shape(objs) out["shape"] = shape @@ -445,8 +414,6 @@ class DimensionenBridge(panel_base.BaseBridge): tuple(sorted((state.get("position") or {}).items())), tuple(sorted((state.get("dimensions") or {}).items())), tuple(sorted((state.get("shape") or {}).items())) if state.get("shape") else None, - state.get("massStyleActive"), - len(state.get("massStyles") or []), ) if not force and sig == self._last_sig: return diff --git a/rhino/mass_style.py b/rhino/mass_style.py index e2bf189..ba99852 100644 --- a/rhino/mass_style.py +++ b/rhino/mass_style.py @@ -186,3 +186,23 @@ def dim_dezimalstellen_default(doc): def dim_einheit_default(doc): p = get_active(doc) return p["dimEinheit"] if p else "m" + + +# --------------------------------------------------------------------------- +# Cross-Module: Raum-Stempel beim Preset-Wechsel mit-aktualisieren. + +def regen_all_rooms(doc): + """Queued ein Regen fuer ALLE raum_outline-Objekte. Aufruf bei Preset- + Wechsel/Save/Delete damit Stempel-Flaechen in der neuen Rundung neu + rendern.""" + if doc is None: return + try: + import elemente + except Exception as ex: + print("[MASS_STYLE] elemente import:", ex); return + for obj in doc.Objects: + try: + m = elemente._read_meta(obj) + if m and m.get("type") == "raum_outline": + elemente._queue_regen(m["id"]) + except Exception: pass diff --git a/rhino/masse_settings.py b/rhino/masse_settings.py new file mode 100644 index 0000000..fadcf3a --- /dev/null +++ b/rhino/masse_settings.py @@ -0,0 +1,85 @@ +#! python 3 +# -*- coding: utf-8 -*- +""" +masse_settings.py +Satellite-Fenster fuer das Bearbeiten der Masse-Presets +(rhino/mass_style.py). Vom Oberleiste-Zahnrad geoeffnet. +""" +import os +import sys +import Rhino +import scriptcontext as sc + +_HERE = os.path.dirname(os.path.abspath(__file__)) +if _HERE not in sys.path: + sys.path.insert(0, _HERE) + +import panel_base +import mass_style + + +def _payload(): + doc = Rhino.RhinoDoc.ActiveDoc + return { + "presets": mass_style.list_presets(doc), + "activeId": mass_style.get_active_id(doc), + } + + +def _notify_oberleiste(): + """Topbar nach Aenderung refreshen — die zeigt den aktiven Preset im + Dropdown an.""" + try: + b = sc.sticky.get("oberleiste_bridge") + if b is not None: + b._send_state(force=True) + except Exception as ex: + print("[MASSE_SETTINGS] notify oberleiste:", ex) + + +class MasseSettingsBridge(panel_base.BaseBridge): + def __init__(self): + panel_base.BaseBridge.__init__(self, "masse_settings") + + def _on_ready(self): + self._send_state() + + def _send_state(self): + self.send("STATE", _payload()) + + def handle(self, data): + if not isinstance(data, dict): return + t = data.get("type", "") + p = data.get("payload") or {} + if not isinstance(p, dict): p = {} + doc = Rhino.RhinoDoc.ActiveDoc + + if t == "READY" or t == "REQUEST_STATE": + self._on_ready() + elif t == "SET_ACTIVE": + mass_style.set_active_id(doc, p.get("id")) + mass_style.regen_all_rooms(doc) + self._send_state() + _notify_oberleiste() + elif t == "SAVE": + mass_style.save_preset(doc, p.get("preset") or {}) + mass_style.regen_all_rooms(doc) + self._send_state() + _notify_oberleiste() + elif t == "DELETE": + mass_style.delete_preset(doc, p.get("id")) + mass_style.regen_all_rooms(doc) + self._send_state() + _notify_oberleiste() + + +def open_as_window(): + """Oeffnet das Masse-Settings-Fenster (Eto.Form + WebView). + Vom Oberleiste-Zahnrad bei OPEN_MASSE_SETTINGS aufgerufen.""" + b = MasseSettingsBridge() + sc.sticky["masse_settings_bridge"] = b + panel_base.open_satellite_window( + "masse_settings", + title="Masse", + size=(440, 520), + bridge=b) diff --git a/rhino/oberleiste.py b/rhino/oberleiste.py index 643f5fd..4050b39 100644 --- a/rhino/oberleiste.py +++ b/rhino/oberleiste.py @@ -898,6 +898,23 @@ class OberleisteBridge(panel_base.BaseBridge): except Exception as ex: print("[OBERLEISTE] open kamera:", ex) + # --- Masse (Mass-Style) ----------------------------------------- + elif t == "SET_MASSE_ACTIVE": + try: + import mass_style + doc = Rhino.RhinoDoc.ActiveDoc + mass_style.set_active_id(doc, p.get("id")) + mass_style.regen_all_rooms(doc) + except Exception as ex: + print("[OBERLEISTE] masse active:", ex) + self._send_state(force=True) + elif t == "OPEN_MASSE_SETTINGS": + try: + import masse_settings + masse_settings.open_as_window() + except Exception as ex: + print("[OBERLEISTE] open masse:", ex) + # --- Display-Mode ----------------------------------------------- elif t == "SET_DISPLAY_MODE": n = p.get("name") @@ -1119,6 +1136,14 @@ class OberleisteBridge(panel_base.BaseBridge): _names_tuple, _active_comb = self._cached_combinations info["layerCombinations"] = list(_names_tuple) info["layerCombinationActive"] = _active_comb + # Masse (Mass-Style Presets) — Liste fuer Topbar-Dropdown + aktive ID + try: + import mass_style + info["massePresets"] = mass_style.list_presets(doc) + info["masseActiveId"] = mass_style.get_active_id(doc) + except Exception: + info["massePresets"] = [] + info["masseActiveId"] = None # Command-Line State prompt = _get_command_prompt() info["cmdPrompt"] = prompt @@ -1147,6 +1172,8 @@ class OberleisteBridge(panel_base.BaseBridge): info.get("overridesActivePreset"), tuple(info.get("overridesPresets") or ()), _names_tuple, _active_comb, + info.get("masseActiveId"), + tuple((p.get("id"), p.get("name")) for p in (info.get("massePresets") or [])), prompt, ) if not force and sig == self._last_state_sig: diff --git a/src/DimensionenApp.jsx b/src/DimensionenApp.jsx index 465f5e3..f4e9290 100644 --- a/src/DimensionenApp.jsx +++ b/src/DimensionenApp.jsx @@ -5,7 +5,6 @@ import { setRefPoint, setCoordSystem, setDimPosition, setDimDimension, setDimRotationZ, setCircleRadius, setLineLength, setRectangleDims, - setMassStyleActive, saveMassStyle, deleteMassStyle, } from './lib/rhinoBridge' // ---- Helpers -------------------------------------------------------------- @@ -141,149 +140,6 @@ function Field({ label, children, style }) { ) } -// ---- Mass-Style Section --------------------------------------------------- -// Globaler Preset-Picker fuer Raum-Rundung + Mass-Linien-Dezimalstellen. -// Hostet hier weil das thematisch zu Dimensionen passt, der Preset wird aber -// dokument-weit angewendet (Raum-Stempel lesen ihn auch). - -const RAUM_RUNDUNGS_LABELS = { - 'exakt': 'Exakt (2 Nachk.)', - '0.01': 'auf 0.01 m²', - '0.1': 'auf 0.1 m²', - '0.5': 'auf 0.5 m²', - '1': 'auf 1 m²', -} - -function MassStyleSection({ massStyles, activeId }) { - const styles = Array.isArray(massStyles) ? massStyles : [] - const active = styles.find(p => p.id === activeId) || styles[0] - const update = (patch) => { - if (!active) return - saveMassStyle({ ...active, ...patch }) - } - const addNew = () => { - const name = (window.prompt('Name für neuen Mass-Style:') || '').trim() - if (!name) return - // Aktuelle Werte als Vorlage uebernehmen (oder Defaults) - const tmpl = active || { raumRundung: '0.1', dimDezimalstellen: 2, dimEinheit: 'm' } - saveMassStyle({ - name, - raumRundung: tmpl.raumRundung, - dimDezimalstellen: tmpl.dimDezimalstellen, - dimEinheit: tmpl.dimEinheit, - }) - } - const remove = () => { - if (!active) return - if (styles.length <= 1) { - window.alert('Mindestens ein Mass-Style muss erhalten bleiben.') - return - } - if (!window.confirm(`Mass-Style "${active.name}" löschen?`)) return - deleteMassStyle(active.id) - } - return ( -
-
- - - Mass-Style - -
-
- - - -
- {active && ( -
-
- - Name - - update({ name: e.target.value })} - style={{ flex: 1, fontSize: 11, padding: '2px 6px' }} - /> -
-
- - Raum-Rundung - - -
-
- - Mass-Dezimalstellen - - -
-
- - Mass-Einheit - - -
-
- )} -
- ) -} - - // ---- Hauptkomponente ------------------------------------------------------ export default function DimensionenApp() { @@ -339,11 +195,6 @@ export default function DimensionenApp() { }}>
- - {/* Header: Selektions-Info + World/CPlane */}
+ {label} +
{children}
+
+ ) +} + +export default function MasseSettingsApp() { + const [presets, setPresets] = useState([]) + const [activeId, setActiveId] = useState(null) + + useEffect(() => { + onMessage('STATE', (s) => { + setPresets(s.presets || []) + setActiveId(s.activeId || null) + }) + notifyReady() + }, []) + + const active = presets.find(p => p.id === activeId) || presets[0] + const update = (patch) => { + if (!active) return + savePreset({ ...active, ...patch }) + } + const addNew = () => { + const name = (window.prompt('Name für neues Mass:') || '').trim() + if (!name) return + const tmpl = active || { raumRundung: '0.1', dimDezimalstellen: 2, dimEinheit: 'm' } + savePreset({ + name, + raumRundung: tmpl.raumRundung, + dimDezimalstellen: tmpl.dimDezimalstellen, + dimEinheit: tmpl.dimEinheit, + }) + } + const remove = () => { + if (!active) return + if (presets.length <= 1) { + window.alert('Mindestens ein Mass muss erhalten bleiben.') + return + } + if (!window.confirm(`Mass "${active.name}" löschen?`)) return + deletePreset(active.id) + } + + return ( +
+ {/* Header */} +
+ + Masse + + Globale Vorgaben für Raum-Rundung und Mass-Linien + +
+ + {/* Picker + Aktionen */} + +
+ + + +
+
+ + {active && ( +
+ + update({ name: e.target.value })} + style={{ width: '100%', fontSize: 12, padding: '4px 8px' }} + /> + + + + + + + + + + + + + +
+ )} + +
+ Änderungen werden sofort auf alle Räume angewendet. Pro-Raum gesetzte + Rundungen (im Element-Panel) haben Vorrang vor dem aktiven Mass. + Mass-Linien-Anwendung kommt im nächsten Schritt. +
+
+ ) +} diff --git a/src/OberleisteApp.jsx b/src/OberleisteApp.jsx index 377ae1b..25f3420 100644 --- a/src/OberleisteApp.jsx +++ b/src/OberleisteApp.jsx @@ -10,6 +10,7 @@ import { pickLayerCombination, saveLayerCombination, deleteLayerCombination, openLayerCombinationsDialog, openDossierSettings, openKameraPanel, + setMasseActive, openMasseSettings, } from './lib/rhinoBridge' const PRESETS = [ @@ -129,6 +130,7 @@ export default function OberleisteApp() { cmdPrompt: '', cmdOptions: [], overridesActivePreset: null, overridesPresets: [], layerCombinations: [], layerCombinationActive: null, + massePresets: [], masseActiveId: null, }) const [appliedScale, setAppliedScale] = useState(null) const appliedScaleRef = useRef(null) @@ -303,6 +305,31 @@ export default function OberleisteApp() {
+ {/* ====== GRUPPE: MASSE ====== */} + Masse + + + +
+ {/* ====== GRUPPE: MASSSTAB ====== */} Massstab