Show undocked video/waveform windows as independent top-levels#11229
Conversation
When the video player or audio visualizer is undocked into its own window, they were being shown via `Window.Show(owner)` with the main window as owner. On Windows this groups the owned windows with the main window in Alt+Tab, and once the user Alt+Tabs to an owned window the OS remembers that window as the group's "last active" — Alt+Tab back from any non-SE window returns to the owned window, not the main one, so the main window becomes unreachable via the keyboard. See PR #11226 discussion. Add `IWindowService.ShowIndependentWindow<T,VM>` which calls `Show()` without an owner, and switch the two undocked windows to use it. They now appear as independent top-level windows in Alt+Tab. `MainViewModel.CleanUp()` already explicitly closes both undocked windows (setting `AllowClose=true` first), so the owner-cascade close was redundant. Tighten `OnClosing` on both VMs to only intercept `WindowCloseReason.WindowClosing` — explicit user closes — so ApplicationShutdown closes (e.g. OS shutdown) aren't trapped in a minimize loop. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Fixes Alt+Tab focus trapping for undocked video player and audio visualizer windows by showing them as independent (ownerless) top-level windows instead of windows owned by the main window. Also tightens the OnClosing handlers to only minimize on explicit user close (title-bar X), letting owner-/shutdown-triggered closes pass through.
Changes:
- Adds
IWindowService.ShowIndependentWindow<T,TViewModel>that mirrorsShowWindowbut callsShow()with no owner. - Switches undocked video player / audio visualizer creation in
MainViewModel.VideoUndockControls()to the new method. - Narrows close interception in both undocked VMs from "anything except OwnerWindowClosing" to "only WindowClosing".
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| src/ui/Logic/WindowsService.cs | Adds ShowIndependentWindow<T,TViewModel> API and implementation (no owner, Show()). |
| src/ui/Features/Main/MainViewModel.cs | Uses the new independent-window API for the two undocked windows. |
| src/ui/Features/Shared/Undocked/VideoPlayerUndockedViewModel.cs | Only intercepts explicit WindowClosing; allows shutdown/owner closes through. |
| src/ui/Features/Shared/Undocked/AudioVisualizerUndockedViewModel.cs | Same close-handling tightening as the video VM. |
|
I am so looking forward to this. Thank you! This has probably been my biggest annoyance with SE 5. I am in no way qualified to evaluate code but a couple of thoughts occurred to me. If I'm reading this correctly, you've made the 2 child windows more like peers of the main window. But there is still a level of subordination that should be preserved. I'm thinking that it should not be possible to close either the video player or the Audio visualizer because there's no way to open them again (short of cycling the whole app). Seems to me at a certain point back in the Preview days that you did something so that "closing" them actually just minimized them. I'm hoping that's still true. Another thing you added that wasn't there initially was to make the audio visualizer float over the main window so it's always visible despite possibly not having focus. I hope this is preserved. The thought crossed my mind that closing the main window should still close the 2 child windows. I believe you covered that so there's no worry there. Otherwise, I am most grateful for this fix. I had totally given up hope, believing this was just another of those annoying artifacts of Avalonia that couldn't be addressed. So I'm quite eager to see beta 33. |
Beta 33 is up now: https://github.com/SubtitleEdit/subtitleedit/releases |
Add entries for recently merged work: - SubtitleEdit#11210 FindNext crash fixes - mjuhasz - SubtitleEdit#11219 Beautify window: profile button position + per-side fallback notes - SubtitleEdit#11221 TTS: unbreak lines centrally before handing text to engines - SubtitleEdit#11224 Replace dialog crashes + stale Count - mjuhasz - SubtitleEdit#11225 Focus subtitle grid on startup - mjuhasz - SubtitleEdit#11226 Restore focus when Find/Replace dismissed; focus edit text box after Find Next/Previous - mjuhasz - SubtitleEdit#11228 Expand SE 4 shortcut importer + SortByNumber/VideoToggleBrightness - SubtitleEdit#11229 Show undocked video/waveform windows as independent top-levels (Alt+Tab fix) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Alt+Tab goes to the main window! HURRAY! But the audio visualizer doesn't stay visible, floating on top of the main window. The only way it stays on top is if it actually is the foreground window. Alt+Tab to the main window puts the Audio visualizer behind the main window, which with my sizes/positions puts the Audio visualizer out of sight. Closing the children does still minimize them instead of closing. One oddity with the video window is that a close/minimize then Alt+Tab to refocus it doesn't restore it in the fullscreen mode I had it in before the "close." A quick Alt+Enter fixes that but it would be nice if it remembered I previously had it in fullscreen. Closing the main window does properly take down the 2 children. |
|
Perhaps this is a clue. Only you can decide. When I open SE, the Audio visualizer appears for a fraction of a second before it disappears behind the main window. It's as if SE is trying to float the Audio visualizer on top of the main window, & then something comes along that prevents that from succeeding. |
Apply WindowService.KeepTopmostWhileOwnerActive — introduced for Find/Replace in SubtitleEdit#11243 — to the undocked video player and audio visualizer windows. They now stay visually above SE main while either SE window is the active app, and drop behind when SE loses focus to another application. This addresses GrampaWildWilly's follow-up on SubtitleEdit#11229 ("the audio visualizer doesn't stay visible, floating on top of the main window") without touching Alt+Tab behavior — KeepTopmost… is just a Z-order knob; the two undocked windows remain independent top-levels in the Alt+Tab list, which was the point of SubtitleEdit#11229. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

Summary
Addresses the long-standing focus complaint raised in #11226:
Root cause:
WindowsService.ShowWindow<T,VM>(owner, …)callswindow.Show(owner). On Windows that makes the new window an owned top-level — the OS groups owner + owned windows in Alt+Tab. Once the user activates an owned window, the OS remembers it as the group's last-active member, so Alt+Tab back from any non-SE window returns to the owned window, not the main one.Fix: the two undocked windows shouldn't be owned by main — they're peer top-levels, not modal sidekicks. Adds
IWindowService.ShowIndependentWindow<T,VM>which callsShow()with no owner, and switches the video-player and audio-visualizer undocked windows to use it. They now appear in Alt+Tab as independent top-levels.Owner-cascade close is no longer available, but
MainViewModel.CleanUp()already explicitly closes both undocked windows (setsAllowClose = truefirst, thenClose()), so the existing shutdown path keeps working. To make sure ApplicationShutdown closes (e.g. OS shutdown, last-window-close) aren't accidentally trapped in a minimize loop, theOnClosinghandlers on both VMs are tightened to only interceptWindowCloseReason.WindowClosing— explicit user closes via the title-bar X.Test plan
🤖 Generated with Claude Code