Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions rivet/platform/windows/src/row_watch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@ pub const FEATURES: &[&str] = &[
"split-grid-3",
"split-grid-4",
"bar-layout",
"bar-folded",
"mini-pop-out",
"mini-move",
"mini-return",
Expand Down
33 changes: 31 additions & 2 deletions rivet/runtime/windows/src/features/address_bar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -692,14 +692,22 @@ pub(crate) unsafe fn with_field_pixels<R>(
})
}

unsafe fn part_at(window: HWND, point: POINT) -> Option<Part> {
/// The control of the top bar under a client pixel. The window's hit test asks it too, so the bar
/// 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<Part> {
let [x, y, width] = place(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)
}

/// 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<Part> {
if py < 0.0 || py >= ROW_HEIGHT as f32 {
return None;
}
let parts = parts_reserving(width, in_pane(window), dev_reserve(window));
let parts = parts_reserving(width, pane, reserve);
// The chip lies inside the address, so it is looked for first.
parts
.iter()
Expand Down Expand Up @@ -1709,6 +1717,27 @@ pub(crate) fn caret(window: HWND) -> Option<RECT> {
mod tests {
use super::*;

#[test]
fn fr12_each_control_answers_where_it_is_painted_shown_and_folded() {
// A bar beside the shown appbar and the wider bar of a folded one, as the browser lays
// the panel out for each.
for width in [1156.0, 1422.0] {
for (part, at, w) in parts_reserving(width, false, 0.0) {
if part == Part::Chip {
continue;
}
let middle = at + w / 2.0;
assert_eq!(
part_in(width, false, 0.0, middle, ROW_HEIGHT as f32 / 2.0),
Some(part),
"{part:?} of a {width} DIP bar"
);
}
assert_eq!(part_in(width, false, 0.0, 10.0, -1.0), None);
assert_eq!(part_in(width, false, 0.0, 10.0, ROW_HEIGHT as f32), None);
}
}

#[test]
fn nfr2_one_key_asks_the_browser_once_and_rivets_own_completion_never() {
// A key down and up leave the text alone, so only the character asks.
Expand Down
20 changes: 20 additions & 0 deletions rivet/runtime/windows/src/frame.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3460,9 +3460,19 @@ fn panel_moved(window: HWND) {
}
}
});
// The top bar is the panel's first row, so it moves with the panel. It is painted after
// this layout, never inside it, and until then its old place would show where the bar no
// longer takes clicks, as after the appbar folds.
PostMessageW(window, bar_moved_message(), 0, 0);
}
}

const BAR_MOVED_MESSAGE: &str = "Rivet.BarMoved";

fn bar_moved_message() -> u32 {
unsafe { RegisterWindowMessageW(wide(BAR_MOVED_MESSAGE).as_ptr()) }
}

/// The thickness of the visible window border along the top edge. A maximized window has none.
unsafe fn top_border(window: HWND) -> i32 {
if IsZoomed(window) != 0 {
Expand Down Expand Up @@ -3589,6 +3599,11 @@ unsafe fn hit_test(window: HWND, lparam: LPARAM) -> StripHit {
&& row_hit(window, point).is_none()
&& crate::dev_bar::hit(window, point).is_none()
{
// The bar's controls are Rivet's, at the places it painted them, whatever the browser's
// own hit test would say of its title bar there.
if crate::features::address_bar::part_at(window, point).is_some() {
return StripHit::Client;
}
let mut client: RECT = std::mem::zeroed();
GetClientRect(window, &mut client);
// The panel starts one gap below the client area's top edge, so the row is measured from it.
Expand Down Expand Up @@ -3767,6 +3782,10 @@ unsafe fn handle(window: HWND, message: u32, wparam: WPARAM, lparam: LPARAM) ->
toggle_appbar(window);
return 0;
}
if message == bar_moved_message() && attached(window) {
update_top_bar(window);
return 0;
}
// Rivet's top bar takes the pointer over it and, while its address is edited, the keyboard and
// the input method.
if attached(window) && !menu_open(window) && !tab_dragging(window) && !dragging(window) {
Expand Down Expand Up @@ -4145,6 +4164,7 @@ unsafe fn handle(window: HWND, message: u32, wparam: WPARAM, lparam: LPARAM) ->
StripHit::Top => HTTOP as LRESULT,
StripHit::TopLeft => HTTOPLEFT as LRESULT,
StripHit::Appbar | StripHit::Resizer => APPBAR_HIT as LRESULT,
StripHit::Client => 1, // HTCLIENT
StripHit::Minimize => HTMINBUTTON as LRESULT,
StripHit::Maximize => HTMAXBUTTON as LRESULT,
StripHit::Close => HTCLOSE as LRESULT,
Expand Down
3 changes: 3 additions & 0 deletions rivet/runtime/windows/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,9 @@ enum StripHit {
Resizer,
/// Belongs to the appbar's own content.
Appbar,
/// A control of Rivet's top bar, which takes its clicks in the client area.
#[cfg_attr(not(windows), allow(dead_code))]
Client,
/// The caption buttons, which Windows needs by name for Snap Layouts.
#[cfg_attr(not(windows), allow(dead_code))]
Minimize,
Expand Down
2 changes: 1 addition & 1 deletion scripts/tests/windows-features.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
# rows run only when no other run is active (windows-test-safety.ps1).
param(
[string[]]$Browsers = @('chrome', 'edge', 'whale', 'brave', 'samsung'),
[string[]]$Features = @('split', 'sidebar', 'newtab', 'space', 'split-start', 'split-menu', 'split-menu-start', 'split-drag', 'split-drag-start', 'split-shortcut', 'split-one-tab', 'split-maximised', 'split-space', 'split-click', 'split-rows', 'split-drop-top', 'split-drop-bottom', 'split-grid-3', 'split-grid-4', 'bar-layout', 'mini-pop-out', 'mini-move', 'mini-return', 'mini-close', 'tab-sections', 'tab-close-select', 'tab-reopen', 'tab-archive', 'ctrl-t-setting', 'link-new-tab', 'command-bar', 'link-preview', 'rivet-settings', 'link-rules', 'address-suggest', 'appbar-width', 'appbar-peek', 'open-link-running', 'open-link-start', 'credentials-browser', 'credentials-system', 'view-click', 'view-click-other', 'view-keys', 'view-ime', 'tab-moves', 'first-frame', 'fullscreen-sidebar', 'taskbar-identity', 'taskbar-relaunch', 'sidebar-scale-monitor', 'split-panel-look', 'sidebar-floats', 'sidebar-open-button', 'dev-row', 'address-editing', 'security-chip', 'permission-bubble', 'password-bubble', 'typing-keeps-active', 'cli-control', 'cli-input', 'cli-hidden'),
[string[]]$Features = @('split', 'sidebar', 'newtab', 'space', 'split-start', 'split-menu', 'split-menu-start', 'split-drag', 'split-drag-start', 'split-shortcut', 'split-one-tab', 'split-maximised', 'split-space', 'split-click', 'split-rows', 'split-drop-top', 'split-drop-bottom', 'split-grid-3', 'split-grid-4', 'bar-layout', 'bar-folded', 'mini-pop-out', 'mini-move', 'mini-return', 'mini-close', 'tab-sections', 'tab-close-select', 'tab-reopen', 'tab-archive', 'ctrl-t-setting', 'link-new-tab', 'command-bar', 'link-preview', 'rivet-settings', 'link-rules', 'address-suggest', 'appbar-width', 'appbar-peek', 'open-link-running', 'open-link-start', 'credentials-browser', 'credentials-system', 'view-click', 'view-click-other', 'view-keys', 'view-ime', 'tab-moves', 'first-frame', 'fullscreen-sidebar', 'taskbar-identity', 'taskbar-relaunch', 'sidebar-scale-monitor', 'split-panel-look', 'sidebar-floats', 'sidebar-open-button', 'dev-row', 'address-editing', 'security-chip', 'permission-bubble', 'password-bubble', 'typing-keeps-active', 'cli-control', 'cli-input', 'cli-hidden'),
[string]$OutDir,
[switch]$NoBuild,
# A browser build outside its usual folder, such as one a row job downloaded. Needs one -Browsers.
Expand Down
Loading