pixel_widgets/widget/
toggle.rs

1use std::mem::replace;
2
3use smallvec::smallvec;
4
5use crate::draw::*;
6use crate::event::{Event, Key};
7use crate::layout::{Rectangle, Size};
8use crate::node::{GenericNode, IntoNode, Node};
9use crate::style::{StyleState, Stylesheet};
10use crate::widget::{Context, StateVec, Widget};
11
12/// State for [`Toggle`](struct.Toggle.html)
13#[allow(missing_docs)]
14pub enum State {
15    Idle,
16    Hover,
17    Pressed,
18    Disabled,
19}
20
21/// A clickable button that toggles some `bool`.
22pub struct Toggle<T, F: Fn(bool) -> T> {
23    checked: bool,
24    on_toggle: F,
25}
26
27impl<'a, T: 'a, F: 'a + Fn(bool) -> T> Toggle<T, F> {
28    /// Constructs a new `Toggle`
29    pub fn new<C: IntoNode<'a, T> + 'a>(checked: bool, on_toggle: F) -> Self {
30        Self { checked, on_toggle }
31    }
32
33    /// Sets the current toggle state of the `Toggle`.
34    pub fn val(mut self, checked: bool) -> Self {
35        self.checked = checked;
36        self
37    }
38
39    /// Sets the on_toggle callback for this `Toggle`, which is called when the toggle state changes.
40    pub fn on_toggle<N: Fn(bool) -> T>(self, on_toggle: N) -> Toggle<T, N> {
41        Toggle {
42            checked: self.checked,
43            on_toggle,
44        }
45    }
46}
47
48impl<'a, T: 'a> Default for Toggle<T, fn(bool) -> T> {
49    fn default() -> Self {
50        Self {
51            checked: false,
52            on_toggle: |_| panic!("on_toggle of `Toggle` must be set"),
53        }
54    }
55}
56
57impl<'a, T, F: Send + Fn(bool) -> T> Widget<'a, T> for Toggle<T, F> {
58    type State = State;
59
60    fn mount(&self) -> Self::State {
61        State::Idle
62    }
63
64    fn widget(&self) -> &'static str {
65        "toggle"
66    }
67
68    fn state(&self, state: &State) -> StateVec {
69        let mut state = match state {
70            State::Idle => StateVec::new(),
71            State::Hover => smallvec![StyleState::Hover],
72            State::Pressed => smallvec![StyleState::Pressed],
73            State::Disabled => smallvec![StyleState::Disabled],
74        };
75
76        if self.checked {
77            state.push(StyleState::Checked);
78        }
79
80        state
81    }
82
83    fn len(&self) -> usize {
84        0
85    }
86
87    fn visit_children(&mut self, _: &mut dyn FnMut(&mut dyn GenericNode<'a, T>)) {}
88
89    fn size(&self, _: &State, stylesheet: &Stylesheet) -> (Size, Size) {
90        match stylesheet.background {
91            Background::Patch(ref patch, _) => {
92                let size = patch.minimum_size();
93                (Size::Exact(size.0), Size::Exact(size.1))
94            }
95            Background::Image(ref image, _) => (Size::Exact(image.size.width()), Size::Exact(image.size.height())),
96            _ => (stylesheet.width, stylesheet.height),
97        }
98    }
99
100    fn event(
101        &mut self,
102        state: &mut State,
103        layout: Rectangle,
104        clip: Rectangle,
105        _: &Stylesheet,
106        event: Event,
107        context: &mut Context<T>,
108    ) {
109        match event {
110            Event::Cursor(x, y) => {
111                *state = match replace(state, State::Idle) {
112                    State::Idle => {
113                        if layout.point_inside(x, y) && clip.point_inside(x, y) {
114                            context.redraw();
115                            State::Hover
116                        } else {
117                            State::Idle
118                        }
119                    }
120                    State::Hover => {
121                        if layout.point_inside(x, y) && clip.point_inside(x, y) {
122                            State::Hover
123                        } else {
124                            context.redraw();
125                            State::Idle
126                        }
127                    }
128                    State::Pressed => {
129                        if layout.point_inside(x, y) && clip.point_inside(x, y) {
130                            State::Pressed
131                        } else {
132                            context.redraw();
133                            State::Idle
134                        }
135                    }
136                    State::Disabled => State::Disabled,
137                };
138            }
139
140            Event::Press(Key::LeftMouseButton) => {
141                *state = match replace(state, State::Idle) {
142                    State::Hover => {
143                        context.redraw();
144                        State::Pressed
145                    }
146                    other => other,
147                };
148            }
149
150            Event::Release(Key::LeftMouseButton) => {
151                *state = match replace(state, State::Idle) {
152                    State::Pressed => {
153                        context.redraw();
154                        context.push((self.on_toggle)(!self.checked));
155                        State::Hover
156                    }
157                    other => other,
158                };
159            }
160
161            _ => (),
162        }
163    }
164
165    fn draw(&mut self, _: &mut State, layout: Rectangle, _: Rectangle, stylesheet: &Stylesheet) -> Vec<Primitive<'a>> {
166        stylesheet.background.render(layout).into_iter().collect()
167    }
168}
169
170impl<'a, T: 'a + Send, F: 'a + Send + Fn(bool) -> T> IntoNode<'a, T> for Toggle<T, F> {
171    fn into_node(self) -> Node<'a, T> {
172        Node::from_widget(self)
173    }
174}