freya_components/
plot.rs

1use std::{
2    any::Any,
3    borrow::Cow,
4    cell::RefCell,
5    collections::HashMap,
6    rc::Rc,
7};
8
9use freya_core::{
10    integration::*,
11    prelude::*,
12};
13use freya_engine::prelude::{
14    ClipOp,
15    Paint,
16    PaintStyle,
17    SkRect,
18};
19pub use plotters;
20pub use skia_plotters_backend::*;
21
22type Callback = Rc<RefCell<dyn FnMut(&mut RenderContext)>>;
23
24pub struct RenderCallback(Callback);
25
26impl RenderCallback {
27    pub fn new(callback: impl FnMut(&mut RenderContext) + 'static) -> Self {
28        Self(Rc::new(RefCell::new(callback)))
29    }
30
31    pub fn call(&self, data: &mut RenderContext) {
32        (self.0.borrow_mut())(data)
33    }
34}
35
36impl Clone for RenderCallback {
37    fn clone(&self) -> Self {
38        Self(self.0.clone())
39    }
40}
41
42impl PartialEq for RenderCallback {
43    fn eq(&self, _other: &Self) -> bool {
44        true
45    }
46}
47
48impl<H: FnMut(&mut RenderContext) + 'static> From<H> for RenderCallback {
49    fn from(value: H) -> Self {
50        RenderCallback::new(value)
51    }
52}
53
54#[derive(PartialEq, Clone)]
55pub struct PlotElement {
56    pub layout: LayoutData,
57    pub event_handlers: FxHashMap<EventName, EventHandlerType>,
58    pub effect: Option<EffectData>,
59    pub on_render: RenderCallback,
60}
61
62impl PlotElement {}
63
64impl ElementExt for PlotElement {
65    fn changed(&self, other: &Rc<dyn ElementExt>) -> bool {
66        let Some(rect) = (other.as_ref() as &dyn Any).downcast_ref::<Self>() else {
67            return false;
68        };
69
70        self != rect
71    }
72
73    fn diff(&self, other: &Rc<dyn ElementExt>) -> DiffModifies {
74        let Some(rect) = (other.as_ref() as &dyn Any).downcast_ref::<Self>() else {
75            return DiffModifies::all();
76        };
77
78        let mut diff = DiffModifies::empty();
79
80        if self.effect != rect.effect {
81            diff.insert(DiffModifies::EFFECT);
82        }
83
84        if !self.layout.layout.self_layout_eq(&rect.layout.layout) {
85            diff.insert(DiffModifies::STYLE);
86            diff.insert(DiffModifies::LAYOUT);
87        }
88
89        if !self.layout.layout.inner_layout_eq(&rect.layout.layout) {
90            diff.insert(DiffModifies::STYLE);
91            diff.insert(DiffModifies::INNER_LAYOUT);
92        }
93
94        if self.event_handlers != rect.event_handlers {
95            diff.insert(DiffModifies::EVENT_HANDLERS);
96        }
97
98        if self.on_render != rect.on_render {
99            diff.insert(DiffModifies::STYLE);
100        }
101
102        diff
103    }
104
105    fn layout(&'_ self) -> Cow<'_, LayoutData> {
106        Cow::Borrowed(&self.layout)
107    }
108
109    fn effect(&'_ self) -> Option<Cow<'_, EffectData>> {
110        self.effect.as_ref().map(Cow::Borrowed)
111    }
112
113    fn style(&'_ self) -> Cow<'_, StyleState> {
114        Cow::Owned(StyleState::default())
115    }
116
117    fn text_style(&'_ self) -> Cow<'_, TextStyleData> {
118        Cow::Owned(TextStyleData::default())
119    }
120
121    fn accessibility(&'_ self) -> Cow<'_, AccessibilityData> {
122        Cow::Owned(AccessibilityData::default())
123    }
124
125    fn relative_layer(&self) -> i16 {
126        0
127    }
128
129    fn events_handlers(&'_ self) -> Option<Cow<'_, FxHashMap<EventName, EventHandlerType>>> {
130        Some(Cow::Borrowed(&self.event_handlers))
131    }
132
133    fn is_point_inside(&self, context: EventMeasurementContext) -> bool {
134        let area = context.layout_node.visible_area();
135        let cursor = context.cursor.to_f32();
136        area.contains(cursor)
137    }
138
139    fn clip(&self, context: ClipContext) {
140        let area = context.visible_area;
141
142        context.canvas.clip_rect(
143            SkRect::new(area.min_x(), area.min_y(), area.max_x(), area.max_y()),
144            ClipOp::Intersect,
145            true,
146        );
147    }
148
149    fn render(&self, mut context: RenderContext) {
150        let style = self.style();
151        let area = context.layout_node.area;
152
153        let mut paint = Paint::default();
154        paint.set_anti_alias(true);
155        paint.set_style(PaintStyle::Fill);
156        style.background.apply_to_paint(&mut paint, area);
157
158        context.canvas.draw_rect(
159            SkRect::new(area.min_x(), area.min_y(), area.max_x(), area.max_y()),
160            &paint,
161        );
162
163        context
164            .canvas
165            .scale((context.scale_factor as f32, context.scale_factor as f32));
166        context.canvas.translate((area.min_x(), area.min_y()));
167        self.on_render.call(&mut context);
168        context.canvas.restore();
169    }
170}
171
172pub struct Plot {
173    element: PlotElement,
174    elements: Vec<Element>,
175    key: DiffKey,
176}
177
178impl ChildrenExt for Plot {
179    fn get_children(&mut self) -> &mut Vec<Element> {
180        &mut self.elements
181    }
182}
183
184impl KeyExt for Plot {
185    fn write_key(&mut self) -> &mut DiffKey {
186        &mut self.key
187    }
188}
189
190impl EventHandlersExt for Plot {
191    fn get_event_handlers(&mut self) -> &mut FxHashMap<EventName, EventHandlerType> {
192        &mut self.element.event_handlers
193    }
194}
195
196impl MaybeExt for Plot {}
197
198impl From<Plot> for Element {
199    fn from(value: Plot) -> Self {
200        Element::Element {
201            key: value.key,
202            element: Rc::new(value.element),
203            elements: value.elements,
204        }
205    }
206}
207
208pub fn plot(on_render: RenderCallback) -> Plot {
209    Plot::new(on_render)
210}
211
212impl Plot {
213    pub fn new(on_render: RenderCallback) -> Self {
214        Self {
215            element: PlotElement {
216                on_render,
217                layout: LayoutData::default(),
218                event_handlers: HashMap::default(),
219                effect: None,
220            },
221            elements: Vec::default(),
222            key: DiffKey::None,
223        }
224    }
225
226    pub fn try_downcast(element: &dyn ElementExt) -> Option<PlotElement> {
227        (element as &dyn Any).downcast_ref::<PlotElement>().cloned()
228    }
229}
230
231impl LayoutExt for Plot {
232    fn get_layout(&mut self) -> &mut LayoutData {
233        &mut self.element.layout
234    }
235}
236
237impl ContainerExt for Plot {}
238
239impl ContainerWithContentExt for Plot {}