summaryrefslogtreecommitdiffstats
path: root/src/element.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/element.rs')
-rw-r--r--src/element.rs184
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);
}
}