Text-Editor 3 echte Bugs gefixt (aus User-Log)

1. Dialog-Positionierung: vp.WorldToScreen existiert nicht auf dieser
   Rhino-Version. Ersetzt durch vp.WorldToClient → System.Drawing.Point
   (viewport-lokale Pixel) + view.ScreenRectangle fuer absolute Position.
   → Dialog poppt jetzt wirklich neben dem Frame statt random.

2. TextArea-Hoehe: DynamicLayout expandiert die TextArea nicht
   zuverlaessig (zeigte sich als 1-Zeilen-Streifen mit Buttons riesig
   daneben). Fix: ta.Size = drawing.Size(...) explizit setzen.

3. 5-arg Font(face, FontWeight, FontStyle, underline, strike): Python.NET
   3.0 erlaubt keinen bool→Enum-Cast mehr (Log: "int can not be converted
   to Enum implicitly"). Fix: echte Enums Rhino.DocObjects.Font.FontWeight.
   Bold/Normal + FontStyle.Italic/Upright benutzen. Damit funktioniert
   auch Underline-Support endlich.

apply_settings_to_selection: kompletter Rewrite — statt Duplicate-Modify
wird eine FRESH TextEntity gebaut + alle Properties (Plane, PlainText,
TextHeight, Font, Align) gesetzt + per Replace eingebunden. DimStyle
wird auf Guid.Empty entkoppelt damit nicht die Style das Font-Setting
ueberschreibt. Sollte Bold/Italic-Un-Toggle-Bug fixen.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-21 01:13:05 +02:00
parent 9eece87cc4
commit cc0e6d814e
+48 -27
View File
@@ -211,16 +211,14 @@ def _inline_editor(p1, p2, initial=""):
try:
view = Rhino.RhinoDoc.ActiveDoc.Views.ActiveView
vp = view.ActiveViewport
ok1, x1, y1 = vp.WorldToScreen(p1)
ok2, x2, y2 = vp.WorldToScreen(p2)
if ok1 and ok2:
view_rect = view.ScreenRectangle
fx = view_rect.X + min(x1, x2)
fy = view_rect.Y + min(y1, y2)
fw = abs(x2 - x1); fh = abs(y2 - y1)
# Dialog rechts neben dem Frame platzieren (oder darunter wenn
# rechts kein Platz). Falls Frame klein, Dialog hat eigene Min-
# Groesse.
# WorldToClient → System.Drawing.Point (viewport-lokale Pixel)
c1 = vp.WorldToClient(p1)
c2 = vp.WorldToClient(p2)
view_rect = view.ScreenRectangle # absolute Viewport-Position
fx = view_rect.X + int(min(c1.X, c2.X))
fy = view_rect.Y + int(min(c1.Y, c2.Y))
fw = abs(int(c2.X - c1.X))
fh = abs(int(c2.Y - c1.Y))
sx = int(fx + fw + 20)
sy = int(fy)
sw = max(360, fw)
@@ -248,6 +246,11 @@ def _inline_editor(p1, p2, initial=""):
ta.Text = initial or ""
try: ta.Font = drawing.Font("Helvetica", 13)
except Exception: pass
# Explizit Groesse setzen — DynamicLayout expandiert TextArea sonst
# nicht zuverlaessig (zeigte sich als 1-Zeilen-Streifen)
try:
ta.Size = drawing.Size(int(sw) - 20, max(120, int(sh) - 70))
except Exception: pass
result = {"text": None, "committed": False}
@@ -381,9 +384,14 @@ def _apply_font(te, face, bold, italic, underline=False):
face = face[:-len(suffix)].strip(); break
print("[TEXT] _apply_font face={!r} bold={} italic={} underline={}".format(
face, bold, italic, underline))
# Pfad 1: 5-arg Font-Konstruktor (mit underline+strikethrough)
# Pfad 1: 5-arg Font-Konstruktor mit echten Enums (Python.NET 3 erlaubt
# keinen impliziten bool→Enum-Cast mehr). Underline-Support nur hier.
try:
font = Rhino.DocObjects.Font(face, bold, italic, underline, False)
FW = Rhino.DocObjects.Font.FontWeight
FS = Rhino.DocObjects.Font.FontStyle
weight = FW.Bold if bold else FW.Normal
style = FS.Italic if italic else FS.Upright
font = Rhino.DocObjects.Font(face, weight, style, underline, False)
if font is not None:
te.Font = font
return True
@@ -454,22 +462,13 @@ def apply_settings_to_selection(doc, patch):
if doc is None or not isinstance(patch, dict): return 0
selected = _selected_text_objects(doc)
if not selected: return 0
import System
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. Vorher PlainText-
# Reset damit eventuelle RichText-Formatierungs-Runs nicht
# das neue te.Font ueberschreiben.
if any(k in patch for k in ("font", "bold", "italic", "underline")):
try:
plain = te.PlainText
te.PlainText = plain # reset zu plain-mode
except Exception: pass
cur = te.Font
old = obj.Geometry
# Aktuelle Werte lesen (vor Modifikation)
cur = old.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
@@ -478,13 +477,35 @@ def apply_settings_to_selection(doc, patch):
except Exception: cur_italic = False
try: cur_underline = bool(cur.Underlined) if cur else False
except Exception: cur_underline = False
# Neue Werte aus Patch + Fallback auf aktuell
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
underline = patch["underline"] if "underline" in patch else cur_underline
size = float(patch["size"]) if "size" in patch else float(old.TextHeight)
align = patch["align"] if patch.get("align") in _ALIGNS else None
# FRESH TextEntity bauen statt Duplicate-Modify. Bypassed
# Probleme wo te.Font-Setter wegen Rich-Text-Runs oder
# DimensionStyle-Override nicht greift.
te = rg.TextEntity()
te.Plane = old.Plane
try: te.PlainText = old.PlainText
except Exception: pass
te.TextHeight = size
# DimensionStyle entkoppeln damit unser Font nicht von Style
# ueberschrieben wird.
try: te.DimensionStyleId = System.Guid.Empty
except Exception: pass
_apply_font(te, face, bool(bold), bool(italic), bool(underline))
if "align" in patch and patch["align"] in _ALIGNS:
_apply_align(te, patch["align"])
# Alignment: aus Patch oder vom alten Entity uebernehmen
if align:
_apply_align(te, align)
else:
try: te.TextHorizontalAlignment = old.TextHorizontalAlignment
except Exception: pass
doc.Objects.Replace(obj.Id, te)
n += 1
except Exception as ex: