diff --git a/rivet/runtime/windows/src/features/address_bar.rs b/rivet/runtime/windows/src/features/address_bar.rs index eb6ebf4..6bec37f 100644 --- a/rivet/runtime/windows/src/features/address_bar.rs +++ b/rivet/runtime/windows/src/features/address_bar.rs @@ -283,6 +283,8 @@ struct Bar { refresh_inline: bool, /// The caret the input method's windows were last placed at, in client pixels. placed: Option<[i32; 4]>, + /// The bar's place, left, top and width in DIPs, when it was last drawn. + drawn_at: Option<[f32; 3]>, } thread_local! { @@ -312,6 +314,7 @@ fn with_bar(window: HWND, work: impl FnOnce(&mut Bar) -> R) -> R { refresh_step: None, refresh_inline: false, placed: None, + drawn_at: None, }); work(bar) }) @@ -607,6 +610,7 @@ pub(crate) unsafe fn view(window: HWND) -> Option { if bar.field.as_ref().is_some_and(|field| field.take_changed()) { bar.generation += 1; } + bar.drawn_at = Some([x, y, width]); View { dev, url: url.clone(), @@ -696,11 +700,21 @@ pub(crate) unsafe fn with_field_pixels( /// takes the clicks of its controls wherever the browser would put its own title bar. pub(crate) unsafe fn part_at(window: HWND, point: POINT) -> Option { let [x, y, width] = place(window)?; + // The bar takes clicks where it is now. When it was drawn elsewhere, as when the browser moved + // its panel without a repaint after, it is drawn again here before the click can come. + if !busy() && needs_redraw_at(with_bar(window, |bar| bar.drawn_at), [x, y, width]) { + crate::frame::request_bar_paint(window); + } let f = factor(window); let (px, py) = (point.x as f32 / f - x, point.y as f32 / f - y); part_in(width, in_pane(window), dev_reserve(window), px, py) } +/// Whether a bar drawn at `drawn` must be drawn again to show at `now`, its place for clicks. +pub(crate) fn needs_redraw_at(drawn: Option<[f32; 3]>, now: [f32; 3]) -> bool { + drawn.is_some_and(|drawn| drawn != now) +} + /// The control of a bar `width` DIPs wide at `px`, `py` DIPs from its top left corner, from the /// same places the bar is painted at. pub(crate) fn part_in(width: f32, pane: bool, reserve: f32, px: f32, py: f32) -> Option { @@ -1771,6 +1785,18 @@ mod tests { assert!(!needs_query(true, "exa", "exa", "mple.com")); } + #[test] + fn fr12_a_bar_drawn_where_it_no_longer_takes_clicks_is_drawn_again() { + // Shown beside the appbar, then folded: the panel moved left and grew. + let shown = [276.0, 8.0, 1156.0]; + let folded = [8.0, 8.0, 1424.0]; + assert!(needs_redraw_at(Some(shown), folded)); + assert!(needs_redraw_at(Some(folded), shown)); + assert!(!needs_redraw_at(Some(folded), folded)); + // A bar never drawn yet is drawn with the window's first paint. + assert!(!needs_redraw_at(None, folded)); + } + #[test] fn nfr2_a_key_that_shows_nothing_new_draws_nothing() { let before = Shown { diff --git a/rivet/runtime/windows/src/frame.rs b/rivet/runtime/windows/src/frame.rs index 5fbe88b..22c6e35 100644 --- a/rivet/runtime/windows/src/frame.rs +++ b/rivet/runtime/windows/src/frame.rs @@ -2147,6 +2147,8 @@ unsafe fn toggle_appbar(window: HWND) { SLIDE.with(|slides| slides.borrow_mut().remove(&(window as isize))); KillTimer(window, PEEK_TIMER); relayout(window); + // The panel moves with the appbar, and the top bar with it. + request_bar_paint(window); } /// Shows or puts away the floating appbar of a hidden one, sliding it. @@ -3469,6 +3471,13 @@ fn panel_moved(window: HWND) { const BAR_MOVED_MESSAGE: &str = "Rivet.BarMoved"; +/// Asks for the top bar to be painted again after the message being handled. +pub(crate) fn request_bar_paint(window: HWND) { + unsafe { + PostMessageW(window, bar_moved_message(), 0, 0); + } +} + fn bar_moved_message() -> u32 { unsafe { RegisterWindowMessageW(wide(BAR_MOVED_MESSAGE).as_ptr()) } }