Moved examples back into crossterm (#332)
This commit is contained in:
parent
8fb9059853
commit
47e8366f2b
36
examples/README.md
Normal file
36
examples/README.md
Normal 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
|
63
examples/event-match-modifiers.rs
Normal file
63
examples/event-match-modifiers.rs
Normal 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,
|
||||
}));
|
||||
}
|
@ -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,
|
||||
|
45
examples/event-read-char-line.rs
Normal file
45
examples/event-read-char-line.rs
Normal 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());
|
||||
}
|
@ -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());
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
13
examples/interactive-demo/Cargo.toml
Normal file
13
examples/interactive-demo/Cargo.toml
Normal 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 = "../../" }
|
29
examples/interactive-demo/src/macros.rs
Normal file
29
examples/interactive-demo/src/macros.rs
Normal 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),
|
||||
_ => { },
|
||||
};
|
||||
)*
|
||||
}
|
||||
}
|
96
examples/interactive-demo/src/main.rs
Normal file
96
examples/interactive-demo/src/main.rs
Normal 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)
|
||||
}
|
4
examples/interactive-demo/src/test.rs
Normal file
4
examples/interactive-demo/src/test.rs
Normal file
@ -0,0 +1,4 @@
|
||||
pub mod attribute;
|
||||
pub mod color;
|
||||
pub mod cursor;
|
||||
pub mod event;
|
52
examples/interactive-demo/src/test/attribute.rs
Normal file
52
examples/interactive-demo/src/test/attribute.rs
Normal 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(())
|
||||
}
|
199
examples/interactive-demo/src/test/color.rs
Normal file
199
examples/interactive-demo/src/test/color.rs
Normal 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(())
|
||||
}
|
207
examples/interactive-demo/src/test/cursor.rs
Normal file
207
examples/interactive-demo/src/test/cursor.rs
Normal 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(())
|
||||
}
|
40
examples/interactive-demo/src/test/event.rs
Normal file
40
examples/interactive-demo/src/test/event.rs
Normal 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
93
examples/stderr.rs
Normal 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),
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user