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