diff options
Diffstat (limited to 'src/element.rs')
-rw-r--r-- | src/element.rs | 184 |
1 files changed, 166 insertions, 18 deletions
diff --git a/src/element.rs b/src/element.rs index 6bc0ad74..70d06f42 100644 --- a/src/element.rs +++ b/src/element.rs @@ -1,13 +1,19 @@ use stretch::{geometry, result}; -use crate::{Event, Hasher, Layout, MouseCursor, Node, Point, Widget}; +use crate::{ + renderer, Event, Hasher, Layout, MouseCursor, Node, Point, Widget, +}; /// A generic [`Widget`]. /// -/// If you have a widget, you should be able to use `widget.into()` to turn it -/// into an [`Element`]. +/// It is useful to build composable user interfaces that do not leak +/// implementation details in their __view logic__. /// -/// [`Widget`]: trait.Widget.html +/// If you have a [built-in widget], you should be able to use `Into<Element>` +/// to turn it into an [`Element`]. +/// +/// [built-in widget]: widget/index.html#built-in-widgets +/// [`Widget`]: widget/trait.Widget.html /// [`Element`]: struct.Element.html pub struct Element<'a, Message, Renderer> { pub(crate) widget: Box<dyn Widget<Message, Renderer> + 'a>, @@ -25,7 +31,7 @@ impl<'a, Message, Renderer> Element<'a, Message, Renderer> { /// Create a new [`Element`] containing the given [`Widget`]. /// /// [`Element`]: struct.Element.html - /// [`Widget`]: trait.Widget.html + /// [`Widget`]: widget/trait.Widget.html pub fn new( widget: impl Widget<Message, Renderer> + 'a, ) -> Element<'a, Message, Renderer> { @@ -37,12 +43,154 @@ impl<'a, Message, Renderer> Element<'a, Message, Renderer> { /// Applies a transformation to the produced message of the [`Element`]. /// /// This method is useful when you want to decouple different parts of your - /// UI. + /// UI and make them __composable__. /// /// [`Element`]: struct.Element.html /// /// # Example - /// TODO + /// 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<Counter>, + /// } + /// ``` + /// + /// 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 { + /// # use iced::{button, Button}; + /// # + /// # #[derive(Debug, Clone, Copy)] + /// # pub enum Message {} + /// # pub struct Counter(button::State); + /// # + /// # impl Counter { + /// # pub fn view(&mut self) -> Button<Message> { + /// # Button::new(&mut self.0, "_") + /// # } + /// # } + /// # } + /// # + /// # mod iced_wgpu { + /// # use iced::{ + /// # button, MouseCursor, Node, Point, Rectangle, Style, + /// # }; + /// # pub struct Renderer; + /// # + /// # impl button::Renderer for Renderer { + /// # fn draw( + /// # &mut self, + /// # _cursor_position: Point, + /// # _bounds: Rectangle, + /// # _state: &button::State, + /// # _label: &str, + /// # _class: button::Class, + /// # ) -> MouseCursor { + /// # MouseCursor::OutOfBounds + /// # } + /// # } + /// # } + /// # + /// # use counter::Counter; + /// # + /// # struct ManyCounters { + /// # counters: Vec<Counter>, + /// # } + /// # + /// # #[derive(Debug, Clone, Copy)] + /// # pub enum Message { + /// # Counter(usize, counter::Message) + /// # } + /// use iced::{Element, Row}; + /// use iced_wgpu::Renderer; + /// + /// impl ManyCounters { + /// pub fn view(&mut self) -> Row<Message, Renderer> { + /// // 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::Message, Renderer> = + /// counter.view().into(); + /// + /// row.push( + /// // Here we turn our `Element<counter::Message>` into + /// // an `Element<Message>` 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<Counter>, + /// # } + /// # + /// # #[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<F, B>(self, f: F) -> Element<'a, B, Renderer> where Message: 'static + Copy, @@ -68,7 +216,7 @@ impl<'a, Message, Renderer> Element<'a, Message, Renderer> { ) -> Element<'a, Message, Renderer> where Message: 'static, - Renderer: 'a + crate::Renderer, + Renderer: 'a + renderer::Debugger, { Element { widget: Box::new(Explain::new(self, color)), @@ -81,8 +229,8 @@ impl<'a, Message, Renderer> Element<'a, Message, Renderer> { node.0.compute_layout(geometry::Size::undefined()).unwrap() } - pub(crate) fn hash(&self, state: &mut Hasher) { - self.widget.hash(state); + pub(crate) fn hash_layout(&self, state: &mut Hasher) { + self.widget.hash_layout(state); } } @@ -151,19 +299,19 @@ where self.widget.draw(renderer, layout, cursor_position) } - fn hash(&self, state: &mut Hasher) { - self.widget.hash(state); + fn hash_layout(&self, state: &mut Hasher) { + self.widget.hash_layout(state); } } -struct Explain<'a, Message, Renderer: crate::Renderer> { +struct Explain<'a, Message, Renderer: renderer::Debugger> { element: Element<'a, Message, Renderer>, color: Renderer::Color, } impl<'a, Message, Renderer> std::fmt::Debug for Explain<'a, Message, Renderer> where - Renderer: crate::Renderer, + Renderer: renderer::Debugger, { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("Explain") @@ -174,7 +322,7 @@ where impl<'a, Message, Renderer> Explain<'a, Message, Renderer> where - Renderer: crate::Renderer, + Renderer: renderer::Debugger, { fn new( element: Element<'a, Message, Renderer>, @@ -187,7 +335,7 @@ where impl<'a, Message, Renderer> Widget<Message, Renderer> for Explain<'a, Message, Renderer> where - Renderer: crate::Renderer, + Renderer: renderer::Debugger, { fn node(&self, renderer: &Renderer) -> Node { self.element.widget.node(renderer) @@ -216,7 +364,7 @@ where self.element.widget.draw(renderer, layout, cursor_position) } - fn hash(&self, state: &mut Hasher) { - self.element.widget.hash(state); + fn hash_layout(&self, state: &mut Hasher) { + self.element.widget.hash_layout(state); } } |