use crate::event::{self, Event}; use crate::layout; use crate::mouse; use crate::overlay; use crate::renderer; use crate::{ Clipboard, Color, Hasher, Layout, Length, Point, Rectangle, Shell, Widget, }; /// 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]: widget/index.html#built-in-widgets #[allow(missing_debug_implementations)] pub struct Element<'a, Message, Renderer> { pub(crate) widget: Box + 'a>, } impl<'a, Message, Renderer> Element<'a, Message, Renderer> where Renderer: crate::Renderer, { /// Creates a new [`Element`] containing the given [`Widget`]. pub fn new( widget: impl Widget + 'a, ) -> Element<'a, Message, Renderer> { Element { widget: Box::new(widget), } } /// 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_native::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_native::Element; /// use iced_native::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: F) -> Element<'a, B, Renderer> where Message: 'static, Renderer: 'a, B: 'static, F: 'static + Fn(Message) -> B, { Element { widget: Box::new(Map::new(self.widget, f)), } } /// Marks the [`Element`] as _to-be-explained_. /// /// The [`Renderer`] will explain the layout of the [`Element`] graphically. /// This can be very useful for debugging your layout! /// /// [`Renderer`]: crate::Renderer pub fn explain>( self, color: C, ) -> Element<'a, Message, Renderer> where Message: 'static, Renderer: 'a, { Element { widget: Box::new(Explain::new(self, color.into())), } } /// Returns the width of the [`Element`]. pub fn width(&self) -> Length { self.widget.width() } /// Returns the height of the [`Element`]. pub fn height(&self) -> Length { self.widget.height() } /// Computes the layout of the [`Element`] in the given [`Limits`]. /// /// [`Limits`]: layout::Limits pub fn layout( &self, renderer: &Renderer, limits: &layout::Limits, ) -> layout::Node { self.widget.layout(renderer, limits) } /// Processes a runtime [`Event`]. pub fn on_event( &mut self, event: Event, layout: Layout<'_>, cursor_position: Point, renderer: &Renderer, clipboard: &mut dyn Clipboard, shell: &mut Shell<'_, Message>, ) -> event::Status { self.widget.on_event( event, layout, cursor_position, renderer, clipboard, shell, ) } /// Draws the [`Element`] and its children using the given [`Layout`]. pub fn draw( &self, renderer: &mut Renderer, style: &renderer::Style, layout: Layout<'_>, cursor_position: Point, viewport: &Rectangle, ) { self.widget .draw(renderer, style, layout, cursor_position, viewport) } /// Returns the current [`mouse::Interaction`] of the [`Element`]. pub fn mouse_interaction( &self, layout: Layout<'_>, cursor_position: Point, viewport: &Rectangle, renderer: &Renderer, ) -> mouse::Interaction { self.widget.mouse_interaction( layout, cursor_position, viewport, renderer, ) } /// Computes the _layout_ hash of the [`Element`]. pub fn hash_layout(&self, state: &mut Hasher) { self.widget.hash_layout(state); } /// Returns the overlay of the [`Element`], if there is any. pub fn overlay<'b>( &'b mut self, layout: Layout<'_>, renderer: &Renderer, ) -> Option> { self.widget.overlay(layout, renderer) } } struct Map<'a, A, B, Renderer> { widget: Box + 'a>, mapper: Box B>, } 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: 'static + Fn(A) -> B, { Map { widget, mapper: Box::new(mapper), } } } impl<'a, A, B, Renderer> Widget for Map<'a, A, B, Renderer> where Renderer: crate::Renderer + 'a, A: 'static, B: 'static, { 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, 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( event, layout, cursor_position, renderer, clipboard, &mut local_shell, ); shell.merge(local_shell, &self.mapper); status } fn draw( &self, renderer: &mut Renderer, style: &renderer::Style, layout: Layout<'_>, cursor_position: Point, viewport: &Rectangle, ) { self.widget .draw(renderer, style, layout, cursor_position, viewport) } fn mouse_interaction( &self, layout: Layout<'_>, cursor_position: Point, viewport: &Rectangle, renderer: &Renderer, ) -> mouse::Interaction { self.widget.mouse_interaction( layout, cursor_position, viewport, renderer, ) } fn hash_layout(&self, state: &mut Hasher) { self.widget.hash_layout(state); } fn overlay( &mut self, layout: Layout<'_>, renderer: &Renderer, ) -> Option> { let mapper = &self.mapper; self.widget .overlay(layout, renderer) .map(move |overlay| overlay.map(mapper)) } } struct Explain<'a, Message, Renderer: crate::Renderer> { element: Element<'a, Message, Renderer>, color: Color, } impl<'a, Message, Renderer> Explain<'a, Message, Renderer> where Renderer: crate::Renderer, { fn new(element: Element<'a, Message, Renderer>, color: Color) -> Self { Explain { element, color } } } impl<'a, Message, Renderer> Widget for Explain<'a, Message, Renderer> where Renderer: crate::Renderer, { fn width(&self) -> Length { self.element.widget.width() } fn height(&self) -> Length { self.element.widget.height() } fn layout( &self, renderer: &Renderer, limits: &layout::Limits, ) -> layout::Node { self.element.widget.layout(renderer, limits) } fn on_event( &mut self, event: Event, layout: Layout<'_>, cursor_position: Point, renderer: &Renderer, clipboard: &mut dyn Clipboard, shell: &mut Shell<'_, Message>, ) -> event::Status { self.element.widget.on_event( event, layout, cursor_position, renderer, clipboard, shell, ) } fn draw( &self, renderer: &mut Renderer, style: &renderer::Style, layout: Layout<'_>, cursor_position: Point, viewport: &Rectangle, ) { fn explain_layout( renderer: &mut Renderer, color: Color, layout: Layout<'_>, ) { renderer.fill_quad( renderer::Quad { bounds: layout.bounds(), border_color: color, border_width: 1.0, border_radius: 0.0, }, Color::TRANSPARENT, ); for child in layout.children() { explain_layout(renderer, color, child); } } self.element.widget.draw( renderer, style, layout, cursor_position, viewport, ); explain_layout(renderer, self.color, layout); } fn mouse_interaction( &self, layout: Layout<'_>, cursor_position: Point, viewport: &Rectangle, renderer: &Renderer, ) -> mouse::Interaction { self.element.widget.mouse_interaction( layout, cursor_position, viewport, renderer, ) } fn hash_layout(&self, state: &mut Hasher) { self.element.widget.hash_layout(state); } fn overlay( &mut self, layout: Layout<'_>, renderer: &Renderer, ) -> Option> { self.element.overlay(layout, renderer) } }