fresh/view/controls/button/
render.rs1use ratatui::layout::Rect;
4use ratatui::style::{Modifier, Style};
5use ratatui::text::{Line, Span};
6use ratatui::widgets::Paragraph;
7use ratatui::Frame;
8
9use super::{ButtonColors, ButtonLayout, ButtonState, FocusState};
10
11pub fn render_button(
22 frame: &mut Frame,
23 area: Rect,
24 state: &ButtonState,
25 colors: &ButtonColors,
26) -> ButtonLayout {
27 if area.height == 0 || area.width < 4 {
28 return ButtonLayout::default();
29 }
30
31 let (text_color, border_color, bg_color) = match state.focus {
32 FocusState::Normal => {
33 if state.pressed {
34 (colors.text, colors.border, Some(colors.pressed_bg))
35 } else {
36 (colors.text, colors.border, None)
37 }
38 }
39 FocusState::Focused => {
40 if state.pressed {
41 (colors.text, colors.focused, Some(colors.pressed_bg))
42 } else {
43 (colors.focused, colors.focused, None)
44 }
45 }
46 FocusState::Hovered => {
47 (colors.hovered, colors.hovered, None)
49 }
50 FocusState::Disabled => (colors.disabled, colors.disabled, None),
51 };
52
53 let button_width = (state.label.len() + 4) as u16;
55 let actual_width = button_width.min(area.width);
56
57 let max_label_len = actual_width.saturating_sub(4) as usize;
59 let display_label: String = state.label.chars().take(max_label_len).collect();
60
61 let mut style = Style::default().fg(text_color);
62 if let Some(bg) = bg_color {
63 style = style.bg(bg);
64 }
65 if state.focus == FocusState::Focused {
66 style = style.add_modifier(Modifier::BOLD);
67 }
68
69 let line = Line::from(vec![
70 Span::styled("[", Style::default().fg(border_color)),
71 Span::raw(" "),
72 Span::styled(&display_label, style),
73 Span::raw(" "),
74 Span::styled("]", Style::default().fg(border_color)),
75 ]);
76
77 let button_area = Rect::new(area.x, area.y, actual_width, 1);
78 let paragraph = Paragraph::new(line);
79 frame.render_widget(paragraph, button_area);
80
81 ButtonLayout { button_area }
82}
83
84pub fn render_button_row(
95 frame: &mut Frame,
96 area: Rect,
97 buttons: &[(&ButtonState, &ButtonColors)],
98 gap: u16,
99) -> Vec<ButtonLayout> {
100 if buttons.is_empty() || area.height == 0 {
101 return Vec::new();
102 }
103
104 let mut layouts = Vec::with_capacity(buttons.len());
105 let mut x = area.x;
106
107 for (state, colors) in buttons {
108 let button_width = (state.label.len() + 4) as u16;
109 if x + button_width > area.x + area.width {
110 break;
111 }
112
113 let button_area = Rect::new(x, area.y, button_width, 1);
114 let layout = render_button(frame, button_area, state, colors);
115 layouts.push(layout);
116
117 x += button_width + gap;
118 }
119
120 layouts
121}