pixel_widgets/widget/
button.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
12pub struct Button<'a, T> {
14 content: Option<Node<'a, T>>,
15 on_clicked: Option<T>,
16}
17
18#[allow(missing_docs)]
20pub enum State {
21 Idle,
22 Hover,
23 Pressed,
24 Disabled,
25}
26
27impl<'a, T: 'a> Default for Button<'a, T> {
28 fn default() -> Self {
29 Self {
30 content: None,
31 on_clicked: None,
32 }
33 }
34}
35
36impl<'a, T: 'a> Button<'a, T> {
37 pub fn new<C: IntoNode<'a, T> + 'a>(content: C) -> Self {
39 Self {
40 content: Some(content.into_node()),
41 on_clicked: None,
42 }
43 }
44
45 pub fn on_clicked(mut self, message: T) -> Self {
47 self.on_clicked = Some(message);
48 self
49 }
50
51 pub fn text(mut self, text: impl Into<String> + 'a) -> Self {
53 self.content = Some(text.into_node());
54 self
55 }
56
57 pub fn extend<I: IntoIterator<Item = N>, N: IntoNode<'a, T>>(mut self, iter: I) -> Self {
60 if self.content.is_none() {
61 self.content = iter.into_iter().next().map(IntoNode::into_node);
62 }
63 self
64 }
65
66 fn content(&self) -> &Node<'a, T> {
67 self.content.as_ref().expect("content of `Button` must be set")
68 }
69
70 fn content_mut(&mut self) -> &mut Node<'a, T> {
71 self.content.as_mut().expect("content of `Button` must be set")
72 }
73}
74
75impl<'a, T: 'a + Send> Widget<'a, T> for Button<'a, T> {
76 type State = State;
77
78 fn mount(&self) -> State {
79 State::Idle
80 }
81
82 fn widget(&self) -> &'static str {
83 "button"
84 }
85
86 fn state(&self, state: &State) -> StateVec {
87 match state {
88 State::Idle => StateVec::new(),
89 State::Hover => smallvec![StyleState::Hover],
90 State::Pressed => smallvec![StyleState::Pressed],
91 State::Disabled => smallvec![StyleState::Disabled],
92 }
93 }
94
95 fn len(&self) -> usize {
96 1
97 }
98
99 fn visit_children(&mut self, visitor: &mut dyn FnMut(&mut dyn GenericNode<'a, T>)) {
100 visitor(&mut **self.content_mut());
101 }
102
103 fn size(&self, _: &State, style: &Stylesheet) -> (Size, Size) {
104 style
105 .background
106 .resolve_size((style.width, style.height), self.content().size(), style.padding)
107 }
108
109 fn event(
110 &mut self,
111 state: &mut State,
112 layout: Rectangle,
113 clip: Rectangle,
114 _: &Stylesheet,
115 event: Event,
116 context: &mut Context<T>,
117 ) {
118 match event {
119 Event::Cursor(x, y) => {
120 *state = match replace(state, State::Idle) {
121 State::Idle => {
122 if layout.point_inside(x, y) && clip.point_inside(x, y) {
123 context.redraw();
124 State::Hover
125 } else {
126 State::Idle
127 }
128 }
129 State::Hover => {
130 if layout.point_inside(x, y) && clip.point_inside(x, y) {
131 State::Hover
132 } else {
133 context.redraw();
134 State::Idle
135 }
136 }
137 State::Pressed => {
138 if layout.point_inside(x, y) && clip.point_inside(x, y) {
139 State::Pressed
140 } else {
141 context.redraw();
142 State::Idle
143 }
144 }
145 State::Disabled => State::Disabled,
146 };
147 }
148
149 Event::Press(Key::LeftMouseButton) => {
150 *state = match replace(state, State::Idle) {
151 State::Hover => {
152 context.redraw();
153 State::Pressed
154 }
155 other => other,
156 };
157 }
158
159 Event::Release(Key::LeftMouseButton) => {
160 *state = match replace(state, State::Idle) {
161 State::Pressed => {
162 context.redraw();
163 context.extend(self.on_clicked.take());
164 State::Hover
165 }
166 other => other,
167 };
168 }
169
170 _ => (),
171 }
172 }
173
174 fn draw(&mut self, _: &mut State, layout: Rectangle, clip: Rectangle, style: &Stylesheet) -> Vec<Primitive<'a>> {
175 let content_rect = style.background.content_rect(layout, style.padding);
176
177 style
178 .background
179 .render(layout)
180 .into_iter()
181 .chain(self.content_mut().draw(content_rect, clip).into_iter())
182 .collect()
183 }
184}
185
186impl<'a, T: 'a + Send> IntoNode<'a, T> for Button<'a, T> {
187 fn into_node(self) -> Node<'a, T> {
188 Node::from_widget(self)
189 }
190}
191
192impl Default for State {
193 fn default() -> Self {
194 State::Idle
195 }
196}