Text-Block: Bug-Fix + Vectorworks-Style + Edit-Selected
User-Probleme: 1. Floating Eto-Dialog erschien nicht nach GetPoint (Mac-Bug) 2. UI war zu klobig, sollte vectorworks-mässig kompakt sein, "+" als kleines Icon-Symbol 3. Selektierten Text aendern war nicht moeglich Fix 1 — Bug: _floating_input geloescht, ersetzt durch _prompt_for_text() das Rhino.UI.Dialogs.ShowEditBox benutzt. Nativer cross-platform Dialog ohne Eto-Modal-Bug. Workflow: GetPoint → ShowEditBox → AddText. Funktioniert auf Mac. Fix 2 — UI: Text-Block kompakt umgebaut. Reihe 1: [Font ▼] | [Size m] (130 + 60 = 196px) Reihe 2: [B][I][+] segmented pill (gleiche Breite wie Reihe 1) "+" ist jetzt kleines add-icon (size 13), kein "+ Text" Label mehr. Fix 3 — Edit-Selection: neue Funktionen in text_create.py: - _selected_text_objects(doc) → Liste der selektierten TextEntities - read_selection_settings(doc) → Settings der ersten Selektion - apply_settings_to_selection(doc, patch) → wendet font/size/bold/italic auf alle selektierten TextEntities an oberleiste.SET_TEXT_SETTINGS handler appliziert die Aenderung jetzt ZUSAETZLICH auf die Selection (wenn vorhanden) — UND speichert als Default. State enthaelt textSelectionSettings, UI nutzt diese als Anzeige-Werte wenn vorhanden (Werte spiegeln Selektion live). Visual: Border-Color der Size/Segmented-Pill wird accent wenn Selection aktiv ist (Hinweis dass Aenderungen auf Selektion wirken). Sig-Update: textSelectionSettings + lastSetView in last_state_sig damit State neu gepusht wird wenn sich Selection/View aendert. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
+67
-54
@@ -272,6 +272,7 @@ export default function OberleisteApp() {
|
||||
layerCombinations: [], layerCombinationActive: null,
|
||||
massePresets: [], masseActiveId: null,
|
||||
textSettings: { font: 'Helvetica', size: 0.20, bold: false, italic: false },
|
||||
textSelectionSettings: null,
|
||||
textFonts: [],
|
||||
northAngle: 0,
|
||||
lastSetView: null,
|
||||
@@ -779,39 +780,46 @@ export default function OberleisteApp() {
|
||||
|
||||
<div style={sep} />
|
||||
|
||||
{/* ====== TEXT-Block 2-Reihen ======
|
||||
Reihe 1: Font-Dropdown + Groesse (mm)
|
||||
Reihe 2: B/I-Toggles + "Text einfuegen"-Button
|
||||
{/* ====== TEXT-Block (Vectorworks-Stil) ======
|
||||
Reihe 1: Font-Dropdown | Size + "m"
|
||||
Reihe 2: B/I/+ kompakte Segmented-Pill
|
||||
Wenn TextEntity selektiert: Werte spiegeln Selektion, Aenderungen
|
||||
applizieren AUF die Selektion + speichern als Default.
|
||||
*/}
|
||||
{(() => {
|
||||
const ts = state.textSettings || {}
|
||||
const sel = state.textSelectionSettings // null wenn nichts selektiert
|
||||
const ts = sel || state.textSettings || {}
|
||||
const fonts = state.textFonts || []
|
||||
const updateTs = (patch) => setTextSettings({ ...ts, ...patch })
|
||||
const TEXT_W = 150
|
||||
const FONT_W = 130
|
||||
const SIZE_W = 60
|
||||
const SEG_W = FONT_W + 6 + SIZE_W // B/I/+ Pill spannt ueber beide Spalten in Row 2
|
||||
const ACTIVE_BORDER = sel ? 'var(--accent)' : 'var(--border)'
|
||||
return (
|
||||
<div style={{
|
||||
display: 'grid', gridTemplateColumns: 'auto auto', gap: '4px 6px',
|
||||
alignItems: 'center', flexShrink: 0,
|
||||
display: 'grid', gridTemplateColumns: `${FONT_W}px ${SIZE_W}px`,
|
||||
gap: '4px 6px', alignItems: 'center', flexShrink: 0,
|
||||
}}>
|
||||
{/* Reihe 1, Spalte 1: Font-Dropdown */}
|
||||
<BarCombo
|
||||
icon="text_fields"
|
||||
value={ts.font || ''}
|
||||
onChange={(v) => updateTs({ font: v })}
|
||||
width={TEXT_W}
|
||||
title="Schriftart"
|
||||
width={FONT_W}
|
||||
title={sel ? 'Schriftart (auf Selektion appliziert)' : 'Schriftart'}
|
||||
>
|
||||
{fonts.length === 0 && <option value="">—</option>}
|
||||
{fonts.map(f => <option key={f} value={f}>{f}</option>)}
|
||||
</BarCombo>
|
||||
{/* Reihe 1, Spalte 2: Groesse (mm) */}
|
||||
{/* Reihe 1, Spalte 2: Size */}
|
||||
<div style={{
|
||||
display: 'inline-flex', alignItems: 'center', gap: 4,
|
||||
height: BAR_H, padding: '0 10px',
|
||||
display: 'inline-flex', alignItems: 'center', gap: 3,
|
||||
height: BAR_H + 2, padding: '0 8px', boxSizing: 'border-box',
|
||||
background: 'var(--bg-input)',
|
||||
border: '1px solid var(--border)',
|
||||
border: '1px solid ' + ACTIVE_BORDER,
|
||||
borderRadius: 999,
|
||||
flexShrink: 0, width: 90,
|
||||
flexShrink: 0, width: SIZE_W,
|
||||
transition: 'border-color 0.15s',
|
||||
}}>
|
||||
<input
|
||||
type="number" step="0.01" min="0.01"
|
||||
@@ -825,19 +833,22 @@ export default function OberleisteApp() {
|
||||
background: 'transparent', border: 'none', outline: 'none',
|
||||
color: 'var(--text-primary)',
|
||||
fontSize: 11, fontFamily: 'DM Mono, monospace',
|
||||
padding: 0, textAlign: 'right',
|
||||
appearance: 'auto',
|
||||
padding: 0, textAlign: 'right', appearance: 'auto',
|
||||
}}
|
||||
title="Texthoehe in Model-Units"
|
||||
title={sel ? 'Texthoehe (auf Selektion appliziert)' : 'Texthoehe (m)'}
|
||||
/>
|
||||
<span style={{ fontSize: 9, color: 'var(--text-muted)' }}>m</span>
|
||||
</div>
|
||||
{/* Reihe 2, Spalte 1: B/I-Toggles */}
|
||||
{/* Reihe 2: B / I / + in einem Segmented-Pill */}
|
||||
<div style={{
|
||||
display: 'inline-flex',
|
||||
border: '1px solid var(--border)', borderRadius: 999,
|
||||
overflow: 'hidden', flexShrink: 0, width: TEXT_W,
|
||||
gridColumn: '1 / span 2',
|
||||
display: 'inline-flex', justifySelf: 'start',
|
||||
border: '1px solid ' + ACTIVE_BORDER,
|
||||
borderRadius: 999, overflow: 'hidden', flexShrink: 0,
|
||||
width: SEG_W, height: BAR_H + 2, boxSizing: 'border-box',
|
||||
transition: 'border-color 0.15s',
|
||||
}}>
|
||||
{/* B */}
|
||||
<button
|
||||
onClick={() => updateTs({ bold: !ts.bold })}
|
||||
onMouseEnter={(e) => {
|
||||
@@ -851,15 +862,18 @@ export default function OberleisteApp() {
|
||||
e.currentTarget.style.color = 'var(--text-primary)'
|
||||
}}
|
||||
style={{
|
||||
flex: 1, height: BAR_H,
|
||||
flex: 1, height: '100%',
|
||||
background: ts.bold ? 'var(--accent)' : 'var(--bg-input)',
|
||||
color: ts.bold ? 'var(--bg-panel)' : 'var(--text-primary)',
|
||||
border: 'none', cursor: 'pointer',
|
||||
fontWeight: 700, fontSize: 11,
|
||||
appearance: 'none', WebkitAppearance: 'none',
|
||||
lineHeight: 1, padding: 0,
|
||||
transition: 'background 0.15s, color 0.15s',
|
||||
}}
|
||||
title="Fett"
|
||||
title={(sel ? 'Fett auf Selektion' : 'Fett') + ' (Default)'}
|
||||
>B</button>
|
||||
{/* I */}
|
||||
<button
|
||||
onClick={() => updateTs({ italic: !ts.italic })}
|
||||
onMouseEnter={(e) => {
|
||||
@@ -873,46 +887,45 @@ export default function OberleisteApp() {
|
||||
e.currentTarget.style.color = 'var(--text-primary)'
|
||||
}}
|
||||
style={{
|
||||
flex: 1, height: BAR_H,
|
||||
flex: 1, height: '100%',
|
||||
background: ts.italic ? 'var(--accent)' : 'var(--bg-input)',
|
||||
color: ts.italic ? 'var(--bg-panel)' : 'var(--text-primary)',
|
||||
border: 'none', borderLeft: '1px solid var(--border)',
|
||||
cursor: 'pointer',
|
||||
fontStyle: 'italic', fontSize: 11,
|
||||
appearance: 'none', WebkitAppearance: 'none',
|
||||
lineHeight: 1, padding: 0,
|
||||
transition: 'background 0.15s, color 0.15s',
|
||||
}}
|
||||
title="Kursiv"
|
||||
title={(sel ? 'Kursiv auf Selektion' : 'Kursiv') + ' (Default)'}
|
||||
>I</button>
|
||||
{/* + Neuen Text einfuegen */}
|
||||
<button
|
||||
onClick={() => createText()}
|
||||
onMouseEnter={(e) => {
|
||||
e.currentTarget.style.background = 'var(--bg-item-hover)'
|
||||
e.currentTarget.style.color = 'var(--accent-light)'
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
e.currentTarget.style.background = 'var(--bg-input)'
|
||||
e.currentTarget.style.color = 'var(--text-primary)'
|
||||
}}
|
||||
style={{
|
||||
flex: 1, height: '100%',
|
||||
background: 'var(--bg-input)',
|
||||
color: 'var(--text-primary)',
|
||||
border: 'none', borderLeft: '1px solid var(--border)',
|
||||
cursor: 'pointer',
|
||||
appearance: 'none', WebkitAppearance: 'none',
|
||||
lineHeight: 1, padding: 0,
|
||||
display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
|
||||
transition: 'background 0.15s, color 0.15s',
|
||||
}}
|
||||
title="Neuen Text einfuegen — Position picken, Text eingeben"
|
||||
>
|
||||
<Icon name="add" size={13} />
|
||||
</button>
|
||||
</div>
|
||||
{/* Reihe 2, Spalte 2: "Text einfuegen" Button */}
|
||||
<button
|
||||
onClick={() => createText()}
|
||||
onMouseEnter={(e) => {
|
||||
e.currentTarget.style.background = 'var(--bg-item-hover)'
|
||||
e.currentTarget.style.borderColor = 'var(--accent)'
|
||||
e.currentTarget.style.color = 'var(--accent-light)'
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
e.currentTarget.style.background = 'var(--bg-input)'
|
||||
e.currentTarget.style.borderColor = 'var(--border)'
|
||||
e.currentTarget.style.color = 'var(--text-primary)'
|
||||
}}
|
||||
style={{
|
||||
width: 90, height: BAR_H,
|
||||
background: 'var(--bg-input)',
|
||||
color: 'var(--text-primary)',
|
||||
border: '1px solid var(--border)',
|
||||
borderRadius: 999,
|
||||
display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
|
||||
gap: 4, cursor: 'pointer',
|
||||
fontSize: 11, fontWeight: 500,
|
||||
transition: 'background 0.15s, color 0.15s, border-color 0.15s',
|
||||
}}
|
||||
title="Position picken → Text tippen → Enter"
|
||||
>
|
||||
<Icon name="add" size={12} />
|
||||
Text
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
})()}
|
||||
|
||||
Reference in New Issue
Block a user