repose_core/
view.rs

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