Splash: borderless+transparent+launcher-dedup, idle-dispatch hide
This commit is contained in:
@@ -294,6 +294,13 @@ fn plugin_loaded_marker_path() -> PathBuf {
|
|||||||
dossier_dir().join("plugin_loaded.flag")
|
dossier_dir().join("plugin_loaded.flag")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn splash_owner_marker_path() -> PathBuf {
|
||||||
|
// Vor Launch von Rhino schreibt Launcher diesen Marker → Plugin-Splash
|
||||||
|
// (rhino/_startup_splash.py) prueft beim Show ob Marker frisch (<30s)
|
||||||
|
// ist und skippt dann, damit nicht beide Splashes gleichzeitig laufen.
|
||||||
|
dossier_dir().join("splash_owner_launcher.flag")
|
||||||
|
}
|
||||||
|
|
||||||
fn open_rhino_internal(app: &tauri::AppHandle, path3dm: &str) -> Result<(), String> {
|
fn open_rhino_internal(app: &tauri::AppHandle, path3dm: &str) -> Result<(), String> {
|
||||||
let settings = load_settings();
|
let settings = load_settings();
|
||||||
// XML-Edit nur sinnvoll wenn Rhino nicht laeuft (sonst ueberschreibt's
|
// XML-Edit nur sinnvoll wenn Rhino nicht laeuft (sonst ueberschreibt's
|
||||||
@@ -314,8 +321,11 @@ fn open_rhino_internal(app: &tauri::AppHandle, path3dm: &str) -> Result<(), Stri
|
|||||||
// Splash NUR zeigen wenn Auto-Load aktiv (sonst gibt's nichts zu warten).
|
// Splash NUR zeigen wenn Auto-Load aktiv (sonst gibt's nichts zu warten).
|
||||||
let show_splash = settings.auto_load_plugin;
|
let show_splash = settings.auto_load_plugin;
|
||||||
let marker = plugin_loaded_marker_path();
|
let marker = plugin_loaded_marker_path();
|
||||||
|
let owner_marker = splash_owner_marker_path();
|
||||||
if show_splash {
|
if show_splash {
|
||||||
let _ = fs::remove_file(&marker);
|
let _ = fs::remove_file(&marker);
|
||||||
|
// Owner-Marker: signalisiert dem Plugin-Splash dass Launcher uebernimmt
|
||||||
|
let _ = fs::write(&owner_marker, b"launcher");
|
||||||
if let Some(splash) = app.get_webview_window("splash") {
|
if let Some(splash) = app.get_webview_window("splash") {
|
||||||
let _ = splash.show();
|
let _ = splash.show();
|
||||||
}
|
}
|
||||||
@@ -344,6 +354,7 @@ fn open_rhino_internal(app: &tauri::AppHandle, path3dm: &str) -> Result<(), Stri
|
|||||||
std::thread::sleep(std::time::Duration::from_millis(250));
|
std::thread::sleep(std::time::Duration::from_millis(250));
|
||||||
}
|
}
|
||||||
let _ = fs::remove_file(&marker);
|
let _ = fs::remove_file(&marker);
|
||||||
|
let _ = fs::remove_file(&owner_marker);
|
||||||
if let Some(splash) = app_clone.get_webview_window("splash") {
|
if let Some(splash) = app_clone.get_webview_window("splash") {
|
||||||
let _ = splash.hide();
|
let _ = splash.hide();
|
||||||
}
|
}
|
||||||
|
|||||||
+220
-62
@@ -12,10 +12,21 @@ Panels registriert + WindowLayout neu anwendet.
|
|||||||
Wird von startup.py beim ersten Idle gezeigt und nach Layout-Apply
|
Wird von startup.py beim ersten Idle gezeigt und nach Layout-Apply
|
||||||
(oder Timeout) wieder versteckt.
|
(oder Timeout) wieder versteckt.
|
||||||
"""
|
"""
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
import Rhino
|
||||||
import scriptcontext as sc
|
import scriptcontext as sc
|
||||||
|
|
||||||
_SPLASH_KEY = "_dossier_startup_splash"
|
_SPLASH_KEY = "_dossier_startup_splash"
|
||||||
_SAFETY_TIMEOUT_SEC = 8.0 # spaetestens nach 8s wegmachen, falls Hide-Hook nicht feuert
|
_SPLASH_SHOWN_AT_KEY = "_dossier_startup_splash_shown_at"
|
||||||
|
_SAFETY_TIMEOUT_SEC = 12.0 # spaetestens nach 12s wegmachen, falls Hide-Hook nicht feuert
|
||||||
|
|
||||||
|
# Marker den der Launcher direkt vor `open -a Rhinoceros` schreibt, damit
|
||||||
|
# Plugin-Splash NICHT zusaetzlich zum Launcher-Splash erscheint.
|
||||||
|
_OWNER_MARKER = os.path.expanduser(
|
||||||
|
"~/Library/Application Support/ch.gabrielevarano.Dossier/splash_owner_launcher.flag"
|
||||||
|
)
|
||||||
|
_OWNER_FRESH_SEC = 30.0 # Stale-Schutz falls Launcher crasht
|
||||||
|
|
||||||
|
|
||||||
_SPLASH_HTML = '''<!DOCTYPE html>
|
_SPLASH_HTML = '''<!DOCTYPE html>
|
||||||
@@ -46,19 +57,9 @@ html, body { margin:0; padding:0; width:100%; height:100%; background:transparen
|
|||||||
.status-row { align-self:end; display:flex; align-items:center; gap:10px;
|
.status-row { align-self:end; display:flex; align-items:center; gap:10px;
|
||||||
margin-top:18px; font-size:11px; letter-spacing:0.10em; color:var(--paper);
|
margin-top:18px; font-size:11px; letter-spacing:0.10em; color:var(--paper);
|
||||||
text-transform:uppercase; }
|
text-transform:uppercase; }
|
||||||
.dot-pulse { width:7px; height:7px; border-radius:50%; background:var(--paper);
|
.dot-pulse { width:7px; height:7px; border-radius:50%; background:var(--paper); }
|
||||||
animation:pulse 1.6s ease-out infinite; }
|
.bar { position:relative; height:2px; width:100%; background:rgba(255,255,255,0.28);
|
||||||
@keyframes pulse {
|
border-radius:2px; margin-top:12px; }
|
||||||
0% { box-shadow:0 0 0 0 rgba(255,255,255,0.55); transform:scale(1); }
|
|
||||||
70% { box-shadow:0 0 0 9px rgba(255,255,255,0); transform:scale(1.05); }
|
|
||||||
100% { box-shadow:0 0 0 0 rgba(255,255,255,0); transform:scale(1); }
|
|
||||||
}
|
|
||||||
.bar { position:relative; height:2px; width:100%; background:rgba(255,255,255,0.18);
|
|
||||||
border-radius:2px; overflow:hidden; margin-top:12px; }
|
|
||||||
.bar::after { content:""; position:absolute; top:0; left:-35%; width:35%; height:100%;
|
|
||||||
background: linear-gradient(90deg, transparent, var(--paper), transparent);
|
|
||||||
animation: slide 1.6s linear infinite; }
|
|
||||||
@keyframes slide { to { left:100%; } }
|
|
||||||
.meta-row { display:flex; align-items:baseline; justify-content:space-between; gap:12px;
|
.meta-row { display:flex; align-items:baseline; justify-content:space-between; gap:12px;
|
||||||
margin-top:10px; font-size:9px; letter-spacing:0.14em; color:var(--paper-faint);
|
margin-top:10px; font-size:9px; letter-spacing:0.14em; color:var(--paper-faint);
|
||||||
text-transform:uppercase; }
|
text-transform:uppercase; }
|
||||||
@@ -85,52 +86,194 @@ html, body { margin:0; padding:0; width:100%; height:100%; background:transparen
|
|||||||
|
|
||||||
def _try_borderless_mac(form):
|
def _try_borderless_mac(form):
|
||||||
"""Mac-spezifisch: direkter NSWindow-Zugriff via Eto.ControlObject um
|
"""Mac-spezifisch: direkter NSWindow-Zugriff via Eto.ControlObject um
|
||||||
titlebar/Decorations komplett zu killen + rounded corners zu setzen.
|
titlebar/Decorations komplett zu killen.
|
||||||
Auf Mac ist Eto.Forms.Form.WindowStyle.None inkonsistent — der echte
|
|
||||||
Borderless-Effekt geht nur ueber AppKit. Probiert verschiedene Wege,
|
Eto.Mac.Forms.EtoWindow IST-A NSWindow (Xamarin.Mac-Subclass).
|
||||||
keiner ist fatal wenn er nicht klappt."""
|
StyleMask ist ein .NET-Enum-Property — Python.NET 3 verlangt explizite
|
||||||
nswindow = None
|
Enum-Konversion (kein impliziter int → Enum cast mehr). Wir leiten
|
||||||
try:
|
den Enum-Typ zur Laufzeit aus dem Getter ab und konstruieren den
|
||||||
# Eto auf Mac: Form.ControlObject ist die NSWindow-Instanz
|
Borderless-Wert via System.Enum.ToObject."""
|
||||||
nswindow = getattr(form, "ControlObject", None)
|
nswindow = getattr(form, "ControlObject", None)
|
||||||
except Exception: pass
|
if nswindow is None:
|
||||||
if nswindow is None: return False
|
print("[SPLASH] keine ControlObject auf Form")
|
||||||
|
return False
|
||||||
# NSBorderlessWindowMask = 0, NSFullSizeContentViewWindowMask = 1<<15
|
print("[SPLASH] ControlObject type:", str(type(nswindow)))
|
||||||
# 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
|
|
||||||
|
|
||||||
|
import System
|
||||||
ok = False
|
ok = False
|
||||||
# 1) Style-Mask auf borderless (entfernt Titlebar/Border)
|
|
||||||
if _try("setStyleMask_", BORDERLESS):
|
# NSWindowStyleMaskBorderless = 0
|
||||||
ok = True
|
# NSWindowStyleMaskTitled = 1, FullSizeContentView = 32768
|
||||||
# 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:
|
try:
|
||||||
from AppKit import NSColor as _NSColor # type: ignore
|
current = nswindow.StyleMask
|
||||||
clear = _NSColor.clearColor()
|
style_type = type(current)
|
||||||
if _try("setBackgroundColor_", clear): ok = True
|
borderless = System.Enum.ToObject(style_type, 0)
|
||||||
|
nswindow.StyleMask = borderless
|
||||||
|
print("[SPLASH] StyleMask=0 (Borderless) gesetzt")
|
||||||
|
ok = True
|
||||||
|
except Exception as ex:
|
||||||
|
print("[SPLASH] StyleMask Enum:", ex)
|
||||||
|
# Fallback: FullSizeContentView (32768) + TitlebarAppearsTransparent
|
||||||
|
# damit Content unter die (transparente) Titlebar reicht
|
||||||
|
try:
|
||||||
|
current = nswindow.StyleMask
|
||||||
|
style_type = type(current)
|
||||||
|
full = System.Enum.ToObject(style_type, 1 | 32768)
|
||||||
|
nswindow.StyleMask = full
|
||||||
|
print("[SPLASH] StyleMask=Titled|FullSize gesetzt (Fallback)")
|
||||||
|
ok = True
|
||||||
|
except Exception as ex2:
|
||||||
|
print("[SPLASH] StyleMask Fallback:", ex2)
|
||||||
|
|
||||||
|
# Titlebar transparent + Titel unsichtbar
|
||||||
|
def _set_prop(prop, value, log=False):
|
||||||
|
try:
|
||||||
|
setattr(nswindow, prop, value)
|
||||||
|
if log: print("[SPLASH] {}={} OK".format(prop, value))
|
||||||
|
return True
|
||||||
|
except Exception as ex:
|
||||||
|
if log: print("[SPLASH] {}:".format(prop), ex)
|
||||||
|
return False
|
||||||
|
|
||||||
|
_set_prop("TitlebarAppearsTransparent", True, True)
|
||||||
|
# NSWindowTitleHidden = 1
|
||||||
|
try:
|
||||||
|
tv_type = type(nswindow.TitleVisibility)
|
||||||
|
nswindow.TitleVisibility = System.Enum.ToObject(tv_type, 1)
|
||||||
|
print("[SPLASH] TitleVisibility=Hidden OK")
|
||||||
|
except Exception as ex:
|
||||||
|
print("[SPLASH] TitleVisibility:", ex)
|
||||||
|
_set_prop("IsOpaque", False)
|
||||||
|
_set_prop("HasShadow", True)
|
||||||
|
_set_prop("MovableByWindowBackground", True)
|
||||||
|
|
||||||
|
# Clear NSWindow background damit rounded corners aus dem HTML sichtbar
|
||||||
|
# werden. Xamarin.Mac exponiert NSColor.Clear als statische Property.
|
||||||
|
try:
|
||||||
|
from AppKit import NSColor as _NSC
|
||||||
|
clear = getattr(_NSC, "Clear", None) or getattr(_NSC, "ClearColor", None)
|
||||||
|
if clear is not None:
|
||||||
|
nswindow.BackgroundColor = clear
|
||||||
|
print("[SPLASH] BackgroundColor=Clear OK")
|
||||||
|
except Exception as ex:
|
||||||
|
print("[SPLASH] BackgroundColor Clear:", ex)
|
||||||
|
|
||||||
|
# Force-Paint: Splash MUSS sichtbar sein BEVOR Rhino den Script-Thread
|
||||||
|
# weiter belegt. Python-Script blockiert sonst die Main-Loop und der
|
||||||
|
# Splash wuerde erst nach Script-Ende paintet werden — viel zu spaet.
|
||||||
|
try: nswindow.OrderFrontRegardless()
|
||||||
except Exception: pass
|
except Exception: pass
|
||||||
# 4) Bewegbar via background-drag
|
try: nswindow.DisplayIfNeeded()
|
||||||
if _try("setMovableByWindowBackground_", True): ok = True
|
except Exception: pass
|
||||||
|
try: nswindow.Display()
|
||||||
|
except Exception: pass
|
||||||
|
|
||||||
return ok
|
return ok
|
||||||
|
|
||||||
|
|
||||||
|
def _try_transparent_webview_mac(wv):
|
||||||
|
"""WKWebView transparent machen damit der NSWindow-Hintergrund (oder
|
||||||
|
nichts) durchscheint und runde Ecken sichtbar werden. wv.ControlObject
|
||||||
|
ist die WKWebView."""
|
||||||
|
wk = getattr(wv, "ControlObject", None)
|
||||||
|
if wk is None:
|
||||||
|
print("[SPLASH] WebView: keine ControlObject"); return
|
||||||
|
print("[SPLASH] WebView ControlObject type:", str(type(wk)))
|
||||||
|
|
||||||
|
# KVC: setValue:forKey:@"drawsBackground" → @NO. Funktioniert sowohl bei
|
||||||
|
# WebView (alt) als auch WKWebView (NSObject KVC). Das ist der zuverlaessige
|
||||||
|
# Weg WebView-Hintergrund komplett zu entfernen, besser als UnderPageBg.
|
||||||
|
try:
|
||||||
|
from Foundation import NSNumber, NSString
|
||||||
|
try:
|
||||||
|
wk.SetValueForKey(NSNumber.FromBoolean(False), NSString("drawsBackground"))
|
||||||
|
print("[SPLASH] WebView drawsBackground=NO via KVC OK")
|
||||||
|
except Exception as ex:
|
||||||
|
print("[SPLASH] KVC drawsBackground:", ex)
|
||||||
|
except Exception as ex:
|
||||||
|
print("[SPLASH] Foundation import:", ex)
|
||||||
|
|
||||||
|
try:
|
||||||
|
from AppKit import NSColor as _NSC
|
||||||
|
clear = getattr(_NSC, "Clear", None) or getattr(_NSC, "ClearColor", None)
|
||||||
|
if clear is not None:
|
||||||
|
try: wk.UnderPageBackgroundColor = clear
|
||||||
|
except Exception: pass
|
||||||
|
try:
|
||||||
|
layer = getattr(wk, "Layer", None)
|
||||||
|
if layer is not None:
|
||||||
|
layer.BackgroundColor = clear.CGColor
|
||||||
|
layer.Opaque = False
|
||||||
|
print("[SPLASH] WebView Layer transparent OK")
|
||||||
|
except Exception as ex:
|
||||||
|
print("[SPLASH] WebView Layer:", ex)
|
||||||
|
except Exception as ex:
|
||||||
|
print("[SPLASH] WebView NSColor:", ex)
|
||||||
|
|
||||||
|
|
||||||
|
def _dispatch_to_main(fn):
|
||||||
|
"""Fuehrt fn beim naechsten Rhino-Idle-Event aus. Mac Eto/AppKit
|
||||||
|
erfordert UI-Mutationen auf dem Main-Thread; threading.Timer-Callbacks
|
||||||
|
laufen im falschen Thread und Close() crasht oder no-op't dort."""
|
||||||
|
handler_ref = [None]
|
||||||
|
def _idle(sender, e):
|
||||||
|
try: Rhino.RhinoApp.Idle -= handler_ref[0]
|
||||||
|
except Exception: pass
|
||||||
|
try: fn()
|
||||||
|
except Exception as ex:
|
||||||
|
print("[SPLASH] dispatched fn:", ex)
|
||||||
|
handler_ref[0] = _idle
|
||||||
|
try: Rhino.RhinoApp.Idle += _idle
|
||||||
|
except Exception as ex:
|
||||||
|
print("[SPLASH] idle subscribe:", ex)
|
||||||
|
try: fn()
|
||||||
|
except Exception: pass
|
||||||
|
|
||||||
|
|
||||||
|
def _install_safety_timeout():
|
||||||
|
"""Registriert Idle-Handler der periodisch prueft ob _SAFETY_TIMEOUT_SEC
|
||||||
|
erreicht ist. Cleanup-self wenn Splash bereits zu."""
|
||||||
|
handler_ref = [None]
|
||||||
|
def _idle(sender, e):
|
||||||
|
try:
|
||||||
|
if sc.sticky.get(_SPLASH_KEY) is None:
|
||||||
|
try: Rhino.RhinoApp.Idle -= handler_ref[0]
|
||||||
|
except Exception: pass
|
||||||
|
return
|
||||||
|
shown_at = sc.sticky.get(_SPLASH_SHOWN_AT_KEY) or 0
|
||||||
|
if shown_at and (time.time() - shown_at) >= _SAFETY_TIMEOUT_SEC:
|
||||||
|
try: Rhino.RhinoApp.Idle -= handler_ref[0]
|
||||||
|
except Exception: pass
|
||||||
|
print("[SPLASH] safety-timeout — auto-hide")
|
||||||
|
try: _hide_main()
|
||||||
|
except Exception: pass
|
||||||
|
except Exception: pass
|
||||||
|
handler_ref[0] = _idle
|
||||||
|
try: Rhino.RhinoApp.Idle += _idle
|
||||||
|
except Exception as ex:
|
||||||
|
print("[SPLASH] safety install:", ex)
|
||||||
|
|
||||||
|
|
||||||
|
def _launcher_owns_splash():
|
||||||
|
"""True wenn Launcher direkt vor Rhino-Launch einen frischen Owner-
|
||||||
|
Marker geschrieben hat. Verhindert doppelte Splashes."""
|
||||||
|
try:
|
||||||
|
if not os.path.isfile(_OWNER_MARKER):
|
||||||
|
return False
|
||||||
|
age = time.time() - os.path.getmtime(_OWNER_MARKER)
|
||||||
|
if age <= _OWNER_FRESH_SEC:
|
||||||
|
return True
|
||||||
|
except Exception: pass
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
def show():
|
def show():
|
||||||
"""Zeigt den Splash. Idempotent — zweiter Aufruf bringt das bestehende
|
"""Zeigt den Splash. Idempotent — zweiter Aufruf bringt das bestehende
|
||||||
Fenster nur in den Vordergrund. Auto-Hide nach _SAFETY_TIMEOUT_SEC
|
Fenster nur in den Vordergrund. Auto-Hide nach _SAFETY_TIMEOUT_SEC
|
||||||
als Fallback falls hide() vergessen wird."""
|
als Fallback via Idle-Polling (NICHT threading.Timer — Mac UI braucht
|
||||||
|
Main-Thread). Skipt wenn Launcher seinen eigenen Splash zeigt."""
|
||||||
|
if _launcher_owns_splash():
|
||||||
|
print("[SPLASH] Launcher zeigt eigenen Splash — skip"); return
|
||||||
if sc.sticky.get(_SPLASH_KEY) is not None:
|
if sc.sticky.get(_SPLASH_KEY) is not None:
|
||||||
print("[SPLASH] schon offen — skip"); return
|
print("[SPLASH] schon offen — skip"); return
|
||||||
try:
|
try:
|
||||||
@@ -193,31 +336,46 @@ def show():
|
|||||||
print("[SPLASH] Borderless (Mac NSWindow) angewendet")
|
print("[SPLASH] Borderless (Mac NSWindow) angewendet")
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
print("[SPLASH] borderless-mac:", ex)
|
print("[SPLASH] borderless-mac:", ex)
|
||||||
|
# WebView transparent (rounded corners via HTML border-radius)
|
||||||
|
try: _try_transparent_webview_mac(wv)
|
||||||
|
except Exception as ex:
|
||||||
|
print("[SPLASH] webview-clear:", ex)
|
||||||
|
# Event-Loop einmal explizit pumpen damit Splash gepainted wird
|
||||||
|
# bevor das Script weiter blockiert (sonst sieht Nutzer die Panels
|
||||||
|
# zuerst entstehen und Splash erscheint erst danach).
|
||||||
|
try:
|
||||||
|
ef.Application.Instance.RunIteration()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
sc.sticky[_SPLASH_KEY] = form
|
sc.sticky[_SPLASH_KEY] = form
|
||||||
|
sc.sticky[_SPLASH_SHOWN_AT_KEY] = time.time()
|
||||||
print("[SPLASH] visible")
|
print("[SPLASH] visible")
|
||||||
# Safety-Timeout — wenn nach 8s niemand hide() ruft, automatisch weg
|
# Safety-Timeout via Idle-Polling (Main-Thread, Mac-safe)
|
||||||
try:
|
_install_safety_timeout()
|
||||||
import threading
|
|
||||||
def _auto_hide():
|
|
||||||
try: hide()
|
|
||||||
except Exception: pass
|
|
||||||
threading.Timer(_SAFETY_TIMEOUT_SEC, _auto_hide).start()
|
|
||||||
except Exception: pass
|
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
print("[SPLASH] show:", ex)
|
print("[SPLASH] show:", ex)
|
||||||
|
|
||||||
|
|
||||||
def hide():
|
def _hide_main():
|
||||||
"""Versteckt + entsorgt den Splash. Idempotent."""
|
"""Synchroner Close — MUSS auf Main-Thread laufen. Nur intern aufrufen,
|
||||||
|
extern hide() verwenden."""
|
||||||
form = sc.sticky.get(_SPLASH_KEY)
|
form = sc.sticky.get(_SPLASH_KEY)
|
||||||
if form is None:
|
if form is None:
|
||||||
return
|
return
|
||||||
try:
|
|
||||||
sc.sticky[_SPLASH_KEY] = None
|
sc.sticky[_SPLASH_KEY] = None
|
||||||
|
sc.sticky[_SPLASH_SHOWN_AT_KEY] = None
|
||||||
try: form.Close()
|
try: form.Close()
|
||||||
except Exception:
|
except Exception:
|
||||||
try: form.Visible = False
|
try: form.Visible = False
|
||||||
except Exception: pass
|
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
print("[SPLASH] hide:", ex)
|
print("[SPLASH] hide visible:", ex)
|
||||||
|
print("[SPLASH] hidden")
|
||||||
|
|
||||||
|
|
||||||
|
def hide():
|
||||||
|
"""Versteckt + entsorgt den Splash. Idempotent + thread-safe —
|
||||||
|
dispatcht auf Rhino-Main-Thread via Idle-Event."""
|
||||||
|
if sc.sticky.get(_SPLASH_KEY) is None:
|
||||||
|
return
|
||||||
|
_dispatch_to_main(_hide_main)
|
||||||
|
|||||||
+3
-4
@@ -1372,12 +1372,11 @@ class OberleisteBridge(panel_base.BaseBridge):
|
|||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
print("[OBERLEISTE] auto-apply (layout/colors):", ex)
|
print("[OBERLEISTE] auto-apply (layout/colors):", ex)
|
||||||
# Splash-Screen (falls noch offen) jetzt wegmachen — Layout-Apply ist
|
# Splash-Screen (falls noch offen) jetzt wegmachen — Layout-Apply ist
|
||||||
# durch, Panels sind in finaler Position. Lazy via Timer 200ms damit
|
# durch, Panels sind in finaler Position. hide() dispatcht intern
|
||||||
# die Layout-Animation kurz auf den finalen Panels sichtbar wird.
|
# auf Main-Thread via Rhino.Idle (Mac-safe).
|
||||||
try:
|
try:
|
||||||
import threading
|
|
||||||
import _startup_splash as _ss
|
import _startup_splash as _ss
|
||||||
threading.Timer(0.2, _ss.hide).start()
|
_ss.hide()
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
print("[OBERLEISTE] splash hide:", ex)
|
print("[OBERLEISTE] splash hide:", ex)
|
||||||
self._send_state(force=True)
|
self._send_state(force=True)
|
||||||
|
|||||||
+15
-11
@@ -15,16 +15,26 @@ import json
|
|||||||
import Rhino
|
import Rhino
|
||||||
import scriptcontext as sc
|
import scriptcontext as sc
|
||||||
|
|
||||||
|
_HERE = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
if _HERE not in sys.path:
|
||||||
|
sys.path.insert(0, _HERE)
|
||||||
|
|
||||||
|
# Splash SOFORT als allererstes — bevor irgendwas anderes passiert, damit der
|
||||||
|
# Nutzer waehrend Python-Imports + Panel-Registrierung nicht in eine schwarze
|
||||||
|
# Rhino-Oberflaeche schaut. Skipt automatisch wenn Launcher seinen eigenen
|
||||||
|
# Splash zeigt (Owner-Marker-Check).
|
||||||
|
try:
|
||||||
|
import _startup_splash as _splash_first
|
||||||
|
_splash_first.show()
|
||||||
|
except Exception as _ex_splash:
|
||||||
|
print("[STARTUP] splash early:", _ex_splash)
|
||||||
|
|
||||||
# DIAGNOSE — welcher Python-Engine laeuft hier wirklich? Einmalig beim Start.
|
# DIAGNOSE — welcher Python-Engine laeuft hier wirklich? Einmalig beim Start.
|
||||||
print("[STARTUP] Python: {}".format(sys.version))
|
print("[STARTUP] Python: {}".format(sys.version))
|
||||||
print("[STARTUP] Implementation: {}".format(
|
print("[STARTUP] Implementation: {}".format(
|
||||||
sys.implementation.name if hasattr(sys, "implementation") else "n/a (IPy2)"))
|
sys.implementation.name if hasattr(sys, "implementation") else "n/a (IPy2)"))
|
||||||
print("[STARTUP] Platform: {}".format(sys.platform))
|
print("[STARTUP] Platform: {}".format(sys.platform))
|
||||||
|
|
||||||
_HERE = os.path.dirname(os.path.abspath(__file__))
|
|
||||||
if _HERE not in sys.path:
|
|
||||||
sys.path.insert(0, _HERE)
|
|
||||||
|
|
||||||
# Pfad zur Custom-UI (Toolbars/Sidebar) — wird einmal pro Session geladen
|
# Pfad zur Custom-UI (Toolbars/Sidebar) — wird einmal pro Session geladen
|
||||||
_UI_FILE = os.path.join(_HERE, "DOSSIERUI.rhw")
|
_UI_FILE = os.path.join(_HERE, "DOSSIERUI.rhw")
|
||||||
|
|
||||||
@@ -240,13 +250,7 @@ def _load_all(sender, e):
|
|||||||
Rhino.RhinoApp.Idle -= _load_all
|
Rhino.RhinoApp.Idle -= _load_all
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
# Splash zeigen bevor irgendwas laeuft — verdeckt visuell die ~3s
|
# Splash wird ganz oben in startup.py (vor diesem Idle) gezeigt.
|
||||||
# Panel-Init + WindowLayout-Apply
|
|
||||||
try:
|
|
||||||
import _startup_splash
|
|
||||||
_startup_splash.show()
|
|
||||||
except Exception as ex:
|
|
||||||
print("[STARTUP] splash show:", ex)
|
|
||||||
print("[STARTUP] Lade DOSSIER-Panels...")
|
print("[STARTUP] Lade DOSSIER-Panels...")
|
||||||
# Migration einmal fuer das beim Start aktive Doc
|
# Migration einmal fuer das beim Start aktive Doc
|
||||||
_migrate_active_doc()
|
_migrate_active_doc()
|
||||||
|
|||||||
Reference in New Issue
Block a user