chore: Improve Scrollbar and Scrollable to support Axis and ScrollbarAxis. (#983)
This commit is contained in:
parent
c5fed17941
commit
1752d3e2b4
6 changed files with 72 additions and 62 deletions
|
|
@ -1,8 +1,8 @@
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use gpui::{
|
use gpui::{
|
||||||
div, px, size, App, AppContext, Context, Entity, Focusable, InteractiveElement, IntoElement,
|
div, px, size, App, AppContext, Axis, Context, Entity, Focusable, InteractiveElement,
|
||||||
ParentElement, Pixels, Render, ScrollHandle, SharedString, Size, Styled, Window,
|
IntoElement, ParentElement, Pixels, Render, ScrollHandle, SharedString, Size, Styled, Window,
|
||||||
};
|
};
|
||||||
use gpui_component::{
|
use gpui_component::{
|
||||||
button::{Button, ButtonGroup},
|
button::{Button, ButtonGroup},
|
||||||
|
|
@ -144,17 +144,17 @@ impl ScrollableStory {
|
||||||
.child(
|
.child(
|
||||||
Button::new("test-axis-both")
|
Button::new("test-axis-both")
|
||||||
.label("Both Scrollbar")
|
.label("Both Scrollbar")
|
||||||
.selected(self.axis == ScrollbarAxis::Both),
|
.selected(self.axis.is_both()),
|
||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
Button::new("test-axis-vertical")
|
Button::new("test-axis-vertical")
|
||||||
.label("Vertical")
|
.label("Vertical")
|
||||||
.selected(self.axis == ScrollbarAxis::Vertical),
|
.selected(self.axis.is_vertical()),
|
||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
Button::new("test-axis-horizontal")
|
Button::new("test-axis-horizontal")
|
||||||
.label("Horizontal")
|
.label("Horizontal")
|
||||||
.selected(self.axis == ScrollbarAxis::Horizontal),
|
.selected(self.axis.is_horizontal()),
|
||||||
)
|
)
|
||||||
.on_click(cx.listener(|view, clicks: &Vec<usize>, _, cx| {
|
.on_click(cx.listener(|view, clicks: &Vec<usize>, _, cx| {
|
||||||
if clicks.contains(&0) {
|
if clicks.contains(&0) {
|
||||||
|
|
@ -298,7 +298,7 @@ impl Render for ScrollableStory {
|
||||||
.p_3()
|
.p_3()
|
||||||
.w(self.test_width)
|
.w(self.test_width)
|
||||||
.id("test-1")
|
.id("test-1")
|
||||||
.scrollable(cx.entity().entity_id(), ScrollbarAxis::Vertical)
|
.scrollable(cx.entity().entity_id(), Axis::Vertical)
|
||||||
.gap_1()
|
.gap_1()
|
||||||
.child("Scrollable Example")
|
.child("Scrollable Example")
|
||||||
.children(self.items.iter().take(500).map(|item| {
|
.children(self.items.iter().take(500).map(|item| {
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,9 @@ use std::{rc::Rc, time::Duration};
|
||||||
|
|
||||||
use gpui::{
|
use gpui::{
|
||||||
anchored, div, point, prelude::FluentBuilder as _, px, Animation, AnimationExt as _,
|
anchored, div, point, prelude::FluentBuilder as _, px, Animation, AnimationExt as _,
|
||||||
AnyElement, App, ClickEvent, DefiniteLength, DismissEvent, Div, EventEmitter, FocusHandle,
|
AnyElement, App, Axis, ClickEvent, DefiniteLength, DismissEvent, Div, EventEmitter,
|
||||||
InteractiveElement as _, IntoElement, KeyBinding, MouseButton, ParentElement, Pixels,
|
FocusHandle, InteractiveElement as _, IntoElement, KeyBinding, MouseButton, ParentElement,
|
||||||
RenderOnce, Styled, Window,
|
Pixels, RenderOnce, Styled, Window,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
|
@ -13,7 +13,6 @@ use crate::{
|
||||||
h_flex,
|
h_flex,
|
||||||
modal::overlay_color,
|
modal::overlay_color,
|
||||||
root::ContextModal as _,
|
root::ContextModal as _,
|
||||||
scroll::ScrollbarAxis,
|
|
||||||
title_bar::TITLE_BAR_HEIGHT,
|
title_bar::TITLE_BAR_HEIGHT,
|
||||||
v_flex, ActiveTheme, IconName, Placement, Sizable, StyledExt as _,
|
v_flex, ActiveTheme, IconName, Placement, Sizable, StyledExt as _,
|
||||||
};
|
};
|
||||||
|
|
@ -205,7 +204,7 @@ impl RenderOnce for Drawer {
|
||||||
// Body
|
// Body
|
||||||
div().flex_1().overflow_hidden().child(
|
div().flex_1().overflow_hidden().child(
|
||||||
v_flex()
|
v_flex()
|
||||||
.scrollable(window.current_view(), ScrollbarAxis::Vertical)
|
.scrollable(window.current_view(), Axis::Vertical)
|
||||||
.child(self.content),
|
.child(self.content),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,8 @@ use std::{rc::Rc, time::Duration};
|
||||||
|
|
||||||
use gpui::{
|
use gpui::{
|
||||||
anchored, div, hsla, point, prelude::FluentBuilder, px, relative, Animation, AnimationExt as _,
|
anchored, div, hsla, point, prelude::FluentBuilder, px, relative, Animation, AnimationExt as _,
|
||||||
AnyElement, App, Bounds, ClickEvent, Div, FocusHandle, Hsla, InteractiveElement, IntoElement,
|
AnyElement, App, Axis, Bounds, ClickEvent, Div, FocusHandle, Hsla, InteractiveElement,
|
||||||
KeyBinding, MouseButton, ParentElement, Pixels, Point, RenderOnce, SharedString,
|
IntoElement, KeyBinding, MouseButton, ParentElement, Pixels, Point, RenderOnce, SharedString,
|
||||||
StyleRefinement, Styled, Window,
|
StyleRefinement, Styled, Window,
|
||||||
};
|
};
|
||||||
use rust_i18n::t;
|
use rust_i18n::t;
|
||||||
|
|
@ -475,10 +475,7 @@ impl RenderOnce for Modal {
|
||||||
v_flex()
|
v_flex()
|
||||||
.pl(padding_left)
|
.pl(padding_left)
|
||||||
.pr(padding_right)
|
.pr(padding_right)
|
||||||
.scrollable(
|
.scrollable(window.current_view(), Axis::Vertical)
|
||||||
window.current_view(),
|
|
||||||
crate::scroll::ScrollbarAxis::Vertical,
|
|
||||||
)
|
|
||||||
.child(self.content),
|
.child(self.content),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,10 @@ use std::{cell::Cell, rc::Rc};
|
||||||
|
|
||||||
use super::{Scrollbar, ScrollbarAxis, ScrollbarState};
|
use super::{Scrollbar, ScrollbarAxis, ScrollbarState};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
canvas, div, relative, AnyElement, App, Div, Element, ElementId, EntityId, GlobalElementId,
|
canvas, div, relative, AnyElement, App, Bounds, Div, Element, ElementId, EntityId,
|
||||||
InteractiveElement, IntoElement, ParentElement, Pixels, Position, ScrollHandle, SharedString,
|
GlobalElementId, InspectorElementId, InteractiveElement, Interactivity, IntoElement, LayoutId,
|
||||||
Size, Stateful, StatefulInteractiveElement, Style, StyleRefinement, Styled, Window,
|
ParentElement, Pixels, Position, ScrollHandle, SharedString, Size, Stateful,
|
||||||
|
StatefulInteractiveElement, Style, StyleRefinement, Styled, Window,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A scroll view is a container that allows the user to scroll through a large amount of content.
|
/// A scroll view is a container that allows the user to scroll through a large amount of content.
|
||||||
|
|
@ -21,9 +22,9 @@ impl<E> Scrollable<E>
|
||||||
where
|
where
|
||||||
E: Element,
|
E: Element,
|
||||||
{
|
{
|
||||||
pub(crate) fn new(view_id: EntityId, element: E, axis: ScrollbarAxis) -> Self {
|
pub(crate) fn new(view_id: EntityId, element: E, axis: impl Into<ScrollbarAxis>) -> Self {
|
||||||
let id = ElementId::Name(SharedString::from(format!(
|
let id = ElementId::Name(SharedString::from(format!(
|
||||||
"ScrollView:{}-{:?}",
|
"scrollable-{}-{:?}",
|
||||||
view_id,
|
view_id,
|
||||||
element.id(),
|
element.id(),
|
||||||
)));
|
)));
|
||||||
|
|
@ -33,7 +34,7 @@ where
|
||||||
_element: div().id("fake"),
|
_element: div().id("fake"),
|
||||||
id,
|
id,
|
||||||
view_id,
|
view_id,
|
||||||
axis,
|
axis: axis.into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -51,8 +52,8 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the axis of the scroll view.
|
/// Set the axis of the scroll view.
|
||||||
pub fn set_axis(&mut self, axis: ScrollbarAxis) {
|
pub fn set_axis(&mut self, axis: impl Into<ScrollbarAxis>) {
|
||||||
self.axis = axis;
|
self.axis = axis.into();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn with_element_state<R>(
|
fn with_element_state<R>(
|
||||||
|
|
@ -117,7 +118,7 @@ impl<E> InteractiveElement for Scrollable<E>
|
||||||
where
|
where
|
||||||
E: Element + InteractiveElement,
|
E: Element + InteractiveElement,
|
||||||
{
|
{
|
||||||
fn interactivity(&mut self) -> &mut gpui::Interactivity {
|
fn interactivity(&mut self) -> &mut Interactivity {
|
||||||
if let Some(element) = &mut self.element {
|
if let Some(element) = &mut self.element {
|
||||||
element.interactivity()
|
element.interactivity()
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -145,7 +146,7 @@ where
|
||||||
type RequestLayoutState = AnyElement;
|
type RequestLayoutState = AnyElement;
|
||||||
type PrepaintState = ScrollViewState;
|
type PrepaintState = ScrollViewState;
|
||||||
|
|
||||||
fn id(&self) -> Option<gpui::ElementId> {
|
fn id(&self) -> Option<ElementId> {
|
||||||
Some(self.id.clone())
|
Some(self.id.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -155,11 +156,11 @@ where
|
||||||
|
|
||||||
fn request_layout(
|
fn request_layout(
|
||||||
&mut self,
|
&mut self,
|
||||||
id: Option<&gpui::GlobalElementId>,
|
id: Option<&GlobalElementId>,
|
||||||
_: Option<&gpui::InspectorElementId>,
|
_: Option<&InspectorElementId>,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> (gpui::LayoutId, Self::RequestLayoutState) {
|
) -> (LayoutId, Self::RequestLayoutState) {
|
||||||
let mut style = Style::default();
|
let mut style = Style::default();
|
||||||
style.flex_grow = 1.0;
|
style.flex_grow = 1.0;
|
||||||
style.position = Position::Relative;
|
style.position = Position::Relative;
|
||||||
|
|
@ -168,7 +169,6 @@ where
|
||||||
|
|
||||||
let axis = self.axis;
|
let axis = self.axis;
|
||||||
let view_id = self.view_id;
|
let view_id = self.view_id;
|
||||||
|
|
||||||
let scroll_id = self.id.clone();
|
let scroll_id = self.id.clone();
|
||||||
let content = self.element.take().map(|c| c.into_any_element());
|
let content = self.element.take().map(|c| c.into_any_element());
|
||||||
|
|
||||||
|
|
@ -208,8 +208,8 @@ where
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.into_any_element();
|
.into_any_element();
|
||||||
let element_id = element.request_layout(window, cx);
|
|
||||||
|
|
||||||
|
let element_id = element.request_layout(window, cx);
|
||||||
let layout_id = window.request_layout(style, vec![element_id], cx);
|
let layout_id = window.request_layout(style, vec![element_id], cx);
|
||||||
|
|
||||||
(layout_id, element)
|
(layout_id, element)
|
||||||
|
|
@ -218,8 +218,8 @@ where
|
||||||
|
|
||||||
fn prepaint(
|
fn prepaint(
|
||||||
&mut self,
|
&mut self,
|
||||||
_: Option<&gpui::GlobalElementId>,
|
_: Option<&GlobalElementId>,
|
||||||
_: Option<&gpui::InspectorElementId>,
|
_: Option<&InspectorElementId>,
|
||||||
_: gpui::Bounds<Pixels>,
|
_: gpui::Bounds<Pixels>,
|
||||||
element: &mut Self::RequestLayoutState,
|
element: &mut Self::RequestLayoutState,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
|
|
@ -232,9 +232,9 @@ where
|
||||||
|
|
||||||
fn paint(
|
fn paint(
|
||||||
&mut self,
|
&mut self,
|
||||||
_: Option<&gpui::GlobalElementId>,
|
_: Option<&GlobalElementId>,
|
||||||
_: Option<&gpui::InspectorElementId>,
|
_: Option<&InspectorElementId>,
|
||||||
_: gpui::Bounds<Pixels>,
|
_: Bounds<Pixels>,
|
||||||
element: &mut Self::RequestLayoutState,
|
element: &mut Self::RequestLayoutState,
|
||||||
_: &mut Self::PrepaintState,
|
_: &mut Self::PrepaintState,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
|
|
|
||||||
|
|
@ -5,12 +5,12 @@ use std::{
|
||||||
time::{Duration, Instant},
|
time::{Duration, Instant},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::ActiveTheme;
|
use crate::{ActiveTheme, AxisExt};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
fill, point, px, relative, size, App, BorderStyle, Bounds, ContentMask, Corner, CursorStyle,
|
fill, point, px, relative, size, App, Axis, BorderStyle, Bounds, ContentMask, Corner,
|
||||||
Edges, Element, EntityId, Hitbox, Hsla, IntoElement, MouseDownEvent, MouseMoveEvent,
|
CursorStyle, Edges, Element, EntityId, Hitbox, Hsla, IntoElement, MouseDownEvent,
|
||||||
MouseUpEvent, PaintQuad, Pixels, Point, Position, ScrollHandle, ScrollWheelEvent, Style,
|
MouseMoveEvent, MouseUpEvent, PaintQuad, Pixels, Point, Position, ScrollHandle,
|
||||||
UniformListScrollHandle, Window,
|
ScrollWheelEvent, Style, UniformListScrollHandle, Window,
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
|
@ -85,9 +85,9 @@ pub struct ScrollbarState(Rc<Cell<ScrollbarStateInner>>);
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct ScrollbarStateInner {
|
pub struct ScrollbarStateInner {
|
||||||
hovered_axis: Option<ScrollbarAxis>,
|
hovered_axis: Option<Axis>,
|
||||||
hovered_on_thumb: Option<ScrollbarAxis>,
|
hovered_on_thumb: Option<Axis>,
|
||||||
dragged_axis: Option<ScrollbarAxis>,
|
dragged_axis: Option<Axis>,
|
||||||
drag_pos: Point<Pixels>,
|
drag_pos: Point<Pixels>,
|
||||||
last_scroll_offset: Point<Pixels>,
|
last_scroll_offset: Point<Pixels>,
|
||||||
last_scroll_time: Option<Instant>,
|
last_scroll_time: Option<Instant>,
|
||||||
|
|
@ -118,7 +118,7 @@ impl Deref for ScrollbarState {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ScrollbarStateInner {
|
impl ScrollbarStateInner {
|
||||||
fn with_drag_pos(&self, axis: ScrollbarAxis, pos: Point<Pixels>) -> Self {
|
fn with_drag_pos(&self, axis: Axis, pos: Point<Pixels>) -> Self {
|
||||||
let mut state = *self;
|
let mut state = *self;
|
||||||
if axis.is_vertical() {
|
if axis.is_vertical() {
|
||||||
state.drag_pos.y = pos.y;
|
state.drag_pos.y = pos.y;
|
||||||
|
|
@ -136,7 +136,7 @@ impl ScrollbarStateInner {
|
||||||
state
|
state
|
||||||
}
|
}
|
||||||
|
|
||||||
fn with_hovered(&self, axis: Option<ScrollbarAxis>) -> Self {
|
fn with_hovered(&self, axis: Option<Axis>) -> Self {
|
||||||
let mut state = *self;
|
let mut state = *self;
|
||||||
state.hovered_axis = axis;
|
state.hovered_axis = axis;
|
||||||
if axis.is_some() {
|
if axis.is_some() {
|
||||||
|
|
@ -145,7 +145,7 @@ impl ScrollbarStateInner {
|
||||||
state
|
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;
|
let mut state = *self;
|
||||||
state.hovered_on_thumb = axis;
|
state.hovered_on_thumb = axis;
|
||||||
if self.is_scrollbar_visible() {
|
if self.is_scrollbar_visible() {
|
||||||
|
|
@ -201,14 +201,28 @@ pub enum ScrollbarAxis {
|
||||||
Both,
|
Both,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<Axis> for ScrollbarAxis {
|
||||||
|
fn from(axis: Axis) -> Self {
|
||||||
|
match axis {
|
||||||
|
Axis::Vertical => Self::Vertical,
|
||||||
|
Axis::Horizontal => Self::Horizontal,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ScrollbarAxis {
|
impl ScrollbarAxis {
|
||||||
#[inline]
|
/// Return true if the scrollbar axis is vertical.
|
||||||
fn is_vertical(&self) -> bool {
|
pub fn is_vertical(&self) -> bool {
|
||||||
matches!(self, Self::Vertical)
|
matches!(self, Self::Vertical)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
/// Return true if the scrollbar axis is horizontal.
|
||||||
fn is_both(&self) -> bool {
|
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)
|
matches!(self, Self::Both)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -223,13 +237,13 @@ impl ScrollbarAxis {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn all(&self) -> Vec<ScrollbarAxis> {
|
fn all(&self) -> Vec<Axis> {
|
||||||
match self {
|
match self {
|
||||||
Self::Vertical => vec![Self::Vertical],
|
Self::Vertical => vec![Axis::Vertical],
|
||||||
Self::Horizontal => vec![Self::Horizontal],
|
Self::Horizontal => vec![Axis::Horizontal],
|
||||||
// This should keep Horizontal first, Vertical is the primary axis
|
// This should keep Horizontal first, Vertical is the primary axis
|
||||||
// if Vertical not need display, then Horizontal will not keep right margin.
|
// 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],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -252,14 +266,14 @@ impl Scrollbar {
|
||||||
fn new(
|
fn new(
|
||||||
view_id: EntityId,
|
view_id: EntityId,
|
||||||
state: ScrollbarState,
|
state: ScrollbarState,
|
||||||
axis: ScrollbarAxis,
|
axis: impl Into<ScrollbarAxis>,
|
||||||
scroll_handle: impl ScrollHandleOffsetable + 'static,
|
scroll_handle: impl ScrollHandleOffsetable + 'static,
|
||||||
scroll_size: gpui::Size<Pixels>,
|
scroll_size: gpui::Size<Pixels>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
view_id,
|
view_id,
|
||||||
state,
|
state,
|
||||||
axis,
|
axis: axis.into(),
|
||||||
scroll_size,
|
scroll_size,
|
||||||
scroll_handle: Rc::new(Box::new(scroll_handle)),
|
scroll_handle: Rc::new(Box::new(scroll_handle)),
|
||||||
max_fps: 120,
|
max_fps: 120,
|
||||||
|
|
@ -337,8 +351,8 @@ impl Scrollbar {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set scrollbar axis.
|
/// Set scrollbar axis.
|
||||||
pub fn axis(mut self, axis: ScrollbarAxis) -> Self {
|
pub fn axis(mut self, axis: impl Into<ScrollbarAxis>) -> Self {
|
||||||
self.axis = axis;
|
self.axis = axis.into();
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -416,7 +430,7 @@ pub struct PrepaintState {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct AxisPrepaintState {
|
pub struct AxisPrepaintState {
|
||||||
axis: ScrollbarAxis,
|
axis: Axis,
|
||||||
bar_hitbox: Hitbox,
|
bar_hitbox: Hitbox,
|
||||||
bounds: Bounds<Pixels>,
|
bounds: Bounds<Pixels>,
|
||||||
radius: Pixels,
|
radius: Pixels,
|
||||||
|
|
|
||||||
|
|
@ -144,7 +144,7 @@ pub trait StyledExt: Styled + Sized {
|
||||||
///
|
///
|
||||||
/// Current this is only have a vertical scrollbar.
|
/// Current this is only have a vertical scrollbar.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn scrollable(self, view_id: EntityId, axis: ScrollbarAxis) -> Scrollable<Self>
|
fn scrollable(self, view_id: EntityId, axis: impl Into<ScrollbarAxis>) -> Scrollable<Self>
|
||||||
where
|
where
|
||||||
Self: Element,
|
Self: Element,
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue