chore: Improve Request Screening (#101)

* open chat while screening

* close panel on ignore

* bypass screening

* .

* improve settings

* refine modal

* .

* .

* .

* .

* .
This commit is contained in:
reya
2025-07-27 07:22:31 +07:00
committed by GitHub
parent 91cca37d69
commit 3cf9dde882
40 changed files with 1038 additions and 2035 deletions

View File

@@ -1,10 +1,8 @@
use std::cell::Cell;
use std::rc::Rc;
use gpui::{
canvas, div, relative, AnyElement, App, Div, Element, ElementId, EntityId, GlobalElementId,
InteractiveElement, IntoElement, ParentElement, Pixels, Position, ScrollHandle, SharedString,
Size, Stateful, StatefulInteractiveElement, Style, StyleRefinement, Styled, Window,
div, relative, AnyElement, App, Bounds, Div, Element, ElementId, GlobalElementId,
InspectorElementId, InteractiveElement, Interactivity, IntoElement, LayoutId, ParentElement,
Pixels, Position, ScrollHandle, SharedString, Size, Stateful, StatefulInteractiveElement,
Style, StyleRefinement, Styled, Window,
};
use super::{Scrollbar, ScrollbarAxis, ScrollbarState};
@@ -13,7 +11,6 @@ use super::{Scrollbar, ScrollbarAxis, ScrollbarState};
pub struct Scrollable<E> {
id: ElementId,
element: Option<E>,
view_id: EntityId,
axis: ScrollbarAxis,
/// This is a fake element to handle Styled, InteractiveElement, not used.
_element: Stateful<Div>,
@@ -23,19 +20,16 @@ impl<E> Scrollable<E>
where
E: Element,
{
pub(crate) fn new(view_id: EntityId, element: E, axis: ScrollbarAxis) -> Self {
let id = ElementId::Name(SharedString::from(format!(
"ScrollView:{}-{:?}",
view_id,
element.id(),
)));
pub(crate) fn new(axis: impl Into<ScrollbarAxis>, element: E) -> Self {
let id = ElementId::Name(SharedString::from(
format!("scrollable-{:?}", element.id(),),
));
Self {
element: Some(element),
_element: div().id("fake"),
id,
view_id,
axis,
axis: axis.into(),
}
}
@@ -53,8 +47,8 @@ where
}
/// Set the axis of the scroll view.
pub fn set_axis(&mut self, axis: ScrollbarAxis) {
self.axis = axis;
pub fn set_axis(&mut self, axis: impl Into<ScrollbarAxis>) {
self.axis = axis.into();
}
fn with_element_state<R>(
@@ -76,8 +70,7 @@ where
}
pub struct ScrollViewState {
scroll_size: Rc<Cell<Size<Pixels>>>,
state: Rc<Cell<ScrollbarState>>,
state: ScrollbarState,
handle: ScrollHandle,
}
@@ -85,8 +78,7 @@ impl Default for ScrollViewState {
fn default() -> Self {
Self {
handle: ScrollHandle::new(),
scroll_size: Rc::new(Cell::new(Size::default())),
state: Rc::new(Cell::new(ScrollbarState::default())),
state: ScrollbarState::default(),
}
}
}
@@ -119,7 +111,7 @@ impl<E> InteractiveElement for Scrollable<E>
where
E: Element + InteractiveElement,
{
fn interactivity(&mut self) -> &mut gpui::Interactivity {
fn interactivity(&mut self) -> &mut Interactivity {
if let Some(element) = &mut self.element {
element.interactivity()
} else {
@@ -127,7 +119,6 @@ where
}
}
}
impl<E> StatefulInteractiveElement for Scrollable<E> where E: Element + StatefulInteractiveElement {}
impl<E> IntoElement for Scrollable<E>
@@ -148,7 +139,7 @@ where
type PrepaintState = ScrollViewState;
type RequestLayoutState = AnyElement;
fn id(&self) -> Option<gpui::ElementId> {
fn id(&self) -> Option<ElementId> {
Some(self.id.clone())
}
@@ -158,11 +149,11 @@ where
fn request_layout(
&mut self,
id: Option<&gpui::GlobalElementId>,
_: Option<&gpui::InspectorElementId>,
id: Option<&GlobalElementId>,
_: Option<&InspectorElementId>,
window: &mut Window,
cx: &mut App,
) -> (gpui::LayoutId, Self::RequestLayoutState) {
) -> (LayoutId, Self::RequestLayoutState) {
let style = Style {
position: Position::Relative,
flex_grow: 1.0,
@@ -175,16 +166,10 @@ where
};
let axis = self.axis;
let view_id = self.view_id;
let scroll_id = self.id.clone();
let content = self.element.take().map(|c| c.into_any_element());
self.with_element_state(id.unwrap(), window, cx, |_, element_state, window, cx| {
let handle = element_state.handle.clone();
let state = element_state.state.clone();
let scroll_size = element_state.scroll_size.clone();
let mut element = div()
.relative()
.size_full()
@@ -192,16 +177,11 @@ where
.child(
div()
.id(scroll_id)
.track_scroll(&handle)
.track_scroll(&element_state.handle)
.overflow_scroll()
.relative()
.size_full()
.child(div().children(content).child({
let scroll_size = element_state.scroll_size.clone();
canvas(move |b, _, _| scroll_size.set(b.size), |_, _, _, _| {})
.absolute()
.size_full()
})),
.child(div().children(content)),
)
.child(
div()
@@ -211,8 +191,7 @@ where
.right_0()
.bottom_0()
.child(
Scrollbar::both(view_id, state, handle.clone(), scroll_size.get())
.axis(axis),
Scrollbar::both(&element_state.state, &element_state.handle).axis(axis),
),
)
.into_any_element();
@@ -226,9 +205,9 @@ where
fn prepaint(
&mut self,
_: Option<&gpui::GlobalElementId>,
_: Option<&gpui::InspectorElementId>,
_: gpui::Bounds<Pixels>,
_: Option<&GlobalElementId>,
_: Option<&InspectorElementId>,
_: Bounds<Pixels>,
element: &mut Self::RequestLayoutState,
window: &mut Window,
cx: &mut App,
@@ -240,9 +219,9 @@ where
fn paint(
&mut self,
_: Option<&gpui::GlobalElementId>,
_: Option<&gpui::InspectorElementId>,
_: gpui::Bounds<Pixels>,
_: Option<&GlobalElementId>,
_: Option<&InspectorElementId>,
_: Bounds<Pixels>,
element: &mut Self::RequestLayoutState,
_: &mut Self::PrepaintState,
window: &mut Window,

View File

@@ -1,7 +1,7 @@
use gpui::{
px, relative, App, Axis, BorderStyle, Bounds, ContentMask, Corners, Edges, Element, ElementId,
EntityId, GlobalElementId, Hitbox, HitboxBehavior, Hsla, IntoElement, IsZero as _, LayoutId,
PaintQuad, Pixels, Point, Position, ScrollHandle, ScrollWheelEvent, Size, Style, Window,
EntityId, GlobalElementId, Hitbox, Hsla, IntoElement, IsZero as _, LayoutId, PaintQuad, Pixels,
Point, Position, ScrollHandle, ScrollWheelEvent, Size, Style, Window,
};
use crate::AxisExt;
@@ -96,7 +96,7 @@ impl Element for ScrollableMask {
size: bounds.size,
};
window.insert_hitbox(cover_bounds, HitboxBehavior::Normal)
window.insert_hitbox(cover_bounds, gpui::HitboxBehavior::Normal)
}
fn paint(
@@ -118,9 +118,9 @@ impl Element for ScrollableMask {
bounds,
border_widths: Edges::all(px(1.0)),
border_color: color,
border_style: BorderStyle::Solid,
background: gpui::transparent_white().into(),
corner_radii: Corners::all(px(0.)),
border_style: BorderStyle::default(),
});
}

View File

@@ -1,22 +1,31 @@
use std::cell::Cell;
use std::ops::Deref;
use std::rc::Rc;
use std::time::{Duration, Instant};
use gpui::{
fill, point, px, relative, App, BorderStyle, Bounds, ContentMask, CursorStyle, Edges, Element,
EntityId, Hitbox, HitboxBehavior, Hsla, IntoElement, MouseDownEvent, MouseMoveEvent,
MouseUpEvent, PaintQuad, Pixels, Point, Position, ScrollHandle, ScrollWheelEvent,
UniformListScrollHandle, Window,
fill, point, px, relative, size, App, Axis, BorderStyle, Bounds, ContentMask, Corner,
CursorStyle, Edges, Element, GlobalElementId, Hitbox, HitboxBehavior, Hsla, InspectorElementId,
IntoElement, LayoutId, MouseDownEvent, MouseMoveEvent, MouseUpEvent, PaintQuad, Pixels, Point,
Position, ScrollHandle, ScrollWheelEvent, Size, UniformListScrollHandle, Window,
};
use theme::ActiveTheme;
use theme::{ActiveTheme, ScrollBarMode};
const WIDTH: Pixels = px(12.);
const BORDER_WIDTH: Pixels = px(0.);
const MIN_THUMB_SIZE: f32 = 80.;
const THUMB_RADIUS: Pixels = Pixels(4.0);
const THUMB_INSET: Pixels = Pixels(3.);
const FADE_OUT_DURATION: f32 = 2.0;
const FADE_OUT_DELAY: f32 = 1.2;
use crate::AxisExt;
const WIDTH: Pixels = px(2. * 2. + 8.);
const MIN_THUMB_SIZE: f32 = 48.;
const THUMB_WIDTH: Pixels = px(6.);
const THUMB_RADIUS: Pixels = Pixels(6. / 2.);
const THUMB_INSET: Pixels = Pixels(2.);
const THUMB_ACTIVE_WIDTH: Pixels = px(8.);
const THUMB_ACTIVE_RADIUS: Pixels = Pixels(8. / 2.);
const THUMB_ACTIVE_INSET: Pixels = Pixels(2.);
const FADE_OUT_DURATION: f32 = 3.0;
const FADE_OUT_DELAY: f32 = 2.0;
pub trait ScrollHandleOffsetable {
fn offset(&self) -> Point<Pixels>;
@@ -24,6 +33,8 @@ pub trait ScrollHandleOffsetable {
fn is_uniform_list(&self) -> bool {
false
}
/// The full size of the content, including padding.
fn content_size(&self) -> Size<Pixels>;
}
impl ScrollHandleOffsetable for ScrollHandle {
@@ -34,6 +45,10 @@ impl ScrollHandleOffsetable for ScrollHandle {
fn set_offset(&self, offset: Point<Pixels>) {
self.set_offset(offset);
}
fn content_size(&self) -> Size<Pixels> {
self.max_offset() + self.bounds().size
}
}
impl ScrollHandleOffsetable for UniformListScrollHandle {
@@ -48,13 +63,21 @@ impl ScrollHandleOffsetable for UniformListScrollHandle {
fn is_uniform_list(&self) -> bool {
true
}
fn content_size(&self) -> Size<Pixels> {
let base_handle = &self.0.borrow().base_handle;
base_handle.max_offset() + base_handle.bounds().size
}
}
#[derive(Debug, Clone)]
pub struct ScrollbarState(Rc<Cell<ScrollbarStateInner>>);
#[derive(Debug, Clone, Copy)]
pub struct ScrollbarState {
hovered_axis: Option<ScrollbarAxis>,
hovered_on_thumb: Option<ScrollbarAxis>,
dragged_axis: Option<ScrollbarAxis>,
pub struct ScrollbarStateInner {
hovered_axis: Option<Axis>,
hovered_on_thumb: Option<Axis>,
dragged_axis: Option<Axis>,
drag_pos: Point<Pixels>,
last_scroll_offset: Point<Pixels>,
last_scroll_time: Option<Instant>,
@@ -64,7 +87,7 @@ pub struct ScrollbarState {
impl Default for ScrollbarState {
fn default() -> Self {
Self {
Self(Rc::new(Cell::new(ScrollbarStateInner {
hovered_axis: None,
hovered_on_thumb: None,
dragged_axis: None,
@@ -72,16 +95,20 @@ impl Default for ScrollbarState {
last_scroll_offset: point(px(0.), px(0.)),
last_scroll_time: None,
last_update: Instant::now(),
}
})))
}
}
impl ScrollbarState {
pub fn new() -> Self {
Self::default()
}
impl Deref for ScrollbarState {
type Target = Rc<Cell<ScrollbarStateInner>>;
fn with_drag_pos(&self, axis: ScrollbarAxis, pos: Point<Pixels>) -> Self {
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl ScrollbarStateInner {
fn with_drag_pos(&self, axis: Axis, pos: Point<Pixels>) -> Self {
let mut state = *self;
if axis.is_vertical() {
state.drag_pos.y = pos.y;
@@ -99,7 +126,7 @@ impl ScrollbarState {
state
}
fn with_hovered(&self, axis: Option<ScrollbarAxis>) -> Self {
fn with_hovered(&self, axis: Option<Axis>) -> Self {
let mut state = *self;
state.hovered_axis = axis;
if axis.is_some() {
@@ -108,10 +135,10 @@ impl ScrollbarState {
state
}
fn with_hovered_on_thumb(&self, axis: Option<ScrollbarAxis>) -> Self {
fn with_hovered_on_thumb(&self, axis: Option<Axis>) -> Self {
let mut state = *self;
state.hovered_on_thumb = axis;
if axis.is_some() {
if self.is_scrollbar_visible() && axis.is_some() {
state.last_scroll_time = Some(std::time::Instant::now());
}
state
@@ -162,14 +189,28 @@ pub enum ScrollbarAxis {
Both,
}
impl From<Axis> for ScrollbarAxis {
fn from(axis: Axis) -> Self {
match axis {
Axis::Vertical => Self::Vertical,
Axis::Horizontal => Self::Horizontal,
}
}
}
impl ScrollbarAxis {
#[inline]
fn is_vertical(&self) -> bool {
/// Return true if the scrollbar axis is vertical.
pub fn is_vertical(&self) -> bool {
matches!(self, Self::Vertical)
}
#[inline]
fn is_both(&self) -> bool {
/// Return true if the scrollbar axis is horizontal.
pub fn is_horizontal(&self) -> bool {
matches!(self, Self::Horizontal)
}
/// Return true if the scrollbar axis is both vertical and horizontal.
pub fn is_both(&self) -> bool {
matches!(self, Self::Both)
}
@@ -184,24 +225,23 @@ impl ScrollbarAxis {
}
#[inline]
fn all(&self) -> Vec<ScrollbarAxis> {
fn all(&self) -> Vec<Axis> {
match self {
Self::Vertical => vec![Self::Vertical],
Self::Horizontal => vec![Self::Horizontal],
Self::Vertical => vec![Axis::Vertical],
Self::Horizontal => vec![Axis::Horizontal],
// This should keep Horizontal first, Vertical is the primary axis
// if Vertical not need display, then Horizontal will not keep right margin.
Self::Both => vec![Self::Horizontal, Self::Vertical],
Self::Both => vec![Axis::Horizontal, Axis::Vertical],
}
}
}
/// Scrollbar control for scroll-area or a uniform-list.
pub struct Scrollbar {
view_id: EntityId,
axis: ScrollbarAxis,
scroll_handle: Rc<Box<dyn ScrollHandleOffsetable>>,
scroll_size: gpui::Size<Pixels>,
state: Rc<Cell<ScrollbarState>>,
state: ScrollbarState,
scroll_size: Option<Size<Pixels>>,
/// Maximum frames per second for scrolling by drag. Default is 120 FPS.
///
/// This is used to limit the update rate of the scrollbar when it is
@@ -211,95 +251,62 @@ pub struct Scrollbar {
impl Scrollbar {
fn new(
view_id: EntityId,
state: Rc<Cell<ScrollbarState>>,
axis: ScrollbarAxis,
scroll_handle: impl ScrollHandleOffsetable + 'static,
scroll_size: gpui::Size<Pixels>,
axis: impl Into<ScrollbarAxis>,
state: &ScrollbarState,
scroll_handle: &(impl ScrollHandleOffsetable + Clone + 'static),
) -> Self {
Self {
view_id,
state,
axis,
scroll_size,
scroll_handle: Rc::new(Box::new(scroll_handle)),
state: state.clone(),
axis: axis.into(),
scroll_handle: Rc::new(Box::new(scroll_handle.clone())),
max_fps: 120,
scroll_size: None,
}
}
/// Create with vertical and horizontal scrollbar.
pub fn both(
view_id: EntityId,
state: Rc<Cell<ScrollbarState>>,
scroll_handle: impl ScrollHandleOffsetable + 'static,
scroll_size: gpui::Size<Pixels>,
state: &ScrollbarState,
scroll_handle: &(impl ScrollHandleOffsetable + Clone + 'static),
) -> Self {
Self::new(
view_id,
state,
ScrollbarAxis::Both,
scroll_handle,
scroll_size,
)
Self::new(ScrollbarAxis::Both, state, scroll_handle)
}
/// Create with horizontal scrollbar.
pub fn horizontal(
view_id: EntityId,
state: Rc<Cell<ScrollbarState>>,
scroll_handle: impl ScrollHandleOffsetable + 'static,
scroll_size: gpui::Size<Pixels>,
state: &ScrollbarState,
scroll_handle: &(impl ScrollHandleOffsetable + Clone + 'static),
) -> Self {
Self::new(
view_id,
state,
ScrollbarAxis::Horizontal,
scroll_handle,
scroll_size,
)
Self::new(ScrollbarAxis::Horizontal, state, scroll_handle)
}
/// Create with vertical scrollbar.
pub fn vertical(
view_id: EntityId,
state: Rc<Cell<ScrollbarState>>,
scroll_handle: impl ScrollHandleOffsetable + 'static,
scroll_size: gpui::Size<Pixels>,
state: &ScrollbarState,
scroll_handle: &(impl ScrollHandleOffsetable + Clone + 'static),
) -> Self {
Self::new(
view_id,
state,
ScrollbarAxis::Vertical,
scroll_handle,
scroll_size,
)
Self::new(ScrollbarAxis::Vertical, state, scroll_handle)
}
/// Create vertical scrollbar for uniform list.
pub fn uniform_scroll(
view_id: EntityId,
state: Rc<Cell<ScrollbarState>>,
scroll_handle: UniformListScrollHandle,
state: &ScrollbarState,
scroll_handle: &(impl ScrollHandleOffsetable + Clone + 'static),
) -> Self {
let scroll_size = scroll_handle
.0
.borrow()
.last_item_size
.map(|size| size.contents)
.unwrap_or_default();
Self::new(ScrollbarAxis::Vertical, state, scroll_handle)
}
Self::new(
view_id,
state,
ScrollbarAxis::Vertical,
scroll_handle,
scroll_size,
)
/// Set a special scroll size of the content area, default is None.
///
/// Default will sync the `content_size` from `scroll_handle`.
pub fn scroll_size(mut self, scroll_size: Size<Pixels>) -> Self {
self.scroll_size = Some(scroll_size);
self
}
/// Set scrollbar axis.
pub fn axis(mut self, axis: ScrollbarAxis) -> Self {
self.axis = axis;
pub fn axis(mut self, axis: impl Into<ScrollbarAxis>) -> Self {
self.axis = axis.into();
self
}
@@ -313,47 +320,54 @@ impl Scrollbar {
self
}
fn style_for_active(cx: &App) -> (Hsla, Hsla, Hsla, Pixels, Pixels) {
fn style_for_active(cx: &App) -> (Hsla, Hsla, Hsla, Pixels, Pixels, Pixels) {
(
cx.theme().scrollbar_thumb_hover_background,
cx.theme().scrollbar_thumb_background,
cx.theme().scrollbar_thumb_border,
THUMB_INSET - px(1.),
THUMB_RADIUS,
THUMB_ACTIVE_WIDTH,
THUMB_ACTIVE_INSET,
THUMB_ACTIVE_RADIUS,
)
}
fn style_for_hovered_thumb(cx: &App) -> (Hsla, Hsla, Hsla, Pixels, Pixels) {
fn style_for_hovered_thumb(cx: &App) -> (Hsla, Hsla, Hsla, Pixels, Pixels, Pixels) {
(
cx.theme().scrollbar_thumb_hover_background,
cx.theme().scrollbar_thumb_background,
cx.theme().scrollbar_thumb_border,
THUMB_INSET - px(1.),
THUMB_RADIUS,
THUMB_ACTIVE_WIDTH,
THUMB_ACTIVE_INSET,
THUMB_ACTIVE_RADIUS,
)
}
fn style_for_hovered_bar(cx: &App) -> (Hsla, Hsla, Hsla, Pixels, Pixels) {
let (inset, radius) = (THUMB_INSET - px(1.), THUMB_RADIUS);
fn style_for_hovered_bar(cx: &App) -> (Hsla, Hsla, Hsla, Pixels, Pixels, Pixels) {
(
cx.theme().scrollbar_thumb_background,
cx.theme().scrollbar_thumb_border,
gpui::transparent_black(),
THUMB_ACTIVE_WIDTH,
THUMB_ACTIVE_INSET,
THUMB_ACTIVE_RADIUS,
)
}
fn style_for_idle(cx: &App) -> (Hsla, Hsla, Hsla, Pixels, Pixels, Pixels) {
let (width, inset, radius) = match cx.theme().scrollbar_mode {
ScrollBarMode::Scrolling => (THUMB_WIDTH, THUMB_INSET, THUMB_RADIUS),
_ => (THUMB_ACTIVE_WIDTH, THUMB_ACTIVE_INSET, THUMB_ACTIVE_RADIUS),
};
(
gpui::transparent_black(),
gpui::transparent_black(),
gpui::transparent_black(),
width,
inset,
radius,
)
}
fn style_for_idle(_: &App) -> (Hsla, Hsla, Hsla, Pixels, Pixels) {
(
gpui::transparent_black(),
gpui::transparent_black(),
gpui::transparent_black(),
THUMB_INSET,
THUMB_RADIUS - px(1.),
)
}
}
impl IntoElement for Scrollbar {
@@ -370,7 +384,7 @@ pub struct PrepaintState {
}
pub struct AxisPrepaintState {
axis: ScrollbarAxis,
axis: Axis,
bar_hitbox: Hitbox,
bounds: Bounds<Pixels>,
radius: Pixels,
@@ -400,11 +414,11 @@ impl Element for Scrollbar {
fn request_layout(
&mut self,
_: Option<&gpui::GlobalElementId>,
_: Option<&gpui::InspectorElementId>,
_: Option<&GlobalElementId>,
_: Option<&InspectorElementId>,
window: &mut Window,
cx: &mut App,
) -> (gpui::LayoutId, Self::RequestLayoutState) {
) -> (LayoutId, Self::RequestLayoutState) {
let style = gpui::Style {
position: Position::Absolute,
flex_grow: 1.0,
@@ -421,8 +435,8 @@ impl Element for Scrollbar {
fn prepaint(
&mut self,
_: Option<&gpui::GlobalElementId>,
_: Option<&gpui::InspectorElementId>,
_: Option<&GlobalElementId>,
_: Option<&InspectorElementId>,
bounds: Bounds<Pixels>,
_: &mut Self::RequestLayoutState,
window: &mut Window,
@@ -434,26 +448,30 @@ impl Element for Scrollbar {
let mut states = vec![];
let mut has_both = self.axis.is_both();
let scroll_size = self
.scroll_size
.unwrap_or(self.scroll_handle.content_size());
for axis in self.axis.all().into_iter() {
let is_vertical = axis.is_vertical();
let (scroll_area_size, container_size, scroll_position) = if is_vertical {
(
self.scroll_size.height,
scroll_size.height,
hitbox.size.height,
self.scroll_handle.offset().y,
)
} else {
(
self.scroll_size.width,
scroll_size.width,
hitbox.size.width,
self.scroll_handle.offset().x,
)
};
// The horizontal scrollbar is set avoid overlapping with the vertical scrollbar, if the vertical scrollbar is visible.
let margin_end = if has_both && !is_vertical {
WIDTH
THUMB_ACTIVE_WIDTH
} else {
px(0.)
};
@@ -494,12 +512,27 @@ impl Element for Scrollbar {
};
let state = self.state.clone();
let is_always_to_show = cx.theme().scrollbar_mode.is_always();
let is_hover_to_show = cx.theme().scrollbar_mode.is_hover();
let is_hovered_on_bar = state.get().hovered_axis == Some(axis);
let is_hovered_on_thumb = state.get().hovered_on_thumb == Some(axis);
let (thumb_bg, bar_bg, bar_border, inset, radius) =
let (thumb_bg, bar_bg, bar_border, thumb_width, inset, radius) =
if state.get().dragged_axis == Some(axis) {
Self::style_for_active(cx)
} else if is_hover_to_show && (is_hovered_on_bar || is_hovered_on_thumb) {
if is_hovered_on_thumb {
Self::style_for_hovered_thumb(cx)
} else {
Self::style_for_hovered_bar(cx)
}
} else if is_always_to_show {
#[allow(clippy::if_same_then_else)]
if is_hovered_on_thumb {
Self::style_for_hovered_thumb(cx)
} else {
Self::style_for_hovered_bar(cx)
}
} else {
let mut idle_state = Self::style_for_idle(cx);
// Delay 2s to fade out the scrollbar thumb (in 1s)
@@ -531,43 +564,39 @@ impl Element for Scrollbar {
idle_state
};
// The clickable area of the thumb
let thumb_length = thumb_end - thumb_start - inset * 2;
let thumb_bounds = if is_vertical {
Bounds::from_corners(
point(bounds.origin.x, bounds.origin.y + thumb_start),
point(bounds.origin.x + WIDTH, bounds.origin.y + thumb_end),
Bounds::from_corner_and_size(
Corner::TopRight,
bounds.top_right() + point(-inset, inset + thumb_start),
size(WIDTH, thumb_length),
)
} else {
Bounds::from_corners(
point(bounds.origin.x + thumb_start, bounds.origin.y),
point(bounds.origin.x + thumb_end, bounds.origin.y + WIDTH),
Bounds::from_corner_and_size(
Corner::BottomLeft,
bounds.bottom_left() + point(inset + thumb_start, -inset),
size(thumb_length, WIDTH),
)
};
// The actual render area of the thumb
let thumb_fill_bounds = if is_vertical {
Bounds::from_corners(
point(
bounds.origin.x + inset + BORDER_WIDTH,
bounds.origin.y + thumb_start + inset,
),
point(
bounds.origin.x + WIDTH - inset,
bounds.origin.y + thumb_end - inset,
),
Bounds::from_corner_and_size(
Corner::TopRight,
bounds.top_right() + point(-inset, inset + thumb_start),
size(thumb_width, thumb_length),
)
} else {
Bounds::from_corners(
point(
bounds.origin.x + thumb_start + inset,
bounds.origin.y + inset + BORDER_WIDTH,
),
point(
bounds.origin.x + thumb_end - inset,
bounds.origin.y + WIDTH - inset,
),
Bounds::from_corner_and_size(
Corner::BottomLeft,
bounds.bottom_left() + point(inset + thumb_start, -inset),
size(thumb_length, thumb_width),
)
};
let bar_hitbox = window.with_content_mask(Some(ContentMask { bounds }), |window| {
window.insert_hitbox(bounds, HitboxBehavior::Normal)
window.insert_hitbox(bounds, gpui::HitboxBehavior::Normal)
});
states.push(AxisPrepaintState {
@@ -592,16 +621,19 @@ impl Element for Scrollbar {
fn paint(
&mut self,
_: Option<&gpui::GlobalElementId>,
_: Option<&gpui::InspectorElementId>,
_: Option<&GlobalElementId>,
_: Option<&InspectorElementId>,
_: Bounds<Pixels>,
_: &mut Self::RequestLayoutState,
prepaint: &mut Self::PrepaintState,
window: &mut Window,
_cx: &mut App,
cx: &mut App,
) {
let view_id = window.current_view();
let hitbox_bounds = prepaint.hitbox.bounds;
let is_visible = self.state.get().is_scrollbar_visible();
let is_visible =
self.state.get().is_scrollbar_visible() || cx.theme().scrollbar_mode.is_always();
let is_hover_to_show = cx.theme().scrollbar_mode.is_hover();
// Update last_scroll_time when offset is changed.
if self.scroll_handle.offset() != self.state.get().last_scroll_offset {
@@ -637,23 +669,14 @@ impl Element for Scrollbar {
bounds,
corner_radii: (0.).into(),
background: gpui::transparent_black().into(),
border_widths: if is_vertical {
Edges {
top: px(0.),
right: px(0.),
bottom: px(0.),
left: BORDER_WIDTH,
}
} else {
Edges {
top: BORDER_WIDTH,
right: px(0.),
bottom: px(0.),
left: px(0.),
}
border_widths: Edges {
top: px(0.),
right: px(0.),
bottom: px(0.),
left: px(0.),
},
border_color: state.border,
border_style: BorderStyle::Solid,
border_style: BorderStyle::default(),
});
cx.paint_quad(
@@ -663,7 +686,6 @@ impl Element for Scrollbar {
window.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| {
@@ -682,10 +704,9 @@ impl Element for Scrollbar {
let safe_range = (-scroll_area_size + container_size)..px(0.);
if is_visible {
if is_hover_to_show || is_visible {
window.on_mouse_event({
let state = self.state.clone();
let view_id = self.view_id;
let scroll_handle = self.scroll_handle.clone();
move |event: &MouseDownEvent, phase, _, cx| {
@@ -734,14 +755,13 @@ impl Element for Scrollbar {
window.on_mouse_event({
let scroll_handle = self.scroll_handle.clone();
let state = self.state.clone();
let view_id = self.view_id;
let max_fps_duration = Duration::from_millis((1000 / self.max_fps) as u64);
move |event: &MouseMoveEvent, _, _, cx| {
let mut notify = false;
// When is hover to show mode or it was visible,
// we need to update the hovered state and increase the last_scroll_time.
let need_hover_to_update = is_visible;
let need_hover_to_update = is_hover_to_show || is_visible;
// Update hovered state for scrollbar
if bounds.contains(&event.position) && need_hover_to_update {
state.set(state.get().with_hovered(Some(axis)));
@@ -815,7 +835,6 @@ impl Element for Scrollbar {
});
window.on_mouse_event({
let view_id = self.view_id;
let state = self.state.clone();
move |_event: &MouseUpEvent, phase, _, cx| {