Stempelstile-Tab in ProjectSettings (Manager analog Raumstile)
Beide Stempel-Familien (Raum-Stempel + Bilanz-Stempel) sind jetzt konsistent verwaltbar in den Project-Settings. Backend: - elemente.py: DUPLICATE_STEMPEL_STIL + REORDER_STEMPEL_STILE handlers - rhinopanel.py ProjectSettings-Bridge: - stempel_stile in params (initial) - 4 neue dispatch-Handler (SAVE/DELETE/DUPLICATE/REORDER_STEMPEL_STIL) - direkter Dispatch zu elemente.load/save_stempel_stile (kein Roundtrip) - STEMPEL_STILE_UPDATED-Message nach jeder Op Frontend: - rhinoBridge.js: reorderStempelStile + duplicateStempelStil exports - ProjectSettingsDialog: - Neuer Tab "Stempelstile" parallel zu "Raumstile" - Drag-Reorder via HTML5 native, Inline-Rename, Duplicate, Delete - Mini-Preview: Header + Anzahl aktiver Show-Flags - Name wird mit Font/Bold/Italic des Stils gerendert als Vorschau
This commit is contained in:
@@ -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.
|
||||
|
||||
+106
-1
@@ -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)
|
||||
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:
|
||||
|
||||
@@ -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
|
||||
@@ -766,6 +773,7 @@ export default function ProjectSettingsDialog({
|
||||
{ key: 'hatches', label: 'Schraffuren' },
|
||||
{ key: 'symbols', label: 'Symbole' },
|
||||
{ key: 'raumstile', label: 'Raumstile' },
|
||||
{ key: 'stempelstile', label: 'Stempelstile' },
|
||||
]} active={tab} onChange={setTab} />
|
||||
|
||||
{/* Body */}
|
||||
@@ -1461,6 +1469,118 @@ export default function ProjectSettingsDialog({
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{tab === 'stempelstile' && (
|
||||
<div style={{ maxWidth: 760 }}>
|
||||
<div style={{ fontSize: 10, color: 'var(--text-muted)',
|
||||
padding: '6px 0 10px', lineHeight: 1.5 }}>
|
||||
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.
|
||||
</div>
|
||||
{stempelStile.length === 0 ? (
|
||||
<div style={{ padding: 30, textAlign: 'center',
|
||||
color: 'var(--text-muted)', fontSize: 11 }}>
|
||||
Noch keine Stempel-Stile gespeichert. Im Elemente-Panel
|
||||
einen Stempel platzieren, konfigurieren und im Stempel-
|
||||
Properties "+ Aktuelle Settings als Stil speichern".
|
||||
</div>
|
||||
) : (
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
|
||||
{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 (
|
||||
<div key={s.id}
|
||||
draggable
|
||||
onDragStart={(e) => {
|
||||
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',
|
||||
}}>
|
||||
<Icon name="drag_indicator" size={14}
|
||||
style={{ color: 'var(--text-muted)' }} />
|
||||
<input type="text" value={s.name}
|
||||
onChange={(e) => {
|
||||
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' }} />
|
||||
<span style={{ fontSize: 9,
|
||||
color: 'var(--text-muted)',
|
||||
fontFamily: 'DM Mono, monospace',
|
||||
minWidth: 140, textAlign: 'right' }}>
|
||||
{s.header ? `"${s.header}"` : '—'} · {onCount} Felder
|
||||
</span>
|
||||
<button onClick={() => duplicateStempelStil(s.id)}
|
||||
title="Duplizieren"
|
||||
style={{
|
||||
background: 'transparent', border: '1px solid var(--border)',
|
||||
borderRadius: 'var(--r)', cursor: 'pointer',
|
||||
padding: '4px 8px', fontSize: 10,
|
||||
color: 'var(--text-primary)',
|
||||
}}>
|
||||
<Icon name="content_copy" size={11} />
|
||||
</button>
|
||||
<button onClick={() => {
|
||||
if (window.confirm(`Stil "${s.name}" löschen?`))
|
||||
deleteStempelStil(s.id)
|
||||
}}
|
||||
title="Löschen"
|
||||
style={{
|
||||
background: 'transparent', border: '1px solid var(--border)',
|
||||
borderRadius: 'var(--r)', cursor: 'pointer',
|
||||
padding: '4px 8px', fontSize: 10,
|
||||
color: 'var(--text-danger, #c44)',
|
||||
}}>
|
||||
<Icon name="delete" size={11} />
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Footer — Pill-Buttons */}
|
||||
|
||||
@@ -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', {
|
||||
|
||||
Reference in New Issue
Block a user