tui_lipan/widgets/button/
mod.rs1mod layout;
4mod node;
5mod reconcile;
6
7pub use layout::measure_button;
8pub use node::ButtonNode;
9pub use reconcile::reconcile_button;
10
11use std::sync::Arc;
12
13use crate::callback::{Callback, KeyHandler};
14use crate::core::element::{Element, ElementKind};
15use crate::core::event::MouseEvent;
16use crate::input::KeyBindings;
17use crate::style::{Align, BorderStyle, LayoutConstraints, Length, Padding, Style, StyleSlot};
18
19#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
21pub enum ButtonVariant {
22 #[default]
24 Bracket,
25 Filled,
27 Outlined,
29}
30
31#[derive(Clone)]
33pub struct Button {
34 pub label: Arc<str>,
36 pub icon: Option<Arc<str>>,
38 pub icon_style: Style,
40 pub icon_gap: u16,
42 pub shortcut: Option<Arc<str>>,
44 pub shortcut_style: Style,
46 pub shortcut_gap: u16,
48 pub style: Style,
50 pub hover_style: StyleSlot,
52 pub focus_style: StyleSlot,
54 pub align: Align,
57 pub width: Length,
60 pub height: Length,
63 pub variant: ButtonVariant,
65 pub border_style: BorderStyle,
68 pub hover_border_style: Option<BorderStyle>,
70 pub focus_border_style: Option<BorderStyle>,
72 pub padding: Padding,
75 pub disabled: bool,
77 pub disabled_style: Style,
79 pub on_click: Option<Callback<MouseEvent>>,
85 pub on_key: Option<KeyHandler>,
87 pub focusable: bool,
89 pub tab_stop: bool,
91 pub on_focus: Option<Callback<()>>,
93 pub on_blur: Option<Callback<()>>,
95}
96
97impl Button {
98 pub fn new(label: impl Into<Arc<str>>) -> Self {
100 Self {
101 label: label.into(),
102 icon: None,
103 icon_style: Style::default(),
104 icon_gap: 1,
105 shortcut: None,
106 shortcut_style: Style::default(),
107 shortcut_gap: 1,
108 style: Style::default(),
109 hover_style: StyleSlot::Inherit,
110 focus_style: StyleSlot::Inherit,
111 align: Align::Center,
112 width: Length::Auto,
113 height: Length::Auto,
114 variant: ButtonVariant::Bracket,
115 border_style: BorderStyle::Plain,
116 hover_border_style: None,
117 focus_border_style: None,
118 padding: Padding {
119 left: 1,
120 right: 1,
121 top: 0,
122 bottom: 0,
123 },
124 disabled: false,
125 disabled_style: Style::default(),
126 on_click: None,
127 on_key: None,
128 focusable: true,
129 tab_stop: true,
130 on_focus: None,
131 on_blur: None,
132 }
133 }
134
135 pub fn filled(label: impl Into<Arc<str>>) -> Self {
137 let mut button = Self::new(label);
138 button.variant = ButtonVariant::Filled;
139 button
140 }
141
142 pub fn outlined(label: impl Into<Arc<str>>) -> Self {
144 let mut button = Self::new(label);
145 button.variant = ButtonVariant::Outlined;
146 button.border_style = BorderStyle::Plain;
147 button.hover_border_style = None;
148 button.focus_border_style = None;
149 button
150 }
151
152 pub fn style(mut self, style: Style) -> Self {
154 self.style = style;
155 self
156 }
157
158 pub fn icon(mut self, icon: impl Into<Arc<str>>) -> Self {
160 self.icon = Some(icon.into());
161 self
162 }
163
164 pub fn icon_style(mut self, style: Style) -> Self {
166 self.icon_style = style;
167 self
168 }
169
170 pub fn icon_gap(mut self, gap: u16) -> Self {
172 self.icon_gap = gap;
173 self
174 }
175
176 pub fn shortcut(mut self, shortcut: impl Into<Arc<str>>) -> Self {
178 self.shortcut = Some(shortcut.into());
179 self
180 }
181
182 pub fn shortcut_bindings(mut self, bindings: KeyBindings) -> Self {
184 self.shortcut = Some(bindings.to_string().into());
185 self
186 }
187
188 pub fn shortcut_style(mut self, style: Style) -> Self {
190 self.shortcut_style = style;
191 self
192 }
193
194 pub fn shortcut_gap(mut self, gap: u16) -> Self {
196 self.shortcut_gap = gap;
197 self
198 }
199
200 pub fn hover_style(mut self, style: Style) -> Self {
202 self.hover_style = StyleSlot::Replace(style);
203 self
204 }
205
206 pub fn extend_hover_style(mut self, style: Style) -> Self {
208 self.hover_style = StyleSlot::Extend(style);
209 self
210 }
211
212 pub fn inherit_hover_style(mut self) -> Self {
214 self.hover_style = StyleSlot::Inherit;
215 self
216 }
217
218 pub fn hover_style_slot(mut self, slot: StyleSlot) -> Self {
220 self.hover_style = slot;
221 self
222 }
223
224 pub fn focus_style(mut self, style: Style) -> Self {
226 self.focus_style = StyleSlot::Replace(style);
227 self
228 }
229
230 pub fn extend_focus_style(mut self, style: Style) -> Self {
232 self.focus_style = StyleSlot::Extend(style);
233 self
234 }
235
236 pub fn inherit_focus_style(mut self) -> Self {
238 self.focus_style = StyleSlot::Inherit;
239 self
240 }
241
242 pub fn focus_style_slot(mut self, slot: StyleSlot) -> Self {
244 self.focus_style = slot;
245 self
246 }
247
248 pub fn align(mut self, align: Align) -> Self {
250 self.align = align;
251 self
252 }
253
254 pub fn width(mut self, width: Length) -> Self {
256 self.width = width;
257 self
258 }
259
260 pub fn height(mut self, height: Length) -> Self {
262 self.height = height;
263 self
264 }
265
266 pub fn variant(mut self, variant: ButtonVariant) -> Self {
268 self.variant = variant;
269 self
270 }
271
272 pub fn border_style(mut self, border_style: BorderStyle) -> Self {
274 self.border_style = border_style;
275 self
276 }
277
278 pub fn hover_border_style(mut self, border_style: Option<BorderStyle>) -> Self {
280 self.hover_border_style = border_style;
281 self
282 }
283
284 pub fn focus_border_style(mut self, border_style: Option<BorderStyle>) -> Self {
286 self.focus_border_style = border_style;
287 self
288 }
289
290 pub fn padding(mut self, padding: impl Into<Padding>) -> Self {
292 self.padding = padding.into();
293 self
294 }
295
296 pub fn disabled(mut self, disabled: bool) -> Self {
298 self.disabled = disabled;
299 self
300 }
301
302 pub fn disabled_style(mut self, style: Style) -> Self {
304 self.disabled_style = style;
305 self
306 }
307
308 pub fn full_width(mut self, full_width: bool) -> Self {
310 self.width = if full_width {
311 Length::Flex(1)
312 } else {
313 Length::Auto
314 };
315 self
316 }
317
318 pub fn on_click(mut self, cb: Callback<MouseEvent>) -> Self {
320 self.on_click = Some(cb);
321 self
322 }
323
324 pub fn on_key(mut self, handler: KeyHandler) -> Self {
326 self.on_key = Some(handler);
327 self
328 }
329
330 pub fn focusable(mut self, focusable: bool) -> Self {
332 self.focusable = focusable;
333 self
334 }
335
336 pub fn tab_stop(mut self, tab_stop: bool) -> Self {
338 self.tab_stop = tab_stop;
339 self
340 }
341
342 pub fn on_focus(mut self, cb: Callback<()>) -> Self {
344 self.on_focus = Some(cb);
345 self
346 }
347
348 pub fn on_blur(mut self, cb: Callback<()>) -> Self {
350 self.on_blur = Some(cb);
351 self
352 }
353}
354
355impl From<Button> for Element {
356 fn from(value: Button) -> Self {
357 let (min_w, min_h) = measure_button(&value);
358 Element::new(ElementKind::Button(Box::new(value))).with_layout(
359 LayoutConstraints::default()
360 .min_width(Length::Px(min_w))
361 .min_height(Length::Px(min_h)),
362 )
363 }
364}
365
366impl crate::layout::hash::LayoutHash for Button {
367 fn layout_hash(
368 &self,
369 hasher: &mut impl std::hash::Hasher,
370 _recurse: &dyn Fn(&Element) -> Option<u64>,
371 ) -> Option<()> {
372 use std::hash::Hash;
373 self.width.hash(hasher);
374 self.height.hash(hasher);
375 self.variant.hash(hasher);
376 self.padding.hash(hasher);
377 self.label.hash(hasher);
378 self.icon.hash(hasher);
379 self.icon_gap.hash(hasher);
380 self.shortcut.hash(hasher);
381 self.shortcut_gap.hash(hasher);
382 Some(())
383 }
384}