summaryrefslogtreecommitdiffstats
path: root/src/element.rs
diff options
context:
space:
mode:
authorLibravatar Héctor Ramón Jiménez <hector0193@gmail.com>2019-07-20 19:12:31 +0200
committerLibravatar Héctor Ramón Jiménez <hector0193@gmail.com>2019-07-20 19:12:31 +0200
commit2b7ad3d50eae48b1963aa8e866e184c41133ca3d (patch)
treeae5b0c851aebb2dd8c01c08620d1cea7aa9d2466 /src/element.rs
parenteefdcbe06cce97b452ee71ccb6fcd1a423d29075 (diff)
downloadiced-2b7ad3d50eae48b1963aa8e866e184c41133ca3d.tar.gz
iced-2b7ad3d50eae48b1963aa8e866e184c41133ca3d.tar.bz2
iced-2b7ad3d50eae48b1963aa8e866e184c41133ca3d.zip
Decouple `iced` from `coffee`
Diffstat (limited to 'src/element.rs')
-rw-r--r--src/element.rs222
1 files changed, 222 insertions, 0 deletions
diff --git a/src/element.rs b/src/element.rs
new file mode 100644
index 00000000..6bc0ad74
--- /dev/null
+++ b/src/element.rs
@@ -0,0 +1,222 @@
+use stretch::{geometry, result};
+
+use crate::{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`].
+///
+/// [`Widget`]: trait.Widget.html
+/// [`Element`]: struct.Element.html
+pub struct Element<'a, Message, Renderer> {
+ pub(crate) widget: Box<dyn Widget<Message, Renderer> + 'a>,
+}
+
+impl<'a, Message, Renderer> std::fmt::Debug for Element<'a, Message, Renderer> {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.debug_struct("Element")
+ .field("widget", &self.widget)
+ .finish()
+ }
+}
+
+impl<'a, Message, Renderer> Element<'a, Message, Renderer> {
+ /// Create a new [`Element`] containing the given [`Widget`].
+ ///
+ /// [`Element`]: struct.Element.html
+ /// [`Widget`]: trait.Widget.html
+ pub fn new(
+ widget: impl Widget<Message, Renderer> + '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.
+ ///
+ /// [`Element`]: struct.Element.html
+ ///
+ /// # Example
+ /// TODO
+ pub fn map<F, B>(self, f: F) -> Element<'a, B, Renderer>
+ where
+ Message: 'static + Copy,
+ 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!
+ ///
+ /// [`Element`]: struct.Element.html
+ /// [`Renderer`]: trait.Renderer.html
+ pub fn explain(
+ self,
+ color: Renderer::Color,
+ ) -> Element<'a, Message, Renderer>
+ where
+ Message: 'static,
+ Renderer: 'a + crate::Renderer,
+ {
+ Element {
+ widget: Box::new(Explain::new(self, color)),
+ }
+ }
+
+ pub(crate) fn compute_layout(&self, renderer: &Renderer) -> result::Layout {
+ let node = self.widget.node(renderer);
+
+ node.0.compute_layout(geometry::Size::undefined()).unwrap()
+ }
+
+ pub(crate) fn hash(&self, state: &mut Hasher) {
+ self.widget.hash(state);
+ }
+}
+
+struct Map<'a, A, B, Renderer> {
+ widget: Box<dyn Widget<A, Renderer> + 'a>,
+ mapper: Box<dyn Fn(A) -> B>,
+}
+
+impl<'a, A, B, Renderer> std::fmt::Debug for Map<'a, A, B, Renderer> {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.debug_struct("Map").field("widget", &self.widget).finish()
+ }
+}
+
+impl<'a, A, B, Renderer> Map<'a, A, B, Renderer> {
+ pub fn new<F>(
+ widget: Box<dyn Widget<A, Renderer> + '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<B, Renderer> for Map<'a, A, B, Renderer>
+where
+ A: Copy,
+{
+ fn node(&self, renderer: &Renderer) -> Node {
+ self.widget.node(renderer)
+ }
+
+ fn on_event(
+ &mut self,
+ event: Event,
+ layout: Layout<'_>,
+ cursor_position: Point,
+ messages: &mut Vec<B>,
+ ) {
+ let mut original_messages = Vec::new();
+
+ self.widget.on_event(
+ event,
+ layout,
+ cursor_position,
+ &mut original_messages,
+ );
+
+ original_messages
+ .iter()
+ .cloned()
+ .for_each(|message| messages.push((self.mapper)(message)));
+ }
+
+ fn draw(
+ &self,
+ renderer: &mut Renderer,
+ layout: Layout<'_>,
+ cursor_position: Point,
+ ) -> MouseCursor {
+ self.widget.draw(renderer, layout, cursor_position)
+ }
+
+ fn hash(&self, state: &mut Hasher) {
+ self.widget.hash(state);
+ }
+}
+
+struct Explain<'a, Message, Renderer: crate::Renderer> {
+ element: Element<'a, Message, Renderer>,
+ color: Renderer::Color,
+}
+
+impl<'a, Message, Renderer> std::fmt::Debug for Explain<'a, Message, Renderer>
+where
+ Renderer: crate::Renderer,
+{
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.debug_struct("Explain")
+ .field("element", &self.element)
+ .finish()
+ }
+}
+
+impl<'a, Message, Renderer> Explain<'a, Message, Renderer>
+where
+ Renderer: crate::Renderer,
+{
+ fn new(
+ element: Element<'a, Message, Renderer>,
+ color: Renderer::Color,
+ ) -> Self {
+ Explain { element, color }
+ }
+}
+
+impl<'a, Message, Renderer> Widget<Message, Renderer>
+ for Explain<'a, Message, Renderer>
+where
+ Renderer: crate::Renderer,
+{
+ fn node(&self, renderer: &Renderer) -> Node {
+ self.element.widget.node(renderer)
+ }
+
+ fn on_event(
+ &mut self,
+ event: Event,
+ layout: Layout<'_>,
+ cursor_position: Point,
+ messages: &mut Vec<Message>,
+ ) {
+ self.element
+ .widget
+ .on_event(event, layout, cursor_position, messages)
+ }
+
+ fn draw(
+ &self,
+ renderer: &mut Renderer,
+ layout: Layout<'_>,
+ cursor_position: Point,
+ ) -> MouseCursor {
+ renderer.explain(&layout, self.color);
+
+ self.element.widget.draw(renderer, layout, cursor_position)
+ }
+
+ fn hash(&self, state: &mut Hasher) {
+ self.element.widget.hash(state);
+ }
+}