Skip to content

fix: eliminate black-square flash on startup, start cleanly in tray#47

Merged
Daolyap merged 1 commit into
mainfrom
fix/startup-black-square-tray
Jun 12, 2026
Merged

fix: eliminate black-square flash on startup, start cleanly in tray#47
Daolyap merged 1 commit into
mainfrom
fix/startup-black-square-tray

Conversation

@Daolyap

@Daolyap Daolyap commented Jun 12, 2026

Copy link
Copy Markdown
Owner

What

Fixes the black square that briefly appears on app startup and ensures the app starts minimized to the system tray with no visible window.

Why

App.xaml used StartupUri="MainWindow.xaml", which makes WPF Show() the window during startup. The StartInTray check (and Hide()) only ran afterward in the Loaded handler — which fires after the window is shown and composed. With WindowStyle="None" + WindowChrome, the brief frame before the WPF content painted appeared as a black square that then vanished. It was an unavoidable Show-then-Hide flash.

How

  • App.xaml — removed StartupUri (which forces Show()); set ShutdownMode="OnExplicitShutdown", the correct mode for a tray app whose main window may never be shown (otherwise WPF could quit when a transient dialog like the editor closes).
  • App.xaml.csOnStartup now creates MainWindow explicitly and calls InitializeApplication() instead of relying on StartupUri.
  • MainWindow.xaml.cs — replaced the Loaded handler with InitializeApplication(). When StartInTray is set it calls WindowInteropHelper.EnsureHandle() to realize the native HWND (needed for global hotkey registration) without painting the window; otherwise it shows the window normally. Hotkeys bind after the handle exists in both paths.

Testing

  • dotnet build clean (0 warnings / 0 errors).
  • dotnet test — 109/109 service tests pass.
  • Launched the built exe: process stays alive with MainWindowHandle: 0, and an EnumWindows sweep confirmed zero visible top-level windows for the process — starts silently in the tray, no black-square flash.

Reviewer notes

  • No automated UI coverage exists (per DEVELOPER.md). Manual check worth doing on Windows: launch, then tray icon → "Show Window", and confirm a capture hotkey (Print Screen) still fires.
  • WindowStartupLocation="CenterScreen" is computed at handle-creation time inside CreateSourceWindow, so it is still honored even when the handle is realized early via EnsureHandle().

🤖 Generated with Claude Code

StartupUri="MainWindow.xaml" forced WPF to Show() the window during
startup; the StartInTray check (and Hide()) only ran later in the Loaded
handler. With WindowStyle="None" + WindowChrome, the frame shown before
the WPF content painted appeared as a black square that then vanished.

Stop showing the window when starting in the tray. App.OnStartup now
creates MainWindow explicitly and calls InitializeApplication(), which
realizes the native handle via WindowInteropHelper.EnsureHandle() (so
global hotkeys can still bind) without ever painting the window. The
window is only Show()n when StartInTray is false. ShutdownMode is set to
OnExplicitShutdown so the app doesn't quit when a transient dialog (e.g.
the editor) closes while the main window has never been shown.

Verified: build clean, 109/109 tests pass, and an EnumWindows sweep
confirms zero visible top-level windows for the running process.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@Daolyap Daolyap marked this pull request as ready for review June 12, 2026 10:15
@Daolyap Daolyap merged commit 2ca4060 into main Jun 12, 2026
2 checks passed
@Daolyap Daolyap deleted the fix/startup-black-square-tray branch June 12, 2026 10:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants