Skip to main content

repose_core/
view.rs

1use crate::{Brush, Color, Modifier, Rect, TextSpan, 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 struct OverlayEntry {
20    pub id: u64,
21    pub view: Box<View>,
22}
23
24#[derive(Clone)]
25pub enum ViewKind {
26    Surface,
27    Box,
28    Row,
29    Column,
30    Stack,
31    OverlayHost,
32    ScrollV {
33        on_scroll: Option<ScrollCallback>,
34        set_viewport_height: Option<Rc<dyn Fn(f32)>>,
35        set_content_height: Option<Rc<dyn Fn(f32)>>,
36        get_scroll_offset: Option<Rc<dyn Fn() -> f32>>,
37        set_scroll_offset: Option<Rc<dyn Fn(f32)>>,
38        show_scrollbar: bool,
39    },
40    ScrollXY {
41        on_scroll: Option<ScrollCallback>,
42        set_viewport_width: Option<Rc<dyn Fn(f32)>>,
43        set_viewport_height: Option<Rc<dyn Fn(f32)>>,
44        set_content_width: Option<Rc<dyn Fn(f32)>>,
45        set_content_height: Option<Rc<dyn Fn(f32)>>,
46        get_scroll_offset_xy: Option<Rc<dyn Fn() -> (f32, f32)>>,
47        set_scroll_offset_xy: Option<Rc<dyn Fn(f32, f32)>>,
48        show_scrollbar: bool,
49    },
50    Text {
51        text: String,
52        color: Color,
53        font_size: f32,
54        soft_wrap: bool,
55        max_lines: Option<usize>,
56        overflow: TextOverflow,
57        font_family: Option<&'static str>,
58        annotations: Option<Arc<[TextSpan]>>,
59    },
60    Button {
61        on_click: Option<Callback>,
62    },
63    TextField {
64        state_key: ViewId,
65        hint: String,
66        multiline: bool,
67        on_change: Option<Rc<dyn Fn(String)>>,
68        on_submit: Option<Rc<dyn Fn(String)>>,
69    },
70    Checkbox {
71        checked: bool,
72        on_change: Option<Rc<dyn Fn(bool)>>,
73    },
74    RadioButton {
75        selected: bool,
76        on_select: Option<Callback>,
77    },
78    Switch {
79        checked: bool,
80        on_change: Option<Rc<dyn Fn(bool)>>,
81    },
82    Slider {
83        value: f32,
84        min: f32,
85        max: f32,
86        step: Option<f32>,
87        on_change: Option<CallbackF32>,
88    },
89    RangeSlider {
90        start: f32,
91        end: f32,
92        min: f32,
93        max: f32,
94        step: Option<f32>,
95        on_change: Option<CallbackRange>,
96    },
97    ProgressBar {
98        value: f32,
99        min: f32,
100        max: f32,
101        circular: bool,
102    },
103    Image {
104        handle: ImageHandle,
105        tint: Color, // multiplicative (WHITE = no tint)
106        fit: ImageFit,
107    },
108    Ellipse {
109        rect: Rect,
110        color: Color,
111    },
112    EllipseBorder {
113        rect: Rect,
114        color: Color,
115        width: f32, // screen-space width (px)
116    },
117}
118
119impl std::fmt::Debug for ViewKind {
120    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
121        match self {
122            Self::Surface => f.write_str("Surface"),
123            Self::Box => f.write_str("Box"),
124            Self::Row => f.write_str("Row"),
125            Self::Column => f.write_str("Column"),
126            Self::Stack => f.write_str("Stack"),
127            Self::OverlayHost => f.write_str("OverlayHost"),
128            Self::ScrollV { .. } => f.write_str("ScrollV"),
129            Self::ScrollXY { .. } => f.write_str("ScrollXY"),
130            Self::Button { .. } => f.write_str("Button"),
131            Self::Image { .. } => f.write_str("Image"),
132            Self::Ellipse { .. } => f.write_str("Ellipse"),
133            Self::EllipseBorder { .. } => f.write_str("EllipseBorder"),
134            Self::Text { text, .. } => write!(f, "Text({:?})", text),
135            Self::TextField { hint, .. } => write!(f, "TextField({:?})", hint),
136            Self::Checkbox { checked, .. } => write!(f, "Checkbox({})", checked),
137            Self::RadioButton { selected, .. } => write!(f, "Radio({})", selected),
138            Self::Switch { checked, .. } => write!(f, "Switch({})", checked),
139            Self::Slider { value, .. } => write!(f, "Slider({})", value),
140            Self::RangeSlider { start, end, .. } => write!(f, "Range({}..{})", start, end),
141            Self::ProgressBar { value, .. } => write!(f, "Progress({})", value),
142        }
143    }
144}
145
146#[derive(Clone, Debug)]
147pub struct View {
148    pub id: ViewId,
149    pub kind: ViewKind,
150    pub modifier: Modifier,
151    pub children: Vec<View>,
152    pub semantics: Option<crate::semantics::Semantics>,
153}
154
155impl View {
156    pub fn new(id: ViewId, kind: ViewKind) -> Self {
157        View {
158            id,
159            kind,
160            modifier: Modifier::default(),
161            children: vec![],
162            semantics: None,
163        }
164    }
165    pub fn modifier(mut self, m: Modifier) -> Self {
166        self.modifier = m;
167        self
168    }
169    /// Mark this view as disabled - ignores pointer events.
170    pub fn disabled(mut self) -> Self {
171        self.modifier.disabled = true;
172        self
173    }
174    pub fn with_children(mut self, kids: Vec<View>) -> Self {
175        self.children = kids;
176        self
177    }
178    pub fn semantics(mut self, s: crate::semantics::Semantics) -> Self {
179        self.semantics = Some(s);
180        self
181    }
182}
183
184/// Renderable scene
185#[derive(Clone, Debug, Default)]
186pub struct Scene {
187    pub clear_color: Color,
188    pub nodes: Vec<SceneNode>,
189}
190
191#[derive(Clone, Debug)]
192pub enum SceneNode {
193    Rect {
194        rect: Rect,
195        brush: Brush,
196        radius: f32,
197    },
198    Border {
199        rect: Rect,
200        color: Color,
201        width: f32,
202        radius: f32,
203    },
204    Text {
205        rect: Rect,
206        text: Arc<str>,
207        color: Color,
208        size: f32,
209        font_family: Option<&'static str>,
210    },
211    Ellipse {
212        rect: Rect,
213        brush: Brush,
214    },
215    EllipseBorder {
216        rect: Rect,
217        color: Color,
218        width: f32, // screen-space width (px)
219    },
220    PushClip {
221        rect: Rect,
222        radius: f32,
223    },
224    PopClip,
225    PushTransform {
226        transform: Transform,
227    },
228    PopTransform,
229    Image {
230        rect: Rect,
231        handle: ImageHandle,
232        tint: Color,
233        fit: ImageFit,
234    },
235    /// Shadow behind a rounded rect, typically driven by `StateElevation`.
236    /// The `elevation` field controls offset and alpha.
237    Shadow {
238        rect: Rect,
239        radius: f32,
240        elevation: f32,
241        color: Color,
242    },
243}
244
245pub type CallbackF32 = Rc<dyn Fn(f32)>;
246pub type CallbackRange = Rc<dyn Fn(f32, f32)>;
247
248#[derive(Clone, Copy, Debug, PartialEq, Eq)]
249pub enum TextOverflow {
250    Visible,
251    Clip,
252    Ellipsis,
253}