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
+70 -47
View File
@@ -198,40 +198,47 @@ def _prompt_for_text(default=""):
def _inline_editor(p1, p2, initial=""): def _inline_editor(p1, p2, initial=""):
"""Inline-Editor: chromeloses Eto.Form ueber dem gepickten Frame im """Editor-Dialog mit Multi-Line-TextArea, positioniert NEBEN dem
Viewport positioniert. Cmd+Enter / Ctrl+Enter = commit, Esc = abbrechen. gepickten Frame (statt zentriert). Eto.Dialog.ShowModal — reliable
Returns Text-String oder None.""" auf Mac/Win. Returns Text-String oder None."""
import Eto.Forms as forms import Eto.Forms as forms
import Eto.Drawing as drawing 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: try:
view = Rhino.RhinoDoc.ActiveDoc.Views.ActiveView view = Rhino.RhinoDoc.ActiveDoc.Views.ActiveView
vp = view.ActiveViewport vp = view.ActiveViewport
# WorldToScreen → (bool, int_x, int_y) viewport-lokale Pixel
ok1, x1, y1 = vp.WorldToScreen(p1) ok1, x1, y1 = vp.WorldToScreen(p1)
ok2, x2, y2 = vp.WorldToScreen(p2) 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: except Exception as ex:
print("[TEXT] viewport-coords:", 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() dlg = forms.Dialog()
try: form.WindowStyle = forms.WindowStyle.None_ dlg.Title = "Text einfuegen"
except Exception: dlg.Resizable = True
try: form.WindowStyle = getattr(forms.WindowStyle, "None") dlg.Padding = drawing.Padding(8)
try: dlg.MinimumSize = drawing.Size(360, 180)
except Exception: pass except Exception: pass
try: form.Topmost = True try: dlg.ClientSize = drawing.Size(int(sw), int(sh))
except Exception: pass except Exception: pass
form.Resizable = False # Position neben dem Frame (falls verfuegbar)
form.ClientSize = drawing.Size(int(sw), int(sh)) if sx is not None and sy is not None:
try: try: dlg.Location = drawing.Point(sx, sy)
form.Location = drawing.Point(int(sx), int(sy))
except Exception: pass except Exception: pass
ta = forms.TextArea() ta = forms.TextArea()
@@ -241,12 +248,26 @@ def _inline_editor(p1, p2, initial=""):
ta.Text = initial or "" ta.Text = initial or ""
try: ta.Font = drawing.Font("Helvetica", 13) try: ta.Font = drawing.Font("Helvetica", 13)
except Exception: pass except Exception: pass
try: ta.BackgroundColor = drawing.Color.FromArgb(245, 245, 245)
except Exception: pass
ta.ShowBorder = False
result = {"text": None, "committed": 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): def on_keydown(sender, e):
try: try:
is_cmd = (e.Modifiers == forms.Keys.Application or is_cmd = (e.Modifiers == forms.Keys.Application or
@@ -254,34 +275,36 @@ def _inline_editor(p1, p2, initial=""):
except Exception: except Exception:
is_cmd = False is_cmd = False
if e.Key == forms.Keys.Enter and is_cmd: if e.Key == forms.Keys.Enter and is_cmd:
result["text"] = ta.Text or "" on_ok(sender, e); e.Handled = True
result["committed"] = True
try: form.Close()
except Exception: pass
e.Handled = True
elif e.Key == forms.Keys.Escape: elif e.Key == forms.Keys.Escape:
try: form.Close() on_cancel(sender, e); e.Handled = True
except Exception: pass
e.Handled = True
ta.KeyDown += on_keydown ta.KeyDown += on_keydown
form.Content = ta
try: form.Show() layout = forms.DynamicLayout()
except Exception as ex: layout.Spacing = drawing.Size(6, 6)
print("[TEXT] inline editor show:", ex) layout.BeginVertical()
return None layout.AddRow(ta)
try: ta.Focus() 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 except Exception: pass
# Warten bis User Esc oder Cmd+Enter drueckt
while True:
try: try:
if form.Closed: break parent = Rhino.UI.RhinoEtoApp.MainWindow
except Exception: if parent is not None: dlg.ShowModal(parent)
break else: dlg.ShowModal()
try: Rhino.RhinoApp.Wait() except Exception as ex:
except Exception: break print("[TEXT] dialog show:", ex)
return None
if not result["committed"]: return None if not result["committed"]: return None
return (result["text"] or "").strip() or None return (result["text"] or "").strip() or None