Using only CrossTerm to print stuff (a to block, q to quit)

This commit is contained in:
Jarkko Toivanen 2024-07-31 20:07:25 +03:00
parent e3b6f51f64
commit fa5cddb358
Signed by: jt
GPG key ID: 9151B109B73ECAD5
5 changed files with 64 additions and 449 deletions

261
Cargo.lock generated
View file

@ -2,24 +2,6 @@
# It is not intended for manual editing.
version = 3
[[package]]
name = "ahash"
version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
dependencies = [
"cfg-if",
"once_cell",
"version_check",
"zerocopy",
]
[[package]]
name = "allocator-api2"
version = "0.2.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f"
[[package]]
name = "autocfg"
version = "1.3.0"
@ -32,40 +14,12 @@ version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
[[package]]
name = "cassowary"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53"
[[package]]
name = "castaway"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0abae9be0aaf9ea96a3b1b8b1b55c602ca751eba1b1500220cea4ecbafe7c0d5"
dependencies = [
"rustversion",
]
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "compact_str"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f86b9c4c00838774a6d902ef931eff7470720c51d90c2e32cfe15dc304737b3f"
dependencies = [
"castaway",
"cfg-if",
"itoa",
"ryu",
"static_assertions",
]
[[package]]
name = "crossterm"
version = "0.27.0"
@ -91,43 +45,6 @@ dependencies = [
"winapi",
]
[[package]]
name = "either"
version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
[[package]]
name = "hashbrown"
version = "0.14.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
dependencies = [
"ahash",
"allocator-api2",
]
[[package]]
name = "heck"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "itertools"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
dependencies = [
"either",
]
[[package]]
name = "itoa"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
[[package]]
name = "libc"
version = "0.2.155"
@ -150,15 +67,6 @@ version = "0.4.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
[[package]]
name = "lru"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3262e75e648fce39813cb56ac41f3c3e3f65217ebf3844d818d1f9398cfb0dc"
dependencies = [
"hashbrown",
]
[[package]]
name = "mio"
version = "0.8.11"
@ -175,15 +83,9 @@ dependencies = [
name = "nuudel"
version = "0.1.0"
dependencies = [
"ratatui",
"crossterm",
]
[[package]]
name = "once_cell"
version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]]
name = "parking_lot"
version = "0.12.3"
@ -207,51 +109,6 @@ dependencies = [
"windows-targets 0.52.6",
]
[[package]]
name = "paste"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
[[package]]
name = "proc-macro2"
version = "1.0.86"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
dependencies = [
"proc-macro2",
]
[[package]]
name = "ratatui"
version = "0.27.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d16546c5b5962abf8ce6e2881e722b4e0ae3b6f1a08a26ae3573c55853ca68d3"
dependencies = [
"bitflags",
"cassowary",
"compact_str",
"crossterm",
"itertools",
"lru",
"paste",
"stability",
"strum",
"strum_macros",
"unicode-segmentation",
"unicode-truncate",
"unicode-width",
]
[[package]]
name = "redox_syscall"
version = "0.5.3"
@ -261,18 +118,6 @@ dependencies = [
"bitflags",
]
[[package]]
name = "rustversion"
version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6"
[[package]]
name = "ryu"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
[[package]]
name = "scopeguard"
version = "1.2.0"
@ -315,90 +160,6 @@ version = "1.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
[[package]]
name = "stability"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d904e7009df136af5297832a3ace3370cd14ff1546a232f4f185036c2736fcac"
dependencies = [
"quote",
"syn",
]
[[package]]
name = "static_assertions"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]]
name = "strum"
version = "0.26.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06"
dependencies = [
"strum_macros",
]
[[package]]
name = "strum_macros"
version = "0.26.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be"
dependencies = [
"heck",
"proc-macro2",
"quote",
"rustversion",
"syn",
]
[[package]]
name = "syn"
version = "2.0.72"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "unicode-segmentation"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
[[package]]
name = "unicode-truncate"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3644627a5af5fa321c95b9b235a72fd24cd29c648c2c379431e6628655627bf"
dependencies = [
"itertools",
"unicode-segmentation",
"unicode-width",
]
[[package]]
name = "unicode-width"
version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d"
[[package]]
name = "version_check"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
@ -556,23 +317,3 @@ name = "windows_x86_64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "zerocopy"
version = "0.7.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.7.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
dependencies = [
"proc-macro2",
"quote",
"syn",
]

View file

@ -4,7 +4,8 @@ version = "0.1.0"
edition = "2021"
[dependencies]
ratatui = "0.27.0"
crossterm = "0.27.0"
#ratatui = "0.27.0"
[profile.release]
lto = true

View file

@ -1,125 +1,78 @@
use std::panic;
use std::io;
use std::io::Result;
use ratatui::{
buffer::Buffer,
crossterm::event::{
self,
Event,
KeyCode,
KeyEvent,
KeyEventKind
},
layout::{Alignment, Rect},
style::Stylize,
symbols::border,
text::{Line, Text},
widgets::{
block::{Position, Title},
Block, Paragraph, Widget,
},
Frame,
use crossterm::{
event::{poll, read, Event, KeyCode},
terminal, ExecutableCommand, cursor,
};
use std::panic;
use std::{
io::{stdout, Result, Write},
time::Duration,
};
mod tui;
#[cfg(test)]
mod test;
#[derive(Debug, Default)]
pub struct App {
counter: u8,
exit: bool,
}
fn main() -> Result<()> {
// Setup panic handling
let default_panic = panic::take_hook();
panic::set_hook(Box::new(move |info| {
let _= tui::restore();
let _ = restore();
default_panic(info);
}));
let mut terminal = tui::init()?;
App::default().run(&mut terminal)?;
tui::restore()?;
// Start terminaling
init()?;
let mut stdout = stdout();
print!("Press Q to quit");
// Main loop
let quitting = false;
while !quitting {
//stdout.execute(terminal::BeginSynchronizedUpdate)?;
handle_events()?;
stdout.flush()?;
//stdout.execute(terminal::EndSynchronizedUpdate)?;
}
// Stop terminaling
restore()?;
Ok(())
}
impl App {
// Main app loop until quit
pub fn run(&mut self, terminal: &mut tui::Tui) -> Result<()> {
while !self.exit {
terminal.draw(|frame| self.render_frame(frame))?;
self.handle_events()?;
}
Ok(())
}
fn render_frame(&self, frame: &mut Frame) {
frame.render_widget(self, frame.size());
}
fn handle_events(&mut self) -> io::Result<()> {
match event::read()? {
Event::Key(key_event) if key_event.kind == KeyEventKind::Press => {
self.handle_key_event(key_event)
}
_ => {}
};
Ok(())
}
fn init() -> Result<()> {
terminal::enable_raw_mode()?;
stdout().execute(terminal::EnterAlternateScreen)?;
Ok(())
}
fn handle_key_event(&mut self, key_event: KeyEvent) {
match key_event.code {
KeyCode::Char('q') => self.exit(),
KeyCode::Left => self.decrement_counter(),
KeyCode::Right => self.increment_counter(),
fn restore() -> Result<()> {
stdout().execute(terminal::LeaveAlternateScreen)?;
terminal::disable_raw_mode()?;
Ok(())
}
fn handle_events() -> Result<()> {
if poll(Duration::from_secs(0))? {
match read()? {
Event::Key(event) => match event.code {
KeyCode::Char('q') => {
panic!("Just quitting, in a way...");
},
KeyCode::Char('a') => {
print!("");
},
KeyCode::Left => {
stdout().execute(cursor::MoveLeft(1))?;
},
KeyCode::Right => {
stdout().execute(cursor::MoveRight(1))?;
},
KeyCode::Up => {
stdout().execute(cursor::MoveUp(1))?;
},
KeyCode::Down => {
stdout().execute(cursor::MoveDown(1))?;
},
_ => {}
},
_ => {}
}
}
fn exit(&mut self) {
self.exit = true;
}
fn increment_counter(&mut self) {
self.counter += 1;
}
fn decrement_counter(&mut self) {
self.counter -= 1;
}
}
impl Widget for &App {
fn render(self, area: Rect, buf: &mut Buffer) {
let title = Title::from(" Counter App Tutorial ".bold());
let instructions = Title::from(Line::from(vec![
" Decrement ".into(),
"<Left>".blue().bold(),
" Increment ".into(),
"<Right>".blue().bold(),
" Quit ".into(),
"<Q> ".blue().bold(),
]));
let block = Block::bordered()
.title(title.alignment(Alignment::Center))
.title(
instructions
.alignment(Alignment::Center)
.position(Position::Bottom),
)
.border_set(border::THICK);
let counter_text = Text::from(vec![Line::from(vec![
"Value: ".into(),
self.counter.to_string().yellow(),
])]);
Paragraph::new(counter_text)
.centered()
.block(block)
.render(area, buf);
}
Ok(())
}

View file

@ -1,45 +0,0 @@
use super::*;
use ratatui::style::Style;
#[test]
fn render() {
let app = App::default();
let mut buf = Buffer::empty(Rect::new(0, 0, 50, 4));
app.render(buf.area, &mut buf);
let mut expected = Buffer::with_lines(vec![
"┏━━━━━━━━━━━━━ Counter App Tutorial ━━━━━━━━━━━━━┓",
"┃ Value: 0 ┃",
"┃ ┃",
"┗━ Decrement <Left> Increment <Right> Quit <Q> ━━┛",
]);
let title_style = Style::new().bold();
let counter_style = Style::new().yellow();
let key_style = Style::new().blue().bold();
expected.set_style(Rect::new(14, 0, 22, 1), title_style);
expected.set_style(Rect::new(28, 1, 1, 1), counter_style);
expected.set_style(Rect::new(13, 3, 6, 1), key_style);
expected.set_style(Rect::new(30, 3, 7, 1), key_style);
expected.set_style(Rect::new(43, 3, 4, 1), key_style);
// note ratatui also has an assert_buffer_eq! macro that can be used to
// compare buffers and display the differences in a more readable way
assert_eq!(buf, expected);
}
#[test]
fn handle_key_event() -> io::Result<()> {
let mut app = App::default();
app.handle_key_event(KeyCode::Right.into());
assert_eq!(app.counter, 1);
app.handle_key_event(KeyCode::Left.into());
assert_eq!(app.counter, 0);
let mut app = App::default();
app.handle_key_event(KeyCode::Char('q').into());
assert_eq!(app.exit, true);
Ok(())
}

View file

@ -1,35 +0,0 @@
use std::io::{self, stdout, Stdout};
use ratatui::{
backend::CrosstermBackend,
crossterm::{
execute,
terminal::{
disable_raw_mode,
enable_raw_mode,
EnterAlternateScreen,
LeaveAlternateScreen,
},
event::{
EnableMouseCapture,
DisableMouseCapture,
},
},
Terminal,
};
pub type Tui = Terminal<CrosstermBackend<Stdout>>;
pub fn init() -> io::Result<Tui> {
execute!(stdout(), EnterAlternateScreen)?;
execute!(stdout(), EnableMouseCapture)?;
enable_raw_mode()?;
Terminal::new(CrosstermBackend::new(stdout()))
}
pub fn restore() -> io::Result<()> {
execute!(stdout(), LeaveAlternateScreen)?;
execute!(stdout(), DisableMouseCapture)?;
disable_raw_mode()?;
Ok(())
}