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:
parent
2abc5893c1
commit
9086784038
16 changed files with 231 additions and 99 deletions
|
@ -2162,7 +2162,7 @@ impl ActiveThread {
|
||||||
.inset_0()
|
.inset_0()
|
||||||
.bg(panel_bg)
|
.bg(panel_bg)
|
||||||
.opacity(0.8)
|
.opacity(0.8)
|
||||||
.stop_mouse_events_except_scroll()
|
.block_mouse_except_scroll()
|
||||||
.on_click(cx.listener(Self::handle_cancel_click));
|
.on_click(cx.listener(Self::handle_cancel_click));
|
||||||
|
|
||||||
v_flex()
|
v_flex()
|
||||||
|
|
|
@ -699,7 +699,7 @@ fn render_diff_hunk_controls(
|
||||||
.rounded_b_md()
|
.rounded_b_md()
|
||||||
.bg(cx.theme().colors().editor_background)
|
.bg(cx.theme().colors().editor_background)
|
||||||
.gap_1()
|
.gap_1()
|
||||||
.stop_mouse_events_except_scroll()
|
.block_mouse_except_scroll()
|
||||||
.shadow_md()
|
.shadow_md()
|
||||||
.children(vec![
|
.children(vec![
|
||||||
Button::new(("reject", row as u64), "Reject")
|
Button::new(("reject", row as u64), "Reject")
|
||||||
|
|
|
@ -21907,7 +21907,7 @@ fn render_diff_hunk_controls(
|
||||||
.rounded_b_lg()
|
.rounded_b_lg()
|
||||||
.bg(cx.theme().colors().editor_background)
|
.bg(cx.theme().colors().editor_background)
|
||||||
.gap_1()
|
.gap_1()
|
||||||
.stop_mouse_events_except_scroll()
|
.block_mouse_except_scroll()
|
||||||
.shadow_md()
|
.shadow_md()
|
||||||
.child(if status.has_secondary_hunk() {
|
.child(if status.has_secondary_hunk() {
|
||||||
Button::new(("stage", row as u64), "Stage")
|
Button::new(("stage", row as u64), "Stage")
|
||||||
|
|
|
@ -42,13 +42,13 @@ use git::{
|
||||||
use gpui::{
|
use gpui::{
|
||||||
Action, Along, AnyElement, App, AppContext, AvailableSpace, Axis as ScrollbarAxis, BorderStyle,
|
Action, Along, AnyElement, App, AppContext, AvailableSpace, Axis as ScrollbarAxis, BorderStyle,
|
||||||
Bounds, ClickEvent, ContentMask, Context, Corner, Corners, CursorStyle, DispatchPhase, Edges,
|
Bounds, ClickEvent, ContentMask, Context, Corner, Corners, CursorStyle, DispatchPhase, Edges,
|
||||||
Element, ElementInputHandler, Entity, Focusable as _, FontId, GlobalElementId, Hitbox, Hsla,
|
Element, ElementInputHandler, Entity, Focusable as _, FontId, GlobalElementId, Hitbox,
|
||||||
InteractiveElement, IntoElement, IsZero, Keystroke, Length, ModifiersChangedEvent, MouseButton,
|
HitboxBehavior, Hsla, InteractiveElement, IntoElement, IsZero, Keystroke, Length,
|
||||||
MouseDownEvent, MouseMoveEvent, MouseUpEvent, PaintQuad, ParentElement, Pixels, ScrollDelta,
|
ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, PaintQuad,
|
||||||
ScrollHandle, ScrollWheelEvent, ShapedLine, SharedString, Size, StatefulInteractiveElement,
|
ParentElement, Pixels, ScrollDelta, ScrollHandle, ScrollWheelEvent, ShapedLine, SharedString,
|
||||||
Style, Styled, TextRun, TextStyleRefinement, WeakEntity, Window, anchored, deferred, div, fill,
|
Size, StatefulInteractiveElement, Style, Styled, TextRun, TextStyleRefinement, WeakEntity,
|
||||||
linear_color_stop, linear_gradient, outline, point, px, quad, relative, size, solid_background,
|
Window, anchored, deferred, div, fill, linear_color_stop, linear_gradient, outline, point, px,
|
||||||
transparent_black,
|
quad, relative, size, solid_background, transparent_black,
|
||||||
};
|
};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use language::language_settings::{
|
use language::language_settings::{
|
||||||
|
@ -1620,7 +1620,7 @@ impl EditorElement {
|
||||||
);
|
);
|
||||||
|
|
||||||
let layout = ScrollbarLayout::for_minimap(
|
let layout = ScrollbarLayout::for_minimap(
|
||||||
window.insert_hitbox(minimap_bounds, false),
|
window.insert_hitbox(minimap_bounds, HitboxBehavior::Normal),
|
||||||
visible_editor_lines,
|
visible_editor_lines,
|
||||||
total_editor_lines,
|
total_editor_lines,
|
||||||
minimap_line_height,
|
minimap_line_height,
|
||||||
|
@ -1791,7 +1791,7 @@ impl EditorElement {
|
||||||
if matches!(hunk, DisplayDiffHunk::Unfolded { .. }) {
|
if matches!(hunk, DisplayDiffHunk::Unfolded { .. }) {
|
||||||
let hunk_bounds =
|
let hunk_bounds =
|
||||||
Self::diff_hunk_bounds(snapshot, line_height, gutter_hitbox.bounds, hunk);
|
Self::diff_hunk_bounds(snapshot, line_height, gutter_hitbox.bounds, hunk);
|
||||||
*hitbox = Some(window.insert_hitbox(hunk_bounds, true));
|
*hitbox = Some(window.insert_hitbox(hunk_bounds, HitboxBehavior::BlockMouse));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2883,7 +2883,7 @@ impl EditorElement {
|
||||||
let hitbox = line_origin.map(|line_origin| {
|
let hitbox = line_origin.map(|line_origin| {
|
||||||
window.insert_hitbox(
|
window.insert_hitbox(
|
||||||
Bounds::new(line_origin, size(shaped_line.width, line_height)),
|
Bounds::new(line_origin, size(shaped_line.width, line_height)),
|
||||||
false,
|
HitboxBehavior::Normal,
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -6371,7 +6371,7 @@ impl EditorElement {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if phase == DispatchPhase::Bubble && hitbox.is_hovered(window) {
|
if phase == DispatchPhase::Bubble && hitbox.should_handle_scroll(window) {
|
||||||
delta = delta.coalesce(event.delta);
|
delta = delta.coalesce(event.delta);
|
||||||
editor.update(cx, |editor, cx| {
|
editor.update(cx, |editor, cx| {
|
||||||
let position_map: &PositionMap = &position_map;
|
let position_map: &PositionMap = &position_map;
|
||||||
|
@ -7651,15 +7651,17 @@ impl Element for EditorElement {
|
||||||
.map(|(guide, active)| (self.column_pixels(*guide, window, cx), *active))
|
.map(|(guide, active)| (self.column_pixels(*guide, window, cx), *active))
|
||||||
.collect::<SmallVec<[_; 2]>>();
|
.collect::<SmallVec<[_; 2]>>();
|
||||||
|
|
||||||
let hitbox = window.insert_hitbox(bounds, false);
|
let hitbox = window.insert_hitbox(bounds, HitboxBehavior::Normal);
|
||||||
let gutter_hitbox =
|
let gutter_hitbox = window.insert_hitbox(
|
||||||
window.insert_hitbox(gutter_bounds(bounds, gutter_dimensions), false);
|
gutter_bounds(bounds, gutter_dimensions),
|
||||||
|
HitboxBehavior::Normal,
|
||||||
|
);
|
||||||
let text_hitbox = window.insert_hitbox(
|
let text_hitbox = window.insert_hitbox(
|
||||||
Bounds {
|
Bounds {
|
||||||
origin: gutter_hitbox.top_right(),
|
origin: gutter_hitbox.top_right(),
|
||||||
size: size(text_width, bounds.size.height),
|
size: size(text_width, bounds.size.height),
|
||||||
},
|
},
|
||||||
false,
|
HitboxBehavior::Normal,
|
||||||
);
|
);
|
||||||
|
|
||||||
let content_origin = text_hitbox.origin + content_offset;
|
let content_origin = text_hitbox.origin + content_offset;
|
||||||
|
@ -8880,7 +8882,7 @@ impl EditorScrollbars {
|
||||||
})
|
})
|
||||||
.map(|(viewport_size, scroll_range)| {
|
.map(|(viewport_size, scroll_range)| {
|
||||||
ScrollbarLayout::new(
|
ScrollbarLayout::new(
|
||||||
window.insert_hitbox(scrollbar_bounds_for(axis), false),
|
window.insert_hitbox(scrollbar_bounds_for(axis), HitboxBehavior::Normal),
|
||||||
viewport_size,
|
viewport_size,
|
||||||
scroll_range,
|
scroll_range,
|
||||||
glyph_grid_cell.along(axis),
|
glyph_grid_cell.along(axis),
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
use gpui::{
|
use gpui::{
|
||||||
App, Application, Bounds, Context, CursorStyle, Decorations, Hsla, MouseButton, Pixels, Point,
|
App, Application, Bounds, Context, CursorStyle, Decorations, HitboxBehavior, Hsla, MouseButton,
|
||||||
ResizeEdge, Size, Window, WindowBackgroundAppearance, WindowBounds, WindowDecorations,
|
Pixels, Point, ResizeEdge, Size, Window, WindowBackgroundAppearance, WindowBounds,
|
||||||
WindowOptions, black, canvas, div, green, point, prelude::*, px, rgb, size, transparent_black,
|
WindowDecorations, WindowOptions, black, canvas, div, green, point, prelude::*, px, rgb, size,
|
||||||
white,
|
transparent_black, white,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct WindowShadow {}
|
struct WindowShadow {}
|
||||||
|
@ -37,7 +37,7 @@ impl Render for WindowShadow {
|
||||||
point(px(0.0), px(0.0)),
|
point(px(0.0), px(0.0)),
|
||||||
window.window_bounds().get_bounds().size,
|
window.window_bounds().get_bounds().size,
|
||||||
),
|
),
|
||||||
false,
|
HitboxBehavior::Normal,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
move |_bounds, hitbox, window, _cx| {
|
move |_bounds, hitbox, window, _cx| {
|
||||||
|
|
|
@ -17,10 +17,10 @@
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Action, AnyDrag, AnyElement, AnyTooltip, AnyView, App, Bounds, ClickEvent, DispatchPhase,
|
Action, AnyDrag, AnyElement, AnyTooltip, AnyView, App, Bounds, ClickEvent, DispatchPhase,
|
||||||
Element, ElementId, Entity, FocusHandle, Global, GlobalElementId, Hitbox, HitboxId,
|
Element, ElementId, Entity, FocusHandle, Global, GlobalElementId, Hitbox, HitboxBehavior,
|
||||||
InspectorElementId, IntoElement, IsZero, KeyContext, KeyDownEvent, KeyUpEvent, LayoutId,
|
HitboxId, InspectorElementId, IntoElement, IsZero, KeyContext, KeyDownEvent, KeyUpEvent,
|
||||||
ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Overflow,
|
LayoutId, ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent,
|
||||||
ParentElement, Pixels, Point, Render, ScrollWheelEvent, SharedString, Size, Style,
|
Overflow, ParentElement, Pixels, Point, Render, ScrollWheelEvent, SharedString, Size, Style,
|
||||||
StyleRefinement, Styled, Task, TooltipId, Visibility, Window, point, px, size,
|
StyleRefinement, Styled, Task, TooltipId, Visibility, Window, point, px, size,
|
||||||
};
|
};
|
||||||
use collections::HashMap;
|
use collections::HashMap;
|
||||||
|
@ -313,7 +313,7 @@ impl Interactivity {
|
||||||
) {
|
) {
|
||||||
self.scroll_wheel_listeners
|
self.scroll_wheel_listeners
|
||||||
.push(Box::new(move |event, phase, hitbox, window, cx| {
|
.push(Box::new(move |event, phase, hitbox, window, cx| {
|
||||||
if phase == DispatchPhase::Bubble && hitbox.is_hovered(window) {
|
if phase == DispatchPhase::Bubble && hitbox.should_handle_scroll(window) {
|
||||||
(listener)(event, window, cx);
|
(listener)(event, window, cx);
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
@ -567,19 +567,20 @@ impl Interactivity {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Block the mouse from interacting with this element or any of its children
|
/// Block the mouse from all interactions with elements behind this element's hitbox. Typically
|
||||||
|
/// `block_mouse_except_scroll` should be preferred.
|
||||||
|
///
|
||||||
/// The imperative API equivalent to [`InteractiveElement::occlude`]
|
/// The imperative API equivalent to [`InteractiveElement::occlude`]
|
||||||
pub fn occlude_mouse(&mut self) {
|
pub fn occlude_mouse(&mut self) {
|
||||||
self.occlude_mouse = true;
|
self.hitbox_behavior = HitboxBehavior::BlockMouse;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Registers event handles that stop propagation of mouse events for non-scroll events.
|
/// Block non-scroll mouse interactions with elements behind this element's hitbox. See
|
||||||
|
/// [`Hitbox::is_hovered`] for details.
|
||||||
|
///
|
||||||
/// The imperative API equivalent to [`InteractiveElement::block_mouse_except_scroll`]
|
/// The imperative API equivalent to [`InteractiveElement::block_mouse_except_scroll`]
|
||||||
pub fn stop_mouse_events_except_scroll(&mut self) {
|
pub fn block_mouse_except_scroll(&mut self) {
|
||||||
self.on_any_mouse_down(|_, _, cx| cx.stop_propagation());
|
self.hitbox_behavior = HitboxBehavior::BlockMouseExceptScroll;
|
||||||
self.on_any_mouse_up(|_, _, cx| cx.stop_propagation());
|
|
||||||
self.on_click(|_, _, cx| cx.stop_propagation());
|
|
||||||
self.on_hover(|_, _, cx| cx.stop_propagation());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -949,7 +950,8 @@ pub trait InteractiveElement: Sized {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Block the mouse from interacting with this element or any of its children
|
/// Block the mouse from all interactions with elements behind this element's hitbox. Typically
|
||||||
|
/// `block_mouse_except_scroll` should be preferred.
|
||||||
/// The fluent API equivalent to [`Interactivity::occlude_mouse`]
|
/// The fluent API equivalent to [`Interactivity::occlude_mouse`]
|
||||||
fn occlude(mut self) -> Self {
|
fn occlude(mut self) -> Self {
|
||||||
self.interactivity().occlude_mouse();
|
self.interactivity().occlude_mouse();
|
||||||
|
@ -961,10 +963,12 @@ pub trait InteractiveElement: Sized {
|
||||||
self.on_mouse_down(MouseButton::Left, |_, _, cx| cx.stop_propagation())
|
self.on_mouse_down(MouseButton::Left, |_, _, cx| cx.stop_propagation())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Registers event handles that stop propagation of mouse events for non-scroll events.
|
/// Block non-scroll mouse interactions with elements behind this element's hitbox. See
|
||||||
|
/// [`Hitbox::is_hovered`] for details.
|
||||||
|
///
|
||||||
/// The fluent API equivalent to [`Interactivity::block_mouse_except_scroll`]
|
/// The fluent API equivalent to [`Interactivity::block_mouse_except_scroll`]
|
||||||
fn stop_mouse_events_except_scroll(mut self) -> Self {
|
fn block_mouse_except_scroll(mut self) -> Self {
|
||||||
self.interactivity().stop_mouse_events_except_scroll();
|
self.interactivity().block_mouse_except_scroll();
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1448,7 +1452,7 @@ pub struct Interactivity {
|
||||||
pub(crate) drag_listener: Option<(Arc<dyn Any>, DragListener)>,
|
pub(crate) drag_listener: Option<(Arc<dyn Any>, DragListener)>,
|
||||||
pub(crate) hover_listener: Option<Box<dyn Fn(&bool, &mut Window, &mut App)>>,
|
pub(crate) hover_listener: Option<Box<dyn Fn(&bool, &mut Window, &mut App)>>,
|
||||||
pub(crate) tooltip_builder: Option<TooltipBuilder>,
|
pub(crate) tooltip_builder: Option<TooltipBuilder>,
|
||||||
pub(crate) occlude_mouse: bool,
|
pub(crate) hitbox_behavior: HitboxBehavior,
|
||||||
|
|
||||||
#[cfg(any(feature = "inspector", debug_assertions))]
|
#[cfg(any(feature = "inspector", debug_assertions))]
|
||||||
pub(crate) source_location: Option<&'static core::panic::Location<'static>>,
|
pub(crate) source_location: Option<&'static core::panic::Location<'static>>,
|
||||||
|
@ -1594,7 +1598,7 @@ impl Interactivity {
|
||||||
style.overflow_mask(bounds, window.rem_size()),
|
style.overflow_mask(bounds, window.rem_size()),
|
||||||
|window| {
|
|window| {
|
||||||
let hitbox = if self.should_insert_hitbox(&style, window, cx) {
|
let hitbox = if self.should_insert_hitbox(&style, window, cx) {
|
||||||
Some(window.insert_hitbox(bounds, self.occlude_mouse))
|
Some(window.insert_hitbox(bounds, self.hitbox_behavior))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
@ -1611,7 +1615,7 @@ impl Interactivity {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn should_insert_hitbox(&self, style: &Style, window: &Window, cx: &App) -> bool {
|
fn should_insert_hitbox(&self, style: &Style, window: &Window, cx: &App) -> bool {
|
||||||
self.occlude_mouse
|
self.hitbox_behavior != HitboxBehavior::Normal
|
||||||
|| style.mouse_cursor.is_some()
|
|| style.mouse_cursor.is_some()
|
||||||
|| self.group.is_some()
|
|| self.group.is_some()
|
||||||
|| self.scroll_offset.is_some()
|
|| self.scroll_offset.is_some()
|
||||||
|
@ -2270,7 +2274,7 @@ impl Interactivity {
|
||||||
let hitbox = hitbox.clone();
|
let hitbox = hitbox.clone();
|
||||||
let current_view = window.current_view();
|
let current_view = window.current_view();
|
||||||
window.on_mouse_event(move |event: &ScrollWheelEvent, phase, window, cx| {
|
window.on_mouse_event(move |event: &ScrollWheelEvent, phase, window, cx| {
|
||||||
if phase == DispatchPhase::Bubble && hitbox.is_hovered(window) {
|
if phase == DispatchPhase::Bubble && hitbox.should_handle_scroll(window) {
|
||||||
let mut scroll_offset = scroll_offset.borrow_mut();
|
let mut scroll_offset = scroll_offset.borrow_mut();
|
||||||
let old_scroll_offset = *scroll_offset;
|
let old_scroll_offset = *scroll_offset;
|
||||||
let delta = event.delta.pixel_delta(line_height);
|
let delta = event.delta.pixel_delta(line_height);
|
||||||
|
|
|
@ -9,8 +9,9 @@
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
AnyElement, App, AvailableSpace, Bounds, ContentMask, DispatchPhase, Edges, Element, EntityId,
|
AnyElement, App, AvailableSpace, Bounds, ContentMask, DispatchPhase, Edges, Element, EntityId,
|
||||||
FocusHandle, GlobalElementId, Hitbox, InspectorElementId, IntoElement, Overflow, Pixels, Point,
|
FocusHandle, GlobalElementId, Hitbox, HitboxBehavior, InspectorElementId, IntoElement,
|
||||||
ScrollWheelEvent, Size, Style, StyleRefinement, Styled, Window, point, px, size,
|
Overflow, Pixels, Point, ScrollWheelEvent, Size, Style, StyleRefinement, Styled, Window, point,
|
||||||
|
px, size,
|
||||||
};
|
};
|
||||||
use collections::VecDeque;
|
use collections::VecDeque;
|
||||||
use refineable::Refineable as _;
|
use refineable::Refineable as _;
|
||||||
|
@ -906,7 +907,7 @@ impl Element for List {
|
||||||
let mut style = Style::default();
|
let mut style = Style::default();
|
||||||
style.refine(&self.style);
|
style.refine(&self.style);
|
||||||
|
|
||||||
let hitbox = window.insert_hitbox(bounds, false);
|
let hitbox = window.insert_hitbox(bounds, HitboxBehavior::Normal);
|
||||||
|
|
||||||
// If the width of the list has changed, invalidate all cached item heights
|
// If the width of the list has changed, invalidate all cached item heights
|
||||||
if state.last_layout_bounds.map_or(true, |last_bounds| {
|
if state.last_layout_bounds.map_or(true, |last_bounds| {
|
||||||
|
@ -962,7 +963,7 @@ impl Element for List {
|
||||||
let scroll_top = prepaint.layout.scroll_top;
|
let scroll_top = prepaint.layout.scroll_top;
|
||||||
let hitbox_id = prepaint.hitbox.id;
|
let hitbox_id = prepaint.hitbox.id;
|
||||||
window.on_mouse_event(move |event: &ScrollWheelEvent, phase, window, cx| {
|
window.on_mouse_event(move |event: &ScrollWheelEvent, phase, window, cx| {
|
||||||
if phase == DispatchPhase::Bubble && hitbox_id.is_hovered(window) {
|
if phase == DispatchPhase::Bubble && hitbox_id.should_handle_scroll(window) {
|
||||||
list_state.0.borrow_mut().scroll(
|
list_state.0.borrow_mut().scroll(
|
||||||
&scroll_top,
|
&scroll_top,
|
||||||
height,
|
height,
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
ActiveTooltip, AnyView, App, Bounds, DispatchPhase, Element, ElementId, GlobalElementId,
|
ActiveTooltip, AnyView, App, Bounds, DispatchPhase, Element, ElementId, GlobalElementId,
|
||||||
HighlightStyle, Hitbox, InspectorElementId, IntoElement, LayoutId, MouseDownEvent,
|
HighlightStyle, Hitbox, HitboxBehavior, InspectorElementId, IntoElement, LayoutId,
|
||||||
MouseMoveEvent, MouseUpEvent, Pixels, Point, SharedString, Size, TextOverflow, TextRun,
|
MouseDownEvent, MouseMoveEvent, MouseUpEvent, Pixels, Point, SharedString, Size, TextOverflow,
|
||||||
TextStyle, TooltipId, WhiteSpace, Window, WrappedLine, WrappedLineLayout,
|
TextRun, TextStyle, TooltipId, WhiteSpace, Window, WrappedLine, WrappedLineLayout,
|
||||||
register_tooltip_mouse_handlers, set_tooltip_on_window,
|
register_tooltip_mouse_handlers, set_tooltip_on_window,
|
||||||
};
|
};
|
||||||
use anyhow::Context as _;
|
use anyhow::Context as _;
|
||||||
|
@ -739,7 +739,7 @@ impl Element for InteractiveText {
|
||||||
|
|
||||||
self.text
|
self.text
|
||||||
.prepaint(None, inspector_id, bounds, state, window, cx);
|
.prepaint(None, inspector_id, bounds, state, window, cx);
|
||||||
let hitbox = window.insert_hitbox(bounds, false);
|
let hitbox = window.insert_hitbox(bounds, HitboxBehavior::Normal);
|
||||||
(hitbox, interactive_state)
|
(hitbox, interactive_state)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
|
@ -413,14 +413,42 @@ pub(crate) struct CursorStyleRequest {
|
||||||
pub(crate) style: CursorStyle,
|
pub(crate) style: CursorStyle,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An identifier for a [Hitbox].
|
#[derive(Default, Eq, PartialEq)]
|
||||||
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Hash)]
|
pub(crate) struct HitTest {
|
||||||
pub struct HitboxId(usize);
|
pub(crate) ids: SmallVec<[HitboxId; 8]>,
|
||||||
|
pub(crate) hover_hitbox_count: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An identifier for a [Hitbox] which also includes [HitboxBehavior].
|
||||||
|
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||||
|
pub struct HitboxId(u64);
|
||||||
|
|
||||||
impl HitboxId {
|
impl HitboxId {
|
||||||
/// Checks if the hitbox with this id is currently hovered.
|
/// Checks if the hitbox with this ID is currently hovered. Except when handling
|
||||||
pub fn is_hovered(&self, window: &Window) -> bool {
|
/// `ScrollWheelEvent`, this is typically what you want when determining whether to handle mouse
|
||||||
window.mouse_hit_test.0.contains(self)
|
/// events or paint hover styles.
|
||||||
|
///
|
||||||
|
/// See [`Hitbox::is_hovered`] for details.
|
||||||
|
pub fn is_hovered(self, window: &Window) -> bool {
|
||||||
|
let hit_test = &window.mouse_hit_test;
|
||||||
|
for id in hit_test.ids.iter().take(hit_test.hover_hitbox_count) {
|
||||||
|
if self == *id {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks if the hitbox with this ID contains the mouse and should handle scroll events.
|
||||||
|
/// Typically this should only be used when handling `ScrollWheelEvent`, and otherwise
|
||||||
|
/// `is_hovered` should be used. See the documentation of `Hitbox::is_hovered` for details about
|
||||||
|
/// this distinction.
|
||||||
|
pub fn should_handle_scroll(self, window: &Window) -> bool {
|
||||||
|
window.mouse_hit_test.ids.contains(&self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next(mut self) -> HitboxId {
|
||||||
|
HitboxId(self.0.wrapping_add(1))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -435,19 +463,98 @@ pub struct Hitbox {
|
||||||
pub bounds: Bounds<Pixels>,
|
pub bounds: Bounds<Pixels>,
|
||||||
/// The content mask when the hitbox was inserted.
|
/// The content mask when the hitbox was inserted.
|
||||||
pub content_mask: ContentMask<Pixels>,
|
pub content_mask: ContentMask<Pixels>,
|
||||||
/// Whether the hitbox occludes other hitboxes inserted prior.
|
/// Flags that specify hitbox behavior.
|
||||||
pub opaque: bool,
|
pub behavior: HitboxBehavior,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Hitbox {
|
impl Hitbox {
|
||||||
/// Checks if the hitbox is currently hovered.
|
/// Checks if the hitbox is currently hovered. Except when handling `ScrollWheelEvent`, this is
|
||||||
|
/// typically what you want when determining whether to handle mouse events or paint hover
|
||||||
|
/// styles.
|
||||||
|
///
|
||||||
|
/// This can return `false` even when the hitbox contains the mouse, if a hitbox in front of
|
||||||
|
/// this sets `HitboxBehavior::BlockMouse` (`InteractiveElement::occlude`) or
|
||||||
|
/// `HitboxBehavior::BlockMouseExceptScroll` (`InteractiveElement::block_mouse_except_scroll`).
|
||||||
|
///
|
||||||
|
/// Handling of `ScrollWheelEvent` should typically use `should_handle_scroll` instead.
|
||||||
|
/// Concretely, this is due to use-cases like overlays that cause the elements under to be
|
||||||
|
/// non-interactive while still allowing scrolling. More abstractly, this is because
|
||||||
|
/// `is_hovered` is about element interactions directly under the mouse - mouse moves, clicks,
|
||||||
|
/// hover styling, etc. In contrast, scrolling is about finding the current outer scrollable
|
||||||
|
/// container.
|
||||||
pub fn is_hovered(&self, window: &Window) -> bool {
|
pub fn is_hovered(&self, window: &Window) -> bool {
|
||||||
self.id.is_hovered(window)
|
self.id.is_hovered(window)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Checks if the hitbox contains the mouse and should handle scroll events. Typically this
|
||||||
|
/// should only be used when handling `ScrollWheelEvent`, and otherwise `is_hovered` should be
|
||||||
|
/// used. See the documentation of `Hitbox::is_hovered` for details about this distinction.
|
||||||
|
///
|
||||||
|
/// This can return `false` even when the hitbox contains the mouse, if a hitbox in front of
|
||||||
|
/// this sets `HitboxBehavior::BlockMouse` (`InteractiveElement::occlude`).
|
||||||
|
pub fn should_handle_scroll(&self, window: &Window) -> bool {
|
||||||
|
self.id.should_handle_scroll(window)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Eq, PartialEq)]
|
/// How the hitbox affects mouse behavior.
|
||||||
pub(crate) struct HitTest(SmallVec<[HitboxId; 8]>);
|
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
|
||||||
|
pub enum HitboxBehavior {
|
||||||
|
/// Normal hitbox mouse behavior, doesn't affect mouse handling for other hitboxes.
|
||||||
|
#[default]
|
||||||
|
Normal,
|
||||||
|
|
||||||
|
/// All hitboxes behind this hitbox will be ignored and so will have `hitbox.is_hovered() ==
|
||||||
|
/// false` and `hitbox.should_handle_scroll() == false`. Typically for elements this causes
|
||||||
|
/// skipping of all mouse events, hover styles, and tooltips. This flag is set by
|
||||||
|
/// [`InteractiveElement::occlude`].
|
||||||
|
///
|
||||||
|
/// For mouse handlers that check those hitboxes, this behaves the same as registering a
|
||||||
|
/// bubble-phase handler for every mouse event type:
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// window.on_mouse_event(move |_: &EveryMouseEventTypeHere, phase, window, cx| {
|
||||||
|
/// if phase == DispatchPhase::Capture && hitbox.is_hovered(window) {
|
||||||
|
/// cx.stop_propagation();
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// This has effects beyond event handling - any use of hitbox checking, such as hover
|
||||||
|
/// styles and tooltops. These other behaviors are the main point of this mechanism. An
|
||||||
|
/// alternative might be to not affect mouse event handling - but this would allow
|
||||||
|
/// inconsistent UI where clicks and moves interact with elements that are not considered to
|
||||||
|
/// be hovered.
|
||||||
|
BlockMouse,
|
||||||
|
|
||||||
|
/// All hitboxes behind this hitbox will have `hitbox.is_hovered() == false`, even when
|
||||||
|
/// `hitbox.should_handle_scroll() == true`. Typically for elements this causes all mouse
|
||||||
|
/// interaction except scroll events to be ignored - see the documentation of
|
||||||
|
/// [`Hitbox::is_hovered`] for details. This flag is set by
|
||||||
|
/// [`InteractiveElement::block_mouse_except_scroll`].
|
||||||
|
///
|
||||||
|
/// For mouse handlers that check those hitboxes, this behaves the same as registering a
|
||||||
|
/// bubble-phase handler for every mouse event type **except** `ScrollWheelEvent`:
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// window.on_mouse_event(move |_: &EveryMouseEventTypeExceptScroll, phase, window, _cx| {
|
||||||
|
/// if phase == DispatchPhase::Bubble && hitbox.should_handle_scroll(window) {
|
||||||
|
/// cx.stop_propagation();
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// See the documentation of [`Hitbox::is_hovered`] for details of why `ScrollWheelEvent` is
|
||||||
|
/// handled differently than other mouse events. If also blocking these scroll events is
|
||||||
|
/// desired, then a `cx.stop_propagation()` handler like the one above can be used.
|
||||||
|
///
|
||||||
|
/// This has effects beyond event handling - this affects any use of `is_hovered`, such as
|
||||||
|
/// hover styles and tooltops. These other behaviors are the main point of this mechanism.
|
||||||
|
/// An alternative might be to not affect mouse event handling - but this would allow
|
||||||
|
/// inconsistent UI where clicks and moves interact with elements that are not considered to
|
||||||
|
/// be hovered.
|
||||||
|
BlockMouseExceptScroll,
|
||||||
|
}
|
||||||
|
|
||||||
/// An identifier for a tooltip.
|
/// An identifier for a tooltip.
|
||||||
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
|
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
|
||||||
|
@ -578,16 +685,26 @@ impl Frame {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn hit_test(&self, position: Point<Pixels>) -> HitTest {
|
pub(crate) fn hit_test(&self, position: Point<Pixels>) -> HitTest {
|
||||||
|
let mut set_hover_hitbox_count = false;
|
||||||
let mut hit_test = HitTest::default();
|
let mut hit_test = HitTest::default();
|
||||||
for hitbox in self.hitboxes.iter().rev() {
|
for hitbox in self.hitboxes.iter().rev() {
|
||||||
let bounds = hitbox.bounds.intersect(&hitbox.content_mask.bounds);
|
let bounds = hitbox.bounds.intersect(&hitbox.content_mask.bounds);
|
||||||
if bounds.contains(&position) {
|
if bounds.contains(&position) {
|
||||||
hit_test.0.push(hitbox.id);
|
hit_test.ids.push(hitbox.id);
|
||||||
if hitbox.opaque {
|
if !set_hover_hitbox_count
|
||||||
|
&& hitbox.behavior == HitboxBehavior::BlockMouseExceptScroll
|
||||||
|
{
|
||||||
|
hit_test.hover_hitbox_count = hit_test.ids.len();
|
||||||
|
set_hover_hitbox_count = true;
|
||||||
|
}
|
||||||
|
if hitbox.behavior == HitboxBehavior::BlockMouse {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if !set_hover_hitbox_count {
|
||||||
|
hit_test.hover_hitbox_count = hit_test.ids.len();
|
||||||
|
}
|
||||||
hit_test
|
hit_test
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -638,7 +755,7 @@ pub struct Window {
|
||||||
pub(crate) image_cache_stack: Vec<AnyImageCache>,
|
pub(crate) image_cache_stack: Vec<AnyImageCache>,
|
||||||
pub(crate) rendered_frame: Frame,
|
pub(crate) rendered_frame: Frame,
|
||||||
pub(crate) next_frame: Frame,
|
pub(crate) next_frame: Frame,
|
||||||
pub(crate) next_hitbox_id: HitboxId,
|
next_hitbox_id: HitboxId,
|
||||||
pub(crate) next_tooltip_id: TooltipId,
|
pub(crate) next_tooltip_id: TooltipId,
|
||||||
pub(crate) tooltip_bounds: Option<TooltipBounds>,
|
pub(crate) tooltip_bounds: Option<TooltipBounds>,
|
||||||
next_frame_callbacks: Rc<RefCell<Vec<FrameCallback>>>,
|
next_frame_callbacks: Rc<RefCell<Vec<FrameCallback>>>,
|
||||||
|
@ -927,7 +1044,7 @@ impl Window {
|
||||||
rendered_frame: Frame::new(DispatchTree::new(cx.keymap.clone(), cx.actions.clone())),
|
rendered_frame: Frame::new(DispatchTree::new(cx.keymap.clone(), cx.actions.clone())),
|
||||||
next_frame: Frame::new(DispatchTree::new(cx.keymap.clone(), cx.actions.clone())),
|
next_frame: Frame::new(DispatchTree::new(cx.keymap.clone(), cx.actions.clone())),
|
||||||
next_frame_callbacks,
|
next_frame_callbacks,
|
||||||
next_hitbox_id: HitboxId::default(),
|
next_hitbox_id: HitboxId(0),
|
||||||
next_tooltip_id: TooltipId::default(),
|
next_tooltip_id: TooltipId::default(),
|
||||||
tooltip_bounds: None,
|
tooltip_bounds: None,
|
||||||
dirty_views: FxHashSet::default(),
|
dirty_views: FxHashSet::default(),
|
||||||
|
@ -2870,17 +2987,17 @@ impl Window {
|
||||||
/// to determine whether the inserted hitbox was the topmost.
|
/// to determine whether the inserted hitbox was the topmost.
|
||||||
///
|
///
|
||||||
/// This method should only be called as part of the prepaint phase of element drawing.
|
/// This method should only be called as part of the prepaint phase of element drawing.
|
||||||
pub fn insert_hitbox(&mut self, bounds: Bounds<Pixels>, opaque: bool) -> Hitbox {
|
pub fn insert_hitbox(&mut self, bounds: Bounds<Pixels>, behavior: HitboxBehavior) -> Hitbox {
|
||||||
self.invalidator.debug_assert_prepaint();
|
self.invalidator.debug_assert_prepaint();
|
||||||
|
|
||||||
let content_mask = self.content_mask();
|
let content_mask = self.content_mask();
|
||||||
let id = self.next_hitbox_id;
|
let mut id = self.next_hitbox_id;
|
||||||
self.next_hitbox_id.0 += 1;
|
self.next_hitbox_id = self.next_hitbox_id.next();
|
||||||
let hitbox = Hitbox {
|
let hitbox = Hitbox {
|
||||||
id,
|
id,
|
||||||
bounds,
|
bounds,
|
||||||
content_mask,
|
content_mask,
|
||||||
opaque,
|
behavior,
|
||||||
};
|
};
|
||||||
self.next_frame.hitboxes.push(hitbox.clone());
|
self.next_frame.hitboxes.push(hitbox.clone());
|
||||||
hitbox
|
hitbox
|
||||||
|
@ -4042,7 +4159,7 @@ impl Window {
|
||||||
inspector.update(cx, |inspector, _cx| {
|
inspector.update(cx, |inspector, _cx| {
|
||||||
if let Some(depth) = inspector.pick_depth.as_mut() {
|
if let Some(depth) = inspector.pick_depth.as_mut() {
|
||||||
*depth += delta_y.0 / SCROLL_PIXELS_PER_LAYER;
|
*depth += delta_y.0 / SCROLL_PIXELS_PER_LAYER;
|
||||||
let max_depth = self.mouse_hit_test.0.len() as f32 - 0.5;
|
let max_depth = self.mouse_hit_test.ids.len() as f32 - 0.5;
|
||||||
if *depth < 0.0 {
|
if *depth < 0.0 {
|
||||||
*depth = 0.0;
|
*depth = 0.0;
|
||||||
} else if *depth > max_depth {
|
} else if *depth > max_depth {
|
||||||
|
@ -4067,9 +4184,9 @@ impl Window {
|
||||||
) -> Option<(HitboxId, crate::InspectorElementId)> {
|
) -> Option<(HitboxId, crate::InspectorElementId)> {
|
||||||
if let Some(pick_depth) = inspector.pick_depth {
|
if let Some(pick_depth) = inspector.pick_depth {
|
||||||
let depth = (pick_depth as i64).try_into().unwrap_or(0);
|
let depth = (pick_depth as i64).try_into().unwrap_or(0);
|
||||||
let max_skipped = self.mouse_hit_test.0.len().saturating_sub(1);
|
let max_skipped = self.mouse_hit_test.ids.len().saturating_sub(1);
|
||||||
let skip_count = (depth as usize).min(max_skipped);
|
let skip_count = (depth as usize).min(max_skipped);
|
||||||
for hitbox_id in self.mouse_hit_test.0.iter().skip(skip_count) {
|
for hitbox_id in self.mouse_hit_test.ids.iter().skip(skip_count) {
|
||||||
if let Some(inspector_id) = frame.inspector_hitboxes.get(hitbox_id) {
|
if let Some(inspector_id) = frame.inspector_hitboxes.get(hitbox_id) {
|
||||||
return Some((*hitbox_id, inspector_id.clone()));
|
return Some((*hitbox_id, inspector_id.clone()));
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ mod path_range;
|
||||||
|
|
||||||
use base64::Engine as _;
|
use base64::Engine as _;
|
||||||
use futures::FutureExt as _;
|
use futures::FutureExt as _;
|
||||||
|
use gpui::HitboxBehavior;
|
||||||
use language::LanguageName;
|
use language::LanguageName;
|
||||||
use log::Level;
|
use log::Level;
|
||||||
pub use path_range::{LineCol, PathWithRange};
|
pub use path_range::{LineCol, PathWithRange};
|
||||||
|
@ -1211,7 +1212,7 @@ impl Element for MarkdownElement {
|
||||||
window.set_focus_handle(&focus_handle, cx);
|
window.set_focus_handle(&focus_handle, cx);
|
||||||
window.set_view_id(self.markdown.entity_id());
|
window.set_view_id(self.markdown.entity_id());
|
||||||
|
|
||||||
let hitbox = window.insert_hitbox(bounds, false);
|
let hitbox = window.insert_hitbox(bounds, HitboxBehavior::Normal);
|
||||||
rendered_markdown.element.prepaint(window, cx);
|
rendered_markdown.element.prepaint(window, cx);
|
||||||
self.autoscroll(&rendered_markdown.text, window, cx);
|
self.autoscroll(&rendered_markdown.text, window, cx);
|
||||||
hitbox
|
hitbox
|
||||||
|
|
|
@ -136,7 +136,9 @@ pub struct IndentGuideLayout {
|
||||||
|
|
||||||
/// Implements the necessary functionality for rendering indent guides inside a uniform list.
|
/// Implements the necessary functionality for rendering indent guides inside a uniform list.
|
||||||
mod uniform_list {
|
mod uniform_list {
|
||||||
use gpui::{DispatchPhase, Hitbox, MouseButton, MouseDownEvent, MouseMoveEvent};
|
use gpui::{
|
||||||
|
DispatchPhase, Hitbox, HitboxBehavior, MouseButton, MouseDownEvent, MouseMoveEvent,
|
||||||
|
};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
@ -256,7 +258,12 @@ mod uniform_list {
|
||||||
.indent_guides
|
.indent_guides
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.iter()
|
.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();
|
.collect();
|
||||||
Self::PrepaintState::Interactive {
|
Self::PrepaintState::Interactive {
|
||||||
hitboxes: Rc::new(hitboxes),
|
hitboxes: Rc::new(hitboxes),
|
||||||
|
|
|
@ -2,9 +2,9 @@ use std::{cell::RefCell, rc::Rc};
|
||||||
|
|
||||||
use gpui::{
|
use gpui::{
|
||||||
AnyElement, AnyView, App, Bounds, Corner, DismissEvent, DispatchPhase, Element, ElementId,
|
AnyElement, AnyView, App, Bounds, Corner, DismissEvent, DispatchPhase, Element, ElementId,
|
||||||
Entity, Focusable as _, GlobalElementId, HitboxId, InteractiveElement, IntoElement, LayoutId,
|
Entity, Focusable as _, GlobalElementId, HitboxBehavior, HitboxId, InteractiveElement,
|
||||||
Length, ManagedView, MouseDownEvent, ParentElement, Pixels, Point, Style, Window, anchored,
|
IntoElement, LayoutId, Length, ManagedView, MouseDownEvent, ParentElement, Pixels, Point,
|
||||||
deferred, div, point, prelude::FluentBuilder, px, size,
|
Style, Window, anchored, deferred, div, point, prelude::FluentBuilder, px, size,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
@ -421,7 +421,7 @@ impl<M: ManagedView> Element for PopoverMenu<M> {
|
||||||
((), element_state)
|
((), element_state)
|
||||||
});
|
});
|
||||||
|
|
||||||
window.insert_hitbox(bounds, false).id
|
window.insert_hitbox(bounds, HitboxBehavior::Normal).id
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,9 +2,9 @@ use std::{cell::RefCell, rc::Rc};
|
||||||
|
|
||||||
use gpui::{
|
use gpui::{
|
||||||
AnyElement, App, Bounds, Corner, DismissEvent, DispatchPhase, Element, ElementId, Entity,
|
AnyElement, App, Bounds, Corner, DismissEvent, DispatchPhase, Element, ElementId, Entity,
|
||||||
Focusable as _, GlobalElementId, Hitbox, InteractiveElement, IntoElement, LayoutId,
|
Focusable as _, GlobalElementId, Hitbox, HitboxBehavior, InteractiveElement, IntoElement,
|
||||||
ManagedView, MouseButton, MouseDownEvent, ParentElement, Pixels, Point, Window, anchored,
|
LayoutId, ManagedView, MouseButton, MouseDownEvent, ParentElement, Pixels, Point, Window,
|
||||||
deferred, div, px,
|
anchored, deferred, div, px,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct RightClickMenu<M: ManagedView> {
|
pub struct RightClickMenu<M: ManagedView> {
|
||||||
|
@ -185,7 +185,7 @@ impl<M: ManagedView> Element for RightClickMenu<M> {
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> PrepaintState {
|
) -> 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() {
|
if let Some(child) = request_layout.child_element.as_mut() {
|
||||||
child.prepaint(window, cx);
|
child.prepaint(window, cx);
|
||||||
|
|
|
@ -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 crate::{IntoElement, prelude::*, px, relative};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
Along, App, Axis as ScrollbarAxis, BorderStyle, Bounds, ContentMask, Corners, Edges, Element,
|
Along, App, Axis as ScrollbarAxis, BorderStyle, Bounds, ContentMask, Corners, Edges, Element,
|
||||||
ElementId, Entity, EntityId, GlobalElementId, Hitbox, Hsla, IsZero, LayoutId, ListState,
|
ElementId, Entity, EntityId, GlobalElementId, Hitbox, HitboxBehavior, Hsla, IsZero, LayoutId,
|
||||||
MouseDownEvent, MouseMoveEvent, MouseUpEvent, Pixels, Point, ScrollHandle, ScrollWheelEvent,
|
ListState, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Pixels, Point, ScrollHandle,
|
||||||
Size, Style, UniformListScrollHandle, Window, quad,
|
ScrollWheelEvent, Size, Style, UniformListScrollHandle, Window, quad,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct Scrollbar {
|
pub struct Scrollbar {
|
||||||
|
@ -226,7 +226,7 @@ impl Element for Scrollbar {
|
||||||
_: &mut App,
|
_: &mut App,
|
||||||
) -> Self::PrepaintState {
|
) -> Self::PrepaintState {
|
||||||
window.with_content_mask(Some(ContentMask { bounds }), |window| {
|
window.with_content_mask(Some(ContentMask { bounds }), |window| {
|
||||||
window.insert_hitbox(bounds, false)
|
window.insert_hitbox(bounds, HitboxBehavior::Normal)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -902,9 +902,9 @@ mod element {
|
||||||
use std::{cell::RefCell, iter, rc::Rc, sync::Arc};
|
use std::{cell::RefCell, iter, rc::Rc, sync::Arc};
|
||||||
|
|
||||||
use gpui::{
|
use gpui::{
|
||||||
Along, AnyElement, App, Axis, BorderStyle, Bounds, Element, GlobalElementId, IntoElement,
|
Along, AnyElement, App, Axis, BorderStyle, Bounds, Element, GlobalElementId,
|
||||||
MouseDownEvent, MouseMoveEvent, MouseUpEvent, ParentElement, Pixels, Point, Size, Style,
|
HitboxBehavior, IntoElement, MouseDownEvent, MouseMoveEvent, MouseUpEvent, ParentElement,
|
||||||
WeakEntity, Window, px, relative, size,
|
Pixels, Point, Size, Style, WeakEntity, Window, px, relative, size,
|
||||||
};
|
};
|
||||||
use gpui::{CursorStyle, Hitbox};
|
use gpui::{CursorStyle, Hitbox};
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
|
@ -1091,7 +1091,7 @@ mod element {
|
||||||
};
|
};
|
||||||
|
|
||||||
PaneAxisHandleLayout {
|
PaneAxisHandleLayout {
|
||||||
hitbox: window.insert_hitbox(handle_bounds, true),
|
hitbox: window.insert_hitbox(handle_bounds, HitboxBehavior::Normal),
|
||||||
divider_bounds,
|
divider_bounds,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,10 +37,10 @@ use futures::{
|
||||||
use gpui::{
|
use gpui::{
|
||||||
Action, AnyEntity, AnyView, AnyWeakView, App, AsyncApp, AsyncWindowContext, Bounds, Context,
|
Action, AnyEntity, AnyView, AnyWeakView, App, AsyncApp, AsyncWindowContext, Bounds, Context,
|
||||||
CursorStyle, Decorations, DragMoveEvent, Entity, EntityId, EventEmitter, FocusHandle,
|
CursorStyle, Decorations, DragMoveEvent, Entity, EntityId, EventEmitter, FocusHandle,
|
||||||
Focusable, Global, Hsla, KeyContext, Keystroke, ManagedView, MouseButton, PathPromptOptions,
|
Focusable, Global, HitboxBehavior, Hsla, KeyContext, Keystroke, ManagedView, MouseButton,
|
||||||
Point, PromptLevel, Render, ResizeEdge, Size, Stateful, Subscription, Task, Tiling, WeakEntity,
|
PathPromptOptions, Point, PromptLevel, Render, ResizeEdge, Size, Stateful, Subscription, Task,
|
||||||
WindowBounds, WindowHandle, WindowId, WindowOptions, action_as, actions, canvas,
|
Tiling, WeakEntity, WindowBounds, WindowHandle, WindowId, WindowOptions, action_as, actions,
|
||||||
impl_action_as, impl_actions, point, relative, size, transparent_black,
|
canvas, impl_action_as, impl_actions, point, relative, size, transparent_black,
|
||||||
};
|
};
|
||||||
pub use history_manager::*;
|
pub use history_manager::*;
|
||||||
pub use item::{
|
pub use item::{
|
||||||
|
@ -7344,7 +7344,7 @@ pub fn client_side_decorations(
|
||||||
point(px(0.0), px(0.0)),
|
point(px(0.0), px(0.0)),
|
||||||
window.window_bounds().get_bounds().size,
|
window.window_bounds().get_bounds().size,
|
||||||
),
|
),
|
||||||
false,
|
HitboxBehavior::Normal,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
move |_bounds, hitbox, window, cx| {
|
move |_bounds, hitbox, window, cx| {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue