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 gpui::{
|
||||
div, px, size, App, AppContext, Axis, Context, Entity, FocusHandle, Focusable,
|
||||
InteractiveElement, IntoElement, ParentElement, Pixels, Render, Size, Styled, Window,
|
||||
App, AppContext, Axis, Context, Entity, FocusHandle, Focusable, InteractiveElement,
|
||||
IntoElement, ParentElement, Pixels, Render, Size, Styled, Window, div, px, size,
|
||||
};
|
||||
use gpui_component::{
|
||||
ActiveTheme as _, Selectable, StyledExt as _,
|
||||
button::{Button, ButtonGroup},
|
||||
h_flex,
|
||||
scroll::ScrollbarState,
|
||||
v_flex, ActiveTheme as _, Selectable, StyledExt as _,
|
||||
h_flex, v_flex,
|
||||
};
|
||||
|
||||
pub struct ScrollableStory {
|
||||
focus_handle: FocusHandle,
|
||||
scroll_state: ScrollbarState,
|
||||
items: Vec<String>,
|
||||
item_sizes: Rc<Vec<Size<Pixels>>>,
|
||||
test_width: Pixels,
|
||||
|
|
@ -33,7 +31,6 @@ impl ScrollableStory {
|
|||
|
||||
Self {
|
||||
focus_handle: cx.focus_handle(),
|
||||
scroll_state: ScrollbarState::default(),
|
||||
items,
|
||||
item_sizes: Rc::new(item_sizes),
|
||||
test_width,
|
||||
|
|
@ -69,7 +66,6 @@ impl ScrollableStory {
|
|||
.map(|_| size(self.test_width, ITEM_HEIGHT))
|
||||
.collect::<Vec<_>>()
|
||||
.into();
|
||||
self.scroll_state = ScrollbarState::default();
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,14 +9,13 @@ use gpui_component::{
|
|||
button::{Button, ButtonGroup},
|
||||
divider::Divider,
|
||||
h_flex,
|
||||
scroll::{Scrollbar, ScrollbarAxis, ScrollbarState},
|
||||
scroll::{Scrollbar, ScrollbarAxis},
|
||||
v_flex, v_virtual_list,
|
||||
};
|
||||
|
||||
pub struct VirtualListStory {
|
||||
focus_handle: FocusHandle,
|
||||
scroll_handle: VirtualListScrollHandle,
|
||||
scroll_state: ScrollbarState,
|
||||
items: Vec<String>,
|
||||
item_sizes: Rc<Vec<Size<Pixels>>>,
|
||||
columns_count: usize,
|
||||
|
|
@ -35,7 +34,6 @@ impl VirtualListStory {
|
|||
Self {
|
||||
focus_handle: cx.focus_handle(),
|
||||
scroll_handle: VirtualListScrollHandle::new(),
|
||||
scroll_state: ScrollbarState::default(),
|
||||
items,
|
||||
item_sizes: Rc::new(item_sizes),
|
||||
columns_count: 100,
|
||||
|
|
@ -68,8 +66,6 @@ impl VirtualListStory {
|
|||
}
|
||||
|
||||
self.item_sizes = Rc::new(self.items.iter().map(|_| ITEM_SIZE).collect());
|
||||
|
||||
self.scroll_state = ScrollbarState::default();
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
|
|
@ -293,10 +289,7 @@ impl Render for VirtualListStory {
|
|||
.left_0()
|
||||
.right_0()
|
||||
.bottom_0()
|
||||
.child(
|
||||
Scrollbar::both(&self.scroll_state, &self.scroll_handle)
|
||||
.axis(self.axis),
|
||||
)
|
||||
.child(Scrollbar::new(&self.scroll_handle).axis(self.axis))
|
||||
}),
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ use std::{
|
|||
use crate::{
|
||||
ActiveTheme, Icon, IconName, h_flex,
|
||||
history::{History, HistoryItem},
|
||||
scroll::{Scrollbar, ScrollbarShow, ScrollbarState},
|
||||
scroll::{Scrollbar, ScrollbarShow},
|
||||
v_flex,
|
||||
};
|
||||
|
||||
|
|
@ -139,7 +139,6 @@ pub struct Tiles {
|
|||
resizing_drag_data: Option<ResizeDrag>,
|
||||
bounds: Bounds<Pixels>,
|
||||
history: History<TileChange>,
|
||||
scroll_state: ScrollbarState,
|
||||
scroll_handle: ScrollHandle,
|
||||
scrollbar_show: Option<ScrollbarShow>,
|
||||
}
|
||||
|
|
@ -195,7 +194,6 @@ impl Tiles {
|
|||
resizing_drag_data: None,
|
||||
bounds: Bounds::default(),
|
||||
history: History::new().group_interval(std::time::Duration::from_millis(100)),
|
||||
scroll_state: ScrollbarState::default(),
|
||||
scroll_handle: ScrollHandle::default(),
|
||||
}
|
||||
}
|
||||
|
|
@ -1220,7 +1218,7 @@ impl Render for Tiles {
|
|||
.right_0()
|
||||
.bottom_0()
|
||||
.child(
|
||||
Scrollbar::both(&self.scroll_state, &self.scroll_handle)
|
||||
Scrollbar::new(&self.scroll_handle)
|
||||
.scroll_size(scroll_size)
|
||||
.when_some(self.scrollbar_show, |this, scrollbar_show| {
|
||||
this.scrollbar_show(scrollbar_show)
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
use gpui::prelude::FluentBuilder as _;
|
||||
use gpui::{
|
||||
div, px, relative, AnyElement, App, DefiniteLength, Edges, EdgesRefinement, Entity,
|
||||
InteractiveElement as _, IntoElement, IsZero, MouseButton, ParentElement as _, Pixels, Rems,
|
||||
RenderOnce, StyleRefinement, Styled, Window,
|
||||
AnyElement, App, DefiniteLength, Edges, EdgesRefinement, Entity, InteractiveElement as _,
|
||||
IntoElement, IsZero, MouseButton, ParentElement as _, Pixels, Rems, RenderOnce,
|
||||
StyleRefinement, Styled, Window, div, px, relative,
|
||||
};
|
||||
|
||||
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::scroll::Scrollbar;
|
||||
use crate::spinner::Spinner;
|
||||
use crate::{h_flex, Selectable, StyledExt};
|
||||
use crate::{v_flex, ActiveTheme};
|
||||
use crate::{ActiveTheme, v_flex};
|
||||
use crate::{IconName, Size};
|
||||
use crate::{Selectable, StyledExt, h_flex};
|
||||
use crate::{Sizable, StyleSized};
|
||||
|
||||
use super::InputState;
|
||||
|
|
@ -213,9 +213,9 @@ impl Input {
|
|||
};
|
||||
|
||||
let scrollbar = if !state.soft_wrap {
|
||||
Scrollbar::both(&state.scroll_state, &state.scroll_handle)
|
||||
Scrollbar::new(&state.scroll_handle)
|
||||
} else {
|
||||
Scrollbar::vertical(&state.scroll_state, &state.scroll_handle)
|
||||
Scrollbar::vertical(&state.scroll_handle)
|
||||
};
|
||||
|
||||
this.relative().child(
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ use crate::input::{
|
|||
text_wrapper::LineLayout,
|
||||
};
|
||||
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};
|
||||
|
||||
#[derive(Action, Clone, PartialEq, Eq, Deserialize)]
|
||||
|
|
@ -295,7 +295,6 @@ pub struct InputState {
|
|||
pub(crate) scroll_handle: ScrollHandle,
|
||||
/// The deferred scroll offset to apply on next layout.
|
||||
pub(crate) deferred_scroll_offset: Option<Point<Pixels>>,
|
||||
pub(super) scroll_state: ScrollbarState,
|
||||
/// The size of the scrollable content.
|
||||
pub(crate) scroll_size: gpui::Size<Pixels>,
|
||||
|
||||
|
|
@ -396,7 +395,6 @@ impl InputState {
|
|||
last_selected_range: None,
|
||||
last_cursor: None,
|
||||
scroll_handle: ScrollHandle::new(),
|
||||
scroll_state: ScrollbarState::default(),
|
||||
scroll_size: gpui::size(px(0.), px(0.)),
|
||||
deferred_scroll_offset: None,
|
||||
preferred_column: None,
|
||||
|
|
|
|||
|
|
@ -5,20 +5,21 @@ use crate::actions::{Cancel, Confirm, SelectDown, SelectUp};
|
|||
use crate::input::InputState;
|
||||
use crate::list::cache::{MeasuredEntrySize, RowEntry, RowsCache};
|
||||
use crate::{
|
||||
ActiveTheme, IconName, Size,
|
||||
input::{Input, InputEvent},
|
||||
scroll::{Scrollbar, ScrollbarState},
|
||||
v_flex, ActiveTheme, IconName, Size,
|
||||
scroll::Scrollbar,
|
||||
v_flex,
|
||||
};
|
||||
use crate::{list::ListDelegate, v_virtual_list, VirtualListScrollHandle};
|
||||
use crate::{Icon, IndexPath, Selectable, Sizable, StyledExt};
|
||||
use crate::{VirtualListScrollHandle, list::ListDelegate, v_virtual_list};
|
||||
use gpui::{
|
||||
div, prelude::FluentBuilder, AppContext, Entity, FocusHandle, Focusable, InteractiveElement,
|
||||
IntoElement, KeyBinding, Length, MouseButton, ParentElement, Render, Styled, Task, Window,
|
||||
App, AvailableSpace, ClickEvent, Context, DefiniteLength, EdgesRefinement, EventEmitter,
|
||||
ListSizingBehavior, RenderOnce, ScrollStrategy, SharedString, StatefulInteractiveElement,
|
||||
StyleRefinement, Subscription, px, size,
|
||||
};
|
||||
use gpui::{
|
||||
px, size, App, AvailableSpace, ClickEvent, Context, DefiniteLength, EdgesRefinement,
|
||||
EventEmitter, ListSizingBehavior, RenderOnce, ScrollStrategy, SharedString,
|
||||
StatefulInteractiveElement, StyleRefinement, Subscription,
|
||||
AppContext, Entity, FocusHandle, Focusable, InteractiveElement, IntoElement, KeyBinding,
|
||||
Length, MouseButton, ParentElement, Render, Styled, Task, Window, div, prelude::FluentBuilder,
|
||||
};
|
||||
use rust_i18n::t;
|
||||
use smol::Timer;
|
||||
|
|
@ -72,7 +73,6 @@ pub struct ListState<D: ListDelegate> {
|
|||
delegate: D,
|
||||
last_query: Option<String>,
|
||||
scroll_handle: VirtualListScrollHandle,
|
||||
scroll_state: ScrollbarState,
|
||||
rows_cache: RowsCache,
|
||||
selected_index: Option<IndexPath>,
|
||||
item_to_measure_index: IndexPath,
|
||||
|
|
@ -111,7 +111,6 @@ where
|
|||
deferred_scroll_to_index: None,
|
||||
mouse_right_clicked_index: None,
|
||||
scroll_handle: VirtualListScrollHandle::new(),
|
||||
scroll_state: ScrollbarState::default(),
|
||||
reset_on_cancel: true,
|
||||
_search_task: Task::ready(()),
|
||||
_load_more_task: Task::ready(()),
|
||||
|
|
@ -484,7 +483,6 @@ where
|
|||
let rows_cache = self.rows_cache.clone();
|
||||
let scrollbar_visible = self.options.scrollbar_visible;
|
||||
let scroll_handle = self.scroll_handle.clone();
|
||||
let scroll_state = self.scroll_state.clone();
|
||||
let measured_size = rows_cache.measured_size();
|
||||
|
||||
v_flex()
|
||||
|
|
@ -550,7 +548,7 @@ where
|
|||
}
|
||||
})
|
||||
.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::{SelectLeft, SelectRight};
|
||||
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::{Side, Size, StyledExt, kbd::Kbd};
|
||||
use gpui::{
|
||||
|
|
@ -287,7 +287,6 @@ pub struct PopupMenu {
|
|||
scrollable: bool,
|
||||
external_link_icon: bool,
|
||||
scroll_handle: ScrollHandle,
|
||||
scroll_state: ScrollbarState,
|
||||
// This will update on render
|
||||
submenu_anchor: (Corner, Pixels),
|
||||
|
||||
|
|
@ -309,7 +308,6 @@ impl PopupMenu {
|
|||
bounds: Bounds::default(),
|
||||
scrollable: false,
|
||||
scroll_handle: ScrollHandle::default(),
|
||||
scroll_state: ScrollbarState::default(),
|
||||
external_link_icon: true,
|
||||
size: Size::default(),
|
||||
submenu_anchor: (Corner::TopLeft, Pixels::ZERO),
|
||||
|
|
@ -1318,7 +1316,7 @@ impl Render for PopupMenu {
|
|||
.left_0()
|
||||
.right_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::{
|
||||
div, relative, AnyElement, App, Bounds, Div, Element, ElementId, GlobalElementId,
|
||||
InspectorElementId, InteractiveElement, Interactivity, IntoElement, LayoutId, ParentElement,
|
||||
Pixels, Position, ScrollHandle, SharedString, Stateful, StatefulInteractiveElement, Style,
|
||||
StyleRefinement, Styled, Window,
|
||||
AnyElement, App, Bounds, Div, Element, ElementId, GlobalElementId, InspectorElementId,
|
||||
InteractiveElement, Interactivity, IntoElement, LayoutId, ParentElement, Pixels, Position,
|
||||
ScrollHandle, SharedString, Stateful, StatefulInteractiveElement, Style, StyleRefinement,
|
||||
Styled, Window, div, relative,
|
||||
};
|
||||
|
||||
/// 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)]
|
||||
pub struct ScrollViewState {
|
||||
state: ScrollbarState,
|
||||
handle: ScrollHandle,
|
||||
}
|
||||
|
||||
|
|
@ -77,7 +76,6 @@ impl Default for ScrollViewState {
|
|||
fn default() -> Self {
|
||||
Self {
|
||||
handle: ScrollHandle::new(),
|
||||
state: ScrollbarState::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -184,9 +182,7 @@ where
|
|||
.left_0()
|
||||
.right_0()
|
||||
.bottom_0()
|
||||
.child(
|
||||
Scrollbar::both(&element_state.state, &element_state.handle).axis(axis),
|
||||
),
|
||||
.child(Scrollbar::new(&element_state.handle).axis(axis)),
|
||||
)
|
||||
.into_any_element();
|
||||
|
||||
|
|
|
|||
|
|
@ -1,17 +1,18 @@
|
|||
use std::{
|
||||
cell::Cell,
|
||||
ops::Deref,
|
||||
panic::Location,
|
||||
rc::Rc,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
use crate::{ActiveTheme, AxisExt};
|
||||
use gpui::{
|
||||
fill, point, px, relative, size, App, Axis, BorderStyle, Bounds, ContentMask, Corner,
|
||||
CursorStyle, Edges, Element, GlobalElementId, Hitbox, HitboxBehavior, Hsla, InspectorElementId,
|
||||
IntoElement, IsZero, LayoutId, ListState, MouseDownEvent, MouseMoveEvent, MouseUpEvent,
|
||||
PaintQuad, Pixels, Point, Position, ScrollHandle, ScrollWheelEvent, Size, Style, Timer,
|
||||
UniformListScrollHandle, Window,
|
||||
App, Axis, BorderStyle, Bounds, ContentMask, Corner, CursorStyle, Edges, Element, ElementId,
|
||||
GlobalElementId, Hitbox, HitboxBehavior, Hsla, InspectorElementId, IntoElement, IsZero,
|
||||
LayoutId, ListState, MouseDownEvent, MouseMoveEvent, MouseUpEvent, PaintQuad, Pixels, Point,
|
||||
Position, ScrollHandle, ScrollWheelEvent, Size, Style, Timer, UniformListScrollHandle, Window,
|
||||
fill, point, px, relative, size,
|
||||
};
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
|
@ -54,7 +55,7 @@ impl ScrollbarShow {
|
|||
}
|
||||
|
||||
/// 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.
|
||||
fn offset(&self) -> Point<Pixels>;
|
||||
/// Set the offset of the scroll handle.
|
||||
|
|
@ -67,7 +68,7 @@ pub trait ScrollHandleOffsetable {
|
|||
fn end_drag(&self) {}
|
||||
}
|
||||
|
||||
impl ScrollHandleOffsetable for ScrollHandle {
|
||||
impl ScrollbarHandle for ScrollHandle {
|
||||
fn offset(&self) -> Point<Pixels> {
|
||||
self.offset()
|
||||
}
|
||||
|
|
@ -81,7 +82,7 @@ impl ScrollHandleOffsetable for ScrollHandle {
|
|||
}
|
||||
}
|
||||
|
||||
impl ScrollHandleOffsetable for UniformListScrollHandle {
|
||||
impl ScrollbarHandle for UniformListScrollHandle {
|
||||
fn offset(&self) -> Point<Pixels> {
|
||||
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> {
|
||||
self.scroll_px_offset_for_scrollbar()
|
||||
}
|
||||
|
|
@ -120,11 +121,11 @@ impl ScrollHandleOffsetable for ListState {
|
|||
|
||||
#[doc(hidden)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ScrollbarState(Rc<Cell<ScrollbarStateInner>>);
|
||||
struct ScrollbarState(Rc<Cell<ScrollbarStateInner>>);
|
||||
|
||||
#[doc(hidden)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct ScrollbarStateInner {
|
||||
struct ScrollbarStateInner {
|
||||
hovered_axis: Option<Axis>,
|
||||
hovered_on_thumb: Option<Axis>,
|
||||
dragged_axis: Option<Axis>,
|
||||
|
|
@ -307,10 +308,10 @@ impl ScrollbarAxis {
|
|||
|
||||
/// Scrollbar control for scroll-area or a uniform-list.
|
||||
pub struct Scrollbar {
|
||||
pub(crate) id: ElementId,
|
||||
axis: ScrollbarAxis,
|
||||
scrollbar_show: Option<ScrollbarShow>,
|
||||
scroll_handle: Rc<dyn ScrollHandleOffsetable>,
|
||||
state: ScrollbarState,
|
||||
scroll_handle: Rc<dyn ScrollbarHandle>,
|
||||
scroll_size: Option<Size<Pixels>>,
|
||||
/// Maximum frames per second for scrolling by drag. Default is 120 FPS.
|
||||
///
|
||||
|
|
@ -320,14 +321,15 @@ pub struct Scrollbar {
|
|||
}
|
||||
|
||||
impl Scrollbar {
|
||||
fn new(
|
||||
axis: impl Into<ScrollbarAxis>,
|
||||
state: &ScrollbarState,
|
||||
scroll_handle: &(impl ScrollHandleOffsetable + Clone + 'static),
|
||||
) -> Self {
|
||||
/// Create a new scrollbar.
|
||||
///
|
||||
/// This will have both vertical and horizontal scrollbars.
|
||||
#[track_caller]
|
||||
pub fn new<H: ScrollbarHandle + Clone>(scroll_handle: &H) -> Self {
|
||||
let caller = Location::caller();
|
||||
Self {
|
||||
state: state.clone(),
|
||||
axis: axis.into(),
|
||||
id: ElementId::CodeLocation(*caller),
|
||||
axis: ScrollbarAxis::Both,
|
||||
scrollbar_show: None,
|
||||
scroll_handle: Rc::new(scroll_handle.clone()),
|
||||
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.
|
||||
pub fn horizontal(
|
||||
state: &ScrollbarState,
|
||||
scroll_handle: &(impl ScrollHandleOffsetable + Clone + 'static),
|
||||
) -> Self {
|
||||
Self::new(ScrollbarAxis::Horizontal, state, scroll_handle)
|
||||
#[track_caller]
|
||||
pub fn horizontal<H: ScrollbarHandle + Clone>(scroll_handle: &H) -> Self {
|
||||
Self::new(scroll_handle).axis(ScrollbarAxis::Horizontal)
|
||||
}
|
||||
|
||||
/// Create with vertical scrollbar.
|
||||
pub fn vertical(
|
||||
state: &ScrollbarState,
|
||||
scroll_handle: &(impl ScrollHandleOffsetable + Clone + 'static),
|
||||
) -> Self {
|
||||
Self::new(ScrollbarAxis::Vertical, state, scroll_handle)
|
||||
#[track_caller]
|
||||
pub fn vertical<H: ScrollbarHandle + Clone>(scroll_handle: &H) -> Self {
|
||||
Self::new(scroll_handle).axis(ScrollbarAxis::Vertical)
|
||||
}
|
||||
|
||||
/// 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`.
|
||||
|
|
@ -473,6 +471,7 @@ impl IntoElement for Scrollbar {
|
|||
#[doc(hidden)]
|
||||
pub struct PrepaintState {
|
||||
hitbox: Hitbox,
|
||||
scrollbar_state: ScrollbarState,
|
||||
states: Vec<AxisPrepaintState>,
|
||||
}
|
||||
|
||||
|
|
@ -499,7 +498,7 @@ impl Element for Scrollbar {
|
|||
type PrepaintState = PrepaintState;
|
||||
|
||||
fn id(&self) -> Option<gpui::ElementId> {
|
||||
None
|
||||
Some(self.id.clone())
|
||||
}
|
||||
|
||||
fn source_location(&self) -> Option<&'static std::panic::Location<'static>> {
|
||||
|
|
@ -536,6 +535,11 @@ impl Element for Scrollbar {
|
|||
window.insert_hitbox(bounds, HitboxBehavior::Normal)
|
||||
});
|
||||
|
||||
let state = window
|
||||
.use_state(cx, |_, _| ScrollbarState::default())
|
||||
.read(cx)
|
||||
.clone();
|
||||
|
||||
let mut states = vec![];
|
||||
let mut has_both = self.axis.is_both();
|
||||
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 state = self.state.clone();
|
||||
let is_always_to_show = scrollbar_show.is_always();
|
||||
let is_hover_to_show = scrollbar_show.is_hover();
|
||||
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(
|
||||
|
|
@ -729,16 +736,17 @@ impl Element for Scrollbar {
|
|||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) {
|
||||
let scrollbar_state = &prepaint.scrollbar_state;
|
||||
let scrollbar_show = self.scrollbar_show.unwrap_or(cx.theme().scrollbar_show);
|
||||
let view_id = window.current_view();
|
||||
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();
|
||||
|
||||
// Update last_scroll_time when offset is changed.
|
||||
if self.scroll_handle.offset() != self.state.get().last_scroll_offset {
|
||||
self.state.set(
|
||||
self.state
|
||||
if self.scroll_handle.offset() != scrollbar_state.get().last_scroll_offset {
|
||||
scrollbar_state.set(
|
||||
scrollbar_state
|
||||
.get()
|
||||
.with_last_scroll(self.scroll_handle.offset(), Some(Instant::now())),
|
||||
);
|
||||
|
|
@ -798,7 +806,7 @@ impl Element for Scrollbar {
|
|||
});
|
||||
|
||||
window.on_mouse_event({
|
||||
let state = self.state.clone();
|
||||
let state = scrollbar_state.clone();
|
||||
let scroll_handle = self.scroll_handle.clone();
|
||||
|
||||
move |event: &ScrollWheelEvent, phase, _, cx| {
|
||||
|
|
@ -818,7 +826,7 @@ impl Element for Scrollbar {
|
|||
|
||||
if is_hover_to_show || is_visible {
|
||||
window.on_mouse_event({
|
||||
let state = self.state.clone();
|
||||
let state = scrollbar_state.clone();
|
||||
let scroll_handle = self.scroll_handle.clone();
|
||||
|
||||
move |event: &MouseDownEvent, phase, _, cx| {
|
||||
|
|
@ -867,7 +875,7 @@ impl Element for Scrollbar {
|
|||
|
||||
window.on_mouse_event({
|
||||
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);
|
||||
|
||||
move |event: &MouseMoveEvent, _, _, cx| {
|
||||
|
|
@ -955,8 +963,8 @@ impl Element for Scrollbar {
|
|||
});
|
||||
|
||||
window.on_mouse_event({
|
||||
let state = scrollbar_state.clone();
|
||||
let scroll_handle = self.scroll_handle.clone();
|
||||
let state = self.state.clone();
|
||||
|
||||
move |_event: &MouseUpEvent, phase, _, cx| {
|
||||
if phase.bubble() {
|
||||
|
|
|
|||
|
|
@ -1,16 +1,17 @@
|
|||
use gpui::{
|
||||
div, list, prelude::FluentBuilder as _, px, App, Entity, InteractiveElement as _, IntoElement,
|
||||
ListAlignment, ListState, ParentElement as _, SharedString, Styled, Window,
|
||||
App, Entity, InteractiveElement as _, IntoElement, ListAlignment, ListState,
|
||||
ParentElement as _, SharedString, Styled, Window, div, list, prelude::FluentBuilder as _, px,
|
||||
};
|
||||
use rust_i18n::t;
|
||||
|
||||
use crate::{
|
||||
ActiveTheme, IconName, Sizable,
|
||||
button::{Button, ButtonVariants},
|
||||
h_flex,
|
||||
label::Label,
|
||||
scroll::{Scrollbar, ScrollbarState},
|
||||
setting::{settings::SettingsState, RenderOptions, SettingGroup},
|
||||
v_flex, ActiveTheme, IconName, Sizable,
|
||||
scroll::Scrollbar,
|
||||
setting::{RenderOptions, SettingGroup, settings::SettingsState},
|
||||
v_flex,
|
||||
};
|
||||
|
||||
/// A setting page that can contain multiple setting groups.
|
||||
|
|
@ -100,16 +101,11 @@ impl SettingPage {
|
|||
.collect::<Vec<_>>();
|
||||
let groups_count = groups.len();
|
||||
|
||||
let (scroll_state, list_state) = window
|
||||
let list_state = window
|
||||
.use_keyed_state(
|
||||
SharedString::from(format!("list-state:{}", ix)),
|
||||
cx,
|
||||
|_, _| {
|
||||
(
|
||||
ScrollbarState::default(),
|
||||
ListState::new(groups_count, ListAlignment::Top, px(100.)),
|
||||
)
|
||||
},
|
||||
|_, _| ListState::new(groups_count, ListAlignment::Top, px(100.)),
|
||||
)
|
||||
.read(cx)
|
||||
.clone();
|
||||
|
|
@ -188,7 +184,7 @@ impl SettingPage {
|
|||
.left_0()
|
||||
.right_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},
|
||||
h_flex,
|
||||
menu::{ContextMenuExt, PopupMenu},
|
||||
scroll::{ScrollableMask, Scrollbar, ScrollbarState},
|
||||
scroll::{ScrollableMask, Scrollbar},
|
||||
v_flex,
|
||||
};
|
||||
use gpui::{
|
||||
|
|
@ -95,9 +95,7 @@ pub struct TableState<D: TableDelegate> {
|
|||
pub col_fixed: bool,
|
||||
|
||||
pub vertical_scroll_handle: UniformListScrollHandle,
|
||||
pub vertical_scroll_state: ScrollbarState,
|
||||
pub horizontal_scroll_handle: VirtualListScrollHandle,
|
||||
pub horizontal_scroll_state: ScrollbarState,
|
||||
|
||||
selected_row: Option<usize>,
|
||||
selection_state: SelectionState,
|
||||
|
|
@ -127,8 +125,6 @@ where
|
|||
col_groups: Vec::new(),
|
||||
horizontal_scroll_handle: VirtualListScrollHandle::new(),
|
||||
vertical_scroll_handle: UniformListScrollHandle::new(),
|
||||
vertical_scroll_state: ScrollbarState::default(),
|
||||
horizontal_scroll_state: ScrollbarState::default(),
|
||||
selection_state: SelectionState::Row,
|
||||
selected_row: None,
|
||||
right_clicked_row: None,
|
||||
|
|
@ -1209,10 +1205,7 @@ where
|
|||
.right_0()
|
||||
.bottom_0()
|
||||
.w(Scrollbar::width())
|
||||
.child(
|
||||
Scrollbar::vertical(&self.vertical_scroll_state, &self.vertical_scroll_handle)
|
||||
.max_fps(60),
|
||||
),
|
||||
.child(Scrollbar::vertical(&self.vertical_scroll_handle).max_fps(60)),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -1228,10 +1221,7 @@ where
|
|||
.right_0()
|
||||
.bottom_0()
|
||||
.h(Scrollbar::width())
|
||||
.child(Scrollbar::horizontal(
|
||||
&self.horizontal_scroll_state,
|
||||
&self.horizontal_scroll_handle,
|
||||
))
|
||||
.child(Scrollbar::horizontal(&self.horizontal_scroll_handle))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,25 +6,24 @@ use std::time::Duration;
|
|||
|
||||
use gpui::prelude::FluentBuilder;
|
||||
use gpui::{
|
||||
div, px, AnyElement, App, AppContext, Bounds, ClipboardItem, Context, Element, ElementId,
|
||||
Entity, EntityId, FocusHandle, GlobalElementId, InspectorElementId, InteractiveElement,
|
||||
IntoElement, KeyBinding, LayoutId, ListState, MouseDownEvent, MouseMoveEvent, MouseUpEvent,
|
||||
ParentElement, Pixels, Point, RenderOnce, SharedString, Size, StyleRefinement, Styled, Timer,
|
||||
Window,
|
||||
AnyElement, App, AppContext, Bounds, ClipboardItem, Context, Element, ElementId, Entity,
|
||||
EntityId, FocusHandle, GlobalElementId, InspectorElementId, InteractiveElement, IntoElement,
|
||||
KeyBinding, LayoutId, ListState, MouseDownEvent, MouseMoveEvent, MouseUpEvent, ParentElement,
|
||||
Pixels, Point, RenderOnce, SharedString, Size, StyleRefinement, Styled, Timer, Window, div, px,
|
||||
};
|
||||
use smol::stream::StreamExt;
|
||||
|
||||
use crate::highlighter::HighlightTheme;
|
||||
use crate::scroll::{Scrollbar, ScrollbarState};
|
||||
use crate::scroll::Scrollbar;
|
||||
use crate::{ActiveTheme, StyledExt, v_flex};
|
||||
use crate::{
|
||||
global_state::GlobalState,
|
||||
input::{self},
|
||||
text::{
|
||||
node::{self, NodeContext},
|
||||
TextViewStyle,
|
||||
node::{self, NodeContext},
|
||||
},
|
||||
};
|
||||
use crate::{v_flex, ActiveTheme, StyledExt};
|
||||
|
||||
const CONTEXT: &'static str = "TextView";
|
||||
|
||||
|
|
@ -218,7 +217,6 @@ pub(crate) struct TextViewState {
|
|||
/// Is current in selection.
|
||||
is_selecting: bool,
|
||||
is_selectable: bool,
|
||||
scrollbar_state: ScrollbarState,
|
||||
list_state: ListState,
|
||||
}
|
||||
|
||||
|
|
@ -234,7 +232,6 @@ impl TextViewState {
|
|||
selection_positions: (None, None),
|
||||
is_selecting: false,
|
||||
is_selectable: false,
|
||||
scrollbar_state: ScrollbarState::default(),
|
||||
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 });
|
||||
}
|
||||
|
||||
let scrollbar_state = &self.state.read(cx).scrollbar_state;
|
||||
let list_state = &self.state.read(cx).list_state;
|
||||
|
||||
let focus_handle = self
|
||||
|
|
@ -638,7 +634,7 @@ impl Element for TextView {
|
|||
.top_0()
|
||||
.right_0()
|
||||
.bottom_0()
|
||||
.child(Scrollbar::vertical(scrollbar_state, list_state)),
|
||||
.child(Scrollbar::vertical(list_state)),
|
||||
)
|
||||
})
|
||||
.into_any_element();
|
||||
|
|
@ -800,7 +796,7 @@ fn selection_bounds(
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use gpui::{point, px, size, Bounds};
|
||||
use gpui::{Bounds, point, px, size};
|
||||
|
||||
#[test]
|
||||
fn test_text_view_state_selection_bounds() {
|
||||
|
|
|
|||
|
|
@ -1,17 +1,17 @@
|
|||
use std::{cell::RefCell, ops::Range, rc::Rc};
|
||||
|
||||
use gpui::{
|
||||
div, prelude::FluentBuilder as _, uniform_list, App, Context, ElementId, Entity, FocusHandle,
|
||||
InteractiveElement as _, IntoElement, KeyBinding, ListSizingBehavior, MouseButton,
|
||||
ParentElement, Render, RenderOnce, SharedString, StyleRefinement, Styled,
|
||||
UniformListScrollHandle, Window,
|
||||
App, Context, ElementId, Entity, FocusHandle, InteractiveElement as _, IntoElement, KeyBinding,
|
||||
ListSizingBehavior, MouseButton, ParentElement, Render, RenderOnce, SharedString,
|
||||
StyleRefinement, Styled, UniformListScrollHandle, Window, div, prelude::FluentBuilder as _,
|
||||
uniform_list,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
StyledExt,
|
||||
actions::{Confirm, SelectDown, SelectLeft, SelectRight, SelectUp},
|
||||
list::ListItem,
|
||||
scroll::{Scrollbar, ScrollbarState},
|
||||
StyledExt,
|
||||
scroll::Scrollbar,
|
||||
};
|
||||
|
||||
const CONTEXT: &str = "Tree";
|
||||
|
|
@ -179,7 +179,6 @@ impl TreeItem {
|
|||
pub struct TreeState {
|
||||
focus_handle: FocusHandle,
|
||||
entries: Vec<TreeEntry>,
|
||||
scrollbar_state: ScrollbarState,
|
||||
scroll_handle: UniformListScrollHandle,
|
||||
selected_ix: Option<usize>,
|
||||
render_item: Rc<dyn Fn(usize, &TreeEntry, bool, &mut Window, &mut App) -> ListItem>,
|
||||
|
|
@ -191,7 +190,6 @@ impl TreeState {
|
|||
Self {
|
||||
selected_ix: None,
|
||||
focus_handle: cx.focus_handle(),
|
||||
scrollbar_state: ScrollbarState::default(),
|
||||
scroll_handle: UniformListScrollHandle::default(),
|
||||
entries: Vec::new(),
|
||||
render_item: Rc::new(|_, _, _, _, _| ListItem::new(0)),
|
||||
|
|
@ -395,10 +393,7 @@ impl Render for TreeState {
|
|||
.right_0()
|
||||
.bottom_0()
|
||||
.w(Scrollbar::width())
|
||||
.child(Scrollbar::vertical(
|
||||
&self.scrollbar_state,
|
||||
&self.scroll_handle,
|
||||
)),
|
||||
.child(Scrollbar::vertical(&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.
|
||||
//!
|
||||
|
|
@ -18,15 +18,15 @@ use std::{
|
|||
};
|
||||
|
||||
use gpui::{
|
||||
div, point, px, size, Along, AnyElement, App, AvailableSpace, Axis, Bounds, ContentMask,
|
||||
Context, DeferredScrollToItem, Div, Element, ElementId, Entity, GlobalElementId, Half, Hitbox,
|
||||
Along, AnyElement, App, AvailableSpace, Axis, Bounds, ContentMask, Context,
|
||||
DeferredScrollToItem, Div, Element, ElementId, Entity, GlobalElementId, Half, Hitbox,
|
||||
InteractiveElement, IntoElement, IsZero as _, ListSizingBehavior, Pixels, Point, Render,
|
||||
ScrollHandle, ScrollStrategy, Size, Stateful, StatefulInteractiveElement, StyleRefinement,
|
||||
Styled, Window,
|
||||
Styled, Window, div, point, px, size,
|
||||
};
|
||||
use smallvec::SmallVec;
|
||||
|
||||
use crate::{scroll::ScrollHandleOffsetable, AxisExt, PixelsExt};
|
||||
use crate::{AxisExt, PixelsExt, scroll::ScrollbarHandle};
|
||||
|
||||
struct VirtualListScrollHandleState {
|
||||
axis: Axis,
|
||||
|
|
@ -57,7 +57,7 @@ impl AsRef<ScrollHandle> for VirtualListScrollHandle {
|
|||
}
|
||||
}
|
||||
|
||||
impl ScrollHandleOffsetable for VirtualListScrollHandle {
|
||||
impl ScrollbarHandle for VirtualListScrollHandle {
|
||||
fn offset(&self) -> Point<Pixels> {
|
||||
self.base_handle.offset()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ A comprehensive scrollable container component that provides custom scrollbars,
|
|||
|
||||
```rust
|
||||
use gpui_component::{
|
||||
scroll::{Scrollable, ScrollbarState, ScrollbarAxis, ScrollbarShow},
|
||||
scroll::{Scrollable, ScrollbarAxis, ScrollbarShow},
|
||||
StyledExt as _,
|
||||
};
|
||||
```
|
||||
|
|
@ -86,10 +86,9 @@ div()
|
|||
For more control, you can create scrollbars manually:
|
||||
|
||||
```rust
|
||||
use gpui_component::scroll::{Scrollbar, ScrollbarState};
|
||||
use gpui_component::scroll::{Scrollbar};
|
||||
|
||||
pub struct ScrollableView {
|
||||
scroll_state: ScrollbarState,
|
||||
scroll_handle: ScrollHandle,
|
||||
}
|
||||
|
||||
|
|
@ -107,7 +106,7 @@ impl Render for ScrollableView {
|
|||
.child("Your scrollable content")
|
||||
)
|
||||
.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
|
||||
|
||||
```rust
|
||||
Scrollbar::both(&scroll_state, &scroll_handle)
|
||||
Scrollbar::both(&scroll_handle)
|
||||
.axis(ScrollbarAxis::Vertical)
|
||||
.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
|
||||
|
||||
### VirtualList for Large Datasets
|
||||
|
|
@ -287,110 +234,6 @@ Sync scrollbar behavior with system preferences:
|
|||
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
|
||||
|
||||
### File Browser with Scrolling
|
||||
|
|
@ -398,7 +241,6 @@ v_flex()
|
|||
```rust
|
||||
pub struct FileBrowser {
|
||||
files: Vec<String>,
|
||||
scroll_state: ScrollbarState,
|
||||
}
|
||||
|
||||
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