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?
|
// 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 {
|
||||||
|
@ -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.
|
||||||
|
@ -154,9 +154,8 @@ 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;
|
||||||
}
|
}
|
||||||
|
@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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 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<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);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.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);
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
}
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user