2018-07-25 02:47:41 +03:00
|
|
|
//! This example shows how to make a basic widget that accumulates
|
|
|
|
//! text input and renders it to the screen
|
2020-01-10 08:52:47 +03:00
|
|
|
#![allow(unused)]
|
2018-07-24 23:49:47 +03:00
|
|
|
use termwiz::caps::Capabilities;
|
2018-07-28 23:21:56 +03:00
|
|
|
use termwiz::cell::AttributeChange;
|
2022-07-27 07:26:20 +03:00
|
|
|
use termwiz::color::{AnsiColor, ColorAttribute};
|
2018-07-24 23:49:47 +03:00
|
|
|
use termwiz::input::*;
|
2018-07-29 22:45:18 +03:00
|
|
|
use termwiz::surface::Change;
|
2018-07-24 23:49:47 +03:00
|
|
|
use termwiz::terminal::buffered::BufferedTerminal;
|
|
|
|
use termwiz::terminal::{new_terminal, Terminal};
|
2020-01-10 08:52:47 +03:00
|
|
|
#[cfg(feature = "widgets")]
|
2018-07-24 23:49:47 +03:00
|
|
|
use termwiz::widgets::*;
|
2021-01-08 11:21:54 +03:00
|
|
|
use termwiz::Error;
|
2018-07-24 23:49:47 +03:00
|
|
|
|
2018-07-29 22:45:18 +03:00
|
|
|
/// This is a widget for our application
|
|
|
|
struct MainScreen<'a> {
|
|
|
|
/// Holds the input text that we wish the widget to display
|
2018-08-01 17:56:24 +03:00
|
|
|
text: &'a mut String,
|
2018-07-24 23:49:47 +03:00
|
|
|
}
|
|
|
|
|
2018-07-29 22:45:18 +03:00
|
|
|
impl<'a> MainScreen<'a> {
|
|
|
|
/// Initialize the widget with the input text
|
2018-08-01 17:56:24 +03:00
|
|
|
pub fn new(text: &'a mut String) -> Self {
|
2018-07-29 22:45:18 +03:00
|
|
|
Self { text }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-10 08:52:47 +03:00
|
|
|
#[cfg(feature = "widgets")]
|
2018-07-29 22:45:18 +03:00
|
|
|
impl<'a> Widget for MainScreen<'a> {
|
2018-08-01 17:56:24 +03:00
|
|
|
fn process_event(&mut self, event: &WidgetEvent, _args: &mut UpdateArgs) -> bool {
|
|
|
|
match event {
|
|
|
|
WidgetEvent::Input(InputEvent::Key(KeyEvent {
|
|
|
|
key: KeyCode::Char(c),
|
|
|
|
..
|
|
|
|
})) => self.text.push(*c),
|
|
|
|
WidgetEvent::Input(InputEvent::Key(KeyEvent {
|
|
|
|
key: KeyCode::Enter,
|
|
|
|
..
|
|
|
|
})) => {
|
|
|
|
self.text.push_str("\r\n");
|
|
|
|
}
|
|
|
|
WidgetEvent::Input(InputEvent::Paste(s)) => {
|
|
|
|
self.text.push_str(&s);
|
2018-07-24 23:49:47 +03:00
|
|
|
}
|
2018-08-01 17:56:24 +03:00
|
|
|
_ => {}
|
2018-07-24 23:49:47 +03:00
|
|
|
}
|
|
|
|
|
2018-08-01 17:56:24 +03:00
|
|
|
true // handled it all
|
|
|
|
}
|
2018-07-29 22:45:18 +03:00
|
|
|
|
2018-08-01 17:56:24 +03:00
|
|
|
/// Draw ourselves into the surface provided by RenderArgs
|
|
|
|
fn render(&mut self, args: &mut RenderArgs) {
|
|
|
|
args.surface.add_change(Change::ClearScreen(
|
2018-07-28 23:21:56 +03:00
|
|
|
ColorAttribute::TrueColorWithPaletteFallback(
|
2022-07-27 07:26:20 +03:00
|
|
|
(0x31, 0x1B, 0x92).into(),
|
2018-07-28 23:21:56 +03:00
|
|
|
AnsiColor::Black.into(),
|
|
|
|
),
|
|
|
|
));
|
2018-08-01 17:56:24 +03:00
|
|
|
args.surface
|
|
|
|
.add_change(Change::Attribute(AttributeChange::Foreground(
|
|
|
|
ColorAttribute::TrueColorWithPaletteFallback(
|
2022-07-27 07:26:20 +03:00
|
|
|
(0xB3, 0x88, 0xFF).into(),
|
2018-08-01 17:56:24 +03:00
|
|
|
AnsiColor::Purple.into(),
|
|
|
|
),
|
|
|
|
)));
|
|
|
|
let dims = args.surface.dimensions();
|
|
|
|
args.surface
|
|
|
|
.add_change(format!("🤷 surface size is {:?}\r\n", dims));
|
|
|
|
args.surface.add_change(self.text.clone());
|
2018-07-25 02:47:41 +03:00
|
|
|
|
2018-07-29 22:45:18 +03:00
|
|
|
// Place the cursor at the end of the text.
|
|
|
|
// A more advanced text editing widget would manage the
|
|
|
|
// cursor position differently.
|
2018-08-01 17:56:24 +03:00
|
|
|
*args.cursor = CursorShapeAndPosition {
|
|
|
|
coords: args.surface.cursor_position().into(),
|
2018-07-27 01:33:57 +03:00
|
|
|
shape: termwiz::surface::CursorShape::SteadyBar,
|
2018-07-25 02:47:41 +03:00
|
|
|
..Default::default()
|
2018-07-29 22:45:18 +03:00
|
|
|
};
|
2018-07-24 23:49:47 +03:00
|
|
|
}
|
2018-08-02 07:23:24 +03:00
|
|
|
|
|
|
|
fn get_size_constraints(&self) -> layout::Constraints {
|
|
|
|
layout::Constraints::with_fixed_width_height(80, 24)
|
|
|
|
}
|
2018-07-24 23:49:47 +03:00
|
|
|
}
|
|
|
|
|
2020-01-10 08:52:47 +03:00
|
|
|
#[cfg(feature = "widgets")]
|
2018-07-24 23:49:47 +03:00
|
|
|
fn main() -> Result<(), Error> {
|
2018-07-29 22:45:18 +03:00
|
|
|
// Start with an empty string; typing into the app will
|
|
|
|
// update this string.
|
|
|
|
let mut typed_text = String::new();
|
2018-07-24 23:49:47 +03:00
|
|
|
|
2018-07-29 22:45:18 +03:00
|
|
|
{
|
|
|
|
// Create a terminal and put it into full screen raw mode
|
|
|
|
let caps = Capabilities::new_from_env()?;
|
|
|
|
let mut buf = BufferedTerminal::new(new_terminal(caps)?)?;
|
|
|
|
buf.terminal().set_raw_mode()?;
|
2021-12-22 22:21:39 +03:00
|
|
|
buf.terminal().enter_alternate_screen()?;
|
2018-07-24 23:49:47 +03:00
|
|
|
|
2018-07-29 22:45:18 +03:00
|
|
|
// Set up the UI
|
|
|
|
let mut ui = Ui::new();
|
2018-08-01 17:56:24 +03:00
|
|
|
|
|
|
|
ui.set_root(MainScreen::new(&mut typed_text));
|
2018-07-24 23:49:47 +03:00
|
|
|
|
2018-07-29 22:45:18 +03:00
|
|
|
loop {
|
2018-08-01 17:56:24 +03:00
|
|
|
ui.process_event_queue()?;
|
2018-07-24 23:49:47 +03:00
|
|
|
|
2018-07-29 22:45:18 +03:00
|
|
|
// After updating and processing all of the widgets, compose them
|
|
|
|
// and render them to the screen.
|
|
|
|
if ui.render_to_screen(&mut buf)? {
|
|
|
|
// We have more events to process immediately; don't block waiting
|
|
|
|
// for input below, but jump to the top of the loop to re-run the
|
|
|
|
// updates.
|
|
|
|
continue;
|
2018-07-27 18:48:34 +03:00
|
|
|
}
|
2018-07-29 22:45:18 +03:00
|
|
|
// Compute an optimized delta to apply to the terminal and display it
|
|
|
|
buf.flush()?;
|
|
|
|
|
|
|
|
// Wait for user input
|
2019-04-28 19:31:35 +03:00
|
|
|
match buf.terminal().poll_input(None) {
|
2018-07-29 22:45:18 +03:00
|
|
|
Ok(Some(InputEvent::Resized { rows, cols })) => {
|
|
|
|
// FIXME: this is working around a bug where we don't realize
|
|
|
|
// that we should redraw everything on resize in BufferedTerminal.
|
|
|
|
buf.add_change(Change::ClearScreen(Default::default()));
|
|
|
|
buf.resize(cols, rows);
|
2018-07-24 23:49:47 +03:00
|
|
|
}
|
2018-07-29 22:45:18 +03:00
|
|
|
Ok(Some(input)) => match input {
|
|
|
|
InputEvent::Key(KeyEvent {
|
|
|
|
key: KeyCode::Escape,
|
|
|
|
..
|
|
|
|
}) => {
|
|
|
|
// Quit the app when escape is pressed
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
input @ _ => {
|
|
|
|
// Feed input into the Ui
|
|
|
|
ui.queue_event(WidgetEvent::Input(input));
|
|
|
|
}
|
|
|
|
},
|
|
|
|
Ok(None) => {}
|
|
|
|
Err(e) => {
|
|
|
|
print!("{:?}\r\n", e);
|
|
|
|
break;
|
2018-07-24 23:49:47 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-29 22:45:18 +03:00
|
|
|
// After we've stopped the full screen raw terminal,
|
|
|
|
// print out the final edited value of the input text.
|
|
|
|
println!("The text you entered: {}", typed_text);
|
|
|
|
|
2018-07-24 23:49:47 +03:00
|
|
|
Ok(())
|
|
|
|
}
|
2020-01-10 08:52:47 +03:00
|
|
|
|
|
|
|
#[cfg(not(feature = "widgets"))]
|
|
|
|
fn main() {
|
|
|
|
println!("recompile with --features widgets");
|
|
|
|
}
|