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?
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<StackView>,
global_callbacks: HashMap<Event, Callback>,
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 {

View File

@ -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.

View File

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

View File

@ -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()
}
}

View File

@ -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);
}

View File

@ -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<MenuTree>) {
// 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<Callback> {
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())
}));
});
}
_ => (),
}
None
_ => return EventResult::Ignored,
}
EventResult::Consumed(None)
}
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.
// 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);
fn take_focus(&mut self, _: direction::Direction) -> bool {
self.state = State::Selected;
true
}
})
.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 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)
}

View File

@ -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