Make Menubar a proper View

This commit is contained in:
Alexandre Bury 2016-07-19 20:44:20 -07:00
parent 2065be3e88
commit a1d0231e2c
8 changed files with 108 additions and 85 deletions

View File

@ -82,7 +82,6 @@ pub mod direction;
// This probably doesn't need to be public? // This probably doesn't need to be public?
mod printer; mod printer;
mod menubar;
mod xy; mod xy;
mod with; mod with;
@ -94,7 +93,6 @@ mod backend;
pub use xy::XY; pub use xy::XY;
pub use with::With; pub use with::With;
pub use printer::Printer; pub use printer::Printer;
pub use menubar::Menubar;
use backend::{Backend, NcursesBackend}; use backend::{Backend, NcursesBackend};
@ -123,7 +121,7 @@ pub struct Cursive {
theme: theme::Theme, theme: theme::Theme,
screens: Vec<StackView>, screens: Vec<StackView>,
global_callbacks: HashMap<Event, Callback>, global_callbacks: HashMap<Event, Callback>,
menubar: menubar::Menubar, menubar: view::Menubar,
active_screen: ScreenId, active_screen: ScreenId,
@ -149,7 +147,7 @@ impl Cursive {
theme: theme, theme: theme,
screens: Vec::new(), screens: Vec::new(),
global_callbacks: HashMap::new(), global_callbacks: HashMap::new(),
menubar: menubar::Menubar::new(), menubar: view::Menubar::new(),
active_screen: 0, active_screen: 0,
running: true, running: true,
}; };
@ -161,7 +159,7 @@ impl Cursive {
/// Selects the menubar /// Selects the menubar
pub fn select_menubar(&mut self) { pub fn select_menubar(&mut self) {
self.menubar.take_focus(); self.menubar.take_focus(direction::Direction::none());
} }
/// Sets the menubar autohide_menubar feature. /// Sets the menubar autohide_menubar feature.
@ -173,7 +171,7 @@ impl Cursive {
} }
/// Retrieve the menu tree used by the menubar. /// 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 &mut self.menubar
} }
@ -358,7 +356,8 @@ impl Cursive {
// * Current screen (top layer) // * Current screen (top layer)
// * Global callbacks // * Global callbacks
if self.menubar.receive_events() { 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); cb(self);
} }
} else { } else {

View File

@ -140,12 +140,8 @@ impl Printer {
self.print_hline(start + (1, 0), size.x - 1, ""); self.print_hline(start + (1, 0), size.x - 1, "");
self.print_vline(start + (0, 1), size.y - 1, ""); self.print_vline(start + (0, 1), size.y - 1, "");
self.print_hline(start + (1, 0) + size.keep_y(), self.print_hline(start + (1, 0) + size.keep_y(), size.x - 1, "");
size.x - 1, self.print_vline(start + (0, 1) + size.keep_x(), size.y - 1, "");
"");
self.print_vline(start + (0, 1) + size.keep_x(),
size.y - 1,
"");
} }
/// Apply a selection style and call the given function. /// Apply a selection style and call the given function.

View File

@ -154,8 +154,7 @@ impl View for Dialog {
} }
// What do we have left? // What do we have left?
let taken = Vec2::new(0, buttons_height) + let taken = Vec2::new(0, buttons_height) + self.borders.combined() +
self.borders.combined() +
self.padding.combined(); self.padding.combined();
if !taken.fits_in(printer.size) { if !taken.fits_in(printer.size) {
return; return;

View File

@ -30,14 +30,14 @@ impl Child {
} }
// Currently unused // Currently unused
/* //
fn is_delimiter(&self) -> bool { // fn is_delimiter(&self) -> bool {
match *self { // match *self {
Child::Row(_, _) => false, // Child::Row(_, _) => false,
Child::Delimiter => true, // Child::Delimiter => true,
} // }
} // }
*/ //
} }
/// Displays a scrollable list of elements. /// Displays a scrollable list of elements.
@ -266,6 +266,10 @@ impl View for ListView {
} }
fn find(&mut self, selector: &Selector) -> Option<&mut Any> { 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()
} }
} }

View File

@ -159,13 +159,17 @@ impl View for MenuPopup {
printer.print_hdelim((0, 0), printer.size.x) printer.print_hdelim((0, 0), printer.size.x)
} }
MenuItem::Subtree(ref label, _) => { 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_hline((1, 0), printer.size.x - 2, " ");
printer.print((2, 0), label); printer.print((2, 0), label);
printer.print((printer.size.x - 4, 0), ">>"); printer.print((printer.size.x - 4, 0), ">>");
} }
MenuItem::Leaf(ref label, _) => { 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_hline((1, 0), printer.size.x - 2, " ");
printer.print((2, 0), label); printer.print((2, 0), label);
} }

View File

@ -1,4 +1,8 @@
use Cursive; use Cursive;
use view::View;
use vec::Vec2;
use direction;
use menu::MenuTree; use menu::MenuTree;
use backend::Backend; use backend::Backend;
use view::MenuPopup; use view::MenuPopup;
@ -62,13 +66,6 @@ impl Menubar {
::B::clear(); ::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. /// True if we should be receiving events.
pub fn receive_events(&self) -> bool { pub fn receive_events(&self) -> bool {
self.state == State::Selected self.state == State::Selected
@ -87,11 +84,47 @@ impl Menubar {
self.menus.push((title.to_string(), Rc::new(menu))); self.menus.push((title.to_string(), Rc::new(menu)));
self self
} }
}
/// Draws the view. fn show_child(s: &mut Cursive, offset: (usize, usize), menu: Rc<MenuTree>) {
/// // Adds a new layer located near the item title with the menu popup.
/// TODO: impl View // Also adds two key callbacks on this new view, to handle `left` and
pub fn draw(&mut self, printer: &Printer) { // `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 // Draw the bar at the top
printer.with_color(ColorStyle::Primary, |printer| { printer.with_color(ColorStyle::Primary, |printer| {
printer.print_hline((0, 0), printer.size.x, " "); printer.print_hline((0, 0), printer.size.x, " ");
@ -111,10 +144,7 @@ impl Menubar {
} }
} }
/// Reacts to event. fn on_event(&mut self, event: Event) -> EventResult {
///
/// TODO: impl View
pub fn on_event(&mut self, event: Event) -> Option<Callback> {
match event { match event {
Event::Key(Key::Esc) => self.hide(), Event::Key(Key::Esc) => self.hide(),
Event::Key(Key::Left) => { Event::Key(Key::Left) => {
@ -148,49 +178,31 @@ impl Menubar {
}); });
// Since the closure will be called multiple times, // Since the closure will be called multiple times,
// we also need a new Rc on every call. // 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()) show_child(s, offset, menu.clone())
})); });
} }
_ => (), _ => return EventResult::Ignored,
}
None
} }
EventResult::Consumed(None)
} }
fn show_child(s: &mut Cursive, offset: (usize, usize), menu: Rc<MenuTree>) { fn take_focus(&mut self, _: direction::Direction) -> bool {
// Adds a new layer located near the item title with the menu popup. self.state = State::Selected;
// Also adds two key callbacks on this new view, to handle `left` and true
// `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);
}
}));
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)
}
} }

View File

@ -56,6 +56,7 @@ mod id_view;
mod key_event_view; mod key_event_view;
mod linear_layout; mod linear_layout;
mod list_view; mod list_view;
mod menubar;
mod menu_popup; mod menu_popup;
mod shadow_view; mod shadow_view;
mod select_view; mod select_view;
@ -87,6 +88,7 @@ pub use self::full_view::FullView;
pub use self::key_event_view::KeyEventView; pub use self::key_event_view::KeyEventView;
pub use self::linear_layout::LinearLayout; pub use self::linear_layout::LinearLayout;
pub use self::list_view::ListView; pub use self::list_view::ListView;
pub use self::menubar::Menubar;
pub use self::menu_popup::MenuPopup; pub use self::menu_popup::MenuPopup;
pub use self::view_path::ViewPath; pub use self::view_path::ViewPath;
pub use self::select_view::SelectView; pub use self::select_view::SelectView;
@ -106,7 +108,12 @@ pub trait View {
} }
/// Returns the minimum size the view requires with the given restrictions. /// 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) Vec2::new(1, 1)
} }

View File

@ -137,7 +137,9 @@ impl ScrollBase {
let max_y = min(self.view_height, let max_y = min(self.view_height,
self.content_height - self.start_line); self.content_height - self.start_line);
let w = if self.scrollable() { 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 printer.size.x - 2 + self.scrollbar_padding // TODO: 2
} else { } else {
printer.size.x printer.size.x