From 66a66e595117db1e7a6850376e309d0c3d659445 Mon Sep 17 00:00:00 2001 From: Alexandre Bury Date: Sun, 18 Mar 2018 10:09:58 -0700 Subject: [PATCH 01/15] Update doc links in tutorials --- doc/tutorial_1.md | 18 +++++++++--------- doc/tutorial_2.md | 14 +++++++------- doc/tutorial_3.md | 34 +++++++++++++++++----------------- 3 files changed, 33 insertions(+), 33 deletions(-) diff --git a/doc/tutorial_1.md b/doc/tutorial_1.md index ff5b06e..586dfd9 100644 --- a/doc/tutorial_1.md +++ b/doc/tutorial_1.md @@ -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 leaving the application (you'll have to press to kill it). -[`Cursive`]: http://gyscos.github.io/Cursive/cursive/struct.Cursive.html +[`Cursive`]: https://docs.rs/cursive/0/cursive/struct.Cursive.html ## Interactivity @@ -128,10 +128,10 @@ fn main() { As expected, running it show no visible change, but hitting the `q` key at least closes the application. -[`add_global_callback`]: http://gyscos.github.io/Cursive/cursive/struct.Cursive.html#method.add_global_callback -[`event::Event`]: http://gyscos.github.io/Cursive/cursive/event/enum.Event.html -[`event::Key`]: http://gyscos.github.io/Cursive/cursive/event/enum.Key.html -[`Cursive::quit`]: http://gyscos.github.io/Cursive/cursive/struct.Cursive.html#method.quit +[`add_global_callback`]: https://docs.rs/cursive/0/cursive/struct.Cursive.html#method.add_global_callback +[`event::Event`]: https://docs.rs/cursive/0/cursive/event/enum.Event.html +[`event::Key`]: https://docs.rs/cursive/0/cursive/event/enum.Key.html +[`Cursive::quit`]: https://docs.rs/cursive/0/cursive/struct.Cursive.html#method.quit ## Views @@ -169,9 +169,9 @@ fn main() { } ``` -[`View`s]: http://gyscos.github.io/Cursive/cursive/view/trait.View.html -[`TextView`]: http://gyscos.github.io/Cursive/cursive/views/struct.TextView.html -[`StackView`]: http://gyscos.github.io/Cursive/cursive/views/struct.StackView.html -[`Cursive::add_layer`]: http://gyscos.github.io/Cursive/cursive/struct.Cursive.html#method.add_layer +[`View`s]: https://docs.rs/cursive/0/cursive/view/trait.View.html +[`TextView`]: https://docs.rs/cursive/0/cursive/views/struct.TextView.html +[`StackView`]: https://docs.rs/cursive/0/cursive/views/struct.StackView.html +[`Cursive::add_layer`]: https://docs.rs/cursive/0/cursive/struct.Cursive.html#method.add_layer Next: [Starting with Cursive (2/3)](./tutorial_2.md) diff --git a/doc/tutorial_2.md b/doc/tutorial_2.md index 5bc0ade..ba3634c 100644 --- a/doc/tutorial_2.md +++ b/doc/tutorial_2.md @@ -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 and configuring it in one spot. -[`TextView`]: http://gyscos.github.io/Cursive/cursive/views/struct.TextView -[`Dialog`]: http://gyscos.github.io/Cursive/cursive/views/struct.Dialog.html -[`Dialog::around`]: http://gyscos.github.io/Cursive/cursive/views/struct.Dialog.html#method.around -[`Dialog::text`]: http://gyscos.github.io/Cursive/cursive/views/struct.Dialog.html#method.text -[`Dialog::title`]: http://gyscos.github.io/Cursive/cursive/views/struct.Dialog.html#method.title +[`TextView`]: https://docs.rs/cursive/0/cursive/views/struct.TextView +[`Dialog`]: https://docs.rs/cursive/0/cursive/views/struct.Dialog.html +[`Dialog::around`]: https://docs.rs/cursive/0/cursive/views/struct.Dialog.html#method.around +[`Dialog::text`]: https://docs.rs/cursive/0/cursive/views/struct.Dialog.html#method.text +[`Dialog::title`]: https://docs.rs/cursive/0/cursive/views/struct.Dialog.html#method.title ## Buttons @@ -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 @@ -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 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 diff --git a/doc/tutorial_3.md b/doc/tutorial_3.md index a63460e..b310f30 100644 --- a/doc/tutorial_3.md +++ b/doc/tutorial_3.md @@ -138,11 +138,11 @@ not on the `BoxView` returned by `fixed_size`!) What we do there should be pretty familiar by now: replace the layer with a simple dialog. -[`SelectView`]: http://gyscos.github.io/Cursive/cursive/views/struct.SelectView.html -[`BoxView`]: http://gyscos.github.io/Cursive/cursive/views/struct.BoxView.html -[`Boxable`]: http://gyscos.github.io/Cursive/cursive/view/trait.Boxable.html -[`traits`]: http://gyscos.github.io/Cursive/cursive/traits/index.html -[`SelectView::on_submit`]: http://gyscos.github.io/Cursive/cursive/views/struct.SelectView.html#method.on_submit +[`SelectView`]: https://docs.rs/cursive/0/cursive/views/struct.SelectView.html +[`BoxView`]: https://docs.rs/cursive/0/cursive/views/struct.BoxView.html +[`Boxable`]: https://docs.rs/cursive/0/cursive/view/trait.Boxable.html +[`traits`]: https://docs.rs/cursive/0/cursive/traits/index.html +[`SelectView::on_submit`]: https://docs.rs/cursive/0/cursive/views/struct.SelectView.html#method.on_submit ## 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 this list with names! -[`Button`]: http://gyscos.github.io/Cursive/cursive/views/struct.Button.html -[`Dialog::around`]: http://gyscos.github.io/Cursive/cursive/views/struct.Dialog.html#method.new -[`LinearLayout`]: http://gyscos.github.io/Cursive/cursive/views/struct.LinearLayout.html -[`DummyView`]: http://gyscos.github.io/Cursive/cursive/views/struct.DummyView.html +[`Button`]: https://docs.rs/cursive/0/cursive/views/struct.Button.html +[`Dialog::around`]: https://docs.rs/cursive/0/cursive/views/struct.Dialog.html#method.new +[`LinearLayout`]: https://docs.rs/cursive/0/cursive/views/struct.LinearLayout.html +[`DummyView`]: https://docs.rs/cursive/0/cursive/views/struct.DummyView.html ## 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 view without borrowing the `Cursive` root, leaving us free to pop layers. -[`EditView`]: http://gyscos.github.io/Cursive/cursive/views/struct.EditView.html -[`IdView`]: http://gyscos.github.io/Cursive/cursive/views/struct.IdView.html -[`IdView::new`]: http://gyscos.github.io/Cursive/cursive/prelude/struct.IdView.html#method.around -[`Identifiable`]: http://gyscos.github.io/Cursive/cursive/view/trait.Identifiable.html -[`Cursive::find_id`]: http://gyscos.github.io/Cursive/cursive/struct.Cursive.html#method.find_id -[`Cursive::call_on_id`]: http://gyscos.github.io/Cursive/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::remove_item`]: http://gyscos.github.io/Cursive/cursive/views/struct.SelectView.html#method.remove_item +[`EditView`]: https://docs.rs/cursive/0/cursive/views/struct.EditView.html +[`IdView`]: https://docs.rs/cursive/0/cursive/views/struct.IdView.html +[`IdView::new`]: https://docs.rs/cursive/0/cursive/prelude/struct.IdView.html#method.around +[`Identifiable`]: https://docs.rs/cursive/0/cursive/view/trait.Identifiable.html +[`Cursive::find_id`]: https://docs.rs/cursive/0/cursive/struct.Cursive.html#method.find_id +[`Cursive::call_on_id`]: https://docs.rs/cursive/0/cursive/struct.Cursive.html#method.call_on_id +[`SelectView::selected_id`]: https://docs.rs/cursive/0/cursive/views/struct.SelectView.html#method.selected_id +[`SelectView::remove_item`]: https://docs.rs/cursive/0/cursive/views/struct.SelectView.html#method.remove_item ## Conclusion From 3588955f0b420123fcc3731fb3209d96ab86f85c Mon Sep 17 00:00:00 2001 From: Alexandre Bury Date: Sun, 25 Mar 2018 00:30:48 -0700 Subject: [PATCH 02/15] Add `SelectView::insert_item` --- src/views/select_view.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/views/select_view.rs b/src/views/select_view.rs index 7919df1..f42f9fc 100644 --- a/src/views/select_view.rs +++ b/src/views/select_view.rs @@ -260,6 +260,15 @@ impl SelectView { self.make_select_cb().unwrap_or_else(Callback::dummy) } + /// Inserts an item at position `index`, shifting all elements after it to + /// the right. + pub fn insert_item(&mut self, index: usize, label: S, value: T) + where + S: Into, + { + self.items.insert(index, Item::new(label.into(), value)); + } + /// Chainable variant of add_item pub fn item>(self, label: S, value: T) -> Self { self.with(|s| s.add_item(label, value)) @@ -630,6 +639,12 @@ impl SelectView { self.with(|s| s.add_item_str(label)) } + /// Convenient method to use the label as value. + pub fn insert_item_str(&mut self, index: usize, label: S) where S: Into { + let label = label.into(); + self.insert_item(index, label.clone(), label); + } + /// Adds all strings from an iterator. /// /// # Examples From 9df4b21a26f26e8293a9b9393423a0eae0233651 Mon Sep 17 00:00:00 2001 From: marcin Date: Wed, 28 Mar 2018 21:02:53 +0200 Subject: [PATCH 03/15] Adding disable/enable functionality to text_area --- src/views/text_area.rs | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/views/text_area.rs b/src/views/text_area.rs index 3e72e43..08e248c 100644 --- a/src/views/text_area.rs +++ b/src/views/text_area.rs @@ -107,6 +107,37 @@ impl TextArea { 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 fn row_at(&self, offset: usize) -> usize { debug!("Offset: {}", offset); From 76d340f11dc3ef7b23b49580d51b30b6c541617a Mon Sep 17 00:00:00 2001 From: SEGFAULT Date: Mon, 2 Apr 2018 00:35:37 +0200 Subject: [PATCH 04/15] Making `backend::Backend` into a `std::Box`able trait (#229) * Making Backend into a Boxable Trait * Fixed up some typos in the previous and modified some of the docs so they still compile. * Minor Changes requested by @gyscos * Whoops --- src/backend/blt.rs | 48 ++++++++++++++++++++++++++++++--------- src/backend/curses/n.rs | 30 ++++++++++++++---------- src/backend/curses/pan.rs | 29 ++++++++++++++--------- src/backend/mod.rs | 12 ++++++---- src/backend/termion.rs | 23 +++++++++---------- src/cursive.rs | 2 +- src/printer.rs | 19 +++++++++------- src/view/scroll.rs | 2 +- 8 files changed, 105 insertions(+), 60 deletions(-) diff --git a/src/backend/blt.rs b/src/backend/blt.rs index f3a47cc..3c28b6c 100644 --- a/src/backend/blt.rs +++ b/src/backend/blt.rs @@ -145,7 +145,7 @@ impl Concrete { } impl backend::Backend for Concrete { - fn init() -> Self { + fn init() -> Box { terminal::open("Cursive", 80, 24); terminal::set(terminal::config::Window::empty().resizeable(true)); terminal::set(vec![ @@ -159,37 +159,59 @@ impl backend::Backend for Concrete { }, ]); - Concrete { + let c = Concrete { mouse_position: Vec2::zero(), buttons_pressed: HashSet::new(), - } + }; + + Box::new(c) } fn finish(&mut self) { terminal::close(); } - fn with_color(&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 bg = colour_to_blt_colour(color.back, ColorRole::Background); - terminal::with_colors(fg, bg, f); + + terminal::set_colors(fg, bg); + + current } - fn with_effect(&self, effect: Effect, f: F) { + fn set_effect(&self, effect: Effect) { match effect { // TODO: does BLT support bold/italic/underline? Effect::Bold | Effect::Italic | Effect::Underline - | Effect::Simple => f(), + | Effect::Simple => {}, // TODO: how to do this correctly?` // BLT itself doesn't do this kind of thing, // we'd need the colours in our position, // but `f()` can do whatever - Effect::Reverse => terminal::with_colors( - BltColor::from_rgb(0, 0, 0), - BltColor::from_rgb(255, 255, 255), - f, + Effect::Reverse => terminal::set_colors( + state::background(), state::foreground() + ), + } + } + + 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() ), } } @@ -282,6 +304,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 { let (r, g, b) = match clr { Color::TerminalDefault => { diff --git a/src/backend/curses/n.rs b/src/backend/curses/n.rs index ad88f9e..7624598 100644 --- a/src/backend/curses/n.rs +++ b/src/backend/curses/n.rs @@ -150,7 +150,7 @@ impl Concrete { } impl backend::Backend for Concrete { - fn init() -> Self { + fn init() -> Box { // Change the locale. // For some reasons it's mandatory to get some UTF-8 support. ncurses::setlocale(ncurses::LcCategory::all, ""); @@ -186,7 +186,7 @@ impl backend::Backend for Concrete { print!("\x1B[?1002h"); stdout().flush().expect("could not flush stdout"); - Concrete { + let c = Concrete { current_style: Cell::new(ColorPair::from_256colors(0, 0)), pairs: RefCell::new(HashMap::new()), @@ -194,7 +194,9 @@ impl backend::Backend for Concrete { event_queue: Vec::new(), key_codes: initialize_keymap(), - } + }; + + Box::new(c) } fn screen_size(&self) -> (usize, usize) { @@ -214,21 +216,16 @@ impl backend::Backend for Concrete { ncurses::endwin(); } - fn with_color(&self, colors: ColorPair, f: F) { + fn set_color(&self, colors: ColorPair) -> ColorPair { // eprintln!("Color used: {:?}", colors); let current = self.current_style.get(); if current != colors { self.set_colors(colors); } - - f(); - - if current != colors { - self.set_colors(current); - } + return current; } - fn with_effect(&self, effect: Effect, f: F) { + fn set_effect(&self, effect: Effect) { let style = match effect { Effect::Reverse => ncurses::A_REVERSE(), Effect::Simple => ncurses::A_NORMAL(), @@ -237,7 +234,16 @@ impl backend::Backend for Concrete { Effect::Underline => ncurses::A_UNDERLINE(), }; 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); } diff --git a/src/backend/curses/pan.rs b/src/backend/curses/pan.rs index 48f905d..ba9741e 100644 --- a/src/backend/curses/pan.rs +++ b/src/backend/curses/pan.rs @@ -133,7 +133,7 @@ impl Concrete { } impl backend::Backend for Concrete { - fn init() -> Self { + fn init() -> Box { ::std::env::set_var("ESCDELAY", "25"); let window = pancurses::initscr(); @@ -155,14 +155,16 @@ impl backend::Backend for Concrete { print!("\x1B[?1002h"); stdout().flush().expect("could not flush stdout"); - Concrete { + let c = 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(), - } + }; + + Box::new(c) } fn screen_size(&self) -> (usize, usize) { @@ -180,21 +182,17 @@ impl backend::Backend for Concrete { pancurses::endwin(); } - fn with_color(&self, colors: ColorPair, f: F) { + fn set_color(&self, colors: ColorPair) -> ColorPair { let current = self.current_style.get(); if current != colors { self.set_colors(colors); } - f(); - - if current != colors { - self.set_colors(current); - } + current } - fn with_effect(&self, effect: Effect, f: F) { + fn set_effect(&self, effect: Effect) { let style = match effect { Effect::Simple => pancurses::Attribute::Normal, Effect::Reverse => pancurses::Attribute::Reverse, @@ -203,7 +201,16 @@ impl backend::Backend for Concrete { Effect::Underline => pancurses::Attribute::Underline, }; 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); } diff --git a/src/backend/mod.rs b/src/backend/mod.rs index f5619f8..4e6cd0f 100644 --- a/src/backend/mod.rs +++ b/src/backend/mod.rs @@ -16,7 +16,7 @@ pub use self::curses::*; pub use self::termion::*; pub trait Backend { - fn init() -> Self; + fn init() -> Box where Self: Sized; // TODO: take `self` by value? // Or implement Drop? fn finish(&mut self); @@ -34,7 +34,11 @@ pub trait Backend { fn clear(&self, color: theme::Color); fn set_refresh_rate(&mut self, fps: u32); - // TODO: unify those into a single method? - fn with_color(&self, colors: theme::ColorPair, f: F); - fn with_effect(&self, effect: theme::Effect, f: F); + + // 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; + + fn set_effect(&self, effect: theme::Effect); + fn unset_effect(&self, effect: theme::Effect); } diff --git a/src/backend/termion.rs b/src/backend/termion.rs index 3eeb3e3..33b3a3b 100644 --- a/src/backend/termion.rs +++ b/src/backend/termion.rs @@ -137,7 +137,7 @@ impl Concrete { } impl backend::Backend for Concrete { - fn init() -> Self { + fn init() -> Box { print!("{}", termion::cursor::Hide); let resize = chan_signal::notify(&[chan_signal::Signal::WINCH]); @@ -157,14 +157,16 @@ impl backend::Backend for Concrete { } }); - Concrete { + let c = Concrete { 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 finish(&mut self) { @@ -177,7 +179,7 @@ impl backend::Backend for Concrete { ); } - fn with_color(&self, color: theme::ColorPair, f: F) { + fn set_color(&self, color: theme::ColorPair) -> theme::ColorPair { let current_style = self.current_style.get(); if current_style != color { @@ -185,17 +187,14 @@ impl backend::Backend for Concrete { self.current_style.set(color); } - f(); - - if current_style != color { - self.current_style.set(current_style); - self.apply_colors(current_style); - } + return current_style; } - fn with_effect(&self, effect: theme::Effect, f: F) { + fn set_effect(&self, effect: theme::Effect) { effect.on(); - f(); + } + + fn unset_effect(&self, effect: theme::Effect) { effect.off(); } diff --git a/src/cursive.rs b/src/cursive.rs index 2af1d5b..8626f00 100644 --- a/src/cursive.rs +++ b/src/cursive.rs @@ -54,7 +54,7 @@ pub struct Cursive { running: bool, - backend: backend::Concrete, + backend: Box, cb_source: mpsc::Receiver>, cb_sink: mpsc::Sender>, diff --git a/src/printer.rs b/src/printer.rs index f55a4ee..1b49ff4 100644 --- a/src/printer.rs +++ b/src/printer.rs @@ -1,6 +1,6 @@ //! Makes drawing on ncurses windows easier. -use backend::{self, Backend}; +use backend::Backend; use enumset::EnumSet; use std::cell::Cell; use std::cmp::min; @@ -24,7 +24,7 @@ pub struct Printer<'a> { /// `true` if nothing has been drawn yet. new: Rc>, /// Backend used to actually draw things - backend: &'a backend::Concrete, + backend: &'a Box, } impl<'a> Printer<'a> { @@ -33,7 +33,7 @@ impl<'a> Printer<'a> { /// But nobody needs to know that. #[doc(hidden)] pub fn new>( - size: T, theme: &'a Theme, backend: &'a backend::Concrete + size: T, theme: &'a Theme, backend: &'a Box ) -> Self { Printer { offset: Vec2::zero(), @@ -121,7 +121,7 @@ impl<'a> Printer<'a> { /// # use cursive::Printer; /// # use cursive::theme; /// # use cursive::backend::{self, Backend}; - /// # let b = backend::Concrete::init(); + /// # let b: Box = backend::Concrete::init(); /// # let t = theme::load_default(); /// # let printer = Printer::new((6,4), &t, &b); /// printer.with_color(theme::ColorStyle::highlight(), |printer| { @@ -132,8 +132,9 @@ impl<'a> Printer<'a> { where F: FnOnce(&Printer), { - self.backend - .with_color(c.resolve(&self.theme.palette), || f(self)); + let old = self.backend.set_color(c.resolve(&self.theme.palette)); + f(self); + self.backend.set_color(old); } /// Call the given closure with a styled printer, @@ -165,7 +166,9 @@ impl<'a> Printer<'a> { where 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 @@ -196,7 +199,7 @@ impl<'a> Printer<'a> { /// # use cursive::Printer; /// # use cursive::theme; /// # use cursive::backend::{self, Backend}; - /// # let b = backend::Concrete::init(); + /// # let b: Box = backend::Concrete::init(); /// # let t = theme::load_default(); /// # let printer = Printer::new((6,4), &t, &b); /// printer.print_box((0,0), (6,4), false); diff --git a/src/view/scroll.rs b/src/view/scroll.rs index f9f90b6..0b6dd05 100644 --- a/src/view/scroll.rs +++ b/src/view/scroll.rs @@ -232,7 +232,7 @@ impl ScrollBase { /// # use cursive::theme; /// # use cursive::backend::{self, Backend}; /// # let scrollbase = ScrollBase::new(); - /// # let b = backend::Concrete::init(); + /// # let b: Box = backend::Concrete::init(); /// # let t = theme::load_default(); /// # let printer = Printer::new((5,1), &t, &b); /// # let printer = &printer; From 126530b9a9e10bbfc9cb425299bcc9219873d926 Mon Sep 17 00:00:00 2001 From: Alexandre Bury Date: Sun, 1 Apr 2018 16:39:03 -0700 Subject: [PATCH 05/15] Cursive::new now takes the backend as argument --- .travis.yml | 10 +--- doc/tutorial_1.md | 8 +-- doc/tutorial_2.md | 8 +-- doc/tutorial_3.md | 2 +- examples/colors.rs | 2 +- examples/dialog.rs | 2 +- examples/edit.rs | 2 +- examples/hello_world.rs | 2 +- examples/key_codes.rs | 2 +- examples/linear.rs | 2 +- examples/list_view.rs | 2 +- examples/logs.rs | 2 +- examples/lorem.rs | 2 +- examples/markup.rs | 2 +- examples/menubar.rs | 2 +- examples/mines/main.rs | 2 +- examples/mutation.rs | 2 +- examples/position.rs | 2 +- examples/progress.rs | 2 +- examples/radio.rs | 2 +- examples/refcell_view.rs | 2 +- examples/select.rs | 2 +- examples/slider.rs | 2 +- examples/terminal_default.rs | 2 +- examples/text_area.rs | 2 +- examples/theme.rs | 2 +- examples/theme_manual.rs | 2 +- src/backend/blt.rs | 50 ++++++++--------- src/backend/curses/mod.rs | 8 +-- src/backend/curses/n.rs | 104 +++++++++++++++++------------------ src/backend/curses/pan.rs | 74 ++++++++++++------------- src/backend/dummy.rs | 48 ++++++++++++++++ src/backend/mod.rs | 22 ++++---- src/backend/termion.rs | 70 +++++++++++------------ src/cursive.rs | 98 +++++++++++++++++++++++++-------- src/lib.rs | 4 +- src/printer.rs | 12 ++-- src/view/identifiable.rs | 4 +- src/view/scroll.rs | 6 +- src/view/view_wrapper.rs | 4 +- src/views/edit_view.rs | 4 +- src/views/select_view.rs | 4 +- src/views/text_view.rs | 4 +- 43 files changed, 340 insertions(+), 250 deletions(-) create mode 100644 src/backend/dummy.rs diff --git a/.travis.yml b/.travis.yml index 48dbc63..416789e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,11 +4,5 @@ rust: - stable - nightly script: - - cargo build --verbose - - cargo test --verbose - - - - 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 + - cargo build --verbose --features "pancurses-backend termion-backend" + - cargo test --verbose --features "pancurses-backend termion-backend" diff --git a/doc/tutorial_1.md b/doc/tutorial_1.md index 586dfd9..762f9bd 100644 --- a/doc/tutorial_1.md +++ b/doc/tutorial_1.md @@ -12,7 +12,7 @@ use cursive::Cursive; use cursive::views::TextView; fn main() { - let mut siv = Cursive::new(); + let mut siv = Cursive::default(); siv.add_global_callback('q', |s| s.quit()); @@ -80,7 +80,7 @@ extern crate cursive; use cursive::Cursive; fn main() { - let mut siv = Cursive::new(); + let mut siv = Cursive::default(); siv.run(); } @@ -117,7 +117,7 @@ extern crate cursive; use cursive::Cursive; fn main() { - let mut siv = Cursive::new(); + let mut siv = Cursive::default(); siv.add_global_callback('q', |s| s.quit()); @@ -159,7 +159,7 @@ use cursive::Cursive; use cursive::views::TextView; fn main() { - let mut siv = Cursive::new(); + let mut siv = Cursive::default(); siv.add_global_callback('q', |s| s.quit()); diff --git a/doc/tutorial_2.md b/doc/tutorial_2.md index ba3634c..cd4b731 100644 --- a/doc/tutorial_2.md +++ b/doc/tutorial_2.md @@ -12,7 +12,7 @@ use cursive::Cursive; use cursive::views::Dialog; fn main() { - let mut siv = Cursive::new(); + let mut siv = Cursive::default(); siv.add_layer(Dialog::text("This is a survey!\nPress when you're ready.") .title("Important survey") @@ -51,7 +51,7 @@ extern crate cursive; use cursive::Cursive; fn main() { - let mut siv = Cursive::new(); + let mut siv = Cursive::default(); siv.run(); } @@ -74,7 +74,7 @@ use cursive::views::Dialog; use cursive::views::TextView; fn main() { - let mut siv = Cursive::new(); + let mut siv = Cursive::default(); siv.add_layer(Dialog::around(TextView::new("..."))); @@ -135,7 +135,7 @@ use cursive::Cursive; use cursive::views::Dialog; fn main() { - let mut siv = Cursive::new(); + let mut siv = Cursive::default(); siv.add_layer(Dialog::text("This is a survey!\nPress when you're ready.") .title("Important survey") diff --git a/doc/tutorial_3.md b/doc/tutorial_3.md index b310f30..ab51a25 100644 --- a/doc/tutorial_3.md +++ b/doc/tutorial_3.md @@ -17,7 +17,7 @@ use cursive::views::{Button, Dialog, DummyView, EditView, use cursive::traits::*; fn main() { - let mut siv = Cursive::new(); + let mut siv = Cursive::default(); let select = SelectView::::new() .on_submit(on_submit) diff --git a/examples/colors.rs b/examples/colors.rs index 7188c36..1f7213b 100644 --- a/examples/colors.rs +++ b/examples/colors.rs @@ -17,7 +17,7 @@ use cursive::views::Canvas; // 256 colors. fn main() { - let mut siv = Cursive::new(); + let mut siv = Cursive::default(); siv.add_layer(Canvas::new(()).with_draw(draw).fixed_size((20, 10))); diff --git a/examples/dialog.rs b/examples/dialog.rs index 6668ac0..126ad32 100644 --- a/examples/dialog.rs +++ b/examples/dialog.rs @@ -5,7 +5,7 @@ use cursive::views::{Dialog, TextView}; fn main() { // 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 siv.add_layer( diff --git a/examples/edit.rs b/examples/edit.rs index f2a3470..b6d695f 100644 --- a/examples/edit.rs +++ b/examples/edit.rs @@ -5,7 +5,7 @@ use cursive::traits::*; use cursive::views::{Dialog, EditView, TextView}; fn main() { - let mut siv = Cursive::new(); + let mut siv = Cursive::default(); // Create a dialog with an edit text and a button. // The user can either hit the button, diff --git a/examples/hello_world.rs b/examples/hello_world.rs index d1c4cee..fb5fae7 100644 --- a/examples/hello_world.rs +++ b/examples/hello_world.rs @@ -4,7 +4,7 @@ use cursive::Cursive; use cursive::views::TextView; fn main() { - let mut siv = Cursive::new(); + let mut siv = Cursive::default(); // We can quit by pressing `q` siv.add_global_callback('q', Cursive::quit); diff --git a/examples/key_codes.rs b/examples/key_codes.rs index ff64bc3..65b1a6b 100644 --- a/examples/key_codes.rs +++ b/examples/key_codes.rs @@ -8,7 +8,7 @@ use cursive::traits::*; // This is a handy way to check the input received by cursive. fn main() { - let mut siv = Cursive::new(); + let mut siv = Cursive::default(); siv.add_layer(KeyCodeView::new(10).full_width().fixed_height(10)); siv.run(); diff --git a/examples/linear.rs b/examples/linear.rs index bce55d6..1d38f9d 100644 --- a/examples/linear.rs +++ b/examples/linear.rs @@ -8,7 +8,7 @@ use cursive::views::{Dialog, DummyView, LinearLayout, TextView}; // This example uses a LinearLayout to stick multiple views next to each other. fn main() { - let mut siv = Cursive::new(); + let mut siv = Cursive::default(); // Some description text let text = "This is a very simple example of linear layout. Two views \ diff --git a/examples/list_view.rs b/examples/list_view.rs index af69e68..a87ed11 100644 --- a/examples/list_view.rs +++ b/examples/list_view.rs @@ -10,7 +10,7 @@ use cursive::views::{Checkbox, Dialog, EditView, LinearLayout, ListView, // ListView can be used to build forms, with a list of inputs. fn main() { - let mut siv = Cursive::new(); + let mut siv = Cursive::default(); siv.add_layer( Dialog::new() diff --git a/examples/logs.rs b/examples/logs.rs index 871f348..a37add2 100644 --- a/examples/logs.rs +++ b/examples/logs.rs @@ -14,7 +14,7 @@ use std::time::Duration; fn main() { // 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. siv.set_fps(10); diff --git a/examples/lorem.rs b/examples/lorem.rs index 014949a..a76a9e3 100644 --- a/examples/lorem.rs +++ b/examples/lorem.rs @@ -9,7 +9,7 @@ fn main() { // Read some long text from a file. let content = include_str!("../assets/lorem.txt"); - let mut siv = Cursive::new(); + let mut siv = Cursive::default(); // We can quit by pressing q siv.add_global_callback('q', |s| s.quit()); diff --git a/examples/markup.rs b/examples/markup.rs index 8d4d8ab..bc260fe 100644 --- a/examples/markup.rs +++ b/examples/markup.rs @@ -9,7 +9,7 @@ use cursive::utils::markup::StyledString; use cursive::views::{Dialog, TextView}; fn main() { - let mut siv = Cursive::new(); + let mut siv = Cursive::default(); let mut styled = StyledString::plain("Isn't "); styled.append(StyledString::styled("that ", Color::Dark(BaseColor::Red))); diff --git a/examples/menubar.rs b/examples/menubar.rs index c1432c7..fb063ff 100644 --- a/examples/menubar.rs +++ b/examples/menubar.rs @@ -11,7 +11,7 @@ use std::sync::atomic::{AtomicUsize, Ordering}; // application. fn main() { - let mut siv = Cursive::new(); + let mut siv = Cursive::default(); // We'll use a counter to name new files. let counter = AtomicUsize::new(1); diff --git a/examples/mines/main.rs b/examples/mines/main.rs index 4d2ba70..0937183 100644 --- a/examples/mines/main.rs +++ b/examples/mines/main.rs @@ -12,7 +12,7 @@ use cursive::vec::Vec2; use cursive::views::{Button, Dialog, LinearLayout, Panel, SelectView}; fn main() { - let mut siv = Cursive::new(); + let mut siv = Cursive::default(); siv.add_layer( Dialog::new() diff --git a/examples/mutation.rs b/examples/mutation.rs index 346ca3f..db65c26 100644 --- a/examples/mutation.rs +++ b/examples/mutation.rs @@ -8,7 +8,7 @@ use cursive::views::{Dialog, OnEventView, TextView}; // This example modifies a view after creation. 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 \ popup."; diff --git a/examples/position.rs b/examples/position.rs index 094765e..cf4dff4 100644 --- a/examples/position.rs +++ b/examples/position.rs @@ -22,7 +22,7 @@ fn move_top(c: &mut Cursive, x_in: isize, y_in: isize) { } fn main() { - let mut siv = Cursive::new(); + let mut siv = Cursive::default(); siv.set_fps(60); // We can quit by pressing `q` diff --git a/examples/progress.rs b/examples/progress.rs index 5fcb3aa..0897036 100644 --- a/examples/progress.rs +++ b/examples/progress.rs @@ -17,7 +17,7 @@ use std::time::Duration; // "ticked" to indicate progress. fn main() { - let mut siv = Cursive::new(); + let mut siv = Cursive::default(); // We'll start slowly with a simple start button... siv.add_layer( diff --git a/examples/radio.rs b/examples/radio.rs index f544af7..f7ccfbc 100644 --- a/examples/radio.rs +++ b/examples/radio.rs @@ -6,7 +6,7 @@ use cursive::views::{Dialog, DummyView, LinearLayout, RadioGroup}; // This example uses radio buttons. fn main() { - let mut siv = Cursive::new(); + let mut siv = Cursive::default(); // We need to pre-create the groups for our RadioButtons. let mut color_group: RadioGroup = RadioGroup::new(); diff --git a/examples/refcell_view.rs b/examples/refcell_view.rs index 6f4fc81..44f16e3 100644 --- a/examples/refcell_view.rs +++ b/examples/refcell_view.rs @@ -7,7 +7,7 @@ use cursive::views::{Dialog, EditView, LinearLayout, TextView}; // This example shows a way to access multiple views at the same time. fn main() { - let mut siv = Cursive::new(); + let mut siv = Cursive::default(); // Create a dialog with 2 edit fields, and a text view. // The text view indicates when the 2 fields content match. diff --git a/examples/select.rs b/examples/select.rs index 045771d..d930be6 100644 --- a/examples/select.rs +++ b/examples/select.rs @@ -33,7 +33,7 @@ fn main() { 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 // (it can scroll anyway). diff --git a/examples/slider.rs b/examples/slider.rs index 42fa634..b84a2e3 100644 --- a/examples/slider.rs +++ b/examples/slider.rs @@ -5,7 +5,7 @@ use cursive::traits::*; use cursive::views::{Dialog, SliderView}; fn main() { - let mut siv = Cursive::new(); + let mut siv = Cursive::default(); siv.add_global_callback('q', |s| s.quit()); diff --git a/examples/terminal_default.rs b/examples/terminal_default.rs index 3531d24..57f4c36 100644 --- a/examples/terminal_default.rs +++ b/examples/terminal_default.rs @@ -9,7 +9,7 @@ use cursive::views::TextView; // This way, it looks more natural. fn main() { - let mut siv = Cursive::new(); + let mut siv = Cursive::default(); let theme = custom_theme_from_cursive(&siv); siv.set_theme(theme); diff --git a/examples/text_area.rs b/examples/text_area.rs index c351121..e72f4a1 100644 --- a/examples/text_area.rs +++ b/examples/text_area.rs @@ -6,7 +6,7 @@ use cursive::traits::*; use cursive::views::{Dialog, EditView, OnEventView, TextArea}; fn main() { - let mut siv = Cursive::new(); + let mut siv = Cursive::default(); // The main dialog will just have a textarea. // Its size expand automatically with the content. diff --git a/examples/theme.rs b/examples/theme.rs index 3f2d754..02e694a 100644 --- a/examples/theme.rs +++ b/examples/theme.rs @@ -4,7 +4,7 @@ use cursive::Cursive; use cursive::views::{Dialog, TextView}; 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. siv.load_theme_file("assets/style.toml").unwrap(); diff --git a/examples/theme_manual.rs b/examples/theme_manual.rs index 1160833..4168b83 100644 --- a/examples/theme_manual.rs +++ b/examples/theme_manual.rs @@ -5,7 +5,7 @@ use cursive::theme::{BaseColor, BorderStyle, Color, ColorStyle}; use cursive::views::{Dialog, EditView, LinearLayout, TextView}; fn main() { - let mut siv = Cursive::new(); + let mut siv = Cursive::default(); let layout = LinearLayout::vertical() .child(TextView::new("This is a dynamic theme example!")) diff --git a/src/backend/blt.rs b/src/backend/blt.rs index 3c28b6c..2e40dc5 100644 --- a/src/backend/blt.rs +++ b/src/backend/blt.rs @@ -15,12 +15,34 @@ enum ColorRole { Background, } -pub struct Concrete { +pub struct Backend { mouse_position: Vec2, buttons_pressed: HashSet, } -impl Concrete { +impl Backend { + pub fn init() -> Box { + 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( &mut self, kc: KeyCode, shift: bool, ctrl: bool ) -> Event { @@ -144,29 +166,7 @@ impl Concrete { } } -impl backend::Backend for Concrete { - fn init() -> Box { - 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 = Concrete { - mouse_position: Vec2::zero(), - buttons_pressed: HashSet::new(), - }; - - Box::new(c) - } - +impl backend::Backend for Backend { fn finish(&mut self) { terminal::close(); } diff --git a/src/backend/curses/mod.rs b/src/backend/curses/mod.rs index 8a8034e..90dbe13 100644 --- a/src/backend/curses/mod.rs +++ b/src/backend/curses/mod.rs @@ -3,14 +3,10 @@ use std::collections::HashMap; use theme::{BaseColor, Color}; #[cfg(feature = "ncurses")] -mod n; -#[cfg(feature = "ncurses")] -pub use self::n::*; +pub mod n; #[cfg(feature = "pancurses")] -mod pan; -#[cfg(feature = "pancurses")] -pub use self::pan::*; +pub mod pan; fn split_i32(code: i32) -> Vec { (0..4).map(|i| ((code >> (8 * i)) & 0xFF) as u8).collect() diff --git a/src/backend/curses/n.rs b/src/backend/curses/n.rs index 7624598..38327a5 100644 --- a/src/backend/curses/n.rs +++ b/src/backend/curses/n.rs @@ -11,7 +11,7 @@ use theme::{Color, ColorPair, Effect}; use utf8; use vec::Vec2; -pub struct Concrete { +pub struct Backend { current_style: Cell, pairs: RefCell>, @@ -21,7 +21,56 @@ pub struct Concrete { event_queue: Vec, } -impl Concrete { +impl Backend { + pub fn init() -> Box { + // 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"); + + 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. fn insert_color( &self, pairs: &mut HashMap, pair: ColorPair @@ -149,56 +198,7 @@ impl Concrete { } } -impl backend::Backend for Concrete { - fn init() -> Box { - // 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"); - - let c = 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(), - }; - - Box::new(c) - } - +impl backend::Backend for Backend { fn screen_size(&self) -> (usize, usize) { let mut x: i32 = 0; let mut y: i32 = 0; diff --git a/src/backend/curses/pan.rs b/src/backend/curses/pan.rs index ba9741e..42002d0 100644 --- a/src/backend/curses/pan.rs +++ b/src/backend/curses/pan.rs @@ -10,7 +10,7 @@ use std::io::{stdout, Write}; use theme::{Color, ColorPair, Effect}; use vec::Vec2; -pub struct Concrete { +pub struct Backend { // Used current_style: Cell, pairs: RefCell>, @@ -24,7 +24,41 @@ pub struct Concrete { window: pancurses::Window, } -impl Concrete { +impl Backend { + pub fn init() -> Box { + ::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. fn insert_color( &self, pairs: &mut HashMap, pair: ColorPair @@ -132,41 +166,7 @@ impl Concrete { } } -impl backend::Backend for Concrete { - fn init() -> Box { - ::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 = 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(), - }; - - Box::new(c) - } - +impl backend::Backend for Backend { fn screen_size(&self) -> (usize, usize) { let (y, x) = self.window.get_max_yx(); (x as usize, y as usize) diff --git a/src/backend/dummy.rs b/src/backend/dummy.rs new file mode 100644 index 0000000..4ed8a80 --- /dev/null +++ b/src/backend/dummy.rs @@ -0,0 +1,48 @@ +//! Dummy backend +use backend; +use theme; +use event; + +pub struct Backend; + +impl Backend { + pub fn init() -> Box + 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) -> (usize, usize) { + (1, 1) + } + + fn poll_event(&mut self) -> event::Event { + event::Event::Exit + } + + fn print_at(&self, _: (usize, usize), _: &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) {} +} diff --git a/src/backend/mod.rs b/src/backend/mod.rs index 4e6cd0f..ad01489 100644 --- a/src/backend/mod.rs +++ b/src/backend/mod.rs @@ -1,22 +1,20 @@ use event; use theme; -#[cfg(feature = "termion")] -mod termion; -#[cfg(feature = "bear-lib-terminal")] -mod blt; -#[cfg(any(feature = "ncurses", feature = "pancurses"))] -mod curses; +pub mod dummy; -#[cfg(feature = "bear-lib-terminal")] -pub use self::blt::*; -#[cfg(any(feature = "ncurses", feature = "pancurses"))] -pub use self::curses::*; +/// Backend using the pure-rust termion library. #[cfg(feature = "termion")] -pub use self::termion::*; +pub mod termion; + +/// Backend using BearLibTerminal +#[cfg(feature = "bear-lib-terminal")] +pub mod blt; + +#[cfg(any(feature = "ncurses", feature = "pancurses"))] +pub mod curses; pub trait Backend { - fn init() -> Box where Self: Sized; // TODO: take `self` by value? // Or implement Drop? fn finish(&mut self); diff --git a/src/backend/termion.rs b/src/backend/termion.rs index 33b3a3b..5d7f8d4 100644 --- a/src/backend/termion.rs +++ b/src/backend/termion.rs @@ -20,7 +20,7 @@ use std::thread; use theme; use vec::Vec2; -pub struct Concrete { +pub struct Backend { terminal: AlternateScreen>>, current_style: Cell, input: chan::Receiver, @@ -56,7 +56,39 @@ impl Effectable for theme::Effect { } } -impl Concrete { +impl Backend { + pub fn init() -> Box { + 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) { with_color(&colors.front, |c| print!("{}", tcolor::Fg(c))); with_color(&colors.back, |c| print!("{}", tcolor::Bg(c))); @@ -136,39 +168,7 @@ impl Concrete { } } -impl backend::Backend for Concrete { - fn init() -> Box { - 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 = Concrete { - terminal: terminal, - current_style: Cell::new(theme::ColorPair::from_256colors(0, 0)), - input: receiver, - resize: resize, - timeout: None, - last_button: None, - }; - - Box::new(c) - } - +impl backend::Backend for Backend { fn finish(&mut self) { print!("{}{}", termion::cursor::Show, termion::cursor::Goto(1, 1)); print!( diff --git a/src/cursive.rs b/src/cursive.rs index 8626f00..127a7c3 100644 --- a/src/cursive.rs +++ b/src/cursive.rs @@ -1,5 +1,4 @@ use backend; -use backend::Backend; use direction; use event::{Callback, Event, EventResult}; use printer::Printer; @@ -33,6 +32,34 @@ impl () + 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. /// /// It initializes ncurses on creation and cleans up on drop. @@ -60,13 +87,9 @@ pub struct Cursive { cb_sink: mpsc::Sender>, } -new_default!(Cursive); - impl Cursive { /// Creates a new Cursive root, and initialize the back-end. - pub fn new() -> Self { - let backend = backend::Concrete::init(); - + pub fn new(backend: Box) -> Self { let theme = theme::load_default(); // theme.activate(&mut backend); // 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 the sender part of a channel, that allows to send @@ -100,11 +154,11 @@ impl Cursive { /// /// # Examples /// - /// ```rust,no_run + /// ```rust /// # extern crate cursive; /// # use cursive::*; /// # fn main() { - /// let mut siv = Cursive::new(); + /// let mut siv = Cursive::dummy(); /// siv.set_fps(10); /// /// // quit() will be called during the next event cycle @@ -136,7 +190,7 @@ impl Cursive { /// /// # Examples /// - /// ```rust,no_run + /// ```rust /// # extern crate cursive; /// # /// # use cursive::{Cursive, event}; @@ -145,7 +199,7 @@ impl Cursive { /// # use cursive::menu::*; /// # /// # fn main() { - /// let mut siv = Cursive::new(); + /// let mut siv = Cursive::dummy(); /// /// siv.menubar() /// .add_subtree("File", @@ -289,13 +343,13 @@ impl Cursive { /// /// # Examples /// - /// ```rust,no_run + /// ```rust /// # extern crate cursive; /// # use cursive::{Cursive, views, view}; /// # 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").with_id("text")); /// @@ -327,12 +381,12 @@ impl Cursive { /// /// # Examples /// - /// ```rust,no_run + /// ```rust /// # extern crate cursive; /// # use cursive::{Cursive, views}; /// # use cursive::traits::*; /// # fn main() { - /// let mut siv = Cursive::new(); + /// let mut siv = Cursive::dummy(); /// /// siv.add_layer(views::TextView::new("Text #1") /// .with_id("text")); @@ -360,10 +414,10 @@ impl Cursive { /// /// # Examples /// - /// ```rust,no_run + /// ```rust /// # use cursive::Cursive; /// # use cursive::views::{TextView, ViewRef}; - /// # let mut siv = Cursive::new(); + /// # let mut siv = Cursive::dummy(); /// use cursive::traits::Identifiable; /// /// siv.add_layer(TextView::new("foo").with_id("id")); @@ -400,11 +454,11 @@ impl Cursive { /// /// # Examples /// - /// ```rust,no_run + /// ```rust /// # extern crate cursive; /// # use cursive::*; /// # fn main() { - /// let mut siv = Cursive::new(); + /// let mut siv = Cursive::dummy(); /// /// siv.add_global_callback('q', |s| s.quit()); /// # } @@ -423,11 +477,11 @@ impl Cursive { /// /// # Examples /// - /// ```rust,no_run + /// ```rust /// # extern crate cursive; /// # use cursive::*; /// # fn main() { - /// let mut siv = Cursive::new(); + /// let mut siv = Cursive::dummy(); /// /// siv.add_global_callback('q', |s| s.quit()); /// siv.clear_global_callbacks('q'); @@ -445,11 +499,11 @@ impl Cursive { /// /// # Examples /// - /// ```rust,no_run + /// ```rust /// # extern crate cursive; /// # use cursive::*; /// # fn main() { - /// let mut siv = Cursive::new(); + /// let mut siv = Cursive::dummy(); /// /// siv.add_layer(views::TextView::new("Hello world!")); /// # } diff --git a/src/lib.rs b/src/lib.rs index 76c9816..487c0a1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -32,14 +32,14 @@ //! //! ## Examples //! -//! ```no_run +//! ```rust //! extern crate cursive; //! //! use cursive::Cursive; //! use cursive::views::TextView; //! //! fn main() { -//! let mut siv = Cursive::new(); +//! let mut siv = Cursive::dummy(); //! //! siv.add_layer(TextView::new("Hello World!\nPress q to quit.")); //! diff --git a/src/printer.rs b/src/printer.rs index 1b49ff4..6351b33 100644 --- a/src/printer.rs +++ b/src/printer.rs @@ -117,11 +117,11 @@ impl<'a> Printer<'a> { /// /// # Examples /// - /// ```no_run + /// ```rust /// # use cursive::Printer; /// # use cursive::theme; - /// # use cursive::backend::{self, Backend}; - /// # let b: Box = backend::Concrete::init(); + /// # use cursive::backend; + /// # let b = backend::dummy::Backend::init(); /// # let t = theme::load_default(); /// # let printer = Printer::new((6,4), &t, &b); /// printer.with_color(theme::ColorStyle::highlight(), |printer| { @@ -195,11 +195,11 @@ impl<'a> Printer<'a> { /// /// # Examples /// - /// ```no_run + /// ```rust /// # use cursive::Printer; /// # use cursive::theme; - /// # use cursive::backend::{self, Backend}; - /// # let b: Box = backend::Concrete::init(); + /// # use cursive::backend; + /// # let b = backend::dummy::Backend::init(); /// # let t = theme::load_default(); /// # let printer = Printer::new((6,4), &t, &b); /// printer.print_box((0,0), (6,4), false); diff --git a/src/view/identifiable.rs b/src/view/identifiable.rs index 15a116c..27b55d1 100644 --- a/src/view/identifiable.rs +++ b/src/view/identifiable.rs @@ -13,13 +13,13 @@ pub trait Identifiable: View + Sized { /// /// # Examples /// - /// ```rust,no_run + /// ```rust /// # use cursive::Cursive; /// # use cursive::views::TextView; /// # use cursive::view::Boxable; /// use cursive::view::Identifiable; /// - /// let mut siv = Cursive::new(); + /// let mut siv = Cursive::dummy(); /// siv.add_layer( /// TextView::new("foo") /// .with_id("text") diff --git a/src/view/scroll.rs b/src/view/scroll.rs index 0b6dd05..2399bbf 100644 --- a/src/view/scroll.rs +++ b/src/view/scroll.rs @@ -226,13 +226,13 @@ impl ScrollBase { /// /// # Examples /// - /// ```no_run + /// ```rust /// # use cursive::view::ScrollBase; /// # use cursive::Printer; /// # use cursive::theme; - /// # use cursive::backend::{self, Backend}; + /// # use cursive::backend; /// # let scrollbase = ScrollBase::new(); - /// # let b: Box = backend::Concrete::init(); + /// # let b = backend::dummy::Backend::init(); /// # let t = theme::load_default(); /// # let printer = Printer::new((5,1), &t, &b); /// # let printer = &printer; diff --git a/src/view/view_wrapper.rs b/src/view/view_wrapper.rs index 2ce8925..980fc12 100644 --- a/src/view/view_wrapper.rs +++ b/src/view/view_wrapper.rs @@ -141,7 +141,7 @@ impl View for T { /// /// # Examples /// -/// ```no_run +/// ```rust /// # #[macro_use] extern crate cursive; /// # use cursive::view::{View,ViewWrapper}; /// struct FooView { @@ -186,7 +186,7 @@ macro_rules! wrap_impl { /// /// # Examples /// -/// ```no_run +/// ```rust /// # #[macro_use] extern crate cursive; /// # use cursive::view::{View,ViewWrapper}; /// struct FooView { diff --git a/src/views/edit_view.rs b/src/views/edit_view.rs index 181af1e..6641ab4 100644 --- a/src/views/edit_view.rs +++ b/src/views/edit_view.rs @@ -29,13 +29,13 @@ pub type OnSubmit = Fn(&mut Cursive, &str); /// /// [1]: https://github.com/gyscos/Cursive/blob/master/examples/edit.rs /// -/// ```no_run +/// ```rust /// # extern crate cursive; /// # use cursive::Cursive; /// # use cursive::traits::*; /// # use cursive::views::{Dialog, EditView, TextView}; /// # fn main() { -/// let mut siv = Cursive::new(); +/// let mut siv = Cursive::dummy(); /// /// // Create a dialog with an edit text and a button. /// // The user can either hit the button, diff --git a/src/views/select_view.rs b/src/views/select_view.rs index f42f9fc..9cf704d 100644 --- a/src/views/select_view.rs +++ b/src/views/select_view.rs @@ -21,7 +21,7 @@ use views::MenuPopup; /// /// # Examples /// -/// ```no_run +/// ```rust /// # extern crate cursive; /// # use cursive::Cursive; /// # use cursive::views::{SelectView, Dialog, TextView}; @@ -39,7 +39,7 @@ use views::MenuPopup; /// .button("Quit", |s| s.quit())); /// }); /// -/// let mut siv = Cursive::new(); +/// let mut siv = Cursive::dummy(); /// siv.add_layer(Dialog::around(time_select) /// .title("How long is your wait?")); /// # } diff --git a/src/views/text_view.rs b/src/views/text_view.rs index e0f3370..e661791 100644 --- a/src/views/text_view.rs +++ b/src/views/text_view.rs @@ -155,10 +155,10 @@ impl TextContentInner { /// /// # Examples /// -/// ```rust,no_run +/// ```rust /// # use cursive::Cursive; /// # use cursive::views::TextView; -/// let mut siv = Cursive::new(); +/// let mut siv = Cursive::dummy(); /// /// siv.add_layer(TextView::new("Hello world!")); /// ``` From 1c72ef7ade1d13f166d90c4d924d40254a908cd1 Mon Sep 17 00:00:00 2001 From: Alexandre Bury Date: Sun, 1 Apr 2018 22:58:25 -0700 Subject: [PATCH 06/15] Update changelog --- CHANGELOG.md | 25 ++++++++++++++++++++++++- Cargo.toml | 2 +- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b080a71..e59f3e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,29 @@ # 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 diff --git a/Cargo.toml b/Cargo.toml index 87dca91..a947a51 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ license = "MIT" name = "cursive" readme = "Readme.md" repository = "https://github.com/gyscos/Cursive" -version = "0.8.2-alpha.0" +version = "0.9.0-alpha.0" [badges.travis-ci] repository = "gyscos/Cursive" From fd09b5f806b103154d693ca829ac4b35d09ce66e Mon Sep 17 00:00:00 2001 From: Alexandre Bury Date: Mon, 2 Apr 2018 18:08:12 -0700 Subject: [PATCH 07/15] Use Vec2 in backend methods --- .travis.yml | 1 + src/backend/blt.rs | 13 +++++++---- src/backend/curses/mod.rs | 5 ++++ src/backend/curses/n.rs | 12 ++++++---- src/backend/curses/pan.rs | 13 +++++++---- src/backend/dummy.rs | 7 +++--- src/backend/mod.rs | 44 +++++++++++++++++++++++++++--------- src/backend/termion.rs | 13 +++++++---- src/cursive.rs | 7 +----- src/printer.rs | 6 ++--- src/utils/markup/markdown.rs | 2 +- 11 files changed, 81 insertions(+), 42 deletions(-) diff --git a/.travis.yml b/.travis.yml index 416789e..6fc53dc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,5 +4,6 @@ rust: - stable - nightly script: + - cargo check --all-features - cargo build --verbose --features "pancurses-backend termion-backend" - cargo test --verbose --features "pancurses-backend termion-backend" diff --git a/src/backend/blt.rs b/src/backend/blt.rs index 2e40dc5..fcc9997 100644 --- a/src/backend/blt.rs +++ b/src/backend/blt.rs @@ -1,3 +1,8 @@ +//! Backend using BearLibTerminal +//! +//! Requires the `blt-backend` feature. +#![cfg(feature = "bear-lib-terminal")] + extern crate bear_lib_terminal; use self::bear_lib_terminal::Color as BltColor; @@ -220,9 +225,9 @@ impl backend::Backend for Backend { true } - fn screen_size(&self) -> (usize, usize) { + fn screen_size(&self) -> Vec2 { let Size { width, height } = terminal::state::size(); - (width as usize, height as usize) + (width, height).into() } fn clear(&self, color: Color) { @@ -237,8 +242,8 @@ impl backend::Backend for Backend { terminal::refresh(); } - fn print_at(&self, (x, y): (usize, usize), text: &str) { - terminal::print_xy(x as i32, y as i32, text); + fn print_at(&self, pos: Vec2, text: &str) { + terminal::print_xy(pos.x as i32, pos.y as i32, text); } fn set_refresh_rate(&mut self, _: u32) { diff --git a/src/backend/curses/mod.rs b/src/backend/curses/mod.rs index 90dbe13..cd01bd1 100644 --- a/src/backend/curses/mod.rs +++ b/src/backend/curses/mod.rs @@ -1,3 +1,8 @@ +//! 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 std::collections::HashMap; use theme::{BaseColor, Color}; diff --git a/src/backend/curses/n.rs b/src/backend/curses/n.rs index 38327a5..1ea1c66 100644 --- a/src/backend/curses/n.rs +++ b/src/backend/curses/n.rs @@ -73,7 +73,9 @@ impl Backend { /// Save a new color pair. fn insert_color( - &self, pairs: &mut HashMap, pair: ColorPair + &self, + pairs: &mut HashMap, + pair: ColorPair, ) -> i16 { let n = 1 + pairs.len() as i16; let target = if ncurses::COLOR_PAIRS() > i32::from(n) { @@ -199,11 +201,11 @@ impl Backend { } impl backend::Backend for Backend { - fn screen_size(&self) -> (usize, usize) { + fn screen_size(&self) -> Vec2 { let mut x: i32 = 0; let mut y: i32 = 0; ncurses::getmaxyx(ncurses::stdscr(), &mut y, &mut x); - (x as usize, y as usize) + (x, y).into() } fn has_colors(&self) -> bool { @@ -261,8 +263,8 @@ impl backend::Backend for Backend { ncurses::refresh(); } - fn print_at(&self, (x, y): (usize, usize), text: &str) { - ncurses::mvaddstr(y as i32, x as i32, text); + fn print_at(&self, pos: Vec2, text: &str) { + ncurses::mvaddstr(pos.y as i32, pos.x as i32, text); } fn poll_event(&mut self) -> Event { diff --git a/src/backend/curses/pan.rs b/src/backend/curses/pan.rs index 42002d0..45bac6e 100644 --- a/src/backend/curses/pan.rs +++ b/src/backend/curses/pan.rs @@ -61,7 +61,9 @@ impl Backend { /// Save a new color pair. fn insert_color( - &self, pairs: &mut HashMap, pair: ColorPair + &self, + pairs: &mut HashMap, + pair: ColorPair, ) -> i32 { let n = 1 + pairs.len() as i32; @@ -167,9 +169,10 @@ impl Backend { } impl backend::Backend for Backend { - fn screen_size(&self) -> (usize, usize) { + fn screen_size(&self) -> Vec2 { + // Coordinates are reversed here let (y, x) = self.window.get_max_yx(); - (x as usize, y as usize) + (x, y).into() } fn has_colors(&self) -> bool { @@ -227,8 +230,8 @@ impl backend::Backend for Backend { self.window.refresh(); } - fn print_at(&self, (x, y): (usize, usize), text: &str) { - self.window.mvaddstr(y as i32, x as i32, text); + fn print_at(&self, pos: Vec2, text: &str) { + self.window.mvaddstr(pos.y as i32, pos.x as i32, text); } fn poll_event(&mut self) -> Event { diff --git a/src/backend/dummy.rs b/src/backend/dummy.rs index 4ed8a80..0d7e907 100644 --- a/src/backend/dummy.rs +++ b/src/backend/dummy.rs @@ -2,6 +2,7 @@ use backend; use theme; use event; +use vec::Vec2; pub struct Backend; @@ -23,15 +24,15 @@ impl backend::Backend for Backend { false } - fn screen_size(&self) -> (usize, usize) { - (1, 1) + fn screen_size(&self) -> Vec2 { + (1, 1).into() } fn poll_event(&mut self) -> event::Event { event::Event::Exit } - fn print_at(&self, _: (usize, usize), _: &str) {} + fn print_at(&self, _: Vec2, _: &str) {} fn clear(&self, _: theme::Color) {} diff --git a/src/backend/mod.rs b/src/backend/mod.rs index ad01489..e2a82bd 100644 --- a/src/backend/mod.rs +++ b/src/backend/mod.rs @@ -1,42 +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 theme; +use vec::Vec2; + pub mod dummy; -/// Backend using the pure-rust termion library. -#[cfg(feature = "termion")] pub mod termion; - -/// Backend using BearLibTerminal -#[cfg(feature = "bear-lib-terminal")] pub mod blt; - -#[cfg(any(feature = "ncurses", feature = "pancurses"))] pub mod curses; +/// Trait defining the required methods to be a backend. pub trait Backend { // TODO: take `self` by value? // Or implement Drop? + /// Prepares to close the backend. + /// + /// This should clear any state in the terminal. fn finish(&mut self); + /// Refresh the screen. fn refresh(&mut self); + /// Should return `true` if this backend supports colors. fn has_colors(&self) -> bool; - fn screen_size(&self) -> (usize, usize); + + /// Returns the screen size. + fn screen_size(&self) -> Vec2; /// Main input method fn poll_event(&mut self) -> event::Event; /// 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); + /// 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); - // This sets the Colours and returns the previous colours - // to allow you to set them back when you're done. + /// Starts using a new color. + /// + /// 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); } diff --git a/src/backend/termion.rs b/src/backend/termion.rs index 5d7f8d4..1932898 100644 --- a/src/backend/termion.rs +++ b/src/backend/termion.rs @@ -1,3 +1,8 @@ +//! Backend using the pure-rust termion library. +//! +//! Requires the `termion-backend` feature. +#![cfg(feature = "termion")] + extern crate termion; extern crate chan_signal; @@ -203,9 +208,9 @@ impl backend::Backend for Backend { true } - fn screen_size(&self) -> (usize, usize) { + fn screen_size(&self) -> Vec2 { 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) { @@ -220,10 +225,10 @@ impl backend::Backend for Backend { self.terminal.flush().unwrap(); } - fn print_at(&self, (x, y): (usize, usize), text: &str) { + fn print_at(&self, pos: Vec2, text: &str) { 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 ); } diff --git a/src/cursive.rs b/src/cursive.rs index 127a7c3..3b2f314 100644 --- a/src/cursive.rs +++ b/src/cursive.rs @@ -551,12 +551,7 @@ impl Cursive { /// Returns the size of the screen, in characters. pub fn screen_size(&self) -> Vec2 { - let (x, y) = self.backend.screen_size(); - - Vec2 { - x: x as usize, - y: y as usize, - } + self.backend.screen_size() } fn layout(&mut self) { diff --git a/src/printer.rs b/src/printer.rs index 6351b33..ab4547d 100644 --- a/src/printer.rs +++ b/src/printer.rs @@ -78,7 +78,7 @@ impl<'a> Printer<'a> { let text = &text[..prefix_len]; 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. @@ -93,7 +93,7 @@ impl<'a> Printer<'a> { let p = p + self.offset; for y in 0..len { - self.backend.print_at((p.x, (p.y + y)), c); + self.backend.print_at(p + (0,y), c); } } @@ -109,7 +109,7 @@ impl<'a> Printer<'a> { let text: String = ::std::iter::repeat(c).take(len).collect(); 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, diff --git a/src/utils/markup/markdown.rs b/src/utils/markup/markdown.rs index 7637323..781b5fe 100644 --- a/src/utils/markup/markdown.rs +++ b/src/utils/markup/markdown.rs @@ -18,7 +18,7 @@ where let spans = parse_spans(&input); - StyledString::new(input, spans) + StyledString::with_spans(input, spans) } /// Iterator that parse a markdown text and outputs styled spans. From b34fb14957f877173c21793425a79890574a8639 Mon Sep 17 00:00:00 2001 From: Alexandre Bury Date: Thu, 5 Apr 2018 17:36:58 -0700 Subject: [PATCH 08/15] Use ncurses::newterm instead of initscr This leaves stdout untouched, so it can better be composed in other commands. --- Cargo.toml | 1 + src/backend/curses/n.rs | 11 +++++++---- src/lib.rs | 1 + 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a947a51..69bc1be 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,6 +23,7 @@ toml = "0.4" unicode-segmentation = "1.0" unicode-width = "0.1" xi-unicode = "0.1.0" +libc = "0.2" [dependencies.maplit] optional = true diff --git a/src/backend/curses/n.rs b/src/backend/curses/n.rs index 1ea1c66..3c5225c 100644 --- a/src/backend/curses/n.rs +++ b/src/backend/curses/n.rs @@ -4,8 +4,10 @@ use self::ncurses::mmask_t; use self::super::{find_closest, split_i32}; use backend; use event::{Event, Key, MouseButton, MouseEvent}; +use libc; use std::cell::{Cell, RefCell}; use std::collections::HashMap; +use std::ffi::CString; use std::io::{stdout, Write}; use theme::{Color, ColorPair, Effect}; use utf8; @@ -32,7 +34,10 @@ impl Backend { // Default delay is way too long. 25 is imperceptible yet works fine. ::std::env::set_var("ESCDELAY", "25"); - ncurses::initscr(); + 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, @@ -73,9 +78,7 @@ impl Backend { /// Save a new color pair. fn insert_color( - &self, - pairs: &mut HashMap, - pair: ColorPair, + &self, pairs: &mut HashMap, pair: ColorPair ) -> i16 { let n = 1 + pairs.len() as i16; let target = if ncurses::COLOR_PAIRS() > i32::from(n) { diff --git a/src/lib.rs b/src/lib.rs index 487c0a1..968b752 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -73,6 +73,7 @@ extern crate log; extern crate maplit; extern crate num; +extern crate libc; extern crate owning_ref; extern crate toml; extern crate unicode_segmentation; From 2729e77838b2312054251d95fe162e1ee7d092d1 Mon Sep 17 00:00:00 2001 From: Alexandre Bury Date: Sun, 8 Apr 2018 22:51:51 -0700 Subject: [PATCH 09/15] Ncurses backend: write mouse command to /dev/tty --- src/backend/curses/n.rs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/backend/curses/n.rs b/src/backend/curses/n.rs index 3c5225c..c0d8833 100644 --- a/src/backend/curses/n.rs +++ b/src/backend/curses/n.rs @@ -8,6 +8,8 @@ use libc; use std::cell::{Cell, RefCell}; use std::collections::HashMap; use std::ffi::CString; +use std::fs::File; +use std::io; use std::io::{stdout, Write}; use theme::{Color, ColorPair, Effect}; use utf8; @@ -23,6 +25,13 @@ pub struct Backend { event_queue: Vec, } +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)?; + Ok(()) +} + impl Backend { pub fn init() -> Box { // Change the locale. @@ -60,7 +69,7 @@ impl Backend { // 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"); + write_to_tty(b"\x1B[?1002h").unwrap(); stdout().flush().expect("could not flush stdout"); let c = Backend { @@ -216,7 +225,7 @@ impl backend::Backend for Backend { } fn finish(&mut self) { - print!("\x1B[?1002l"); + write_to_tty(b"\x1B[?1002l").unwrap(); stdout().flush().expect("could not flush stdout"); ncurses::endwin(); } From d5178e778b56329917498498f647e9c96d10d2bf Mon Sep 17 00:00:00 2001 From: Alexandre Bury Date: Mon, 9 Apr 2018 10:06:11 -0700 Subject: [PATCH 10/15] Do not flush stdout in ncurses backend --- src/backend/curses/n.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/backend/curses/n.rs b/src/backend/curses/n.rs index c0d8833..dac09bb 100644 --- a/src/backend/curses/n.rs +++ b/src/backend/curses/n.rs @@ -10,7 +10,7 @@ use std::collections::HashMap; use std::ffi::CString; use std::fs::File; use std::io; -use std::io::{stdout, Write}; +use std::io::{Write}; use theme::{Color, ColorPair, Effect}; use utf8; use vec::Vec2; @@ -25,10 +25,15 @@ pub struct Backend { event_queue: Vec, } +/// 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(()) } @@ -70,7 +75,6 @@ impl Backend { // (Mouse move when a button is pressed). // Replacing 1002 with 1003 would give us ANY mouse move. write_to_tty(b"\x1B[?1002h").unwrap(); - stdout().flush().expect("could not flush stdout"); let c = Backend { current_style: Cell::new(ColorPair::from_256colors(0, 0)), @@ -226,7 +230,6 @@ impl backend::Backend for Backend { fn finish(&mut self) { write_to_tty(b"\x1B[?1002l").unwrap(); - stdout().flush().expect("could not flush stdout"); ncurses::endwin(); } From 063589b0cdd9efbd52942fd6158d778e28771884 Mon Sep 17 00:00:00 2001 From: Alexandre Bury Date: Tue, 10 Apr 2018 11:45:02 -0700 Subject: [PATCH 11/15] Move Counter to utils module --- src/utils/counter.rs | 30 ++++++++++++++++++++++++ src/utils/lines/simple/lines_iterator.rs | 6 ++++- src/utils/lines/spans/chunk_iterator.rs | 15 ++++++++---- src/utils/lines/spans/lines_iterator.rs | 12 +++++++--- src/utils/lines/spans/mod.rs | 10 ++++---- src/utils/lines/spans/tests.rs | 4 +++- src/utils/markup/markdown.rs | 6 +++-- src/utils/mod.rs | 6 +++-- src/utils/reader.rs | 2 +- src/views/mod.rs | 2 +- src/views/progress_bar.rs | 29 +---------------------- 11 files changed, 74 insertions(+), 48 deletions(-) create mode 100644 src/utils/counter.rs diff --git a/src/utils/counter.rs b/src/utils/counter.rs new file mode 100644 index 0000000..5a13875 --- /dev/null +++ b/src/utils/counter.rs @@ -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); + +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); + } +} diff --git a/src/utils/lines/simple/lines_iterator.rs b/src/utils/lines/simple/lines_iterator.rs index 8305739..c14696d 100644 --- a/src/utils/lines/simple/lines_iterator.rs +++ b/src/utils/lines/simple/lines_iterator.rs @@ -70,6 +70,10 @@ impl<'a> Iterator for LinesIterator<'a> { let width = row.width; - Some(Row { start, end, width }) + Some(Row { + start, + end, + width, + }) } } diff --git a/src/utils/lines/spans/chunk_iterator.rs b/src/utils/lines/spans/chunk_iterator.rs index a92fa73..9034279 100644 --- a/src/utils/lines/spans/chunk_iterator.rs +++ b/src/utils/lines/spans/chunk_iterator.rs @@ -47,7 +47,10 @@ where } // Skip empty spans - if self.source.spans()[self.current_span].as_ref().is_empty() { + if self.source.spans()[self.current_span] + .as_ref() + .is_empty() + { self.current_span += 1; return self.next(); } @@ -120,9 +123,13 @@ where self.current_span += 1; // Skip empty spans - while let Some(true) = - self.source.spans().get(self.current_span).map(|span| { - span.as_ref().resolve(self.source.source()).is_empty() + while let Some(true) = self.source + .spans() + .get(self.current_span) + .map(|span| { + span.as_ref() + .resolve(self.source.source()) + .is_empty() }) { self.current_span += 1; } diff --git a/src/utils/lines/spans/lines_iterator.rs b/src/utils/lines/spans/lines_iterator.rs index 5acd9af..e14e831 100644 --- a/src/utils/lines/spans/lines_iterator.rs +++ b/src/utils/lines/spans/lines_iterator.rs @@ -76,8 +76,11 @@ where self.width }; - let mut chunks = - prefix(&mut self.iter, allowed_width, &mut self.chunk_offset); + let mut chunks = prefix( + &mut self.iter, + allowed_width, + &mut self.chunk_offset, + ); // println!("Chunks..: {:?}", chunks); @@ -162,6 +165,9 @@ where // TODO: merge consecutive segments of the same span - Some(Row { segments, width }) + Some(Row { + segments, + width, + }) } } diff --git a/src/utils/lines/spans/mod.rs b/src/utils/lines/spans/mod.rs index 2fbda35..ab4c275 100644 --- a/src/utils/lines/spans/mod.rs +++ b/src/utils/lines/spans/mod.rs @@ -4,13 +4,13 @@ //! //! Computed rows will include a list of span segments. //! 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_iterator; +mod lines_iterator; +mod prefix; +mod row; mod segment; +mod segment_merge_iterator; #[cfg(test)] mod tests; diff --git a/src/utils/lines/spans/tests.rs b/src/utils/lines/spans/tests.rs index f4d645c..32fe742 100644 --- a/src/utils/lines/spans/tests.rs +++ b/src/utils/lines/spans/tests.rs @@ -8,7 +8,9 @@ fn input() -> StyledString { text.append(StyledString::styled("didn't", Effect::Bold)); text.append(StyledString::plain(" say ")); text.append(StyledString::styled("half", Effect::Italic)); - text.append(StyledString::plain(" the things people say I did.")); + text.append(StyledString::plain( + " the things people say I did.", + )); text.append(StyledString::plain("\n")); text.append(StyledString::plain("\n")); text.append(StyledString::plain(" - A. Einstein")); diff --git a/src/utils/markup/markdown.rs b/src/utils/markup/markdown.rs index 781b5fe..0bf983e 100644 --- a/src/utils/markup/markdown.rs +++ b/src/utils/markup/markdown.rs @@ -139,8 +139,10 @@ Attention ==== I *really* love __Cursive__!"; let spans = parse_spans(input); - let spans: Vec<_> = - spans.iter().map(|span| span.resolve(input)).collect(); + let spans: Vec<_> = spans + .iter() + .map(|span| span.resolve(input)) + .collect(); // println!("{:?}", spans); assert_eq!( diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 06d9b9b..88ee597 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -1,8 +1,10 @@ //! Toolbox to make text layout easier. -mod reader; -pub mod span; +mod counter; pub mod lines; pub mod markup; +mod reader; +pub mod span; +pub use self::counter::Counter; pub use self::reader::ProgressReader; diff --git a/src/utils/reader.rs b/src/utils/reader.rs index 056ccea..03ccad0 100644 --- a/src/utils/reader.rs +++ b/src/utils/reader.rs @@ -1,5 +1,5 @@ use std::io::{self, Read}; -use views::Counter; +use utils::Counter; /// Wrapper around a `Read` that reports the progress made. /// diff --git a/src/views/mod.rs b/src/views/mod.rs index b26217a..3661037 100644 --- a/src/views/mod.rs +++ b/src/views/mod.rs @@ -77,7 +77,7 @@ pub use self::menu_popup::MenuPopup; pub use self::menubar::Menubar; pub use self::on_event_view::OnEventView; 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::select_view::SelectView; pub use self::shadow_view::ShadowView; diff --git a/src/views/progress_bar.rs b/src/views/progress_bar.rs index 44538e4..3377024 100644 --- a/src/views/progress_bar.rs +++ b/src/views/progress_bar.rs @@ -1,40 +1,13 @@ use Printer; use align::HAlign; use std::cmp; -use std::sync::Arc; -use std::sync::atomic::{AtomicUsize, Ordering}; use std::thread; use theme::{ColorStyle, Effect}; +use utils::Counter; use view::View; // pub type CbPromise = Option>; -/// Atomic counter used by `ProgressBar`. -#[derive(Clone)] -pub struct Counter(pub Arc); - -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. /// /// This bar has an internal counter, and adapts the length of the displayed From b1e9afe0ffd8ec19d9ca6fc7a77c0ae7bd791d56 Mon Sep 17 00:00:00 2001 From: Alexandre Bury Date: Tue, 10 Apr 2018 11:46:02 -0700 Subject: [PATCH 12/15] Add vpv example --- Cargo.toml | 1 + examples/vpv.rs | 139 ++++++++++++++++++++++++++++++++++++++++++++ src/utils/reader.rs | 2 +- src/with.rs | 11 ++++ 4 files changed, 152 insertions(+), 1 deletion(-) create mode 100644 examples/vpv.rs diff --git a/Cargo.toml b/Cargo.toml index 69bc1be..e098758 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -62,6 +62,7 @@ version = "1.5.0" [dev-dependencies] rand = "0.4" +pretty-bytes = "0.2.2" [features] blt-backend = ["bear-lib-terminal"] diff --git a/examples/vpv.rs b/examples/vpv.rs new file mode 100644 index 0000000..c3ba833 --- /dev/null +++ b/examples/vpv.rs @@ -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(); +} diff --git a/src/utils/reader.rs b/src/utils/reader.rs index 03ccad0..1f5485f 100644 --- a/src/utils/reader.rs +++ b/src/utils/reader.rs @@ -32,7 +32,7 @@ impl ProgressReader { impl Read for ProgressReader { fn read(&mut self, buf: &mut [u8]) -> io::Result { - let result = try!(self.reader.read(buf)); + let result = self.reader.read(buf)?; self.counter.tick(result); Ok(result) } diff --git a/src/with.rs b/src/with.rs index ba6df23..b183737 100644 --- a/src/with.rs +++ b/src/with.rs @@ -14,6 +14,17 @@ pub trait With: Sized { f(&mut self)?; Ok(self) } + + /// Calls the given closure if `condition == true`. + fn with_if(mut self, condition: bool, f: F) -> Self + where + F: FnOnce(&mut Self), + { + if condition { + f(&mut self); + } + self + } } impl With for T {} From a3551718447d9bce69fc3d8702a88939f8542b15 Mon Sep 17 00:00:00 2001 From: Alexandre Bury Date: Tue, 10 Apr 2018 11:53:25 -0700 Subject: [PATCH 13/15] Fix clippy warnings --- src/backend/curses/n.rs | 5 +++-- src/cursive.rs | 2 +- src/printer.rs | 8 ++++---- src/view/margins.rs | 29 ++++++++++++++++++----------- src/view/size_cache.rs | 6 +++--- src/views/box_view.rs | 2 +- src/views/canvas.rs | 2 +- src/views/linear_layout.rs | 2 +- src/views/menu_popup.rs | 2 +- src/views/on_event_view.rs | 2 +- src/views/panel.rs | 2 +- src/views/radio.rs | 6 +++--- src/views/select_view.rs | 2 +- src/xy.rs | 2 +- 14 files changed, 40 insertions(+), 32 deletions(-) diff --git a/src/backend/curses/n.rs b/src/backend/curses/n.rs index dac09bb..6b9b684 100644 --- a/src/backend/curses/n.rs +++ b/src/backend/curses/n.rs @@ -159,7 +159,7 @@ impl Backend { let make_event = |event| Event::Mouse { offset: Vec2::zero(), position: Vec2::new(mevent.x as usize, mevent.y as usize), - event: event, + event, }; if mevent.bstate == ncurses::REPORT_MOUSE_POSITION as mmask_t { @@ -239,7 +239,8 @@ impl backend::Backend for Backend { if current != colors { self.set_colors(colors); } - return current; + + current } fn set_effect(&self, effect: Effect) { diff --git a/src/cursive.rs b/src/cursive.rs index 3b2f314..784d0ac 100644 --- a/src/cursive.rs +++ b/src/cursive.rs @@ -569,7 +569,7 @@ impl Cursive { } 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(); diff --git a/src/printer.rs b/src/printer.rs index ab4547d..0b8c3fe 100644 --- a/src/printer.rs +++ b/src/printer.rs @@ -24,7 +24,7 @@ pub struct Printer<'a> { /// `true` if nothing has been drawn yet. new: Rc>, /// Backend used to actually draw things - backend: &'a Box, + backend: &'a Backend, } impl<'a> Printer<'a> { @@ -33,15 +33,15 @@ impl<'a> Printer<'a> { /// But nobody needs to know that. #[doc(hidden)] pub fn new>( - size: T, theme: &'a Theme, backend: &'a Box + size: T, theme: &'a Theme, backend: &'a Backend ) -> Self { Printer { offset: Vec2::zero(), size: size.into(), focused: true, - theme: theme, + theme, new: Rc::new(Cell::new(true)), - backend: backend, + backend, } } diff --git a/src/view/margins.rs b/src/view/margins.rs index 4772b7e..e358bda 100644 --- a/src/view/margins.rs +++ b/src/view/margins.rs @@ -1,5 +1,5 @@ -use vec::Vec2; use std::ops::{Add, Div, Mul, Sub}; +use vec::Vec2; /// Four values representing each direction. #[derive(Clone, Copy)] @@ -18,10 +18,10 @@ impl Margins { /// Creates a new Margins. pub fn new(left: usize, right: usize, top: usize, bottom: usize) -> Self { Margins { - left: left, - right: right, - top: top, - bottom: bottom, + left, + right, + top, + bottom, } } @@ -52,25 +52,34 @@ impl Margins { } impl From<(usize, usize, usize, usize)> for Margins { - fn from((left, right, top, bottom): (usize, usize, usize, usize)) -> Margins { + fn from( + (left, right, top, bottom): (usize, usize, usize, usize), + ) -> Margins { Margins::new(left, right, top, bottom) } } impl From<(i32, i32, i32, i32)> for Margins { fn from((left, right, top, bottom): (i32, i32, i32, i32)) -> Margins { - (left as usize, right as usize, top as usize, bottom as usize).into() + ( + left as usize, + right as usize, + top as usize, + bottom as usize, + ).into() } } impl From<((i32, i32), (i32, i32))> for Margins { - fn from(((left, right), (top, bottom)): ((i32, i32), (i32, i32))) -> Margins { + fn from( + ((left, right), (top, bottom)): ((i32, i32), (i32, i32)), + ) -> Margins { (left, right, top, bottom).into() } } impl From<((usize, usize), (usize, usize))> for Margins { fn from( - ((left, right), (top, bottom)): ((usize, usize), (usize, usize)) + ((left, right), (top, bottom)): ((usize, usize), (usize, usize)), ) -> Margins { (left, right, top, bottom).into() } @@ -131,5 +140,3 @@ impl Mul for Margins { } } } - - diff --git a/src/view/size_cache.rs b/src/view/size_cache.rs index 021a6b3..3786eab 100644 --- a/src/view/size_cache.rs +++ b/src/view/size_cache.rs @@ -1,5 +1,5 @@ -use XY; use vec::Vec2; +use XY; /// Cache around a one-dimensional layout result. /// @@ -19,8 +19,8 @@ impl SizeCache { /// Creates a new sized cache pub fn new(value: usize, constrained: bool) -> Self { SizeCache { - value: value, - constrained: constrained, + value, + constrained, } } diff --git a/src/views/box_view.rs b/src/views/box_view.rs index 48cfaa1..af55c6a 100644 --- a/src/views/box_view.rs +++ b/src/views/box_view.rs @@ -44,7 +44,7 @@ impl BoxView { BoxView { size: (width, height).into(), squishable: false, - view: view, + view, } } diff --git a/src/views/canvas.rs b/src/views/canvas.rs index 8d5d7af..f89be69 100644 --- a/src/views/canvas.rs +++ b/src/views/canvas.rs @@ -48,7 +48,7 @@ impl Canvas { /// ``` pub fn new(state: T) -> Self { Canvas { - state: state, + state, draw: Box::new(|_, _| ()), on_event: Box::new(|_, _| EventResult::Ignored), required_size: Box::new(|_, _| Vec2::new(1, 1)), diff --git a/src/views/linear_layout.rs b/src/views/linear_layout.rs index 605903a..1dc406f 100644 --- a/src/views/linear_layout.rs +++ b/src/views/linear_layout.rs @@ -113,7 +113,7 @@ impl LinearLayout { pub fn new(orientation: direction::Orientation) -> Self { LinearLayout { children: Vec::new(), - orientation: orientation, + orientation, focus: 0, cache: None, } diff --git a/src/views/menu_popup.rs b/src/views/menu_popup.rs index f583a3e..e255a09 100644 --- a/src/views/menu_popup.rs +++ b/src/views/menu_popup.rs @@ -26,7 +26,7 @@ impl MenuPopup { /// Creates a new `MenuPopup` using the given menu tree. pub fn new(menu: Rc) -> Self { MenuPopup { - menu: menu, + menu, focus: 0, scrollbase: ScrollBase::new().scrollbar_offset(1).right_padding(0), align: Align::top_left(), diff --git a/src/views/on_event_view.rs b/src/views/on_event_view.rs index e7a7e03..4db522a 100644 --- a/src/views/on_event_view.rs +++ b/src/views/on_event_view.rs @@ -72,7 +72,7 @@ impl OnEventView { /// Wraps the given view in a new OnEventView. pub fn new(view: T) -> Self { OnEventView { - view: view, + view, callbacks: HashMap::new(), } } diff --git a/src/views/panel.rs b/src/views/panel.rs index 07627fb..63e577f 100644 --- a/src/views/panel.rs +++ b/src/views/panel.rs @@ -12,7 +12,7 @@ pub struct Panel { impl Panel { /// Creates a new panel around the given view. pub fn new(view: V) -> Self { - Panel { view: view } + Panel { view } } inner_getters!(self.view: V); diff --git a/src/views/radio.rs b/src/views/radio.rs index 15cd4e3..9afb823 100644 --- a/src/views/radio.rs +++ b/src/views/radio.rs @@ -107,10 +107,10 @@ impl RadioButton { state: Rc>>, id: usize, label: String ) -> Self { RadioButton { - state: state, - id: id, + state, + id, enabled: true, - label: label, + label, } } diff --git a/src/views/select_view.rs b/src/views/select_view.rs index 9cf704d..edc44e8 100644 --- a/src/views/select_view.rs +++ b/src/views/select_view.rs @@ -341,7 +341,7 @@ impl SelectView { // TODO: Check if `i >= self.len()` ? // assert!(i < self.len(), "SelectView: trying to select out-of-bound"); // Or just cap the ID? - let i = if self.len() == 0 { + let i = if self.is_empty() { 0 } else { min(i, self.len() - 1) diff --git a/src/xy.rs b/src/xy.rs index 35e2f20..9c73b5b 100644 --- a/src/xy.rs +++ b/src/xy.rs @@ -13,7 +13,7 @@ pub struct XY { impl XY { /// Creates a new `XY` from the given values. pub fn new(x: T, y: T) -> Self { - XY { x: x, y: y } + XY { x, y } } /// Creates a new `XY` by applying `f` to `x` and `y`. From ff9f669d73b9ebfddac2ff1787e5b93fcc8c14ba Mon Sep 17 00:00:00 2001 From: Alexandre Bury Date: Tue, 10 Apr 2018 12:50:40 -0700 Subject: [PATCH 14/15] Fix tests & examples --- examples/progress.rs | 2 +- src/printer.rs | 4 ++-- src/view/scroll.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/progress.rs b/examples/progress.rs index 0897036..107b036 100644 --- a/examples/progress.rs +++ b/examples/progress.rs @@ -4,7 +4,7 @@ extern crate rand; use cursive::Cursive; use cursive::traits::*; use cursive::views::{Button, Dialog, LinearLayout, ProgressBar, TextView}; -use cursive::views::Counter; +use cursive::utils::Counter; use rand::Rng; use std::cmp::min; use std::thread; diff --git a/src/printer.rs b/src/printer.rs index 0b8c3fe..8ac6825 100644 --- a/src/printer.rs +++ b/src/printer.rs @@ -123,7 +123,7 @@ impl<'a> Printer<'a> { /// # use cursive::backend; /// # let b = backend::dummy::Backend::init(); /// # 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.print((0,0), "This text is highlighted!"); /// }); @@ -201,7 +201,7 @@ impl<'a> Printer<'a> { /// # use cursive::backend; /// # let b = backend::dummy::Backend::init(); /// # 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); /// ``` pub fn print_box, S: Into>( diff --git a/src/view/scroll.rs b/src/view/scroll.rs index 2399bbf..0ee3097 100644 --- a/src/view/scroll.rs +++ b/src/view/scroll.rs @@ -234,7 +234,7 @@ impl ScrollBase { /// # let scrollbase = ScrollBase::new(); /// # let b = backend::dummy::Backend::init(); /// # 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 lines = ["Line 1", "Line number 2"]; /// scrollbase.draw(printer, |printer, i| { From 3731b7375d3929eace090326c6c85e0d3a438fb4 Mon Sep 17 00:00:00 2001 From: Alexandre Bury Date: Wed, 11 Apr 2018 22:08:21 -0700 Subject: [PATCH 15/15] Ncurses: better color approximation On terminals with only 8 colors (like with `TERM=screen`) --- src/backend/curses/mod.rs | 47 +++++++++++++++++++++++++++++---------- src/backend/curses/n.rs | 29 ++++++++++++++---------- src/backend/curses/pan.rs | 21 ++++++++--------- 3 files changed, 63 insertions(+), 34 deletions(-) diff --git a/src/backend/curses/mod.rs b/src/backend/curses/mod.rs index cd01bd1..7458b31 100644 --- a/src/backend/curses/mod.rs +++ b/src/backend/curses/mod.rs @@ -5,7 +5,7 @@ use event::{Event, Key}; use std::collections::HashMap; -use theme::{BaseColor, Color}; +use theme::{BaseColor, Color, ColorPair}; #[cfg(feature = "ncurses")] pub mod n; @@ -61,7 +61,14 @@ where } } -fn find_closest(color: &Color) -> i16 { +fn find_closest_pair(pair: &ColorPair, max_colors: i16) -> (i16, i16) { + ( + find_closest(&pair.front,max_colors), + find_closest(&pair.back, max_colors), + ) +} + +fn find_closest(color: &Color, max_colors: i16) -> i16 { match *color { Color::TerminalDefault => -1, Color::Dark(BaseColor::Black) => 0, @@ -72,29 +79,45 @@ fn find_closest(color: &Color) -> i16 { Color::Dark(BaseColor::Magenta) => 5, Color::Dark(BaseColor::Cyan) => 6, Color::Dark(BaseColor::White) => 7, - Color::Light(BaseColor::Black) => 8, - Color::Light(BaseColor::Red) => 9, - Color::Light(BaseColor::Green) => 10, - Color::Light(BaseColor::Yellow) => 11, - Color::Light(BaseColor::Blue) => 12, - Color::Light(BaseColor::Magenta) => 13, - Color::Light(BaseColor::Cyan) => 14, - Color::Light(BaseColor::White) => 15, - Color::Rgb(r, g, b) => { + Color::Light(BaseColor::Black) => 8 % max_colors, + Color::Light(BaseColor::Red) => 9 % max_colors, + Color::Light(BaseColor::Green) => 10 % max_colors, + Color::Light(BaseColor::Yellow) => 11 % max_colors, + Color::Light(BaseColor::Blue) => 12 % max_colors, + Color::Light(BaseColor::Magenta) => 13 % max_colors, + Color::Light(BaseColor::Cyan) => 14 % max_colors, + Color::Light(BaseColor::White) => 15 % max_colors, + Color::Rgb(r, g, b) if max_colors >= 256 => { // If r = g = b, it may be a grayscale value! if r == g && g == b && r != 0 && r < 250 { + // Grayscale // (r = g = b) = 8 + 10 * n // (r - 8) / 10 = n // let n = (r - 8) / 10; (232 + n) as i16 } else { + // Generic RGB let r = 6 * u16::from(r) / 256; let g = 6 * u16::from(g) / 256; let b = 6 * u16::from(b) / 256; (16 + 36 * r + 6 * g + b) as i16 } } - Color::RgbLowRes(r, g, b) => i16::from(16 + 36 * r + 6 * g + b), + Color::Rgb(r, g, b) => { + let r = if r > 127 { 1 } else { 0 }; + let g = if g > 127 { 1 } else { 0 }; + let b = if b > 127 { 1 } else { 0 }; + (r + 2 * g + 4 * b) as i16 + } + Color::RgbLowRes(r, g, b) if max_colors >= 256 => { + i16::from(16 + 36 * r + 6 * g + b) + } + Color::RgbLowRes(r, g, b) => { + let r = if r > 2 { 1 } else { 0 }; + let g = if g > 2 { 1 } else { 0 }; + let b = if b > 2 { 1 } else { 0 }; + (r + 2 * g + 4 * b) as i16 + } } } diff --git a/src/backend/curses/n.rs b/src/backend/curses/n.rs index 6b9b684..6d38bff 100644 --- a/src/backend/curses/n.rs +++ b/src/backend/curses/n.rs @@ -1,7 +1,7 @@ extern crate ncurses; use self::ncurses::mmask_t; -use self::super::{find_closest, split_i32}; +use self::super::split_i32; use backend; use event::{Event, Key, MouseButton, MouseEvent}; use libc; @@ -17,7 +17,9 @@ use vec::Vec2; pub struct Backend { current_style: Cell, - pairs: RefCell>, + + // Maps (front, back) ncurses colors to ncurses pairs + pairs: RefCell>, key_codes: HashMap, @@ -25,6 +27,10 @@ pub struct Backend { event_queue: Vec, } +fn find_closest_pair(pair: &ColorPair) -> (i16, i16) { + super::find_closest_pair(pair, ncurses::COLORS() as i16) +} + /// Writes some bytes directly to `/dev/tty` /// /// Since this is not going to be used often, we can afford to re-open the @@ -89,11 +95,13 @@ impl Backend { Box::new(c) } + /// Save a new color pair. fn insert_color( - &self, pairs: &mut HashMap, pair: ColorPair + &self, pairs: &mut HashMap<(i16, i16), i16>, (front, back): (i16, i16) ) -> i16 { let n = 1 + pairs.len() as i16; + let target = if ncurses::COLOR_PAIRS() > i32::from(n) { // We still have plenty of space for everyone. n @@ -104,12 +112,8 @@ impl Backend { pairs.retain(|_, &mut v| v != target); target }; - pairs.insert(pair, target); - ncurses::init_pair( - target, - find_closest(&pair.front), - find_closest(&pair.back), - ); + pairs.insert((front, back), target); + ncurses::init_pair(target, front, back); target } @@ -118,11 +122,12 @@ impl Backend { let mut pairs = self.pairs.borrow_mut(); // Find if we have this color in stock - if pairs.contains_key(&pair) { + let (front, back) = find_closest_pair(&pair); + if pairs.contains_key(&(front, back)) { // We got it! - pairs[&pair] + pairs[&(front, back)] } else { - self.insert_color(&mut *pairs, pair) + self.insert_color(&mut *pairs, (front, back)) } } diff --git a/src/backend/curses/pan.rs b/src/backend/curses/pan.rs index 45bac6e..0f350dd 100644 --- a/src/backend/curses/pan.rs +++ b/src/backend/curses/pan.rs @@ -1,7 +1,7 @@ extern crate pancurses; use self::pancurses::mmask_t; -use self::super::{find_closest, split_i32}; +use self::super::split_i32; use backend; use event::{Event, Key, MouseButton, MouseEvent}; use std::cell::{Cell, RefCell}; @@ -13,7 +13,7 @@ use vec::Vec2; pub struct Backend { // Used current_style: Cell, - pairs: RefCell>, + pairs: RefCell>, key_codes: HashMap, @@ -24,6 +24,10 @@ pub struct Backend { window: pancurses::Window, } +fn find_closest_pair(pair: &ColorPair) -> (i16, i16) { + super::find_closest_pair(pair, pancurses::COLORS() as i16) +} + impl Backend { pub fn init() -> Box { ::std::env::set_var("ESCDELAY", "25"); @@ -62,8 +66,8 @@ impl Backend { /// Save a new color pair. fn insert_color( &self, - pairs: &mut HashMap, - pair: ColorPair, + pairs: &mut HashMap<(i16,i16), i32>, + (front, back): (i16, i16), ) -> i32 { let n = 1 + pairs.len() as i32; @@ -78,18 +82,15 @@ impl Backend { pairs.retain(|_, &mut v| v != target); target }; - pairs.insert(pair, target); - pancurses::init_pair( - target as i16, - find_closest(&pair.front), - find_closest(&pair.back), - ); + pairs.insert((front, back), target); + pancurses::init_pair(target as i16, front, back); target } /// Checks the pair in the cache, or re-define a color if needed. fn get_or_create(&self, pair: ColorPair) -> i32 { let mut pairs = self.pairs.borrow_mut(); + let pair = find_closest_pair(&pair); // Find if we have this color in stock if pairs.contains_key(&pair) {