Merge branch 'master' into scroll

This commit is contained in:
Alexandre Bury 2018-04-16 22:42:42 -07:00
commit f0b3287d73
65 changed files with 844 additions and 424 deletions

View File

@ -4,11 +4,6 @@ rust:
- stable - stable
- nightly - nightly
script: script:
- cargo build --verbose - cargo check --all-features
- cargo test --verbose - cargo build --verbose --features "pancurses-backend termion-backend"
- - cargo test --verbose --features "pancurses-backend termion-backend"
- cargo build --verbose --features=pancurses-backend --no-default-features
- cargo test --verbose --features=pancurses-backend --no-default-features
-
- cargo build --verbose --features=termion-backend --no-default-features
- cargo test --verbose --features=termion-backend --no-default-features

View File

@ -1,6 +1,29 @@
# Changelog # Changelog
## Next version: 0.8.2 ## Next version: 0.9.0
### New features
- Make backend a dynamic choice
- User must select a backend in `Cursive::new`
- 3rd party libraries do not need to play with backend features anymore
- Add `StackView::find_layer_from_id`
- Add `SelectView::insert_item`
- Add `TextArea::{enable, disable}`
- Reworked `AnyView`
- `SelectView`: Fix mouse events
- Return callbacks from manual control methods
- `SelectView::{set_selection, select_up, select_down, remove_item}`
- `EditView::{set_content, insert, remove}`
- Add `rect::Rect`
### Changes
- Renamed `Vec4` to `Margins`
- `Callbacks` cannot be created from functions that return a value
- The returned value used to be completely ignored
- `AnyView` does not extend `View` anymore (instead, `View` extends `AnyView`)
- If you were using `AnyView` before, you probably need to replace it with `View`
## 0.8.1 ## 0.8.1

View File

@ -9,7 +9,7 @@ license = "MIT"
name = "cursive" name = "cursive"
readme = "Readme.md" readme = "Readme.md"
repository = "https://github.com/gyscos/Cursive" repository = "https://github.com/gyscos/Cursive"
version = "0.8.2-alpha.0" version = "0.9.0-alpha.0"
[badges.travis-ci] [badges.travis-ci]
repository = "gyscos/Cursive" repository = "gyscos/Cursive"
@ -23,6 +23,7 @@ toml = "0.4"
unicode-segmentation = "1.0" unicode-segmentation = "1.0"
unicode-width = "0.1" unicode-width = "0.1"
xi-unicode = "0.1.0" xi-unicode = "0.1.0"
libc = "0.2"
[dependencies.maplit] [dependencies.maplit]
optional = true optional = true
@ -61,6 +62,7 @@ version = "1.5.0"
[dev-dependencies] [dev-dependencies]
rand = "0.4" rand = "0.4"
pretty-bytes = "0.2.2"
[features] [features]
blt-backend = ["bear-lib-terminal"] blt-backend = ["bear-lib-terminal"]

View File

@ -12,7 +12,7 @@ use cursive::Cursive;
use cursive::views::TextView; use cursive::views::TextView;
fn main() { fn main() {
let mut siv = Cursive::new(); let mut siv = Cursive::default();
siv.add_global_callback('q', |s| s.quit()); siv.add_global_callback('q', |s| s.quit());
@ -80,7 +80,7 @@ extern crate cursive;
use cursive::Cursive; use cursive::Cursive;
fn main() { fn main() {
let mut siv = Cursive::new(); let mut siv = Cursive::default();
siv.run(); siv.run();
} }
@ -91,7 +91,7 @@ It's rather easy to identify the two steps involved.
If you run this, you'll get an empty blue terminal, with no way of properly If you run this, you'll get an empty blue terminal, with no way of properly
leaving the application (you'll have to press <Ctrl-C> to kill it). leaving the application (you'll have to press <Ctrl-C> to kill it).
[`Cursive`]: http://gyscos.github.io/Cursive/cursive/struct.Cursive.html [`Cursive`]: https://docs.rs/cursive/0/cursive/struct.Cursive.html
## Interactivity ## Interactivity
@ -117,7 +117,7 @@ extern crate cursive;
use cursive::Cursive; use cursive::Cursive;
fn main() { fn main() {
let mut siv = Cursive::new(); let mut siv = Cursive::default();
siv.add_global_callback('q', |s| s.quit()); siv.add_global_callback('q', |s| s.quit());
@ -128,10 +128,10 @@ fn main() {
As expected, running it show no visible change, but hitting the `q` key at As expected, running it show no visible change, but hitting the `q` key at
least closes the application. least closes the application.
[`add_global_callback`]: http://gyscos.github.io/Cursive/cursive/struct.Cursive.html#method.add_global_callback [`add_global_callback`]: https://docs.rs/cursive/0/cursive/struct.Cursive.html#method.add_global_callback
[`event::Event`]: http://gyscos.github.io/Cursive/cursive/event/enum.Event.html [`event::Event`]: https://docs.rs/cursive/0/cursive/event/enum.Event.html
[`event::Key`]: http://gyscos.github.io/Cursive/cursive/event/enum.Key.html [`event::Key`]: https://docs.rs/cursive/0/cursive/event/enum.Key.html
[`Cursive::quit`]: http://gyscos.github.io/Cursive/cursive/struct.Cursive.html#method.quit [`Cursive::quit`]: https://docs.rs/cursive/0/cursive/struct.Cursive.html#method.quit
## Views ## Views
@ -159,7 +159,7 @@ use cursive::Cursive;
use cursive::views::TextView; use cursive::views::TextView;
fn main() { fn main() {
let mut siv = Cursive::new(); let mut siv = Cursive::default();
siv.add_global_callback('q', |s| s.quit()); siv.add_global_callback('q', |s| s.quit());
@ -169,9 +169,9 @@ fn main() {
} }
``` ```
[`View`s]: http://gyscos.github.io/Cursive/cursive/view/trait.View.html [`View`s]: https://docs.rs/cursive/0/cursive/view/trait.View.html
[`TextView`]: http://gyscos.github.io/Cursive/cursive/views/struct.TextView.html [`TextView`]: https://docs.rs/cursive/0/cursive/views/struct.TextView.html
[`StackView`]: http://gyscos.github.io/Cursive/cursive/views/struct.StackView.html [`StackView`]: https://docs.rs/cursive/0/cursive/views/struct.StackView.html
[`Cursive::add_layer`]: http://gyscos.github.io/Cursive/cursive/struct.Cursive.html#method.add_layer [`Cursive::add_layer`]: https://docs.rs/cursive/0/cursive/struct.Cursive.html#method.add_layer
Next: [Starting with Cursive (2/3)](./tutorial_2.md) Next: [Starting with Cursive (2/3)](./tutorial_2.md)

View File

@ -12,7 +12,7 @@ use cursive::Cursive;
use cursive::views::Dialog; use cursive::views::Dialog;
fn main() { fn main() {
let mut siv = Cursive::new(); let mut siv = Cursive::default();
siv.add_layer(Dialog::text("This is a survey!\nPress <Next> when you're ready.") siv.add_layer(Dialog::text("This is a survey!\nPress <Next> when you're ready.")
.title("Important survey") .title("Important survey")
@ -51,7 +51,7 @@ extern crate cursive;
use cursive::Cursive; use cursive::Cursive;
fn main() { fn main() {
let mut siv = Cursive::new(); let mut siv = Cursive::default();
siv.run(); siv.run();
} }
@ -74,7 +74,7 @@ use cursive::views::Dialog;
use cursive::views::TextView; use cursive::views::TextView;
fn main() { fn main() {
let mut siv = Cursive::new(); let mut siv = Cursive::default();
siv.add_layer(Dialog::around(TextView::new("..."))); siv.add_layer(Dialog::around(TextView::new("...")));
@ -103,11 +103,11 @@ This way of chaining method to set-up the view is very common in cursive. Most
views provide chainable variants of their methods, to allow creating the view views provide chainable variants of their methods, to allow creating the view
and configuring it in one spot. and configuring it in one spot.
[`TextView`]: http://gyscos.github.io/Cursive/cursive/views/struct.TextView [`TextView`]: https://docs.rs/cursive/0/cursive/views/struct.TextView
[`Dialog`]: http://gyscos.github.io/Cursive/cursive/views/struct.Dialog.html [`Dialog`]: https://docs.rs/cursive/0/cursive/views/struct.Dialog.html
[`Dialog::around`]: http://gyscos.github.io/Cursive/cursive/views/struct.Dialog.html#method.around [`Dialog::around`]: https://docs.rs/cursive/0/cursive/views/struct.Dialog.html#method.around
[`Dialog::text`]: http://gyscos.github.io/Cursive/cursive/views/struct.Dialog.html#method.text [`Dialog::text`]: https://docs.rs/cursive/0/cursive/views/struct.Dialog.html#method.text
[`Dialog::title`]: http://gyscos.github.io/Cursive/cursive/views/struct.Dialog.html#method.title [`Dialog::title`]: https://docs.rs/cursive/0/cursive/views/struct.Dialog.html#method.title
## Buttons ## Buttons
@ -135,7 +135,7 @@ use cursive::Cursive;
use cursive::views::Dialog; use cursive::views::Dialog;
fn main() { fn main() {
let mut siv = Cursive::new(); let mut siv = Cursive::default();
siv.add_layer(Dialog::text("This is a survey!\nPress <Next> when you're ready.") siv.add_layer(Dialog::text("This is a survey!\nPress <Next> when you're ready.")
.title("Important survey") .title("Important survey")
@ -149,7 +149,7 @@ fn show_next(_: &mut Cursive) {
} }
``` ```
[`Dialog::button`]: http://gyscos.github.io/Cursive/cursive/views/struct.Dialog.html#method.button [`Dialog::button`]: https://docs.rs/cursive/0/cursive/views/struct.Dialog.html#method.button
## Layers ## Layers
@ -210,7 +210,7 @@ fn show_answer(s: &mut Cursive, msg: &str) {
Here, `show_answer()` does the same thing: remove the previous layer, and add Here, `show_answer()` does the same thing: remove the previous layer, and add
a new `Dialog` instead. a new `Dialog` instead.
[`Cursive::pop_layer`]: http://gyscos.github.io/Cursive/cursive/struct.Cursive.html#method.pop_layer [`Cursive::pop_layer`]: https://docs.rs/cursive/0/cursive/struct.Cursive.html#method.pop_layer
## Conclusion ## Conclusion

View File

@ -17,7 +17,7 @@ use cursive::views::{Button, Dialog, DummyView, EditView,
use cursive::traits::*; use cursive::traits::*;
fn main() { fn main() {
let mut siv = Cursive::new(); let mut siv = Cursive::default();
let select = SelectView::<String>::new() let select = SelectView::<String>::new()
.on_submit(on_submit) .on_submit(on_submit)
@ -138,11 +138,11 @@ not on the `BoxView` returned by `fixed_size`!)
What we do there should be pretty familiar by now: What we do there should be pretty familiar by now:
replace the layer with a simple dialog. replace the layer with a simple dialog.
[`SelectView`]: http://gyscos.github.io/Cursive/cursive/views/struct.SelectView.html [`SelectView`]: https://docs.rs/cursive/0/cursive/views/struct.SelectView.html
[`BoxView`]: http://gyscos.github.io/Cursive/cursive/views/struct.BoxView.html [`BoxView`]: https://docs.rs/cursive/0/cursive/views/struct.BoxView.html
[`Boxable`]: http://gyscos.github.io/Cursive/cursive/view/trait.Boxable.html [`Boxable`]: https://docs.rs/cursive/0/cursive/view/trait.Boxable.html
[`traits`]: http://gyscos.github.io/Cursive/cursive/traits/index.html [`traits`]: https://docs.rs/cursive/0/cursive/traits/index.html
[`SelectView::on_submit`]: http://gyscos.github.io/Cursive/cursive/views/struct.SelectView.html#method.on_submit [`SelectView::on_submit`]: https://docs.rs/cursive/0/cursive/views/struct.SelectView.html#method.on_submit
## Linear layouts ## Linear layouts
@ -188,10 +188,10 @@ We've added a `DummyView` again to add some space between the list and the
buttons. Though with an empty list, it doesn't look like much yet. Let's fill buttons. Though with an empty list, it doesn't look like much yet. Let's fill
this list with names! this list with names!
[`Button`]: http://gyscos.github.io/Cursive/cursive/views/struct.Button.html [`Button`]: https://docs.rs/cursive/0/cursive/views/struct.Button.html
[`Dialog::around`]: http://gyscos.github.io/Cursive/cursive/views/struct.Dialog.html#method.new [`Dialog::around`]: https://docs.rs/cursive/0/cursive/views/struct.Dialog.html#method.new
[`LinearLayout`]: http://gyscos.github.io/Cursive/cursive/views/struct.LinearLayout.html [`LinearLayout`]: https://docs.rs/cursive/0/cursive/views/struct.LinearLayout.html
[`DummyView`]: http://gyscos.github.io/Cursive/cursive/views/struct.DummyView.html [`DummyView`]: https://docs.rs/cursive/0/cursive/views/struct.DummyView.html
## IDs ## IDs
@ -311,14 +311,14 @@ this method returns a handle, through which we can mutate the view.
It uses `Rc` and `RefCell` under the hood to provide mutable access to the It uses `Rc` and `RefCell` under the hood to provide mutable access to the
view without borrowing the `Cursive` root, leaving us free to pop layers. view without borrowing the `Cursive` root, leaving us free to pop layers.
[`EditView`]: http://gyscos.github.io/Cursive/cursive/views/struct.EditView.html [`EditView`]: https://docs.rs/cursive/0/cursive/views/struct.EditView.html
[`IdView`]: http://gyscos.github.io/Cursive/cursive/views/struct.IdView.html [`IdView`]: https://docs.rs/cursive/0/cursive/views/struct.IdView.html
[`IdView::new`]: http://gyscos.github.io/Cursive/cursive/prelude/struct.IdView.html#method.around [`IdView::new`]: https://docs.rs/cursive/0/cursive/prelude/struct.IdView.html#method.around
[`Identifiable`]: http://gyscos.github.io/Cursive/cursive/view/trait.Identifiable.html [`Identifiable`]: https://docs.rs/cursive/0/cursive/view/trait.Identifiable.html
[`Cursive::find_id`]: http://gyscos.github.io/Cursive/cursive/struct.Cursive.html#method.find_id [`Cursive::find_id`]: https://docs.rs/cursive/0/cursive/struct.Cursive.html#method.find_id
[`Cursive::call_on_id`]: http://gyscos.github.io/Cursive/cursive/struct.Cursive.html#method.call_on_id [`Cursive::call_on_id`]: https://docs.rs/cursive/0/cursive/struct.Cursive.html#method.call_on_id
[`SelectView::selected_id`]: http://gyscos.github.io/Cursive/cursive/views/struct.SelectView.html#method.selected_id [`SelectView::selected_id`]: https://docs.rs/cursive/0/cursive/views/struct.SelectView.html#method.selected_id
[`SelectView::remove_item`]: http://gyscos.github.io/Cursive/cursive/views/struct.SelectView.html#method.remove_item [`SelectView::remove_item`]: https://docs.rs/cursive/0/cursive/views/struct.SelectView.html#method.remove_item
## Conclusion ## Conclusion

View File

@ -17,7 +17,7 @@ use cursive::views::Canvas;
// 256 colors. // 256 colors.
fn main() { fn main() {
let mut siv = Cursive::new(); let mut siv = Cursive::default();
siv.add_layer( siv.add_layer(
Canvas::new(()) Canvas::new(())

View File

@ -5,7 +5,7 @@ use cursive::views::{Dialog, TextView};
fn main() { fn main() {
// Creates the cursive root - required for every application. // Creates the cursive root - required for every application.
let mut siv = Cursive::new(); let mut siv = Cursive::default();
// Creates a dialog with a single "Quit" button // Creates a dialog with a single "Quit" button
siv.add_layer( siv.add_layer(

View File

@ -5,7 +5,7 @@ use cursive::traits::*;
use cursive::views::{Dialog, EditView, TextView}; use cursive::views::{Dialog, EditView, TextView};
fn main() { fn main() {
let mut siv = Cursive::new(); let mut siv = Cursive::default();
// Create a dialog with an edit text and a button. // Create a dialog with an edit text and a button.
// The user can either hit the <Ok> button, // The user can either hit the <Ok> button,

View File

@ -4,7 +4,7 @@ use cursive::Cursive;
use cursive::views::TextView; use cursive::views::TextView;
fn main() { fn main() {
let mut siv = Cursive::new(); let mut siv = Cursive::default();
// 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);

View File

@ -1,8 +1,8 @@
extern crate cursive; extern crate cursive;
use cursive::{Cursive, Printer};
use cursive::event::{Event, EventResult}; use cursive::event::{Event, EventResult};
use cursive::traits::*; use cursive::traits::*;
use cursive::{Cursive, Printer};
// This example define a custom view that prints any event it receives. // This example define a custom view that prints any event it receives.
// This is a handy way to check the input received by cursive. // This is a handy way to check the input received by cursive.

View File

@ -8,7 +8,7 @@ use cursive::views::{Dialog, DummyView, LinearLayout, TextView};
// This example uses a LinearLayout to stick multiple views next to each other. // 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::default();
// Some description text // Some description text
let text = "This is a very simple example of linear layout. Two views \ let text = "This is a very simple example of linear layout. Two views \

View File

@ -10,7 +10,7 @@ use cursive::views::{Checkbox, Dialog, EditView, LinearLayout, ListView,
// ListView can be used to build forms, with a list of inputs. // 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::default();
siv.add_layer( siv.add_layer(
Dialog::new() Dialog::new()

View File

@ -14,7 +14,7 @@ use std::time::Duration;
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::default();
// We want to refresh the page even when no input is given. // We want to refresh the page even when no input is given.
siv.set_fps(10); siv.set_fps(10);

View File

@ -9,7 +9,7 @@ fn main() {
// Read some long text from a file. // Read some long text from a file.
let content = include_str!("../assets/lorem.txt"); let content = include_str!("../assets/lorem.txt");
let mut siv = Cursive::new(); let mut siv = Cursive::default();
// We can quit by pressing q // We can quit by pressing q
siv.add_global_callback('q', |s| s.quit()); siv.add_global_callback('q', |s| s.quit());

View File

@ -9,7 +9,7 @@ use cursive::utils::markup::StyledString;
use cursive::views::{Dialog, TextView}; use cursive::views::{Dialog, TextView};
fn main() { fn main() {
let mut siv = Cursive::new(); let mut siv = Cursive::default();
let mut styled = StyledString::plain("Isn't "); let mut styled = StyledString::plain("Isn't ");
styled.append(StyledString::styled( styled.append(StyledString::styled(

View File

@ -11,7 +11,7 @@ use std::sync::atomic::{AtomicUsize, Ordering};
// application. // application.
fn main() { fn main() {
let mut siv = Cursive::new(); let mut siv = Cursive::default();
// We'll use a counter to name new files. // We'll use a counter to name new files.
let counter = AtomicUsize::new(1); let counter = AtomicUsize::new(1);

View File

@ -12,7 +12,7 @@ use cursive::vec::Vec2;
use cursive::views::{Button, Dialog, LinearLayout, Panel, SelectView}; use cursive::views::{Button, Dialog, LinearLayout, Panel, SelectView};
fn main() { fn main() {
let mut siv = Cursive::new(); let mut siv = Cursive::default();
siv.add_layer( siv.add_layer(
Dialog::new() Dialog::new()

View File

@ -8,7 +8,7 @@ use cursive::views::{Dialog, OnEventView, TextView};
// This example modifies a view after creation. // This example modifies a view after creation.
fn main() { fn main() {
let mut siv = Cursive::new(); let mut siv = Cursive::default();
let content = "Press Q to quit the application.\n\nPress P to open the \ let content = "Press Q to quit the application.\n\nPress P to open the \
popup."; popup.";

View File

@ -22,7 +22,7 @@ fn move_top(c: &mut Cursive, x_in: isize, y_in: isize) {
} }
fn main() { fn main() {
let mut siv = Cursive::new(); let mut siv = Cursive::default();
siv.set_fps(60); siv.set_fps(60);
// We can quit by pressing `q` // We can quit by pressing `q`

View File

@ -4,7 +4,7 @@ extern crate rand;
use cursive::Cursive; use cursive::Cursive;
use cursive::traits::*; use cursive::traits::*;
use cursive::views::{Button, Dialog, LinearLayout, ProgressBar, TextView}; use cursive::views::{Button, Dialog, LinearLayout, ProgressBar, TextView};
use cursive::views::Counter; use cursive::utils::Counter;
use rand::Rng; use rand::Rng;
use std::cmp::min; use std::cmp::min;
use std::thread; use std::thread;
@ -17,7 +17,7 @@ use std::time::Duration;
// "ticked" to indicate progress. // "ticked" to indicate progress.
fn main() { fn main() {
let mut siv = Cursive::new(); let mut siv = Cursive::default();
// We'll start slowly with a simple start button... // We'll start slowly with a simple start button...
siv.add_layer( siv.add_layer(

View File

@ -6,7 +6,7 @@ use cursive::views::{Dialog, DummyView, LinearLayout, RadioGroup};
// This example uses radio buttons. // This example uses radio buttons.
fn main() { fn main() {
let mut siv = Cursive::new(); let mut siv = Cursive::default();
// We need to pre-create the groups for our RadioButtons. // We need to pre-create the groups for our RadioButtons.
let mut color_group: RadioGroup<String> = RadioGroup::new(); let mut color_group: RadioGroup<String> = RadioGroup::new();

View File

@ -7,7 +7,7 @@ use cursive::views::{Dialog, EditView, LinearLayout, TextView};
// This example shows a way to access multiple views at the same time. // 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::default();
// Create a dialog with 2 edit fields, and a text view. // Create a dialog with 2 edit fields, and a text view.
// The text view indicates when the 2 fields content match. // The text view indicates when the 2 fields content match.

View File

@ -33,7 +33,7 @@ fn main() {
Some(EventResult::Consumed(None)) Some(EventResult::Consumed(None))
}); });
let mut siv = Cursive::new(); let mut siv = Cursive::default();
// Let's add a BoxView to keep the list at a reasonable size // Let's add a BoxView to keep the list at a reasonable size
// (it can scroll anyway). // (it can scroll anyway).

View File

@ -5,7 +5,7 @@ use cursive::traits::*;
use cursive::views::{Dialog, SliderView}; use cursive::views::{Dialog, SliderView};
fn main() { fn main() {
let mut siv = Cursive::new(); let mut siv = Cursive::default();
siv.add_global_callback('q', |s| s.quit()); siv.add_global_callback('q', |s| s.quit());

View File

@ -9,7 +9,7 @@ use cursive::views::TextView;
// This way, it looks more natural. // This way, it looks more natural.
fn main() { fn main() {
let mut siv = Cursive::new(); let mut siv = Cursive::default();
let theme = custom_theme_from_cursive(&siv); let theme = custom_theme_from_cursive(&siv);
siv.set_theme(theme); siv.set_theme(theme);

View File

@ -6,7 +6,7 @@ use cursive::traits::*;
use cursive::views::{Dialog, EditView, OnEventView, TextArea}; use cursive::views::{Dialog, EditView, OnEventView, TextArea};
fn main() { fn main() {
let mut siv = Cursive::new(); let mut siv = Cursive::default();
// The main dialog will just have a textarea. // The main dialog will just have a textarea.
// Its size expand automatically with the content. // Its size expand automatically with the content.

View File

@ -4,7 +4,7 @@ use cursive::Cursive;
use cursive::views::{Dialog, TextView}; use cursive::views::{Dialog, TextView};
fn main() { fn main() {
let mut siv = Cursive::new(); let mut siv = Cursive::default();
// You can load a theme from a file at runtime for fast development. // You can load a theme from a file at runtime for fast development.
siv.load_theme_file("assets/style.toml") siv.load_theme_file("assets/style.toml")
.unwrap(); .unwrap();

View File

@ -5,7 +5,7 @@ use cursive::theme::{BaseColor, BorderStyle, Color, ColorStyle};
use cursive::views::{Dialog, EditView, LinearLayout, TextView}; use cursive::views::{Dialog, EditView, LinearLayout, TextView};
fn main() { fn main() {
let mut siv = Cursive::new(); let mut siv = Cursive::default();
let layout = LinearLayout::vertical() let layout = LinearLayout::vertical()
.child(TextView::new( .child(TextView::new(

139
examples/vpv.rs Normal file
View File

@ -0,0 +1,139 @@
extern crate cursive;
extern crate pretty_bytes;
use std::io;
use cursive::Cursive;
use cursive::traits::{Boxable, With};
use cursive::utils;
use cursive::views::{Canvas, Dialog, LinearLayout, ProgressBar};
use pretty_bytes::converter::convert;
use std::thread;
use std::time;
// This example is a visual version of the `pv` tool.
fn main() {
let mut siv = Cursive::default();
// We'll use this channel to signal the end of the transfer
let cb_sink = siv.cb_sink().clone();
// Use a counter to track progress
let counter = utils::Counter::new(0);
let counter_copy = counter.clone();
let start = time::Instant::now();
// If an argument is given, it is the file we'll read from.
// Else, read from stdin.
let (source, len) = match std::env::args().nth(1) {
Some(source) => {
let meta = std::fs::metadata(&source).unwrap();
// If possible, read the file size to have a progress bar.
let len = meta.len();
(
Some(source),
if len > 0 { Some(len) } else { None },
)
}
None => (None, None),
};
// Start the copy in a separate thread
thread::spawn(move || {
// Copy to stdout - lock it for better performance.
let stdout = io::stdout();
let mut stdout = stdout.lock();
match source {
None => {
// Copy from stdin - lock it for better performance.
let stdin = io::stdin();
let stdin = stdin.lock();
let mut reader =
utils::ProgressReader::new(counter_copy, stdin);
// And copy!
io::copy(&mut reader, &mut stdout).unwrap();
}
Some(source) => {
// Copy from stdin - lock it for better performance.
let input = std::fs::File::open(source).unwrap();
let mut reader =
utils::ProgressReader::new(counter_copy, input);
// And copy!
io::copy(&mut reader, &mut stdout).unwrap();
}
}
// When we're done, shut down the application
cb_sink
.send(Box::new(|s: &mut Cursive| s.quit()))
.unwrap();
});
// Add a single view: progress status
siv.add_layer(
Dialog::new().title("Copying...").content(
LinearLayout::vertical()
.child(
Canvas::new(counter.clone())
.with_draw(move |c, printer| {
let ticks = c.get() as f64;
let now = time::Instant::now();
let duration = now - start;
let seconds = duration.as_secs() as f64
+ duration.subsec_nanos() as f64 * 1e-9;
let speed = ticks / seconds;
// Print ETA if we have a file size
// Otherwise prints elapsed time.
if let Some(len) = len {
let remaining = (len as f64 - ticks) / speed;
printer.print(
(0, 0),
&format!(
"ETA: {:.1} seconds",
remaining
),
);
} else {
printer.print(
(0, 0),
&format!(
"Elapsed: {:.1} seconds",
seconds
),
);
}
printer.print(
(0, 1),
&format!("Copied: {}", convert(ticks)),
);
printer.print(
(0, 2),
&format!("Speed: {}/s", convert(speed)),
);
})
.fixed_size((25, 3)),
)
.with(|l| {
// If we have a file length, add a progress bar
if let Some(len) = len {
l.add_child(
ProgressBar::new()
.max(len as usize)
.with_value(counter.clone()),
);
}
}),
),
);
siv.set_fps(10);
siv.run();
}

View File

@ -1,3 +1,8 @@
//! Backend using BearLibTerminal
//!
//! Requires the `blt-backend` feature.
#![cfg(feature = "bear-lib-terminal")]
extern crate bear_lib_terminal; extern crate bear_lib_terminal;
use self::bear_lib_terminal::Color as BltColor; use self::bear_lib_terminal::Color as BltColor;
@ -15,12 +20,34 @@ enum ColorRole {
Background, Background,
} }
pub struct Concrete { pub struct Backend {
mouse_position: Vec2, mouse_position: Vec2,
buttons_pressed: HashSet<MouseButton>, buttons_pressed: HashSet<MouseButton>,
} }
impl Concrete { impl Backend {
pub fn init() -> Box<Self> {
terminal::open("Cursive", 80, 24);
terminal::set(terminal::config::Window::empty().resizeable(true));
terminal::set(vec![
terminal::config::InputFilter::Group {
group: terminal::config::InputFilterGroup::Keyboard,
both: false,
},
terminal::config::InputFilter::Group {
group: terminal::config::InputFilterGroup::Mouse,
both: true,
},
]);
let c = Backend {
mouse_position: Vec2::zero(),
buttons_pressed: HashSet::new(),
};
Box::new(c)
}
fn blt_keycode_to_ev( fn blt_keycode_to_ev(
&mut self, kc: KeyCode, shift: bool, ctrl: bool &mut self, kc: KeyCode, shift: bool, ctrl: bool
) -> Event { ) -> Event {
@ -144,52 +171,52 @@ impl Concrete {
} }
} }
impl backend::Backend for Concrete { impl backend::Backend for Backend {
fn init() -> Self {
terminal::open("Cursive", 80, 24);
terminal::set(terminal::config::Window::empty().resizeable(true));
terminal::set(vec![
terminal::config::InputFilter::Group {
group: terminal::config::InputFilterGroup::Keyboard,
both: false,
},
terminal::config::InputFilter::Group {
group: terminal::config::InputFilterGroup::Mouse,
both: true,
},
]);
Concrete {
mouse_position: Vec2::zero(),
buttons_pressed: HashSet::new(),
}
}
fn finish(&mut self) { fn finish(&mut self) {
terminal::close(); terminal::close();
} }
fn with_color<F: FnOnce()>(&self, color: ColorPair, f: F) { fn set_color(&self, color: ColorPair) -> ColorPair {
let current = ColorPair {
front: blt_colour_to_colour(state::foreground()),
back: blt_colour_to_colour(state::background())
};
let fg = colour_to_blt_colour(color.front, ColorRole::Foreground); let fg = colour_to_blt_colour(color.front, ColorRole::Foreground);
let bg = colour_to_blt_colour(color.back, ColorRole::Background); let bg = colour_to_blt_colour(color.back, ColorRole::Background);
terminal::with_colors(fg, bg, f);
terminal::set_colors(fg, bg);
current
} }
fn with_effect<F: FnOnce()>(&self, effect: Effect, f: F) { fn set_effect(&self, effect: Effect) {
match effect { match effect {
// TODO: does BLT support bold/italic/underline? // TODO: does BLT support bold/italic/underline?
Effect::Bold Effect::Bold
| Effect::Italic | Effect::Italic
| Effect::Underline | Effect::Underline
| Effect::Simple => f(), | Effect::Simple => {},
// TODO: how to do this correctly?` // TODO: how to do this correctly?`
// BLT itself doesn't do this kind of thing, // BLT itself doesn't do this kind of thing,
// we'd need the colours in our position, // we'd need the colours in our position,
// but `f()` can do whatever // but `f()` can do whatever
Effect::Reverse => terminal::with_colors( Effect::Reverse => terminal::set_colors(
BltColor::from_rgb(0, 0, 0), state::background(), state::foreground()
BltColor::from_rgb(255, 255, 255), ),
f, }
}
fn unset_effect(&self, effect: Effect) {
match effect {
// TODO: does BLT support bold/italic/underline?
Effect::Bold
| Effect::Italic
| Effect::Underline
| Effect::Simple => {},
// The process of reversing is the same as unreversing
Effect::Reverse => terminal::set_colors(
state::background(), state::foreground()
), ),
} }
} }
@ -198,9 +225,9 @@ impl backend::Backend for Concrete {
true true
} }
fn screen_size(&self) -> (usize, usize) { fn screen_size(&self) -> Vec2 {
let Size { width, height } = terminal::state::size(); let Size { width, height } = terminal::state::size();
(width as usize, height as usize) (width, height).into()
} }
fn clear(&self, color: Color) { fn clear(&self, color: Color) {
@ -215,8 +242,8 @@ impl backend::Backend for Concrete {
terminal::refresh(); terminal::refresh();
} }
fn print_at(&self, (x, y): (usize, usize), text: &str) { fn print_at(&self, pos: Vec2, text: &str) {
terminal::print_xy(x as i32, y as i32, text); terminal::print_xy(pos.x as i32, pos.y as i32, text);
} }
fn set_refresh_rate(&mut self, _: u32) { fn set_refresh_rate(&mut self, _: u32) {
@ -284,6 +311,10 @@ impl backend::Backend for Concrete {
} }
} }
fn blt_colour_to_colour(c: BltColor) -> Color {
Color::Rgb(c.red, c.green, c.blue)
}
fn colour_to_blt_colour(clr: Color, role: ColorRole) -> BltColor { fn colour_to_blt_colour(clr: Color, role: ColorRole) -> BltColor {
let (r, g, b) = match clr { let (r, g, b) = match clr {
Color::TerminalDefault => { Color::TerminalDefault => {

View File

@ -1,16 +1,17 @@
//! Common module for the ncurses and pancurses backends.
//!
//! Requires either of `ncurses-backend` or `pancurses-backend`.
#![cfg(any(feature = "ncurses", feature = "pancurses"))]
use event::{Event, Key}; use event::{Event, Key};
use std::collections::HashMap; use std::collections::HashMap;
use theme::{BaseColor, Color, ColorPair}; use theme::{BaseColor, Color, ColorPair};
#[cfg(feature = "ncurses")] #[cfg(feature = "ncurses")]
mod n; pub mod n;
#[cfg(feature = "ncurses")]
pub use self::n::*;
#[cfg(feature = "pancurses")] #[cfg(feature = "pancurses")]
mod pan; pub mod pan;
#[cfg(feature = "pancurses")]
pub use self::pan::*;
fn split_i32(code: i32) -> Vec<u8> { fn split_i32(code: i32) -> Vec<u8> {
(0..4).map(|i| ((code >> (8 * i)) & 0xFF) as u8).collect() (0..4).map(|i| ((code >> (8 * i)) & 0xFF) as u8).collect()

View File

@ -1,17 +1,21 @@
extern crate ncurses; extern crate ncurses;
use self::ncurses::mmask_t;
use self::super::split_i32; use self::super::split_i32;
use self::ncurses::mmask_t;
use backend; use backend;
use event::{Event, Key, MouseButton, MouseEvent}; use event::{Event, Key, MouseButton, MouseEvent};
use libc;
use std::cell::{Cell, RefCell}; use std::cell::{Cell, RefCell};
use std::collections::HashMap; use std::collections::HashMap;
use std::io::{stdout, Write}; use std::ffi::CString;
use std::fs::File;
use std::io;
use std::io::Write;
use theme::{Color, ColorPair, Effect}; use theme::{Color, ColorPair, Effect};
use utf8; use utf8;
use vec::Vec2; use vec::Vec2;
pub struct Concrete { pub struct Backend {
current_style: Cell<ColorPair>, current_style: Cell<ColorPair>,
// Maps (front, back) ncurses colors to ncurses pairs // Maps (front, back) ncurses colors to ncurses pairs
@ -27,10 +31,73 @@ fn find_closest_pair(pair: &ColorPair) -> (i16, i16) {
super::find_closest_pair(pair, ncurses::COLORS() as i16) super::find_closest_pair(pair, ncurses::COLORS() as i16)
} }
impl Concrete { /// Writes some bytes directly to `/dev/tty`
///
/// Since this is not going to be used often, we can afford to re-open the
/// file every time.
fn write_to_tty(bytes: &[u8]) -> io::Result<()> {
let mut tty_output =
File::create("/dev/tty").expect("cursive can only run with a tty");
tty_output.write_all(bytes)?;
// tty_output will be flushed automatically at the end of the function.
Ok(())
}
impl Backend {
pub fn init() -> Box<backend::Backend> {
// Change the locale.
// For some reasons it's mandatory to get some UTF-8 support.
ncurses::setlocale(ncurses::LcCategory::all, "");
// The delay is the time ncurses wait after pressing ESC
// to see if it's an escape sequence.
// Default delay is way too long. 25 is imperceptible yet works fine.
::std::env::set_var("ESCDELAY", "25");
let tty_path = CString::new("/dev/tty").unwrap();
let mode = CString::new("r+").unwrap();
let tty = unsafe { libc::fopen(tty_path.as_ptr(), mode.as_ptr()) };
ncurses::newterm(None, tty, tty);
ncurses::keypad(ncurses::stdscr(), true);
// This disables mouse click detection,
// and provides 0-delay access to mouse presses.
ncurses::mouseinterval(0);
// Listen to all mouse events.
ncurses::mousemask(
(ncurses::ALL_MOUSE_EVENTS | ncurses::REPORT_MOUSE_POSITION)
as mmask_t,
None,
);
ncurses::noecho();
ncurses::cbreak();
ncurses::start_color();
// Pick up background and text color from the terminal theme.
ncurses::use_default_colors();
// No cursor
ncurses::curs_set(ncurses::CURSOR_VISIBILITY::CURSOR_INVISIBLE);
// This asks the terminal to provide us with mouse drag events
// (Mouse move when a button is pressed).
// Replacing 1002 with 1003 would give us ANY mouse move.
write_to_tty(b"\x1B[?1002h").unwrap();
let c = Backend {
current_style: Cell::new(ColorPair::from_256colors(0, 0)),
pairs: RefCell::new(HashMap::new()),
last_mouse_button: None,
event_queue: Vec::new(),
key_codes: initialize_keymap(),
};
Box::new(c)
}
/// Save a new color pair. /// Save a new color pair.
fn insert_color( fn insert_color(
&self, pairs: &mut HashMap<(i16, i16), i16>, (front, back): (i16, i16) &self, pairs: &mut HashMap<(i16, i16), i16>, (front, back): (i16, i16),
) -> i16 { ) -> i16 {
let n = 1 + pairs.len() as i16; let n = 1 + pairs.len() as i16;
@ -96,7 +163,7 @@ impl Concrete {
let make_event = |event| Event::Mouse { let make_event = |event| Event::Mouse {
offset: Vec2::zero(), offset: Vec2::zero(),
position: Vec2::new(mevent.x as usize, mevent.y as usize), position: Vec2::new(mevent.x as usize, mevent.y as usize),
event: event, event,
}; };
if mevent.bstate == ncurses::REPORT_MOUSE_POSITION as mmask_t { if mevent.bstate == ncurses::REPORT_MOUSE_POSITION as mmask_t {
@ -153,59 +220,12 @@ impl Concrete {
} }
} }
impl backend::Backend for Concrete { impl backend::Backend for Backend {
fn init() -> Self { fn screen_size(&self) -> Vec2 {
// Change the locale.
// For some reasons it's mandatory to get some UTF-8 support.
ncurses::setlocale(ncurses::LcCategory::all, "");
// The delay is the time ncurses wait after pressing ESC
// to see if it's an escape sequence.
// Default delay is way too long. 25 is imperceptible yet works fine.
::std::env::set_var("ESCDELAY", "25");
ncurses::initscr();
ncurses::keypad(ncurses::stdscr(), true);
// This disables mouse click detection,
// and provides 0-delay access to mouse presses.
ncurses::mouseinterval(0);
// Listen to all mouse events.
ncurses::mousemask(
(ncurses::ALL_MOUSE_EVENTS | ncurses::REPORT_MOUSE_POSITION)
as mmask_t,
None,
);
ncurses::noecho();
ncurses::cbreak();
ncurses::start_color();
// Pick up background and text color from the terminal theme.
ncurses::use_default_colors();
// No cursor
ncurses::curs_set(ncurses::CURSOR_VISIBILITY::CURSOR_INVISIBLE);
// This asks the terminal to provide us with mouse drag events
// (Mouse move when a button is pressed).
// Replacing 1002 with 1003 would give us ANY mouse move.
print!("\x1B[?1002h");
stdout().flush().expect("could not flush stdout");
Concrete {
current_style: Cell::new(ColorPair::from_256colors(0, 0)),
pairs: RefCell::new(HashMap::new()),
last_mouse_button: None,
event_queue: Vec::new(),
key_codes: initialize_keymap(),
}
}
fn screen_size(&self) -> (usize, usize) {
let mut x: i32 = 0; let mut x: i32 = 0;
let mut y: i32 = 0; let mut y: i32 = 0;
ncurses::getmaxyx(ncurses::stdscr(), &mut y, &mut x); ncurses::getmaxyx(ncurses::stdscr(), &mut y, &mut x);
(x as usize, y as usize) (x, y).into()
} }
fn has_colors(&self) -> bool { fn has_colors(&self) -> bool {
@ -213,26 +233,21 @@ impl backend::Backend for Concrete {
} }
fn finish(&mut self) { fn finish(&mut self) {
print!("\x1B[?1002l"); write_to_tty(b"\x1B[?1002l").unwrap();
stdout().flush().expect("could not flush stdout");
ncurses::endwin(); ncurses::endwin();
} }
fn with_color<F: FnOnce()>(&self, colors: ColorPair, f: F) { fn set_color(&self, colors: ColorPair) -> ColorPair {
// eprintln!("Color used: {:?}", colors); // eprintln!("Color used: {:?}", colors);
let current = self.current_style.get(); let current = self.current_style.get();
if current != colors { if current != colors {
self.set_colors(colors); self.set_colors(colors);
} }
f(); current
if current != colors {
self.set_colors(current);
}
} }
fn with_effect<F: FnOnce()>(&self, effect: Effect, f: F) { fn set_effect(&self, effect: Effect) {
let style = match effect { let style = match effect {
Effect::Reverse => ncurses::A_REVERSE(), Effect::Reverse => ncurses::A_REVERSE(),
Effect::Simple => ncurses::A_NORMAL(), Effect::Simple => ncurses::A_NORMAL(),
@ -241,7 +256,16 @@ impl backend::Backend for Concrete {
Effect::Underline => ncurses::A_UNDERLINE(), Effect::Underline => ncurses::A_UNDERLINE(),
}; };
ncurses::attron(style); ncurses::attron(style);
f(); }
fn unset_effect(&self, effect: Effect) {
let style = match effect {
Effect::Reverse => ncurses::A_REVERSE(),
Effect::Simple => ncurses::A_NORMAL(),
Effect::Bold => ncurses::A_BOLD(),
Effect::Italic => ncurses::A_ITALIC(),
Effect::Underline => ncurses::A_UNDERLINE(),
};
ncurses::attroff(style); ncurses::attroff(style);
} }
@ -259,8 +283,8 @@ impl backend::Backend for Concrete {
ncurses::refresh(); ncurses::refresh();
} }
fn print_at(&self, (x, y): (usize, usize), text: &str) { fn print_at(&self, pos: Vec2, text: &str) {
ncurses::mvaddstr(y as i32, x as i32, text); ncurses::mvaddstr(pos.y as i32, pos.x as i32, text);
} }
fn poll_event(&mut self) -> Event { fn poll_event(&mut self) -> Event {

View File

@ -1,7 +1,7 @@
extern crate pancurses; extern crate pancurses;
use self::pancurses::mmask_t;
use self::super::split_i32; use self::super::split_i32;
use self::pancurses::mmask_t;
use backend; use backend;
use event::{Event, Key, MouseButton, MouseEvent}; use event::{Event, Key, MouseButton, MouseEvent};
use std::cell::{Cell, RefCell}; use std::cell::{Cell, RefCell};
@ -10,7 +10,7 @@ use std::io::{stdout, Write};
use theme::{Color, ColorPair, Effect}; use theme::{Color, ColorPair, Effect};
use vec::Vec2; use vec::Vec2;
pub struct Concrete { pub struct Backend {
// Used // Used
current_style: Cell<ColorPair>, current_style: Cell<ColorPair>,
pairs: RefCell<HashMap<(i16, i16), i32>>, pairs: RefCell<HashMap<(i16, i16), i32>>,
@ -28,10 +28,46 @@ fn find_closest_pair(pair: &ColorPair) -> (i16, i16) {
super::find_closest_pair(pair, pancurses::COLORS() as i16) super::find_closest_pair(pair, pancurses::COLORS() as i16)
} }
impl Concrete { impl Backend {
pub fn init() -> Box<Self> {
::std::env::set_var("ESCDELAY", "25");
let window = pancurses::initscr();
window.keypad(true);
pancurses::noecho();
pancurses::cbreak();
pancurses::start_color();
pancurses::use_default_colors();
pancurses::curs_set(0);
pancurses::mouseinterval(0);
pancurses::mousemask(
pancurses::ALL_MOUSE_EVENTS | pancurses::REPORT_MOUSE_POSITION,
::std::ptr::null_mut(),
);
// This asks the terminal to provide us with mouse drag events
// (Mouse move when a button is pressed).
// Replacing 1002 with 1003 would give us ANY mouse move.
print!("\x1B[?1002h");
stdout()
.flush()
.expect("could not flush stdout");
let c = Backend {
current_style: Cell::new(ColorPair::from_256colors(0, 0)),
pairs: RefCell::new(HashMap::new()),
window: window,
last_mouse_button: None,
event_queue: Vec::new(),
key_codes: initialize_keymap(),
};
Box::new(c)
}
/// Save a new color pair. /// Save a new color pair.
fn insert_color( fn insert_color(
&self, pairs: &mut HashMap<(i16, i16), i32>, (front, back): (i16, i16) &self, pairs: &mut HashMap<(i16, i16), i32>, (front, back): (i16, i16),
) -> i32 { ) -> i32 {
let n = 1 + pairs.len() as i32; let n = 1 + pairs.len() as i32;
@ -133,42 +169,11 @@ impl Concrete {
} }
} }
impl backend::Backend for Concrete { impl backend::Backend for Backend {
fn init() -> Self { fn screen_size(&self) -> Vec2 {
::std::env::set_var("ESCDELAY", "25"); // Coordinates are reversed here
let window = pancurses::initscr();
window.keypad(true);
pancurses::noecho();
pancurses::cbreak();
pancurses::start_color();
pancurses::use_default_colors();
pancurses::curs_set(0);
pancurses::mouseinterval(0);
pancurses::mousemask(
pancurses::ALL_MOUSE_EVENTS | pancurses::REPORT_MOUSE_POSITION,
::std::ptr::null_mut(),
);
// This asks the terminal to provide us with mouse drag events
// (Mouse move when a button is pressed).
// Replacing 1002 with 1003 would give us ANY mouse move.
print!("\x1B[?1002h");
stdout().flush().expect("could not flush stdout");
Concrete {
current_style: Cell::new(ColorPair::from_256colors(0, 0)),
pairs: RefCell::new(HashMap::new()),
window: window,
last_mouse_button: None,
event_queue: Vec::new(),
key_codes: initialize_keymap(),
}
}
fn screen_size(&self) -> (usize, usize) {
let (y, x) = self.window.get_max_yx(); let (y, x) = self.window.get_max_yx();
(x as usize, y as usize) (x, y).into()
} }
fn has_colors(&self) -> bool { fn has_colors(&self) -> bool {
@ -177,25 +182,23 @@ impl backend::Backend for Concrete {
fn finish(&mut self) { fn finish(&mut self) {
print!("\x1B[?1002l"); print!("\x1B[?1002l");
stdout().flush().expect("could not flush stdout"); stdout()
.flush()
.expect("could not flush stdout");
pancurses::endwin(); pancurses::endwin();
} }
fn with_color<F: FnOnce()>(&self, colors: ColorPair, f: F) { fn set_color(&self, colors: ColorPair) -> ColorPair {
let current = self.current_style.get(); let current = self.current_style.get();
if current != colors { if current != colors {
self.set_colors(colors); self.set_colors(colors);
} }
f(); current
if current != colors {
self.set_colors(current);
}
} }
fn with_effect<F: FnOnce()>(&self, effect: Effect, f: F) { fn set_effect(&self, effect: Effect) {
let style = match effect { let style = match effect {
Effect::Simple => pancurses::Attribute::Normal, Effect::Simple => pancurses::Attribute::Normal,
Effect::Reverse => pancurses::Attribute::Reverse, Effect::Reverse => pancurses::Attribute::Reverse,
@ -204,7 +207,16 @@ impl backend::Backend for Concrete {
Effect::Underline => pancurses::Attribute::Underline, Effect::Underline => pancurses::Attribute::Underline,
}; };
self.window.attron(style); self.window.attron(style);
f(); }
fn unset_effect(&self, effect: Effect) {
let style = match effect {
Effect::Simple => pancurses::Attribute::Normal,
Effect::Reverse => pancurses::Attribute::Reverse,
Effect::Bold => pancurses::Attribute::Bold,
Effect::Italic => pancurses::Attribute::Italic,
Effect::Underline => pancurses::Attribute::Underline,
};
self.window.attroff(style); self.window.attroff(style);
} }
@ -221,8 +233,9 @@ impl backend::Backend for Concrete {
self.window.refresh(); self.window.refresh();
} }
fn print_at(&self, (x, y): (usize, usize), text: &str) { fn print_at(&self, pos: Vec2, text: &str) {
self.window.mvaddstr(y as i32, x as i32, text); self.window
.mvaddstr(pos.y as i32, pos.x as i32, text);
} }
fn poll_event(&mut self) -> Event { fn poll_event(&mut self) -> Event {

49
src/backend/dummy.rs Normal file
View File

@ -0,0 +1,49 @@
//! Dummy backend
use backend;
use theme;
use event;
use vec::Vec2;
pub struct Backend;
impl Backend {
pub fn init() -> Box<backend::Backend>
where
Self: Sized,
{
Box::new(Backend)
}
}
impl backend::Backend for Backend {
fn finish(&mut self) {}
fn refresh(&mut self) {}
fn has_colors(&self) -> bool {
false
}
fn screen_size(&self) -> Vec2 {
(1, 1).into()
}
fn poll_event(&mut self) -> event::Event {
event::Event::Exit
}
fn print_at(&self, _: Vec2, _: &str) {}
fn clear(&self, _: theme::Color) {}
fn set_refresh_rate(&mut self, _: u32) {}
// This sets the Colours and returns the previous colours
// to allow you to set them back when you're done.
fn set_color(&self, colors: theme::ColorPair) -> theme::ColorPair {
colors
}
fn set_effect(&self, _: theme::Effect) {}
fn unset_effect(&self, _: theme::Effect) {}
}

View File

@ -1,40 +1,64 @@
//! Define backends using common libraries.
//!
//! Cursive doesn't print anything by itself: it delegates this job to a
//! backend library, which handles all actual input and output.
//!
//! This module defines the `Backend` trait, as well as a few implementations
//! using some common libraries. Each of those included backends needs a
//! corresonding feature to be enabled.
use event; use event;
use theme; use theme;
#[cfg(feature = "termion")] use vec::Vec2;
mod termion;
#[cfg(feature = "bear-lib-terminal")]
mod blt;
#[cfg(any(feature = "ncurses", feature = "pancurses"))]
mod curses;
#[cfg(feature = "bear-lib-terminal")] pub mod dummy;
pub use self::blt::*;
#[cfg(any(feature = "ncurses", feature = "pancurses"))]
pub use self::curses::*;
#[cfg(feature = "termion")]
pub use self::termion::*;
pub mod termion;
pub mod blt;
pub mod curses;
/// Trait defining the required methods to be a backend.
pub trait Backend { pub trait Backend {
fn init() -> Self;
// TODO: take `self` by value? // TODO: take `self` by value?
// Or implement Drop? // Or implement Drop?
/// Prepares to close the backend.
///
/// This should clear any state in the terminal.
fn finish(&mut self); fn finish(&mut self);
/// Refresh the screen.
fn refresh(&mut self); fn refresh(&mut self);
/// Should return `true` if this backend supports colors.
fn has_colors(&self) -> bool; fn has_colors(&self) -> bool;
fn screen_size(&self) -> (usize, usize);
/// Returns the screen size.
fn screen_size(&self) -> Vec2;
/// Main input method /// Main input method
fn poll_event(&mut self) -> event::Event; fn poll_event(&mut self) -> event::Event;
/// Main method used for printing /// Main method used for printing
fn print_at(&self, (usize, usize), &str); fn print_at(&self, pos: Vec2, text: &str);
/// Clears the screen with the given color.
fn clear(&self, color: theme::Color); fn clear(&self, color: theme::Color);
/// Sets the refresh rate for the backend.
///
/// If no event is detected in the interval, send an `Event::Refresh`.
fn set_refresh_rate(&mut self, fps: u32); fn set_refresh_rate(&mut self, fps: u32);
// TODO: unify those into a single method?
fn with_color<F: FnOnce()>(&self, colors: theme::ColorPair, f: F); /// Starts using a new color.
fn with_effect<F: FnOnce()>(&self, effect: theme::Effect, f: F); ///
/// This should return the previously active color.
fn set_color(&self, colors: theme::ColorPair) -> theme::ColorPair;
/// Enables the given effect.
fn set_effect(&self, effect: theme::Effect);
/// Disables the given effect.
fn unset_effect(&self, effect: theme::Effect);
} }

View File

@ -1,3 +1,8 @@
//! Backend using the pure-rust termion library.
//!
//! Requires the `termion-backend` feature.
#![cfg(feature = "termion")]
extern crate termion; extern crate termion;
extern crate chan_signal; extern crate chan_signal;
@ -20,7 +25,7 @@ use std::thread;
use theme; use theme;
use vec::Vec2; use vec::Vec2;
pub struct Concrete { pub struct Backend {
terminal: AlternateScreen<MouseTerminal<RawTerminal<Stdout>>>, terminal: AlternateScreen<MouseTerminal<RawTerminal<Stdout>>>,
current_style: Cell<theme::ColorPair>, current_style: Cell<theme::ColorPair>,
input: chan::Receiver<TEvent>, input: chan::Receiver<TEvent>,
@ -56,7 +61,39 @@ impl Effectable for theme::Effect {
} }
} }
impl Concrete { impl Backend {
pub fn init() -> Box<Self> {
print!("{}", termion::cursor::Hide);
let resize = chan_signal::notify(&[chan_signal::Signal::WINCH]);
// TODO: lock stdout
let terminal = AlternateScreen::from(MouseTerminal::from(
::std::io::stdout().into_raw_mode().unwrap(),
));
let (sender, receiver) = chan::async();
thread::spawn(move || {
for key in ::std::io::stdin().events() {
if let Ok(key) = key {
sender.send(key)
}
}
});
let c = Backend {
terminal: terminal,
current_style: Cell::new(theme::ColorPair::from_256colors(0, 0)),
input: receiver,
resize: resize,
timeout: None,
last_button: None,
};
Box::new(c)
}
fn apply_colors(&self, colors: theme::ColorPair) { fn apply_colors(&self, colors: theme::ColorPair) {
with_color(&colors.front, |c| print!("{}", tcolor::Fg(c))); with_color(&colors.front, |c| print!("{}", tcolor::Fg(c)));
with_color(&colors.back, |c| print!("{}", tcolor::Bg(c))); with_color(&colors.back, |c| print!("{}", tcolor::Bg(c)));
@ -136,37 +173,7 @@ impl Concrete {
} }
} }
impl backend::Backend for Concrete { impl backend::Backend for Backend {
fn init() -> Self {
print!("{}", termion::cursor::Hide);
let resize = chan_signal::notify(&[chan_signal::Signal::WINCH]);
// TODO: lock stdout
let terminal = AlternateScreen::from(MouseTerminal::from(
::std::io::stdout().into_raw_mode().unwrap(),
));
let (sender, receiver) = chan::async();
thread::spawn(move || {
for key in ::std::io::stdin().events() {
if let Ok(key) = key {
sender.send(key)
}
}
});
Concrete {
terminal: terminal,
current_style: Cell::new(theme::ColorPair::from_256colors(0, 0)),
input: receiver,
resize: resize,
timeout: None,
last_button: None,
}
}
fn finish(&mut self) { fn finish(&mut self) {
print!( print!(
"{}{}", "{}{}",
@ -181,7 +188,7 @@ impl backend::Backend for Concrete {
); );
} }
fn with_color<F: FnOnce()>(&self, color: theme::ColorPair, f: F) { fn set_color(&self, color: theme::ColorPair) -> theme::ColorPair {
let current_style = self.current_style.get(); let current_style = self.current_style.get();
if current_style != color { if current_style != color {
@ -189,17 +196,14 @@ impl backend::Backend for Concrete {
self.current_style.set(color); self.current_style.set(color);
} }
f(); return current_style;
if current_style != color {
self.current_style.set(current_style);
self.apply_colors(current_style);
}
} }
fn with_effect<F: FnOnce()>(&self, effect: theme::Effect, f: F) { fn set_effect(&self, effect: theme::Effect) {
effect.on(); effect.on();
f(); }
fn unset_effect(&self, effect: theme::Effect) {
effect.off(); effect.off();
} }
@ -208,9 +212,9 @@ impl backend::Backend for Concrete {
true true
} }
fn screen_size(&self) -> (usize, usize) { fn screen_size(&self) -> Vec2 {
let (x, y) = termion::terminal_size().unwrap_or((1, 1)); let (x, y) = termion::terminal_size().unwrap_or((1, 1));
(x as usize, y as usize) (x, y).into()
} }
fn clear(&self, color: theme::Color) { fn clear(&self, color: theme::Color) {
@ -225,10 +229,10 @@ impl backend::Backend for Concrete {
self.terminal.flush().unwrap(); self.terminal.flush().unwrap();
} }
fn print_at(&self, (x, y): (usize, usize), text: &str) { fn print_at(&self, pos: Vec2, text: &str) {
print!( print!(
"{}{}", "{}{}",
termion::cursor::Goto(1 + x as u16, 1 + y as u16), termion::cursor::Goto(1 + pos.x as u16, 1 + pos.y as u16),
text text
); );
} }

View File

@ -1,5 +1,4 @@
use backend; use backend;
use backend::Backend;
use direction; use direction;
use event::{Callback, Event, EventResult}; use event::{Callback, Event, EventResult};
use printer::Printer; use printer::Printer;
@ -33,6 +32,34 @@ impl<F: FnOnce(&mut Cursive) -> () + Send> CbFunc for F {
} }
} }
#[cfg(feature = "termion")]
impl Default for Cursive {
fn default() -> Self {
Self::termion()
}
}
#[cfg(all(not(feature = "termion"), feature = "pancurses"))]
impl Default for Cursive {
fn default() -> Self {
Self::pancurses()
}
}
#[cfg(all(not(feature = "termion"), not(feature = "pancurses"), feature = "bear-lib-terminal"))]
impl Default for Cursive {
fn default() -> Self {
Self::blt()
}
}
#[cfg(all(not(feature = "termion"), not(feature = "pancurses"), not(feature = "bear-lib-terminal"), feature = "ncurses"))]
impl Default for Cursive {
fn default() -> Self {
Self::ncurses()
}
}
/// Central part of the cursive library. /// Central part of the cursive library.
/// ///
/// It initializes ncurses on creation and cleans up on drop. /// It initializes ncurses on creation and cleans up on drop.
@ -54,19 +81,15 @@ pub struct Cursive {
running: bool, running: bool,
backend: backend::Concrete, backend: Box<backend::Backend>,
cb_source: mpsc::Receiver<Box<CbFunc>>, cb_source: mpsc::Receiver<Box<CbFunc>>,
cb_sink: mpsc::Sender<Box<CbFunc>>, cb_sink: mpsc::Sender<Box<CbFunc>>,
} }
new_default!(Cursive);
impl Cursive { impl Cursive {
/// Creates a new Cursive root, and initialize the back-end. /// Creates a new Cursive root, and initialize the back-end.
pub fn new() -> Self { pub fn new(backend: Box<backend::Backend>) -> Self {
let backend = backend::Concrete::init();
let theme = theme::load_default(); let theme = theme::load_default();
// theme.activate(&mut backend); // theme.activate(&mut backend);
// let theme = theme::load_theme("assets/style.toml").unwrap(); // let theme = theme::load_theme("assets/style.toml").unwrap();
@ -87,6 +110,37 @@ impl Cursive {
} }
} }
/// Creates a new Cursive root using a ncurses backend.
#[cfg(feature = "ncurses")]
pub fn ncurses() -> Self {
Self::new(backend::curses::n::Backend::init())
}
/// Creates a new Cursive root using a pancurses backend.
#[cfg(feature = "pancurses")]
pub fn pancurses() -> Self {
Self::new(backend::curses::pan::Backend::init())
}
/// Creates a new Cursive root using a termion backend.
#[cfg(feature = "termion")]
pub fn termion() -> Self {
Self::new(backend::termion::Backend::init())
}
/// Creates a new Cursive root using a bear-lib-terminal backend.
#[cfg(feature = "bear-lib-terminal")]
pub fn blt() -> Self {
Self::new(backend::blt::Backend::init())
}
/// Creates a new Cursive root using a dummy backend.
///
/// Nothing will be output. This is mostly here for tests.
pub fn dummy() -> Self {
Self::new(backend::dummy::Backend::init())
}
/// Returns a sink for asynchronous callbacks. /// Returns a sink for asynchronous callbacks.
/// ///
/// Returns the sender part of a channel, that allows to send /// Returns the sender part of a channel, that allows to send
@ -100,11 +154,11 @@ impl Cursive {
/// ///
/// # Examples /// # Examples
/// ///
/// ```rust,no_run /// ```rust
/// # extern crate cursive; /// # extern crate cursive;
/// # use cursive::*; /// # use cursive::*;
/// # fn main() { /// # fn main() {
/// let mut siv = Cursive::new(); /// let mut siv = Cursive::dummy();
/// siv.set_fps(10); /// siv.set_fps(10);
/// ///
/// // quit() will be called during the next event cycle /// // quit() will be called during the next event cycle
@ -137,7 +191,7 @@ impl Cursive {
/// ///
/// # Examples /// # Examples
/// ///
/// ```rust,no_run /// ```rust
/// # extern crate cursive; /// # extern crate cursive;
/// # /// #
/// # use cursive::{Cursive, event}; /// # use cursive::{Cursive, event};
@ -146,7 +200,7 @@ impl Cursive {
/// # use cursive::menu::*; /// # use cursive::menu::*;
/// # /// #
/// # fn main() { /// # fn main() {
/// let mut siv = Cursive::new(); /// let mut siv = Cursive::dummy();
/// ///
/// siv.menubar() /// siv.menubar()
/// .add_subtree("File", /// .add_subtree("File",
@ -290,13 +344,13 @@ impl Cursive {
/// ///
/// # Examples /// # Examples
/// ///
/// ```rust,no_run /// ```rust
/// # extern crate cursive; /// # extern crate cursive;
/// # use cursive::{Cursive, views, view}; /// # use cursive::{Cursive, views, view};
/// # use cursive::traits::*; /// # use cursive::traits::*;
/// # fn main() { /// # fn main() {
/// fn main() { /// fn main() {
/// let mut siv = Cursive::new(); /// let mut siv = Cursive::dummy();
/// ///
/// siv.add_layer(views::TextView::new("Text #1").with_id("text")); /// siv.add_layer(views::TextView::new("Text #1").with_id("text"));
/// ///
@ -328,12 +382,12 @@ impl Cursive {
/// ///
/// # Examples /// # Examples
/// ///
/// ```rust,no_run /// ```rust
/// # extern crate cursive; /// # extern crate cursive;
/// # use cursive::{Cursive, views}; /// # use cursive::{Cursive, views};
/// # use cursive::traits::*; /// # use cursive::traits::*;
/// # fn main() { /// # fn main() {
/// let mut siv = Cursive::new(); /// let mut siv = Cursive::dummy();
/// ///
/// siv.add_layer(views::TextView::new("Text #1") /// siv.add_layer(views::TextView::new("Text #1")
/// .with_id("text")); /// .with_id("text"));
@ -361,10 +415,10 @@ impl Cursive {
/// ///
/// # Examples /// # Examples
/// ///
/// ```rust,no_run /// ```rust
/// # use cursive::Cursive; /// # use cursive::Cursive;
/// # use cursive::views::{TextView, ViewRef}; /// # use cursive::views::{TextView, ViewRef};
/// # let mut siv = Cursive::new(); /// # let mut siv = Cursive::dummy();
/// use cursive::traits::Identifiable; /// use cursive::traits::Identifiable;
/// ///
/// siv.add_layer(TextView::new("foo").with_id("id")); /// siv.add_layer(TextView::new("foo").with_id("id"));
@ -401,11 +455,11 @@ impl Cursive {
/// ///
/// # Examples /// # Examples
/// ///
/// ```rust,no_run /// ```rust
/// # extern crate cursive; /// # extern crate cursive;
/// # use cursive::*; /// # use cursive::*;
/// # fn main() { /// # fn main() {
/// let mut siv = Cursive::new(); /// let mut siv = Cursive::dummy();
/// ///
/// siv.add_global_callback('q', |s| s.quit()); /// siv.add_global_callback('q', |s| s.quit());
/// # } /// # }
@ -424,11 +478,11 @@ impl Cursive {
/// ///
/// # Examples /// # Examples
/// ///
/// ```rust,no_run /// ```rust
/// # extern crate cursive; /// # extern crate cursive;
/// # use cursive::*; /// # use cursive::*;
/// # fn main() { /// # fn main() {
/// let mut siv = Cursive::new(); /// let mut siv = Cursive::dummy();
/// ///
/// siv.add_global_callback('q', |s| s.quit()); /// siv.add_global_callback('q', |s| s.quit());
/// siv.clear_global_callbacks('q'); /// siv.clear_global_callbacks('q');
@ -446,11 +500,11 @@ impl Cursive {
/// ///
/// # Examples /// # Examples
/// ///
/// ```rust,no_run /// ```rust
/// # extern crate cursive; /// # extern crate cursive;
/// # use cursive::*; /// # use cursive::*;
/// # fn main() { /// # fn main() {
/// let mut siv = Cursive::new(); /// let mut siv = Cursive::dummy();
/// ///
/// siv.add_layer(views::TextView::new("Hello world!")); /// siv.add_layer(views::TextView::new("Hello world!"));
/// # } /// # }
@ -499,12 +553,7 @@ impl Cursive {
/// Returns the size of the screen, in characters. /// Returns the size of the screen, in characters.
pub fn screen_size(&self) -> Vec2 { pub fn screen_size(&self) -> Vec2 {
let (x, y) = self.backend.screen_size(); self.backend.screen_size()
Vec2 {
x: x as usize,
y: y as usize,
}
} }
fn layout(&mut self) { fn layout(&mut self) {
@ -526,7 +575,7 @@ impl Cursive {
} }
let printer = let printer =
Printer::new(self.screen_size(), &self.theme, &self.backend); Printer::new(self.screen_size(), &self.theme, &*self.backend);
let selected = self.menubar.receive_events(); let selected = self.menubar.receive_events();

View File

@ -32,14 +32,14 @@
//! //!
//! ## Examples //! ## Examples
//! //!
//! ```no_run //! ```rust
//! extern crate cursive; //! extern crate cursive;
//! //!
//! use cursive::Cursive; //! use cursive::Cursive;
//! use cursive::views::TextView; //! use cursive::views::TextView;
//! //!
//! fn main() { //! fn main() {
//! let mut siv = Cursive::new(); //! let mut siv = Cursive::dummy();
//! //!
//! siv.add_layer(TextView::new("Hello World!\nPress q to quit.")); //! siv.add_layer(TextView::new("Hello World!\nPress q to quit."));
//! //!
@ -73,6 +73,7 @@ extern crate log;
extern crate maplit; extern crate maplit;
extern crate num; extern crate num;
extern crate libc;
extern crate owning_ref; extern crate owning_ref;
extern crate toml; extern crate toml;
extern crate unicode_segmentation; extern crate unicode_segmentation;

View File

@ -1,6 +1,6 @@
//! Makes drawing on ncurses windows easier. //! Makes drawing on ncurses windows easier.
use backend::{self, Backend}; use backend::Backend;
use enumset::EnumSet; use enumset::EnumSet;
use std::cell::Cell; use std::cell::Cell;
use std::cmp::min; use std::cmp::min;
@ -34,7 +34,7 @@ pub struct Printer<'a> {
new: Rc<Cell<bool>>, new: Rc<Cell<bool>>,
/// Backend used to actually draw things /// Backend used to actually draw things
backend: &'a backend::Concrete, backend: &'a Backend,
} }
impl<'a> Clone for Printer<'a> { impl<'a> Clone for Printer<'a> {
@ -57,16 +57,16 @@ impl<'a> Printer<'a> {
/// But nobody needs to know that. /// But nobody needs to know that.
#[doc(hidden)] #[doc(hidden)]
pub fn new<T: Into<Vec2>>( pub fn new<T: Into<Vec2>>(
size: T, theme: &'a Theme, backend: &'a backend::Concrete size: T, theme: &'a Theme, backend: &'a Backend
) -> Self { ) -> Self {
Printer { Printer {
offset: Vec2::zero(), offset: Vec2::zero(),
content_offset: Vec2::zero(), content_offset: Vec2::zero(),
size: size.into(), size: size.into(),
focused: true, focused: true,
theme: theme, theme,
new: Rc::new(Cell::new(true)), new: Rc::new(Cell::new(true)),
backend: backend, backend,
} }
} }
@ -103,7 +103,7 @@ impl<'a> Printer<'a> {
let text = &text[..prefix_len]; let text = &text[..prefix_len];
let p = p + self.offset; let p = p + self.offset;
self.backend.print_at((p.x, p.y), text); self.backend.print_at(p, text);
} }
/// Prints a vertical line using the given character. /// Prints a vertical line using the given character.
@ -118,7 +118,7 @@ impl<'a> Printer<'a> {
let p = p + self.offset; let p = p + self.offset;
for y in 0..len { for y in 0..len {
self.backend.print_at((p.x, (p.y + y)), c); self.backend.print_at(p + (0,y), c);
} }
} }
@ -134,7 +134,7 @@ impl<'a> Printer<'a> {
let text: String = ::std::iter::repeat(c).take(len).collect(); let text: String = ::std::iter::repeat(c).take(len).collect();
let p = p + self.offset; let p = p + self.offset;
self.backend.print_at((p.x, p.y), &text); self.backend.print_at(p, &text);
} }
/// Call the given closure with a colored printer, /// Call the given closure with a colored printer,
@ -142,13 +142,13 @@ impl<'a> Printer<'a> {
/// ///
/// # Examples /// # Examples
/// ///
/// ```no_run /// ```rust
/// # use cursive::Printer; /// # use cursive::Printer;
/// # use cursive::theme; /// # use cursive::theme;
/// # use cursive::backend::{self, Backend}; /// # use cursive::backend;
/// # let b = backend::Concrete::init(); /// # let b = backend::dummy::Backend::init();
/// # let t = theme::load_default(); /// # let t = theme::load_default();
/// # let printer = Printer::new((6,4), &t, &b); /// # let printer = Printer::new((6,4), &t, &*b);
/// printer.with_color(theme::ColorStyle::highlight(), |printer| { /// printer.with_color(theme::ColorStyle::highlight(), |printer| {
/// printer.print((0,0), "This text is highlighted!"); /// printer.print((0,0), "This text is highlighted!");
/// }); /// });
@ -157,8 +157,9 @@ impl<'a> Printer<'a> {
where where
F: FnOnce(&Printer), F: FnOnce(&Printer),
{ {
self.backend let old = self.backend.set_color(c.resolve(&self.theme.palette));
.with_color(c.resolve(&self.theme.palette), || f(self)); f(self);
self.backend.set_color(old);
} }
/// Call the given closure with a styled printer, /// Call the given closure with a styled printer,
@ -190,7 +191,9 @@ impl<'a> Printer<'a> {
where where
F: FnOnce(&Printer), F: FnOnce(&Printer),
{ {
self.backend.with_effect(effect, || f(self)); self.backend.set_effect(effect);
f(self);
self.backend.unset_effect(effect);
} }
/// Call the given closure with a modified printer /// Call the given closure with a modified printer
@ -217,13 +220,13 @@ impl<'a> Printer<'a> {
/// ///
/// # Examples /// # Examples
/// ///
/// ```no_run /// ```rust
/// # use cursive::Printer; /// # use cursive::Printer;
/// # use cursive::theme; /// # use cursive::theme;
/// # use cursive::backend::{self, Backend}; /// # use cursive::backend;
/// # let b = backend::Concrete::init(); /// # let b = backend::dummy::Backend::init();
/// # let t = theme::load_default(); /// # let t = theme::load_default();
/// # let printer = Printer::new((6,4), &t, &b); /// # let printer = Printer::new((6,4), &t, &*b);
/// printer.print_box((0,0), (6,4), false); /// printer.print_box((0,0), (6,4), false);
/// ``` /// ```
pub fn print_box<T: Into<Vec2>, S: Into<Vec2>>( pub fn print_box<T: Into<Vec2>, S: Into<Vec2>>(

30
src/utils/counter.rs Normal file
View File

@ -0,0 +1,30 @@
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
/// Atomic counter used by [`ProgressBar`].
///
/// [`ProgressBar`]: ../views/struct.ProgressBar.html
#[derive(Clone)]
pub struct Counter(pub Arc<AtomicUsize>);
impl Counter {
/// Creates a new `Counter` starting with the given value.
pub fn new(value: usize) -> Self {
Counter(Arc::new(AtomicUsize::new(value)))
}
/// Retrieves the current progress value.
pub fn get(&self) -> usize {
self.0.load(Ordering::Relaxed)
}
/// Sets the current progress value.
pub fn set(&self, value: usize) {
self.0.store(value, Ordering::Relaxed);
}
/// Increase the current progress by `ticks`.
pub fn tick(&self, ticks: usize) {
self.0.fetch_add(ticks, Ordering::Relaxed);
}
}

View File

@ -4,13 +4,13 @@
//! //!
//! Computed rows will include a list of span segments. //! Computed rows will include a list of span segments.
//! Each segment include the source span ID, and start/end byte offsets. //! Each segment include the source span ID, and start/end byte offsets.
mod lines_iterator;
mod chunk_iterator;
mod segment_merge_iterator;
mod row;
mod prefix;
mod chunk; mod chunk;
mod chunk_iterator;
mod lines_iterator;
mod prefix;
mod row;
mod segment; mod segment;
mod segment_merge_iterator;
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;

View File

@ -18,7 +18,7 @@ where
let spans = parse_spans(&input); let spans = parse_spans(&input);
StyledString::new(input, spans) StyledString::with_spans(input, spans)
} }
/// Iterator that parse a markdown text and outputs styled spans. /// Iterator that parse a markdown text and outputs styled spans.

View File

@ -1,8 +1,10 @@
//! Toolbox to make text layout easier. //! Toolbox to make text layout easier.
mod reader; mod counter;
pub mod span;
pub mod lines; pub mod lines;
pub mod markup; pub mod markup;
mod reader;
pub mod span;
pub use self::counter::Counter;
pub use self::reader::ProgressReader; pub use self::reader::ProgressReader;

View File

@ -1,5 +1,5 @@
use std::io::{self, Read}; use std::io::{self, Read};
use views::Counter; use utils::Counter;
/// Wrapper around a `Read` that reports the progress made. /// Wrapper around a `Read` that reports the progress made.
/// ///
@ -32,7 +32,7 @@ impl<R: Read> ProgressReader<R> {
impl<R: Read> Read for ProgressReader<R> { impl<R: Read> Read for ProgressReader<R> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
let result = try!(self.reader.read(buf)); let result = self.reader.read(buf)?;
self.counter.tick(result); self.counter.tick(result);
Ok(result) Ok(result)
} }

View File

@ -13,13 +13,13 @@ pub trait Identifiable: View + Sized {
/// ///
/// # Examples /// # Examples
/// ///
/// ```rust,no_run /// ```rust
/// # use cursive::Cursive; /// # use cursive::Cursive;
/// # use cursive::views::TextView; /// # use cursive::views::TextView;
/// # use cursive::view::Boxable; /// # use cursive::view::Boxable;
/// use cursive::view::Identifiable; /// use cursive::view::Identifiable;
/// ///
/// let mut siv = Cursive::new(); /// let mut siv = Cursive::dummy();
/// siv.add_layer( /// siv.add_layer(
/// TextView::new("foo") /// TextView::new("foo")
/// .with_id("text") /// .with_id("text")

View File

@ -18,10 +18,10 @@ impl Margins {
/// Creates a new Margins. /// Creates a new Margins.
pub fn new(left: usize, right: usize, top: usize, bottom: usize) -> Self { pub fn new(left: usize, right: usize, top: usize, bottom: usize) -> Self {
Margins { Margins {
left: left, left,
right: right, right,
top: top, top,
bottom: bottom, bottom,
} }
} }
@ -53,7 +53,7 @@ impl Margins {
impl From<(usize, usize, usize, usize)> for Margins { impl From<(usize, usize, usize, usize)> for Margins {
fn from( fn from(
(left, right, top, bottom): (usize, usize, usize, usize) (left, right, top, bottom): (usize, usize, usize, usize),
) -> Margins { ) -> Margins {
Margins::new(left, right, top, bottom) Margins::new(left, right, top, bottom)
} }
@ -72,14 +72,14 @@ impl From<(i32, i32, i32, i32)> for Margins {
impl From<((i32, i32), (i32, i32))> for Margins { impl From<((i32, i32), (i32, i32))> for Margins {
fn from( fn from(
((left, right), (top, bottom)): ((i32, i32), (i32, i32)) ((left, right), (top, bottom)): ((i32, i32), (i32, i32)),
) -> Margins { ) -> Margins {
(left, right, top, bottom).into() (left, right, top, bottom).into()
} }
} }
impl From<((usize, usize), (usize, usize))> for Margins { impl From<((usize, usize), (usize, usize))> for Margins {
fn from( fn from(
((left, right), (top, bottom)): ((usize, usize), (usize, usize)) ((left, right), (top, bottom)): ((usize, usize), (usize, usize)),
) -> Margins { ) -> Margins {
(left, right, top, bottom).into() (left, right, top, bottom).into()
} }

View File

@ -228,15 +228,15 @@ impl ScrollBase {
/// ///
/// # Examples /// # Examples
/// ///
/// ```no_run /// ```rust
/// # use cursive::view::ScrollBase; /// # use cursive::view::ScrollBase;
/// # use cursive::Printer; /// # use cursive::Printer;
/// # use cursive::theme; /// # use cursive::theme;
/// # use cursive::backend::{self, Backend}; /// # use cursive::backend;
/// # let scrollbase = ScrollBase::new(); /// # let scrollbase = ScrollBase::new();
/// # let b = backend::Concrete::init(); /// # let b = backend::dummy::Backend::init();
/// # let t = theme::load_default(); /// # let t = theme::load_default();
/// # let printer = Printer::new((5,1), &t, &b); /// # let printer = Printer::new((5,1), &t, &*b);
/// # let printer = &printer; /// # let printer = &printer;
/// let lines = ["Line 1", "Line number 2"]; /// let lines = ["Line 1", "Line number 2"];
/// scrollbase.draw(printer, |printer, i| { /// scrollbase.draw(printer, |printer, i| {

View File

@ -1,5 +1,5 @@
use XY;
use vec::Vec2; use vec::Vec2;
use XY;
/// Cache around a one-dimensional layout result. /// Cache around a one-dimensional layout result.
/// ///
@ -19,8 +19,8 @@ impl SizeCache {
/// Creates a new sized cache /// Creates a new sized cache
pub fn new(value: usize, constrained: bool) -> Self { pub fn new(value: usize, constrained: bool) -> Self {
SizeCache { SizeCache {
value: value, value,
constrained: constrained, constrained,
} }
} }

View File

@ -152,7 +152,7 @@ impl<T: ViewWrapper> View for T {
/// ///
/// # Examples /// # Examples
/// ///
/// ```no_run /// ```rust
/// # #[macro_use] extern crate cursive; /// # #[macro_use] extern crate cursive;
/// # use cursive::view::{View,ViewWrapper}; /// # use cursive::view::{View,ViewWrapper};
/// struct FooView<T: View> { /// struct FooView<T: View> {
@ -197,7 +197,7 @@ macro_rules! wrap_impl {
/// ///
/// # Examples /// # Examples
/// ///
/// ```no_run /// ```rust
/// # #[macro_use] extern crate cursive; /// # #[macro_use] extern crate cursive;
/// # use cursive::view::{View,ViewWrapper}; /// # use cursive::view::{View,ViewWrapper};
/// struct FooView<T: View> { /// struct FooView<T: View> {

View File

@ -44,7 +44,7 @@ impl<T: View> BoxView<T> {
BoxView { BoxView {
size: (width, height).into(), size: (width, height).into(),
squishable: false, squishable: false,
view: view, view,
} }
} }

View File

@ -55,7 +55,7 @@ impl<T> Canvas<T> {
/// ``` /// ```
pub fn new(state: T) -> Self { pub fn new(state: T) -> Self {
Canvas { Canvas {
state: state, state,
draw: Box::new(|_, _| ()), draw: Box::new(|_, _| ()),
on_event: Box::new(|_, _| EventResult::Ignored), on_event: Box::new(|_, _| EventResult::Ignored),
required_size: Box::new(|_, _| Vec2::new(1, 1)), required_size: Box::new(|_, _| Vec2::new(1, 1)),

View File

@ -30,13 +30,13 @@ pub type OnSubmit = Fn(&mut Cursive, &str);
/// ///
/// [1]: https://github.com/gyscos/Cursive/blob/master/examples/edit.rs /// [1]: https://github.com/gyscos/Cursive/blob/master/examples/edit.rs
/// ///
/// ```no_run /// ```rust
/// # extern crate cursive; /// # extern crate cursive;
/// # use cursive::Cursive; /// # use cursive::Cursive;
/// # use cursive::traits::*; /// # use cursive::traits::*;
/// # use cursive::views::{Dialog, EditView, TextView}; /// # use cursive::views::{Dialog, EditView, TextView};
/// # fn main() { /// # fn main() {
/// let mut siv = Cursive::new(); /// let mut siv = Cursive::dummy();
/// ///
/// // Create a dialog with an edit text and a button. /// // Create a dialog with an edit text and a button.
/// // The user can either hit the <Ok> button, /// // The user can either hit the <Ok> button,

View File

@ -113,7 +113,7 @@ impl LinearLayout {
pub fn new(orientation: direction::Orientation) -> Self { pub fn new(orientation: direction::Orientation) -> Self {
LinearLayout { LinearLayout {
children: Vec::new(), children: Vec::new(),
orientation: orientation, orientation,
focus: 0, focus: 0,
cache: None, cache: None,
} }

View File

@ -27,7 +27,7 @@ impl MenuPopup {
/// Creates a new `MenuPopup` using the given menu tree. /// Creates a new `MenuPopup` using the given menu tree.
pub fn new(menu: Rc<MenuTree>) -> Self { pub fn new(menu: Rc<MenuTree>) -> Self {
MenuPopup { MenuPopup {
menu: menu, menu,
focus: 0, focus: 0,
scrollbase: ScrollBase::new() scrollbase: ScrollBase::new()
.scrollbar_offset(1) .scrollbar_offset(1)

View File

@ -78,7 +78,7 @@ pub use self::menu_popup::MenuPopup;
pub use self::menubar::Menubar; pub use self::menubar::Menubar;
pub use self::on_event_view::OnEventView; pub use self::on_event_view::OnEventView;
pub use self::panel::Panel; pub use self::panel::Panel;
pub use self::progress_bar::{Counter, ProgressBar}; pub use self::progress_bar::ProgressBar;
pub use self::radio::{RadioButton, RadioGroup}; pub use self::radio::{RadioButton, RadioGroup};
pub use self::scroll_view::ScrollView; pub use self::scroll_view::ScrollView;
pub use self::select_view::SelectView; pub use self::select_view::SelectView;

View File

@ -72,7 +72,7 @@ impl<T: View> OnEventView<T> {
/// Wraps the given view in a new OnEventView. /// Wraps the given view in a new OnEventView.
pub fn new(view: T) -> Self { pub fn new(view: T) -> Self {
OnEventView { OnEventView {
view: view, view,
callbacks: HashMap::new(), callbacks: HashMap::new(),
} }
} }

View File

@ -13,7 +13,7 @@ pub struct Panel<V: View> {
impl<V: View> Panel<V> { impl<V: View> Panel<V> {
/// Creates a new panel around the given view. /// Creates a new panel around the given view.
pub fn new(view: V) -> Self { pub fn new(view: V) -> Self {
Panel { view: view } Panel { view }
} }
inner_getters!(self.view: V); inner_getters!(self.view: V);

View File

@ -1,40 +1,13 @@
use Printer; use Printer;
use align::HAlign; use align::HAlign;
use std::cmp; use std::cmp;
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::thread; use std::thread;
use theme::{ColorStyle, Effect}; use theme::{ColorStyle, Effect};
use utils::Counter;
use view::View; use view::View;
// pub type CbPromise = Option<Box<Fn(&mut Cursive) + Send>>; // pub type CbPromise = Option<Box<Fn(&mut Cursive) + Send>>;
/// Atomic counter used by `ProgressBar`.
#[derive(Clone)]
pub struct Counter(pub Arc<AtomicUsize>);
impl Counter {
/// Creates a new `Counter` starting with the given value.
pub fn new(value: usize) -> Self {
Counter(Arc::new(AtomicUsize::new(value)))
}
/// Retrieves the current progress value.
pub fn get(&self) -> usize {
self.0.load(Ordering::Relaxed)
}
/// Sets the current progress value.
pub fn set(&self, value: usize) {
self.0.store(value, Ordering::Relaxed);
}
/// Increase the current progress by `ticks`.
pub fn tick(&self, ticks: usize) {
self.0.fetch_add(ticks, Ordering::Relaxed);
}
}
/// Animated bar showing a progress value. /// Animated bar showing a progress value.
/// ///
/// This bar has an internal counter, and adapts the length of the displayed /// This bar has an internal counter, and adapts the length of the displayed

View File

@ -110,10 +110,10 @@ impl<T> RadioButton<T> {
state: Rc<RefCell<SharedState<T>>>, id: usize, label: String state: Rc<RefCell<SharedState<T>>>, id: usize, label: String
) -> Self { ) -> Self {
RadioButton { RadioButton {
state: state, state,
id: id, id,
enabled: true, enabled: true,
label: label, label,
} }
} }

View File

@ -22,7 +22,7 @@ use views::MenuPopup;
/// ///
/// # Examples /// # Examples
/// ///
/// ```no_run /// ```rust
/// # extern crate cursive; /// # extern crate cursive;
/// # use cursive::Cursive; /// # use cursive::Cursive;
/// # use cursive::views::{SelectView, Dialog, TextView}; /// # use cursive::views::{SelectView, Dialog, TextView};
@ -40,7 +40,7 @@ use views::MenuPopup;
/// .button("Quit", |s| s.quit())); /// .button("Quit", |s| s.quit()));
/// }); /// });
/// ///
/// let mut siv = Cursive::new(); /// let mut siv = Cursive::dummy();
/// siv.add_layer(Dialog::around(time_select) /// siv.add_layer(Dialog::around(time_select)
/// .title("How long is your wait?")); /// .title("How long is your wait?"));
/// # } /// # }
@ -262,6 +262,15 @@ impl<T: 'static> SelectView<T> {
.unwrap_or_else(Callback::dummy) .unwrap_or_else(Callback::dummy)
} }
/// Inserts an item at position `index`, shifting all elements after it to
/// the right.
pub fn insert_item<S>(&mut self, index: usize, label: S, value: T)
where
S: Into<String>,
{
self.items.insert(index, Item::new(label.into(), value));
}
/// Chainable variant of add_item /// Chainable variant of add_item
pub fn item<S: Into<String>>(self, label: S, value: T) -> Self { pub fn item<S: Into<String>>(self, label: S, value: T) -> Self {
self.with(|s| s.add_item(label, value)) self.with(|s| s.add_item(label, value))
@ -334,7 +343,7 @@ impl<T: 'static> SelectView<T> {
// TODO: Check if `i >= self.len()` ? // TODO: Check if `i >= self.len()` ?
// assert!(i < self.len(), "SelectView: trying to select out-of-bound"); // assert!(i < self.len(), "SelectView: trying to select out-of-bound");
// Or just cap the ID? // Or just cap the ID?
let i = if self.len() == 0 { let i = if self.is_empty() {
0 0
} else { } else {
min(i, self.len() - 1) min(i, self.len() - 1)
@ -638,6 +647,12 @@ impl SelectView<String> {
self.with(|s| s.add_item_str(label)) self.with(|s| s.add_item_str(label))
} }
/// Convenient method to use the label as value.
pub fn insert_item_str<S>(&mut self, index: usize, label: S) where S: Into<String> {
let label = label.into();
self.insert_item(index, label.clone(), label);
}
/// Adds all strings from an iterator. /// Adds all strings from an iterator.
/// ///
/// # Examples /// # Examples

View File

@ -110,6 +110,37 @@ impl TextArea {
self.with(|s| s.set_content(content)) self.with(|s| s.set_content(content))
} }
/// Disables this view.
///
/// A disabled view cannot be selected.
pub fn disable(&mut self) {
self.enabled = false;
}
/// Disables this view.
///
/// Chainable variant.
pub fn disabled(self) -> Self {
self.with(Self::disable)
}
/// Re-enables this view.
pub fn enable(&mut self) {
self.enabled = true;
}
/// Re-enables this view.
///
/// Chainable variant.
pub fn enabled(self) -> Self {
self.with(Self::enable)
}
/// Returns `true` if this view is enabled.
pub fn is_enabled(&self) -> bool {
self.enabled
}
/// Finds the row containing the grapheme at the given offset /// Finds the row containing the grapheme at the given offset
fn row_at(&self, offset: usize) -> usize { fn row_at(&self, offset: usize) -> usize {
debug!("Offset: {}", offset); debug!("Offset: {}", offset);

View File

@ -155,10 +155,10 @@ impl TextContentInner {
/// ///
/// # Examples /// # Examples
/// ///
/// ```rust,no_run /// ```rust
/// # use cursive::Cursive; /// # use cursive::Cursive;
/// # use cursive::views::TextView; /// # use cursive::views::TextView;
/// let mut siv = Cursive::new(); /// let mut siv = Cursive::dummy();
/// ///
/// siv.add_layer(TextView::new("Hello world!")); /// siv.add_layer(TextView::new("Hello world!"));
/// ``` /// ```

View File

@ -14,6 +14,17 @@ pub trait With: Sized {
f(&mut self)?; f(&mut self)?;
Ok(self) Ok(self)
} }
/// Calls the given closure if `condition == true`.
fn with_if<F>(mut self, condition: bool, f: F) -> Self
where
F: FnOnce(&mut Self),
{
if condition {
f(&mut self);
}
self
}
} }
impl<T: Sized> With for T {} impl<T: Sized> With for T {}

View File

@ -13,7 +13,7 @@ pub struct XY<T> {
impl<T> XY<T> { impl<T> XY<T> {
/// Creates a new `XY` from the given values. /// Creates a new `XY` from the given values.
pub fn new(x: T, y: T) -> Self { pub fn new(x: T, y: T) -> Self {
XY { x: x, y: y } XY { x, y }
} }
/// Returns `f(self.x, self.y)` /// Returns `f(self.x, self.y)`