pixel_widgets/widget/
toggle.rs1use 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#[allow(missing_docs)]
14pub enum State {
15 Idle,
16 Hover,
17 Pressed,
18 Disabled,
19}
20
21pub 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 pub fn new<C: IntoNode<'a, T> + 'a>(checked: bool, on_toggle: F) -> Self {
30 Self { checked, on_toggle }
31 }
32
33 pub fn val(mut self, checked: bool) -> Self {
35 self.checked = checked;
36 self
37 }
38
39 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}