Text-Editor: Selection-Preservation + per-Span Font/Size
User-Bug: Stile aendern nichts im Editor oder springen alle in eine
Zeile. Mit "Herumfummeln" partiell ge-fixed. Root-Causes:
1. Toolbar-Buttons stehlen Focus aus Editor → Selection futsch →
execCommand wirkt auf nichts. Fix: onMouseDown + preventDefault
auf B/I/U/Sup/Sub/Align (Pill akzeptiert jetzt onMouseDown prop).
2. Editor-div hat fontFamily/fontSize aus React-State → ueberschreibt
per-Span-Styles → alles sieht gleich aus. Fix: editor-div hat
statische Defaults (Helvetica 20px), per-Selection Styles wirken
ueber span-Wrapping (applyInlineStyleToSelection).
3. Newlines kollabieren (text springt auf eine Zeile). Fix:
white-space: pre-wrap auf editor-div.
4. Font/Size dropdowns: alter execCommand fontName war buggy. Neu:
applyInlineStyleToSelection('font-family', font) bzw. 'font-size'
wickelt die Selektion in ein <span style="..."> ein, neue Selection
liegt auf dem Span (Folge-Operationen wirken sauber).
5. Selection-change Event-Listener speichert die letzte Editor-Selection
in savedRangeRef. restoreSelection() vor jeder Operation stellt sie
wieder her — robust auch wenn der Focus zwischendurch weg war.
Backend (_runs_to_rtf): BASE_PX = base_size_m * 100 statt hardcoded 14.
Frontend rendert 1m = 100px, also entspricht base_size_m*100px dem
\\fs20 in RTF (= 1.0× TextEntity.TextHeight). _commit passes settings.
size mit, damit das Mapping stimmt.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
+10
-5
@@ -148,7 +148,10 @@ class TextEditorBridge(panel_base.BaseBridge):
|
||||
te.Plane = plane
|
||||
|
||||
# Rich-Text (Phase 2) wenn vorhanden + nicht-trivial, sonst Plain
|
||||
rtf = _runs_to_rtf(runs, st.get("font") or "Helvetica") if runs else None
|
||||
rtf = _runs_to_rtf(
|
||||
runs,
|
||||
st.get("font") or "Helvetica",
|
||||
base_size_m=float(st.get("size") or 0.2)) if runs else None
|
||||
applied_rtf = False
|
||||
if rtf:
|
||||
try:
|
||||
@@ -282,9 +285,11 @@ def _rtf_escape(s):
|
||||
return "".join(out)
|
||||
|
||||
|
||||
def _runs_to_rtf(runs, default_font):
|
||||
def _runs_to_rtf(runs, default_font, base_size_m=0.20):
|
||||
"""Konvertiert Format-Runs in Rhinos RTF-Dialekt. Runs ist Liste von
|
||||
dicts mit Keys text/font/bold/italic/underline/sup/sub/color."""
|
||||
dicts mit Keys text/font/bold/italic/underline/sup/sub/color/fontSizePx.
|
||||
base_size_m: TextEntity.TextHeight (in m). Frontend rendert 1m = 100px,
|
||||
also entspricht base_size_m * 100 dem "Standard" \\fs20 in RTF."""
|
||||
if not runs: return None
|
||||
# Triviale Runs (alle plain, ein Font) → kein RTF noetig
|
||||
nontrivial = False
|
||||
@@ -322,8 +327,8 @@ def _runs_to_rtf(runs, default_font):
|
||||
parts.append("}")
|
||||
parts.append("\\pard")
|
||||
|
||||
# Editor Default-Font-Size in px (siehe TextEditorApp Editor-div: 14px)
|
||||
BASE_PX = 14
|
||||
# Frontend rendert 1m = 100px; Standard-Run-Size ist base_size_m * 100
|
||||
BASE_PX = max(1.0, base_size_m * 100.0)
|
||||
for run in runs:
|
||||
codes = []
|
||||
codes.append("\\f{}".format(font_idx(run.get("font") or default_font)))
|
||||
|
||||
Reference in New Issue
Block a user