1use super::{Scrollbar, ScrollbarAxis, ScrollbarState};
2use gpui::{
3 div, relative, AnyElement, App, Bounds, Div, Element, ElementId, GlobalElementId,
4 InspectorElementId, InteractiveElement, Interactivity, IntoElement, LayoutId, ParentElement,
5 Pixels, Position, ScrollHandle, SharedString, Stateful, StatefulInteractiveElement, Style,
6 StyleRefinement, Styled, Window,
7};
8
9pub struct Scrollable<E> {
11 id: ElementId,
12 element: Option<E>,
13 axis: ScrollbarAxis,
14 _element: Stateful<Div>,
16}
17
18impl<E> Scrollable<E>
19where
20 E: Element,
21{
22 pub(crate) fn new(axis: impl Into<ScrollbarAxis>, element: E) -> Self {
23 let id = ElementId::Name(SharedString::from(
24 format!("scrollable-{:?}", element.id(),),
25 ));
26
27 Self {
28 element: Some(element),
29 _element: div().id("fake"),
30 id,
31 axis: axis.into(),
32 }
33 }
34
35 pub fn vertical(mut self) -> Self {
37 self.set_axis(ScrollbarAxis::Vertical);
38 self
39 }
40
41 pub fn horizontal(mut self) -> Self {
44 self.set_axis(ScrollbarAxis::Horizontal);
45 self
46 }
47
48 pub fn set_axis(&mut self, axis: impl Into<ScrollbarAxis>) {
50 self.axis = axis.into();
51 }
52
53 fn with_element_state<R>(
54 &mut self,
55 id: &GlobalElementId,
56 window: &mut Window,
57 cx: &mut App,
58 f: impl FnOnce(&mut Self, &mut ScrollViewState, &mut Window, &mut App) -> R,
59 ) -> R {
60 window.with_optional_element_state::<ScrollViewState, _>(
61 Some(id),
62 |element_state, window| {
63 let mut element_state = element_state.unwrap().unwrap_or_default();
64 let result = f(self, &mut element_state, window, cx);
65 (result, Some(element_state))
66 },
67 )
68 }
69}
70
71pub struct ScrollViewState {
72 state: ScrollbarState,
73 handle: ScrollHandle,
74}
75
76impl Default for ScrollViewState {
77 fn default() -> Self {
78 Self {
79 handle: ScrollHandle::new(),
80 state: ScrollbarState::default(),
81 }
82 }
83}
84
85impl<E> ParentElement for Scrollable<E>
86where
87 E: Element + ParentElement,
88{
89 fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
90 if let Some(element) = &mut self.element {
91 element.extend(elements);
92 }
93 }
94}
95
96impl<E> Styled for Scrollable<E>
97where
98 E: Element + Styled,
99{
100 fn style(&mut self) -> &mut StyleRefinement {
101 if let Some(element) = &mut self.element {
102 element.style()
103 } else {
104 self._element.style()
105 }
106 }
107}
108
109impl<E> InteractiveElement for Scrollable<E>
110where
111 E: Element + InteractiveElement,
112{
113 fn interactivity(&mut self) -> &mut Interactivity {
114 if let Some(element) = &mut self.element {
115 element.interactivity()
116 } else {
117 self._element.interactivity()
118 }
119 }
120}
121impl<E> StatefulInteractiveElement for Scrollable<E> where E: Element + StatefulInteractiveElement {}
122
123impl<E> IntoElement for Scrollable<E>
124where
125 E: Element,
126{
127 type Element = Self;
128
129 fn into_element(self) -> Self::Element {
130 self
131 }
132}
133
134impl<E> Element for Scrollable<E>
135where
136 E: Element,
137{
138 type RequestLayoutState = AnyElement;
139 type PrepaintState = ScrollViewState;
140
141 fn id(&self) -> Option<ElementId> {
142 Some(self.id.clone())
143 }
144
145 fn source_location(&self) -> Option<&'static std::panic::Location<'static>> {
146 None
147 }
148
149 fn request_layout(
150 &mut self,
151 id: Option<&GlobalElementId>,
152 _: Option<&InspectorElementId>,
153 window: &mut Window,
154 cx: &mut App,
155 ) -> (LayoutId, Self::RequestLayoutState) {
156 let mut style = Style::default();
157 style.flex_grow = 1.0;
158 style.position = Position::Relative;
159 style.size.width = relative(1.0).into();
160 style.size.height = relative(1.0).into();
161
162 let axis = self.axis;
163 let scroll_id = self.id.clone();
164 let content = self.element.take().map(|c| c.into_any_element());
165
166 self.with_element_state(id.unwrap(), window, cx, |_, element_state, window, cx| {
167 let mut element = div()
168 .relative()
169 .size_full()
170 .overflow_hidden()
171 .child(
172 div()
173 .id(scroll_id)
174 .track_scroll(&element_state.handle)
175 .overflow_scroll()
176 .relative()
177 .size_full()
178 .child(div().children(content)),
179 )
180 .child(
181 div()
182 .absolute()
183 .top_0()
184 .left_0()
185 .right_0()
186 .bottom_0()
187 .child(
188 Scrollbar::both(&element_state.state, &element_state.handle).axis(axis),
189 ),
190 )
191 .into_any_element();
192
193 let element_id = element.request_layout(window, cx);
194 let layout_id = window.request_layout(style, vec![element_id], cx);
195
196 (layout_id, element)
197 })
198 }
199
200 fn prepaint(
201 &mut self,
202 _: Option<&GlobalElementId>,
203 _: Option<&InspectorElementId>,
204 _: Bounds<Pixels>,
205 element: &mut Self::RequestLayoutState,
206 window: &mut Window,
207 cx: &mut App,
208 ) -> Self::PrepaintState {
209 element.prepaint(window, cx);
210 ScrollViewState::default()
212 }
213
214 fn paint(
215 &mut self,
216 _: Option<&GlobalElementId>,
217 _: Option<&InspectorElementId>,
218 _: Bounds<Pixels>,
219 element: &mut Self::RequestLayoutState,
220 _: &mut Self::PrepaintState,
221 window: &mut Window,
222 cx: &mut App,
223 ) {
224 element.paint(window, cx)
225 }
226}