This commit is contained in:
Alex Emmet 2026-04-04 21:43:54 +02:00
commit 71ac8d97fa
12 changed files with 445 additions and 445 deletions

View file

@ -13,6 +13,8 @@ pub mod screens {
}
pub mod util {
pub mod borders;
pub mod buttons;
pub mod terms_focus;
}
pub mod app_state;
pub mod input_handler;

View file

@ -0,0 +1,175 @@
use ratatui::{
layout::{Alignment, Rect},
style::{Color, Modifier, Style},
text::{Line, Span},
widgets::{Block, Borders, Paragraph},
};
use crate::util::terms_focus::Focus;
#[allow(mismatched_lifetime_syntaxes)]
pub fn checkbox(label: &str, checked: bool, active: bool, allowed: bool) -> Line {
let box_char = if checked { "[x]" } else { "[ ]" };
let (box_style, text_style) = if active {
if allowed {
(
Style::default()
.fg(Color::Yellow)
.add_modifier(Modifier::BOLD),
Style::default()
.fg(Color::Yellow)
.add_modifier(Modifier::BOLD),
)
} else {
(
Style::default().fg(Color::Gray),
Style::default().fg(Color::Red).add_modifier(Modifier::BOLD),
)
}
} else {
(Style::default(), Style::default())
};
Line::from(vec![
Span::styled(box_char, box_style),
Span::raw(" "),
Span::styled(label, text_style),
])
}
pub fn draw_button(f: &mut ratatui::Frame, area: Rect, label: &str, style: Style) {
let p = Paragraph::new(Span::styled(label, style))
.alignment(Alignment::Center)
.block(Block::default().borders(Borders::ALL));
f.render_widget(p, area);
}
pub fn draw_buttons(
f: &mut ratatui::Frame,
area: Rect,
current_focus: Focus,
state: (bool, bool),
update_needed: bool,
downgrade_scenario: bool,
tos_or_privacy: bool,
) {
let cancel_text = if update_needed {
"[Q] Quit"
} else {
"[Q] Not now"
};
let continue_text = if downgrade_scenario {
"Downgrade"
} else {
"Continue"
};
let mut buttons = vec![
(cancel_text, Focus::Cancel),
(continue_text, Focus::Continue),
];
if tos_or_privacy {
buttons.push(("Continue with Tensamin Services", Focus::ContinueAll));
}
let padding = 2;
let min_widths: Vec<u16> = buttons
.iter()
.map(|(label, _)| label.len() as u16 + padding)
.collect();
let widths = compute_widths(area.width, &min_widths);
let mut x = area.x;
for ((label, focus), width) in buttons.iter().zip(widths) {
let chunk = Rect {
x,
y: area.y,
width,
height: area.height,
};
x += width;
let is_focused = current_focus == *focus;
let style = match focus {
Focus::Cancel => {
if is_focused {
Style::default()
.fg(Color::Black)
.bg(Color::Red)
.add_modifier(Modifier::BOLD)
} else {
Style::default().fg(Color::Red)
}
}
Focus::Continue => {
if is_focused && state.0 {
Style::default()
.fg(Color::Black)
.bg(Color::Green)
.add_modifier(Modifier::BOLD)
} else if state.0 {
Style::default().fg(Color::Green)
} else {
Style::default().fg(Color::DarkGray)
}
}
Focus::ContinueAll => {
if is_focused && state.1 {
Style::default()
.fg(Color::Black)
.bg(Color::Green)
.add_modifier(Modifier::BOLD)
} else if state.1 {
Style::default().fg(Color::Green)
} else {
Style::default().fg(Color::DarkGray)
}
}
_ => Style::default().fg(Color::DarkGray),
};
draw_button(f, chunk, label, style);
}
}
pub fn compute_widths(area_width: u16, min_widths: &[u16]) -> Vec<u16> {
let mut widths = vec![0; min_widths.len()];
let mut remaining: Vec<usize> = (0..min_widths.len()).collect();
let mut remaining_width = area_width;
while !remaining.is_empty() {
let count = remaining.len() as u16;
let equal = remaining_width / count;
let mut clamped = Vec::new();
for &i in &remaining {
if min_widths[i] > equal {
widths[i] = min_widths[i];
remaining_width -= min_widths[i];
clamped.push(i);
}
}
if clamped.is_empty() {
let mut remainder = remaining_width % count;
for &i in &remaining {
widths[i] = equal
+ if remainder > 0 {
remainder -= 1;
1
} else {
0
};
}
break;
}
remaining.retain(|i| !clamped.contains(i));
}
widths
}

View file

@ -0,0 +1,25 @@
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Focus {
Eula,
Tos,
Pp,
Cancel,
Continue,
ContinueAll,
}
impl Focus {
pub fn next(&mut self, order: &[Focus]) {
if let Some(pos) = order.iter().position(|&f| f == *self) {
let next_pos = (pos + 1) % order.len();
*self = order[next_pos];
}
}
pub fn prev(&mut self, order: &[Focus]) {
if let Some(pos) = order.iter().position(|&f| f == *self) {
let prev_pos = if pos == 0 { order.len() - 1 } else { pos - 1 };
*self = order[prev_pos];
}
}
}