1use crate::prelude::*;
2use crate::ui::button::{events::EventData, Type};
3
4#[CustomControl(overwrite=OnPaint+OnDefaultAction+OnKeyPressed+OnMouseEvent, internal=true)]
5pub struct Button {
6 button_type: Type,
7 caption: Caption,
8 pressed: bool,
9}
10impl Button {
11 pub fn new(caption: &str, layout: Layout) -> Self {
21 Self::inner_create(caption, layout, button::Type::Normal, StatusFlags::ThemeType)
22 }
23 pub fn with_type(caption: &str, layout: Layout, button_type: Type) -> Self {
30 Self::inner_create(caption, layout, button_type, StatusFlags::None)
31 }
32
33 fn inner_create(caption: &str, layout: Layout, button_type: Type, status: StatusFlags) -> Self {
34 let mut but = Button {
35 base: ControlBase::with_status_flags(layout, StatusFlags::Visible | StatusFlags::Enabled | StatusFlags::AcceptInput | status),
36 caption: Caption::new(caption, ExtractHotKeyMethod::AltPlusKey),
37 button_type,
38 pressed: false,
39 };
40 but.update_bounds_limits();
41 let hotkey = but.caption.hotkey();
42 but.set_hotkey(hotkey);
43 but
44 }
45
46 fn update_bounds_limits(&mut self) {
47 let (min_w, min_h) = match self.button_type {
48 Type::Normal => (4, 2),
49 Type::Flat => (3, 1),
50 Type::Raised => (5, 3),
51 };
52 self.set_size_bounds(min_w, min_h, u16::MAX, min_h);
53 }
54
55 pub fn set_caption(&mut self, caption: &str) {
64 self.caption.set_text(caption, ExtractHotKeyMethod::AltPlusKey);
65 let hotkey = self.caption.hotkey();
66 self.set_hotkey(hotkey);
67 }
68 pub fn caption(&self) -> &str {
70 self.caption.text()
71 }
72
73 fn paint_normal(&self, surface: &mut Surface, theme: &Theme) {
74 let col_text = match () {
75 _ if !self.is_enabled() => theme.button.regular.text.inactive,
76 _ if self.has_focus() => theme.button.regular.text.focused,
77 _ if self.is_mouse_over() => theme.button.regular.text.hovered,
78 _ => theme.button.regular.text.normal,
79 };
80 let w = self.size().width.saturating_sub(1);
81 let x = (w / 2) as i32;
82 let mut format = TextFormatBuilder::new()
83 .position(x, 0)
84 .attribute(col_text)
85 .align(TextAlignment::Center)
86 .chars_count(self.caption.chars_count() as u16)
87 .wrap_type(WrapType::SingleLineWrap(w as u16))
88 .build();
89
90 if self.caption.has_hotkey() {
91 format.set_hotkey(
92 match () {
93 _ if !self.is_enabled() => theme.button.regular.hotkey.inactive,
94 _ if self.has_focus() => theme.button.regular.hotkey.focused,
95 _ if self.is_mouse_over() => theme.button.regular.hotkey.hovered,
96 _ => theme.button.regular.hotkey.normal,
97 },
98 self.caption.hotkey_pos().unwrap() as u32,
99 );
100 }
101 if self.pressed {
102 surface.fill_horizontal_line_with_size(1, 0, w, Character::with_attributes(' ', col_text));
103 format.x += 1;
104 surface.write_text(self.caption.text(), &format);
105 } else {
106 surface.fill_horizontal_line_with_size(0, 0, w, Character::with_attributes(' ', col_text));
107 surface.write_text(self.caption.text(), &format);
108 surface.fill_horizontal_line_with_size(
109 1,
110 1,
111 w,
112 Character::with_attributes(SpecialChar::BlockUpperHalf, theme.button.regular.shadow),
113 );
114 surface.write_char(
115 w as i32,
116 0,
117 Character::with_attributes(SpecialChar::BlockLowerHalf, theme.button.regular.shadow),
118 );
119 }
120 }
121 fn paint_flat(&self, surface: &mut Surface, theme: &Theme) {
122 let col_text = match () {
123 _ if !self.is_enabled() => theme.button.regular.text.inactive,
124 _ if self.has_focus() => theme.button.regular.text.focused,
125 _ if self.is_mouse_over() => theme.button.regular.text.hovered,
126 _ => theme.button.regular.text.normal,
127 };
128 let w = self.size().width;
129 let x = (w / 2) as i32;
130 let mut format = TextFormatBuilder::new()
131 .position(x, 0)
132 .attribute(col_text)
133 .align(TextAlignment::Center)
134 .chars_count(self.caption.chars_count() as u16)
135 .wrap_type(WrapType::SingleLineWrap(w as u16))
136 .build();
137
138 if self.caption.has_hotkey() {
139 format.set_hotkey(
140 match () {
141 _ if !self.is_enabled() => theme.button.regular.hotkey.inactive,
142 _ if self.has_focus() => theme.button.regular.hotkey.focused,
143 _ if self.is_mouse_over() => theme.button.regular.hotkey.hovered,
144 _ => theme.button.regular.hotkey.normal,
145 },
146 self.caption.hotkey_pos().unwrap() as u32,
147 );
148 }
149 surface.clear(Character::with_attributes(' ', col_text));
150 surface.write_text(self.caption.text(), &format);
151 }
152 fn paint_raised(&self, surface: &mut Surface, theme: &Theme) {
153 let enabled = self.is_enabled();
154 let focus = self.has_focus();
155 let col_text = match () {
156 _ if !enabled => theme.button.bevel.text.inactive,
157 _ if self.pressed => theme.button.bevel.text.pressed_or_selected,
158 _ if focus => theme.button.bevel.text.focused,
159 _ if self.is_mouse_over() => theme.button.bevel.text.hovered,
160 _ => theme.button.bevel.text.normal,
161 };
162 let w = self.size().width;
163 let x = (w / 2) as i32 + if self.pressed { 1 } else { 0 };
164 let mut format = TextFormatBuilder::new()
165 .position(x, 1)
166 .attribute(col_text)
167 .align(TextAlignment::Center)
168 .chars_count(self.caption.chars_count() as u16)
169 .wrap_type(WrapType::SingleLineWrap((w as u16).saturating_sub(2)))
170 .build();
171
172 if self.caption.has_hotkey() {
173 format.set_hotkey(
174 match () {
175 _ if !enabled => theme.button.bevel.hotkey.inactive,
176 _ if self.pressed => theme.button.bevel.hotkey.pressed_or_selected,
177 _ if focus => theme.button.bevel.hotkey.focused,
178 _ if self.is_mouse_over() => theme.button.bevel.hotkey.hovered,
179 _ => theme.button.bevel.hotkey.normal,
180 },
181 self.caption.hotkey_pos().unwrap() as u32,
182 );
183 }
184 surface.write_text(self.caption.text(), &format);
185 let r = Rect::with_point_and_size(Point::ORIGIN, self.size());
186 if enabled {
187 surface.draw_bevel_rect(
188 r,
189 LineType::SingleRound,
190 theme.button.bevel.dark_margin,
191 theme.button.bevel.light_margin,
192 !self.pressed,
193 );
194 } else {
195 surface.draw_rect(r, LineType::SingleRound, theme.button.bevel.text.inactive);
196 }
197 }
198}
199impl OnDefaultAction for Button {
200 fn on_default_action(&mut self) {
201 self.raise_event(ControlEvent {
202 emitter: self.handle,
203 receiver: self.event_processor,
204 data: ControlEventData::Button(EventData {}),
205 });
206 }
207}
208impl OnKeyPressed for Button {
209 fn on_key_pressed(&mut self, key: Key, _character: char) -> EventProcessStatus {
210 match key.value() {
211 key!("Space") | key!("Enter") => {
212 self.on_default_action();
213 EventProcessStatus::Processed
214 }
215 _ => EventProcessStatus::Ignored,
216 }
217 }
218}
219
220impl OnPaint for Button {
221 fn on_paint(&self, surface: &mut Surface, theme: &Theme) {
222 match self.button_type {
223 Type::Normal => self.paint_normal(surface, theme),
224 Type::Flat => self.paint_flat(surface, theme),
225 Type::Raised => self.paint_raised(surface, theme),
226 }
227 }
228}
229impl OnMouseEvent for Button {
230 fn on_mouse_event(&mut self, event: &MouseEvent) -> EventProcessStatus {
231 match event {
232 MouseEvent::Enter => {
233 if self.caption.chars_count() > (self.size().width - 2) as usize {
234 self.show_tooltip(self.caption.text());
235 }
236 EventProcessStatus::Processed
237 }
238 MouseEvent::Leave => EventProcessStatus::Processed,
239 MouseEvent::Released(data) => {
240 self.pressed = false;
241 if self.is_coord_in_control(data.x, data.y) {
242 self.on_default_action();
243 }
244 EventProcessStatus::Processed
245 }
246 MouseEvent::Drag(data) => {
247 if self.pressed && (!self.is_coord_in_control(data.x, data.y)) {
248 self.pressed = false;
249 return EventProcessStatus::Processed;
250 }
251 EventProcessStatus::Ignored
252 }
253 MouseEvent::Pressed(_) => {
254 self.pressed = true;
255 EventProcessStatus::Processed
256 }
257 _ => EventProcessStatus::Ignored,
258 }
259 }
260}