mirror of
https://github.com/fdehau/tui-rs.git
synced 2024-10-30 21:20:22 +00:00
feat: add rustbox and crossterm demo
This commit is contained in:
parent
cd41ca571f
commit
f20512b599
13
Cargo.toml
13
Cargo.toml
@ -38,11 +38,16 @@ failure = "0.1"
|
||||
structopt = "0.2"
|
||||
|
||||
[[example]]
|
||||
name = "rustbox"
|
||||
path = "examples/rustbox.rs"
|
||||
name = "termion_demo"
|
||||
path = "examples/termion_demo.rs"
|
||||
required-features = ["termion"]
|
||||
|
||||
[[example]]
|
||||
name = "rustbox_demo"
|
||||
path = "examples/rustbox_demo.rs"
|
||||
required-features = ["rustbox"]
|
||||
|
||||
[[example]]
|
||||
name = "crossterm"
|
||||
path = "examples/crossterm.rs"
|
||||
name = "crossterm_demo"
|
||||
path = "examples/crossterm_demo.rs"
|
||||
required-features = ["crossterm"]
|
||||
|
13
README.md
13
README.md
@ -36,7 +36,16 @@ you may rely on the previously cited libraries to achieve such features.
|
||||
|
||||
### Demo
|
||||
|
||||
The [source code](examples/demo.rs) of the demo gif.
|
||||
The demo shown in the gif can be run with all available backends
|
||||
(`exmples/*_demo.rs` files). For example to see the `termion` version one could
|
||||
run:
|
||||
|
||||
```
|
||||
cargo run --example termion_demo --release --tick-rate 200
|
||||
```
|
||||
|
||||
The UI code is in [examples/demo/ui.rs](examples/demo/ui.rs) while the
|
||||
application state is in [examples/demo/app.rs](examples/demo/app.rs).
|
||||
|
||||
### Widgets
|
||||
|
||||
@ -56,6 +65,8 @@ The library comes with the following list of widgets:
|
||||
Click on each item to see the source of the example. Run the examples with with
|
||||
cargo (e.g. to run the demo `cargo run --example demo`), and quit by pressing `q`.
|
||||
|
||||
You can run all examples by running `make run-examples`.
|
||||
|
||||
### Third-party widgets
|
||||
|
||||
* [tui-logger](https://github.com/gin66/tui-logger)
|
||||
|
@ -1,38 +0,0 @@
|
||||
use tui::backend::CrosstermBackend;
|
||||
use tui::style::{Color, Modifier, Style};
|
||||
use tui::widgets::{Block, Borders, Paragraph, Text, Widget};
|
||||
use tui::Terminal;
|
||||
|
||||
fn main() -> Result<(), failure::Error> {
|
||||
let mut terminal = Terminal::new(CrosstermBackend::new())?;
|
||||
terminal.clear()?;
|
||||
terminal.hide_cursor()?;
|
||||
|
||||
loop {
|
||||
terminal.draw(|mut f| {
|
||||
let size = f.size();
|
||||
let text = [
|
||||
Text::raw("It "),
|
||||
Text::styled("works", Style::default().fg(Color::Yellow)),
|
||||
];
|
||||
Paragraph::new(text.iter())
|
||||
.block(
|
||||
Block::default()
|
||||
.title("Crossterm Backend")
|
||||
.title_style(Style::default().fg(Color::Yellow).modifier(Modifier::Bold))
|
||||
.borders(Borders::ALL)
|
||||
.border_style(Style::default().fg(Color::Magenta)),
|
||||
)
|
||||
.render(&mut f, size);
|
||||
})?;
|
||||
|
||||
let input = crossterm::input();
|
||||
match input.read_char()? {
|
||||
'q' => {
|
||||
break;
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
Ok(())
|
||||
}
|
90
examples/crossterm_demo.rs
Normal file
90
examples/crossterm_demo.rs
Normal file
@ -0,0 +1,90 @@
|
||||
#[allow(dead_code)]
|
||||
mod demo;
|
||||
#[allow(dead_code)]
|
||||
mod util;
|
||||
|
||||
use std::sync::mpsc;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
use structopt::StructOpt;
|
||||
use tui::backend::CrosstermBackend;
|
||||
use tui::Terminal;
|
||||
|
||||
use crate::demo::{ui, App};
|
||||
|
||||
enum Event<I> {
|
||||
Input(I),
|
||||
Tick,
|
||||
}
|
||||
|
||||
#[derive(Debug, StructOpt)]
|
||||
struct Cli {
|
||||
#[structopt(long = "tick-rate", default_value = "250")]
|
||||
tick_rate: u64,
|
||||
#[structopt(long = "log")]
|
||||
log: bool,
|
||||
}
|
||||
|
||||
fn main() -> Result<(), failure::Error> {
|
||||
let cli = Cli::from_args();
|
||||
stderrlog::new().quiet(!cli.log).verbosity(4).init()?;
|
||||
|
||||
let backend = CrosstermBackend::new();
|
||||
let mut terminal = Terminal::new(backend)?;
|
||||
terminal.hide_cursor()?;
|
||||
|
||||
// Setup input handling
|
||||
let (tx, rx) = mpsc::channel();
|
||||
{
|
||||
let tx = tx.clone();
|
||||
thread::spawn(move || {
|
||||
let input = crossterm::input();
|
||||
loop {
|
||||
match input.read_char() {
|
||||
Ok(key) => {
|
||||
if let Err(_) = tx.send(Event::Input(key)) {
|
||||
return;
|
||||
}
|
||||
if key == 'q' {
|
||||
return;
|
||||
}
|
||||
}
|
||||
Err(_) => {}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
{
|
||||
let tx = tx.clone();
|
||||
thread::spawn(move || {
|
||||
let tx = tx.clone();
|
||||
loop {
|
||||
tx.send(Event::Tick).unwrap();
|
||||
thread::sleep(Duration::from_millis(cli.tick_rate));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let mut app = App::new("Crossterm Demo");
|
||||
|
||||
terminal.clear()?;
|
||||
|
||||
loop {
|
||||
ui::draw(&mut terminal, &app)?;
|
||||
match rx.recv()? {
|
||||
Event::Input(key) => {
|
||||
// TODO: handle key events once they are supported by crossterm
|
||||
app.on_key(key);
|
||||
}
|
||||
Event::Tick => {
|
||||
app.on_tick();
|
||||
}
|
||||
}
|
||||
if app.should_quit {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
249
examples/demo/app.rs
Normal file
249
examples/demo/app.rs
Normal file
@ -0,0 +1,249 @@
|
||||
use crate::util::{RandomSignal, SinSignal, TabsState};
|
||||
|
||||
const TASKS: [&'static str; 24] = [
|
||||
"Item1", "Item2", "Item3", "Item4", "Item5", "Item6", "Item7", "Item8", "Item9", "Item10",
|
||||
"Item11", "Item12", "Item13", "Item14", "Item15", "Item16", "Item17", "Item18", "Item19",
|
||||
"Item20", "Item21", "Item22", "Item23", "Item24",
|
||||
];
|
||||
|
||||
const LOGS: [(&'static str, &'static str); 26] = [
|
||||
("Event1", "INFO"),
|
||||
("Event2", "INFO"),
|
||||
("Event3", "CRITICAL"),
|
||||
("Event4", "ERROR"),
|
||||
("Event5", "INFO"),
|
||||
("Event6", "INFO"),
|
||||
("Event7", "WARNING"),
|
||||
("Event8", "INFO"),
|
||||
("Event9", "INFO"),
|
||||
("Event10", "INFO"),
|
||||
("Event11", "CRITICAL"),
|
||||
("Event12", "INFO"),
|
||||
("Event13", "INFO"),
|
||||
("Event14", "INFO"),
|
||||
("Event15", "INFO"),
|
||||
("Event16", "INFO"),
|
||||
("Event17", "ERROR"),
|
||||
("Event18", "ERROR"),
|
||||
("Event19", "INFO"),
|
||||
("Event20", "INFO"),
|
||||
("Event21", "WARNING"),
|
||||
("Event22", "INFO"),
|
||||
("Event23", "INFO"),
|
||||
("Event24", "WARNING"),
|
||||
("Event25", "INFO"),
|
||||
("Event26", "INFO"),
|
||||
];
|
||||
|
||||
const EVENTS: [(&'static str, u64); 24] = [
|
||||
("B1", 9),
|
||||
("B2", 12),
|
||||
("B3", 5),
|
||||
("B4", 8),
|
||||
("B5", 2),
|
||||
("B6", 4),
|
||||
("B7", 5),
|
||||
("B8", 9),
|
||||
("B9", 14),
|
||||
("B10", 15),
|
||||
("B11", 1),
|
||||
("B12", 0),
|
||||
("B13", 4),
|
||||
("B14", 6),
|
||||
("B15", 4),
|
||||
("B16", 6),
|
||||
("B17", 4),
|
||||
("B18", 7),
|
||||
("B19", 13),
|
||||
("B20", 8),
|
||||
("B21", 11),
|
||||
("B22", 9),
|
||||
("B23", 3),
|
||||
("B24", 5),
|
||||
];
|
||||
|
||||
pub struct Signal<S: Iterator> {
|
||||
source: S,
|
||||
pub points: Vec<S::Item>,
|
||||
tick_rate: usize,
|
||||
}
|
||||
|
||||
impl<S> Signal<S>
|
||||
where
|
||||
S: Iterator,
|
||||
{
|
||||
fn on_tick(&mut self) {
|
||||
for _ in 0..self.tick_rate {
|
||||
self.points.remove(0);
|
||||
}
|
||||
self.points
|
||||
.extend(self.source.by_ref().take(self.tick_rate));
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Signals {
|
||||
pub sin1: Signal<SinSignal>,
|
||||
pub sin2: Signal<SinSignal>,
|
||||
pub window: [f64; 2],
|
||||
}
|
||||
|
||||
impl Signals {
|
||||
fn on_tick(&mut self) {
|
||||
self.sin1.on_tick();
|
||||
self.sin2.on_tick();
|
||||
self.window[0] += 1.0;
|
||||
self.window[1] += 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ListState<I> {
|
||||
pub items: Vec<I>,
|
||||
pub selected: usize,
|
||||
}
|
||||
|
||||
impl<I> ListState<I> {
|
||||
fn new(items: Vec<I>) -> ListState<I> {
|
||||
ListState { items, selected: 0 }
|
||||
}
|
||||
fn select_previous(&mut self) {
|
||||
if self.selected > 0 {
|
||||
self.selected -= 1;
|
||||
}
|
||||
}
|
||||
fn select_next(&mut self) {
|
||||
if self.selected < self.items.len() - 1 {
|
||||
self.selected += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Server<'a> {
|
||||
pub name: &'a str,
|
||||
pub location: &'a str,
|
||||
pub coords: (f64, f64),
|
||||
pub status: &'a str,
|
||||
}
|
||||
|
||||
pub struct App<'a> {
|
||||
pub title: &'a str,
|
||||
pub should_quit: bool,
|
||||
pub tabs: TabsState<'a>,
|
||||
pub show_chart: bool,
|
||||
pub progress: u16,
|
||||
pub sparkline: Signal<RandomSignal>,
|
||||
pub tasks: ListState<(&'a str)>,
|
||||
pub logs: ListState<(&'a str, &'a str)>,
|
||||
pub signals: Signals,
|
||||
pub barchart: Vec<(&'a str, u64)>,
|
||||
pub servers: Vec<Server<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> App<'a> {
|
||||
pub fn new(title: &'a str) -> App<'a> {
|
||||
let mut rand_signal = RandomSignal::new(0, 100);
|
||||
let sparkline_points = rand_signal.by_ref().take(300).collect();
|
||||
let mut sin_signal = SinSignal::new(0.2, 3.0, 18.0);
|
||||
let sin1_points = sin_signal.by_ref().take(100).collect();
|
||||
let mut sin_signal2 = SinSignal::new(0.1, 2.0, 10.0);
|
||||
let sin2_points = sin_signal2.by_ref().take(200).collect();
|
||||
App {
|
||||
title,
|
||||
should_quit: false,
|
||||
tabs: TabsState::new(vec!["Tab0", "Tab1"]),
|
||||
show_chart: true,
|
||||
progress: 0,
|
||||
sparkline: Signal {
|
||||
source: rand_signal,
|
||||
points: sparkline_points,
|
||||
tick_rate: 1,
|
||||
},
|
||||
tasks: ListState::new(TASKS.to_vec()),
|
||||
logs: ListState::new(LOGS.to_vec()),
|
||||
signals: Signals {
|
||||
sin1: Signal {
|
||||
source: sin_signal,
|
||||
points: sin1_points,
|
||||
tick_rate: 5,
|
||||
},
|
||||
sin2: Signal {
|
||||
source: sin_signal2,
|
||||
points: sin2_points,
|
||||
tick_rate: 10,
|
||||
},
|
||||
window: [0.0, 20.0],
|
||||
},
|
||||
barchart: EVENTS.to_vec(),
|
||||
servers: vec![
|
||||
Server {
|
||||
name: "NorthAmerica-1",
|
||||
location: "New York City",
|
||||
coords: (40.71, -74.00),
|
||||
status: "Up",
|
||||
},
|
||||
Server {
|
||||
name: "Europe-1",
|
||||
location: "Paris",
|
||||
coords: (48.85, 2.35),
|
||||
status: "Failure",
|
||||
},
|
||||
Server {
|
||||
name: "SouthAmerica-1",
|
||||
location: "São Paulo",
|
||||
coords: (-23.54, -46.62),
|
||||
status: "Up",
|
||||
},
|
||||
Server {
|
||||
name: "Asia-1",
|
||||
location: "Singapore",
|
||||
coords: (1.35, 103.86),
|
||||
status: "Up",
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn on_up(&mut self) {
|
||||
self.tasks.select_previous();
|
||||
}
|
||||
|
||||
pub fn on_down(&mut self) {
|
||||
self.tasks.select_next();
|
||||
}
|
||||
|
||||
pub fn on_right(&mut self) {
|
||||
self.tabs.next();
|
||||
}
|
||||
|
||||
pub fn on_left(&mut self) {
|
||||
self.tabs.previous();
|
||||
}
|
||||
|
||||
pub fn on_key(&mut self, c: char) {
|
||||
match c {
|
||||
'q' => {
|
||||
self.should_quit = true;
|
||||
}
|
||||
't' => {
|
||||
self.show_chart = !self.show_chart;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn on_tick(&mut self) {
|
||||
// Update progress
|
||||
self.progress += 5;
|
||||
if self.progress > 100 {
|
||||
self.progress = 0;
|
||||
}
|
||||
|
||||
self.sparkline.on_tick();
|
||||
self.signals.on_tick();
|
||||
|
||||
let log = self.logs.items.pop().unwrap();
|
||||
self.logs.items.insert(0, log);
|
||||
|
||||
let event = self.barchart.pop().unwrap();
|
||||
self.barchart.insert(0, event);
|
||||
}
|
||||
}
|
3
examples/demo/mod.rs
Normal file
3
examples/demo/mod.rs
Normal file
@ -0,0 +1,3 @@
|
||||
mod app;
|
||||
pub mod ui;
|
||||
pub use app::App;
|
@ -1,15 +1,6 @@
|
||||
#[allow(dead_code)]
|
||||
mod util;
|
||||
|
||||
use std::io;
|
||||
use std::time::Duration;
|
||||
|
||||
use structopt::StructOpt;
|
||||
use termion::event::Key;
|
||||
use termion::input::MouseTerminal;
|
||||
use termion::raw::IntoRawMode;
|
||||
use termion::screen::AlternateScreen;
|
||||
use tui::backend::{Backend, TermionBackend};
|
||||
use tui::backend::Backend;
|
||||
use tui::layout::{Constraint, Direction, Layout, Rect};
|
||||
use tui::style::{Color, Modifier, Style};
|
||||
use tui::widgets::canvas::{Canvas, Line, Map, MapResolution, Rectangle};
|
||||
@ -19,168 +10,15 @@ use tui::widgets::{
|
||||
};
|
||||
use tui::{Frame, Terminal};
|
||||
|
||||
use crate::util::event::{Config, Event, Events};
|
||||
use crate::util::{RandomSignal, SinSignal, TabsState};
|
||||
use crate::demo::App;
|
||||
|
||||
struct Server<'a> {
|
||||
name: &'a str,
|
||||
location: &'a str,
|
||||
coords: (f64, f64),
|
||||
status: &'a str,
|
||||
}
|
||||
|
||||
struct App<'a> {
|
||||
items: Vec<&'a str>,
|
||||
events: Vec<(&'a str, &'a str)>,
|
||||
selected: usize,
|
||||
tabs: TabsState<'a>,
|
||||
show_chart: bool,
|
||||
progress: u16,
|
||||
data: Vec<u64>,
|
||||
data2: Vec<(f64, f64)>,
|
||||
data3: Vec<(f64, f64)>,
|
||||
data4: Vec<(&'a str, u64)>,
|
||||
window: [f64; 2],
|
||||
colors: [Color; 2],
|
||||
color_index: usize,
|
||||
servers: Vec<Server<'a>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, StructOpt)]
|
||||
struct Cli {
|
||||
#[structopt(long = "tick-rate", default_value = "250")]
|
||||
tick_rate: u64,
|
||||
#[structopt(long = "log")]
|
||||
log: bool,
|
||||
}
|
||||
|
||||
fn main() -> Result<(), failure::Error> {
|
||||
let cli = Cli::from_args();
|
||||
|
||||
stderrlog::new().quiet(!cli.log).verbosity(4).init()?;
|
||||
|
||||
let stdout = io::stdout().into_raw_mode()?;
|
||||
let stdout = MouseTerminal::from(stdout);
|
||||
let stdout = AlternateScreen::from(stdout);
|
||||
let backend = TermionBackend::new(stdout);
|
||||
let mut terminal = Terminal::new(backend)?;
|
||||
terminal.hide_cursor()?;
|
||||
|
||||
let events = Events::with_config(Config {
|
||||
tick_rate: Duration::from_millis(cli.tick_rate),
|
||||
..Config::default()
|
||||
});
|
||||
|
||||
let mut rand_signal = RandomSignal::new(0, 100);
|
||||
let mut sin_signal = SinSignal::new(0.2, 3.0, 18.0);
|
||||
let mut sin_signal2 = SinSignal::new(0.1, 2.0, 10.0);
|
||||
|
||||
let mut app = App {
|
||||
items: vec![
|
||||
"Item1", "Item2", "Item3", "Item4", "Item5", "Item6", "Item7", "Item8", "Item9",
|
||||
"Item10", "Item11", "Item12", "Item13", "Item14", "Item15", "Item16", "Item17",
|
||||
"Item18", "Item19", "Item20", "Item21", "Item22", "Item23", "Item24",
|
||||
],
|
||||
events: vec![
|
||||
("Event1", "INFO"),
|
||||
("Event2", "INFO"),
|
||||
("Event3", "CRITICAL"),
|
||||
("Event4", "ERROR"),
|
||||
("Event5", "INFO"),
|
||||
("Event6", "INFO"),
|
||||
("Event7", "WARNING"),
|
||||
("Event8", "INFO"),
|
||||
("Event9", "INFO"),
|
||||
("Event10", "INFO"),
|
||||
("Event11", "CRITICAL"),
|
||||
("Event12", "INFO"),
|
||||
("Event13", "INFO"),
|
||||
("Event14", "INFO"),
|
||||
("Event15", "INFO"),
|
||||
("Event16", "INFO"),
|
||||
("Event17", "ERROR"),
|
||||
("Event18", "ERROR"),
|
||||
("Event19", "INFO"),
|
||||
("Event20", "INFO"),
|
||||
("Event21", "WARNING"),
|
||||
("Event22", "INFO"),
|
||||
("Event23", "INFO"),
|
||||
("Event24", "WARNING"),
|
||||
("Event25", "INFO"),
|
||||
("Event26", "INFO"),
|
||||
],
|
||||
selected: 0,
|
||||
tabs: TabsState::new(vec!["Tab0", "Tab1"]),
|
||||
show_chart: true,
|
||||
progress: 0,
|
||||
data: rand_signal.by_ref().take(300).collect(),
|
||||
data2: sin_signal.by_ref().take(100).collect(),
|
||||
data3: sin_signal2.by_ref().take(200).collect(),
|
||||
data4: vec![
|
||||
("B1", 9),
|
||||
("B2", 12),
|
||||
("B3", 5),
|
||||
("B4", 8),
|
||||
("B5", 2),
|
||||
("B6", 4),
|
||||
("B7", 5),
|
||||
("B8", 9),
|
||||
("B9", 14),
|
||||
("B10", 15),
|
||||
("B11", 1),
|
||||
("B12", 0),
|
||||
("B13", 4),
|
||||
("B14", 6),
|
||||
("B15", 4),
|
||||
("B16", 6),
|
||||
("B17", 4),
|
||||
("B18", 7),
|
||||
("B19", 13),
|
||||
("B20", 8),
|
||||
("B21", 11),
|
||||
("B22", 9),
|
||||
("B23", 3),
|
||||
("B24", 5),
|
||||
],
|
||||
window: [0.0, 20.0],
|
||||
colors: [Color::Magenta, Color::Red],
|
||||
color_index: 0,
|
||||
servers: vec![
|
||||
Server {
|
||||
name: "NorthAmerica-1",
|
||||
location: "New York City",
|
||||
coords: (40.71, -74.00),
|
||||
status: "Up",
|
||||
},
|
||||
Server {
|
||||
name: "Europe-1",
|
||||
location: "Paris",
|
||||
coords: (48.85, 2.35),
|
||||
status: "Failure",
|
||||
},
|
||||
Server {
|
||||
name: "SouthAmerica-1",
|
||||
location: "São Paulo",
|
||||
coords: (-23.54, -46.62),
|
||||
status: "Up",
|
||||
},
|
||||
Server {
|
||||
name: "Asia-1",
|
||||
location: "Singapore",
|
||||
coords: (1.35, 103.86),
|
||||
status: "Up",
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
loop {
|
||||
// Draw UI
|
||||
pub fn draw<B: Backend>(terminal: &mut Terminal<B>, app: &App) -> Result<(), io::Error> {
|
||||
terminal.draw(|mut f| {
|
||||
let chunks = Layout::default()
|
||||
.constraints([Constraint::Length(3), Constraint::Min(0)].as_ref())
|
||||
.split(f.size());
|
||||
Tabs::default()
|
||||
.block(Block::default().borders(Borders::ALL).title("Tabs"))
|
||||
.block(Block::default().borders(Borders::ALL).title(app.title))
|
||||
.titles(&app.tabs.titles)
|
||||
.style(Style::default().fg(Color::Green))
|
||||
.highlight_style(Style::default().fg(Color::Yellow))
|
||||
@ -191,63 +29,7 @@ fn main() -> Result<(), failure::Error> {
|
||||
1 => draw_second_tab(&mut f, &app, chunks[1]),
|
||||
_ => {}
|
||||
};
|
||||
})?;
|
||||
|
||||
match events.next()? {
|
||||
Event::Input(input) => match input {
|
||||
Key::Char('q') => {
|
||||
break;
|
||||
}
|
||||
Key::Up => {
|
||||
if app.selected > 0 {
|
||||
app.selected -= 1
|
||||
};
|
||||
}
|
||||
Key::Down => {
|
||||
if app.selected < app.items.len() - 1 {
|
||||
app.selected += 1;
|
||||
}
|
||||
}
|
||||
Key::Left => {
|
||||
app.tabs.previous();
|
||||
}
|
||||
Key::Right => {
|
||||
app.tabs.next();
|
||||
}
|
||||
Key::Char('t') => {
|
||||
app.show_chart = !app.show_chart;
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
Event::Tick => {
|
||||
app.progress += 5;
|
||||
if app.progress > 100 {
|
||||
app.progress = 0;
|
||||
}
|
||||
app.data.insert(0, rand_signal.next().unwrap());
|
||||
app.data.pop();
|
||||
for _ in 0..5 {
|
||||
app.data2.remove(0);
|
||||
app.data2.push(sin_signal.next().unwrap());
|
||||
}
|
||||
for _ in 0..10 {
|
||||
app.data3.remove(0);
|
||||
app.data3.push(sin_signal2.next().unwrap());
|
||||
}
|
||||
let i = app.data4.pop().unwrap();
|
||||
app.data4.insert(0, i);
|
||||
app.window[0] += 1.0;
|
||||
app.window[1] += 1.0;
|
||||
let i = app.events.pop().unwrap();
|
||||
app.events.insert(0, i);
|
||||
app.color_index += 1;
|
||||
if app.color_index >= app.colors.len() {
|
||||
app.color_index = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
fn draw_first_tab<B>(f: &mut Frame<B>, app: &App, area: Rect)
|
||||
@ -295,7 +77,7 @@ where
|
||||
Sparkline::default()
|
||||
.block(Block::default().title("Sparkline:"))
|
||||
.style(Style::default().fg(Color::Green))
|
||||
.data(&app.data)
|
||||
.data(&app.sparkline.points)
|
||||
.render(f, chunks[1]);
|
||||
}
|
||||
|
||||
@ -323,8 +105,8 @@ where
|
||||
.split(chunks[0]);
|
||||
SelectableList::default()
|
||||
.block(Block::default().borders(Borders::ALL).title("List"))
|
||||
.items(&app.items)
|
||||
.select(Some(app.selected))
|
||||
.items(&app.tasks.items)
|
||||
.select(Some(app.tasks.selected))
|
||||
.highlight_style(Style::default().fg(Color::Yellow).modifier(Modifier::Bold))
|
||||
.highlight_symbol(">")
|
||||
.render(f, chunks[0]);
|
||||
@ -332,7 +114,7 @@ where
|
||||
let warning_style = Style::default().fg(Color::Yellow);
|
||||
let error_style = Style::default().fg(Color::Magenta);
|
||||
let critical_style = Style::default().fg(Color::Red);
|
||||
let events = app.events.iter().map(|&(evt, level)| {
|
||||
let events = app.logs.items.iter().map(|&(evt, level)| {
|
||||
Text::styled(
|
||||
format!("{}: {}", level, evt),
|
||||
match level {
|
||||
@ -349,7 +131,7 @@ where
|
||||
}
|
||||
BarChart::default()
|
||||
.block(Block::default().borders(Borders::ALL).title("Bar chart"))
|
||||
.data(&app.data4)
|
||||
.data(&app.barchart)
|
||||
.bar_width(3)
|
||||
.bar_gap(2)
|
||||
.value_style(
|
||||
@ -375,11 +157,11 @@ where
|
||||
.title("X Axis")
|
||||
.style(Style::default().fg(Color::Gray))
|
||||
.labels_style(Style::default().modifier(Modifier::Italic))
|
||||
.bounds(app.window)
|
||||
.bounds(app.signals.window)
|
||||
.labels(&[
|
||||
&format!("{}", app.window[0]),
|
||||
&format!("{}", (app.window[0] + app.window[1]) / 2.0),
|
||||
&format!("{}", app.window[1]),
|
||||
&format!("{}", app.signals.window[0]),
|
||||
&format!("{}", (app.signals.window[0] + app.signals.window[1]) / 2.0),
|
||||
&format!("{}", app.signals.window[1]),
|
||||
]),
|
||||
)
|
||||
.y_axis(
|
||||
@ -395,12 +177,12 @@ where
|
||||
.name("data2")
|
||||
.marker(Marker::Dot)
|
||||
.style(Style::default().fg(Color::Cyan))
|
||||
.data(&app.data2),
|
||||
.data(&app.signals.sin1.points),
|
||||
Dataset::default()
|
||||
.name("data3")
|
||||
.marker(Marker::Braille)
|
||||
.style(Style::default().fg(Color::Yellow))
|
||||
.data(&app.data3),
|
||||
.data(&app.signals.sin2.points),
|
||||
])
|
||||
.render(f, chunks[1]);
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
use rustbox::Key;
|
||||
use std::error::Error;
|
||||
|
||||
use tui::backend::RustboxBackend;
|
||||
use tui::style::{Color, Modifier, Style};
|
||||
use tui::widgets::{Block, Borders, Paragraph, Text, Widget};
|
||||
use tui::Terminal;
|
||||
|
||||
fn main() -> Result<(), failure::Error> {
|
||||
let mut terminal = Terminal::new(RustboxBackend::new().unwrap()).unwrap();
|
||||
terminal.clear().unwrap();
|
||||
terminal.hide_cursor().unwrap();
|
||||
loop {
|
||||
draw(&mut terminal)?;
|
||||
match terminal.backend().rustbox().poll_event(false) {
|
||||
Ok(rustbox::Event::KeyEvent(key)) => {
|
||||
if key == Key::Char('q') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Err(e) => panic!("{}", e.description()),
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
terminal.show_cursor()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn draw(t: &mut Terminal<RustboxBackend>) -> Result<(), std::io::Error> {
|
||||
let text = [
|
||||
Text::raw("It "),
|
||||
Text::styled("works", Style::default().fg(Color::Yellow)),
|
||||
];
|
||||
t.draw(|mut f| {
|
||||
let size = f.size();
|
||||
Paragraph::new(text.iter())
|
||||
.block(
|
||||
Block::default()
|
||||
.title("Rustbox backend")
|
||||
.title_style(Style::default().fg(Color::Yellow).modifier(Modifier::Bold))
|
||||
.borders(Borders::ALL)
|
||||
.border_style(Style::default().fg(Color::Magenta)),
|
||||
)
|
||||
.render(&mut f, size)
|
||||
})
|
||||
}
|
66
examples/rustbox_demo.rs
Normal file
66
examples/rustbox_demo.rs
Normal file
@ -0,0 +1,66 @@
|
||||
mod demo;
|
||||
#[allow(dead_code)]
|
||||
mod util;
|
||||
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use rustbox::keyboard::Key;
|
||||
use structopt::StructOpt;
|
||||
use tui::backend::RustboxBackend;
|
||||
use tui::Terminal;
|
||||
|
||||
use crate::demo::{ui, App};
|
||||
|
||||
#[derive(Debug, StructOpt)]
|
||||
struct Cli {
|
||||
#[structopt(long = "tick-rate", default_value = "250")]
|
||||
tick_rate: u64,
|
||||
#[structopt(long = "log")]
|
||||
log: bool,
|
||||
}
|
||||
|
||||
fn main() -> Result<(), failure::Error> {
|
||||
let cli = Cli::from_args();
|
||||
stderrlog::new().quiet(!cli.log).verbosity(4).init()?;
|
||||
|
||||
let backend = RustboxBackend::new()?;
|
||||
let mut terminal = Terminal::new(backend)?;
|
||||
terminal.hide_cursor()?;
|
||||
|
||||
let mut app = App::new("Rustbox demo");
|
||||
|
||||
let mut last_tick = Instant::now();
|
||||
let tick_rate = Duration::from_millis(cli.tick_rate);
|
||||
loop {
|
||||
ui::draw(&mut terminal, &app)?;
|
||||
match terminal.backend().rustbox().peek_event(tick_rate, false) {
|
||||
Ok(rustbox::Event::KeyEvent(key)) => match key {
|
||||
Key::Char(c) => {
|
||||
app.on_key(c);
|
||||
}
|
||||
Key::Up => {
|
||||
app.on_up();
|
||||
}
|
||||
Key::Down => {
|
||||
app.on_down();
|
||||
}
|
||||
Key::Left => {
|
||||
app.on_left();
|
||||
}
|
||||
Key::Right => {
|
||||
app.on_right();
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
if last_tick.elapsed() > tick_rate {
|
||||
app.on_tick();
|
||||
last_tick = Instant::now();
|
||||
}
|
||||
if app.should_quit {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
75
examples/termion_demo.rs
Normal file
75
examples/termion_demo.rs
Normal file
@ -0,0 +1,75 @@
|
||||
mod demo;
|
||||
#[allow(dead_code)]
|
||||
mod util;
|
||||
|
||||
use std::io;
|
||||
use std::time::Duration;
|
||||
|
||||
use structopt::StructOpt;
|
||||
use termion::event::Key;
|
||||
use termion::input::MouseTerminal;
|
||||
use termion::raw::IntoRawMode;
|
||||
use termion::screen::AlternateScreen;
|
||||
use tui::backend::TermionBackend;
|
||||
use tui::Terminal;
|
||||
|
||||
use crate::demo::{ui, App};
|
||||
use crate::util::event::{Config, Event, Events};
|
||||
|
||||
#[derive(Debug, StructOpt)]
|
||||
struct Cli {
|
||||
#[structopt(long = "tick-rate", default_value = "250")]
|
||||
tick_rate: u64,
|
||||
#[structopt(long = "log")]
|
||||
log: bool,
|
||||
}
|
||||
|
||||
fn main() -> Result<(), failure::Error> {
|
||||
let cli = Cli::from_args();
|
||||
stderrlog::new().quiet(!cli.log).verbosity(4).init()?;
|
||||
|
||||
let events = Events::with_config(Config {
|
||||
tick_rate: Duration::from_millis(cli.tick_rate),
|
||||
..Config::default()
|
||||
});
|
||||
|
||||
let stdout = io::stdout().into_raw_mode()?;
|
||||
let stdout = MouseTerminal::from(stdout);
|
||||
let stdout = AlternateScreen::from(stdout);
|
||||
let backend = TermionBackend::new(stdout);
|
||||
let mut terminal = Terminal::new(backend)?;
|
||||
terminal.hide_cursor()?;
|
||||
|
||||
let mut app = App::new("Termion demo");
|
||||
loop {
|
||||
ui::draw(&mut terminal, &app)?;
|
||||
match events.next()? {
|
||||
Event::Input(key) => match key {
|
||||
Key::Char(c) => {
|
||||
app.on_key(c);
|
||||
}
|
||||
Key::Up => {
|
||||
app.on_up();
|
||||
}
|
||||
Key::Down => {
|
||||
app.on_down();
|
||||
}
|
||||
Key::Left => {
|
||||
app.on_left();
|
||||
}
|
||||
Key::Right => {
|
||||
app.on_right();
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
Event::Tick => {
|
||||
app.on_tick();
|
||||
}
|
||||
}
|
||||
if app.should_quit {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
#[cfg(feature = "termion")]
|
||||
pub mod event;
|
||||
|
||||
use rand::distributions::{Distribution, Uniform};
|
||||
|
Loading…
Reference in New Issue
Block a user