feat(tui): make input prompt functional

This commit is contained in:
Orhun Parmaksız 2022-01-02 15:26:58 +03:00
parent cb9d072a3e
commit 4df05f94fe
No known key found for this signature in database
GPG key ID: F83424824B3E4B90
6 changed files with 59 additions and 10 deletions

1
Cargo.lock generated
View file

@ -527,6 +527,7 @@ dependencies = [
"termion",
"thiserror",
"tui",
"unicode-width",
]
[[package]]

View file

@ -6,6 +6,7 @@ edition = "2021"
[dependencies]
termion = "1.5.6"
unicode-width = "0.1.9"
thiserror = "1.0.30"
[dependencies.systeroid-core]

View file

@ -5,11 +5,16 @@ use crate::command::Command;
pub struct App {
/// Whether if the application is running.
pub running: bool,
/// Input buffer.
pub input: Option<String>,
}
impl Default for App {
fn default() -> Self {
Self { running: true }
Self {
running: true,
input: None,
}
}
}
@ -17,6 +22,23 @@ impl App {
/// Runs the given command and updates the application.
pub fn run_command(&mut self, command: Command) {
match command {
Command::UpdateInput(v) => match self.input.as_mut() {
Some(input) => {
input.push(v);
}
None => {
self.input = Some(String::new());
}
},
Command::ClearInput(cancel) => {
if cancel {
self.input = None
} else if let Some(input) = self.input.as_mut() {
if input.pop().is_none() {
self.input = None;
}
}
}
Command::Exit => {
self.running = false;
}

View file

@ -3,17 +3,32 @@ use termion::event::Key;
/// Possible application commands.
#[derive(Debug)]
pub enum Command {
/// Update the input buffer.
UpdateInput(char),
/// Clear the input buffer.
ClearInput(bool),
/// Exit the application.
Exit,
/// Do nothing.
None,
}
impl From<Key> for Command {
fn from(key: Key) -> Self {
match key {
Key::Esc => Command::Exit,
_ => Command::None,
impl Command {
/// Parses a command from the given key.
pub fn parse(key: Key, input_mode: bool) -> Self {
if input_mode {
match key {
Key::Char(c) => Command::UpdateInput(c),
Key::Backspace => Command::ClearInput(false),
Key::Esc => Command::ClearInput(true),
_ => Command::None,
}
} else {
match key {
Key::Char(':') => Command::UpdateInput(' '),
Key::Esc => Command::Exit,
_ => Command::None,
}
}
}
}

View file

@ -37,10 +37,10 @@ pub fn run<Output: Write>(output: Output) -> Result<()> {
let event_handler = EventHandler::new(250);
let mut app = App::default();
while app.running {
terminal.draw(|frame| ui::render(frame))?;
terminal.draw(|frame| ui::render(frame, &mut app))?;
match event_handler.next()? {
Event::KeyPress(key) => {
let command = Command::from(key);
let command = Command::parse(key, app.input.is_some());
app.run_command(command);
}
Event::Tick => {}

View file

@ -1,18 +1,28 @@
use crate::app::App;
use tui::backend::Backend;
use tui::layout::{Constraint, Direction, Layout};
use tui::style::{Color, Style};
use tui::widgets::{Block, BorderType, Borders, Paragraph};
use tui::Frame;
use unicode_width::UnicodeWidthStr;
/// Renders the user interface.
pub fn render<B: Backend>(frame: &mut Frame<'_, B>) {
pub fn render<B: Backend>(frame: &mut Frame<'_, B>, app: &mut App) {
let rect = frame.size();
let chunks = Layout::default()
.direction(Direction::Vertical)
.constraints([Constraint::Min(rect.height - 3), Constraint::Min(3)].as_ref())
.split(rect);
if let Some(input) = &app.input {
frame.set_cursor(input.width() as u16 + 2, rect.height - 2);
}
frame.render_widget(
Paragraph::new(env!("CARGO_PKG_NAME")).block(
Paragraph::new(match &app.input {
Some(input) => format!(":{}", input),
None => String::new(),
})
.block(
Block::default()
.borders(Borders::all())
.border_style(Style::default().fg(Color::White))