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