use crate::overlay; use crate::widget::tree::{self, Tree}; use crate::widget::Widget; use iced_native::event::{self, Event}; use iced_native::layout::{self, Layout}; use iced_native::mouse; use iced_native::renderer; use iced_native::{Clipboard, Length, Point, Rectangle, Shell}; /// A generic [`Widget`]. /// /// It is useful to build composable user interfaces that do not leak /// implementation details in their __view logic__. /// /// If you have a [built-in widget], you should be able to use `Into` /// to turn it into an [`Element`]. /// /// [built-in widget]: crate::widget pub struct Element<'a, Message, Renderer> { widget: Box + 'a>, } impl<'a, Message, Renderer> Element<'a, Message, Renderer> { /// Creates a new [`Element`] containing the given [`Widget`]. pub fn new(widget: impl Widget + 'a) -> Self { Self { widget: Box::new(widget), } } /// Returns a reference to the [`Widget`] of the [`Element`], pub fn as_widget(&self) -> &dyn Widget { self.widget.as_ref() } /// Returns a mutable reference to the [`Widget`] of the [`Element`], pub fn as_widget_mut(&mut self) -> &mut dyn Widget { self.widget.as_mut() } /// Applies a transformation to the produced message of the [`Element`]. /// /// This method is useful when you want to decouple different parts of your /// UI and make them __composable__. /// /// # Example /// Imagine we want to use [our counter](index.html#usage). But instead of /// showing a single counter, we want to display many of them. We can reuse /// the `Counter` type as it is! /// /// We use composition to model the __state__ of our new application: /// /// ``` /// # mod counter { /// # pub struct Counter; /// # } /// use counter::Counter; /// /// struct ManyCounters { /// counters: Vec, /// } /// ``` /// /// We can store the state of multiple counters now. However, the /// __messages__ we implemented before describe the user interactions /// of a __single__ counter. Right now, we need to also identify which /// counter is receiving user interactions. Can we use composition again? /// Yes. /// /// ``` /// # mod counter { /// # #[derive(Debug, Clone, Copy)] /// # pub enum Message {} /// # } /// #[derive(Debug, Clone, Copy)] /// pub enum Message { /// Counter(usize, counter::Message) /// } /// ``` /// /// We compose the previous __messages__ with the index of the counter /// producing them. Let's implement our __view logic__ now: /// /// ``` /// # mod counter { /// # type Text = iced_pure::widget::Text; /// # /// # #[derive(Debug, Clone, Copy)] /// # pub enum Message {} /// # pub struct Counter; /// # /// # impl Counter { /// # pub fn view(&mut self) -> Text { /// # Text::new("") /// # } /// # } /// # } /// # /// # mod iced_wgpu { /// # pub use iced_native::renderer::Null as Renderer; /// # } /// # /// # use counter::Counter; /// # /// # struct ManyCounters { /// # counters: Vec, /// # } /// # /// # #[derive(Debug, Clone, Copy)] /// # pub enum Message { /// # Counter(usize, counter::Message) /// # } /// use iced_pure::Element; /// use iced_pure::widget::Row; /// use iced_wgpu::Renderer; /// /// impl ManyCounters { /// pub fn view(&mut self) -> Row { /// // We can quickly populate a `Row` by folding over our counters /// self.counters.iter_mut().enumerate().fold( /// Row::new().spacing(20), /// |row, (index, counter)| { /// // We display the counter /// let element: Element = /// counter.view().into(); /// /// row.push( /// // Here we turn our `Element` into /// // an `Element` by combining the `index` and the /// // message of the `element`. /// element.map(move |message| Message::Counter(index, message)) /// ) /// } /// ) /// } /// } /// ``` /// /// Finally, our __update logic__ is pretty straightforward: simple /// delegation. /// /// ``` /// # mod counter { /// # #[derive(Debug, Clone, Copy)] /// # pub enum Message {} /// # pub struct Counter; /// # /// # impl Counter { /// # pub fn update(&mut self, _message: Message) {} /// # } /// # } /// # /// # use counter::Counter; /// # /// # struct ManyCounters { /// # counters: Vec, /// # } /// # /// # #[derive(Debug, Clone, Copy)] /// # pub enum Message { /// # Counter(usize, counter::Message) /// # } /// impl ManyCounters { /// pub fn update(&mut self, message: Message) { /// match message { /// Message::Counter(index, counter_msg) => { /// if let Some(counter) = self.counters.get_mut(index) { /// counter.update(counter_msg); /// } /// } /// } /// } /// } /// ``` pub fn map( self, f: impl Fn(Message) -> B + 'a, ) -> Element<'a, B, Renderer> where Message: 'a, Renderer: iced_native::Renderer + 'a, B: 'a, { Element::new(Map::new(self.widget, f)) } } struct Map<'a, A, B, Renderer> { widget: Box + 'a>, mapper: Box B + 'a>, } impl<'a, A, B, Renderer> Map<'a, A, B, Renderer> { pub fn new( widget: Box + 'a>, mapper: F, ) -> Map<'a, A, B, Renderer> where F: 'a + Fn(A) -> B, { Map { widget, mapper: Box::new(mapper), } } } impl<'a, A, B, Renderer> Widget for Map<'a, A, B, Renderer> where Renderer: iced_native::Renderer + 'a, A: 'a, B: 'a, { fn tag(&self) -> tree::Tag { self.widget.tag() } fn state(&self) -> tree::State { self.widget.state() } fn children(&self) -> Vec { self.widget.children() } fn diff(&self, tree: &mut Tree) { self.widget.diff(tree) } fn width(&self) -> Length { self.widget.width() } fn height(&self) -> Length { self.widget.height() } fn layout( &self, renderer: &Renderer, limits: &layout::Limits, ) -> layout::Node { self.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<'_, B>, ) -> event::Status { let mut local_messages = Vec::new(); let mut local_shell = Shell::new(&mut local_messages); let status = self.widget.on_event( tree, event, layout, cursor_position, renderer, clipboard, &mut local_shell, ); shell.merge(local_shell, &self.mapper); status } fn draw( &self, tree: &Tree, renderer: &mut Renderer, style: &renderer::Style, layout: Layout<'_>, cursor_position: Point, viewport: &Rectangle, ) { self.widget.draw( tree, renderer, style, layout, cursor_position, viewport, ) } fn mouse_interaction( &self, tree: &Tree, layout: Layout<'_>, cursor_position: Point, viewport: &Rectangle, renderer: &Renderer, ) -> mouse::Interaction { self.widget.mouse_interaction( tree, layout, cursor_position, viewport, renderer, ) } fn overlay<'b>( &'b self, tree: &'b mut Tree, layout: Layout<'_>, renderer: &Renderer, ) -> Option> { let mapper = &self.mapper; self.widget .overlay(tree, layout, renderer) .map(move |overlay| overlay.map(mapper)) } }