vizia_core/views/
badge.rs

1use crate::prelude::*;
2
3/// Enum which represents the placement of a badge on its parent.
4#[derive(Default, Debug, Clone, Copy, Data, PartialEq)]
5pub enum BadgePlacement {
6    /// The badge should be placed at the top-left of the view.
7    TopLeft,
8    /// The badge should be placed at the top of the view.
9    Top,
10    /// The badge should be placed at the top-right of the view.
11    #[default]
12    TopRight,
13    /// The badge should be placed at the left of the view.
14    Left,
15    /// The badge should be placed at the right of the view.
16    Right,
17    /// The badge should be placed at the bottom-left of the view.
18    BottomLeft,
19    /// The badge should be placed at the bottom of the view.
20    Bottom,
21    /// The badge should be placed at the bottom-right of the view.
22    BottomRight,
23}
24
25impl_res_simple!(BadgePlacement);
26
27/// A Badge view for showing notifications, counts, or status information.
28#[derive(Lens)]
29pub struct Badge {
30    placement: Option<BadgePlacement>,
31}
32
33impl Badge {
34    fn common<F>(cx: &mut Context, content: F) -> Handle<Self>
35    where
36        F: FnOnce(&mut Context),
37    {
38        Self { placement: None }.build(cx, content).bind(
39            Badge::placement,
40            |mut handle, placement| {
41                if let Some(placement) = placement.get(&handle) {
42                    let (t, b) = match placement {
43                        BadgePlacement::TopLeft | BadgePlacement::TopRight => {
44                            (Stretch(1.0), Percentage(85.35))
45                        }
46                        BadgePlacement::Top => (Stretch(1.0), Percentage(100.0)),
47                        BadgePlacement::Bottom => (Percentage(100.0), Stretch(1.0)),
48                        BadgePlacement::BottomLeft | BadgePlacement::BottomRight => {
49                            (Percentage(85.35), Stretch(1.0))
50                        }
51
52                        BadgePlacement::Left | BadgePlacement::Right => {
53                            (Stretch(1.0), Stretch(1.0))
54                        }
55                    };
56
57                    let (l, r) = match placement {
58                        BadgePlacement::TopLeft | BadgePlacement::BottomLeft => {
59                            (Stretch(1.0), Percentage(85.35))
60                        }
61                        BadgePlacement::TopRight | BadgePlacement::BottomRight => {
62                            (Percentage(85.35), Stretch(1.0))
63                        }
64                        BadgePlacement::Left => (Stretch(1.0), Percentage(100.0)),
65                        BadgePlacement::Right => (Percentage(100.0), Stretch(1.0)),
66                        BadgePlacement::Top | BadgePlacement::Bottom => {
67                            (Stretch(1.0), Stretch(1.0))
68                        }
69                    };
70
71                    handle = handle.top(t).bottom(b).left(l).right(r);
72
73                    let translate = match placement {
74                        BadgePlacement::TopLeft => (Percentage(50.0), Percentage(50.0)),
75                        BadgePlacement::Top => (Percentage(0.0), Percentage(50.0)),
76                        BadgePlacement::TopRight => (Percentage(-50.0), Percentage(50.0)),
77                        BadgePlacement::BottomLeft => (Percentage(50.0), Percentage(-50.0)),
78                        BadgePlacement::Bottom => (Percentage(0.0), Percentage(-50.0)),
79                        BadgePlacement::BottomRight => (Percentage(-50.0), Percentage(-50.0)),
80                        BadgePlacement::Left => (Percentage(50.0), Percentage(0.0)),
81                        BadgePlacement::Right => (Percentage(-50.0), Percentage(0.0)),
82                    };
83                    handle.translate(translate);
84                }
85            },
86        )
87    }
88
89    /// Creates an empty badge.
90    ///
91    /// ```
92    /// # use vizia_core::prelude::*;
93    /// # let cx = &mut Context::default();
94    /// Avatar::new(cx, |cx|{
95    ///     Svg::new(cx, ICON_USER);
96    /// })
97    /// .badge(|cx| Badge::empty(cx).class("error"));
98    /// ```
99    pub fn empty(cx: &mut Context) -> Handle<Self> {
100        Self::common(cx, |_| {})
101    }
102
103    /// Creates a new badge with the provided content.
104    ///
105    /// ```
106    /// # use vizia_core::prelude::*;
107    /// # let cx = &mut Context::default();
108    /// Avatar::new(cx, |cx|{
109    ///     Svg::new(cx, ICON_USER);
110    /// })
111    /// .badge(|cx| Badge::new(|cx| Label::new("2")));
112    /// ```
113    pub fn new<F, V>(cx: &mut Context, content: F) -> Handle<Self>
114    where
115        F: FnOnce(&mut Context) -> Handle<V>,
116        V: View,
117    {
118        Self::common(cx, |cx| {
119            (content)(cx);
120        })
121    }
122}
123
124impl View for Badge {
125    fn element(&self) -> Option<&'static str> {
126        Some("badge")
127    }
128}
129
130impl Handle<'_, Badge> {
131    /// Sets the placement of a badge relative to its parent. Accepts a value of, or lens to, a [BadgePlacement].
132    pub fn placement<U: Into<BadgePlacement>>(self, placement: impl Res<U>) -> Self {
133        self.bind(placement, |handle, val| {
134            let placement = val.get(&handle).into();
135            handle.modify(|badge| badge.placement = Some(placement));
136        })
137    }
138}