Moved examples back into crossterm (#332)

This commit is contained in:
Timon 2019-12-10 21:07:15 +01:00 committed by GitHub
parent 8fb9059853
commit 47e8366f2b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 894 additions and 13 deletions

36
examples/README.md Normal file
View File

@ -0,0 +1,36 @@
![Lines of Code][s7] [![MIT][s2]][l2] [![Join us on Discord][s5]][l5]
# Crossterm Examples
The examples are compatible with the latest release.
## Structure
```
├── examples
│   └── interactive-test
│   └── event-*
│   └── stderr
```
* `examples/interactive-test`, interactive demo
* `event-*`, event reading demo
* `stderr` , crossterm over stderr demo
## Run examples
```bash
$ cargo run --example [file name]
```
## License
This project is licensed under the MIT License - see the [LICENSE.md](LICENSE) file for details.
[s2]: https://img.shields.io/badge/license-MIT-blue.svg
[l2]: LICENSE
[s5]: https://img.shields.io/discord/560857607196377088.svg?logo=discord
[l5]: https://discord.gg/K4nyTDB
[s7]: https://travis-ci.org/crossterm-rs/examples.svg?branch=master

View File

@ -0,0 +1,63 @@
//! Demonstrates how to match on modifiers like: Control, alt, shift.
//!
//! cargo run --example event-match-modifiers
use crossterm::event::{Event, KeyCode, KeyEvent, KeyModifiers};
fn match_event(read_event: Event) {
match read_event {
// Match one one modifier:
Event::Key(KeyEvent {
modifiers: KeyModifiers::CONTROL,
code,
}) => {
println!("Control + {:?}", code);
}
Event::Key(KeyEvent {
modifiers: KeyModifiers::SHIFT,
code,
}) => {
println!("Shift + {:?}", code);
}
Event::Key(KeyEvent {
modifiers: KeyModifiers::ALT,
code,
}) => {
println!("Alt + {:?}", code);
}
// Match on multiple modifiers:
Event::Key(KeyEvent { code, modifiers }) => {
if modifiers == (KeyModifiers::ALT | KeyModifiers::SHIFT) {
println!("Alt + Shift {:?}", code);
} else {
println!("({:?}) with key: {:?}", modifiers, code)
}
}
_ => {}
}
}
fn main() {
match_event(Event::Key(KeyEvent {
modifiers: KeyModifiers::CONTROL,
code: KeyCode::Char('z'),
}));
match_event(Event::Key(KeyEvent {
modifiers: KeyModifiers::SHIFT,
code: KeyCode::Left,
}));
match_event(Event::Key(KeyEvent {
modifiers: KeyModifiers::ALT,
code: KeyCode::Delete,
}));
match_event(Event::Key(KeyEvent {
modifiers: KeyModifiers::ALT | KeyModifiers::SHIFT,
code: KeyCode::Right,
}));
match_event(Event::Key(KeyEvent {
modifiers: KeyModifiers::ALT | KeyModifiers::CONTROL,
code: KeyCode::Home,
}));
}

View File

@ -1,6 +1,7 @@
//
// cargo run --example event-poll-read
//
//! Demonstrates how to match on modifiers like: Control, alt, shift.
//!
//! cargo run --example event-poll-read
use std::{
io::{stdout, Write},
time::Duration,

View File

@ -0,0 +1,45 @@
//! Demonstrates how to block read characters or a full line.
//! Just note that crossterm is not required to do this and can be done with `io::stdin()`.
//!
//! cargo run --example event-read-char-line
use crossterm::{
event::{self, Event, KeyCode, KeyEvent},
Result,
};
pub fn read_char() -> Result<char> {
loop {
if let Event::Key(KeyEvent {
code: KeyCode::Char(c),
..
}) = event::read()?
{
return Ok(c);
}
}
}
pub fn read_line() -> Result<String> {
let mut line = String::new();
while let Event::Key(KeyEvent { code, .. }) = event::read()? {
match code {
KeyCode::Enter => {
break;
}
KeyCode::Char(c) => {
line.push(c);
}
_ => {}
}
}
return Ok(line);
}
fn main() {
println!("read line:");
println!("{:?}", read_line());
println!("read char:");
println!("{:?}", read_char());
}

View File

@ -1,6 +1,7 @@
//
// cargo run --example event-read
//
//! Demonstrates how to block read events.
//!
//! cargo run --example event-read
use std::io::{stdout, Write};
use crossterm::{
@ -22,7 +23,7 @@ fn print_events() -> Result<()> {
// Blocking read
let event = read()?;
println!("Event::{:?}\r", event);
println!("Event: {:?}\r", event);
if event == Event::Key(KeyCode::Char('c').into()) {
println!("Cursor position: {:?}\r", position());

View File

@ -1,6 +1,7 @@
//
// cargo run --features event-stream --example event-stream-async-std
//
//! Demonstrates how to read events asynchronously with async-std.
//!
//! cargo run --example event-read-char-line
use std::{
io::{stdout, Write},
time::Duration,

View File

@ -1,6 +1,7 @@
//
// cargo run --features event-stream --example event-stream-tokio
//
//! Demonstrates how to read events asynchronously with tokio.
//!
//! cargo run --features event-stream --example event-stream-tokio
use std::{
io::{stdout, Write},
time::Duration,

View File

@ -0,0 +1,13 @@
[package]
name = "interactive-demo"
version = "0.0.1"
authors = ["T. Post", "Robert Vojta <rvojta@me.com>"]
edition = "2018"
description = "Interactive demo for crossterm."
license = "MIT"
exclude = ["target", "Cargo.lock"]
readme = "README.md"
publish = false
[dependencies]
crossterm = { path = "../../" }

View File

@ -0,0 +1,29 @@
macro_rules! run_tests {
(
$dst:expr,
$(
$testfn:ident
),*
$(,)?
) => {
use crossterm::{queue, style, terminal, cursor};
$(
queue!(
$dst,
style::ResetColor,
terminal::Clear(terminal::ClearType::All),
cursor::MoveTo(1, 1),
cursor::Show,
cursor::EnableBlinking
)?;
$testfn($dst)?;
match $crate::read_char() {
Ok('q') => return Ok(()),
Err(e) => return Err(e),
_ => { },
};
)*
}
}

View File

@ -0,0 +1,96 @@
#![allow(clippy::cognitive_complexity)]
use std::io::{self, Write};
pub use crossterm::{
cursor,
event::{self, Event, KeyCode, KeyEvent},
execute, queue, style,
terminal::{self, ClearType},
Command, Result,
};
#[macro_use]
mod macros;
mod test;
const MENU: &str = r#"Crossterm interactive test
Controls:
- 'q' - quit interactive test (or return to this menu)
- any other key - continue with next step
Available tests:
1. cursor
2. color (foreground, background)
3. attributes (bold, italic, ...)
4. input
Select test to run ('1', '2', ...) or hit 'q' to quit.
"#;
fn run<W>(w: &mut W) -> Result<()>
where
W: Write,
{
execute!(w, terminal::EnterAlternateScreen)?;
terminal::enable_raw_mode()?;
loop {
queue!(
w,
style::ResetColor,
terminal::Clear(ClearType::All),
cursor::Hide,
cursor::MoveTo(1, 1)
)?;
for line in MENU.split('\n') {
queue!(w, style::Print(line), cursor::MoveToNextLine(1))?;
}
w.flush()?;
match read_char()? {
'1' => test::cursor::run(w)?,
'2' => test::color::run(w)?,
'3' => test::attribute::run(w)?,
'4' => test::event::run(w)?,
'q' => break,
_ => {}
};
}
execute!(
w,
style::ResetColor,
cursor::Show,
terminal::LeaveAlternateScreen
)?;
terminal::disable_raw_mode()
}
pub fn read_char() -> Result<char> {
loop {
if let Ok(Event::Key(KeyEvent {
code: KeyCode::Char(c),
..
})) = event::read()
{
return Ok(c);
}
}
}
pub fn buffer_size() -> Result<(u16, u16)> {
terminal::size()
}
fn main() -> Result<()> {
let mut stderr = io::stdout();
run(&mut stderr)
}

View File

@ -0,0 +1,4 @@
pub mod attribute;
pub mod color;
pub mod cursor;
pub mod event;

View File

@ -0,0 +1,52 @@
#![allow(clippy::cognitive_complexity)]
use crate::Result;
use crossterm::{cursor, queue, style};
use std::io::Write;
const ATTRIBUTES: [(style::Attribute, style::Attribute); 6] = [
(style::Attribute::Bold, style::Attribute::NoBold),
(style::Attribute::Italic, style::Attribute::NoItalic),
(style::Attribute::Underlined, style::Attribute::NoUnderline),
(style::Attribute::Reverse, style::Attribute::NoReverse),
(
style::Attribute::CrossedOut,
style::Attribute::NotCrossedOut,
),
(style::Attribute::SlowBlink, style::Attribute::NoBlink),
];
fn test_set_display_attributes<W>(w: &mut W) -> Result<()>
where
W: Write,
{
queue!(
w,
style::Print("Display attributes"),
cursor::MoveToNextLine(2)
)?;
for (on, off) in &ATTRIBUTES {
queue!(
w,
style::SetAttribute(*on),
style::Print(format!("{:>width$} ", format!("{:?}", on), width = 35)),
style::SetAttribute(*off),
style::Print(format!("{:>width$}", format!("{:?}", off), width = 35)),
style::ResetColor,
cursor::MoveToNextLine(1)
)?;
}
w.flush()?;
Ok(())
}
pub fn run<W>(w: &mut W) -> Result<()>
where
W: Write,
{
run_tests!(w, test_set_display_attributes,);
Ok(())
}

View File

@ -0,0 +1,199 @@
#![allow(clippy::cognitive_complexity)]
use crate::Result;
use crossterm::{cursor, queue, style, style::Color};
use std::io::Write;
const COLORS: [Color; 21] = [
Color::Black,
Color::DarkGrey,
Color::Grey,
Color::White,
Color::DarkRed,
Color::Red,
Color::DarkGreen,
Color::Green,
Color::DarkYellow,
Color::Yellow,
Color::DarkBlue,
Color::Blue,
Color::DarkMagenta,
Color::Magenta,
Color::DarkCyan,
Color::Cyan,
Color::AnsiValue(0),
Color::AnsiValue(15),
Color::Rgb { r: 255, g: 0, b: 0 },
Color::Rgb { r: 0, g: 255, b: 0 },
Color::Rgb { r: 0, g: 0, b: 255 },
];
fn test_set_foreground_color<W>(w: &mut W) -> Result<()>
where
W: Write,
{
queue!(
w,
style::Print("Foreground colors on the black & white background"),
cursor::MoveToNextLine(2)
)?;
for color in &COLORS {
queue!(
w,
style::SetForegroundColor(*color),
style::SetBackgroundColor(Color::Black),
style::Print(format!(
"{:>width$} ",
format!("{:?} ████████████", color),
width = 40
)),
style::SetBackgroundColor(Color::White),
style::Print(format!(
"{:>width$}",
format!("{:?} ████████████", color),
width = 40
)),
cursor::MoveToNextLine(1)
)?;
}
w.flush()?;
Ok(())
}
fn test_set_background_color<W>(w: &mut W) -> Result<()>
where
W: Write,
{
queue!(
w,
style::Print("Background colors with black & white foreground"),
cursor::MoveToNextLine(2)
)?;
for color in &COLORS {
queue!(
w,
style::SetBackgroundColor(*color),
style::SetForegroundColor(Color::Black),
style::Print(format!(
"{:>width$} ",
format!("{:?} ▒▒▒▒▒▒▒▒▒▒▒▒", color),
width = 40
)),
style::SetForegroundColor(Color::White),
style::Print(format!(
"{:>width$}",
format!("{:?} ▒▒▒▒▒▒▒▒▒▒▒▒", color),
width = 40
)),
cursor::MoveToNextLine(1)
)?;
}
w.flush()?;
Ok(())
}
fn test_color_values_matrix_16x16<W, F>(w: &mut W, title: &str, color: F) -> Result<()>
where
W: Write,
F: Fn(u16, u16) -> Color,
{
queue!(w, style::Print(title))?;
for idx in 0..=15 {
queue!(
w,
cursor::MoveTo(1, idx + 4),
style::Print(format!("{:>width$}", idx, width = 2))
)?;
queue!(
w,
cursor::MoveTo(idx * 3 + 3, 3),
style::Print(format!("{:>width$}", idx, width = 3))
)?;
}
for row in 0..=15u16 {
queue!(w, cursor::MoveTo(4, row + 4))?;
for col in 0..=15u16 {
queue!(
w,
style::SetForegroundColor(color(col, row)),
style::Print("███")
)?;
}
queue!(
w,
style::SetForegroundColor(Color::White),
style::Print(format!("{:>width$} ..= ", row * 16, width = 3)),
style::Print(format!("{:>width$}", row * 16 + 15, width = 3))
)?;
}
w.flush()?;
Ok(())
}
fn test_color_ansi_values<W>(w: &mut W) -> Result<()>
where
W: Write,
{
test_color_values_matrix_16x16(w, "Color::Ansi values", |col, row| {
Color::AnsiValue((row * 16 + col) as u8)
})
}
fn test_rgb_red_values<W>(w: &mut W) -> Result<()>
where
W: Write,
{
test_color_values_matrix_16x16(w, "Color::Rgb red values", |col, row| Color::Rgb {
r: (row * 16 + col) as u8,
g: 0 as u8,
b: 0,
})
}
fn test_rgb_green_values<W>(w: &mut W) -> Result<()>
where
W: Write,
{
test_color_values_matrix_16x16(w, "Color::Rgb green values", |col, row| Color::Rgb {
r: 0,
g: (row * 16 + col) as u8,
b: 0,
})
}
fn test_rgb_blue_values<W>(w: &mut W) -> Result<()>
where
W: Write,
{
test_color_values_matrix_16x16(w, "Color::Rgb blue values", |col, row| Color::Rgb {
r: 0,
g: 0,
b: (row * 16 + col) as u8,
})
}
pub fn run<W>(w: &mut W) -> Result<()>
where
W: Write,
{
run_tests!(
w,
test_set_foreground_color,
test_set_background_color,
test_color_ansi_values,
test_rgb_red_values,
test_rgb_green_values,
test_rgb_blue_values,
);
Ok(())
}

View File

@ -0,0 +1,207 @@
#![allow(clippy::cognitive_complexity)]
use std::io::Write;
use crate::Result;
use crossterm::{cursor, execute, queue, style, style::Colorize, Command};
use std::thread;
use std::time::Duration;
fn test_move_cursor_up<W>(w: &mut W) -> Result<()>
where
W: Write,
{
draw_cursor_box(w, "Move Up (2)", |_, _| cursor::MoveUp(2))
}
fn test_move_cursor_down<W>(w: &mut W) -> Result<()>
where
W: Write,
{
draw_cursor_box(w, "Move Down (2)", |_, _| cursor::MoveDown(2))
}
fn test_move_cursor_left<W>(w: &mut W) -> Result<()>
where
W: Write,
{
draw_cursor_box(w, "Move Left (2)", |_, _| cursor::MoveLeft(2))
}
fn test_move_cursor_right<W>(w: &mut W) -> Result<()>
where
W: Write,
{
draw_cursor_box(w, "Move Right (2)", |_, _| cursor::MoveRight(2))
}
fn test_move_cursor_to_previous_line<W>(w: &mut W) -> Result<()>
where
W: Write,
{
draw_cursor_box(w, "MoveToPreviousLine (2)", |_, _| {
cursor::MoveToPreviousLine(2)
})
}
fn test_move_cursor_to_next_line<W>(w: &mut W) -> Result<()>
where
W: Write,
{
draw_cursor_box(w, "MoveToNextLine (2)", |_, _| cursor::MoveToNextLine(2))
}
fn test_move_cursor_to_column<W>(w: &mut W) -> Result<()>
where
W: Write,
{
draw_cursor_box(w, "MoveToColumn (2)", |center_x, _| {
cursor::MoveToColumn(center_x + 2)
})
}
fn test_hide_cursor<W>(w: &mut W) -> Result<()>
where
W: Write,
{
execute!(w, style::Print("HideCursor"), cursor::Hide)
}
fn test_show_cursor<W>(w: &mut W) -> Result<()>
where
W: Write,
{
execute!(w, style::Print("ShowCursor"), cursor::Show)
}
fn test_enable_cursor_blinking<W>(w: &mut W) -> Result<()>
where
W: Write,
{
execute!(
w,
style::Print("EnableCursorBlinking"),
cursor::EnableBlinking
)
}
fn test_disable_cursor_blinking<W>(w: &mut W) -> Result<()>
where
W: Write,
{
execute!(
w,
style::Print("DisableCursorBlinking"),
cursor::DisableBlinking
)
}
fn test_move_cursor_to<W>(w: &mut W) -> Result<()>
where
W: Write,
{
draw_cursor_box(
w,
"MoveTo (x: 1, y: 1) removed from center",
|center_x, center_y| cursor::MoveTo(center_x + 1, center_y + 1),
)
}
fn test_save_restore_cursor_position<W>(w: &mut W) -> Result<()>
where
W: Write,
{
execute!(w,
cursor::MoveTo(0, 0),
style::Print("Save position, print character else were, after three seconds restore to old position."),
cursor::MoveToNextLine(2),
style::Print("Save ->[ ]<- Position"),
cursor::MoveTo(8, 2),
cursor::SavePosition,
cursor::MoveTo(10,10),
style::Print("Move To ->[√]<- Position")
)?;
thread::sleep(Duration::from_secs(3));
execute!(w, cursor::RestorePosition, style::Print(""))
}
/// Draws a box with an colored center, this center can be taken as a reference point after running the given cursor command.
fn draw_cursor_box<W, F, T>(w: &mut W, description: &str, cursor_command: F) -> Result<()>
where
W: Write,
F: Fn(u16, u16) -> T,
T: Command<AnsiType = String>,
{
execute!(
w,
cursor::Hide,
cursor::MoveTo(0, 0),
style::SetForegroundColor(style::Color::Red),
style::Print(format!(
"Red box is the center. After the action: '{}' another box is drawn.",
description
))
)?;
let start_y = 2;
let width = 21;
let height = 11 + start_y;
let center_x = width / 2;
let center_y = (height + start_y) / 2;
for row in start_y..=10 + start_y {
for column in 0..=width {
if (row == start_y || row == height - 1) || (column == 0 || column == width) {
queue!(
w,
cursor::MoveTo(column, row),
style::PrintStyledContent("".red())
)?;
} else {
queue!(
w,
cursor::MoveTo(column, row),
style::PrintStyledContent("_".red().on_white())
)?;
}
}
}
queue!(
w,
cursor::MoveTo(center_x, center_y),
style::PrintStyledContent("".red().on_white())
)?;
queue!(
w,
cursor_command(center_x, center_y),
style::PrintStyledContent("".magenta().on_white())
)?;
w.flush()?;
Ok(())
}
pub fn run<W>(w: &mut W) -> Result<()>
where
W: Write,
{
run_tests!(
w,
test_hide_cursor,
test_show_cursor,
test_enable_cursor_blinking,
test_disable_cursor_blinking,
test_move_cursor_left,
test_move_cursor_right,
test_move_cursor_up,
test_move_cursor_down,
test_move_cursor_to,
test_move_cursor_to_next_line,
test_move_cursor_to_previous_line,
test_move_cursor_to_column,
test_save_restore_cursor_position
);
Ok(())
}

View File

@ -0,0 +1,40 @@
#![allow(clippy::cognitive_complexity)]
use crossterm::{
cursor::position,
event::{read, EnableMouseCapture, Event, KeyCode},
execute, Result,
};
use std::io::Write;
fn test_event<W>(w: &mut W) -> Result<()>
where
W: Write,
{
execute!(w, EnableMouseCapture)?;
loop {
// Blocking read
let event = read()?;
println!("Event::{:?}\r", event);
if event == Event::Key(KeyCode::Char('c').into()) {
println!("Cursor position: {:?}\r", position());
}
if event == Event::Key(KeyCode::Char('q').into()) {
break;
}
}
Ok(())
}
pub fn run<W>(w: &mut W) -> Result<()>
where
W: Write,
{
run_tests!(w, test_event);
Ok(())
}

93
examples/stderr.rs Normal file
View File

@ -0,0 +1,93 @@
//! This shows how an application can write on stderr
//! instead of stdout, thus making it possible to
//! the command API instead of the "old style" direct
//! unbuffered API.
//!
//! This particular example is only suited to Unix
//! for now.
use std::io::{stderr, Write};
use crossterm::event::{Event, KeyCode, KeyEvent};
use crossterm::{
cursor::{Hide, MoveTo, Show},
event, execute, queue,
style::Print,
terminal::{self, EnterAlternateScreen, LeaveAlternateScreen},
Result,
};
const TEXT: &str = r#"
This screen is ran on stderr.
And when you hit enter, it prints on stdout.
This makes it possible to run an application and choose what will
be sent to any application calling yours.
For example, assuming you build this example with
cargo build --bin stderr
and then you run it with
cd "$(target/debug/stderr)"
what the application prints on stdout is used as argument to cd.
Try it out.
Hit any key to quit this screen:
1 will print `..`
2 will print `/`
3 will print `~`
Any other key will print this text (so that you may copy-paste)
"#;
fn run_app<W>(write: &mut W) -> Result<char>
where
W: Write,
{
queue!(
write,
EnterAlternateScreen, // enter alternate screen
Hide // hide the cursor
)?;
let mut y = 1;
for line in TEXT.split('\n') {
queue!(write, MoveTo(1, y), Print(line.to_string()))?;
y += 1;
}
write.flush()?;
terminal::enable_raw_mode()?;
let user_char = read_char()?; // we wait for the user to hit a key
execute!(write, Show, LeaveAlternateScreen)?; // restore the cursor and leave the alternate screen
terminal::disable_raw_mode()?;
Ok(user_char)
}
pub fn read_char() -> Result<char> {
loop {
if let Event::Key(KeyEvent {
code: KeyCode::Char(c),
..
}) = event::read()?
{
return Ok(c);
}
}
}
// cargo run --example stderr
fn main() {
match run_app(&mut stderr()).unwrap() {
'1' => print!(".."),
'2' => print!("/"),
'3' => print!("~"),
_ => println!("{}", TEXT),
}
}