ori_core/
context.rs

1use std::{
2    any::Any,
3    collections::HashMap,
4    ops::{Deref, DerefMut, Range},
5};
6
7use glam::Vec2;
8use ori_graphics::{
9    Frame, ImageHandle, ImageSource, Quad, Rect, Renderer, TextHit, TextSection, WeakImageHandle,
10};
11
12use crate::{
13    BoxConstraints, EventSink, FromStyleAttribute, NodeState, RequestRedrawEvent, SendSync,
14    StyleAttribute, StyleSelectors, StyleSpecificity, Stylesheet, Unit,
15};
16
17#[derive(Clone, Debug, Default)]
18pub struct ImageCache {
19    images: HashMap<ImageSource, WeakImageHandle>,
20}
21
22impl ImageCache {
23    pub fn new() -> Self {
24        Self::default()
25    }
26
27    pub fn len(&self) -> usize {
28        self.images.len()
29    }
30
31    pub fn is_empty(&self) -> bool {
32        self.images.is_empty()
33    }
34
35    pub fn get(&self, source: &ImageSource) -> Option<ImageHandle> {
36        self.images.get(source)?.upgrade()
37    }
38
39    pub fn insert(&mut self, source: ImageSource, handle: ImageHandle) {
40        self.images.insert(source, handle.downgrade());
41    }
42
43    pub fn clear(&mut self) {
44        self.images.clear();
45    }
46
47    pub fn clean(&mut self) {
48        self.images.retain(|_, v| v.is_alive());
49    }
50}
51
52pub struct EventContext<'a> {
53    pub style: &'a Stylesheet,
54    pub state: &'a mut NodeState,
55    pub renderer: &'a dyn Renderer,
56    pub selectors: &'a StyleSelectors,
57    pub event_sink: &'a EventSink,
58    pub image_cache: &'a mut ImageCache,
59}
60
61pub struct LayoutContext<'a> {
62    pub style: &'a Stylesheet,
63    pub state: &'a mut NodeState,
64    pub renderer: &'a dyn Renderer,
65    pub selectors: &'a StyleSelectors,
66    pub event_sink: &'a EventSink,
67    pub image_cache: &'a mut ImageCache,
68}
69
70impl<'a> LayoutContext<'a> {
71    pub fn style_constraints(&mut self, bc: BoxConstraints) -> BoxConstraints {
72        let min_width = self.style_range_group("min-width", "width", bc.width());
73        let max_width = self.style_range_group("max-width", "width", bc.width());
74
75        let min_height = self.style_range_group("min-height", "height", bc.height());
76        let max_height = self.style_range_group("max-height", "height", bc.height());
77
78        let min_size = bc.constrain(Vec2::new(min_width, min_height));
79        let max_size = bc.constrain(Vec2::new(max_width, max_height));
80
81        BoxConstraints::new(min_size, max_size)
82    }
83
84    pub fn messure_text(&self, section: &TextSection) -> Option<Rect> {
85        self.renderer.messure_text(section)
86    }
87
88    pub fn hit_text(&self, section: &TextSection, pos: Vec2) -> Option<TextHit> {
89        self.renderer.hit_text(section, pos)
90    }
91}
92
93pub struct DrawLayer<'a, 'b> {
94    draw_context: &'b mut DrawContext<'a>,
95    depth: f32,
96    clip: Option<Rect>,
97}
98
99impl<'a, 'b> DrawLayer<'a, 'b> {
100    pub fn depth(mut self, depth: f32) -> Self {
101        self.depth = depth;
102        self
103    }
104
105    pub fn clip(mut self, clip: Rect) -> Self {
106        self.clip = Some(clip.round());
107        self
108    }
109
110    pub fn draw(self, f: impl FnOnce(&mut DrawContext)) {
111        let layer = self
112            .draw_context
113            .frame
114            .layer()
115            .depth(self.depth)
116            .clip(self.clip);
117
118        layer.draw(|frame| {
119            let mut child = DrawContext {
120                style: self.draw_context.style,
121                state: self.draw_context.state,
122                frame,
123                renderer: self.draw_context.renderer,
124                selectors: self.draw_context.selectors,
125                event_sink: self.draw_context.event_sink,
126                image_cache: self.draw_context.image_cache,
127            };
128
129            f(&mut child);
130        });
131    }
132}
133
134pub struct DrawContext<'a> {
135    pub style: &'a Stylesheet,
136    pub state: &'a mut NodeState,
137    pub frame: &'a mut Frame,
138    pub renderer: &'a dyn Renderer,
139    pub selectors: &'a StyleSelectors,
140    pub event_sink: &'a EventSink,
141    pub image_cache: &'a mut ImageCache,
142}
143
144impl<'a> DrawContext<'a> {
145    pub fn frame(&mut self) -> &mut Frame {
146        self.frame
147    }
148
149    pub fn layer<'b>(&'b mut self) -> DrawLayer<'a, 'b> {
150        DrawLayer {
151            draw_context: self,
152            depth: 1.0,
153            clip: None,
154        }
155    }
156
157    /// Runs the given callback on a new layer offset by the given amount.
158    ///
159    /// `offset` should almost always be `1.0`.
160    pub fn draw_layer(&mut self, f: impl FnOnce(&mut DrawContext)) {
161        self.layer().draw(f);
162    }
163
164    /// Draws the quad at the current layout rect.
165    ///
166    /// This will use the following style attributes:
167    /// - `background-color`: The background color of the quad.
168    /// - `border-radius`: The border radius of the quad overwritten by the more specific
169    /// attributes.
170    /// - `border-top-left-radius`: The top left border radius of the quad.
171    /// - `border-top-right-radius`: The top right border radius of the quad.
172    /// - `border-bottom-right-radius`: The bottom right border radius of the quad.
173    /// - `border-bottom-left-radius`: The bottom left border radius of the quad.
174    /// - `border-width`: The border width of the quad.
175    pub fn draw_quad(&mut self) {
176        let range = 0.0..self.rect().max.min_element() / 2.0;
177
178        let tl = "border-top-left-radius";
179        let tr = "border-top-right-radius";
180        let br = "border-bottom-right-radius";
181        let bl = "border-bottom-left-radius";
182
183        let tl = self.style_range_group(tl, "border-radius", range.clone());
184        let tr = self.style_range_group(tr, "border-radius", range.clone());
185        let br = self.style_range_group(br, "border-radius", range.clone());
186        let bl = self.style_range_group(bl, "border-radius", range.clone());
187
188        let quad = Quad {
189            rect: self.rect(),
190            background: self.style("background-color"),
191            border_radius: [tl, tr, br, bl],
192            border_width: self.style_range("border-width", range),
193            border_color: self.style("border-color"),
194        };
195
196        self.draw(quad);
197    }
198}
199
200impl<'a> Deref for DrawContext<'a> {
201    type Target = Frame;
202
203    fn deref(&self) -> &Self::Target {
204        self.frame
205    }
206}
207
208impl<'a> DerefMut for DrawContext<'a> {
209    fn deref_mut(&mut self) -> &mut Self::Target {
210        self.frame
211    }
212}
213
214pub trait Context {
215    fn stylesheet(&self) -> &Stylesheet;
216    fn state(&self) -> &NodeState;
217    fn state_mut(&mut self) -> &mut NodeState;
218    fn renderer(&self) -> &dyn Renderer;
219    fn selectors(&self) -> &StyleSelectors;
220    fn event_sink(&self) -> &EventSink;
221    fn image_cache(&self) -> &ImageCache;
222    fn image_cache_mut(&mut self) -> &mut ImageCache;
223
224    fn get_style_attribute(&self, key: &str) -> Option<StyleAttribute> {
225        if let Some(attribute) = self.state().style.attributes.get(key) {
226            return Some(attribute.clone());
227        }
228
229        let attribute = self.stylesheet().get_attribute(self.selectors(), key)?;
230        Some(attribute.clone())
231    }
232
233    fn get_style_attribute_specificity(
234        &self,
235        key: &str,
236    ) -> Option<(StyleAttribute, StyleSpecificity)> {
237        if let Some(attribute) = self.state().style.attributes.get(key) {
238            return Some((attribute.clone(), StyleSpecificity::INLINE));
239        }
240
241        let stylesheet = self.stylesheet();
242        let selectors = self.selectors();
243        let (attribute, specificity) = stylesheet.get_attribute_specificity(selectors, key)?;
244        Some((attribute.clone(), specificity))
245    }
246
247    fn get_style<T: FromStyleAttribute + 'static>(&mut self, key: &str) -> Option<T> {
248        let attribute = self.get_style_attribute(key)?;
249        let value = T::from_attribute(attribute.value)?;
250        let transition = attribute.transition;
251
252        Some(self.state_mut().transition(key, value, transition))
253    }
254
255    fn get_style_specificity<T: FromStyleAttribute + 'static>(
256        &mut self,
257        key: &str,
258    ) -> Option<(T, StyleSpecificity)> {
259        let (attribute, specificity) = self.get_style_attribute_specificity(key)?;
260        let value = T::from_attribute(attribute.value)?;
261        let transition = attribute.transition;
262
263        Some((
264            self.state_mut().transition(key, value, transition),
265            specificity,
266        ))
267    }
268
269    /// Get the value of a style attribute, if it has a transition, the value will be
270    /// interpolated between the current value and the new value.
271    #[track_caller]
272    fn style<T: FromStyleAttribute + Default + 'static>(&mut self, key: &str) -> T {
273        self.get_style(key).unwrap_or_default()
274    }
275
276    fn style_group<T: FromStyleAttribute + Default + 'static>(
277        &mut self,
278        primary_key: &str,
279        secondary_key: &str,
280    ) -> T {
281        let primary = self.get_style_specificity(primary_key);
282        let secondary = self.get_style_specificity(secondary_key);
283
284        match (primary, secondary) {
285            (Some((primary, primary_specificity)), Some((secondary, secondary_specificity))) => {
286                if primary_specificity >= secondary_specificity {
287                    primary
288                } else {
289                    secondary
290                }
291            }
292            (Some((value, _)), None) | (None, Some((value, _))) => value,
293            _ => T::default(),
294        }
295    }
296
297    fn get_style_range(&mut self, key: &str, range: Range<f32>) -> Option<f32> {
298        let attribute = self.get_style_attribute(key)?;
299        let value = Unit::from_attribute(attribute.value)?;
300        let transition = attribute.transition;
301
302        let pixels = value.pixels(
303            range,
304            self.renderer().scale(),
305            self.renderer().window_size(),
306        );
307
308        Some((self.state_mut()).transition(key, pixels, transition))
309    }
310
311    fn get_style_range_specificity(
312        &mut self,
313        key: &str,
314        range: Range<f32>,
315    ) -> Option<(f32, StyleSpecificity)> {
316        let (attribute, specificity) = self.get_style_attribute_specificity(key)?;
317        let value = Unit::from_attribute(attribute.value)?;
318        let transition = attribute.transition;
319
320        let pixels = value.pixels(
321            range,
322            self.renderer().scale(),
323            self.renderer().window_size(),
324        );
325
326        Some((
327            (self.state_mut()).transition(key, pixels, transition),
328            specificity,
329        ))
330    }
331
332    /// Get the value of a style attribute, if it has a transition, the value will be
333    /// interpolated between the current value and the new value.
334    ///
335    /// This is a convenience method for getting a value in pixels, as opposed to
336    /// `style` which returns a `Unit`.
337    #[track_caller]
338    fn style_range(&mut self, key: &str, range: Range<f32>) -> f32 {
339        self.get_style_range(key, range).unwrap_or_default()
340    }
341
342    fn style_range_group(
343        &mut self,
344        primary_key: &str,
345        secondary_key: &str,
346        range: Range<f32>,
347    ) -> f32 {
348        let primary = self.get_style_range_specificity(primary_key, range.clone());
349        let secondary = self.get_style_range_specificity(secondary_key, range);
350
351        match (primary, secondary) {
352            (Some((primary, primary_specificity)), Some((secondary, secondary_specificity))) => {
353                if primary_specificity >= secondary_specificity {
354                    primary
355                } else {
356                    secondary
357                }
358            }
359            (Some((value, _)), None) | (None, Some((value, _))) => value,
360            _ => 0.0,
361        }
362    }
363
364    fn downcast_renderer<T: Renderer>(&self) -> Option<&T> {
365        self.renderer().downcast_ref()
366    }
367
368    fn load_image(&mut self, source: &ImageSource) -> ImageHandle {
369        if let Some(handle) = self.image_cache().get(source) {
370            return handle;
371        }
372
373        let data = source.load();
374        let image = self.renderer().create_image(&data);
375        self.image_cache_mut().insert(source.clone(), image.clone());
376        image
377    }
378
379    fn active(&self) -> bool {
380        self.state().active
381    }
382
383    fn hovered(&self) -> bool {
384        self.state().hovered
385    }
386
387    fn focused(&self) -> bool {
388        self.state().focused
389    }
390
391    fn focus(&mut self) {
392        if self.focused() {
393            return;
394        }
395
396        self.state_mut().focused = true;
397        self.request_redraw();
398    }
399
400    fn unfocus(&mut self) {
401        if !self.focused() {
402            return;
403        }
404
405        self.state_mut().focused = false;
406        self.request_redraw();
407    }
408
409    fn activate(&mut self) {
410        if self.active() {
411            return;
412        }
413
414        self.state_mut().active = true;
415        self.request_redraw();
416    }
417
418    fn deactivate(&mut self) {
419        if !self.active() {
420            return;
421        }
422
423        self.state_mut().active = false;
424        self.request_redraw();
425    }
426
427    fn local_rect(&self) -> Rect {
428        self.state().local_rect
429    }
430
431    fn rect(&self) -> Rect {
432        self.state().global_rect
433    }
434
435    fn size(&self) -> Vec2 {
436        self.state().local_rect.size()
437    }
438
439    fn request_redraw(&mut self) {
440        self.send_event(RequestRedrawEvent);
441    }
442
443    fn send_event(&self, event: impl Any + SendSync) {
444        self.event_sink().send(event);
445    }
446
447    fn delta_time(&self) -> f32 {
448        self.state().delta()
449    }
450}
451
452macro_rules! context {
453    ($name:ident) => {
454        impl<'a> Context for $name<'a> {
455            fn stylesheet(&self) -> &Stylesheet {
456                self.style
457            }
458
459            fn state(&self) -> &NodeState {
460                self.state
461            }
462
463            fn state_mut(&mut self) -> &mut NodeState {
464                self.state
465            }
466
467            fn renderer(&self) -> &dyn Renderer {
468                self.renderer
469            }
470
471            fn selectors(&self) -> &StyleSelectors {
472                &self.selectors
473            }
474
475            fn event_sink(&self) -> &EventSink {
476                &self.event_sink
477            }
478
479            fn image_cache(&self) -> &ImageCache {
480                &self.image_cache
481            }
482
483            fn image_cache_mut(&mut self) -> &mut ImageCache {
484                &mut self.image_cache
485            }
486        }
487    };
488}
489
490context!(EventContext);
491context!(LayoutContext);
492context!(DrawContext);