From a1d0231e2c2216b4b9d2c04cbdbbcdd07317ea63 Mon Sep 17 00:00:00 2001 From: Alexandre Bury Date: Tue, 19 Jul 2016 20:44:20 -0700 Subject: [PATCH] Make `Menubar` a proper `View` --- src/lib.rs | 13 ++-- src/printer.rs | 8 +-- src/view/dialog.rs | 5 +- src/view/list_view.rs | 22 ++++--- src/view/menu_popup.rs | 8 ++- src/{ => view}/menubar.rs | 124 +++++++++++++++++++++----------------- src/view/mod.rs | 9 ++- src/view/scroll.rs | 4 +- 8 files changed, 108 insertions(+), 85 deletions(-) rename src/{ => view}/menubar.rs (85%) diff --git a/src/lib.rs b/src/lib.rs index 3ef9c21..ec1e43b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -82,7 +82,6 @@ pub mod direction; // This probably doesn't need to be public? mod printer; -mod menubar; mod xy; mod with; @@ -94,7 +93,6 @@ mod backend; pub use xy::XY; pub use with::With; pub use printer::Printer; -pub use menubar::Menubar; use backend::{Backend, NcursesBackend}; @@ -123,7 +121,7 @@ pub struct Cursive { theme: theme::Theme, screens: Vec, global_callbacks: HashMap, - menubar: menubar::Menubar, + menubar: view::Menubar, active_screen: ScreenId, @@ -149,7 +147,7 @@ impl Cursive { theme: theme, screens: Vec::new(), global_callbacks: HashMap::new(), - menubar: menubar::Menubar::new(), + menubar: view::Menubar::new(), active_screen: 0, running: true, }; @@ -161,7 +159,7 @@ impl Cursive { /// Selects the menubar pub fn select_menubar(&mut self) { - self.menubar.take_focus(); + self.menubar.take_focus(direction::Direction::none()); } /// Sets the menubar autohide_menubar feature. @@ -173,7 +171,7 @@ impl Cursive { } /// Retrieve the menu tree used by the menubar. - pub fn menubar(&mut self) -> &mut menubar::Menubar { + pub fn menubar(&mut self) -> &mut view::Menubar { &mut self.menubar } @@ -358,7 +356,8 @@ impl Cursive { // * Current screen (top layer) // * Global callbacks if self.menubar.receive_events() { - if let Some(cb) = self.menubar.on_event(event) { + if let EventResult::Consumed(Some(cb)) = self.menubar + .on_event(event) { cb(self); } } else { diff --git a/src/printer.rs b/src/printer.rs index d108afe..36c6ae8 100644 --- a/src/printer.rs +++ b/src/printer.rs @@ -140,12 +140,8 @@ impl Printer { self.print_hline(start + (1, 0), size.x - 1, "─"); self.print_vline(start + (0, 1), size.y - 1, "│"); - self.print_hline(start + (1, 0) + size.keep_y(), - size.x - 1, - "─"); - self.print_vline(start + (0, 1) + size.keep_x(), - size.y - 1, - "│"); + self.print_hline(start + (1, 0) + size.keep_y(), size.x - 1, "─"); + self.print_vline(start + (0, 1) + size.keep_x(), size.y - 1, "│"); } /// Apply a selection style and call the given function. diff --git a/src/view/dialog.rs b/src/view/dialog.rs index 2b72180..63b6bf7 100644 --- a/src/view/dialog.rs +++ b/src/view/dialog.rs @@ -154,9 +154,8 @@ impl View for Dialog { } // What do we have left? - let taken = Vec2::new(0, buttons_height) + - self.borders.combined() + - self.padding.combined(); + let taken = Vec2::new(0, buttons_height) + self.borders.combined() + + self.padding.combined(); if !taken.fits_in(printer.size) { return; } diff --git a/src/view/list_view.rs b/src/view/list_view.rs index db76a88..dcda291 100644 --- a/src/view/list_view.rs +++ b/src/view/list_view.rs @@ -30,14 +30,14 @@ impl Child { } // Currently unused - /* - fn is_delimiter(&self) -> bool { - match *self { - Child::Row(_, _) => false, - Child::Delimiter => true, - } - } - */ + // + // fn is_delimiter(&self) -> bool { + // match *self { + // Child::Row(_, _) => false, + // Child::Delimiter => true, + // } + // } + // } /// Displays a scrollable list of elements. @@ -266,6 +266,10 @@ impl View for ListView { } fn find(&mut self, selector: &Selector) -> Option<&mut Any> { - self.children.iter_mut().filter_map(Child::view).filter_map(|v| v.find(selector)).next() + self.children + .iter_mut() + .filter_map(Child::view) + .filter_map(|v| v.find(selector)) + .next() } } diff --git a/src/view/menu_popup.rs b/src/view/menu_popup.rs index 5d6d18f..69bea21 100644 --- a/src/view/menu_popup.rs +++ b/src/view/menu_popup.rs @@ -159,13 +159,17 @@ impl View for MenuPopup { printer.print_hdelim((0, 0), printer.size.x) } MenuItem::Subtree(ref label, _) => { - if printer.size.x < 4 { return; } + if printer.size.x < 4 { + return; + } printer.print_hline((1, 0), printer.size.x - 2, " "); printer.print((2, 0), label); printer.print((printer.size.x - 4, 0), ">>"); } MenuItem::Leaf(ref label, _) => { - if printer.size.x < 2 { return; } + if printer.size.x < 2 { + return; + } printer.print_hline((1, 0), printer.size.x - 2, " "); printer.print((2, 0), label); } diff --git a/src/menubar.rs b/src/view/menubar.rs similarity index 85% rename from src/menubar.rs rename to src/view/menubar.rs index 9b1a436..830a89a 100644 --- a/src/menubar.rs +++ b/src/view/menubar.rs @@ -1,4 +1,8 @@ use Cursive; +use view::View; + +use vec::Vec2; +use direction; use menu::MenuTree; use backend::Backend; use view::MenuPopup; @@ -62,13 +66,6 @@ impl Menubar { ::B::clear(); } - /// Takes the focus. - /// - /// TODO: impl View - pub fn take_focus(&mut self) { - self.state = State::Selected; - } - /// True if we should be receiving events. pub fn receive_events(&self) -> bool { self.state == State::Selected @@ -87,11 +84,47 @@ impl Menubar { self.menus.push((title.to_string(), Rc::new(menu))); self } +} - /// Draws the view. - /// - /// TODO: impl View - pub fn draw(&mut self, printer: &Printer) { +fn show_child(s: &mut Cursive, offset: (usize, usize), menu: Rc) { + // Adds a new layer located near the item title with the menu popup. + // Also adds two key callbacks on this new view, to handle `left` and + // `right` key presses. + // (If the view itself listens for a `left` or `right` press, it will + // consume it before our KeyEventView. This means sub-menus can properly + // be entered.) + s.screen_mut() + .add_layer_at(Position::absolute(offset), + KeyEventView::new(MenuPopup::new(menu) + .on_dismiss(|s| s.select_menubar()) + .on_action(|s| { + s.menubar().state = State::Inactive + })) + .register(Key::Right, |s| { + s.pop_layer(); + s.select_menubar(); + // Act as if we sent "Right" then "Down" + s.menubar().on_event(Event::Key(Key::Right)); + if let EventResult::Consumed(Some(cb)) = s.menubar() + .on_event(Event::Key(Key::Down)) { + cb(s); + } + }) + .register(Key::Left, |s| { + s.pop_layer(); + s.select_menubar(); + // Act as if we sent "Left" then "Down" + s.menubar().on_event(Event::Key(Key::Left)); + if let EventResult::Consumed(Some(cb)) = s.menubar() + .on_event(Event::Key(Key::Down)) { + cb(s); + } + })); + +} + +impl View for Menubar { + fn draw(&self, printer: &Printer) { // Draw the bar at the top printer.with_color(ColorStyle::Primary, |printer| { printer.print_hline((0, 0), printer.size.x, " "); @@ -111,10 +144,7 @@ impl Menubar { } } - /// Reacts to event. - /// - /// TODO: impl View - pub fn on_event(&mut self, event: Event) -> Option { + fn on_event(&mut self, event: Event) -> EventResult { match event { Event::Key(Key::Esc) => self.hide(), Event::Key(Key::Left) => { @@ -148,49 +178,31 @@ impl Menubar { }); // Since the closure will be called multiple times, // we also need a new Rc on every call. - return Some(Rc::new(move |s| { + return EventResult::with_cb(move |s| { show_child(s, offset, menu.clone()) - })); + }); } - _ => (), + _ => return EventResult::Ignored, } - None + EventResult::Consumed(None) + } + + fn take_focus(&mut self, _: direction::Direction) -> bool { + self.state = State::Selected; + true + } + + fn get_min_size(&mut self, _: Vec2) -> Vec2 { + // TODO: scroll the options if the screen is too small? + + // We add 2 to the length of every label for marin. + // Also, we add 1 at the beginning. + // (See the `draw()` method) + let width = self.menus + .iter() + .map(|&(ref title, _)| title.len() + 2) + .fold(1, |a, b| a + b); + + Vec2::new(width, 1) } } - -fn show_child(s: &mut Cursive, offset: (usize, usize), menu: Rc) { - // Adds a new layer located near the item title with the menu popup. - // Also adds two key callbacks on this new view, to handle `left` and - // `right` key presses. - // (If the view itself listens for a `left` or `right` press, it will - // consume it before our KeyEventView. This means sub-menus can properly - // be entered.) - s.screen_mut() - .add_layer_at(Position::absolute(offset), - KeyEventView::new(MenuPopup::new(menu) - .on_dismiss(|s| s.select_menubar()) - .on_action(|s| { - s.menubar().state = State::Inactive - })) - .register(Key::Right, |s| { - s.pop_layer(); - s.select_menubar(); - // Act as if we sent "Right" then "Down" - s.menubar().on_event(Event::Key(Key::Right)); - if let Some(cb) = s.menubar() - .on_event(Event::Key(Key::Down)) { - cb(s); - } - }) - .register(Key::Left, |s| { - s.pop_layer(); - s.select_menubar(); - // Act as if we sent "Left" then "Down" - s.menubar().on_event(Event::Key(Key::Left)); - if let Some(cb) = s.menubar() - .on_event(Event::Key(Key::Down)) { - cb(s); - } - })); - -} diff --git a/src/view/mod.rs b/src/view/mod.rs index aa41be5..7597f89 100644 --- a/src/view/mod.rs +++ b/src/view/mod.rs @@ -56,6 +56,7 @@ mod id_view; mod key_event_view; mod linear_layout; mod list_view; +mod menubar; mod menu_popup; mod shadow_view; mod select_view; @@ -87,6 +88,7 @@ pub use self::full_view::FullView; pub use self::key_event_view::KeyEventView; pub use self::linear_layout::LinearLayout; pub use self::list_view::ListView; +pub use self::menubar::Menubar; pub use self::menu_popup::MenuPopup; pub use self::view_path::ViewPath; pub use self::select_view::SelectView; @@ -106,7 +108,12 @@ pub trait View { } /// Returns the minimum size the view requires with the given restrictions. - fn get_min_size(&mut self, Vec2) -> Vec2 { + /// + /// If the view is flexible (it has multiple size options), it can try + /// to return one that fits the given `constraint`. + /// It's also fine to ignore it and return a fixed value. + fn get_min_size(&mut self, constraint: Vec2) -> Vec2 { + let _ = constraint; Vec2::new(1, 1) } diff --git a/src/view/scroll.rs b/src/view/scroll.rs index adf8655..f7622f5 100644 --- a/src/view/scroll.rs +++ b/src/view/scroll.rs @@ -137,7 +137,9 @@ impl ScrollBase { let max_y = min(self.view_height, self.content_height - self.start_line); let w = if self.scrollable() { - if printer.size.x < 2 { return; } + if printer.size.x < 2 { + return; + } printer.size.x - 2 + self.scrollbar_padding // TODO: 2 } else { printer.size.x