From 9c7ade8ea39d03d5e66de92715d92c53f05151b6 Mon Sep 17 00:00:00 2001 From: tiennm99 Date: Wed, 21 Oct 2026 15:44:00 +0700 Subject: [PATCH] fix: reload the panel for browser keys only after its first page load --- .../phase-07-panel-window.md | 2 +- src/panel.rs | 44 +++++++++++++++---- src/platform/windows.rs | 6 ++- 3 files changed, 41 insertions(+), 11 deletions(-) diff --git a/plans/261021-1028-tauri-v2-port/phase-07-panel-window.md b/plans/261021-1028-tauri-v2-port/phase-07-panel-window.md index 9340a47..4dd8fef 100644 --- a/plans/261021-1028-tauri-v2-port/phase-07-panel-window.md +++ b/plans/261021-1028-tauri-v2-port/phase-07-panel-window.md @@ -92,7 +92,7 @@ dismisses it. - The row labels ("5h", "7d") are not in `PanelPayload`; the page reads them once from the existing `get_strings` command, so the payload contract stays as planned. - The pure page helpers live in `ui/panel-view.js` instead of `ui/panel.js`, because `panel.js` reads `window.__TAURI__` on import and cannot run under `node --test`. -- Tauri does not expose wry's `with_browser_accelerator_keys`, so `platform::windows::disable_browser_accelerator_keys` sets `AreBrowserAcceleratorKeysEnabled` through `with_webview` and reloads the page once at startup, because the setting applies from the next navigation. The `keydown` handler covers the same keys on every platform. +- Tauri does not expose wry's `with_browser_accelerator_keys`, so `platform::windows::disable_browser_accelerator_keys` sets `AreBrowserAcceleratorKeysEnabled` through `with_webview` and reloads the page once, because the setting applies from the next navigation. A page-load hook on the panel runs this after the first `panel.html` load finishes, so the reload cannot replace the initial navigation; still to check on Windows hardware that the panel draws its payload after startup. The `keydown` handler covers the same keys on every platform. - `bubble_click` is an async command, so the toggle is queued to the main thread instead of running inside the webview's IPC callback. Toggles and refreshes both run on the main thread, which keeps a refresh from drawing a provider the panel no longer shows. - The panel background colour is set from the theme before every show, and the payload is emitted before `show()`, against a white or stale flash. - `shadow(false)` is kept until the Windows screenshot comparison says otherwise. diff --git a/src/panel.rs b/src/panel.rs index 959f622..d780511 100644 --- a/src/panel.rs +++ b/src/panel.rs @@ -130,7 +130,7 @@ fn background(is_dark: bool) -> Color { /// Create the hidden panel window. Called once at startup. pub fn create(app: &AppHandle, is_dark: bool) -> tauri::Result<()> { - let window = WebviewWindowBuilder::new(app, PANEL_LABEL, WebviewUrl::App("panel.html".into())) + let builder = WebviewWindowBuilder::new(app, PANEL_LABEL, WebviewUrl::App("panel.html".into())) .title(TITLE) .decorations(false) .always_on_top(true) @@ -140,15 +140,46 @@ pub fn create(app: &AppHandle, is_dark: bool) -> tauri::Result<()> { .shadow(false) .visible(false) .background_color(background(is_dark)) - .inner_size(f64::from(PANEL_W_LOGICAL), f64::from(PANEL_H_LOGICAL)) - .build()?; + .inner_size(f64::from(PANEL_W_LOGICAL), f64::from(PANEL_H_LOGICAL)); + #[cfg(windows)] + let builder = builder.on_page_load(disable_browser_keys_after_first_load()); + let window = builder.build()?; prepare_native(app, &window); log::info!("{PANEL_LABEL} created"); Ok(()) } -/// Windows: drop the panel from Alt-Tab and turn off the browser keys of -/// its webview, on the window's own (main) thread, before the first show. +/// Windows: a page-load hook that turns off the webview's browser keys +/// once `panel.html` has finished its first load. The setting only applies +/// from the next navigation, so it comes with a reload; waiting for the +/// first load keeps that reload from racing the initial navigation, and the +/// flag keeps the reload's own load from repeating it. The work leaves the +/// navigation callback through another thread, which queues it on the main +/// thread for after the callback returns. +#[cfg(windows)] +fn disable_browser_keys_after_first_load( +) -> impl Fn(WebviewWindow, tauri::webview::PageLoadPayload<'_>) + Send + Sync + 'static { + use std::sync::atomic::{AtomicBool, Ordering}; + use tauri::webview::PageLoadEvent; + + let done = AtomicBool::new(false); + move |window, payload| { + if payload.event() != PageLoadEvent::Finished + || payload.url().scheme() == "about" + || done.swap(true, Ordering::SeqCst) + { + return; + } + std::thread::spawn(move || { + if let Err(err) = platform::windows::disable_browser_accelerator_keys(&window) { + log::warn!("{PANEL_LABEL} keeps the browser accelerator keys: {err}"); + } + }); + } +} + +/// Windows: drop the panel from Alt-Tab, on the window's own (main) +/// thread, before the first show. #[cfg(windows)] fn prepare_native(app: &AppHandle, window: &WebviewWindow) { let window = window.clone(); @@ -156,9 +187,6 @@ fn prepare_native(app: &AppHandle, window: &WebviewWindow) { if let Err(err) = platform::windows::apply_toolwindow(&window) { log::warn!("{PANEL_LABEL} may show in Alt-Tab: {err}"); } - if let Err(err) = platform::windows::disable_browser_accelerator_keys(&window) { - log::warn!("{PANEL_LABEL} keeps the browser accelerator keys: {err}"); - } }); if let Err(err) = queued { log::warn!("panel set-up could not reach the main thread: {err}"); diff --git a/src/platform/windows.rs b/src/platform/windows.rs index b80ef5a..3dfa276 100644 --- a/src/platform/windows.rs +++ b/src/platform/windows.rs @@ -254,8 +254,10 @@ pub fn round_corners(window: &tauri::WebviewWindow) -> io::Result<()> { /// Turn off WebView2's browser accelerator keys (F5, Ctrl+R, Ctrl+P, /// Ctrl+F, zoom, F12 and the like) for `window`'s webview, then reload it, -/// because the setting applies from the next navigation. Runs on the main -/// thread; a failure inside the webview is logged there. +/// because the setting applies from the next navigation. Call it only after +/// the first navigation has finished, or the reload can replace that +/// navigation. The work runs on the main thread; a failure inside the +/// webview is logged there. pub fn disable_browser_accelerator_keys(window: &tauri::WebviewWindow) -> io::Result<()> { let label = window.label().to_string(); window