192 lines
5.6 KiB
Rust
192 lines
5.6 KiB
Rust
use std::{
|
|
io,
|
|
marker::PhantomData,
|
|
time::{Duration, Instant},
|
|
};
|
|
|
|
use crossterm::event::{self, Event, KeyCode, KeyEvent, KeyEventKind};
|
|
use ratatui::{
|
|
buffer::Buffer,
|
|
layout::{Constraint, Direction, Layout, Margin, Rect},
|
|
style::{Color, Stylize},
|
|
symbols::border,
|
|
text::Line,
|
|
widgets::{Block, Clear, Widget},
|
|
DefaultTerminal, Frame,
|
|
};
|
|
use tui_rain::Rain;
|
|
|
|
fn main() -> io::Result<()> {
|
|
let mut terminal = ratatui::init();
|
|
let app_result = App::default().run(&mut terminal);
|
|
ratatui::restore();
|
|
app_result
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct App<'a> {
|
|
start_time: Instant,
|
|
last_draw: Instant,
|
|
exit: bool,
|
|
|
|
focus: Focus,
|
|
|
|
_marker: PhantomData<&'a ()>,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
enum Focus {
|
|
Username,
|
|
Password,
|
|
}
|
|
|
|
impl Default for App<'_> {
|
|
fn default() -> Self {
|
|
let start_time = Instant::now();
|
|
let last_draw = Instant::now();
|
|
let exit = false;
|
|
|
|
let focus = Focus::Username;
|
|
|
|
Self {
|
|
start_time,
|
|
last_draw,
|
|
exit,
|
|
|
|
focus,
|
|
|
|
_marker: PhantomData,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl App<'_> {
|
|
pub fn run(&mut self, terminal: &mut DefaultTerminal) -> io::Result<()> {
|
|
while !self.exit {
|
|
let draw_time = Instant::now();
|
|
terminal.draw(|frame| self.draw(frame))?;
|
|
self.last_draw = draw_time;
|
|
self.handle_events()?;
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn draw(&mut self, frame: &mut Frame) {
|
|
let rain_widget = Rain::new_matrix(self.start_time.elapsed())
|
|
.with_character_set(tui_rain::CharacterSet::UnicodeRange {
|
|
start: 0x21,
|
|
len: 94,
|
|
})
|
|
.with_bold_dim_effect(false)
|
|
.with_rain_density(tui_rain::RainDensity::Relative { sparseness: 60 })
|
|
.with_rain_speed(tui_rain::RainSpeed::Absolute { speed: 0.002 })
|
|
.with_rain_speed_variance(10000.0)
|
|
.with_tail_lifespan(Duration::from_millis(2500))
|
|
.with_color(Color::Green)
|
|
.with_head_color(Color::White)
|
|
.with_noise_interval(Duration::from_millis(2000));
|
|
|
|
let layout = Layout::default()
|
|
.direction(Direction::Horizontal)
|
|
.constraints(vec![Constraint::Percentage(40), Constraint::Percentage(20)])
|
|
.split(frame.area());
|
|
let layout = Layout::default()
|
|
.direction(Direction::Vertical)
|
|
.constraints(vec![Constraint::Percentage(40), Constraint::Percentage(20)])
|
|
.split(layout[1]);
|
|
|
|
let prompt_area = layout[1];
|
|
let inner_area = prompt_area.inner(Margin::new(1, 1));
|
|
|
|
let prompt_areas = Layout::default()
|
|
.direction(Direction::Vertical)
|
|
.constraints(vec![Constraint::Length(1), Constraint::Length(1)])
|
|
.split(inner_area);
|
|
|
|
frame.render_widget(rain_widget, frame.area());
|
|
frame.render_widget(Clear, prompt_area);
|
|
|
|
let title = Line::from(" Hewwo! >w< :3 ".bold());
|
|
let instructions = Line::from(vec![
|
|
" Decrement ".into(),
|
|
"<Left>".blue().bold(),
|
|
" Increment ".into(),
|
|
"<Right>".blue().bold(),
|
|
" Quit ".into(),
|
|
"<Q> ".blue().bold(),
|
|
]);
|
|
|
|
let prompt_block = Block::bordered()
|
|
.title(title.left_aligned())
|
|
.title_bottom(instructions.centered())
|
|
.border_set(border::PLAIN);
|
|
|
|
frame.render_widget(prompt_block, prompt_area);
|
|
|
|
// TextPrompt::from("User").draw(frame, prompt_areas[0], &mut self.username_state);
|
|
// TextPrompt::from("Password")
|
|
// .with_render_style(TextRenderStyle::Password)
|
|
// .draw(frame, prompt_areas[1], &mut self.password_state);
|
|
}
|
|
|
|
fn handle_events(&mut self) -> io::Result<()> {
|
|
if event::poll(Duration::from_millis(10))? {
|
|
match event::read()? {
|
|
Event::Key(key_event) if key_event.kind == KeyEventKind::Press => {
|
|
self.handle_key_event(key_event)
|
|
}
|
|
_ => {}
|
|
}
|
|
};
|
|
Ok(())
|
|
}
|
|
|
|
fn handle_key_event(&mut self, key_event: KeyEvent) {
|
|
match key_event.code {
|
|
KeyCode::Esc => self.exit(),
|
|
KeyCode::Tab => match self.focus {
|
|
Focus::Username => self.focus = Focus::Password,
|
|
Focus::Password => self.focus = Focus::Username,
|
|
},
|
|
KeyCode::BackTab => match self.focus {
|
|
Focus::Username => self.focus = Focus::Password,
|
|
Focus::Password => self.focus = Focus::Username,
|
|
},
|
|
_ => self.focus_handle_key_event(key_event),
|
|
}
|
|
}
|
|
|
|
fn focus_handle_key_event(&mut self, key_event: KeyEvent) {
|
|
// let state = match self.focus {
|
|
// Focus::Username => &mut self.username_state,
|
|
// Focus::Password => &mut self.password_state,
|
|
// };
|
|
// state.handle_key_event(key_event);
|
|
}
|
|
|
|
fn exit(&mut self) {
|
|
self.exit = true;
|
|
}
|
|
}
|
|
|
|
impl Widget for &mut App<'_> {
|
|
fn render(self, area: Rect, buf: &mut Buffer) {
|
|
let title = Line::from(" Hewwo >w< :3 ".bold());
|
|
let instructions = Line::from(vec![
|
|
" Decrement ".into(),
|
|
"<Left>".blue().bold(),
|
|
" Increment ".into(),
|
|
"<Right>".blue().bold(),
|
|
" Quit ".into(),
|
|
"<Q> ".blue().bold(),
|
|
]);
|
|
|
|
let block = Block::bordered()
|
|
.title(title.left_aligned())
|
|
.title_bottom(instructions.centered())
|
|
.border_set(border::PLAIN);
|
|
|
|
block.render(area, buf);
|
|
}
|
|
}
|