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 
This commit is contained in:
parent
5f1b68a431
commit
5c8d0d7eb4
1 changed files with 75 additions and 73 deletions
|
|
@ -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
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue