freya-components 0.4.0-alpha.6

Components for Freya apps
Documentation
use std::{
    any::Any,
    borrow::Cow,
    cell::RefCell,
    collections::HashMap,
    rc::Rc,
};

use freya_core::{
    integration::*,
    prelude::*,
};
use freya_engine::prelude::{
    ClipOp,
    Paint,
    PaintStyle,
    SkRect,
};
pub use plotters;
pub use skia_plotters_backend::*;

type Callback = Rc<RefCell<dyn FnMut(&mut RenderContext)>>;

pub struct RenderCallback(Callback);

impl RenderCallback {
    pub fn new(callback: impl FnMut(&mut RenderContext) + 'static) -> Self {
        Self(Rc::new(RefCell::new(callback)))
    }

    pub fn call(&self, data: &mut RenderContext) {
        (self.0.borrow_mut())(data)
    }
}

impl Clone for RenderCallback {
    fn clone(&self) -> Self {
        Self(self.0.clone())
    }
}

impl PartialEq for RenderCallback {
    fn eq(&self, _other: &Self) -> bool {
        true
    }
}

impl<H: FnMut(&mut RenderContext) + 'static> From<H> for RenderCallback {
    fn from(value: H) -> Self {
        RenderCallback::new(value)
    }
}

#[derive(PartialEq, Clone)]
pub struct PlotElement {
    pub layout: LayoutData,
    pub event_handlers: FxHashMap<EventName, EventHandlerType>,
    pub effect: Option<EffectData>,
    pub on_render: RenderCallback,
}

impl PlotElement {}

impl ElementExt for PlotElement {
    fn changed(&self, other: &Rc<dyn ElementExt>) -> bool {
        let Some(rect) = (other.as_ref() as &dyn Any).downcast_ref::<Self>() else {
            return false;
        };

        self != rect
    }

    fn diff(&self, other: &Rc<dyn ElementExt>) -> DiffModifies {
        let Some(rect) = (other.as_ref() as &dyn Any).downcast_ref::<Self>() else {
            return DiffModifies::all();
        };

        let mut diff = DiffModifies::empty();

        if self.effect != rect.effect {
            diff.insert(DiffModifies::EFFECT);
        }

        if !self.layout.layout.self_layout_eq(&rect.layout.layout) {
            diff.insert(DiffModifies::STYLE);
            diff.insert(DiffModifies::LAYOUT);
        }

        if !self.layout.layout.inner_layout_eq(&rect.layout.layout) {
            diff.insert(DiffModifies::STYLE);
            diff.insert(DiffModifies::INNER_LAYOUT);
        }

        if self.event_handlers != rect.event_handlers {
            diff.insert(DiffModifies::EVENT_HANDLERS);
        }

        if self.on_render != rect.on_render {
            diff.insert(DiffModifies::STYLE);
        }

        diff
    }

    fn layout(&'_ self) -> Cow<'_, LayoutData> {
        Cow::Borrowed(&self.layout)
    }

    fn effect(&'_ self) -> Option<Cow<'_, EffectData>> {
        self.effect.as_ref().map(Cow::Borrowed)
    }

    fn style(&'_ self) -> Cow<'_, StyleState> {
        Cow::Owned(StyleState::default())
    }

    fn text_style(&'_ self) -> Cow<'_, TextStyleData> {
        Cow::Owned(TextStyleData::default())
    }

    fn accessibility(&'_ self) -> Cow<'_, AccessibilityData> {
        Cow::Owned(AccessibilityData::default())
    }

    fn events_handlers(&'_ self) -> Option<Cow<'_, FxHashMap<EventName, EventHandlerType>>> {
        Some(Cow::Borrowed(&self.event_handlers))
    }

    fn clip(&self, context: ClipContext) {
        let area = context.visible_area;

        context.canvas.clip_rect(
            SkRect::new(area.min_x(), area.min_y(), area.max_x(), area.max_y()),
            ClipOp::Intersect,
            true,
        );
    }

    fn render(&self, mut context: RenderContext) {
        let style = self.style();
        let area = context.layout_node.area;

        let mut paint = Paint::default();
        paint.set_anti_alias(true);
        paint.set_style(PaintStyle::Fill);
        style.background.apply_to_paint(&mut paint, area);

        context.canvas.draw_rect(
            SkRect::new(area.min_x(), area.min_y(), area.max_x(), area.max_y()),
            &paint,
        );

        context
            .canvas
            .scale((context.scale_factor as f32, context.scale_factor as f32));
        context.canvas.translate((area.min_x(), area.min_y()));
        self.on_render.call(&mut context);
        context.canvas.restore();
    }
}

pub struct Plot {
    element: PlotElement,
    elements: Vec<Element>,
    key: DiffKey,
}

impl ChildrenExt for Plot {
    fn get_children(&mut self) -> &mut Vec<Element> {
        &mut self.elements
    }
}

impl KeyExt for Plot {
    fn write_key(&mut self) -> &mut DiffKey {
        &mut self.key
    }
}

impl EventHandlersExt for Plot {
    fn get_event_handlers(&mut self) -> &mut FxHashMap<EventName, EventHandlerType> {
        &mut self.element.event_handlers
    }
}

impl MaybeExt for Plot {}

impl From<Plot> for Element {
    fn from(value: Plot) -> Self {
        Element::Element {
            key: value.key,
            element: Rc::new(value.element),
            elements: value.elements,
        }
    }
}

pub fn plot(on_render: RenderCallback) -> Plot {
    Plot::new(on_render)
}

impl Plot {
    pub fn new(on_render: RenderCallback) -> Self {
        Self {
            element: PlotElement {
                on_render,
                layout: LayoutData::default(),
                event_handlers: HashMap::default(),
                effect: None,
            },
            elements: Vec::default(),
            key: DiffKey::None,
        }
    }

    pub fn try_downcast(element: &dyn ElementExt) -> Option<PlotElement> {
        (element as &dyn Any).downcast_ref::<PlotElement>().cloned()
    }
}

impl LayoutExt for Plot {
    fn get_layout(&mut self) -> &mut LayoutData {
        &mut self.element.layout
    }
}

impl ContainerExt for Plot {}

impl ContainerWithContentExt for Plot {}