initial commit
This commit is contained in:
commit
a47c45a537
5 changed files with 1174 additions and 0 deletions
103
src/app.rs
Normal file
103
src/app.rs
Normal file
|
@ -0,0 +1,103 @@
|
|||
use std::f64::consts::PI;
|
||||
use ratatui::{layout::Rect, style::{Style, Stylize}, text::{Line, Span}, widgets::{Block, Paragraph}};
|
||||
use terminput::KeyCode;
|
||||
|
||||
use crate::Demo;
|
||||
|
||||
pub(crate) struct MyApp {
|
||||
counter: usize,
|
||||
frame_counter: usize,
|
||||
}
|
||||
|
||||
impl Demo for MyApp {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
counter: 0,
|
||||
frame_counter: 0,
|
||||
}
|
||||
}
|
||||
|
||||
async fn frame(&mut self, term: &mut crate::Term) -> Result<(), String> {
|
||||
term.draw(|fr| {
|
||||
self.frame_counter = self.frame_counter.wrapping_add(1);
|
||||
let style = Style::new().light_yellow().on_black();
|
||||
|
||||
let block = Block::bordered()
|
||||
.style(style)
|
||||
.title("Example demo")
|
||||
.title_bottom("Sine - made by: wilkuu (Jakub Stachurski), jakub@snt.utwente.nl");
|
||||
|
||||
let area = fr.area();
|
||||
let barea = block.inner(area);
|
||||
|
||||
fr.render_widget(block, area);
|
||||
|
||||
let xl = barea.x;
|
||||
let xr = barea.width + barea.x;
|
||||
|
||||
let yt = barea.y + barea.height;
|
||||
|
||||
let w1 = PI * 2.0 / 100.0;
|
||||
let w2 = PI * 2.0 / 300.0;
|
||||
|
||||
let buf = fr.buffer_mut();
|
||||
for x in xl..xr {
|
||||
let p1 = self.frame_counter as f64 / 100.0;
|
||||
let p2 = self.frame_counter as f64 / 50.0;
|
||||
|
||||
let y1: u16 = ((
|
||||
((w1 * Into::<f64>::into(x)) + p1 as f64).sin() + 1.0)
|
||||
* Into::<f64>::into(barea.height / 2-1) + Into::<f64>::into(barea.y)).round() as u16;
|
||||
|
||||
let y2: u16 = ((
|
||||
((w2 * Into::<f64>::into(x)) + p2 as f64).sin() + 1.0)
|
||||
* Into::<f64>::into(barea.height / 2-1) + Into::<f64>::into(barea.y)).round() as u16;
|
||||
|
||||
if y2 == y1 {
|
||||
buf.set_span(x, (y1.saturating_sub(1)).max(barea.y), &Span::from("_").light_magenta(), 1);
|
||||
buf.set_span(x, y1, &Span::from("#").light_magenta(), 1);
|
||||
buf.set_span(x, (y1.saturating_add(1)).min(yt), &Span::from("-").light_magenta(), 1);
|
||||
} else {
|
||||
buf.set_span(x, (y1.saturating_sub(1)).max(barea.y), &Span::from("_").red(), 1);
|
||||
buf.set_span(x, y1, &Span::from("#").red(), 1);
|
||||
buf.set_span(x, (y1.saturating_add(1)).min(yt), &Span::from("-").red(), 1);
|
||||
|
||||
buf.set_span(x, (y2.saturating_sub(1)).max(barea.y), &Span::from("_").blue(), 1);
|
||||
buf.set_span(x, y2, &Span::from("#").blue(), 1);
|
||||
buf.set_span(x, (y2.saturating_add(1)).min(yt), &Span::from("-").blue(), 1);
|
||||
}
|
||||
}
|
||||
|
||||
let mut lines: Vec<Line>= Vec::new();
|
||||
lines.push("Use W/S to change the counter. Enter to exit.".into());
|
||||
lines.push(format!("Counter: {}", self.counter).into());
|
||||
|
||||
let text = Paragraph::new(lines).centered().style(style);
|
||||
|
||||
fr.render_widget(text, Rect::new(barea.width/2 - 25, barea.height/2, 50, 4));
|
||||
|
||||
}).map_err(|e| e.to_string())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn input(&mut self, event: terminput::Event) -> Result<(), String> {
|
||||
if let terminput::Event::Key(kv) = event {
|
||||
match kv.code {
|
||||
KeyCode::Char('w') => {
|
||||
self.counter = self.counter.wrapping_add(1);
|
||||
},
|
||||
KeyCode::Char('s') => {
|
||||
self.counter = self.counter.saturating_sub(1);
|
||||
},
|
||||
_=> {}
|
||||
}
|
||||
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn disconnected(&mut self) {
|
||||
()
|
||||
}
|
||||
}
|
82
src/main.rs
Normal file
82
src/main.rs
Normal file
|
@ -0,0 +1,82 @@
|
|||
use std::{io::{Stdout, Write}, time::Duration};
|
||||
|
||||
use crossterm::event::Event;
|
||||
use ratatui::{prelude::CrosstermBackend, Terminal};
|
||||
use futures::{future::FutureExt, StreamExt};
|
||||
|
||||
mod app;
|
||||
use app::MyApp;
|
||||
use terminput::KeyCode;
|
||||
use terminput_crossterm::to_terminput;
|
||||
use termion::raw::{IntoRawMode, RawTerminal};
|
||||
|
||||
pub type Term = Terminal<CrosstermBackend<RawTerminal<Stdout>>>;
|
||||
|
||||
trait Demo: Send + Sized + 'static {
|
||||
/// Needed to initialize state
|
||||
fn new() -> Self;
|
||||
|
||||
/// Called to draw a frame
|
||||
async fn frame(&mut self, term: &mut Term) -> Result<(), String>;
|
||||
|
||||
/// Called when user puts down input
|
||||
async fn input(&mut self, event: terminput::Event) -> Result<(), String>;
|
||||
|
||||
/// Called on cleanup
|
||||
async fn disconnected(&mut self);
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), std::io::Error>{
|
||||
let mut raw_term = std::io::stdout().into_raw_mode()?;
|
||||
// Open alternate buffer
|
||||
raw_term.write(b"\x1b[?1049h")?;
|
||||
let mut term = Terminal::new(CrosstermBackend::new(raw_term))?;
|
||||
let mut app = MyApp::new();
|
||||
|
||||
let mut input = crossterm::event::EventStream::new();
|
||||
let mut frame_timer = tokio::time::interval(Duration::new(0, 16666)); // 16ms -> 60fps
|
||||
|
||||
|
||||
loop {
|
||||
tokio::select! {
|
||||
ev = input.next().fuse() => if sh_input(ev, &mut app).await? {
|
||||
app.disconnected().await;
|
||||
break;
|
||||
},
|
||||
_ = frame_timer.tick() => app.frame(&mut term).await.map_err(err_map)?,
|
||||
_ = tokio::signal::ctrl_c() => {
|
||||
app.disconnected().await;
|
||||
break;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Clear screen and close the alternate buffer
|
||||
term.backend_mut().write(b"\x1b[2J\x1b[?1049l")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn sh_input<A: Demo>(ev: Option<Result<Event, std::io::Error>>, app: &mut A) -> Result<bool, std::io::Error> {
|
||||
match ev {
|
||||
Some(Ok(event)) => {
|
||||
let input = to_terminput(event).map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;
|
||||
if let Some(k) = input.as_key() && k.code == KeyCode::Enter {
|
||||
// Exit on enter (Standard for demos)
|
||||
Ok(true)
|
||||
} else {
|
||||
app.input(input).await.map(|_| false).map_err(err_map)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Some(Err(e)) => Err(e),
|
||||
None => Ok(true),
|
||||
}
|
||||
}
|
||||
|
||||
fn err_map(e: String) -> std::io::Error {
|
||||
std::io::Error::new(std::io::ErrorKind::Other, e)
|
||||
}
|
||||
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue