druid_material_icons/
lib.rs

1//! This library includes icons from Google's [material design icons
2//! repository](https://github.com/google/material-design-icons).
3
4#[cfg(feature = "druid")]
5use druid::{
6    kurbo::{Affine, PathEl, Point, Rect, Shape, Size},
7    widget::prelude::*,
8    Color, Data,
9};
10#[cfg(not(feature = "druid"))]
11use kurbo::{PathEl, Point, Rect, Shape, Size};
12
13/// A widget that displays a material icon. Use constraints to set the preferred size.
14///
15/// # Examples
16///
17/// ```
18/// # use druid::{Widget, Data, Color, WidgetExt};
19/// # #[derive(Data, Clone)]
20/// # struct MyData;
21/// use druid_material_icons as icons;
22/// fn build_ui() -> impl Widget<MyData> {
23///     icons::normal::content::ADD.new(Color::BLACK).fix_width(12.0).center()
24/// }
25/// ```
26#[derive(Debug, Clone)]
27#[cfg(feature = "druid")]
28pub struct Icon {
29    paths: IconPaths,
30    color: Color,
31}
32
33#[cfg(feature = "druid")]
34impl Icon {
35    #[inline]
36    pub fn new(paths: IconPaths, color: Color) -> Self {
37        Self { paths, color }
38    }
39}
40
41#[cfg(feature = "druid")]
42impl<T: Data> Widget<T> for Icon {
43    fn event(&mut self, _ctx: &mut EventCtx, _event: &Event, _data: &mut T, _env: &Env) {
44        // no events
45    }
46    fn lifecycle(&mut self, _ctx: &mut LifeCycleCtx, _event: &LifeCycle, _data: &T, _env: &Env) {
47        // no lifecycle
48    }
49    fn update(&mut self, _ctx: &mut UpdateCtx, _old_data: &T, _data: &T, _env: &Env) {
50        // no update
51    }
52    fn layout(&mut self, _ctx: &mut LayoutCtx, bc: &BoxConstraints, _data: &T, _env: &Env) -> Size {
53        let Size { width, height } = self.paths.size;
54        bc.constrain_aspect_ratio(height / width, width)
55    }
56    fn paint(&mut self, ctx: &mut PaintCtx, _data: &T, _env: &Env) {
57        let Size { width, height } = ctx.size();
58        let Size {
59            width: icon_width,
60            height: icon_height,
61        } = self.paths.size;
62        ctx.transform(Affine::scale_non_uniform(
63            width * icon_width.recip(),
64            height * icon_height.recip(),
65        ));
66        // TODO This makes slightly more brushes than it needs to. Probably not an issue.
67        for shape in self.paths.paths {
68            let color = self.color.clone();
69            let (_, _, _, alpha) = color.as_rgba();
70            let color = color.with_alpha(alpha * shape.opacity);
71            let brush = ctx.solid_brush(color);
72            ctx.fill(shape, &brush);
73        }
74    }
75}
76
77/// Factories for creating material icons for druid.
78///
79/// # Examples
80///
81/// ```
82/// # use druid::{Widget, Data, Color, WidgetExt};
83/// # #[derive(Data, Clone)]
84/// # struct MyData;
85/// use druid_material_icons as icons;
86/// fn build_ui() -> impl Widget<MyData> {
87///     icons::normal::content::ADD.new(Color::BLACK).fix_width(12.0).center()
88/// }
89/// ```
90#[derive(Debug, Copy, Clone)]
91pub struct IconPaths {
92    pub paths: &'static [IconPath],
93    pub size: Size,
94}
95
96#[cfg(feature = "druid")]
97impl IconPaths {
98    pub fn new(self, color: Color) -> Icon {
99        Icon::new(self, color)
100    }
101}
102
103#[derive(Debug, Copy, Clone)]
104pub struct IconPath {
105    pub els: &'static [PathEl],
106    pub opacity: f64,
107}
108
109impl Shape for IconPath {
110    type PathElementsIter<'a> = std::iter::Copied<std::slice::Iter<'static, PathEl>>;
111    fn path_elements(&self, _tolerance: f64) -> Self::PathElementsIter<'_> {
112        self.els.iter().copied()
113    }
114
115    fn area(&self) -> f64 {
116        self.els.area()
117    }
118
119    fn perimeter(&self, accuracy: f64) -> f64 {
120        self.els.perimeter(accuracy)
121    }
122
123    fn winding(&self, pt: Point) -> i32 {
124        self.els.winding(pt)
125    }
126    fn bounding_box(&self) -> Rect {
127        self.els.bounding_box()
128    }
129
130    fn as_path_slice(&self) -> Option<&[PathEl]> {
131        Some(self.els)
132    }
133}
134
135include!("./icons.rs.in");