scrollbar: Do not show scrollbar event mouse is in Element rect, only show when hovered on scrollbar area or scrolled (#333)

Let the scrollbar only display when hovering and scrolling. The effect
comparison is on the left GPUI and on the right mac. I guess they used
the 10th power ease-in function, and the effect is similar.

Fixes https://github.com/huacnlee/gpui-component/issues/260

![2024-10-11 22 06
51](https://github.com/user-attachments/assets/4fa35150-2f93-4b79-bdbb-d8b7783e4b5a)
This commit is contained in:
xda 2024-10-13 11:53:53 +09:00 committed by GitHub
parent 5f1b68a431
commit 5c8d0d7eb4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -1,10 +1,10 @@
use std::{cell::Cell, rc::Rc}; 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, Edges, Element, EntityId, Hitbox, IntoElement,
MouseDownEvent, MouseMoveEvent, MouseUpEvent, PaintQuad, Pixels, Point, Position, ScrollHandle, MouseDownEvent, MouseMoveEvent, MouseUpEvent, PaintQuad, Pixels, Point, Position, ScrollHandle,
Style, UniformListScrollHandle, ScrollWheelEvent, Style, UniformListScrollHandle,
}; };
const MIN_THUMB_SIZE: f32 = 80.; const MIN_THUMB_SIZE: f32 = 80.;
@ -48,7 +48,8 @@ pub struct ScrollbarState {
hovered_axis: Option<ScrollbarAxis>, hovered_axis: Option<ScrollbarAxis>,
dragged_axis: Option<ScrollbarAxis>, dragged_axis: Option<ScrollbarAxis>,
drag_pos: Point<Pixels>, drag_pos: Point<Pixels>,
visible: bool, last_scroll_offset: Point<Pixels>,
last_scroll_time: Option<Instant>,
} }
impl Default for ScrollbarState { impl Default for ScrollbarState {
@ -57,7 +58,8 @@ impl Default for ScrollbarState {
hovered_axis: None, hovered_axis: None,
dragged_axis: None, dragged_axis: None,
drag_pos: point(px(0.), px(0.)), drag_pos: point(px(0.), px(0.)),
visible: false, last_scroll_offset: point(px(0.), px(0.)),
last_scroll_time: None,
} }
} }
} }
@ -91,9 +93,14 @@ impl ScrollbarState {
state state
} }
fn with_visiable(&self, visiable: bool) -> Self { fn with_last_scroll(
&self,
last_scroll_offset: Point<Pixels>,
last_scroll_time: Option<Instant>,
) -> Self {
let mut state = *self; let mut state = *self;
state.visible = visiable; state.last_scroll_offset = last_scroll_offset;
state.last_scroll_time = last_scroll_time;
state state
} }
} }
@ -365,34 +372,28 @@ impl Element for Scrollbar {
}, },
}; };
let thumb_bg = cx.theme().scrollbar_thumb;
let state = self.state.clone(); let state = self.state.clone();
let (thumb_bg, bar_bg, bar_border, inset, radius) = let mut thumb_bg = cx.theme().transparent;
if state.get().dragged_axis == Some(axis) { let mut bar_bg = cx.theme().transparent;
(
thumb_bg, if state.get().dragged_axis == Some(axis)
cx.theme().scrollbar, || state.get().hovered_axis == Some(axis)
cx.theme().border, {
THUMB_INSET - px(1.), thumb_bg = cx.theme().scrollbar_thumb;
THUMB_RADIUS, bar_bg = cx.theme().scrollbar;
) } else {
} else if state.get().hovered_axis == Some(axis) { if let Some(last_time) = state.get().last_scroll_time {
( let elapsed = Instant::now().duration_since(last_time).as_secs_f32();
thumb_bg, if elapsed < 1.0 {
cx.theme().scrollbar, thumb_bg =
cx.theme().border, cx.theme().scrollbar_thumb.opacity(1.0 - elapsed.powi(10));
THUMB_INSET - px(1.), cx.request_animation_frame();
THUMB_RADIUS, }
) }
} else { }
( let bar_border = cx.theme().border;
thumb_bg.opacity(0.3), let inset = THUMB_INSET - px(1.);
cx.theme().transparent, let radius = THUMB_RADIUS;
gpui::transparent_black(),
THUMB_INSET,
THUMB_RADIUS - px(1.),
)
};
let border_width = px(0.); let border_width = px(0.);
let thumb_bounds = if is_vertical { let thumb_bounds = if is_vertical {
@ -419,33 +420,49 @@ impl Element for Scrollbar {
) )
}; };
if state.get().visible { cx.paint_quad(fill(bounds, bar_bg));
cx.paint_quad(fill(bounds, bar_bg));
cx.paint_quad(PaintQuad { cx.paint_quad(PaintQuad {
bounds, bounds,
corner_radii: (0.).into(), corner_radii: (0.).into(),
background: gpui::transparent_black(), background: gpui::transparent_black(),
border_widths: if is_vertical { border_widths: if is_vertical {
Edges { Edges {
top: px(0.), top: px(0.),
right: px(0.), right: px(0.),
bottom: px(0.), bottom: px(0.),
left: border_width, left: border_width,
} }
} else { } else {
Edges { Edges {
top: border_width, top: 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: bar_border,
}); });
cx.paint_quad(fill(thumb_bounds, thumb_bg).corner_radii(radius)); cx.paint_quad(fill(thumb_bounds, thumb_bg).corner_radii(radius));
}
cx.on_mouse_event({
let state = self.state.clone();
let view_id = self.view_id;
let scroll_handle = self.scroll_handle.clone();
move |event: &ScrollWheelEvent, phase, cx| {
if phase.bubble() && hitbox_bounds.contains(&event.position) {
if scroll_handle.offset() != state.get().last_scroll_offset {
state.set(state.get().with_last_scroll(
scroll_handle.offset(),
Some(Instant::now()),
));
cx.notify(view_id);
}
}
}
});
cx.on_mouse_event({ cx.on_mouse_event({
let state = self.state.clone(); let state = self.state.clone();
@ -514,21 +531,6 @@ impl Element for Scrollbar {
} }
} }
// If mouse out of the bounds, hide scrollbar
if hitbox_bounds.contains(&event.position)
|| state.get().dragged_axis.is_some()
{
if !state.get().visible {
state.set(state.get().with_visiable(true));
cx.notify(view_id);
}
} else {
if state.get().visible {
state.set(state.get().with_visiable(false));
cx.notify(view_id);
}
}
// Move thumb position on dragging // Move thumb position on dragging
if state.get().dragged_axis == Some(axis) && event.dragging() { if state.get().dragged_axis == Some(axis) && event.dragging() {
// drag_pos is the position of the mouse down event // drag_pos is the position of the mouse down event