mirror of
https://github.com/FliegendeWurst/cursive.git
synced 2024-11-23 17:35:00 +00:00
Make Menubar
a proper View
This commit is contained in:
parent
2065be3e88
commit
a1d0231e2c
13
src/lib.rs
13
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<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 {
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user