scrollbar: Use default cursor style when hovered on scrollbar. (#480)

- Move prepare code to `prepaint` stage.

Close #474
This commit is contained in:
Jason Lee 2024-12-10 14:43:02 +08:00 committed by GitHub
parent 22f60c7299
commit 26024ba2b5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 99 additions and 41 deletions

View file

@ -41,8 +41,8 @@ pub use webview_story::WebViewStory;
use gpui::{ use gpui::{
actions, div, prelude::FluentBuilder as _, px, AnyElement, AnyView, AppContext, Div, actions, div, prelude::FluentBuilder as _, px, AnyElement, AnyView, AppContext, Div,
Entity as _, EventEmitter, FocusableView, Hsla, InteractiveElement, IntoElement, ParentElement, EventEmitter, FocusableView, Hsla, InteractiveElement, IntoElement, ParentElement, Render,
Render, SharedString, Styled as _, View, ViewContext, VisualContext, WindowContext, SharedString, Styled as _, View, ViewContext, VisualContext, WindowContext,
}; };
use ui::{ use ui::{
@ -53,9 +53,8 @@ use ui::{
label::Label, label::Label,
notification::Notification, notification::Notification,
popup_menu::PopupMenu, popup_menu::PopupMenu,
scroll::ScrollbarAxis,
theme::ActiveTheme, theme::ActiveTheme,
v_flex, ContextModal, IconName, StyledExt as _, v_flex, ContextModal, IconName,
}; };
const PANEL_NAME: &str = "StoryContainer"; const PANEL_NAME: &str = "StoryContainer";

View file

@ -2,9 +2,9 @@ use std::{cell::Cell, rc::Rc, time::Instant};
use crate::theme::ActiveTheme; use crate::theme::ActiveTheme;
use gpui::{ use gpui::{
fill, point, px, relative, Bounds, ContentMask, Edges, Element, EntityId, Hitbox, IntoElement, fill, point, px, relative, Bounds, ContentMask, CursorStyle, Edges, Element, EntityId, Hitbox,
MouseDownEvent, MouseMoveEvent, MouseUpEvent, PaintQuad, Pixels, Point, Position, ScrollHandle, Hsla, IntoElement, MouseDownEvent, MouseMoveEvent, MouseUpEvent, PaintQuad, Pixels, Point,
ScrollWheelEvent, Style, UniformListScrollHandle, Position, ScrollHandle, ScrollWheelEvent, Style, UniformListScrollHandle,
}; };
const MIN_THUMB_SIZE: f32 = 80.; const MIN_THUMB_SIZE: f32 = 80.;
@ -267,10 +267,31 @@ impl IntoElement for Scrollbar {
} }
} }
pub struct PrepaintState {
hitbox: Hitbox,
states: Vec<AxisPrepaintState>,
}
pub struct AxisPrepaintState {
axis: ScrollbarAxis,
bar_hitbox: Hitbox,
bounds: Bounds<Pixels>,
border_width: Pixels,
radius: Pixels,
bg: Hsla,
border: Hsla,
thumb_bounds: Bounds<Pixels>,
thumb_bg: Hsla,
scroll_size: Pixels,
container_size: Pixels,
thumb_size: Pixels,
margin_end: Pixels,
}
impl Element for Scrollbar { impl Element for Scrollbar {
type RequestLayoutState = (); type RequestLayoutState = ();
type PrepaintState = Hitbox; type PrepaintState = PrepaintState;
fn id(&self) -> Option<gpui::ElementId> { fn id(&self) -> Option<gpui::ElementId> {
None None
@ -298,36 +319,27 @@ impl Element for Scrollbar {
_: &mut Self::RequestLayoutState, _: &mut Self::RequestLayoutState,
cx: &mut gpui::WindowContext, cx: &mut gpui::WindowContext,
) -> Self::PrepaintState { ) -> Self::PrepaintState {
cx.with_content_mask(Some(ContentMask { bounds }), |cx| { let hitbox = cx.with_content_mask(Some(ContentMask { bounds }), |cx| {
cx.insert_hitbox(bounds, false) cx.insert_hitbox(bounds, false)
}) });
}
let mut states = vec![];
fn paint(
&mut self,
_: Option<&gpui::GlobalElementId>,
_: Bounds<Pixels>,
_: &mut Self::RequestLayoutState,
hitbox: &mut Self::PrepaintState,
cx: &mut gpui::WindowContext,
) {
let hitbox_bounds = hitbox.bounds;
let mut has_both = self.axis.is_both(); let mut has_both = self.axis.is_both();
const NORMAL_OPACITY: f32 = 0.6;
for axis in self.axis.all().into_iter() { for axis in self.axis.all().into_iter() {
const NORMAL_OPACITY: f32 = 0.6;
let is_vertical = axis.is_vertical(); let is_vertical = axis.is_vertical();
let (scroll_area_size, container_size, scroll_position) = if is_vertical { let (scroll_area_size, container_size, scroll_position) = if is_vertical {
( (
self.scroll_size.height, self.scroll_size.height,
hitbox_bounds.size.height, hitbox.size.height,
self.scroll_handle.offset().y, self.scroll_handle.offset().y,
) )
} else { } else {
( (
self.scroll_size.width, self.scroll_size.width,
hitbox_bounds.size.width, hitbox.size.width,
self.scroll_handle.offset().x, self.scroll_handle.offset().x,
) )
}; };
@ -354,23 +366,23 @@ impl Element for Scrollbar {
let bounds = Bounds { let bounds = Bounds {
origin: if is_vertical { origin: if is_vertical {
point( point(
hitbox_bounds.origin.x + hitbox_bounds.size.width - self.width, hitbox.origin.x + hitbox.size.width - self.width,
hitbox_bounds.origin.y, hitbox.origin.y,
) )
} else { } else {
point( point(
hitbox_bounds.origin.x, hitbox.origin.x,
hitbox_bounds.origin.y + hitbox_bounds.size.height - self.width, hitbox.origin.y + hitbox.size.height - self.width,
) )
}, },
size: gpui::Size { size: gpui::Size {
width: if is_vertical { width: if is_vertical {
self.width self.width
} else { } else {
hitbox_bounds.size.width hitbox.size.width
}, },
height: if is_vertical { height: if is_vertical {
hitbox_bounds.size.height hitbox.size.height
} else { } else {
self.width self.width
}, },
@ -449,8 +461,55 @@ impl Element for Scrollbar {
) )
}; };
let bar_hitbox = cx.with_content_mask(Some(ContentMask { bounds }), |cx| {
cx.insert_hitbox(bounds, false)
});
states.push(AxisPrepaintState {
axis,
bar_hitbox,
bounds,
border_width,
radius,
bg: bar_bg,
border: bar_border,
thumb_bounds,
thumb_bg,
scroll_size: scroll_area_size,
container_size,
thumb_size: thumb_length,
margin_end,
})
}
PrepaintState { hitbox, states }
}
fn paint(
&mut self,
_: Option<&gpui::GlobalElementId>,
_: Bounds<Pixels>,
_: &mut Self::RequestLayoutState,
prepaint: &mut Self::PrepaintState,
cx: &mut gpui::WindowContext,
) {
let hitbox_bounds = prepaint.hitbox.bounds;
for state in prepaint.states.iter() {
let axis = state.axis;
let radius = state.radius;
let bounds = state.bounds;
let thumb_bounds = state.thumb_bounds;
let scroll_area_size = state.scroll_size;
let container_size = state.container_size;
let thumb_size = state.thumb_size;
let margin_end = state.margin_end;
let is_vertical = axis.is_vertical();
cx.set_cursor_style(CursorStyle::default(), &state.bar_hitbox);
cx.paint_layer(hitbox_bounds, |cx| { cx.paint_layer(hitbox_bounds, |cx| {
cx.paint_quad(fill(bounds, bar_bg)); cx.paint_quad(fill(state.bounds, state.bg));
cx.paint_quad(PaintQuad { cx.paint_quad(PaintQuad {
bounds, bounds,
@ -461,20 +520,20 @@ impl Element for Scrollbar {
top: px(0.), top: px(0.),
right: px(0.), right: px(0.),
bottom: px(0.), bottom: px(0.),
left: border_width, left: state.border_width,
} }
} else { } else {
Edges { Edges {
top: border_width, top: state.border_width,
right: px(0.), right: px(0.),
bottom: px(0.), bottom: px(0.),
left: px(0.), left: px(0.),
} }
}, },
border_color: bar_border, border_color: state.border,
}); });
cx.paint_quad(fill(thumb_bounds, thumb_bg).corner_radii(radius)); cx.paint_quad(fill(thumb_bounds, state.thumb_bg).corner_radii(radius));
}); });
cx.on_mouse_event({ cx.on_mouse_event({
@ -519,11 +578,11 @@ impl Element for Scrollbar {
// Set the thumb bar center to the click position // Set the thumb bar center to the click position
let offset = scroll_handle.offset(); let offset = scroll_handle.offset();
let percentage = if is_vertical { let percentage = if is_vertical {
(event.position.y - thumb_length / 2. - bounds.origin.y) (event.position.y - thumb_size / 2. - bounds.origin.y)
/ (bounds.size.height - thumb_length) / (bounds.size.height - thumb_size)
} else { } else {
(event.position.x - thumb_length / 2. - bounds.origin.x) (event.position.x - thumb_size / 2. - bounds.origin.x)
/ (bounds.size.width - thumb_length) / (bounds.size.width - thumb_size)
} }
.min(1.); .min(1.);
@ -587,10 +646,10 @@ impl Element for Scrollbar {
let percentage = (if is_vertical { let percentage = (if is_vertical {
(event.position.y - drag_pos.y - bounds.origin.y) (event.position.y - drag_pos.y - bounds.origin.y)
/ (bounds.size.height - thumb_length) / (bounds.size.height - thumb_size)
} else { } else {
(event.position.x - drag_pos.x - bounds.origin.x) (event.position.x - drag_pos.x - bounds.origin.x)
/ (bounds.size.width - thumb_length - margin_end) / (bounds.size.width - thumb_size - margin_end)
}) })
.clamp(0., 1.); .clamp(0., 1.);