diff --git a/rhino/elemente.py b/rhino/elemente.py index 54cdad6..04bb32e 100644 --- a/rhino/elemente.py +++ b/rhino/elemente.py @@ -6917,6 +6917,10 @@ class ElementeBridge(panel_base.BaseBridge): self._cmd_delete_stempel_stil(p) elif t == "APPLY_STEMPEL_STIL": self._cmd_apply_stempel_stil(p) + elif t == "DUPLICATE_STEMPEL_STIL": + self._cmd_duplicate_stempel_stil(p) + elif t == "REORDER_STEMPEL_STILE": + self._cmd_reorder_stempel_stile(p) elif t == "OPEN_ELEMENTE_UEBERSICHT": try: import elemente_uebersicht @@ -9339,6 +9343,46 @@ class ElementeBridge(panel_base.BaseBridge): stil.get("name"), n)) self._send_state() + def _cmd_duplicate_stempel_stil(self, p): + doc = Rhino.RhinoDoc.ActiveDoc + if doc is None: return + sid = (p.get("id") or "").strip() + if not sid: return + stile = load_stempel_stile(doc) + src = next((s for s in stile if s.get("id") == sid), None) + if src is None: return + new_name = (p.get("newName") or "").strip() or ( + "{} (Kopie)".format(src.get("name", "Stil"))) + new_stil = dict(src) + new_stil["id"] = "stestil_" + uuid.uuid4().hex[:8] + new_stil["name"] = new_name + try: + i = next(j for j, s in enumerate(stile) if s.get("id") == sid) + stile.insert(i + 1, new_stil) + except StopIteration: + stile.append(new_stil) + save_stempel_stile(doc, stile) + print("[ELEMENTE] Stempel-Stil '{}' dupliziert -> '{}'".format( + src.get("name"), new_name)) + self._send_state() + + def _cmd_reorder_stempel_stile(self, p): + doc = Rhino.RhinoDoc.ActiveDoc + if doc is None: return + new_order = p.get("ids") or [] + if not isinstance(new_order, list) or not new_order: return + stile = load_stempel_stile(doc) + by_id = {s.get("id"): s for s in stile if s.get("id")} + ordered = []; seen = set() + for i in new_order: + if i in by_id and i not in seen: + ordered.append(by_id[i]); seen.add(i) + for s in stile: + if s.get("id") and s["id"] not in seen: + ordered.append(s); seen.add(s["id"]) + save_stempel_stile(doc, ordered) + self._send_state() + def _cmd_create_symbol(self, p): """Platziert ein Library-Item (symbol/object) im Doc. Interactive GetPoint im aktiven Viewport — User klickt Position. diff --git a/rhino/rhinopanel.py b/rhino/rhinopanel.py index d209a87..d45c9cc 100644 --- a/rhino/rhinopanel.py +++ b/rhino/rhinopanel.py @@ -961,13 +961,16 @@ class EbenenBridge(panel_base.BaseBridge): lib_root = library.library_root() except Exception: lib_items = []; lib_root = "" - # Raumstempel-Stile + Fonts aus elemente fuer den "Raumstile"-Tab + # Raumstempel-Stile + Stempel-Stile + Fonts aus elemente fuer die + # "Raumstile" und "Stempelstile" Tabs try: import elemente as _el - raum_stile = _el.load_raum_stempel_stile(doc) - fonts_list = _el._list_system_fonts() + raum_stile = _el.load_raum_stempel_stile(doc) + stempel_stile = _el.load_stempel_stile(doc) + fonts_list = _el._list_system_fonts() except Exception: raum_stile = [] + stempel_stile = [] fonts_list = [] params = { "defaults": current.get("defaults", {}), @@ -980,6 +983,7 @@ class EbenenBridge(panel_base.BaseBridge): "libraryItems": lib_items, "libraryRoot": lib_root, "raumStempelStile": raum_stile, + "stempelStile": stempel_stile, "fonts": fonts_list, } def on_save(updated): @@ -1096,6 +1100,14 @@ class EbenenBridge(panel_base.BaseBridge): self._raum_stil_duplicate(p) elif t == "REORDER_RAUM_STILE": self._raum_stil_reorder(p) + elif t == "SAVE_STEMPEL_STIL": + self._stempel_stil_save(p) + elif t == "DELETE_STEMPEL_STIL": + self._stempel_stil_delete(p) + elif t == "DUPLICATE_STEMPEL_STIL": + self._stempel_stil_duplicate(p) + elif t == "REORDER_STEMPEL_STILE": + self._stempel_stil_reorder(p) # ---- Raumstempel-Stile (Settings-Tab "Raumstile") ---- # Dispatcht direkt zu elemente.py — kein Roundtrip via Elemente- @@ -1190,6 +1202,99 @@ class EbenenBridge(panel_base.BaseBridge): except Exception as ex: print("[PROJECT-SETTINGS] raum_stil reorder:", ex) self._raum_stil_send_updated() + + # ---- Stempel-Stile (analog Raum-Stil-Pattern) ---- + def _stempel_stil_send_updated(self): + try: + import elemente as _el + d = Rhino.RhinoDoc.ActiveDoc + self.send("STEMPEL_STILE_UPDATED", + {"stempelStile": _el.load_stempel_stile(d)}) + except Exception as ex: + print("[PROJECT-SETTINGS] STEMPEL_STILE_UPDATED:", ex) + + def _stempel_stil_save(self, p): + try: + import elemente as _el + import uuid as _uuid + d = Rhino.RhinoDoc.ActiveDoc + name = (p.get("name") or "").strip() + if not name: return + sid = (p.get("id") or "").strip() or ( + "stestil_" + _uuid.uuid4().hex[:8]) + settings = p.get("settings") or {} + stile = _el.load_stempel_stile(d) + new_stil = {"id": sid, "name": name} + for f in _el._STEMPEL_STIL_FIELDS: + if f in settings: new_stil[f] = settings[f] + found = False + for i, s in enumerate(stile): + if s.get("id") == sid: + stile[i] = new_stil; found = True; break + if not found: stile.append(new_stil) + _el.save_stempel_stile(d, stile) + except Exception as ex: + print("[PROJECT-SETTINGS] stempel_stil save:", ex) + self._stempel_stil_send_updated() + + def _stempel_stil_delete(self, p): + try: + import elemente as _el + d = Rhino.RhinoDoc.ActiveDoc + sid = (p.get("id") or "").strip() + if not sid: return + stile = _el.load_stempel_stile(d) + stile = [s for s in stile if s.get("id") != sid] + _el.save_stempel_stile(d, stile) + except Exception as ex: + print("[PROJECT-SETTINGS] stempel_stil delete:", ex) + self._stempel_stil_send_updated() + + def _stempel_stil_duplicate(self, p): + try: + import elemente as _el + import uuid as _uuid + d = Rhino.RhinoDoc.ActiveDoc + sid = (p.get("id") or "").strip() + if not sid: return + stile = _el.load_stempel_stile(d) + src = next((s for s in stile if s.get("id") == sid), None) + if src is None: return + new_name = (p.get("newName") or "").strip() or ( + "{} (Kopie)".format(src.get("name", "Stil"))) + new_stil = dict(src) + new_stil["id"] = "stestil_" + _uuid.uuid4().hex[:8] + new_stil["name"] = new_name + try: + i = next(j for j, s in enumerate(stile) if s.get("id") == sid) + stile.insert(i + 1, new_stil) + except StopIteration: + stile.append(new_stil) + _el.save_stempel_stile(d, stile) + except Exception as ex: + print("[PROJECT-SETTINGS] stempel_stil duplicate:", ex) + self._stempel_stil_send_updated() + + def _stempel_stil_reorder(self, p): + try: + import elemente as _el + d = Rhino.RhinoDoc.ActiveDoc + new_order = p.get("ids") or [] + if not isinstance(new_order, list) or not new_order: return + stile = _el.load_stempel_stile(d) + by_id = {s.get("id"): s for s in stile if s.get("id")} + ordered = []; seen = set() + for i in new_order: + if i in by_id and i not in seen: + ordered.append(by_id[i]); seen.add(i) + for s in stile: + if s.get("id") and s["id"] not in seen: + ordered.append(s); seen.add(s["id"]) + _el.save_stempel_stile(d, ordered) + except Exception as ex: + print("[PROJECT-SETTINGS] stempel_stil reorder:", ex) + self._stempel_stil_send_updated() + def _pick_texture(self, payload): slot = payload.get("slot") or "diffuse" try: diff --git a/src/components/ProjectSettingsDialog.jsx b/src/components/ProjectSettingsDialog.jsx index bd48bd8..f25f1e7 100644 --- a/src/components/ProjectSettingsDialog.jsx +++ b/src/components/ProjectSettingsDialog.jsx @@ -10,6 +10,7 @@ import { listLibraryItems, addLibraryFile, updateLibraryItem, deleteLibraryItem, saveSelectionAsLibrary, saveRaumStil, deleteRaumStil, duplicateRaumStil, reorderRaumStile, + saveStempelStil, deleteStempelStil, duplicateStempelStil, reorderStempelStile, } from '../lib/rhinoBridge' /* Field — Stack-Layout fuer komplexe Inputs (mehrere Felder nebeneinander) */ @@ -651,6 +652,9 @@ export default function ProjectSettingsDialog({ // wenn Backend CRUD-Op fertig ist. Drag-Reorder lokal + commit beim Drop. const [raumStile, setRaumStile] = useState(initial.raumStempelStile || []) const [dragStilIdx, setDragStilIdx] = useState(null) + // Stempel-Stile (Bilanz-Stempel-Presets) — parallele Liste + const [stempelStile, setStempelStile] = useState(initial.stempelStile || []) + const [dragStempelIdx, setDragStempelIdx] = useState(null) const fontsList = initial.fonts || [] // Aktuell ausgewaehltes Material aus Selection ableiten @@ -691,6 +695,9 @@ export default function ProjectSettingsDialog({ onMessage('STILE_UPDATED', ({ raumStempelStile }) => { if (Array.isArray(raumStempelStile)) setRaumStile(raumStempelStile) }) + onMessage('STEMPEL_STILE_UPDATED', ({ stempelStile: ss }) => { + if (Array.isArray(ss)) setStempelStile(ss) + }) }, []) // Backend-File-Picker-Antwort: aktualisiert das Slot im aktuell @@ -760,12 +767,13 @@ export default function ProjectSettingsDialog({
{/* Body */} @@ -1461,6 +1469,118 @@ export default function ProjectSettingsDialog({ )}
)} + + {tab === 'stempelstile' && ( +
+
+ Stempel-Stile (Presets) für SIA-Bilanz-Stempel. Werden via + Stempel-Properties erstellt ("+ Aktuelle Settings als Stil + speichern"), hier nur verwaltet. Drag zum Umsortieren — + Reihenfolge entspricht dem Dropdown in den Stempel-Properties. +
+ {stempelStile.length === 0 ? ( +
+ Noch keine Stempel-Stile gespeichert. Im Elemente-Panel + einen Stempel platzieren, konfigurieren und im Stempel- + Properties "+ Aktuelle Settings als Stil speichern". +
+ ) : ( +
+ {stempelStile.map((s, idx) => { + const isDrag = dragStempelIdx === idx + // Show-Flags zaehlen fuer ein Mini-Preview + const onCount = [ + 'showHnf','showNnf','showNf','showVf','showFf', + 'showNgf','showGf','showAgf','showCount','showScope', + ].filter(k => s[k] !== false).length + return ( +
{ + setDragStempelIdx(idx) + try { e.dataTransfer.effectAllowed = 'move' } catch (_) {} + }} + onDragOver={(e) => { e.preventDefault() }} + onDrop={(e) => { + e.preventDefault() + if (dragStempelIdx == null || dragStempelIdx === idx) return + const next = [...stempelStile] + const [moved] = next.splice(dragStempelIdx, 1) + next.splice(idx, 0, moved) + setStempelStile(next) + reorderStempelStile(next.map(x => x.id)) + setDragStempelIdx(null) + }} + onDragEnd={() => setDragStempelIdx(null)} + style={{ + display: 'flex', alignItems: 'center', gap: 8, + padding: '8px 10px', + background: isDrag ? 'var(--bg-item-active)' : 'var(--bg-input)', + border: '1px solid var(--border)', + borderRadius: 'var(--r)', + cursor: 'grab', + opacity: isDrag ? 0.5 : 1, + transition: 'opacity 0.15s', + }}> + + { + const next = stempelStile.map(x => + x.id === s.id ? { ...x, name: e.target.value } : x) + setStempelStile(next) + }} + onBlur={(e) => { + const newName = (e.target.value || '').trim() + if (newName && newName !== s.name) { + const { id, name: _n, ...rest } = s + saveStempelStil(id, newName, rest) + } + }} + onKeyDown={(e) => { if (e.key === 'Enter') e.target.blur() }} + style={{ flex: 1, fontSize: 11, height: BAR_H, + padding: '0 10px', + fontFamily: s.font ? `"${s.font}", monospace` : 'inherit', + fontWeight: s.bold ? 700 : 400, + fontStyle: s.italic ? 'italic' : 'normal' }} /> + + {s.header ? `"${s.header}"` : '—'} · {onCount} Felder + + + +
+ ) + })} +
+ )} +
+ )} {/* Footer — Pill-Buttons */} diff --git a/src/lib/rhinoBridge.js b/src/lib/rhinoBridge.js index 292d6de..f37b9de 100644 --- a/src/lib/rhinoBridge.js +++ b/src/lib/rhinoBridge.js @@ -265,6 +265,10 @@ export function deleteStempelStil(id) { send('DELETE_STEMPEL_STIL', { id }) } export function applyStempelStil(stilId, ids) { send('APPLY_STEMPEL_STIL', { stilId, ids }) } +export function reorderStempelStile(ids) { send('REORDER_STEMPEL_STILE', { ids }) } +export function duplicateStempelStil(id, newName) { + send('DUPLICATE_STEMPEL_STIL', { id, newName }) +} export function setSectionStyle(enabled, source, color, pattern, scale, rotation, opts = {}) { send('SET_SECTION_STYLE', {