Text-Editor zurueck auf Dialog.ShowModal (chromeloses Form ging nicht)

User: Frame wird gezeichnet aber Editor erscheint nicht — kann nicht
schreiben. Eto.Form mit WindowStyle.None + Form.Show() funktioniert auf
Mac WebKit/Rhino-Build nicht zuverlaessig (kein Render oder hinter Rhino-
Window).

Fix: Eto.Dialog mit ShowModal — laeuft proven auf Mac+Windows. Dialog
hat normale Chrome (Title-Bar, OK/Cancel-Buttons) aber wird neben dem
gepickten Frame positioniert (via vp.WorldToScreen + view.ScreenRectangle
→ Dialog.Location 20px rechts vom Frame). Tradeoff zu "inline im
Viewport": Dialog hat Rahmen, aber ist sichtbar und funktional.

Workflow: pick Frame → Dialog poppt neben Frame mit TextArea + OK/Cancel
+ Cmd/Ctrl+Enter Shortcut + Esc-Abbruch.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-21 01:06:13 +02:00
parent 9256e5866e
commit 9eece87cc4
+73 -50
View File
@@ -198,41 +198,48 @@ def _prompt_for_text(default=""):
def _inline_editor(p1, p2, initial=""):
"""Inline-Editor: chromeloses Eto.Form ueber dem gepickten Frame im
Viewport positioniert. Cmd+Enter / Ctrl+Enter = commit, Esc = abbrechen.
Returns Text-String oder None."""
"""Editor-Dialog mit Multi-Line-TextArea, positioniert NEBEN dem
gepickten Frame (statt zentriert). Eto.Dialog.ShowModal — reliable
auf Mac/Win. Returns Text-String oder None."""
import Eto.Forms as forms
import Eto.Drawing as drawing
# Frame-Position im Screen ermitteln (fuer Dialog-Positionierung)
sx = sy = None
sw = max(360, 280)
sh = max(180, 150)
try:
view = Rhino.RhinoDoc.ActiveDoc.Views.ActiveView
vp = view.ActiveViewport
# WorldToScreen → (bool, int_x, int_y) viewport-lokale Pixel
ok1, x1, y1 = vp.WorldToScreen(p1)
ok2, x2, y2 = vp.WorldToScreen(p2)
view_rect = view.ScreenRectangle # absolute Viewport-Position
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.
sx = int(fx + fw + 20)
sy = int(fy)
sw = max(360, fw)
sh = max(180, fh)
except Exception as ex:
print("[TEXT] viewport-coords:", ex)
return None
if not (ok1 and ok2):
return None
# Frame-Rect in absoluten Screen-Pixeln
sx = view_rect.X + min(x1, x2)
sy = view_rect.Y + min(y1, y2)
sw = max(60, abs(x2 - x1))
sh = max(28, abs(y2 - y1))
form = forms.Form()
try: form.WindowStyle = forms.WindowStyle.None_
except Exception:
try: form.WindowStyle = getattr(forms.WindowStyle, "None")
dlg = forms.Dialog()
dlg.Title = "Text einfuegen"
dlg.Resizable = True
dlg.Padding = drawing.Padding(8)
try: dlg.MinimumSize = drawing.Size(360, 180)
except Exception: pass
try: dlg.ClientSize = drawing.Size(int(sw), int(sh))
except Exception: pass
# Position neben dem Frame (falls verfuegbar)
if sx is not None and sy is not None:
try: dlg.Location = drawing.Point(sx, sy)
except Exception: pass
try: form.Topmost = True
except Exception: pass
form.Resizable = False
form.ClientSize = drawing.Size(int(sw), int(sh))
try:
form.Location = drawing.Point(int(sx), int(sy))
except Exception: pass
ta = forms.TextArea()
ta.AcceptsReturn = True
@@ -241,12 +248,26 @@ def _inline_editor(p1, p2, initial=""):
ta.Text = initial or ""
try: ta.Font = drawing.Font("Helvetica", 13)
except Exception: pass
try: ta.BackgroundColor = drawing.Color.FromArgb(245, 245, 245)
except Exception: pass
ta.ShowBorder = False
result = {"text": None, "committed": False}
ok_btn = forms.Button()
ok_btn.Text = "Einfuegen"
cancel_btn = forms.Button()
cancel_btn.Text = "Abbrechen"
def on_ok(s, e):
result["text"] = ta.Text or ""
result["committed"] = True
try: dlg.Close()
except Exception: pass
def on_cancel(s, e):
try: dlg.Close()
except Exception: pass
ok_btn.Click += on_ok
cancel_btn.Click += on_cancel
# Cmd/Ctrl+Enter Shortcut im TextArea
def on_keydown(sender, e):
try:
is_cmd = (e.Modifiers == forms.Keys.Application or
@@ -254,34 +275,36 @@ def _inline_editor(p1, p2, initial=""):
except Exception:
is_cmd = False
if e.Key == forms.Keys.Enter and is_cmd:
result["text"] = ta.Text or ""
result["committed"] = True
try: form.Close()
except Exception: pass
e.Handled = True
on_ok(sender, e); e.Handled = True
elif e.Key == forms.Keys.Escape:
try: form.Close()
except Exception: pass
e.Handled = True
on_cancel(sender, e); e.Handled = True
ta.KeyDown += on_keydown
form.Content = ta
try: form.Show()
except Exception as ex:
print("[TEXT] inline editor show:", ex)
return None
try: ta.Focus()
layout = forms.DynamicLayout()
layout.Spacing = drawing.Size(6, 6)
layout.BeginVertical()
layout.AddRow(ta)
layout.EndVertical()
layout.BeginVertical()
layout.BeginHorizontal()
layout.Add(None, True, False)
layout.Add(cancel_btn)
layout.Add(ok_btn)
layout.EndHorizontal()
layout.EndVertical()
dlg.Content = layout
try:
dlg.DefaultButton = ok_btn
dlg.AbortButton = cancel_btn
except Exception: pass
# Warten bis User Esc oder Cmd+Enter drueckt
while True:
try:
if form.Closed: break
except Exception:
break
try: Rhino.RhinoApp.Wait()
except Exception: break
try:
parent = Rhino.UI.RhinoEtoApp.MainWindow
if parent is not None: dlg.ShowModal(parent)
else: dlg.ShowModal()
except Exception as ex:
print("[TEXT] dialog show:", ex)
return None
if not result["committed"]: return None
return (result["text"] or "").strip() or None