maycoon_widgets/icon/
mod.rs

1use maycoon_core::app::info::AppInfo;
2use maycoon_core::app::update::Update;
3use maycoon_core::layout::{Dimension, LayoutNode, LayoutStyle, StyleNode};
4use maycoon_core::vg::kurbo::{Affine, Vec2};
5use maycoon_core::vg::Scene;
6use maycoon_core::widget::{Widget, WidgetLayoutExt};
7use maycoon_theme::id::WidgetId;
8use maycoon_theme::theme::Theme;
9use nalgebra::Vector2;
10use vello_svg::usvg;
11
12use crate::icon::svg::SvgIcon;
13use maycoon_core::app::context::AppContext;
14use maycoon_core::signal::MaybeSignal;
15pub use usvg::ImageRendering;
16pub use usvg::ShapeRendering;
17pub use usvg::TextRendering;
18
19/// Contains the [SvgIcon] struct for representing a rendered SVG Icon.
20pub mod svg;
21
22/// Error type for parsing SVGs with [usvg].
23pub type SvgError = usvg::Error;
24
25/// A simple icon widget to display SVG icons using [vello_svg] and [usvg].
26///
27/// ### Theming
28/// The widget itself only draws the underlying icon, so theming is useless.
29pub struct Icon {
30    layout_style: MaybeSignal<LayoutStyle>,
31    icon: MaybeSignal<SvgIcon>,
32}
33
34impl Icon {
35    /// Creates a new icon widget from the given svg icon.
36    pub fn new(icon: impl Into<MaybeSignal<SvgIcon>>) -> Self {
37        Self {
38            layout_style: LayoutStyle {
39                size: Vector2::new(Dimension::length(8.0), Dimension::length(8.0)),
40                ..Default::default()
41            }
42            .into(),
43            icon: icon.into(),
44        }
45    }
46}
47
48impl Widget for Icon {
49    fn render(
50        &mut self,
51        scene: &mut Scene,
52        _: &mut dyn Theme,
53        layout_node: &LayoutNode,
54        _: &AppInfo,
55        _: AppContext,
56    ) {
57        let icon = self.icon.get();
58
59        // The size is divided, as otherwise the icon would be either too large (with 1.0) or too tiny (with 0.1 somehow getting converted to 0.0)
60        let affine = Affine::scale_non_uniform(
61            layout_node.layout.size.width as f64 / 100.0,
62            layout_node.layout.size.height as f64 / 100.0,
63        )
64        .then_translate(Vec2::new(
65            layout_node.layout.location.x as f64,
66            layout_node.layout.location.y as f64,
67        ));
68
69        scene.append(icon.scene(), Some(affine));
70    }
71
72    fn layout_style(&self) -> StyleNode {
73        StyleNode {
74            style: self.layout_style.get().clone(),
75            children: Vec::new(),
76        }
77    }
78
79    fn update(&mut self, _: &LayoutNode, _: AppContext, _: &AppInfo) -> Update {
80        Update::empty()
81    }
82
83    fn widget_id(&self) -> WidgetId {
84        WidgetId::new("maycoon-widgets", "Icon")
85    }
86}
87
88impl WidgetLayoutExt for Icon {
89    fn set_layout_style(&mut self, layout_style: impl Into<MaybeSignal<LayoutStyle>>) {
90        self.layout_style = layout_style.into();
91    }
92}