From 264327432dc67a14ea8c790805edfc05bb3598ff Mon Sep 17 00:00:00 2001 From: karim Date: Wed, 27 May 2026 19:36:09 +0200 Subject: [PATCH] Splash + Layout-Skip-Revert MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit User-Bug: Layout-Skip-Optimierung war zu aggressiv — Mac Rhino haelt die Panel-Anordnung zwischen Sessions doch nicht im internen State, also wurden Panels falsch platziert nach Quick-Restart. Skip-Logik raus, der ~3s _-WindowLayout-Apply laeuft wieder jedes Mal. Das ist OK weil der Splash diese Wartezeit jetzt optisch abdeckt. Splash verbessert: - _try_borderless_mac(): direkter NSWindow-Zugriff via Eto.ControlObject + ObjC-Methoden (setStyleMask_, setOpaque_, setHasShadow_, setBackgroundColor_, setMovableByWindowBackground_) — produziert echten borderless Mac-Look wie der Launcher-Splash - Form-BackgroundColor auf transparent damit das gradient des WebView- HTMLs durchscheint (rounded petrol gradient mit weichem Verlauf) - WebView selber transparenter Hintergrund - Closeable/Minimizable/Maximizable/Resizable alle False - [SPLASH] visible log fuer Debug-Sichtbarkeit --- rhino/_startup_splash.py | 95 ++++++++++++++++++++++++++++++++++------ rhino/oberleiste.py | 19 +++----- 2 files changed, 86 insertions(+), 28 deletions(-) diff --git a/rhino/_startup_splash.py b/rhino/_startup_splash.py index 8bc4c7b..443326e 100644 --- a/rhino/_startup_splash.py +++ b/rhino/_startup_splash.py @@ -83,41 +83,97 @@ html, body { margin:0; padding:0; width:100%; height:100%; background:transparen ''' +def _try_borderless_mac(form): + """Mac-spezifisch: direkter NSWindow-Zugriff via Eto.ControlObject um + titlebar/Decorations komplett zu killen + rounded corners zu setzen. + Auf Mac ist Eto.Forms.Form.WindowStyle.None inkonsistent — der echte + Borderless-Effekt geht nur ueber AppKit. Probiert verschiedene Wege, + keiner ist fatal wenn er nicht klappt.""" + nswindow = None + try: + # Eto auf Mac: Form.ControlObject ist die NSWindow-Instanz + nswindow = getattr(form, "ControlObject", None) + except Exception: pass + if nswindow is None: return False + + # NSBorderlessWindowMask = 0, NSFullSizeContentViewWindowMask = 1<<15 + # NSWindowStyleMaskTitled = 1 + BORDERLESS = 0 + FULL_SIZE = 1 << 15 + + def _try(method_name, *args): + try: + m = getattr(nswindow, method_name, None) + if m is None: return False + m(*args) + return True + except Exception: return False + + ok = False + # 1) Style-Mask auf borderless (entfernt Titlebar/Border) + if _try("setStyleMask_", BORDERLESS): + ok = True + # 2) Hintergrund transparent damit WebView's eigene rounded box scheint + if _try("setOpaque_", False): ok = True + if _try("setHasShadow_", True): ok = True + # 3) Background color clear + try: + from AppKit import NSColor as _NSColor # type: ignore + clear = _NSColor.clearColor() + if _try("setBackgroundColor_", clear): ok = True + except Exception: pass + # 4) Bewegbar via background-drag + if _try("setMovableByWindowBackground_", True): ok = True + return ok + + def show(): """Zeigt den Splash. Idempotent — zweiter Aufruf bringt das bestehende Fenster nur in den Vordergrund. Auto-Hide nach _SAFETY_TIMEOUT_SEC als Fallback falls hide() vergessen wird.""" if sc.sticky.get(_SPLASH_KEY) is not None: - return + print("[SPLASH] schon offen — skip"); return try: import Eto.Forms as ef import Eto.Drawing as ed + except Exception as ex: + print("[SPLASH] Eto-Import:", ex); return + try: form = ef.Form() - form.Title = "Dossier" - # WindowStyle.None — "None" ist Python-keyword, daher via getattr + form.Title = "" # leerer Titel hilft bei Mac-Titlebar-Reduktion + # Versuche WindowStyle.None (Eto-API, funktioniert nicht immer auf Mac) try: form.WindowStyle = getattr(ef.WindowStyle, "None") except Exception: pass - try: form.Resizable = False - except Exception: pass - try: form.Topmost = True - except Exception: pass - try: form.ShowInTaskbar = False - except Exception: pass + # Alle Window-Chrome-Optionen aus + for attr, val in ( + ("Resizable", False), ("Minimizable", False), + ("Maximizable", False), ("Closeable", False), + ("ShowInTaskbar", False), ("Topmost", True), + ): + try: setattr(form, attr, val) + except Exception: pass try: form.Size = ed.Size(420, 160) except Exception: pass + # Transparent so dass WebView's eigene rounded gradient sichtbar wird try: - # Hintergrund weiss, das WebView-content hat seine eigene - # gerundete Petrol-Box — Form muss nur kein graues Border zeigen - form.BackgroundColor = ed.Color(0.18, 0.50, 0.45) - except Exception: pass + form.BackgroundColor = ed.Colors.Transparent + except Exception: + try: form.BackgroundColor = ed.Color(0.37, 0.66, 0.59) + except Exception: pass + wv = ef.WebView() + try: + # WebView selber transparent damit das Form-Hintergrund durchscheint + wv.BackgroundColor = ed.Colors.Transparent + except Exception: pass try: wv.LoadHtml(_SPLASH_HTML) except Exception as ex: print("[SPLASH] LoadHtml:", ex) form.Content = wv + + # Center on screen try: - # Center on screen screen = ef.Screen.PrimaryScreen sb = screen.Bounds x = int(sb.X + (sb.Width - form.Size.Width) / 2) @@ -125,10 +181,21 @@ def show(): form.Location = ed.Point(x, y) except Exception as ex: print("[SPLASH] center:", ex) + try: form.Show() except Exception as ex: print("[SPLASH] Show:", ex); return + + # Mac-spezifischer Borderless-Hack — MUSS nach Show() laufen damit + # die NSWindow existiert + try: + if _try_borderless_mac(form): + print("[SPLASH] Borderless (Mac NSWindow) angewendet") + except Exception as ex: + print("[SPLASH] borderless-mac:", ex) + sc.sticky[_SPLASH_KEY] = form + print("[SPLASH] visible") # Safety-Timeout — wenn nach 8s niemand hide() ruft, automatisch weg try: import threading diff --git a/rhino/oberleiste.py b/rhino/oberleiste.py index e317519..e74b264 100644 --- a/rhino/oberleiste.py +++ b/rhino/oberleiste.py @@ -1353,26 +1353,17 @@ class OberleisteBridge(panel_base.BaseBridge): # Default-Window-Layout anwenden, wenn aktiviert und noch nicht in # DIESER Rhino-Session geschehen (sticky-flag = process-lifetime). # Mac Rhino persistiert die Window-Anordnung zwischen Sessions - # NICHT zuverlaessig — der Cold-Start-Apply lief bisher jedes Mal - # (= ~3s _-WindowLayout RunScript-Blocker). - # Optimierung: Marker-File mit Timestamp + Name. Wenn der letzte - # erfolgreiche Apply < 10 min her ist UND derselbe Layout-Name → skip - # (Rhino "remembers" wahrscheinlich noch). Bei langem Abstand - # (Pause, neuer Tag, Reboot) → apply normal. + # NICHT zuverlaessig — der Cold-Start-Apply muss jedes Mal laufen. + # Die 3s Wartezeit verdeckt jetzt der Splash-Screen optisch. try: cfg = _settings_load() if not sc.sticky.get("_dossier_layout_applied"): layout_name = cfg.get("windowLayout") or cfg.get("defaultLayout") if cfg.get("autoApplyLayout") and layout_name: sc.sticky["_dossier_layout_applied"] = True - if _is_layout_recently_applied(layout_name, max_age_sec=600): - print("[OBERLEISTE] Layout '{}' wurde vor < 10min " - "appliziert — skip (~3s gespart). Manuell neu " - "anwenden via Oberleiste-Einstellungen wenn noetig.".format( - layout_name)) - else: - _apply_window_layout(layout_name) - _mark_layout_applied(layout_name) + _apply_window_layout(layout_name) + try: _mark_layout_applied(layout_name) + except Exception: pass # Viewport-Colors einmalig pro Session auto-applien (wenn aktiviert) if (cfg.get("autoApplyViewColors") and not sc.sticky.get("_dossier_view_colors_applied")):