summaryrefslogtreecommitdiff
path: root/src/layout/node.rs
blob: 4cba3d4ffe766892232c586a36cc879c52701619 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
//! Layout nodes.

use std::any::Any;
use std::fmt::{self, Debug, Formatter};
use std::ops::Deref;

use super::*;

/// A self-contained, styled layout node.
#[derive(Clone, PartialEq)]
pub enum LayoutNode {
    /// A spacing node.
    Spacing(Spacing),
    /// A text node.
    Text(Text),
    /// A dynamic that can implement custom layouting behaviour.
    Dyn(Dynamic),
}

impl LayoutNode {
    /// Create a new dynamic node.
    pub fn dynamic<T: DynNode>(inner: T) -> Self {
        Self::Dyn(Dynamic::new(inner))
    }
}

impl Layout for LayoutNode {
    fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Vec<Layouted> {
        match self {
            Self::Spacing(spacing) => spacing.layout(ctx, areas),
            Self::Text(text) => text.layout(ctx, areas),
            Self::Dyn(boxed) => boxed.layout(ctx, areas),
        }
    }
}

impl Debug for LayoutNode {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        match self {
            Self::Spacing(spacing) => spacing.fmt(f),
            Self::Text(text) => text.fmt(f),
            Self::Dyn(boxed) => boxed.fmt(f),
        }
    }
}

/// A wrapper around a boxed dynamic node.
///
/// _Note_: This is needed because the compiler can't `derive(PartialEq)` for
///         [`LayoutNode`] when directly putting the boxed node in there, see
///         the [Rust Issue].
///
/// [`LayoutNode`]: enum.LayoutNode.html
/// [Rust Issue]: https://github.com/rust-lang/rust/issues/31740
pub struct Dynamic(pub Box<dyn DynNode>);

impl Dynamic {
    /// Wrap a type implementing `DynNode`.
    pub fn new<T: DynNode>(inner: T) -> Self {
        Self(Box::new(inner))
    }
}

impl Deref for Dynamic {
    type Target = dyn DynNode;

    fn deref(&self) -> &Self::Target {
        self.0.as_ref()
    }
}

impl Debug for Dynamic {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        self.0.fmt(f)
    }
}

impl From<Dynamic> for LayoutNode {
    fn from(dynamic: Dynamic) -> Self {
        Self::Dyn(dynamic)
    }
}

impl Clone for Dynamic {
    fn clone(&self) -> Self {
        Self(self.0.dyn_clone())
    }
}

impl PartialEq for Dynamic {
    fn eq(&self, other: &Self) -> bool {
        self.0.dyn_eq(other.0.as_ref())
    }
}

/// A dynamic node, which can implement custom layouting behaviour.
///
/// This trait just combines the requirements for types to qualify as dynamic
/// nodes. The interesting part happens in the inherited trait [`Layout`].
///
/// The trait itself also contains three helper methods to make `Box<dyn
/// DynNode>` able to implement `Clone` and `PartialEq`. However, these are
/// automatically provided by a blanket impl as long as the type in question
/// implements[`Layout`],  `Debug`, `PartialEq`, `Clone` and is `'static`.
///
/// [`Layout`]: ../trait.Layout.html
pub trait DynNode: Debug + Layout + 'static {
    /// Convert into a `dyn Any` to enable downcasting.
    fn as_any(&self) -> &dyn Any;

    /// Check for equality with another trait object.
    fn dyn_eq(&self, other: &dyn DynNode) -> bool;

    /// Clone into a trait object.
    fn dyn_clone(&self) -> Box<dyn DynNode>;
}

impl<T> DynNode for T
where
    T: Debug + Layout + PartialEq + Clone + 'static,
{
    fn as_any(&self) -> &dyn Any {
        self
    }

    fn dyn_eq(&self, other: &dyn DynNode) -> bool {
        if let Some(other) = other.as_any().downcast_ref::<Self>() {
            self == other
        } else {
            false
        }
    }

    fn dyn_clone(&self) -> Box<dyn DynNode> {
        Box::new(self.clone())
    }
}