Add comments to examples

This commit is contained in:
Alexandre Bury 2018-01-15 18:55:27 -08:00
parent 51eb6bf34c
commit 868e59a4fa
18 changed files with 162 additions and 62 deletions

View File

@ -5,6 +5,17 @@ use cursive::theme::{Color, ColorStyle};
use cursive::view::Boxable; use cursive::view::Boxable;
use cursive::views::Canvas; use cursive::views::Canvas;
// This example will draw a colored square with a gradient.
//
// We'll use a Canvas, which lets us only define a draw method.
//
// We will combine 2 gradients: one for the background,
// and one for the foreground.
//
// Note: color reproduction is not as good on all backends.
// termion can do full 16M true colors, but ncurses is currently limited to
// 256 colors.
fn main() { fn main() {
let mut siv = Cursive::new(); let mut siv = Cursive::new();
@ -15,28 +26,14 @@ fn main() {
siv.run(); siv.run();
} }
fn front_color(x: u8, y: u8, x_max: u8, y_max: u8) -> Color {
Color::Rgb(
x * (255 / x_max),
y * (255 / y_max),
(x + 2 * y) * (255 / (x_max + 2 * y_max)),
)
}
fn back_color(x: u8, y: u8, x_max: u8, y_max: u8) -> Color {
Color::Rgb(
128 + (2 * y_max + x - 2 * y) * (128 / (x_max + 2 * y_max)),
255 - y * (255 / y_max),
255 - x * (255 / x_max),
)
}
fn draw(_: &(), p: &Printer) { fn draw(_: &(), p: &Printer) {
// We use the view size to calibrate the color
let x_max = p.size.x as u8; let x_max = p.size.x as u8;
let y_max = p.size.y as u8; let y_max = p.size.y as u8;
for x in 0..x_max { for x in 0..x_max {
for y in 0..y_max { for y in 0..y_max {
// We'll use a different style for each cell
let style = ColorStyle::Custom { let style = ColorStyle::Custom {
front: front_color(x, y, x_max, y_max), front: front_color(x, y, x_max, y_max),
back: back_color(x, y, x_max, y_max), back: back_color(x, y, x_max, y_max),
@ -48,3 +45,21 @@ fn draw(_: &(), p: &Printer) {
} }
} }
} }
// Gradient for the front color
fn front_color(x: u8, y: u8, x_max: u8, y_max: u8) -> Color {
Color::Rgb(
x * (255 / x_max),
y * (255 / y_max),
(x + 2 * y) * (255 / (x_max + 2 * y_max)),
)
}
// Gradient for the background color
fn back_color(x: u8, y: u8, x_max: u8, y_max: u8) -> Color {
Color::Rgb(
128 + (2 * y_max + x - 2 * y) * (128 / (x_max + 2 * y_max)),
255 - y * (255 / y_max),
255 - x * (255 / x_max),
)
}

View File

@ -9,6 +9,7 @@ fn main() {
// Creates a dialog with a single "Quit" button // Creates a dialog with a single "Quit" button
siv.add_layer( siv.add_layer(
// Most views can be configured in a chainable way
Dialog::around(TextView::new("Hello Dialog!")) Dialog::around(TextView::new("Hello Dialog!"))
.title("Cursive") .title("Cursive")
.button("Quit", |s| s.quit()), .button("Quit", |s| s.quit()),

View File

@ -13,6 +13,7 @@ fn main() {
siv.add_layer( siv.add_layer(
Dialog::new() Dialog::new()
.title("Enter your name") .title("Enter your name")
// Padding is (left, right, top, bottom)
.padding((1, 1, 1, 0)) .padding((1, 1, 1, 0))
.content( .content(
EditView::new() EditView::new()
@ -21,9 +22,14 @@ fn main() {
.fixed_width(20), .fixed_width(20),
) )
.button("Ok", |s| { .button("Ok", |s| {
// This will run the given closure, *ONLY* if a view with the
// correct type and the given ID is found.
let name = s.call_on_id("name", |view: &mut EditView| { let name = s.call_on_id("name", |view: &mut EditView| {
// We can return content from the closure!
view.get_content() view.get_content()
}).unwrap(); }).unwrap();
// Run the next step
show_popup(s, &name); show_popup(s, &name);
}), }),
); );
@ -31,6 +37,8 @@ fn main() {
siv.run(); siv.run();
} }
// This will replace the current layer with a new popup.
// If the name is empty, we'll show an error message instead.
fn show_popup(s: &mut Cursive, name: &str) { fn show_popup(s: &mut Cursive, name: &str) {
if name.is_empty() { if name.is_empty() {
s.add_layer(Dialog::info("Please enter a name!")); s.add_layer(Dialog::info("Please enter a name!"));

View File

@ -9,10 +9,12 @@ fn main() {
// We can quit by pressing `q` // We can quit by pressing `q`
siv.add_global_callback('q', Cursive::quit); siv.add_global_callback('q', Cursive::quit);
// Add a simple view
siv.add_layer(TextView::new( siv.add_layer(TextView::new(
"Hello World!\n\ "Hello World!\n\
Press q to quit the application.", Press q to quit the application.",
)); ));
// Run the event loop
siv.run(); siv.run();
} }

View File

@ -4,14 +4,17 @@ use cursive::{Cursive, Printer};
use cursive::event::{Event, EventResult}; use cursive::event::{Event, EventResult};
use cursive::traits::*; use cursive::traits::*;
// This example define a custom view that prints any event it receives.
// This is a handy way to check the input received by cursive.
fn main() { fn main() {
let mut siv = Cursive::new(); let mut siv = Cursive::new();
siv.add_layer(KeyCodeView::new(10).full_width().fixed_height(10)); siv.add_layer(KeyCodeView::new(10).full_width().fixed_height(10));
siv.run(); siv.run();
} }
// Our view will have a small history of the last events.
struct KeyCodeView { struct KeyCodeView {
history: Vec<String>, history: Vec<String>,
size: usize, size: usize,
@ -28,19 +31,23 @@ impl KeyCodeView {
impl View for KeyCodeView { impl View for KeyCodeView {
fn draw(&self, printer: &Printer) { fn draw(&self, printer: &Printer) {
// We simply draw every event from the history.
for (y, line) in self.history.iter().enumerate() { for (y, line) in self.history.iter().enumerate() {
printer.print((0, y), line); printer.print((0, y), line);
} }
} }
fn on_event(&mut self, event: Event) -> EventResult { fn on_event(&mut self, event: Event) -> EventResult {
// Each line will be a debug-format of the event.
let line = format!("{:?}", event); let line = format!("{:?}", event);
self.history.push(line); self.history.push(line);
// Keep a fixed-sized history.
while self.history.len() > self.size { while self.history.len() > self.size {
self.history.remove(0); self.history.remove(0);
} }
// No need to return any callback.
EventResult::Consumed(None) EventResult::Consumed(None)
} }
} }

View File

@ -3,7 +3,9 @@ extern crate cursive;
use cursive::Cursive; use cursive::Cursive;
use cursive::align::HAlign; use cursive::align::HAlign;
use cursive::traits::*; use cursive::traits::*;
use cursive::views::{Dialog, LinearLayout, TextView}; use cursive::views::{Dialog, DummyView, LinearLayout, TextView};
// This example uses a LinearLayout to stick multiple views next to each other.
fn main() { fn main() {
let mut siv = Cursive::new(); let mut siv = Cursive::new();
@ -18,12 +20,16 @@ fn main() {
Dialog::around( Dialog::around(
LinearLayout::vertical() LinearLayout::vertical()
.child(TextView::new("Title").h_align(HAlign::Center)) .child(TextView::new("Title").h_align(HAlign::Center))
// Box the textview, so it doesn't get too wide. // Dummy views can be used for spacing
// A 0 height value means it will be unconstrained. .child(DummyView.fixed_height(1))
.child(TextView::new(text).scrollable(false).fixed_width(30)) // Disabling scrolling means the view cannot shrink.
.child(TextView::new(text).fixed_width(30)) // Try resizing the window, and see what happens!
.child(TextView::new(text).fixed_width(30)) .child(TextView::new(text).scrollable(false))
.child(TextView::new(text).fixed_width(30)), .child(TextView::new(text))
.child(TextView::new(text))
.child(TextView::new(text))
// Give everything a fixed width so it doesn't get too wide
.fixed_width(30),
).button("Quit", |s| s.quit()) ).button("Quit", |s| s.quit())
.h_align(HAlign::Center), .h_align(HAlign::Center),
); );

View File

@ -5,6 +5,10 @@ use cursive::traits::*;
use cursive::views::{Checkbox, Dialog, EditView, LinearLayout, ListView, use cursive::views::{Checkbox, Dialog, EditView, LinearLayout, ListView,
SelectView, TextView}; SelectView, TextView};
// This example uses a ListView.
//
// ListView can be used to build forms, with a list of inputs.
fn main() { fn main() {
let mut siv = Cursive::new(); let mut siv = Cursive::new();
@ -14,10 +18,12 @@ fn main() {
.button("Ok", |s| s.quit()) .button("Ok", |s| s.quit())
.content( .content(
ListView::new() ListView::new()
// Each child is a single-line view with a label
.child("Name", EditView::new().fixed_width(10)) .child("Name", EditView::new().fixed_width(10))
.child( .child(
"Receive spam?", "Receive spam?",
Checkbox::new().on_change(|s, checked| { Checkbox::new().on_change(|s, checked| {
// Enable/Disable the next field depending on this checkbox
for name in &["email1", "email2"] { for name in &["email1", "email2"] {
s.call_on_id(name, |view: &mut EditView| { s.call_on_id(name, |view: &mut EditView| {
view.set_enabled(checked) view.set_enabled(checked)
@ -30,6 +36,8 @@ fn main() {
) )
.child( .child(
"Email", "Email",
// Each child must have a height of 1 line,
// but we can still combine multiple views!
LinearLayout::horizontal() LinearLayout::horizontal()
.child( .child(
EditView::new() EditView::new()
@ -45,9 +53,11 @@ fn main() {
.fixed_width(10), .fixed_width(10),
), ),
) )
// Delimiter currently are just a blank line
.delimiter() .delimiter()
.child( .child(
"Age", "Age",
// Popup-mode SelectView are small enough to fit here
SelectView::new() SelectView::new()
.popup() .popup()
.item_str("0-18") .item_str("0-18")
@ -56,6 +66,7 @@ fn main() {
.item_str("41+"), .item_str("41+"),
) )
.with(|list| { .with(|list| {
// We can also add children procedurally
for i in 0..50 { for i in 0..50 {
list.add_child( list.add_child(
&format!("Item {}", i), &format!("Item {}", i),

View File

@ -8,6 +8,10 @@ use std::sync::mpsc;
use std::thread; use std::thread;
use std::time::Duration; use std::time::Duration;
// This example will print a stream of logs generated from a separate thread.
//
// We will use a custom view using a channel to receive data asynchronously.
fn main() { fn main() {
// As usual, create the Cursive root // As usual, create the Cursive root
let mut siv = Cursive::new(); let mut siv = Cursive::new();

View File

@ -18,6 +18,7 @@ fn main() {
// and will adapt to the terminal size. // and will adapt to the terminal size.
siv.add_fullscreen_layer( siv.add_fullscreen_layer(
Dialog::around(Panel::new(TextView::new(content))) Dialog::around(Panel::new(TextView::new(content)))
// This is the alignment for the button
.h_align(HAlign::Center) .h_align(HAlign::Center)
.button("Quit", |s| s.quit()) .button("Quit", |s| s.quit())
.full_screen(), .full_screen(),

View File

@ -12,12 +12,15 @@ use cursive::views::{Dialog, TextView};
fn main() { fn main() {
let mut siv = Cursive::new(); let mut siv = Cursive::new();
// If markdown is enabled, parse a small text.
#[cfg(feature = "markdown")] #[cfg(feature = "markdown")]
let text = markdown::parse("Isn't *that* **cool**?"); let text = markdown::parse("Isn't *that* **cool**?");
#[cfg(not(feature = "markdown"))] #[cfg(not(feature = "markdown"))]
let text = "Rebuild with --features markdown ;)"; let text = "Rebuild with --features markdown ;)";
// TextView can natively accept StyledString.
siv.add_layer( siv.add_layer(
Dialog::around(TextView::new(text)).button("Hell yeah!", |s| s.quit()), Dialog::around(TextView::new(text)).button("Hell yeah!", |s| s.quit()),
); );

View File

@ -7,6 +7,9 @@ use cursive::traits::*;
use cursive::views::Dialog; use cursive::views::Dialog;
use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::atomic::{AtomicUsize, Ordering};
// This examples shows how to configure and use a menubar at the top of the
// application.
fn main() { fn main() {
let mut siv = Cursive::new(); let mut siv = Cursive::new();

View File

@ -5,26 +5,7 @@ use cursive::traits::*;
use cursive::view::{Offset, Position}; use cursive::view::{Offset, Position};
use cursive::views::{Dialog, OnEventView, TextView}; use cursive::views::{Dialog, OnEventView, TextView};
fn show_popup(siv: &mut Cursive) { // This example modifies a view after creation.
// Let's center the popup horizontally, but offset it down a few rows
siv.screen_mut().add_layer_at(
Position::new(Offset::Center, Offset::Parent(3)),
Dialog::around(TextView::new("Tak!"))
.button("Change", |s| {
// Look for a view tagged "text".
// We _know_ it's there, so unwrap it.
s.call_on_id("text", |view: &mut TextView| {
let content = reverse(view.get_content().source());
view.set_content(content);
});
})
.dismiss_button("Ok"),
);
}
fn reverse(text: &str) -> String {
text.chars().rev().collect()
}
fn main() { fn main() {
let mut siv = Cursive::new(); let mut siv = Cursive::new();
@ -44,3 +25,29 @@ fn main() {
siv.run(); siv.run();
} }
fn show_popup(siv: &mut Cursive) {
// Let's center the popup horizontally, but offset it down a few rows,
// so the user can see both the popup and the view underneath.
siv.screen_mut().add_layer_at(
Position::new(Offset::Center, Offset::Parent(5)),
Dialog::around(TextView::new("Tak!"))
.button("Change", |s| {
// Look for a view tagged "text".
// We _know_ it's there, so unwrap it.
s.call_on_id("text", |view: &mut TextView| {
let content = reverse(view.get_content().source());
view.set_content(content);
});
})
.dismiss_button("Ok"),
);
}
// This just reverses each character
//
// Note: it would be more correct to iterate on graphemes instead.
// Check the unicode_segmentation crate!
fn reverse(text: &str) -> String {
text.chars().rev().collect()
}

View File

@ -10,6 +10,12 @@ use std::cmp::min;
use std::thread; use std::thread;
use std::time::Duration; use std::time::Duration;
// This example shows a ProgressBar reporting the status from an asynchronous
// job.
//
// It works by sharing a counter with the job thread. This counter can be
// "ticked" to indicate progress.
fn main() { fn main() {
let mut siv = Cursive::new(); let mut siv = Cursive::new();
@ -27,15 +33,6 @@ fn main() {
siv.run(); siv.run();
} }
// Function to simulate a long process.
fn fake_load(n_max: usize, counter: &Counter) {
for _ in 0..n_max {
thread::sleep(Duration::from_millis(5));
// The `counter.tick()` method increases the progress value
counter.tick(1);
}
}
fn phase_1(s: &mut Cursive) { fn phase_1(s: &mut Cursive) {
// Phase 1 is easy: a simple pre-loading. // Phase 1 is easy: a simple pre-loading.
@ -48,6 +45,7 @@ fn phase_1(s: &mut Cursive) {
s.pop_layer(); s.pop_layer();
s.add_layer(Dialog::around( s.add_layer(Dialog::around(
ProgressBar::new() ProgressBar::new()
// We need to know how many ticks represent a full bar.
.range(0, n_max) .range(0, n_max)
.with_task(move |counter| { .with_task(move |counter| {
// This closure will be called in a separate thread. // This closure will be called in a separate thread.
@ -131,3 +129,12 @@ fn final_step(s: &mut Cursive) {
.button("That's it?", |s| s.quit()), .button("That's it?", |s| s.quit()),
); );
} }
// Function to simulate a long process.
fn fake_load(n_max: usize, counter: &Counter) {
for _ in 0..n_max {
thread::sleep(Duration::from_millis(5));
// The `counter.tick()` method increases the progress value
counter.tick(1);
}
}

View File

@ -3,6 +3,8 @@ extern crate cursive;
use cursive::Cursive; use cursive::Cursive;
use cursive::views::{Dialog, DummyView, LinearLayout, RadioGroup}; use cursive::views::{Dialog, DummyView, LinearLayout, RadioGroup};
// This example uses radio buttons.
fn main() { fn main() {
let mut siv = Cursive::new(); let mut siv = Cursive::new();
@ -17,13 +19,16 @@ fn main() {
.content(LinearLayout::horizontal() .content(LinearLayout::horizontal()
.child(LinearLayout::vertical() .child(LinearLayout::vertical()
// The color group uses the label itself as stored value // The color group uses the label itself as stored value
// By default, the first item is selected.
.child(color_group.button_str("Red")) .child(color_group.button_str("Red"))
.child(color_group.button_str("Green")) .child(color_group.button_str("Green"))
.child(color_group.button_str("Blue"))) .child(color_group.button_str("Blue")))
// A DummyView is used as a spacer
.child(DummyView) .child(DummyView)
.child(LinearLayout::vertical() .child(LinearLayout::vertical()
// For the size, we store a number separately // For the size, we store a number separately
.child(size_group.button(5, "Small")) .child(size_group.button(5, "Small"))
// The initial selection can also be overriden
.child(size_group.button(15, "Medium").selected()) .child(size_group.button(15, "Medium").selected())
// The large size is out of stock, sorry! // The large size is out of stock, sorry!
.child(size_group.button(25, "Large").disabled()))) .child(size_group.button(25, "Large").disabled())))
@ -34,9 +39,8 @@ fn main() {
s.pop_layer(); s.pop_layer();
// And we simply print the result. // And we simply print the result.
s.add_layer(Dialog::text(format!("Color: {}\nSize: {}cm", let text = format!("Color: {}\nSize: {}cm", color, size);
color, s.add_layer(Dialog::text(text)
size))
.button("Ok", |s| s.quit())); .button("Ok", |s| s.quit()));
}), }),
); );

View File

@ -4,6 +4,8 @@ use cursive::Cursive;
use cursive::view::{Boxable, Identifiable}; use cursive::view::{Boxable, Identifiable};
use cursive::views::{Dialog, EditView, LinearLayout, TextView}; use cursive::views::{Dialog, EditView, LinearLayout, TextView};
// This example shows a way to access multiple views at the same time.
fn main() { fn main() {
let mut siv = Cursive::new(); let mut siv = Cursive::new();
@ -34,6 +36,7 @@ fn on_edit(siv: &mut Cursive, _content: &str, _cursor: usize) {
// Directly compare references to edit_1 and edit_2. // Directly compare references to edit_1 and edit_2.
let matches = edit_1.get_content() == edit_2.get_content(); let matches = edit_1.get_content() == edit_2.get_content();
siv.call_on_id("match", |v: &mut TextView| { siv.call_on_id("match", |v: &mut TextView| {
v.set_content(if matches { "match" } else { "no match" }) v.set_content(if matches { "match" } else { "no match" })
}); });

View File

@ -6,6 +6,11 @@ use cursive::event::EventResult;
use cursive::traits::*; use cursive::traits::*;
use cursive::views::{Dialog, OnEventView, SelectView, TextView}; use cursive::views::{Dialog, OnEventView, SelectView, TextView};
// We'll use a SelectView here.
//
// A SelectView is a scrollable list of items, from which the user can select
// one.
fn main() { fn main() {
let mut select = SelectView::new().h_align(HAlign::Center); let mut select = SelectView::new().h_align(HAlign::Center);

View File

@ -7,12 +7,16 @@ use cursive::views::{Dialog, SliderView};
fn main() { fn main() {
let mut siv = Cursive::new(); let mut siv = Cursive::new();
siv.add_global_callback('q', |s| s.quit());
// Let's add a simple slider in a dialog. // Let's add a simple slider in a dialog.
// Moving the slider will update the dialog's title. // Moving the slider will update the dialog's title.
// And pressing "Enter" will show a new dialog. // And pressing "Enter" will show a new dialog.
siv.add_layer( siv.add_layer(
Dialog::around( Dialog::around(
// We give the number of steps in the constructor
SliderView::horizontal(15) SliderView::horizontal(15)
// Sets the initial value
.value(7) .value(7)
.on_change(|s, v| { .on_change(|s, v| {
let title = format!("[ {} ]", v); let title = format!("[ {} ]", v);

View File

@ -4,19 +4,19 @@ use cursive::Cursive;
use cursive::theme::{Color, Theme}; use cursive::theme::{Color, Theme};
use cursive::views::TextView; use cursive::views::TextView;
fn custom_theme_from_cursive(siv: &Cursive) -> Theme { // This example sets the background color to the terminal default.
let mut theme = siv.current_theme().clone(); //
theme.colors.background = Color::TerminalDefault; // This way, it looks more natural.
theme
}
fn main() { fn main() {
let mut siv = Cursive::new(); let mut siv = Cursive::new();
let theme = custom_theme_from_cursive(&siv); let theme = custom_theme_from_cursive(&siv);
siv.set_theme(theme);
// We can quit by pressing `q` // We can quit by pressing `q`
siv.add_global_callback('q', Cursive::quit); siv.add_global_callback('q', Cursive::quit);
siv.set_theme(theme);
siv.add_layer(TextView::new( siv.add_layer(TextView::new(
"Hello World with default terminal background color!\n\ "Hello World with default terminal background color!\n\
@ -25,3 +25,12 @@ fn main() {
siv.run(); siv.run();
} }
fn custom_theme_from_cursive(siv: &Cursive) -> Theme {
// We'll return the current theme with a small modification.
let mut theme = siv.current_theme().clone();
theme.colors.background = Color::TerminalDefault;
theme
}