1use crate::overlay;
3use crate::widget::tree::{self, Tree};
4use crate::{Element, Widget};
5
6use iced_native::event::{self, Event};
7use iced_native::layout::{self, Layout};
8use iced_native::mouse;
9use iced_native::renderer;
10use iced_native::widget::scrollable;
11use iced_native::{Clipboard, Length, Point, Rectangle, Shell, Vector};
12
13pub use iced_style::scrollable::{Scrollbar, Scroller, StyleSheet};
14
15#[allow(missing_debug_implementations)]
18pub struct Scrollable<'a, Message, Renderer> {
19 height: Length,
20 scrollbar_width: u16,
21 scrollbar_margin: u16,
22 scroller_width: u16,
23 on_scroll: Option<Box<dyn Fn(f32) -> Message + 'a>>,
24 style_sheet: Box<dyn StyleSheet + 'a>,
25 content: Element<'a, Message, Renderer>,
26}
27
28impl<'a, Message, Renderer: iced_native::Renderer>
29 Scrollable<'a, Message, Renderer>
30{
31 pub fn new(content: impl Into<Element<'a, Message, Renderer>>) -> Self {
33 Scrollable {
34 height: Length::Shrink,
35 scrollbar_width: 10,
36 scrollbar_margin: 0,
37 scroller_width: 10,
38 on_scroll: None,
39 style_sheet: Default::default(),
40 content: content.into(),
41 }
42 }
43
44 pub fn height(mut self, height: Length) -> Self {
46 self.height = height;
47 self
48 }
49
50 pub fn scrollbar_width(mut self, scrollbar_width: u16) -> Self {
53 self.scrollbar_width = scrollbar_width.max(1);
54 self
55 }
56
57 pub fn scrollbar_margin(mut self, scrollbar_margin: u16) -> Self {
59 self.scrollbar_margin = scrollbar_margin;
60 self
61 }
62
63 pub fn scroller_width(mut self, scroller_width: u16) -> Self {
67 self.scroller_width = scroller_width.max(1);
68 self
69 }
70
71 pub fn on_scroll(mut self, f: impl Fn(f32) -> Message + 'a) -> Self {
76 self.on_scroll = Some(Box::new(f));
77 self
78 }
79
80 pub fn style(
82 mut self,
83 style_sheet: impl Into<Box<dyn StyleSheet + 'a>>,
84 ) -> Self {
85 self.style_sheet = style_sheet.into();
86 self
87 }
88}
89
90impl<'a, Message, Renderer> Widget<Message, Renderer>
91 for Scrollable<'a, Message, Renderer>
92where
93 Renderer: iced_native::Renderer,
94{
95 fn tag(&self) -> tree::Tag {
96 tree::Tag::of::<scrollable::State>()
97 }
98
99 fn state(&self) -> tree::State {
100 tree::State::new(scrollable::State::new())
101 }
102
103 fn children(&self) -> Vec<Tree> {
104 vec![Tree::new(&self.content)]
105 }
106
107 fn diff(&self, tree: &mut Tree) {
108 tree.diff_children(std::slice::from_ref(&self.content))
109 }
110
111 fn width(&self) -> Length {
112 self.content.as_widget().width()
113 }
114
115 fn height(&self) -> Length {
116 self.height
117 }
118
119 fn layout(
120 &self,
121 renderer: &Renderer,
122 limits: &layout::Limits,
123 ) -> layout::Node {
124 scrollable::layout(
125 renderer,
126 limits,
127 Widget::<Message, Renderer>::width(self),
128 self.height,
129 |renderer, limits| {
130 self.content.as_widget().layout(renderer, limits)
131 },
132 )
133 }
134
135 fn on_event(
136 &mut self,
137 tree: &mut Tree,
138 event: Event,
139 layout: Layout<'_>,
140 cursor_position: Point,
141 renderer: &Renderer,
142 clipboard: &mut dyn Clipboard,
143 shell: &mut Shell<'_, Message>,
144 ) -> event::Status {
145 scrollable::update(
146 tree.state.downcast_mut::<scrollable::State>(),
147 event,
148 layout,
149 cursor_position,
150 clipboard,
151 shell,
152 self.scrollbar_width,
153 self.scrollbar_margin,
154 self.scroller_width,
155 &self.on_scroll,
156 |event, layout, cursor_position, clipboard, shell| {
157 self.content.as_widget_mut().on_event(
158 &mut tree.children[0],
159 event,
160 layout,
161 cursor_position,
162 renderer,
163 clipboard,
164 shell,
165 )
166 },
167 )
168 }
169
170 fn draw(
171 &self,
172 tree: &Tree,
173 renderer: &mut Renderer,
174 style: &renderer::Style,
175 layout: Layout<'_>,
176 cursor_position: Point,
177 _viewport: &Rectangle,
178 ) {
179 scrollable::draw(
180 tree.state.downcast_ref::<scrollable::State>(),
181 renderer,
182 layout,
183 cursor_position,
184 self.scrollbar_width,
185 self.scrollbar_margin,
186 self.scroller_width,
187 self.style_sheet.as_ref(),
188 |renderer, layout, cursor_position, viewport| {
189 self.content.as_widget().draw(
190 &tree.children[0],
191 renderer,
192 style,
193 layout,
194 cursor_position,
195 viewport,
196 )
197 },
198 )
199 }
200
201 fn mouse_interaction(
202 &self,
203 tree: &Tree,
204 layout: Layout<'_>,
205 cursor_position: Point,
206 _viewport: &Rectangle,
207 renderer: &Renderer,
208 ) -> mouse::Interaction {
209 scrollable::mouse_interaction(
210 tree.state.downcast_ref::<scrollable::State>(),
211 layout,
212 cursor_position,
213 self.scrollbar_width,
214 self.scrollbar_margin,
215 self.scroller_width,
216 |layout, cursor_position, viewport| {
217 self.content.as_widget().mouse_interaction(
218 &tree.children[0],
219 layout,
220 cursor_position,
221 viewport,
222 renderer,
223 )
224 },
225 )
226 }
227
228 fn overlay<'b>(
229 &'b self,
230 tree: &'b mut Tree,
231 layout: Layout<'_>,
232 renderer: &Renderer,
233 ) -> Option<overlay::Element<'b, Message, Renderer>> {
234 self.content
235 .as_widget()
236 .overlay(
237 &mut tree.children[0],
238 layout.children().next().unwrap(),
239 renderer,
240 )
241 .map(|overlay| {
242 let bounds = layout.bounds();
243 let content_layout = layout.children().next().unwrap();
244 let content_bounds = content_layout.bounds();
245 let offset = tree
246 .state
247 .downcast_ref::<scrollable::State>()
248 .offset(bounds, content_bounds);
249
250 overlay.translate(Vector::new(0.0, -(offset as f32)))
251 })
252 }
253}
254
255impl<'a, Message, Renderer> From<Scrollable<'a, Message, Renderer>>
256 for Element<'a, Message, Renderer>
257where
258 Message: 'a + Clone,
259 Renderer: 'a + iced_native::Renderer,
260{
261 fn from(
262 text_input: Scrollable<'a, Message, Renderer>,
263 ) -> Element<'a, Message, Renderer> {
264 Element::new(text_input)
265 }
266}