Skip to main content

fresh/view/controls/toggle/
render.rs

1//! Toggle rendering functions
2
3use ratatui::layout::Rect;
4use ratatui::style::Style;
5use ratatui::text::{Line, Span};
6use ratatui::widgets::Paragraph;
7use ratatui::Frame;
8
9use super::{FocusState, ToggleColors, ToggleLayout, ToggleState};
10
11/// Render a toggle control
12///
13/// # Arguments
14/// * `frame` - The ratatui frame to render to
15/// * `area` - Rectangle where the toggle should be rendered
16/// * `state` - The toggle state
17/// * `colors` - Colors for rendering
18///
19/// # Returns
20/// Layout information for hit testing
21pub fn render_toggle(
22    frame: &mut Frame,
23    area: Rect,
24    state: &ToggleState,
25    colors: &ToggleColors,
26) -> ToggleLayout {
27    render_toggle_aligned(frame, area, state, colors, None)
28}
29
30/// Render a toggle control with optional label width alignment
31///
32/// # Arguments
33/// * `frame` - The ratatui frame to render to
34/// * `area` - Rectangle where the toggle should be rendered
35/// * `state` - The toggle state
36/// * `colors` - Colors for rendering
37/// * `label_width` - Optional minimum label width for alignment
38///
39/// # Returns
40/// Layout information for hit testing
41pub fn render_toggle_aligned(
42    frame: &mut Frame,
43    area: Rect,
44    state: &ToggleState,
45    colors: &ToggleColors,
46    label_width: Option<u16>,
47) -> ToggleLayout {
48    if area.height == 0 || area.width < 4 {
49        return ToggleLayout {
50            checkbox_area: Rect::default(),
51            full_area: area,
52        };
53    }
54
55    // Use focused_fg for text when focused (not focused, which is the bg color)
56    let (bracket_color, _check_color, label_color) = match state.focus {
57        FocusState::Normal => (colors.bracket, colors.checkmark, colors.label),
58        FocusState::Focused => (colors.focused_fg, colors.checkmark, colors.focused_fg),
59        FocusState::Hovered => (colors.focused_fg, colors.checkmark, colors.focused_fg),
60        FocusState::Disabled => (colors.disabled, colors.disabled, colors.disabled),
61    };
62
63    // Format: "Label: [✓]" with optional padding
64    let actual_label_width = label_width.unwrap_or(state.label.len() as u16);
65    let padded_label = format!(
66        "{:width$}",
67        state.label,
68        width = actual_label_width as usize
69    );
70
71    let line = if state.checked {
72        Line::from(vec![
73            Span::styled(padded_label, Style::default().fg(label_color)),
74            Span::styled(": ", Style::default().fg(label_color)),
75            Span::styled("[", Style::default().fg(bracket_color)),
76            Span::styled("✓", Style::default().fg(_check_color)),
77            Span::styled("]", Style::default().fg(bracket_color)),
78        ])
79    } else {
80        Line::from(vec![
81            Span::styled(padded_label, Style::default().fg(label_color)),
82            Span::styled(": ", Style::default().fg(label_color)),
83            Span::styled("[ ]", Style::default().fg(bracket_color)),
84        ])
85    };
86
87    let paragraph = Paragraph::new(line);
88    frame.render_widget(paragraph, area);
89
90    // Checkbox position after label
91    let checkbox_start = area.x + actual_label_width + 2; // label + ": "
92    let checkbox_area = Rect::new(checkbox_start, area.y, 3.min(area.width), 1);
93
94    // Full area is label + ": " + checkbox
95    let full_width = (actual_label_width + 2 + 3).min(area.width);
96    let full_area = Rect::new(area.x, area.y, full_width, 1);
97
98    ToggleLayout {
99        checkbox_area,
100        full_area,
101    }
102}