use crate::widget::Tree; use crate::{Element, Widget}; use iced_native::event::{self, Event}; use iced_native::layout::{self, Layout}; use iced_native::mouse; use iced_native::renderer; use iced_native::widget::scrollable; use iced_native::{Clipboard, Hasher, Length, Point, Rectangle, Shell}; pub use iced_style::scrollable::StyleSheet; use std::any::{self, Any}; /// A widget that can vertically display an infinite amount of content with a /// scrollbar. #[allow(missing_debug_implementations)] pub struct Scrollable<'a, Message, Renderer> { height: Length, scrollbar_width: u16, scrollbar_margin: u16, scroller_width: u16, on_scroll: Option Message>>, style_sheet: Box, content: Element<'a, Message, Renderer>, } impl<'a, Message, Renderer: iced_native::Renderer> Scrollable<'a, Message, Renderer> { /// Creates a new [`Scrollable`]. pub fn new(content: impl Into>) -> Self { Scrollable { height: Length::Shrink, scrollbar_width: 10, scrollbar_margin: 0, scroller_width: 10, on_scroll: None, style_sheet: Default::default(), content: content.into(), } } /// Sets the height of the [`Scrollable`]. pub fn height(mut self, height: Length) -> Self { self.height = height; self } /// Sets the scrollbar width of the [`Scrollable`] . /// Silently enforces a minimum value of 1. pub fn scrollbar_width(mut self, scrollbar_width: u16) -> Self { self.scrollbar_width = scrollbar_width.max(1); self } /// Sets the scrollbar margin of the [`Scrollable`] . pub fn scrollbar_margin(mut self, scrollbar_margin: u16) -> Self { self.scrollbar_margin = scrollbar_margin; self } /// Sets the scroller width of the [`Scrollable`] . /// /// It silently enforces a minimum value of 1. pub fn scroller_width(mut self, scroller_width: u16) -> Self { self.scroller_width = scroller_width.max(1); self } /// Sets a function to call when the [`Scrollable`] is scrolled. /// /// The function takes the new relative offset of the [`Scrollable`] /// (e.g. `0` means top, while `1` means bottom). pub fn on_scroll(mut self, f: impl Fn(f32) -> Message + 'static) -> Self { self.on_scroll = Some(Box::new(f)); self } /// Sets the style of the [`Scrollable`] . pub fn style( mut self, style_sheet: impl Into>, ) -> Self { self.style_sheet = style_sheet.into(); self } } impl<'a, Message, Renderer> Widget for Scrollable<'a, Message, Renderer> where Renderer: iced_native::Renderer, { fn tag(&self) -> any::TypeId { any::TypeId::of::() } fn state(&self) -> Box { Box::new(scrollable::State::new()) } fn diff(&self, tree: &mut Tree) { tree.diff_children(std::slice::from_ref(&self.content)) } fn children_state(&self) -> Vec { vec![Tree::new(&self.content)] } fn width(&self) -> Length { self.content.as_widget().width() } fn height(&self) -> Length { self.height } fn hash_layout(&self, state: &mut Hasher) { use std::hash::Hash; self.tag().hash(state); self.height.hash(state); self.content.as_widget().hash_layout(state) } fn layout( &self, renderer: &Renderer, limits: &layout::Limits, ) -> layout::Node { scrollable::layout( renderer, limits, Widget::::width(self), self.height, |renderer, limits| { self.content.as_widget().layout(renderer, limits) }, ) } fn on_event( &mut self, tree: &mut Tree, event: Event, layout: Layout<'_>, cursor_position: Point, renderer: &Renderer, clipboard: &mut dyn Clipboard, shell: &mut Shell<'_, Message>, ) -> event::Status { scrollable::update( tree.state.downcast_mut::(), event, layout, cursor_position, clipboard, shell, self.scrollbar_width, self.scrollbar_margin, self.scroller_width, &self.on_scroll, |event, layout, cursor_position, clipboard, shell| { self.content.as_widget_mut().on_event( &mut tree.children[0], event, layout, cursor_position, renderer, clipboard, shell, ) }, ) } fn draw( &self, tree: &Tree, renderer: &mut Renderer, style: &renderer::Style, layout: Layout<'_>, cursor_position: Point, _viewport: &Rectangle, ) { scrollable::draw( tree.state.downcast_ref::(), renderer, layout, cursor_position, self.scrollbar_width, self.scrollbar_margin, self.scroller_width, self.style_sheet.as_ref(), |renderer, layout, cursor_position, viewport| { self.content.as_widget().draw( &tree.children[0], renderer, style, layout, cursor_position, viewport, ) }, ) } fn mouse_interaction( &self, tree: &Tree, layout: Layout<'_>, cursor_position: Point, _viewport: &Rectangle, renderer: &Renderer, ) -> mouse::Interaction { scrollable::mouse_interaction( tree.state.downcast_ref::(), layout, cursor_position, self.scrollbar_width, self.scrollbar_margin, self.scroller_width, |layout, cursor_position, viewport| { self.content.as_widget().mouse_interaction( &tree.children[0], layout, cursor_position, viewport, renderer, ) }, ) } } impl<'a, Message, Renderer> From> for Element<'a, Message, Renderer> where Message: 'a + Clone, Renderer: 'a + iced_native::Renderer, { fn from( text_input: Scrollable<'a, Message, Renderer>, ) -> Element<'a, Message, Renderer> { Element::new(text_input) } }