pixel_widgets/
component.rs

1use std::any::Any;
2use std::collections::hash_map::DefaultHasher;
3use std::future::Future;
4use std::hash::{Hash, Hasher};
5
6use futures::Stream;
7
8use crate::node::component_node::{Runtime, State};
9use crate::node::Node;
10use crate::style::builder::StyleBuilder;
11use crate::widget::Context as WidgetContext;
12
13/// A re-usable component for defining a fragment of a user interface.
14/// Components are the main building block for user interfaces in pixel-widgets.
15///
16/// The examples in this repository all implement some kind of `Component`,
17/// check them out if you just want to read some code.
18pub trait Component {
19    /// Mutable state associated with this `Component`.
20    type State: 'static + Any + Send + Sync;
21
22    /// The message type this `Component` will receive from it's view.
23    type Message: 'static;
24
25    /// The message type this `Component` submits to its parent.
26    type Output: 'static;
27
28    /// Create a new `State` for the `Component`.
29    /// This will be called only once when the `Component` is first created.
30    fn mount(&self) -> Self::State;
31
32    /// Generate the view for the `Component`.
33    /// This will be called just in time before ui rendering.
34    /// When the `Component` is updated,
35    ///  the view will be invalidated and the runtime will have to call this function again.
36    fn view<'a>(&'a self, state: &'a Self::State) -> Node<'a, Self::Message>;
37
38    /// Update the `Component` state in response to the `message`.
39    /// Asynchronous operations can be submitted to the `context`,
40    ///  which will result in more `update` calls in the future.
41    /// Messages for the parent `Component` or root can also be submitted through the `context`.
42    fn update(
43        &self,
44        _message: Self::Message,
45        _state: State<Self::State>,
46        _context: Context<Self::Message, Self::Output>,
47    ) {
48    }
49
50    /// Returns a `StyleBuilder` with styling information scoped to this component.
51    /// This method will be called when you call
52    /// [`StyleBuilder::component()`](../style/builder/struct.StyleBuilder.html#method.component)
53    /// when building your style.
54    fn style() -> StyleBuilder {
55        StyleBuilder::default()
56    }
57
58    /// Converts the component into a `Node`. This is used by the library to
59    ///  instantiate the component in a user interface.
60    fn into_node<'a>(self) -> Node<'a, Self::Output>
61    where
62        Self: 'a + Sized,
63    {
64        Node::from_component(self)
65    }
66
67    /// Converts the component into a `Node` and sets a style class to it.
68    fn class<'a>(self, class: &'a str) -> Node<'a, Self::Output>
69    where
70        Self: 'a + Sized,
71    {
72        let mut node = self.into_node();
73        node.set_class(class);
74        node
75    }
76
77    /// Converts the component into a `Node` and sets a custom key to it.
78    fn key<'a, K>(self, key: K) -> Node<'a, Self::Output>
79    where
80        Self: 'a + Sized,
81        K: Hash,
82    {
83        let mut hasher = DefaultHasher::new();
84        key.hash(&mut hasher);
85        let mut node = self.into_node();
86        node.set_key(hasher.finish());
87        node
88    }
89}
90
91/// Allows for message passing between components
92pub struct Context<'a, Message, Output> {
93    widget_context: &'a mut WidgetContext<Output>,
94    runtime: &'a mut Runtime<Message>,
95}
96
97impl<'a, Message, Output> Context<'a, Message, Output> {
98    pub(crate) fn new(widget_context: &'a mut WidgetContext<Output>, runtime: &'a mut Runtime<Message>) -> Self {
99        Self {
100            widget_context,
101            runtime,
102        }
103    }
104
105    /// Push a message to the parent.
106    pub fn push(&mut self, message: Output) {
107        self.widget_context.push(message);
108    }
109
110    /// Push multiple messages to the parent using an iterator.
111    pub fn extend<I: IntoIterator<Item = Output>>(&mut self, iter: I) {
112        self.widget_context.extend(iter);
113    }
114
115    /// Returns the cursor position
116    pub fn cursor(&self) -> (f32, f32) {
117        self.widget_context.cursor()
118    }
119
120    /// Submits a messsage to self in the future.
121    pub fn wait<F: 'static + Future<Output = Message> + Send + Sync + Unpin>(&mut self, fut: F) {
122        self.runtime.wait(fut);
123    }
124
125    /// Submits a stream of messages to self in the future.
126    pub fn stream<S: 'static + Stream<Item = Message> + Send + Sync + Unpin>(&mut self, stream: S) {
127        self.runtime.stream(stream);
128    }
129}