99 lines
3.1 KiB
Rust
99 lines
3.1 KiB
Rust
use ratatui::{
|
||
layout::{Alignment, Constraint, Direction, Layout},
|
||
style::{Color, Modifier, Style},
|
||
text::{Line, Span},
|
||
widgets::{Block, Borders, Paragraph},
|
||
Frame,
|
||
};
|
||
|
||
use crate::tui::app::{App, LoginField};
|
||
|
||
pub fn draw(frame: &mut Frame, app: &App) {
|
||
let area = frame.size();
|
||
|
||
// Centre a 50×12 box on screen.
|
||
let v = Layout::default()
|
||
.direction(Direction::Vertical)
|
||
.constraints([
|
||
Constraint::Percentage(30),
|
||
Constraint::Length(14),
|
||
Constraint::Min(0),
|
||
])
|
||
.split(area);
|
||
|
||
let h = Layout::default()
|
||
.direction(Direction::Horizontal)
|
||
.constraints([
|
||
Constraint::Percentage(25),
|
||
Constraint::Percentage(50),
|
||
Constraint::Percentage(25),
|
||
])
|
||
.split(v[1]);
|
||
|
||
let box_area = h[1];
|
||
|
||
let mode_label = if app.login_register { "Register" } else { "Login" };
|
||
|
||
let outer = Block::default()
|
||
.title(format!(" BackChannel — {} ", mode_label))
|
||
.borders(Borders::ALL)
|
||
.style(Style::default().fg(Color::Cyan));
|
||
|
||
frame.render_widget(outer, box_area);
|
||
|
||
let inner = Layout::default()
|
||
.direction(Direction::Vertical)
|
||
.constraints([
|
||
Constraint::Length(1), // padding
|
||
Constraint::Length(3), // username
|
||
Constraint::Length(3), // password
|
||
Constraint::Length(1), // padding
|
||
Constraint::Length(1), // hint
|
||
Constraint::Length(1), // status
|
||
])
|
||
.margin(1)
|
||
.split(box_area);
|
||
|
||
// Username field
|
||
let username_style = if app.login_field == LoginField::Username {
|
||
Style::default().fg(Color::Yellow).add_modifier(Modifier::BOLD)
|
||
} else {
|
||
Style::default()
|
||
};
|
||
let username_widget = Paragraph::new(app.login_username.as_str())
|
||
.block(Block::default().borders(Borders::ALL).title(" Username "))
|
||
.style(username_style);
|
||
frame.render_widget(username_widget, inner[1]);
|
||
|
||
// Password field (mask with asterisks)
|
||
let password_style = if app.login_field == LoginField::Password {
|
||
Style::default().fg(Color::Yellow).add_modifier(Modifier::BOLD)
|
||
} else {
|
||
Style::default()
|
||
};
|
||
let masked: String = "*".repeat(app.login_password.len());
|
||
let password_widget = Paragraph::new(masked.as_str())
|
||
.block(Block::default().borders(Borders::ALL).title(" Password "))
|
||
.style(password_style);
|
||
frame.render_widget(password_widget, inner[2]);
|
||
|
||
// Hints
|
||
let hint = Paragraph::new(Line::from(vec![
|
||
Span::raw("Tab: switch field | Enter: submit | "),
|
||
Span::styled(
|
||
if app.login_register { "T: switch to Login" } else { "T: switch to Register" },
|
||
Style::default().fg(Color::DarkGray),
|
||
),
|
||
]))
|
||
.alignment(Alignment::Center);
|
||
frame.render_widget(hint, inner[4]);
|
||
|
||
// Status / error message
|
||
if let Some(msg) = &app.status_msg {
|
||
let status = Paragraph::new(msg.as_str())
|
||
.style(Style::default().fg(Color::Red))
|
||
.alignment(Alignment::Center);
|
||
frame.render_widget(status, inner[5]);
|
||
}
|
||
}
|