scrollbar: Manage ScrollbarState in Scrollbar internal. (#1690)
This PR to improve the Scrollbar API to manage the state in the
internal.
## Break Changes
Describe any breaking changes introduced by this pull request. If none,
remove this section.
- Removed `scrollbar_state` argument from `Scrollbar::new`,
`Scrollbar::both`, `Scrollbar::vertical` and `Scrollbar::horizontal`.
```diff
- Scrollbar::horizontal(&self.scrollbar_state, &self.scroll_handle)
+ Scrollbar::horizontal(&self.scroll_handle)
- Scrollbar::vertical(&self.scrollbar_state, &self.scroll_handle)
+ Scrollbar::vertical(&self.scroll_handle)
```
- Change `struct ScrollbarState` to private, we not need this not.
```diff
- pub struct ScrollbarState {
+ struct ScrollbarState {
```
- Renamed `trait ScrollHandleOffsetable` to `trait ScrollbarHanle`.
```diff
- pub trait ScrollHandleOffsetable {
+ pub trait ScrollbarHanle {
```
- Removed `Scrollbar::both`, now use `Scrollbar::new` instead.
```diff
- Scrollbar::both(&scroll_handle)
+ Scrollbar::new(&scroll_handle)
```
This commit is contained in:
parent
7f956e0f55
commit
4a3f520c5f
15 changed files with 129 additions and 341 deletions
|
|
@ -1,19 +1,17 @@
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use gpui::{
|
use gpui::{
|
||||||
div, px, size, App, AppContext, Axis, Context, Entity, FocusHandle, Focusable,
|
App, AppContext, Axis, Context, Entity, FocusHandle, Focusable, InteractiveElement,
|
||||||
InteractiveElement, IntoElement, ParentElement, Pixels, Render, Size, Styled, Window,
|
IntoElement, ParentElement, Pixels, Render, Size, Styled, Window, div, px, size,
|
||||||
};
|
};
|
||||||
use gpui_component::{
|
use gpui_component::{
|
||||||
|
ActiveTheme as _, Selectable, StyledExt as _,
|
||||||
button::{Button, ButtonGroup},
|
button::{Button, ButtonGroup},
|
||||||
h_flex,
|
h_flex, v_flex,
|
||||||
scroll::ScrollbarState,
|
|
||||||
v_flex, ActiveTheme as _, Selectable, StyledExt as _,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct ScrollableStory {
|
pub struct ScrollableStory {
|
||||||
focus_handle: FocusHandle,
|
focus_handle: FocusHandle,
|
||||||
scroll_state: ScrollbarState,
|
|
||||||
items: Vec<String>,
|
items: Vec<String>,
|
||||||
item_sizes: Rc<Vec<Size<Pixels>>>,
|
item_sizes: Rc<Vec<Size<Pixels>>>,
|
||||||
test_width: Pixels,
|
test_width: Pixels,
|
||||||
|
|
@ -33,7 +31,6 @@ impl ScrollableStory {
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
focus_handle: cx.focus_handle(),
|
focus_handle: cx.focus_handle(),
|
||||||
scroll_state: ScrollbarState::default(),
|
|
||||||
items,
|
items,
|
||||||
item_sizes: Rc::new(item_sizes),
|
item_sizes: Rc::new(item_sizes),
|
||||||
test_width,
|
test_width,
|
||||||
|
|
@ -69,7 +66,6 @@ impl ScrollableStory {
|
||||||
.map(|_| size(self.test_width, ITEM_HEIGHT))
|
.map(|_| size(self.test_width, ITEM_HEIGHT))
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.into();
|
.into();
|
||||||
self.scroll_state = ScrollbarState::default();
|
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,14 +9,13 @@ use gpui_component::{
|
||||||
button::{Button, ButtonGroup},
|
button::{Button, ButtonGroup},
|
||||||
divider::Divider,
|
divider::Divider,
|
||||||
h_flex,
|
h_flex,
|
||||||
scroll::{Scrollbar, ScrollbarAxis, ScrollbarState},
|
scroll::{Scrollbar, ScrollbarAxis},
|
||||||
v_flex, v_virtual_list,
|
v_flex, v_virtual_list,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct VirtualListStory {
|
pub struct VirtualListStory {
|
||||||
focus_handle: FocusHandle,
|
focus_handle: FocusHandle,
|
||||||
scroll_handle: VirtualListScrollHandle,
|
scroll_handle: VirtualListScrollHandle,
|
||||||
scroll_state: ScrollbarState,
|
|
||||||
items: Vec<String>,
|
items: Vec<String>,
|
||||||
item_sizes: Rc<Vec<Size<Pixels>>>,
|
item_sizes: Rc<Vec<Size<Pixels>>>,
|
||||||
columns_count: usize,
|
columns_count: usize,
|
||||||
|
|
@ -35,7 +34,6 @@ impl VirtualListStory {
|
||||||
Self {
|
Self {
|
||||||
focus_handle: cx.focus_handle(),
|
focus_handle: cx.focus_handle(),
|
||||||
scroll_handle: VirtualListScrollHandle::new(),
|
scroll_handle: VirtualListScrollHandle::new(),
|
||||||
scroll_state: ScrollbarState::default(),
|
|
||||||
items,
|
items,
|
||||||
item_sizes: Rc::new(item_sizes),
|
item_sizes: Rc::new(item_sizes),
|
||||||
columns_count: 100,
|
columns_count: 100,
|
||||||
|
|
@ -68,8 +66,6 @@ impl VirtualListStory {
|
||||||
}
|
}
|
||||||
|
|
||||||
self.item_sizes = Rc::new(self.items.iter().map(|_| ITEM_SIZE).collect());
|
self.item_sizes = Rc::new(self.items.iter().map(|_| ITEM_SIZE).collect());
|
||||||
|
|
||||||
self.scroll_state = ScrollbarState::default();
|
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -293,10 +289,7 @@ impl Render for VirtualListStory {
|
||||||
.left_0()
|
.left_0()
|
||||||
.right_0()
|
.right_0()
|
||||||
.bottom_0()
|
.bottom_0()
|
||||||
.child(
|
.child(Scrollbar::new(&self.scroll_handle).axis(self.axis))
|
||||||
Scrollbar::both(&self.scroll_state, &self.scroll_handle)
|
|
||||||
.axis(self.axis),
|
|
||||||
)
|
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ use std::{
|
||||||
use crate::{
|
use crate::{
|
||||||
ActiveTheme, Icon, IconName, h_flex,
|
ActiveTheme, Icon, IconName, h_flex,
|
||||||
history::{History, HistoryItem},
|
history::{History, HistoryItem},
|
||||||
scroll::{Scrollbar, ScrollbarShow, ScrollbarState},
|
scroll::{Scrollbar, ScrollbarShow},
|
||||||
v_flex,
|
v_flex,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -139,7 +139,6 @@ pub struct Tiles {
|
||||||
resizing_drag_data: Option<ResizeDrag>,
|
resizing_drag_data: Option<ResizeDrag>,
|
||||||
bounds: Bounds<Pixels>,
|
bounds: Bounds<Pixels>,
|
||||||
history: History<TileChange>,
|
history: History<TileChange>,
|
||||||
scroll_state: ScrollbarState,
|
|
||||||
scroll_handle: ScrollHandle,
|
scroll_handle: ScrollHandle,
|
||||||
scrollbar_show: Option<ScrollbarShow>,
|
scrollbar_show: Option<ScrollbarShow>,
|
||||||
}
|
}
|
||||||
|
|
@ -195,7 +194,6 @@ impl Tiles {
|
||||||
resizing_drag_data: None,
|
resizing_drag_data: None,
|
||||||
bounds: Bounds::default(),
|
bounds: Bounds::default(),
|
||||||
history: History::new().group_interval(std::time::Duration::from_millis(100)),
|
history: History::new().group_interval(std::time::Duration::from_millis(100)),
|
||||||
scroll_state: ScrollbarState::default(),
|
|
||||||
scroll_handle: ScrollHandle::default(),
|
scroll_handle: ScrollHandle::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1220,7 +1218,7 @@ impl Render for Tiles {
|
||||||
.right_0()
|
.right_0()
|
||||||
.bottom_0()
|
.bottom_0()
|
||||||
.child(
|
.child(
|
||||||
Scrollbar::both(&self.scroll_state, &self.scroll_handle)
|
Scrollbar::new(&self.scroll_handle)
|
||||||
.scroll_size(scroll_size)
|
.scroll_size(scroll_size)
|
||||||
.when_some(self.scrollbar_show, |this, scrollbar_show| {
|
.when_some(self.scrollbar_show, |this, scrollbar_show| {
|
||||||
this.scrollbar_show(scrollbar_show)
|
this.scrollbar_show(scrollbar_show)
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
use gpui::prelude::FluentBuilder as _;
|
use gpui::prelude::FluentBuilder as _;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
div, px, relative, AnyElement, App, DefiniteLength, Edges, EdgesRefinement, Entity,
|
AnyElement, App, DefiniteLength, Edges, EdgesRefinement, Entity, InteractiveElement as _,
|
||||||
InteractiveElement as _, IntoElement, IsZero, MouseButton, ParentElement as _, Pixels, Rems,
|
IntoElement, IsZero, MouseButton, ParentElement as _, Pixels, Rems, RenderOnce,
|
||||||
RenderOnce, StyleRefinement, Styled, Window,
|
StyleRefinement, Styled, Window, div, px, relative,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::button::{Button, ButtonVariants as _};
|
use crate::button::{Button, ButtonVariants as _};
|
||||||
|
|
@ -10,9 +10,9 @@ use crate::input::clear_button;
|
||||||
use crate::input::element::{LINE_NUMBER_RIGHT_MARGIN, RIGHT_MARGIN};
|
use crate::input::element::{LINE_NUMBER_RIGHT_MARGIN, RIGHT_MARGIN};
|
||||||
use crate::scroll::Scrollbar;
|
use crate::scroll::Scrollbar;
|
||||||
use crate::spinner::Spinner;
|
use crate::spinner::Spinner;
|
||||||
use crate::{h_flex, Selectable, StyledExt};
|
use crate::{ActiveTheme, v_flex};
|
||||||
use crate::{v_flex, ActiveTheme};
|
|
||||||
use crate::{IconName, Size};
|
use crate::{IconName, Size};
|
||||||
|
use crate::{Selectable, StyledExt, h_flex};
|
||||||
use crate::{Sizable, StyleSized};
|
use crate::{Sizable, StyleSized};
|
||||||
|
|
||||||
use super::InputState;
|
use super::InputState;
|
||||||
|
|
@ -213,9 +213,9 @@ impl Input {
|
||||||
};
|
};
|
||||||
|
|
||||||
let scrollbar = if !state.soft_wrap {
|
let scrollbar = if !state.soft_wrap {
|
||||||
Scrollbar::both(&state.scroll_state, &state.scroll_handle)
|
Scrollbar::new(&state.scroll_handle)
|
||||||
} else {
|
} else {
|
||||||
Scrollbar::vertical(&state.scroll_state, &state.scroll_handle)
|
Scrollbar::vertical(&state.scroll_handle)
|
||||||
};
|
};
|
||||||
|
|
||||||
this.relative().child(
|
this.relative().child(
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ use crate::input::{
|
||||||
text_wrapper::LineLayout,
|
text_wrapper::LineLayout,
|
||||||
};
|
};
|
||||||
use crate::input::{RopeExt as _, Selection};
|
use crate::input::{RopeExt as _, Selection};
|
||||||
use crate::{Root, history::History, scroll::ScrollbarState};
|
use crate::{Root, history::History};
|
||||||
use crate::{highlighter::DiagnosticSet, input::text_wrapper::LineItem};
|
use crate::{highlighter::DiagnosticSet, input::text_wrapper::LineItem};
|
||||||
|
|
||||||
#[derive(Action, Clone, PartialEq, Eq, Deserialize)]
|
#[derive(Action, Clone, PartialEq, Eq, Deserialize)]
|
||||||
|
|
@ -295,7 +295,6 @@ pub struct InputState {
|
||||||
pub(crate) scroll_handle: ScrollHandle,
|
pub(crate) scroll_handle: ScrollHandle,
|
||||||
/// The deferred scroll offset to apply on next layout.
|
/// The deferred scroll offset to apply on next layout.
|
||||||
pub(crate) deferred_scroll_offset: Option<Point<Pixels>>,
|
pub(crate) deferred_scroll_offset: Option<Point<Pixels>>,
|
||||||
pub(super) scroll_state: ScrollbarState,
|
|
||||||
/// The size of the scrollable content.
|
/// The size of the scrollable content.
|
||||||
pub(crate) scroll_size: gpui::Size<Pixels>,
|
pub(crate) scroll_size: gpui::Size<Pixels>,
|
||||||
|
|
||||||
|
|
@ -396,7 +395,6 @@ impl InputState {
|
||||||
last_selected_range: None,
|
last_selected_range: None,
|
||||||
last_cursor: None,
|
last_cursor: None,
|
||||||
scroll_handle: ScrollHandle::new(),
|
scroll_handle: ScrollHandle::new(),
|
||||||
scroll_state: ScrollbarState::default(),
|
|
||||||
scroll_size: gpui::size(px(0.), px(0.)),
|
scroll_size: gpui::size(px(0.), px(0.)),
|
||||||
deferred_scroll_offset: None,
|
deferred_scroll_offset: None,
|
||||||
preferred_column: None,
|
preferred_column: None,
|
||||||
|
|
|
||||||
|
|
@ -5,20 +5,21 @@ use crate::actions::{Cancel, Confirm, SelectDown, SelectUp};
|
||||||
use crate::input::InputState;
|
use crate::input::InputState;
|
||||||
use crate::list::cache::{MeasuredEntrySize, RowEntry, RowsCache};
|
use crate::list::cache::{MeasuredEntrySize, RowEntry, RowsCache};
|
||||||
use crate::{
|
use crate::{
|
||||||
|
ActiveTheme, IconName, Size,
|
||||||
input::{Input, InputEvent},
|
input::{Input, InputEvent},
|
||||||
scroll::{Scrollbar, ScrollbarState},
|
scroll::Scrollbar,
|
||||||
v_flex, ActiveTheme, IconName, Size,
|
v_flex,
|
||||||
};
|
};
|
||||||
use crate::{list::ListDelegate, v_virtual_list, VirtualListScrollHandle};
|
|
||||||
use crate::{Icon, IndexPath, Selectable, Sizable, StyledExt};
|
use crate::{Icon, IndexPath, Selectable, Sizable, StyledExt};
|
||||||
|
use crate::{VirtualListScrollHandle, list::ListDelegate, v_virtual_list};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
div, prelude::FluentBuilder, AppContext, Entity, FocusHandle, Focusable, InteractiveElement,
|
App, AvailableSpace, ClickEvent, Context, DefiniteLength, EdgesRefinement, EventEmitter,
|
||||||
IntoElement, KeyBinding, Length, MouseButton, ParentElement, Render, Styled, Task, Window,
|
ListSizingBehavior, RenderOnce, ScrollStrategy, SharedString, StatefulInteractiveElement,
|
||||||
|
StyleRefinement, Subscription, px, size,
|
||||||
};
|
};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
px, size, App, AvailableSpace, ClickEvent, Context, DefiniteLength, EdgesRefinement,
|
AppContext, Entity, FocusHandle, Focusable, InteractiveElement, IntoElement, KeyBinding,
|
||||||
EventEmitter, ListSizingBehavior, RenderOnce, ScrollStrategy, SharedString,
|
Length, MouseButton, ParentElement, Render, Styled, Task, Window, div, prelude::FluentBuilder,
|
||||||
StatefulInteractiveElement, StyleRefinement, Subscription,
|
|
||||||
};
|
};
|
||||||
use rust_i18n::t;
|
use rust_i18n::t;
|
||||||
use smol::Timer;
|
use smol::Timer;
|
||||||
|
|
@ -72,7 +73,6 @@ pub struct ListState<D: ListDelegate> {
|
||||||
delegate: D,
|
delegate: D,
|
||||||
last_query: Option<String>,
|
last_query: Option<String>,
|
||||||
scroll_handle: VirtualListScrollHandle,
|
scroll_handle: VirtualListScrollHandle,
|
||||||
scroll_state: ScrollbarState,
|
|
||||||
rows_cache: RowsCache,
|
rows_cache: RowsCache,
|
||||||
selected_index: Option<IndexPath>,
|
selected_index: Option<IndexPath>,
|
||||||
item_to_measure_index: IndexPath,
|
item_to_measure_index: IndexPath,
|
||||||
|
|
@ -111,7 +111,6 @@ where
|
||||||
deferred_scroll_to_index: None,
|
deferred_scroll_to_index: None,
|
||||||
mouse_right_clicked_index: None,
|
mouse_right_clicked_index: None,
|
||||||
scroll_handle: VirtualListScrollHandle::new(),
|
scroll_handle: VirtualListScrollHandle::new(),
|
||||||
scroll_state: ScrollbarState::default(),
|
|
||||||
reset_on_cancel: true,
|
reset_on_cancel: true,
|
||||||
_search_task: Task::ready(()),
|
_search_task: Task::ready(()),
|
||||||
_load_more_task: Task::ready(()),
|
_load_more_task: Task::ready(()),
|
||||||
|
|
@ -484,7 +483,6 @@ where
|
||||||
let rows_cache = self.rows_cache.clone();
|
let rows_cache = self.rows_cache.clone();
|
||||||
let scrollbar_visible = self.options.scrollbar_visible;
|
let scrollbar_visible = self.options.scrollbar_visible;
|
||||||
let scroll_handle = self.scroll_handle.clone();
|
let scroll_handle = self.scroll_handle.clone();
|
||||||
let scroll_state = self.scroll_state.clone();
|
|
||||||
let measured_size = rows_cache.measured_size();
|
let measured_size = rows_cache.measured_size();
|
||||||
|
|
||||||
v_flex()
|
v_flex()
|
||||||
|
|
@ -550,7 +548,7 @@ where
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.when(scrollbar_visible, |this| {
|
.when(scrollbar_visible, |this| {
|
||||||
this.child(Scrollbar::vertical(&scroll_state, &scroll_handle))
|
this.child(Scrollbar::vertical(&scroll_handle))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::actions::{Cancel, Confirm, SelectDown, SelectUp};
|
use crate::actions::{Cancel, Confirm, SelectDown, SelectUp};
|
||||||
use crate::actions::{SelectLeft, SelectRight};
|
use crate::actions::{SelectLeft, SelectRight};
|
||||||
use crate::menu::menu_item::MenuItemElement;
|
use crate::menu::menu_item::MenuItemElement;
|
||||||
use crate::scroll::{Scrollbar, ScrollbarState};
|
use crate::scroll::Scrollbar;
|
||||||
use crate::{ActiveTheme, Icon, IconName, Sizable as _, h_flex, v_flex};
|
use crate::{ActiveTheme, Icon, IconName, Sizable as _, h_flex, v_flex};
|
||||||
use crate::{Side, Size, StyledExt, kbd::Kbd};
|
use crate::{Side, Size, StyledExt, kbd::Kbd};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
|
|
@ -287,7 +287,6 @@ pub struct PopupMenu {
|
||||||
scrollable: bool,
|
scrollable: bool,
|
||||||
external_link_icon: bool,
|
external_link_icon: bool,
|
||||||
scroll_handle: ScrollHandle,
|
scroll_handle: ScrollHandle,
|
||||||
scroll_state: ScrollbarState,
|
|
||||||
// This will update on render
|
// This will update on render
|
||||||
submenu_anchor: (Corner, Pixels),
|
submenu_anchor: (Corner, Pixels),
|
||||||
|
|
||||||
|
|
@ -309,7 +308,6 @@ impl PopupMenu {
|
||||||
bounds: Bounds::default(),
|
bounds: Bounds::default(),
|
||||||
scrollable: false,
|
scrollable: false,
|
||||||
scroll_handle: ScrollHandle::default(),
|
scroll_handle: ScrollHandle::default(),
|
||||||
scroll_state: ScrollbarState::default(),
|
|
||||||
external_link_icon: true,
|
external_link_icon: true,
|
||||||
size: Size::default(),
|
size: Size::default(),
|
||||||
submenu_anchor: (Corner::TopLeft, Pixels::ZERO),
|
submenu_anchor: (Corner::TopLeft, Pixels::ZERO),
|
||||||
|
|
@ -1318,7 +1316,7 @@ impl Render for PopupMenu {
|
||||||
.left_0()
|
.left_0()
|
||||||
.right_0()
|
.right_0()
|
||||||
.bottom_0()
|
.bottom_0()
|
||||||
.child(Scrollbar::vertical(&self.scroll_state, &self.scroll_handle)),
|
.child(Scrollbar::vertical(&self.scroll_handle)),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
use super::{Scrollbar, ScrollbarAxis, ScrollbarState};
|
use super::{Scrollbar, ScrollbarAxis};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
div, relative, AnyElement, App, Bounds, Div, Element, ElementId, GlobalElementId,
|
AnyElement, App, Bounds, Div, Element, ElementId, GlobalElementId, InspectorElementId,
|
||||||
InspectorElementId, InteractiveElement, Interactivity, IntoElement, LayoutId, ParentElement,
|
InteractiveElement, Interactivity, IntoElement, LayoutId, ParentElement, Pixels, Position,
|
||||||
Pixels, Position, ScrollHandle, SharedString, Stateful, StatefulInteractiveElement, Style,
|
ScrollHandle, SharedString, Stateful, StatefulInteractiveElement, Style, StyleRefinement,
|
||||||
StyleRefinement, Styled, Window,
|
Styled, Window, div, relative,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// 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.
|
||||||
|
|
@ -69,7 +69,6 @@ where
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub struct ScrollViewState {
|
pub struct ScrollViewState {
|
||||||
state: ScrollbarState,
|
|
||||||
handle: ScrollHandle,
|
handle: ScrollHandle,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -77,7 +76,6 @@ impl Default for ScrollViewState {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
handle: ScrollHandle::new(),
|
handle: ScrollHandle::new(),
|
||||||
state: ScrollbarState::default(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -184,9 +182,7 @@ where
|
||||||
.left_0()
|
.left_0()
|
||||||
.right_0()
|
.right_0()
|
||||||
.bottom_0()
|
.bottom_0()
|
||||||
.child(
|
.child(Scrollbar::new(&element_state.handle).axis(axis)),
|
||||||
Scrollbar::both(&element_state.state, &element_state.handle).axis(axis),
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
.into_any_element();
|
.into_any_element();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,18 @@
|
||||||
use std::{
|
use std::{
|
||||||
cell::Cell,
|
cell::Cell,
|
||||||
ops::Deref,
|
ops::Deref,
|
||||||
|
panic::Location,
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
time::{Duration, Instant},
|
time::{Duration, Instant},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{ActiveTheme, AxisExt};
|
use crate::{ActiveTheme, AxisExt};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
fill, point, px, relative, size, App, Axis, BorderStyle, Bounds, ContentMask, Corner,
|
App, Axis, BorderStyle, Bounds, ContentMask, Corner, CursorStyle, Edges, Element, ElementId,
|
||||||
CursorStyle, Edges, Element, GlobalElementId, Hitbox, HitboxBehavior, Hsla, InspectorElementId,
|
GlobalElementId, Hitbox, HitboxBehavior, Hsla, InspectorElementId, IntoElement, IsZero,
|
||||||
IntoElement, IsZero, LayoutId, ListState, MouseDownEvent, MouseMoveEvent, MouseUpEvent,
|
LayoutId, ListState, MouseDownEvent, MouseMoveEvent, MouseUpEvent, PaintQuad, Pixels, Point,
|
||||||
PaintQuad, Pixels, Point, Position, ScrollHandle, ScrollWheelEvent, Size, Style, Timer,
|
Position, ScrollHandle, ScrollWheelEvent, Size, Style, Timer, UniformListScrollHandle, Window,
|
||||||
UniformListScrollHandle, Window,
|
fill, point, px, relative, size,
|
||||||
};
|
};
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
@ -54,7 +55,7 @@ impl ScrollbarShow {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A trait for scroll handles that can get and set offset.
|
/// A trait for scroll handles that can get and set offset.
|
||||||
pub trait ScrollHandleOffsetable {
|
pub trait ScrollbarHandle: 'static {
|
||||||
/// Get the current offset of the scroll handle.
|
/// Get the current offset of the scroll handle.
|
||||||
fn offset(&self) -> Point<Pixels>;
|
fn offset(&self) -> Point<Pixels>;
|
||||||
/// Set the offset of the scroll handle.
|
/// Set the offset of the scroll handle.
|
||||||
|
|
@ -67,7 +68,7 @@ pub trait ScrollHandleOffsetable {
|
||||||
fn end_drag(&self) {}
|
fn end_drag(&self) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ScrollHandleOffsetable for ScrollHandle {
|
impl ScrollbarHandle for ScrollHandle {
|
||||||
fn offset(&self) -> Point<Pixels> {
|
fn offset(&self) -> Point<Pixels> {
|
||||||
self.offset()
|
self.offset()
|
||||||
}
|
}
|
||||||
|
|
@ -81,7 +82,7 @@ impl ScrollHandleOffsetable for ScrollHandle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ScrollHandleOffsetable for UniformListScrollHandle {
|
impl ScrollbarHandle for UniformListScrollHandle {
|
||||||
fn offset(&self) -> Point<Pixels> {
|
fn offset(&self) -> Point<Pixels> {
|
||||||
self.0.borrow().base_handle.offset()
|
self.0.borrow().base_handle.offset()
|
||||||
}
|
}
|
||||||
|
|
@ -96,7 +97,7 @@ impl ScrollHandleOffsetable for UniformListScrollHandle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ScrollHandleOffsetable for ListState {
|
impl ScrollbarHandle for ListState {
|
||||||
fn offset(&self) -> Point<Pixels> {
|
fn offset(&self) -> Point<Pixels> {
|
||||||
self.scroll_px_offset_for_scrollbar()
|
self.scroll_px_offset_for_scrollbar()
|
||||||
}
|
}
|
||||||
|
|
@ -120,11 +121,11 @@ impl ScrollHandleOffsetable for ListState {
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct ScrollbarState(Rc<Cell<ScrollbarStateInner>>);
|
struct ScrollbarState(Rc<Cell<ScrollbarStateInner>>);
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct ScrollbarStateInner {
|
struct ScrollbarStateInner {
|
||||||
hovered_axis: Option<Axis>,
|
hovered_axis: Option<Axis>,
|
||||||
hovered_on_thumb: Option<Axis>,
|
hovered_on_thumb: Option<Axis>,
|
||||||
dragged_axis: Option<Axis>,
|
dragged_axis: Option<Axis>,
|
||||||
|
|
@ -307,10 +308,10 @@ impl ScrollbarAxis {
|
||||||
|
|
||||||
/// Scrollbar control for scroll-area or a uniform-list.
|
/// Scrollbar control for scroll-area or a uniform-list.
|
||||||
pub struct Scrollbar {
|
pub struct Scrollbar {
|
||||||
|
pub(crate) id: ElementId,
|
||||||
axis: ScrollbarAxis,
|
axis: ScrollbarAxis,
|
||||||
scrollbar_show: Option<ScrollbarShow>,
|
scrollbar_show: Option<ScrollbarShow>,
|
||||||
scroll_handle: Rc<dyn ScrollHandleOffsetable>,
|
scroll_handle: Rc<dyn ScrollbarHandle>,
|
||||||
state: ScrollbarState,
|
|
||||||
scroll_size: Option<Size<Pixels>>,
|
scroll_size: Option<Size<Pixels>>,
|
||||||
/// Maximum frames per second for scrolling by drag. Default is 120 FPS.
|
/// Maximum frames per second for scrolling by drag. Default is 120 FPS.
|
||||||
///
|
///
|
||||||
|
|
@ -320,14 +321,15 @@ pub struct Scrollbar {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Scrollbar {
|
impl Scrollbar {
|
||||||
fn new(
|
/// Create a new scrollbar.
|
||||||
axis: impl Into<ScrollbarAxis>,
|
///
|
||||||
state: &ScrollbarState,
|
/// This will have both vertical and horizontal scrollbars.
|
||||||
scroll_handle: &(impl ScrollHandleOffsetable + Clone + 'static),
|
#[track_caller]
|
||||||
) -> Self {
|
pub fn new<H: ScrollbarHandle + Clone>(scroll_handle: &H) -> Self {
|
||||||
|
let caller = Location::caller();
|
||||||
Self {
|
Self {
|
||||||
state: state.clone(),
|
id: ElementId::CodeLocation(*caller),
|
||||||
axis: axis.into(),
|
axis: ScrollbarAxis::Both,
|
||||||
scrollbar_show: None,
|
scrollbar_show: None,
|
||||||
scroll_handle: Rc::new(scroll_handle.clone()),
|
scroll_handle: Rc::new(scroll_handle.clone()),
|
||||||
max_fps: 120,
|
max_fps: 120,
|
||||||
|
|
@ -335,28 +337,24 @@ impl Scrollbar {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create with vertical and horizontal scrollbar.
|
|
||||||
pub fn both(
|
|
||||||
state: &ScrollbarState,
|
|
||||||
scroll_handle: &(impl ScrollHandleOffsetable + Clone + 'static),
|
|
||||||
) -> Self {
|
|
||||||
Self::new(ScrollbarAxis::Both, state, scroll_handle)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create with horizontal scrollbar.
|
/// Create with horizontal scrollbar.
|
||||||
pub fn horizontal(
|
#[track_caller]
|
||||||
state: &ScrollbarState,
|
pub fn horizontal<H: ScrollbarHandle + Clone>(scroll_handle: &H) -> Self {
|
||||||
scroll_handle: &(impl ScrollHandleOffsetable + Clone + 'static),
|
Self::new(scroll_handle).axis(ScrollbarAxis::Horizontal)
|
||||||
) -> Self {
|
|
||||||
Self::new(ScrollbarAxis::Horizontal, state, scroll_handle)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create with vertical scrollbar.
|
/// Create with vertical scrollbar.
|
||||||
pub fn vertical(
|
#[track_caller]
|
||||||
state: &ScrollbarState,
|
pub fn vertical<H: ScrollbarHandle + Clone>(scroll_handle: &H) -> Self {
|
||||||
scroll_handle: &(impl ScrollHandleOffsetable + Clone + 'static),
|
Self::new(scroll_handle).axis(ScrollbarAxis::Vertical)
|
||||||
) -> Self {
|
}
|
||||||
Self::new(ScrollbarAxis::Vertical, state, scroll_handle)
|
|
||||||
|
/// Set a specific element id, default is the [`Location::caller`].
|
||||||
|
///
|
||||||
|
/// NOTE: In most cases, you don't need to set a specific id for scrollbar.
|
||||||
|
pub fn id(mut self, id: impl Into<ElementId>) -> Self {
|
||||||
|
self.id = id.into();
|
||||||
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the scrollbar show mode [`ScrollbarShow`], if not set use the `cx.theme().scrollbar_show`.
|
/// Set the scrollbar show mode [`ScrollbarShow`], if not set use the `cx.theme().scrollbar_show`.
|
||||||
|
|
@ -473,6 +471,7 @@ impl IntoElement for Scrollbar {
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub struct PrepaintState {
|
pub struct PrepaintState {
|
||||||
hitbox: Hitbox,
|
hitbox: Hitbox,
|
||||||
|
scrollbar_state: ScrollbarState,
|
||||||
states: Vec<AxisPrepaintState>,
|
states: Vec<AxisPrepaintState>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -499,7 +498,7 @@ impl Element for Scrollbar {
|
||||||
type PrepaintState = PrepaintState;
|
type PrepaintState = PrepaintState;
|
||||||
|
|
||||||
fn id(&self) -> Option<gpui::ElementId> {
|
fn id(&self) -> Option<gpui::ElementId> {
|
||||||
None
|
Some(self.id.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn source_location(&self) -> Option<&'static std::panic::Location<'static>> {
|
fn source_location(&self) -> Option<&'static std::panic::Location<'static>> {
|
||||||
|
|
@ -536,6 +535,11 @@ impl Element for Scrollbar {
|
||||||
window.insert_hitbox(bounds, HitboxBehavior::Normal)
|
window.insert_hitbox(bounds, HitboxBehavior::Normal)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let state = window
|
||||||
|
.use_state(cx, |_, _| ScrollbarState::default())
|
||||||
|
.read(cx)
|
||||||
|
.clone();
|
||||||
|
|
||||||
let mut states = vec![];
|
let mut states = vec![];
|
||||||
let mut has_both = self.axis.is_both();
|
let mut has_both = self.axis.is_both();
|
||||||
let scroll_size = self
|
let scroll_size = self
|
||||||
|
|
@ -601,7 +605,6 @@ impl Element for Scrollbar {
|
||||||
};
|
};
|
||||||
|
|
||||||
let scrollbar_show = self.scrollbar_show.unwrap_or(cx.theme().scrollbar_show);
|
let scrollbar_show = self.scrollbar_show.unwrap_or(cx.theme().scrollbar_show);
|
||||||
let state = self.state.clone();
|
|
||||||
let is_always_to_show = scrollbar_show.is_always();
|
let is_always_to_show = scrollbar_show.is_always();
|
||||||
let is_hover_to_show = scrollbar_show.is_hover();
|
let is_hover_to_show = scrollbar_show.is_hover();
|
||||||
let is_hovered_on_bar = state.get().hovered_axis == Some(axis);
|
let is_hovered_on_bar = state.get().hovered_axis == Some(axis);
|
||||||
|
|
@ -716,7 +719,11 @@ impl Element for Scrollbar {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
PrepaintState { hitbox, states }
|
PrepaintState {
|
||||||
|
hitbox,
|
||||||
|
states,
|
||||||
|
scrollbar_state: state,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn paint(
|
fn paint(
|
||||||
|
|
@ -729,16 +736,17 @@ impl Element for Scrollbar {
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) {
|
) {
|
||||||
|
let scrollbar_state = &prepaint.scrollbar_state;
|
||||||
let scrollbar_show = self.scrollbar_show.unwrap_or(cx.theme().scrollbar_show);
|
let scrollbar_show = self.scrollbar_show.unwrap_or(cx.theme().scrollbar_show);
|
||||||
let view_id = window.current_view();
|
let view_id = window.current_view();
|
||||||
let hitbox_bounds = prepaint.hitbox.bounds;
|
let hitbox_bounds = prepaint.hitbox.bounds;
|
||||||
let is_visible = self.state.get().is_scrollbar_visible() || scrollbar_show.is_always();
|
let is_visible = scrollbar_state.get().is_scrollbar_visible() || scrollbar_show.is_always();
|
||||||
let is_hover_to_show = scrollbar_show.is_hover();
|
let is_hover_to_show = scrollbar_show.is_hover();
|
||||||
|
|
||||||
// Update last_scroll_time when offset is changed.
|
// Update last_scroll_time when offset is changed.
|
||||||
if self.scroll_handle.offset() != self.state.get().last_scroll_offset {
|
if self.scroll_handle.offset() != scrollbar_state.get().last_scroll_offset {
|
||||||
self.state.set(
|
scrollbar_state.set(
|
||||||
self.state
|
scrollbar_state
|
||||||
.get()
|
.get()
|
||||||
.with_last_scroll(self.scroll_handle.offset(), Some(Instant::now())),
|
.with_last_scroll(self.scroll_handle.offset(), Some(Instant::now())),
|
||||||
);
|
);
|
||||||
|
|
@ -798,7 +806,7 @@ impl Element for Scrollbar {
|
||||||
});
|
});
|
||||||
|
|
||||||
window.on_mouse_event({
|
window.on_mouse_event({
|
||||||
let state = self.state.clone();
|
let state = scrollbar_state.clone();
|
||||||
let scroll_handle = self.scroll_handle.clone();
|
let scroll_handle = self.scroll_handle.clone();
|
||||||
|
|
||||||
move |event: &ScrollWheelEvent, phase, _, cx| {
|
move |event: &ScrollWheelEvent, phase, _, cx| {
|
||||||
|
|
@ -818,7 +826,7 @@ impl Element for Scrollbar {
|
||||||
|
|
||||||
if is_hover_to_show || is_visible {
|
if is_hover_to_show || is_visible {
|
||||||
window.on_mouse_event({
|
window.on_mouse_event({
|
||||||
let state = self.state.clone();
|
let state = scrollbar_state.clone();
|
||||||
let scroll_handle = self.scroll_handle.clone();
|
let scroll_handle = self.scroll_handle.clone();
|
||||||
|
|
||||||
move |event: &MouseDownEvent, phase, _, cx| {
|
move |event: &MouseDownEvent, phase, _, cx| {
|
||||||
|
|
@ -867,7 +875,7 @@ impl Element for Scrollbar {
|
||||||
|
|
||||||
window.on_mouse_event({
|
window.on_mouse_event({
|
||||||
let scroll_handle = self.scroll_handle.clone();
|
let scroll_handle = self.scroll_handle.clone();
|
||||||
let state = self.state.clone();
|
let state = scrollbar_state.clone();
|
||||||
let max_fps_duration = Duration::from_millis((1000 / self.max_fps) as u64);
|
let max_fps_duration = Duration::from_millis((1000 / self.max_fps) as u64);
|
||||||
|
|
||||||
move |event: &MouseMoveEvent, _, _, cx| {
|
move |event: &MouseMoveEvent, _, _, cx| {
|
||||||
|
|
@ -955,8 +963,8 @@ impl Element for Scrollbar {
|
||||||
});
|
});
|
||||||
|
|
||||||
window.on_mouse_event({
|
window.on_mouse_event({
|
||||||
|
let state = scrollbar_state.clone();
|
||||||
let scroll_handle = self.scroll_handle.clone();
|
let scroll_handle = self.scroll_handle.clone();
|
||||||
let state = self.state.clone();
|
|
||||||
|
|
||||||
move |_event: &MouseUpEvent, phase, _, cx| {
|
move |_event: &MouseUpEvent, phase, _, cx| {
|
||||||
if phase.bubble() {
|
if phase.bubble() {
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,17 @@
|
||||||
use gpui::{
|
use gpui::{
|
||||||
div, list, prelude::FluentBuilder as _, px, App, Entity, InteractiveElement as _, IntoElement,
|
App, Entity, InteractiveElement as _, IntoElement, ListAlignment, ListState,
|
||||||
ListAlignment, ListState, ParentElement as _, SharedString, Styled, Window,
|
ParentElement as _, SharedString, Styled, Window, div, list, prelude::FluentBuilder as _, px,
|
||||||
};
|
};
|
||||||
use rust_i18n::t;
|
use rust_i18n::t;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
ActiveTheme, IconName, Sizable,
|
||||||
button::{Button, ButtonVariants},
|
button::{Button, ButtonVariants},
|
||||||
h_flex,
|
h_flex,
|
||||||
label::Label,
|
label::Label,
|
||||||
scroll::{Scrollbar, ScrollbarState},
|
scroll::Scrollbar,
|
||||||
setting::{settings::SettingsState, RenderOptions, SettingGroup},
|
setting::{RenderOptions, SettingGroup, settings::SettingsState},
|
||||||
v_flex, ActiveTheme, IconName, Sizable,
|
v_flex,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A setting page that can contain multiple setting groups.
|
/// A setting page that can contain multiple setting groups.
|
||||||
|
|
@ -100,16 +101,11 @@ impl SettingPage {
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
let groups_count = groups.len();
|
let groups_count = groups.len();
|
||||||
|
|
||||||
let (scroll_state, list_state) = window
|
let list_state = window
|
||||||
.use_keyed_state(
|
.use_keyed_state(
|
||||||
SharedString::from(format!("list-state:{}", ix)),
|
SharedString::from(format!("list-state:{}", ix)),
|
||||||
cx,
|
cx,
|
||||||
|_, _| {
|
|_, _| ListState::new(groups_count, ListAlignment::Top, px(100.)),
|
||||||
(
|
|
||||||
ScrollbarState::default(),
|
|
||||||
ListState::new(groups_count, ListAlignment::Top, px(100.)),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
.read(cx)
|
.read(cx)
|
||||||
.clone();
|
.clone();
|
||||||
|
|
@ -188,7 +184,7 @@ impl SettingPage {
|
||||||
.left_0()
|
.left_0()
|
||||||
.right_0()
|
.right_0()
|
||||||
.bottom_0()
|
.bottom_0()
|
||||||
.child(Scrollbar::vertical(&scroll_state, &list_state)),
|
.child(Scrollbar::vertical(&list_state)),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ use crate::{
|
||||||
actions::{Cancel, SelectDown, SelectUp},
|
actions::{Cancel, SelectDown, SelectUp},
|
||||||
h_flex,
|
h_flex,
|
||||||
menu::{ContextMenuExt, PopupMenu},
|
menu::{ContextMenuExt, PopupMenu},
|
||||||
scroll::{ScrollableMask, Scrollbar, ScrollbarState},
|
scroll::{ScrollableMask, Scrollbar},
|
||||||
v_flex,
|
v_flex,
|
||||||
};
|
};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
|
|
@ -95,9 +95,7 @@ pub struct TableState<D: TableDelegate> {
|
||||||
pub col_fixed: bool,
|
pub col_fixed: bool,
|
||||||
|
|
||||||
pub vertical_scroll_handle: UniformListScrollHandle,
|
pub vertical_scroll_handle: UniformListScrollHandle,
|
||||||
pub vertical_scroll_state: ScrollbarState,
|
|
||||||
pub horizontal_scroll_handle: VirtualListScrollHandle,
|
pub horizontal_scroll_handle: VirtualListScrollHandle,
|
||||||
pub horizontal_scroll_state: ScrollbarState,
|
|
||||||
|
|
||||||
selected_row: Option<usize>,
|
selected_row: Option<usize>,
|
||||||
selection_state: SelectionState,
|
selection_state: SelectionState,
|
||||||
|
|
@ -127,8 +125,6 @@ where
|
||||||
col_groups: Vec::new(),
|
col_groups: Vec::new(),
|
||||||
horizontal_scroll_handle: VirtualListScrollHandle::new(),
|
horizontal_scroll_handle: VirtualListScrollHandle::new(),
|
||||||
vertical_scroll_handle: UniformListScrollHandle::new(),
|
vertical_scroll_handle: UniformListScrollHandle::new(),
|
||||||
vertical_scroll_state: ScrollbarState::default(),
|
|
||||||
horizontal_scroll_state: ScrollbarState::default(),
|
|
||||||
selection_state: SelectionState::Row,
|
selection_state: SelectionState::Row,
|
||||||
selected_row: None,
|
selected_row: None,
|
||||||
right_clicked_row: None,
|
right_clicked_row: None,
|
||||||
|
|
@ -1209,10 +1205,7 @@ where
|
||||||
.right_0()
|
.right_0()
|
||||||
.bottom_0()
|
.bottom_0()
|
||||||
.w(Scrollbar::width())
|
.w(Scrollbar::width())
|
||||||
.child(
|
.child(Scrollbar::vertical(&self.vertical_scroll_handle).max_fps(60)),
|
||||||
Scrollbar::vertical(&self.vertical_scroll_state, &self.vertical_scroll_handle)
|
|
||||||
.max_fps(60),
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1228,10 +1221,7 @@ where
|
||||||
.right_0()
|
.right_0()
|
||||||
.bottom_0()
|
.bottom_0()
|
||||||
.h(Scrollbar::width())
|
.h(Scrollbar::width())
|
||||||
.child(Scrollbar::horizontal(
|
.child(Scrollbar::horizontal(&self.horizontal_scroll_handle))
|
||||||
&self.horizontal_scroll_state,
|
|
||||||
&self.horizontal_scroll_handle,
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,25 +6,24 @@ use std::time::Duration;
|
||||||
|
|
||||||
use gpui::prelude::FluentBuilder;
|
use gpui::prelude::FluentBuilder;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
div, px, AnyElement, App, AppContext, Bounds, ClipboardItem, Context, Element, ElementId,
|
AnyElement, App, AppContext, Bounds, ClipboardItem, Context, Element, ElementId, Entity,
|
||||||
Entity, EntityId, FocusHandle, GlobalElementId, InspectorElementId, InteractiveElement,
|
EntityId, FocusHandle, GlobalElementId, InspectorElementId, InteractiveElement, IntoElement,
|
||||||
IntoElement, KeyBinding, LayoutId, ListState, MouseDownEvent, MouseMoveEvent, MouseUpEvent,
|
KeyBinding, LayoutId, ListState, MouseDownEvent, MouseMoveEvent, MouseUpEvent, ParentElement,
|
||||||
ParentElement, Pixels, Point, RenderOnce, SharedString, Size, StyleRefinement, Styled, Timer,
|
Pixels, Point, RenderOnce, SharedString, Size, StyleRefinement, Styled, Timer, Window, div, px,
|
||||||
Window,
|
|
||||||
};
|
};
|
||||||
use smol::stream::StreamExt;
|
use smol::stream::StreamExt;
|
||||||
|
|
||||||
use crate::highlighter::HighlightTheme;
|
use crate::highlighter::HighlightTheme;
|
||||||
use crate::scroll::{Scrollbar, ScrollbarState};
|
use crate::scroll::Scrollbar;
|
||||||
|
use crate::{ActiveTheme, StyledExt, v_flex};
|
||||||
use crate::{
|
use crate::{
|
||||||
global_state::GlobalState,
|
global_state::GlobalState,
|
||||||
input::{self},
|
input::{self},
|
||||||
text::{
|
text::{
|
||||||
node::{self, NodeContext},
|
|
||||||
TextViewStyle,
|
TextViewStyle,
|
||||||
|
node::{self, NodeContext},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use crate::{v_flex, ActiveTheme, StyledExt};
|
|
||||||
|
|
||||||
const CONTEXT: &'static str = "TextView";
|
const CONTEXT: &'static str = "TextView";
|
||||||
|
|
||||||
|
|
@ -218,7 +217,6 @@ pub(crate) struct TextViewState {
|
||||||
/// Is current in selection.
|
/// Is current in selection.
|
||||||
is_selecting: bool,
|
is_selecting: bool,
|
||||||
is_selectable: bool,
|
is_selectable: bool,
|
||||||
scrollbar_state: ScrollbarState,
|
|
||||||
list_state: ListState,
|
list_state: ListState,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -234,7 +232,6 @@ impl TextViewState {
|
||||||
selection_positions: (None, None),
|
selection_positions: (None, None),
|
||||||
is_selecting: false,
|
is_selecting: false,
|
||||||
is_selectable: false,
|
is_selectable: false,
|
||||||
scrollbar_state: ScrollbarState::default(),
|
|
||||||
list_state: ListState::new(0, gpui::ListAlignment::Top, px(1000.)),
|
list_state: ListState::new(0, gpui::ListAlignment::Top, px(1000.)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -600,7 +597,6 @@ impl Element for TextView {
|
||||||
self.init_state = Some(InitState::Initialized { tx });
|
self.init_state = Some(InitState::Initialized { tx });
|
||||||
}
|
}
|
||||||
|
|
||||||
let scrollbar_state = &self.state.read(cx).scrollbar_state;
|
|
||||||
let list_state = &self.state.read(cx).list_state;
|
let list_state = &self.state.read(cx).list_state;
|
||||||
|
|
||||||
let focus_handle = self
|
let focus_handle = self
|
||||||
|
|
@ -638,7 +634,7 @@ impl Element for TextView {
|
||||||
.top_0()
|
.top_0()
|
||||||
.right_0()
|
.right_0()
|
||||||
.bottom_0()
|
.bottom_0()
|
||||||
.child(Scrollbar::vertical(scrollbar_state, list_state)),
|
.child(Scrollbar::vertical(list_state)),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.into_any_element();
|
.into_any_element();
|
||||||
|
|
@ -800,7 +796,7 @@ fn selection_bounds(
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use gpui::{point, px, size, Bounds};
|
use gpui::{Bounds, point, px, size};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_text_view_state_selection_bounds() {
|
fn test_text_view_state_selection_bounds() {
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,17 @@
|
||||||
use std::{cell::RefCell, ops::Range, rc::Rc};
|
use std::{cell::RefCell, ops::Range, rc::Rc};
|
||||||
|
|
||||||
use gpui::{
|
use gpui::{
|
||||||
div, prelude::FluentBuilder as _, uniform_list, App, Context, ElementId, Entity, FocusHandle,
|
App, Context, ElementId, Entity, FocusHandle, InteractiveElement as _, IntoElement, KeyBinding,
|
||||||
InteractiveElement as _, IntoElement, KeyBinding, ListSizingBehavior, MouseButton,
|
ListSizingBehavior, MouseButton, ParentElement, Render, RenderOnce, SharedString,
|
||||||
ParentElement, Render, RenderOnce, SharedString, StyleRefinement, Styled,
|
StyleRefinement, Styled, UniformListScrollHandle, Window, div, prelude::FluentBuilder as _,
|
||||||
UniformListScrollHandle, Window,
|
uniform_list,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
StyledExt,
|
||||||
actions::{Confirm, SelectDown, SelectLeft, SelectRight, SelectUp},
|
actions::{Confirm, SelectDown, SelectLeft, SelectRight, SelectUp},
|
||||||
list::ListItem,
|
list::ListItem,
|
||||||
scroll::{Scrollbar, ScrollbarState},
|
scroll::Scrollbar,
|
||||||
StyledExt,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const CONTEXT: &str = "Tree";
|
const CONTEXT: &str = "Tree";
|
||||||
|
|
@ -179,7 +179,6 @@ impl TreeItem {
|
||||||
pub struct TreeState {
|
pub struct TreeState {
|
||||||
focus_handle: FocusHandle,
|
focus_handle: FocusHandle,
|
||||||
entries: Vec<TreeEntry>,
|
entries: Vec<TreeEntry>,
|
||||||
scrollbar_state: ScrollbarState,
|
|
||||||
scroll_handle: UniformListScrollHandle,
|
scroll_handle: UniformListScrollHandle,
|
||||||
selected_ix: Option<usize>,
|
selected_ix: Option<usize>,
|
||||||
render_item: Rc<dyn Fn(usize, &TreeEntry, bool, &mut Window, &mut App) -> ListItem>,
|
render_item: Rc<dyn Fn(usize, &TreeEntry, bool, &mut Window, &mut App) -> ListItem>,
|
||||||
|
|
@ -191,7 +190,6 @@ impl TreeState {
|
||||||
Self {
|
Self {
|
||||||
selected_ix: None,
|
selected_ix: None,
|
||||||
focus_handle: cx.focus_handle(),
|
focus_handle: cx.focus_handle(),
|
||||||
scrollbar_state: ScrollbarState::default(),
|
|
||||||
scroll_handle: UniformListScrollHandle::default(),
|
scroll_handle: UniformListScrollHandle::default(),
|
||||||
entries: Vec::new(),
|
entries: Vec::new(),
|
||||||
render_item: Rc::new(|_, _, _, _, _| ListItem::new(0)),
|
render_item: Rc::new(|_, _, _, _, _| ListItem::new(0)),
|
||||||
|
|
@ -395,10 +393,7 @@ impl Render for TreeState {
|
||||||
.right_0()
|
.right_0()
|
||||||
.bottom_0()
|
.bottom_0()
|
||||||
.w(Scrollbar::width())
|
.w(Scrollbar::width())
|
||||||
.child(Scrollbar::vertical(
|
.child(Scrollbar::vertical(&self.scroll_handle)),
|
||||||
&self.scrollbar_state,
|
|
||||||
&self.scroll_handle,
|
|
||||||
)),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
//! Vistual List for render a large number of differently sized rows/columns.
|
//! Virtual List for render a large number of differently sized rows/columns.
|
||||||
//!
|
//!
|
||||||
//! > NOTE: This must ensure each column width or row height.
|
//! > NOTE: This must ensure each column width or row height.
|
||||||
//!
|
//!
|
||||||
|
|
@ -18,15 +18,15 @@ use std::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use gpui::{
|
use gpui::{
|
||||||
div, point, px, size, Along, AnyElement, App, AvailableSpace, Axis, Bounds, ContentMask,
|
Along, AnyElement, App, AvailableSpace, Axis, Bounds, ContentMask, Context,
|
||||||
Context, DeferredScrollToItem, Div, Element, ElementId, Entity, GlobalElementId, Half, Hitbox,
|
DeferredScrollToItem, Div, Element, ElementId, Entity, GlobalElementId, Half, Hitbox,
|
||||||
InteractiveElement, IntoElement, IsZero as _, ListSizingBehavior, Pixels, Point, Render,
|
InteractiveElement, IntoElement, IsZero as _, ListSizingBehavior, Pixels, Point, Render,
|
||||||
ScrollHandle, ScrollStrategy, Size, Stateful, StatefulInteractiveElement, StyleRefinement,
|
ScrollHandle, ScrollStrategy, Size, Stateful, StatefulInteractiveElement, StyleRefinement,
|
||||||
Styled, Window,
|
Styled, Window, div, point, px, size,
|
||||||
};
|
};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
use crate::{scroll::ScrollHandleOffsetable, AxisExt, PixelsExt};
|
use crate::{AxisExt, PixelsExt, scroll::ScrollbarHandle};
|
||||||
|
|
||||||
struct VirtualListScrollHandleState {
|
struct VirtualListScrollHandleState {
|
||||||
axis: Axis,
|
axis: Axis,
|
||||||
|
|
@ -57,7 +57,7 @@ impl AsRef<ScrollHandle> for VirtualListScrollHandle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ScrollHandleOffsetable for VirtualListScrollHandle {
|
impl ScrollbarHandle for VirtualListScrollHandle {
|
||||||
fn offset(&self) -> Point<Pixels> {
|
fn offset(&self) -> Point<Pixels> {
|
||||||
self.base_handle.offset()
|
self.base_handle.offset()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ A comprehensive scrollable container component that provides custom scrollbars,
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
use gpui_component::{
|
use gpui_component::{
|
||||||
scroll::{Scrollable, ScrollbarState, ScrollbarAxis, ScrollbarShow},
|
scroll::{Scrollable, ScrollbarAxis, ScrollbarShow},
|
||||||
StyledExt as _,
|
StyledExt as _,
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
@ -86,10 +86,9 @@ div()
|
||||||
For more control, you can create scrollbars manually:
|
For more control, you can create scrollbars manually:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
use gpui_component::scroll::{Scrollbar, ScrollbarState};
|
use gpui_component::scroll::{Scrollbar};
|
||||||
|
|
||||||
pub struct ScrollableView {
|
pub struct ScrollableView {
|
||||||
scroll_state: ScrollbarState,
|
|
||||||
scroll_handle: ScrollHandle,
|
scroll_handle: ScrollHandle,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -107,7 +106,7 @@ impl Render for ScrollableView {
|
||||||
.child("Your scrollable content")
|
.child("Your scrollable content")
|
||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
Scrollbar::vertical(&self.scroll_state, &self.scroll_handle)
|
Scrollbar::vertical(&self.scroll_handle)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -116,63 +115,11 @@ impl Render for ScrollableView {
|
||||||
### Customizing Scrollbar Behavior
|
### Customizing Scrollbar Behavior
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
Scrollbar::both(&scroll_state, &scroll_handle)
|
Scrollbar::both(&scroll_handle)
|
||||||
.axis(ScrollbarAxis::Vertical)
|
.axis(ScrollbarAxis::Vertical)
|
||||||
.scroll_size(size(px(1000.), px(2000.))) // Custom content size
|
.scroll_size(size(px(1000.), px(2000.))) // Custom content size
|
||||||
```
|
```
|
||||||
|
|
||||||
## Scroll Tracking
|
|
||||||
|
|
||||||
### ScrollbarState Management
|
|
||||||
|
|
||||||
The `ScrollbarState` tracks scrollbar visibility, hover states, and drag interactions:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
use gpui_component::scroll::ScrollbarState;
|
|
||||||
|
|
||||||
pub struct MyView {
|
|
||||||
scroll_state: ScrollbarState,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MyView {
|
|
||||||
fn new(cx: &mut Context<Self>) -> Self {
|
|
||||||
Self {
|
|
||||||
scroll_state: ScrollbarState::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Responding to Scroll Events
|
|
||||||
|
|
||||||
```rust
|
|
||||||
// In your render method
|
|
||||||
div()
|
|
||||||
.on_scroll_wheel(|view, event, _, cx| {
|
|
||||||
// Handle scroll wheel events
|
|
||||||
if event.delta.y != px(0.) {
|
|
||||||
println!("Scrolled vertically: {:?}", event.delta.y);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.scrollable(Axis::Vertical)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Programmatic Scrolling
|
|
||||||
|
|
||||||
```rust
|
|
||||||
// Using ScrollHandle for programmatic control
|
|
||||||
impl MyView {
|
|
||||||
fn scroll_to_top(&mut self) {
|
|
||||||
self.scroll_handle.set_offset(point(px(0.), px(0.)));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn scroll_to_bottom(&mut self) {
|
|
||||||
let max_offset = self.scroll_handle.max_offset();
|
|
||||||
self.scroll_handle.set_offset(point(px(0.), max_offset.y));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Virtualization
|
## Virtualization
|
||||||
|
|
||||||
### VirtualList for Large Datasets
|
### VirtualList for Large Datasets
|
||||||
|
|
@ -287,110 +234,6 @@ Sync scrollbar behavior with system preferences:
|
||||||
Theme::sync_scrollbar_appearance(cx);
|
Theme::sync_scrollbar_appearance(cx);
|
||||||
```
|
```
|
||||||
|
|
||||||
## Advanced Usage
|
|
||||||
|
|
||||||
### ScrollableMask for Custom Scroll Areas
|
|
||||||
|
|
||||||
For advanced scroll control over specific areas:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
use gpui_component::scroll::ScrollableMask;
|
|
||||||
|
|
||||||
ScrollableMask::new(Axis::Vertical, &scroll_handle)
|
|
||||||
.debug() // Show debug borders
|
|
||||||
```
|
|
||||||
|
|
||||||
### Performance Optimization
|
|
||||||
|
|
||||||
For high-performance scrolling with many elements:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
// Limit scroll update frequency
|
|
||||||
Scrollbar::vertical(&state, &handle)
|
|
||||||
.max_fps(60) // Limit to 60 FPS during drag
|
|
||||||
```
|
|
||||||
|
|
||||||
### Nested Scrollable Areas
|
|
||||||
|
|
||||||
```rust
|
|
||||||
v_flex()
|
|
||||||
.size_full()
|
|
||||||
.child(
|
|
||||||
// Outer vertical scroll
|
|
||||||
v_flex()
|
|
||||||
.flex_1()
|
|
||||||
.scrollable(Axis::Vertical)
|
|
||||||
.child(
|
|
||||||
// Inner horizontal scroll
|
|
||||||
h_flex()
|
|
||||||
.w_full()
|
|
||||||
.scrollable(Axis::Horizontal)
|
|
||||||
.child("Nested scrollable content")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
## API Reference
|
|
||||||
|
|
||||||
### Scrollable
|
|
||||||
|
|
||||||
| Method | Description |
|
|
||||||
| -------------------- | ----------------------------- |
|
|
||||||
| `new(axis, element)` | Create scrollable wrapper |
|
|
||||||
| `vertical()` | Set vertical scrolling only |
|
|
||||||
| `horizontal()` | Set horizontal scrolling only |
|
|
||||||
| `set_axis(axis)` | Change scroll axis |
|
|
||||||
|
|
||||||
### ScrollbarAxis
|
|
||||||
|
|
||||||
| Variant | Description |
|
|
||||||
| ------------ | ---------------------------- |
|
|
||||||
| `Vertical` | Vertical scrollbar only |
|
|
||||||
| `Horizontal` | Horizontal scrollbar only |
|
|
||||||
| `Both` | Both vertical and horizontal |
|
|
||||||
|
|
||||||
### ScrollbarState
|
|
||||||
|
|
||||||
| Method | Description |
|
|
||||||
| ----------- | -------------------------- |
|
|
||||||
| `default()` | Create new scrollbar state |
|
|
||||||
|
|
||||||
### Scrollbar
|
|
||||||
|
|
||||||
| Method | Description |
|
|
||||||
| --------------------------- | --------------------------- |
|
|
||||||
| `vertical(state, handle)` | Create vertical scrollbar |
|
|
||||||
| `horizontal(state, handle)` | Create horizontal scrollbar |
|
|
||||||
| `both(state, handle)` | Create both scrollbars |
|
|
||||||
| `axis(axis)` | Set scrollbar axis |
|
|
||||||
| `scroll_size(size)` | Set custom content size |
|
|
||||||
|
|
||||||
### VirtualListScrollHandle
|
|
||||||
|
|
||||||
| Method | Description |
|
|
||||||
| --------------------------------- | ------------------------- |
|
|
||||||
| `new()` | Create new handle |
|
|
||||||
| `scroll_to_item(index, strategy)` | Scroll to specific item |
|
|
||||||
| `offset()` | Get current scroll offset |
|
|
||||||
| `set_offset(point)` | Set scroll position |
|
|
||||||
| `content_size()` | Get total content size |
|
|
||||||
|
|
||||||
### ScrollStrategy
|
|
||||||
|
|
||||||
| Variant | Description |
|
|
||||||
| -------- | -------------------- |
|
|
||||||
| `Top` | Align item to top |
|
|
||||||
| `Center` | Center item in view |
|
|
||||||
| `Bottom` | Align item to bottom |
|
|
||||||
|
|
||||||
### ScrollbarShow
|
|
||||||
|
|
||||||
| Variant | Description |
|
|
||||||
| ----------- | ----------------------- |
|
|
||||||
| `Scrolling` | Show only during scroll |
|
|
||||||
| `Hover` | Show on hover |
|
|
||||||
| `Always` | Always visible |
|
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
### File Browser with Scrolling
|
### File Browser with Scrolling
|
||||||
|
|
@ -398,7 +241,6 @@ v_flex()
|
||||||
```rust
|
```rust
|
||||||
pub struct FileBrowser {
|
pub struct FileBrowser {
|
||||||
files: Vec<String>,
|
files: Vec<String>,
|
||||||
scroll_state: ScrollbarState,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Render for FileBrowser {
|
impl Render for FileBrowser {
|
||||||
|
|
@ -482,19 +324,3 @@ impl Render for DataTable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Performance Tips
|
|
||||||
|
|
||||||
1. **Use Virtualization**: For lists with >100 items, use `VirtualList`
|
|
||||||
2. **Limit Scroll Updates**: Use `max_fps()` for heavy content during drag
|
|
||||||
3. **Optimize Render**: Avoid complex rendering in scroll event handlers
|
|
||||||
4. **Batch Updates**: Group multiple scroll changes together
|
|
||||||
5. **Memory Management**: Virtual lists only render visible items
|
|
||||||
|
|
||||||
## Best Practices
|
|
||||||
|
|
||||||
1. **Consistent Behavior**: Keep scroll behavior consistent across your app
|
|
||||||
2. **Visual Feedback**: Provide clear scroll indicators for long content
|
|
||||||
3. **Responsive Design**: Ensure scrolling works well on different screen sizes
|
|
||||||
4. **Error Handling**: Handle edge cases like empty content gracefully
|
|
||||||
5. **Testing**: Test with various content sizes and screen readers
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue