gpui: Support hitbox blocking mouse interaction except scrolling (#31712)

tl;dr: This adds `.block_mouse_except_scroll()` which should typically
be used instead of `.occlude()` for cases when the mouse shouldn't
interact with elements drawn below an element. The rationale for
treating scroll events differently:

* Mouse move / click / styles / tooltips are for elements the user is
interacting with directly.
* Mouse scroll events are about finding the current outer scroll
container.

Most use of `occlude` should probably be switched to this, but I figured
I'd derisk this change by minimizing behavior changes to just the 3 uses
of `block_mouse_except_scroll`.

GPUI changes:

* Added `InteractiveElement::block_mouse_except_scroll()`, and removes
`stop_mouse_events_except_scroll()`

* Added `Hitbox::should_handle_scroll()` to be used when handling scroll
wheel events.

* `Window::insert_hitbox` now takes `HitboxBehavior` instead of
`occlude: bool`.

    - `false` for that bool is now `HitboxBehavior::Normal`.

    - `true` for that bool is now `HitboxBehavior::BlockMouse`.
    
    - The new mode is `HitboxBehavior::BlockMouseExceptScroll`.

* Removes `Default` impl for `HitboxId` since applications should not
manually create `HitboxId(0)`.

Release Notes:

- N/A
This commit is contained in:
Michael Sloan 2025-05-29 15:41:15 -06:00 committed by GitHub
parent 2abc5893c1
commit 9086784038
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 231 additions and 99 deletions

View file

@ -136,7 +136,9 @@ pub struct IndentGuideLayout {
/// Implements the necessary functionality for rendering indent guides inside a uniform list.
mod uniform_list {
use gpui::{DispatchPhase, Hitbox, MouseButton, MouseDownEvent, MouseMoveEvent};
use gpui::{
DispatchPhase, Hitbox, HitboxBehavior, MouseButton, MouseDownEvent, MouseMoveEvent,
};
use super::*;
@ -256,7 +258,12 @@ mod uniform_list {
.indent_guides
.as_ref()
.iter()
.map(|guide| window.insert_hitbox(guide.hitbox.unwrap_or(guide.bounds), false))
.map(|guide| {
window.insert_hitbox(
guide.hitbox.unwrap_or(guide.bounds),
HitboxBehavior::Normal,
)
})
.collect();
Self::PrepaintState::Interactive {
hitboxes: Rc::new(hitboxes),

View file

@ -2,9 +2,9 @@ use std::{cell::RefCell, rc::Rc};
use gpui::{
AnyElement, AnyView, App, Bounds, Corner, DismissEvent, DispatchPhase, Element, ElementId,
Entity, Focusable as _, GlobalElementId, HitboxId, InteractiveElement, IntoElement, LayoutId,
Length, ManagedView, MouseDownEvent, ParentElement, Pixels, Point, Style, Window, anchored,
deferred, div, point, prelude::FluentBuilder, px, size,
Entity, Focusable as _, GlobalElementId, HitboxBehavior, HitboxId, InteractiveElement,
IntoElement, LayoutId, Length, ManagedView, MouseDownEvent, ParentElement, Pixels, Point,
Style, Window, anchored, deferred, div, point, prelude::FluentBuilder, px, size,
};
use crate::prelude::*;
@ -421,7 +421,7 @@ impl<M: ManagedView> Element for PopoverMenu<M> {
((), element_state)
});
window.insert_hitbox(bounds, false).id
window.insert_hitbox(bounds, HitboxBehavior::Normal).id
})
}

View file

@ -2,9 +2,9 @@ use std::{cell::RefCell, rc::Rc};
use gpui::{
AnyElement, App, Bounds, Corner, DismissEvent, DispatchPhase, Element, ElementId, Entity,
Focusable as _, GlobalElementId, Hitbox, InteractiveElement, IntoElement, LayoutId,
ManagedView, MouseButton, MouseDownEvent, ParentElement, Pixels, Point, Window, anchored,
deferred, div, px,
Focusable as _, GlobalElementId, Hitbox, HitboxBehavior, InteractiveElement, IntoElement,
LayoutId, ManagedView, MouseButton, MouseDownEvent, ParentElement, Pixels, Point, Window,
anchored, deferred, div, px,
};
pub struct RightClickMenu<M: ManagedView> {
@ -185,7 +185,7 @@ impl<M: ManagedView> Element for RightClickMenu<M> {
window: &mut Window,
cx: &mut App,
) -> PrepaintState {
let hitbox = window.insert_hitbox(bounds, false);
let hitbox = window.insert_hitbox(bounds, HitboxBehavior::Normal);
if let Some(child) = request_layout.child_element.as_mut() {
child.prepaint(window, cx);

View file

@ -3,9 +3,9 @@ use std::{any::Any, cell::Cell, fmt::Debug, ops::Range, rc::Rc, sync::Arc};
use crate::{IntoElement, prelude::*, px, relative};
use gpui::{
Along, App, Axis as ScrollbarAxis, BorderStyle, Bounds, ContentMask, Corners, Edges, Element,
ElementId, Entity, EntityId, GlobalElementId, Hitbox, Hsla, IsZero, LayoutId, ListState,
MouseDownEvent, MouseMoveEvent, MouseUpEvent, Pixels, Point, ScrollHandle, ScrollWheelEvent,
Size, Style, UniformListScrollHandle, Window, quad,
ElementId, Entity, EntityId, GlobalElementId, Hitbox, HitboxBehavior, Hsla, IsZero, LayoutId,
ListState, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Pixels, Point, ScrollHandle,
ScrollWheelEvent, Size, Style, UniformListScrollHandle, Window, quad,
};
pub struct Scrollbar {
@ -226,7 +226,7 @@ impl Element for Scrollbar {
_: &mut App,
) -> Self::PrepaintState {
window.with_content_mask(Some(ContentMask { bounds }), |window| {
window.insert_hitbox(bounds, false)
window.insert_hitbox(bounds, HitboxBehavior::Normal)
})
}