fix: re-assert always-on-top for overlay on Windows (#383)
* fix: re-assert always-on-top for overlay on Windows On Windows, the recording/transcribing overlay can lose its topmost Z-order and fall behind other windows during certain window manager events (e.g., dragging windows, alt-tabbing, or other apps asserting focus). This fix uses the Win32 SetWindowPos API directly with HWND_TOPMOST to aggressively re-assert the overlay's Z-order immediately after showing it. Key implementation details: - Uses Tauri 2's WebviewWindow::hwnd() to get the native handle - Calls SetWindowPos on the main UI thread via run_on_main_thread - Applies flags: SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_SHOWWINDOW - SWP_NOACTIVATE prevents stealing focus from the active window - SWP_SHOWWINDOW reinforces the topmost state during the show The fix is called after .show() in both show_recording_overlay and show_transcribing_overlay, which is more reliable than setting it before the window is visible. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * keep the fix to windows only --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: CJ Pais <cj@cjpais.com>
This commit is contained in:
parent
4fcf0447b3
commit
ee571b6810
2 changed files with 39 additions and 0 deletions
|
|
@ -86,6 +86,8 @@ windows = { version = "0.61.3", features = [
|
|||
"Win32_Media_Audio_Endpoints",
|
||||
"Win32_System_Com_StructuredStorage",
|
||||
"Win32_System_Variant",
|
||||
"Win32_Foundation",
|
||||
"Win32_UI_WindowsAndMessaging",
|
||||
] }
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies]
|
||||
|
|
|
|||
|
|
@ -39,6 +39,33 @@ const OVERLAY_BOTTOM_OFFSET: f64 = 15.0;
|
|||
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
||||
const OVERLAY_BOTTOM_OFFSET: f64 = 40.0;
|
||||
|
||||
/// Forces a window to be topmost using Win32 API (Windows only)
|
||||
/// This is more reliable than Tauri's set_always_on_top which can be overridden
|
||||
#[cfg(target_os = "windows")]
|
||||
fn force_overlay_topmost(overlay_window: &tauri::webview::WebviewWindow) {
|
||||
use windows::Win32::UI::WindowsAndMessaging::{
|
||||
SetWindowPos, HWND_TOPMOST, SWP_NOMOVE, SWP_NOSIZE, SWP_NOACTIVATE, SWP_SHOWWINDOW,
|
||||
};
|
||||
|
||||
// Clone because run_on_main_thread takes 'static
|
||||
let overlay_clone = overlay_window.clone();
|
||||
|
||||
// Make sure the Win32 call happens on the UI thread
|
||||
let _ = overlay_clone.clone().run_on_main_thread(move || {
|
||||
if let Ok(hwnd) = overlay_clone.hwnd() {
|
||||
unsafe {
|
||||
// Force Z-order: make this window topmost without changing size/pos or stealing focus
|
||||
let _ = SetWindowPos(
|
||||
hwnd,
|
||||
Some(HWND_TOPMOST),
|
||||
0, 0, 0, 0,
|
||||
SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_SHOWWINDOW,
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn get_monitor_with_cursor(app_handle: &AppHandle) -> Option<tauri::Monitor> {
|
||||
let enigo = Enigo::new(&Default::default());
|
||||
if let Ok(enigo) = enigo {
|
||||
|
|
@ -198,6 +225,11 @@ pub fn show_recording_overlay(app_handle: &AppHandle) {
|
|||
}
|
||||
|
||||
let _ = overlay_window.show();
|
||||
|
||||
// On Windows, aggressively re-assert "topmost" in the native Z-order after showing
|
||||
#[cfg(target_os = "windows")]
|
||||
force_overlay_topmost(&overlay_window);
|
||||
|
||||
// Emit event to trigger fade-in animation with recording state
|
||||
let _ = overlay_window.emit("show-overlay", "recording");
|
||||
}
|
||||
|
|
@ -215,6 +247,11 @@ pub fn show_transcribing_overlay(app_handle: &AppHandle) {
|
|||
|
||||
if let Some(overlay_window) = app_handle.get_webview_window("recording_overlay") {
|
||||
let _ = overlay_window.show();
|
||||
|
||||
// On Windows, aggressively re-assert "topmost" in the native Z-order after showing
|
||||
#[cfg(target_os = "windows")]
|
||||
force_overlay_topmost(&overlay_window);
|
||||
|
||||
// Emit event to switch to transcribing state
|
||||
let _ = overlay_window.emit("show-overlay", "transcribing");
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue