From c8c03294e101e0005333553964dd15e3530f409a Mon Sep 17 00:00:00 2001 From: Florian Dehau Date: Thu, 11 Nov 2021 15:56:37 +0100 Subject: [PATCH] chore: self contained examples --- .github/workflows/ci.yml | 22 +-- Cargo.toml | 57 +++++++- Makefile.toml | 107 ++++++++++----- README.md | 35 ++--- examples/chart.rs | 32 ++++- examples/demo/app.rs | 120 +++++++++++++++- .../{crossterm_demo.rs => demo/crossterm.rs} | 26 +--- examples/demo/main.rs | 31 +++++ examples/demo/mod.rs | 3 - examples/{termion_demo.rs => demo/termion.rs} | 25 +--- examples/demo/ui.rs | 2 +- examples/demo/util.rs | 1 + examples/list.rs | 52 ++++++- examples/sparkline.rs | 30 +++- examples/tabs.rs | 31 +++-- examples/util/mod.rs | 128 ------------------ 16 files changed, 421 insertions(+), 281 deletions(-) rename examples/{crossterm_demo.rs => demo/crossterm.rs} (76%) create mode 100644 examples/demo/main.rs delete mode 100644 examples/demo/mod.rs rename examples/{termion_demo.rs => demo/termion.rs} (76%) create mode 100644 examples/demo/util.rs delete mode 100644 examples/util/mod.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b817fa5..eee3180 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,7 +9,7 @@ on: name: CI env: - CI_CARGO_MAKE_VERSION: 0.32.9 + CI_CARGO_MAKE_VERSION: 0.35.6 jobs: linux: @@ -38,16 +38,10 @@ jobs: - name: "Install cargo-make" if: steps.cache-cargo-make.outputs.cache-hit != 'true' run: cargo install cargo-make --version ${{ env.CI_CARGO_MAKE_VERSION }} - - name: "Format" - run: cargo make fmt - - name: "Check" - run: cargo make check - - name: "Test" - run: cargo make test + - name: "Format / Build / Test" + run: cargo make ci env: RUST_BACKTRACE: full - - name: "Clippy" - run: cargo make clippy windows: name: Windows runs-on: windows-latest @@ -73,13 +67,7 @@ jobs: - name: "Install cargo-make" if: steps.cache-cargo-make.outputs.cache-hit != 'true' run: cargo install cargo-make --version ${{ env.CI_CARGO_MAKE_VERSION }} - - name: "Format" - run: cargo make fmt - - name: "Check" - run: cargo make check - - name: "Test" - run: cargo make test + - name: "Format / Build / Test" + run: cargo make ci env: RUST_BACKTRACE: full - - name: "Clippy" - run: cargo make clippy diff --git a/Cargo.toml b/Cargo.toml index 78067bc..50ed4da 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,6 +33,57 @@ rand = "0.8" argh = "0.1" [[example]] -name = "termion_demo" -path = "examples/termion_demo.rs" -required-features = ["termion"] +name = "barchart" +required-features = ["crossterm"] + +[[example]] +name = "block" +required-features = ["crossterm"] + +[[example]] +name = "canvas" +required-features = ["crossterm"] + +[[example]] +name = "chart" +required-features = ["crossterm"] + +[[example]] +name = "custom_widget" +required-features = ["crossterm"] + +[[example]] +name = "gauge" +required-features = ["crossterm"] + +[[example]] +name = "layout" +required-features = ["crossterm"] + +[[example]] +name = "list" +required-features = ["crossterm"] + +[[example]] +name = "paragraph" +required-features = ["crossterm"] + +[[example]] +name = "popup" +required-features = ["crossterm"] + +[[example]] +name = "sparkline" +required-features = ["crossterm"] + +[[example]] +name = "table" +required-features = ["crossterm"] + +[[example]] +name = "tabs" +required-features = ["crossterm"] + +[[example]] +name = "user_input" +required-features = ["crossterm"] diff --git a/Makefile.toml b/Makefile.toml index 9a0771a..22360bd 100644 --- a/Makefile.toml +++ b/Makefile.toml @@ -1,29 +1,35 @@ [config] skip_core_tasks = true -[env.TUI_FEATURES] -source = "${CARGO_MAKE_RUST_TARGET_OS}" -default_value = "unknown" - -[env.TUI_FEATURES.mapping] -linux = "serde,crossterm,termion" -macos = "serde,crossterm,termion" -windows = "serde,crossterm" +[tasks.ci] +run_task = [ + { name = "ci-unix", condition = { platforms = ["linux", "mac"] } }, + { name = "ci-windows", condition = { platforms = ["windows"] } }, +] -[tasks.default] +[tasks.ci-unix] +private = true dependencies = [ - "check", + "fmt", + "check-crossterm", + "check-termion", + "test-crossterm", + "test-termion", + "clippy-crossterm", + "clippy-termion", + "test-doc", ] -[tasks.ci] +[tasks.ci-windows] +private = true dependencies = [ - "fmt", - "check", - "test", - "clippy", + "fmt", + "check-crossterm", + "test-crossterm", + "clippy-crossterm", + "test-doc", ] - [tasks.fmt] command = "cargo" args = [ @@ -33,8 +39,17 @@ args = [ "--check", ] +[tasks.check-crossterm] +env = { TUI_FEATURES = "serde,crossterm" } +run_task = "check" + +[tasks.check-termion] +env = { TUI_FEATURES = "serde,termion" } +run_task = "check" + [tasks.check] command = "cargo" +condition = { env_set = ["TUI_FEATURES"] } args = [ "check", "--no-default-features", @@ -43,8 +58,17 @@ args = [ "--all-targets", ] +[tasks.build-crossterm] +env = { TUI_FEATURES = "serde,crossterm" } +run_task = "build" + +[tasks.build-termion] +env = { TUI_FEATURES = "serde,termion" } +run_task = "build" + [tasks.build] command = "cargo" +condition = { env_set = ["TUI_FEATURES"] } args = [ "build", "--no-default-features", @@ -53,8 +77,17 @@ args = [ "--all-targets", ] +[tasks.clippy-crossterm] +env = { TUI_FEATURES = "serde,crossterm" } +run_task = "clippy" + +[tasks.clippy-termion] +env = { TUI_FEATURES = "serde,termion" } +run_task = "clippy" + [tasks.clippy] command = "cargo" +condition = { env_set = ["TUI_FEATURES"] } args = [ "clippy", "--no-default-features", @@ -65,48 +98,50 @@ args = [ "warnings", ] +[tasks.test-crossterm] +env = { TUI_FEATURES = "serde,crossterm" } +run_task = "test" + +[tasks.test-termion] +env = { TUI_FEATURES = "serde,termion" } +run_task = "test" + [tasks.test] command = "cargo" +condition = { env_set = ["TUI_FEATURES"] } args = [ "test", "--no-default-features", "--features", - "${TUI_FEATURES}" + "${TUI_FEATURES}", + "--lib", + "--tests", + "--examples", +] + +[tasks.test-doc] +command = "cargo" +args = [ + "test", + "--doc", ] [tasks.run-example] private = true -condition = { env_set = ["TUI_EXAMPLE_NAME", "TUI_FEATURES"] } +condition = { env_set = ["TUI_EXAMPLE_NAME"] } command = "cargo" args = [ "run", - "--features", - "${TUI_FEATURES}", "--release", "--example", "${TUI_EXAMPLE_NAME}" ] -[tasks.run-example-windows] -private = true -condition = { env = {"TUI_EXAMPLE_NAME" = "crossterm_demo"} } -run_task = "run-example" - -[tasks.run-example-router] -private = true -run_task = [ - { name = "run-example-windows", condition = { platforms = ["window"] } }, - { name = "run-example" } -] - [tasks.build-examples] -condition = { env_set = ["TUI_FEATURES"] } command = "cargo" args = [ "build", "--examples", - "--features", - "${TUI_FEATURES}", "--release" ] @@ -119,6 +154,6 @@ for file in ${files} name = basename ${file} name = substring ${name} -3 set_env TUI_EXAMPLE_NAME ${name} - cm_run_task run-example-router + cm_run_task run-example end ''' diff --git a/README.md b/README.md index 519f0c2..dc29f31 100644 --- a/README.md +++ b/README.md @@ -11,15 +11,9 @@ user interfaces and dashboards. It is heavily inspired by the `Javascript` library [blessed-contrib](https://github.com/yaronn/blessed-contrib) and the `Go` library [termui](https://github.com/gizak/termui). -The library itself supports four different backends to draw to the terminal. You -can either choose from: - +The library supports multiple backends: + - [crossterm](https://github.com/crossterm-rs/crossterm) [default] - [termion](https://github.com/ticki/termion) - - [rustbox](https://github.com/gchp/rustbox) - - [crossterm](https://github.com/crossterm-rs/crossterm) - - [pancurses](https://github.com/ihalila/pancurses) - -However, some features may only be available in one of the four. The library is based on the principle of immediate rendering with intermediate buffers. This means that at each new frame you should build all widgets that are @@ -34,18 +28,19 @@ you may rely on the previously cited libraries to achieve such features. ### Rust version requirements -Since version 0.10.0, `tui` requires **rustc version 1.44.0 or greater**. +Since version 0.17.0, `tui` requires **rustc version 1.52.1 or greater**. ### [Documentation](https://docs.rs/tui) ### Demo -The demo shown in the gif can be run with all available backends -(`examples/*_demo.rs` files). For example to see the `termion` version one could -run: +The demo shown in the gif can be run with all available backends. ``` -cargo run --example termion_demo --release -- --tick-rate 200 +# crossterm +cargo run --example demo --release -- --tick-rate 200 +# termion +cargo run --example demo --no-default-features --features=termion --release -- --tick-rate 200 ``` where `tick-rate` is the UI refresh rate in ms. @@ -53,18 +48,11 @@ where `tick-rate` is the UI refresh rate in ms. The UI code is in [examples/demo/ui.rs](https://github.com/fdehau/tui-rs/blob/v0.16.0/examples/demo/ui.rs) while the application state is in [examples/demo/app.rs](https://github.com/fdehau/tui-rs/blob/v0.16.0/examples/demo/app.rs). -Beware that the `termion_demo` only works on Unix platforms. If you are a Windows user, -you can see the same demo using the `crossterm` backend with the following command: - -``` -cargo run --example crossterm_demo --no-default-features --features="crossterm" --release -- --tick-rate 200 -``` - If the user interface contains glyphs that are not displayed correctly by your terminal, you may want to run the demo without those symbols: ``` -cargo run --example crossterm_demo --no-default-features --features="crossterm" --release -- --tick-rate 200 --enhanced-graphics false +cargo run --example demo --release -- --tick-rate 200 --enhanced-graphics false ``` ### Widgets @@ -83,9 +71,10 @@ The library comes with the following list of widgets: * [Tabs](https://github.com/fdehau/tui-rs/blob/v0.16.0/examples/tabs.rs) 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`. +cargo (e.g. to run the gauge example `cargo run --example gauge`), and quit by pressing `q`. -You can run all examples by running `make run-examples`. +You can run all examples by running `cargo make run-examples` (require +`cargo-make` that can be installed with `cargo install cargo-make`). ### Third-party widgets diff --git a/examples/chart.rs b/examples/chart.rs index 71a9dba..9e574ac 100644 --- a/examples/chart.rs +++ b/examples/chart.rs @@ -1,7 +1,3 @@ -#[allow(dead_code)] -mod util; - -use crate::util::SinSignal; use crossterm::{ event::{self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode}, execute, @@ -33,6 +29,34 @@ const DATA2: [(f64, f64); 7] = [ (60.0, 3.0), ]; +#[derive(Clone)] +pub struct SinSignal { + x: f64, + interval: f64, + period: f64, + scale: f64, +} + +impl SinSignal { + pub fn new(interval: f64, period: f64, scale: f64) -> SinSignal { + SinSignal { + x: 0.0, + interval, + period, + scale, + } + } +} + +impl Iterator for SinSignal { + type Item = (f64, f64); + fn next(&mut self) -> Option { + let point = (self.x, (self.x * 1.0 / self.period).sin() * self.scale); + self.x += self.interval; + Some(point) + } +} + struct App { signal1: SinSignal, data1: Vec<(f64, f64)>, diff --git a/examples/demo/app.rs b/examples/demo/app.rs index 0291cfd..5cf4bb5 100644 --- a/examples/demo/app.rs +++ b/examples/demo/app.rs @@ -1,4 +1,8 @@ -use crate::util::{RandomSignal, SinSignal, StatefulList, TabsState}; +use rand::{ + distributions::{Distribution, Uniform}, + rngs::ThreadRng, +}; +use tui::widgets::ListState; const TASKS: [&str; 24] = [ "Item1", "Item2", "Item3", "Item4", "Item5", "Item6", "Item7", "Item8", "Item9", "Item10", @@ -62,6 +66,120 @@ const EVENTS: [(&str, u64); 24] = [ ("B24", 5), ]; +#[derive(Clone)] +pub struct RandomSignal { + distribution: Uniform, + rng: ThreadRng, +} + +impl RandomSignal { + pub fn new(lower: u64, upper: u64) -> RandomSignal { + RandomSignal { + distribution: Uniform::new(lower, upper), + rng: rand::thread_rng(), + } + } +} + +impl Iterator for RandomSignal { + type Item = u64; + fn next(&mut self) -> Option { + Some(self.distribution.sample(&mut self.rng)) + } +} + +#[derive(Clone)] +pub struct SinSignal { + x: f64, + interval: f64, + period: f64, + scale: f64, +} + +impl SinSignal { + pub fn new(interval: f64, period: f64, scale: f64) -> SinSignal { + SinSignal { + x: 0.0, + interval, + period, + scale, + } + } +} + +impl Iterator for SinSignal { + type Item = (f64, f64); + fn next(&mut self) -> Option { + let point = (self.x, (self.x * 1.0 / self.period).sin() * self.scale); + self.x += self.interval; + Some(point) + } +} + +pub struct TabsState<'a> { + pub titles: Vec<&'a str>, + pub index: usize, +} + +impl<'a> TabsState<'a> { + pub fn new(titles: Vec<&'a str>) -> TabsState { + TabsState { titles, index: 0 } + } + pub fn next(&mut self) { + self.index = (self.index + 1) % self.titles.len(); + } + + pub fn previous(&mut self) { + if self.index > 0 { + self.index -= 1; + } else { + self.index = self.titles.len() - 1; + } + } +} + +pub struct StatefulList { + pub state: ListState, + pub items: Vec, +} + +impl StatefulList { + pub fn with_items(items: Vec) -> StatefulList { + StatefulList { + state: ListState::default(), + items, + } + } + + pub fn next(&mut self) { + let i = match self.state.selected() { + Some(i) => { + if i >= self.items.len() - 1 { + 0 + } else { + i + 1 + } + } + None => 0, + }; + self.state.select(Some(i)); + } + + pub fn previous(&mut self) { + let i = match self.state.selected() { + Some(i) => { + if i == 0 { + self.items.len() - 1 + } else { + i - 1 + } + } + None => 0, + }; + self.state.select(Some(i)); + } +} + pub struct Signal { source: S, pub points: Vec, diff --git a/examples/crossterm_demo.rs b/examples/demo/crossterm.rs similarity index 76% rename from examples/crossterm_demo.rs rename to examples/demo/crossterm.rs index 7c5adec..df12ea9 100644 --- a/examples/crossterm_demo.rs +++ b/examples/demo/crossterm.rs @@ -1,10 +1,4 @@ -#[allow(dead_code)] -mod demo; -#[allow(dead_code)] -mod util; - -use crate::demo::{ui, App}; -use argh::FromArgs; +use crate::{app::App, ui}; use crossterm::{ event::{self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode}, execute, @@ -20,20 +14,7 @@ use tui::{ Terminal, }; -/// Crossterm demo -#[derive(Debug, FromArgs)] -struct Cli { - /// time in ms between two ticks. - #[argh(option, default = "250")] - tick_rate: u64, - /// whether unicode symbols are used to improve the overall look of the app - #[argh(option, default = "true")] - enhanced_graphics: bool, -} - -fn main() -> Result<(), Box> { - let cli: Cli = argh::from_env(); - +pub fn run(tick_rate: Duration, enhanced_graphics: bool) -> Result<(), Box> { // setup terminal enable_raw_mode()?; let mut stdout = io::stdout(); @@ -42,8 +23,7 @@ fn main() -> Result<(), Box> { let mut terminal = Terminal::new(backend)?; // create app and run it - let tick_rate = Duration::from_millis(cli.tick_rate); - let app = App::new("Crossterm Demo", cli.enhanced_graphics); + let app = App::new("Crossterm Demo", enhanced_graphics); let res = run_app(&mut terminal, app, tick_rate); // restore terminal diff --git a/examples/demo/main.rs b/examples/demo/main.rs new file mode 100644 index 0000000..30a1b43 --- /dev/null +++ b/examples/demo/main.rs @@ -0,0 +1,31 @@ +mod app; +#[cfg(feature = "crossterm")] +mod crossterm; +#[cfg(feature = "termion")] +mod termion; +mod ui; + +#[cfg(feature = "crossterm")] +use crate::crossterm::run; +#[cfg(feature = "termion")] +use crate::termion::run; +use argh::FromArgs; +use std::{error::Error, time::Duration}; + +/// Demo +#[derive(Debug, FromArgs)] +struct Cli { + /// time in ms between two ticks. + #[argh(option, default = "250")] + tick_rate: u64, + /// whether unicode symbols are used to improve the overall look of the app + #[argh(option, default = "true")] + enhanced_graphics: bool, +} + +fn main() -> Result<(), Box> { + let cli: Cli = argh::from_env(); + let tick_rate = Duration::from_millis(cli.tick_rate); + run(tick_rate, cli.enhanced_graphics)?; + Ok(()) +} diff --git a/examples/demo/mod.rs b/examples/demo/mod.rs deleted file mode 100644 index cbf7f15..0000000 --- a/examples/demo/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -mod app; -pub mod ui; -pub use app::App; diff --git a/examples/termion_demo.rs b/examples/demo/termion.rs similarity index 76% rename from examples/termion_demo.rs rename to examples/demo/termion.rs index 9e2c97b..4d87c94 100644 --- a/examples/termion_demo.rs +++ b/examples/demo/termion.rs @@ -1,9 +1,4 @@ -mod demo; -#[allow(dead_code)] -mod util; - -use crate::demo::{ui, App}; -use argh::FromArgs; +use crate::{app::App, ui}; use std::{error::Error, io, sync::mpsc, thread, time::Duration}; use termion::{ event::Key, @@ -16,20 +11,7 @@ use tui::{ Terminal, }; -/// Termion demo -#[derive(Debug, FromArgs)] -struct Cli { - /// time in ms between two ticks. - #[argh(option, default = "250")] - tick_rate: u64, - /// whether unicode symbols are used to improve the overall look of the app - #[argh(option, default = "true")] - enhanced_graphics: bool, -} - -fn main() -> Result<(), Box> { - let cli: Cli = argh::from_env(); - +pub fn run(tick_rate: Duration, enhanced_graphics: bool) -> Result<(), Box> { // setup terminal let stdout = io::stdout().into_raw_mode()?; let stdout = MouseTerminal::from(stdout); @@ -38,8 +20,7 @@ fn main() -> Result<(), Box> { let mut terminal = Terminal::new(backend)?; // create app and run it - let tick_rate = Duration::from_millis(cli.tick_rate); - let app = App::new("Termion demo", cli.enhanced_graphics); + let app = App::new("Termion demo", enhanced_graphics); run_app(&mut terminal, app, tick_rate)?; Ok(()) diff --git a/examples/demo/ui.rs b/examples/demo/ui.rs index 96d4642..837bd42 100644 --- a/examples/demo/ui.rs +++ b/examples/demo/ui.rs @@ -1,4 +1,4 @@ -use crate::demo::App; +use crate::app::App; use tui::{ backend::Backend, layout::{Constraint, Direction, Layout, Rect}, diff --git a/examples/demo/util.rs b/examples/demo/util.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/examples/demo/util.rs @@ -0,0 +1 @@ + diff --git a/examples/list.rs b/examples/list.rs index 0cb14bc..1e3dbcd 100644 --- a/examples/list.rs +++ b/examples/list.rs @@ -1,7 +1,3 @@ -#[allow(dead_code)] -mod util; - -use crate::util::StatefulList; use crossterm::{ event::{self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode}, execute, @@ -17,10 +13,56 @@ use tui::{ layout::{Constraint, Corner, Direction, Layout}, style::{Color, Modifier, Style}, text::{Span, Spans}, - widgets::{Block, Borders, List, ListItem}, + widgets::{Block, Borders, List, ListItem, ListState}, Frame, Terminal, }; +struct StatefulList { + state: ListState, + items: Vec, +} + +impl StatefulList { + fn with_items(items: Vec) -> StatefulList { + StatefulList { + state: ListState::default(), + items, + } + } + + fn next(&mut self) { + let i = match self.state.selected() { + Some(i) => { + if i >= self.items.len() - 1 { + 0 + } else { + i + 1 + } + } + None => 0, + }; + self.state.select(Some(i)); + } + + fn previous(&mut self) { + let i = match self.state.selected() { + Some(i) => { + if i == 0 { + self.items.len() - 1 + } else { + i - 1 + } + } + None => 0, + }; + self.state.select(Some(i)); + } + + fn unselect(&mut self) { + self.state.select(None); + } +} + /// This struct holds the current state of the app. In particular, it has the `items` field which is a wrapper /// around `ListState`. Keeping track of the items state let us render the associated widget with its state /// and have access to features such as natural scrolling. diff --git a/examples/sparkline.rs b/examples/sparkline.rs index 76e7830..999f9e0 100644 --- a/examples/sparkline.rs +++ b/examples/sparkline.rs @@ -1,12 +1,12 @@ -#[allow(dead_code)] -mod util; - -use crate::util::RandomSignal; use crossterm::{ event::{self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode}, execute, terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, }; +use rand::{ + distributions::{Distribution, Uniform}, + rngs::ThreadRng, +}; use std::{ error::Error, io, @@ -20,6 +20,28 @@ use tui::{ Frame, Terminal, }; +#[derive(Clone)] +pub struct RandomSignal { + distribution: Uniform, + rng: ThreadRng, +} + +impl RandomSignal { + pub fn new(lower: u64, upper: u64) -> RandomSignal { + RandomSignal { + distribution: Uniform::new(lower, upper), + rng: rand::thread_rng(), + } + } +} + +impl Iterator for RandomSignal { + type Item = u64; + fn next(&mut self) -> Option { + Some(self.distribution.sample(&mut self.rng)) + } +} + struct App { signal: RandomSignal, data1: Vec, diff --git a/examples/tabs.rs b/examples/tabs.rs index 2cec057..4ca0f9a 100644 --- a/examples/tabs.rs +++ b/examples/tabs.rs @@ -1,7 +1,3 @@ -#[allow(dead_code)] -mod util; - -use crate::util::TabsState; use crossterm::{ event::{self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode}, execute, @@ -18,13 +14,27 @@ use tui::{ }; struct App<'a> { - tabs: TabsState<'a>, + pub titles: Vec<&'a str>, + pub index: usize, } impl<'a> App<'a> { fn new() -> App<'a> { App { - tabs: TabsState::new(vec!["Tab0", "Tab1", "Tab2", "Tab3"]), + titles: vec!["Tab0", "Tab1", "Tab2", "Tab3"], + index: 0, + } + } + + pub fn next(&mut self) { + self.index = (self.index + 1) % self.titles.len(); + } + + pub fn previous(&mut self) { + if self.index > 0 { + self.index -= 1; + } else { + self.index = self.titles.len() - 1; } } } @@ -64,8 +74,8 @@ fn run_app(terminal: &mut Terminal, mut app: App) -> io::Result<( if let Event::Key(key) = event::read()? { match key.code { KeyCode::Char('q') => return Ok(()), - KeyCode::Right => app.tabs.next(), - KeyCode::Left => app.tabs.previous(), + KeyCode::Right => app.next(), + KeyCode::Left => app.previous(), _ => {} } } @@ -83,7 +93,6 @@ fn ui(f: &mut Frame, app: &App) { let block = Block::default().style(Style::default().bg(Color::White).fg(Color::Black)); f.render_widget(block, size); let titles = app - .tabs .titles .iter() .map(|t| { @@ -96,7 +105,7 @@ fn ui(f: &mut Frame, app: &App) { .collect(); let tabs = Tabs::new(titles) .block(Block::default().borders(Borders::ALL).title("Tabs")) - .select(app.tabs.index) + .select(app.index) .style(Style::default().fg(Color::Cyan)) .highlight_style( Style::default() @@ -104,7 +113,7 @@ fn ui(f: &mut Frame, app: &App) { .bg(Color::Black), ); f.render_widget(tabs, chunks[0]); - let inner = match app.tabs.index { + let inner = match app.index { 0 => Block::default().title("Inner 0").borders(Borders::ALL), 1 => Block::default().title("Inner 1").borders(Borders::ALL), 2 => Block::default().title("Inner 2").borders(Borders::ALL), diff --git a/examples/util/mod.rs b/examples/util/mod.rs deleted file mode 100644 index b7573cc..0000000 --- a/examples/util/mod.rs +++ /dev/null @@ -1,128 +0,0 @@ -use rand::distributions::{Distribution, Uniform}; -use rand::rngs::ThreadRng; -use tui::widgets::ListState; - -#[derive(Clone)] -pub struct RandomSignal { - distribution: Uniform, - rng: ThreadRng, -} - -impl RandomSignal { - pub fn new(lower: u64, upper: u64) -> RandomSignal { - RandomSignal { - distribution: Uniform::new(lower, upper), - rng: rand::thread_rng(), - } - } -} - -impl Iterator for RandomSignal { - type Item = u64; - fn next(&mut self) -> Option { - Some(self.distribution.sample(&mut self.rng)) - } -} - -#[derive(Clone)] -pub struct SinSignal { - x: f64, - interval: f64, - period: f64, - scale: f64, -} - -impl SinSignal { - pub fn new(interval: f64, period: f64, scale: f64) -> SinSignal { - SinSignal { - x: 0.0, - interval, - period, - scale, - } - } -} - -impl Iterator for SinSignal { - type Item = (f64, f64); - fn next(&mut self) -> Option { - let point = (self.x, (self.x * 1.0 / self.period).sin() * self.scale); - self.x += self.interval; - Some(point) - } -} - -pub struct TabsState<'a> { - pub titles: Vec<&'a str>, - pub index: usize, -} - -impl<'a> TabsState<'a> { - pub fn new(titles: Vec<&'a str>) -> TabsState { - TabsState { titles, index: 0 } - } - pub fn next(&mut self) { - self.index = (self.index + 1) % self.titles.len(); - } - - pub fn previous(&mut self) { - if self.index > 0 { - self.index -= 1; - } else { - self.index = self.titles.len() - 1; - } - } -} - -pub struct StatefulList { - pub state: ListState, - pub items: Vec, -} - -impl StatefulList { - pub fn new() -> StatefulList { - StatefulList { - state: ListState::default(), - items: Vec::new(), - } - } - - pub fn with_items(items: Vec) -> StatefulList { - StatefulList { - state: ListState::default(), - items, - } - } - - pub fn next(&mut self) { - let i = match self.state.selected() { - Some(i) => { - if i >= self.items.len() - 1 { - 0 - } else { - i + 1 - } - } - None => 0, - }; - self.state.select(Some(i)); - } - - pub fn previous(&mut self) { - let i = match self.state.selected() { - Some(i) => { - if i == 0 { - self.items.len() - 1 - } else { - i - 1 - } - } - None => 0, - }; - self.state.select(Some(i)); - } - - pub fn unselect(&mut self) { - self.state.select(None); - } -}