repose_core/
view.rs

1use crate::{Brush, Color, Modifier, Rect, Transform};
2use std::{rc::Rc, sync::Arc};
3
4pub type ViewId = u64;
5
6pub type ImageHandle = u64;
7#[derive(Clone, Copy, Debug, PartialEq, Eq)]
8pub enum ImageFit {
9    Contain,
10    Cover,
11    FitWidth,
12    FitHeight,
13}
14
15pub type Callback = Rc<dyn Fn()>;
16pub type ScrollCallback = Rc<dyn Fn(crate::Vec2) -> crate::Vec2>;
17
18#[derive(Clone)]
19pub enum ViewKind {
20    Surface,
21    Box,
22    Row,
23    Column,
24    Stack,
25    ScrollV {
26        on_scroll: Option<ScrollCallback>,
27        set_viewport_height: Option<Rc<dyn Fn(f32)>>,
28        set_content_height: Option<Rc<dyn Fn(f32)>>,
29        get_scroll_offset: Option<Rc<dyn Fn() -> f32>>,
30        set_scroll_offset: Option<Rc<dyn Fn(f32)>>,
31    },
32    ScrollXY {
33        on_scroll: Option<ScrollCallback>,
34        set_viewport_width: Option<Rc<dyn Fn(f32)>>,
35        set_viewport_height: Option<Rc<dyn Fn(f32)>>,
36        set_content_width: Option<Rc<dyn Fn(f32)>>,
37        set_content_height: Option<Rc<dyn Fn(f32)>>,
38        get_scroll_offset_xy: Option<Rc<dyn Fn() -> (f32, f32)>>,
39        set_scroll_offset_xy: Option<Rc<dyn Fn(f32, f32)>>,
40    },
41    Text {
42        text: String,
43        color: Color,
44        font_size: f32,
45        soft_wrap: bool,
46        max_lines: Option<usize>,
47        overflow: TextOverflow,
48    },
49    Button {
50        on_click: Option<Callback>,
51    },
52    TextField {
53        state_key: ViewId,
54        hint: String,
55        multiline: bool,
56        on_change: Option<Rc<dyn Fn(String)>>,
57        on_submit: Option<Rc<dyn Fn(String)>>,
58    },
59    Checkbox {
60        checked: bool,
61        on_change: Option<Rc<dyn Fn(bool)>>,
62    },
63    RadioButton {
64        selected: bool,
65        on_select: Option<Callback>,
66    },
67    Switch {
68        checked: bool,
69        on_change: Option<Rc<dyn Fn(bool)>>,
70    },
71    Slider {
72        value: f32,
73        min: f32,
74        max: f32,
75        step: Option<f32>,
76        on_change: Option<CallbackF32>,
77    },
78    RangeSlider {
79        start: f32,
80        end: f32,
81        min: f32,
82        max: f32,
83        step: Option<f32>,
84        on_change: Option<CallbackRange>,
85    },
86    ProgressBar {
87        value: f32,
88        min: f32,
89        max: f32,
90        circular: bool,
91    },
92    Image {
93        handle: ImageHandle,
94        tint: Color, // multiplicative (WHITE = no tint)
95        fit: ImageFit,
96    },
97    Ellipse {
98        rect: Rect,
99        color: Color,
100    },
101    EllipseBorder {
102        rect: Rect,
103        color: Color,
104        width: f32, // screen-space width (px)
105    },
106}
107
108impl std::fmt::Debug for ViewKind {
109    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
110        match self {
111            ViewKind::Checkbox { checked, .. } => f
112                .debug_struct("Checkbox")
113                .field("checked", checked)
114                .finish(),
115            ViewKind::RadioButton { selected, .. } => f
116                .debug_struct("RadioButton")
117                .field("selected", selected)
118                .finish(),
119            ViewKind::Switch { checked, .. } => {
120                f.debug_struct("Switch").field("checked", checked).finish()
121            }
122            ViewKind::Surface => write!(f, "Surface"),
123            ViewKind::Box => write!(f, "Box"),
124            ViewKind::Row => write!(f, "Row"),
125            ViewKind::Column => write!(f, "Column"),
126            ViewKind::Stack => write!(f, "Stack"),
127            ViewKind::ScrollV { .. } => write!(f, "ScrollV"),
128            ViewKind::ScrollXY { .. } => write!(f, "ScrollXY"),
129            ViewKind::Text {
130                text,
131                color,
132                font_size,
133                soft_wrap,
134                max_lines,
135                overflow,
136            } => f
137                .debug_struct("Text")
138                .field("text", text)
139                .field("color", color)
140                .field("font_size", font_size)
141                .field("soft_wrap", soft_wrap)
142                .field("max_lines", max_lines)
143                .field("overflow", overflow)
144                .finish(),
145            ViewKind::Image { handle, tint, fit } => f
146                .debug_struct("Image")
147                .field("handle", handle)
148                .field("tint", tint)
149                .field("fit", fit)
150                .finish(),
151            ViewKind::Button { .. } => f
152                .debug_struct("Button")
153                .field("on_click", &"<callback>")
154                .finish(),
155            ViewKind::TextField {
156                state_key,
157                hint,
158                multiline,
159                on_change,
160                on_submit,
161            } => f
162                .debug_struct("TextField")
163                .field("state_key", state_key)
164                .field("hint", hint)
165                .field("multiline", multiline)
166                .finish(),
167            ViewKind::Slider {
168                value,
169                min,
170                max,
171                step,
172                ..
173            } => f
174                .debug_struct("Slider")
175                .field("value", value)
176                .field("min", min)
177                .field("max", max)
178                .field("step", step)
179                .finish(),
180            ViewKind::RangeSlider {
181                start,
182                end,
183                min,
184                max,
185                step,
186                ..
187            } => f
188                .debug_struct("RangeSlider")
189                .field("start", start)
190                .field("end", end)
191                .field("min", min)
192                .field("max", max)
193                .field("step", step)
194                .finish(),
195            ViewKind::ProgressBar {
196                value,
197                min,
198                max,
199                circular,
200            } => f
201                .debug_struct("ProgressBar")
202                .field("value", value)
203                .field("min", min)
204                .field("max", max)
205                .field("circular", circular)
206                .finish(),
207            ViewKind::Ellipse { rect, color } => f
208                .debug_struct("Ellipse")
209                .field("rect", rect)
210                .field("color", color)
211                .finish(),
212            ViewKind::EllipseBorder { rect, color, width } => f
213                .debug_struct("EllipseBorder")
214                .field("rect", rect)
215                .field("color", color)
216                .field("width", width)
217                .finish(),
218        }
219    }
220}
221
222#[derive(Clone, Debug)]
223pub struct View {
224    pub id: ViewId,
225    pub kind: ViewKind,
226    pub modifier: Modifier,
227    pub children: Vec<View>,
228    pub semantics: Option<crate::semantics::Semantics>,
229}
230
231impl View {
232    pub fn new(id: ViewId, kind: ViewKind) -> Self {
233        View {
234            id,
235            kind,
236            modifier: Modifier::default(),
237            children: vec![],
238            semantics: None,
239        }
240    }
241    pub fn modifier(mut self, m: Modifier) -> Self {
242        self.modifier = m;
243        self
244    }
245    pub fn with_children(mut self, kids: Vec<View>) -> Self {
246        self.children = kids;
247        self
248    }
249    pub fn semantics(mut self, s: crate::semantics::Semantics) -> Self {
250        self.semantics = Some(s);
251        self
252    }
253}
254
255/// Renderable scene
256#[derive(Clone, Debug, Default)]
257pub struct Scene {
258    pub clear_color: Color,
259    pub nodes: Vec<SceneNode>,
260}
261
262#[derive(Clone, Debug)]
263pub enum SceneNode {
264    Rect {
265        rect: Rect,
266        brush: Brush,
267        radius: f32,
268    },
269    Border {
270        rect: Rect,
271        color: Color,
272        width: f32,
273        radius: f32,
274    },
275    Text {
276        rect: Rect,
277        text: Arc<str>,
278        color: Color,
279        size: f32,
280    },
281    Ellipse {
282        rect: Rect,
283        brush: Brush,
284    },
285    EllipseBorder {
286        rect: Rect,
287        color: Color,
288        width: f32, // screen-space width (px)
289    },
290    PushClip {
291        rect: Rect,
292        radius: f32,
293    },
294    PopClip,
295    PushTransform {
296        transform: Transform,
297    },
298    PopTransform,
299    Image {
300        rect: Rect,
301        handle: ImageHandle,
302        tint: Color,
303        fit: ImageFit,
304    },
305}
306
307pub type CallbackF32 = Rc<dyn Fn(f32)>;
308pub type CallbackRange = Rc<dyn Fn(f32, f32)>;
309
310#[derive(Clone, Copy, Debug, PartialEq, Eq)]
311pub enum TextOverflow {
312    Visible,
313    Clip,
314    Ellipsis,
315}