Files
psg-backchannel/backchannel-client/src/tui/views/login.rs
2026-02-19 13:51:07 +08:00

99 lines
3.1 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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]);
}
}