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:
+14
-3
@@ -931,8 +931,12 @@ class OberleisteBridge(panel_base.BaseBridge):
|
|||||||
elif t == "SET_TEXT_SETTINGS":
|
elif t == "SET_TEXT_SETTINGS":
|
||||||
try:
|
try:
|
||||||
import text_create
|
import text_create
|
||||||
text_create.save_settings(
|
doc = Rhino.RhinoDoc.ActiveDoc
|
||||||
Rhino.RhinoDoc.ActiveDoc, p.get("settings") or {})
|
patch = p.get("settings") or {}
|
||||||
|
text_create.save_settings(doc, patch)
|
||||||
|
# Wenn TextEntities selektiert: gleiche Aenderung direkt
|
||||||
|
# auf die selektierten Texte applizieren.
|
||||||
|
text_create.apply_settings_to_selection(doc, patch)
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
print("[OBERLEISTE] text settings:", ex)
|
print("[OBERLEISTE] text settings:", ex)
|
||||||
self._send_state(force=True)
|
self._send_state(force=True)
|
||||||
@@ -1183,15 +1187,19 @@ class OberleisteBridge(panel_base.BaseBridge):
|
|||||||
except Exception:
|
except Exception:
|
||||||
info["massePresets"] = []
|
info["massePresets"] = []
|
||||||
info["masseActiveId"] = None
|
info["masseActiveId"] = None
|
||||||
# Text-Settings + verfuegbare Fonts (Fonts nur einmal initial)
|
# Text-Settings + verfuegbare Fonts (Fonts nur einmal initial).
|
||||||
|
# Wenn TextEntity selektiert ist, deren Settings ergaenzen damit
|
||||||
|
# die UI die Werte des selektierten Textes spiegelt.
|
||||||
try:
|
try:
|
||||||
import text_create
|
import text_create
|
||||||
info["textSettings"] = text_create.load_settings(doc)
|
info["textSettings"] = text_create.load_settings(doc)
|
||||||
|
info["textSelectionSettings"] = text_create.read_selection_settings(doc)
|
||||||
if not getattr(self, "_fonts_sent", False):
|
if not getattr(self, "_fonts_sent", False):
|
||||||
info["textFonts"] = text_create.available_fonts()
|
info["textFonts"] = text_create.available_fonts()
|
||||||
self._fonts_sent = True
|
self._fonts_sent = True
|
||||||
except Exception:
|
except Exception:
|
||||||
info["textSettings"] = {}
|
info["textSettings"] = {}
|
||||||
|
info["textSelectionSettings"] = None
|
||||||
# Norden-Rotation fuer N/O/S/W-Buttons
|
# Norden-Rotation fuer N/O/S/W-Buttons
|
||||||
try:
|
try:
|
||||||
import kamera
|
import kamera
|
||||||
@@ -1230,6 +1238,9 @@ class OberleisteBridge(panel_base.BaseBridge):
|
|||||||
_names_tuple, _active_comb,
|
_names_tuple, _active_comb,
|
||||||
info.get("masseActiveId"),
|
info.get("masseActiveId"),
|
||||||
tuple((p.get("id"), p.get("name")) for p in (info.get("massePresets") or [])),
|
tuple((p.get("id"), p.get("name")) for p in (info.get("massePresets") or [])),
|
||||||
|
# textSelectionSettings: dict → tuple fuer sig-Vergleich
|
||||||
|
tuple(sorted((info.get("textSelectionSettings") or {}).items())) if info.get("textSelectionSettings") else None,
|
||||||
|
info.get("lastSetView"),
|
||||||
prompt,
|
prompt,
|
||||||
)
|
)
|
||||||
if not force and sig == self._last_state_sig:
|
if not force and sig == self._last_state_sig:
|
||||||
|
|||||||
+83
-60
@@ -72,69 +72,20 @@ def available_fonts():
|
|||||||
return ["Helvetica", "Arial", "Times New Roman", "Courier New"]
|
return ["Helvetica", "Arial", "Times New Roman", "Courier New"]
|
||||||
|
|
||||||
|
|
||||||
def _floating_input():
|
def _prompt_for_text(default=""):
|
||||||
"""Modal Eto-Dialog mit TextBox bei der Maus-Position. Returns
|
"""Nativer Rhino-Dialog fuer Text-Eingabe via ShowEditBox. Wirkt
|
||||||
eingegebenen Text oder None (Escape/leer)."""
|
cross-platform (Mac+Windows), kein Eto-Modal-Bug. Returns text oder
|
||||||
import Eto.Forms as forms
|
None bei Abbruch/leer."""
|
||||||
import Eto.Drawing as drawing
|
|
||||||
|
|
||||||
dlg = forms.Dialog()
|
|
||||||
dlg.Title = "Text"
|
|
||||||
dlg.Resizable = False
|
|
||||||
dlg.MinimumSize = drawing.Size(280, 0)
|
|
||||||
dlg.Padding = drawing.Padding(6)
|
|
||||||
|
|
||||||
tb = forms.TextBox()
|
|
||||||
tb.PlaceholderText = "Text eingeben — Enter = einfuegen, Esc = abbrechen"
|
|
||||||
tb.Font = drawing.Font("Helvetica", 13)
|
|
||||||
tb.Width = 280
|
|
||||||
|
|
||||||
result = {"text": None, "committed": False}
|
|
||||||
|
|
||||||
def on_keydown(sender, e):
|
|
||||||
if e.Key == forms.Keys.Enter:
|
|
||||||
result["text"] = tb.Text or ""
|
|
||||||
result["committed"] = True
|
|
||||||
try: dlg.Close()
|
|
||||||
except Exception: pass
|
|
||||||
e.Handled = True
|
|
||||||
elif e.Key == forms.Keys.Escape:
|
|
||||||
try: dlg.Close()
|
|
||||||
except Exception: pass
|
|
||||||
e.Handled = True
|
|
||||||
|
|
||||||
tb.KeyDown += on_keydown
|
|
||||||
|
|
||||||
layout = forms.StackLayout()
|
|
||||||
layout.Padding = drawing.Padding(0)
|
|
||||||
layout.Items.Add(tb)
|
|
||||||
dlg.Content = layout
|
|
||||||
|
|
||||||
# Bei Maus-Cursor positionieren (Position wo User gerade geklickt hat)
|
|
||||||
try:
|
try:
|
||||||
m = forms.Mouse.Position
|
rc, text = Rhino.UI.Dialogs.ShowEditBox(
|
||||||
dlg.Location = drawing.Point(int(m.X) + 10, int(m.Y) + 12)
|
"Text", "Text:", default or "", False)
|
||||||
except Exception: pass
|
if not rc: return None
|
||||||
|
text = (text or "").strip()
|
||||||
# ShowModal blockiert bis Close. Parent = Rhinos Main-Window damit
|
return text or None
|
||||||
# der Dialog ueber dem Viewport rendert (Mac).
|
|
||||||
try:
|
|
||||||
parent = Rhino.UI.RhinoEtoApp.MainWindow
|
|
||||||
except Exception:
|
|
||||||
parent = None
|
|
||||||
try:
|
|
||||||
if parent is not None:
|
|
||||||
dlg.ShowModal(parent)
|
|
||||||
else:
|
|
||||||
dlg.ShowModal()
|
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
print("[TEXT] dialog show:", ex)
|
print("[TEXT] ShowEditBox:", ex)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
if not result["committed"]: return None
|
|
||||||
txt = (result["text"] or "").strip()
|
|
||||||
return txt or None
|
|
||||||
|
|
||||||
|
|
||||||
def _apply_font(te, face, bold, italic):
|
def _apply_font(te, face, bold, italic):
|
||||||
"""Setzt Font auf TextEntity. Mehrere Fallback-Pfade fuer
|
"""Setzt Font auf TextEntity. Mehrere Fallback-Pfade fuer
|
||||||
@@ -162,6 +113,78 @@ def _apply_font(te, face, bold, italic):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def _selected_text_objects(doc):
|
||||||
|
"""Liefert Liste der selektierten TextEntity-Objekte."""
|
||||||
|
out = []
|
||||||
|
if doc is None: return out
|
||||||
|
try:
|
||||||
|
for obj in doc.Objects.GetSelectedObjects(False, False):
|
||||||
|
try:
|
||||||
|
if isinstance(obj.Geometry, rg.TextEntity):
|
||||||
|
out.append(obj)
|
||||||
|
except Exception: pass
|
||||||
|
except Exception: pass
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
def apply_settings_to_selection(doc, patch):
|
||||||
|
"""Wendet font/size/bold/italic auf alle selektierten TextEntities an.
|
||||||
|
Returns Anzahl der geaenderten Objekte."""
|
||||||
|
if doc is None or not isinstance(patch, dict): return 0
|
||||||
|
selected = _selected_text_objects(doc)
|
||||||
|
if not selected: return 0
|
||||||
|
n = 0
|
||||||
|
for obj in selected:
|
||||||
|
try:
|
||||||
|
te = obj.Geometry.Duplicate()
|
||||||
|
if "size" in patch:
|
||||||
|
try: te.TextHeight = float(patch["size"])
|
||||||
|
except Exception: pass
|
||||||
|
# Font: bei jeder Aenderung neu setzen (face+bold+italic kombiniert)
|
||||||
|
if any(k in patch for k in ("font", "bold", "italic")):
|
||||||
|
cur = te.Font
|
||||||
|
try: cur_face = cur.QuartetName if cur else "Helvetica"
|
||||||
|
except Exception: cur_face = "Helvetica"
|
||||||
|
try: cur_bold = bool(cur.Bold) if cur else False
|
||||||
|
except Exception: cur_bold = False
|
||||||
|
try: cur_italic = bool(cur.Italic) if cur else False
|
||||||
|
except Exception: cur_italic = False
|
||||||
|
face = patch.get("font") or cur_face
|
||||||
|
bold = patch["bold"] if "bold" in patch else cur_bold
|
||||||
|
italic = patch["italic"] if "italic" in patch else cur_italic
|
||||||
|
_apply_font(te, face, bool(bold), bool(italic))
|
||||||
|
doc.Objects.Replace(obj.Id, te)
|
||||||
|
n += 1
|
||||||
|
except Exception as ex:
|
||||||
|
print("[TEXT] apply selection:", ex)
|
||||||
|
if n > 0:
|
||||||
|
try: doc.Views.Redraw()
|
||||||
|
except Exception: pass
|
||||||
|
return n
|
||||||
|
|
||||||
|
|
||||||
|
def read_selection_settings(doc):
|
||||||
|
"""Wenn TextEntities selektiert: liefert die Settings des ersten als
|
||||||
|
dict (font/size/bold/italic). Sonst None."""
|
||||||
|
sel = _selected_text_objects(doc)
|
||||||
|
if not sel: return None
|
||||||
|
try:
|
||||||
|
te = sel[0].Geometry
|
||||||
|
font = te.Font
|
||||||
|
face = font.QuartetName if font else "Helvetica"
|
||||||
|
bold = bool(font.Bold) if font else False
|
||||||
|
italic = bool(font.Italic) if font else False
|
||||||
|
return {
|
||||||
|
"font": face,
|
||||||
|
"size": float(te.TextHeight),
|
||||||
|
"bold": bold,
|
||||||
|
"italic": italic,
|
||||||
|
}
|
||||||
|
except Exception as ex:
|
||||||
|
print("[TEXT] read selection:", ex)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
def create_text():
|
def create_text():
|
||||||
"""Triggered von der Oberleiste. Vollstaendiger Workflow."""
|
"""Triggered von der Oberleiste. Vollstaendiger Workflow."""
|
||||||
doc = Rhino.RhinoDoc.ActiveDoc
|
doc = Rhino.RhinoDoc.ActiveDoc
|
||||||
@@ -173,7 +196,7 @@ def create_text():
|
|||||||
if rc != Rhino.Commands.Result.Success: return
|
if rc != Rhino.Commands.Result.Success: return
|
||||||
if pt is None: return
|
if pt is None: return
|
||||||
|
|
||||||
text = _floating_input()
|
text = _prompt_for_text()
|
||||||
if not text: return
|
if not text: return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|||||||
+51
-38
@@ -272,6 +272,7 @@ export default function OberleisteApp() {
|
|||||||
layerCombinations: [], layerCombinationActive: null,
|
layerCombinations: [], layerCombinationActive: null,
|
||||||
massePresets: [], masseActiveId: null,
|
massePresets: [], masseActiveId: null,
|
||||||
textSettings: { font: 'Helvetica', size: 0.20, bold: false, italic: false },
|
textSettings: { font: 'Helvetica', size: 0.20, bold: false, italic: false },
|
||||||
|
textSelectionSettings: null,
|
||||||
textFonts: [],
|
textFonts: [],
|
||||||
northAngle: 0,
|
northAngle: 0,
|
||||||
lastSetView: null,
|
lastSetView: null,
|
||||||
@@ -779,39 +780,46 @@ export default function OberleisteApp() {
|
|||||||
|
|
||||||
<div style={sep} />
|
<div style={sep} />
|
||||||
|
|
||||||
{/* ====== TEXT-Block 2-Reihen ======
|
{/* ====== TEXT-Block (Vectorworks-Stil) ======
|
||||||
Reihe 1: Font-Dropdown + Groesse (mm)
|
Reihe 1: Font-Dropdown | Size + "m"
|
||||||
Reihe 2: B/I-Toggles + "Text einfuegen"-Button
|
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 fonts = state.textFonts || []
|
||||||
const updateTs = (patch) => setTextSettings({ ...ts, ...patch })
|
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 (
|
return (
|
||||||
<div style={{
|
<div style={{
|
||||||
display: 'grid', gridTemplateColumns: 'auto auto', gap: '4px 6px',
|
display: 'grid', gridTemplateColumns: `${FONT_W}px ${SIZE_W}px`,
|
||||||
alignItems: 'center', flexShrink: 0,
|
gap: '4px 6px', alignItems: 'center', flexShrink: 0,
|
||||||
}}>
|
}}>
|
||||||
{/* Reihe 1, Spalte 1: Font-Dropdown */}
|
{/* Reihe 1, Spalte 1: Font-Dropdown */}
|
||||||
<BarCombo
|
<BarCombo
|
||||||
icon="text_fields"
|
icon="text_fields"
|
||||||
value={ts.font || ''}
|
value={ts.font || ''}
|
||||||
onChange={(v) => updateTs({ font: v })}
|
onChange={(v) => updateTs({ font: v })}
|
||||||
width={TEXT_W}
|
width={FONT_W}
|
||||||
title="Schriftart"
|
title={sel ? 'Schriftart (auf Selektion appliziert)' : 'Schriftart'}
|
||||||
>
|
>
|
||||||
{fonts.length === 0 && <option value="">—</option>}
|
{fonts.length === 0 && <option value="">—</option>}
|
||||||
{fonts.map(f => <option key={f} value={f}>{f}</option>)}
|
{fonts.map(f => <option key={f} value={f}>{f}</option>)}
|
||||||
</BarCombo>
|
</BarCombo>
|
||||||
{/* Reihe 1, Spalte 2: Groesse (mm) */}
|
{/* Reihe 1, Spalte 2: Size */}
|
||||||
<div style={{
|
<div style={{
|
||||||
display: 'inline-flex', alignItems: 'center', gap: 4,
|
display: 'inline-flex', alignItems: 'center', gap: 3,
|
||||||
height: BAR_H, padding: '0 10px',
|
height: BAR_H + 2, padding: '0 8px', boxSizing: 'border-box',
|
||||||
background: 'var(--bg-input)',
|
background: 'var(--bg-input)',
|
||||||
border: '1px solid var(--border)',
|
border: '1px solid ' + ACTIVE_BORDER,
|
||||||
borderRadius: 999,
|
borderRadius: 999,
|
||||||
flexShrink: 0, width: 90,
|
flexShrink: 0, width: SIZE_W,
|
||||||
|
transition: 'border-color 0.15s',
|
||||||
}}>
|
}}>
|
||||||
<input
|
<input
|
||||||
type="number" step="0.01" min="0.01"
|
type="number" step="0.01" min="0.01"
|
||||||
@@ -825,19 +833,22 @@ export default function OberleisteApp() {
|
|||||||
background: 'transparent', border: 'none', outline: 'none',
|
background: 'transparent', border: 'none', outline: 'none',
|
||||||
color: 'var(--text-primary)',
|
color: 'var(--text-primary)',
|
||||||
fontSize: 11, fontFamily: 'DM Mono, monospace',
|
fontSize: 11, fontFamily: 'DM Mono, monospace',
|
||||||
padding: 0, textAlign: 'right',
|
padding: 0, textAlign: 'right', appearance: 'auto',
|
||||||
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>
|
<span style={{ fontSize: 9, color: 'var(--text-muted)' }}>m</span>
|
||||||
</div>
|
</div>
|
||||||
{/* Reihe 2, Spalte 1: B/I-Toggles */}
|
{/* Reihe 2: B / I / + in einem Segmented-Pill */}
|
||||||
<div style={{
|
<div style={{
|
||||||
display: 'inline-flex',
|
gridColumn: '1 / span 2',
|
||||||
border: '1px solid var(--border)', borderRadius: 999,
|
display: 'inline-flex', justifySelf: 'start',
|
||||||
overflow: 'hidden', flexShrink: 0, width: TEXT_W,
|
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
|
<button
|
||||||
onClick={() => updateTs({ bold: !ts.bold })}
|
onClick={() => updateTs({ bold: !ts.bold })}
|
||||||
onMouseEnter={(e) => {
|
onMouseEnter={(e) => {
|
||||||
@@ -851,15 +862,18 @@ export default function OberleisteApp() {
|
|||||||
e.currentTarget.style.color = 'var(--text-primary)'
|
e.currentTarget.style.color = 'var(--text-primary)'
|
||||||
}}
|
}}
|
||||||
style={{
|
style={{
|
||||||
flex: 1, height: BAR_H,
|
flex: 1, height: '100%',
|
||||||
background: ts.bold ? 'var(--accent)' : 'var(--bg-input)',
|
background: ts.bold ? 'var(--accent)' : 'var(--bg-input)',
|
||||||
color: ts.bold ? 'var(--bg-panel)' : 'var(--text-primary)',
|
color: ts.bold ? 'var(--bg-panel)' : 'var(--text-primary)',
|
||||||
border: 'none', cursor: 'pointer',
|
border: 'none', cursor: 'pointer',
|
||||||
fontWeight: 700, fontSize: 11,
|
fontWeight: 700, fontSize: 11,
|
||||||
|
appearance: 'none', WebkitAppearance: 'none',
|
||||||
|
lineHeight: 1, padding: 0,
|
||||||
transition: 'background 0.15s, color 0.15s',
|
transition: 'background 0.15s, color 0.15s',
|
||||||
}}
|
}}
|
||||||
title="Fett"
|
title={(sel ? 'Fett auf Selektion' : 'Fett') + ' (Default)'}
|
||||||
>B</button>
|
>B</button>
|
||||||
|
{/* I */}
|
||||||
<button
|
<button
|
||||||
onClick={() => updateTs({ italic: !ts.italic })}
|
onClick={() => updateTs({ italic: !ts.italic })}
|
||||||
onMouseEnter={(e) => {
|
onMouseEnter={(e) => {
|
||||||
@@ -873,47 +887,46 @@ export default function OberleisteApp() {
|
|||||||
e.currentTarget.style.color = 'var(--text-primary)'
|
e.currentTarget.style.color = 'var(--text-primary)'
|
||||||
}}
|
}}
|
||||||
style={{
|
style={{
|
||||||
flex: 1, height: BAR_H,
|
flex: 1, height: '100%',
|
||||||
background: ts.italic ? 'var(--accent)' : 'var(--bg-input)',
|
background: ts.italic ? 'var(--accent)' : 'var(--bg-input)',
|
||||||
color: ts.italic ? 'var(--bg-panel)' : 'var(--text-primary)',
|
color: ts.italic ? 'var(--bg-panel)' : 'var(--text-primary)',
|
||||||
border: 'none', borderLeft: '1px solid var(--border)',
|
border: 'none', borderLeft: '1px solid var(--border)',
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
fontStyle: 'italic', fontSize: 11,
|
fontStyle: 'italic', fontSize: 11,
|
||||||
|
appearance: 'none', WebkitAppearance: 'none',
|
||||||
|
lineHeight: 1, padding: 0,
|
||||||
transition: 'background 0.15s, color 0.15s',
|
transition: 'background 0.15s, color 0.15s',
|
||||||
}}
|
}}
|
||||||
title="Kursiv"
|
title={(sel ? 'Kursiv auf Selektion' : 'Kursiv') + ' (Default)'}
|
||||||
>I</button>
|
>I</button>
|
||||||
</div>
|
{/* + Neuen Text einfuegen */}
|
||||||
{/* Reihe 2, Spalte 2: "Text einfuegen" Button */}
|
|
||||||
<button
|
<button
|
||||||
onClick={() => createText()}
|
onClick={() => createText()}
|
||||||
onMouseEnter={(e) => {
|
onMouseEnter={(e) => {
|
||||||
e.currentTarget.style.background = 'var(--bg-item-hover)'
|
e.currentTarget.style.background = 'var(--bg-item-hover)'
|
||||||
e.currentTarget.style.borderColor = 'var(--accent)'
|
|
||||||
e.currentTarget.style.color = 'var(--accent-light)'
|
e.currentTarget.style.color = 'var(--accent-light)'
|
||||||
}}
|
}}
|
||||||
onMouseLeave={(e) => {
|
onMouseLeave={(e) => {
|
||||||
e.currentTarget.style.background = 'var(--bg-input)'
|
e.currentTarget.style.background = 'var(--bg-input)'
|
||||||
e.currentTarget.style.borderColor = 'var(--border)'
|
|
||||||
e.currentTarget.style.color = 'var(--text-primary)'
|
e.currentTarget.style.color = 'var(--text-primary)'
|
||||||
}}
|
}}
|
||||||
style={{
|
style={{
|
||||||
width: 90, height: BAR_H,
|
flex: 1, height: '100%',
|
||||||
background: 'var(--bg-input)',
|
background: 'var(--bg-input)',
|
||||||
color: 'var(--text-primary)',
|
color: 'var(--text-primary)',
|
||||||
border: '1px solid var(--border)',
|
border: 'none', borderLeft: '1px solid var(--border)',
|
||||||
borderRadius: 999,
|
cursor: 'pointer',
|
||||||
|
appearance: 'none', WebkitAppearance: 'none',
|
||||||
|
lineHeight: 1, padding: 0,
|
||||||
display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
|
display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
|
||||||
gap: 4, cursor: 'pointer',
|
transition: 'background 0.15s, color 0.15s',
|
||||||
fontSize: 11, fontWeight: 500,
|
|
||||||
transition: 'background 0.15s, color 0.15s, border-color 0.15s',
|
|
||||||
}}
|
}}
|
||||||
title="Position picken → Text tippen → Enter"
|
title="Neuen Text einfuegen — Position picken, Text eingeben"
|
||||||
>
|
>
|
||||||
<Icon name="add" size={12} />
|
<Icon name="add" size={13} />
|
||||||
Text
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
)
|
)
|
||||||
})()}
|
})()}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user