mirror of
https://github.com/FliegendeWurst/cursive.git
synced 2024-11-23 17:35:00 +00:00
Add enabled state to menu items
This commit is contained in:
parent
06d64be0a0
commit
6543287704
@ -2,80 +2,159 @@
|
||||
//!
|
||||
//! Menus are a way to arrange many actions in groups of more manageable size.
|
||||
//!
|
||||
//! A menu can be seen as a [`MenuTree`]. It has a list of children:
|
||||
//! A menu can be seen as a [`Tree`]. It has a list of children:
|
||||
//!
|
||||
//! * Leaf nodes are made of a label and a callback
|
||||
//! * Sub-trees are made of a label, and another `MenuTree`.
|
||||
//! * Sub-trees are made of a label, and another `Tree`.
|
||||
//! * Delimiters are just there to separate groups of related children.
|
||||
//!
|
||||
//! The [menubar] is the main way to show menus.
|
||||
//!
|
||||
//! [`MenuTree`]: struct.MenuTree.html
|
||||
//! [`Tree`]: struct.Tree.html
|
||||
//! [menubar]: ../struct.Cursive.html#method.menubar
|
||||
|
||||
use crate::event::Callback;
|
||||
use crate::Cursive;
|
||||
use crate::With;
|
||||
use crate::{event::Callback, Cursive, With};
|
||||
use std::rc::Rc;
|
||||
|
||||
/// Root of a menu tree.
|
||||
#[derive(Default, Clone)]
|
||||
pub struct MenuTree {
|
||||
pub struct Tree {
|
||||
/// Menu items
|
||||
pub children: Vec<MenuItem>,
|
||||
pub children: Vec<Item>,
|
||||
}
|
||||
|
||||
/// Node in the menu tree.
|
||||
#[derive(Clone)]
|
||||
pub enum MenuItem {
|
||||
pub enum Item {
|
||||
/// Actionnable button with a label.
|
||||
Leaf(String, Callback),
|
||||
Leaf {
|
||||
/// Text displayed for this entry.
|
||||
label: String,
|
||||
/// Callback to run when the entry is selected.
|
||||
cb: Callback,
|
||||
/// Whether this item is enabled.
|
||||
///
|
||||
/// Disabled items cannot be selected and are displayed grayed out.
|
||||
enabled: bool,
|
||||
},
|
||||
|
||||
/// Sub-menu with a label.
|
||||
Subtree(String, Rc<MenuTree>),
|
||||
Subtree {
|
||||
/// Text displayed for this entry.
|
||||
label: String,
|
||||
/// Subtree under this item.
|
||||
tree: Rc<Tree>,
|
||||
/// Whether this item is enabled.
|
||||
///
|
||||
/// Disabled items cannot be selected and are displayed grayed out.
|
||||
enabled: bool,
|
||||
},
|
||||
|
||||
/// Delimiter without a label.
|
||||
Delimiter,
|
||||
}
|
||||
|
||||
impl MenuItem {
|
||||
impl Item {
|
||||
/// Create a new leaf menu item.
|
||||
pub fn leaf<S, F>(label: S, cb: F) -> Self
|
||||
where
|
||||
S: Into<String>,
|
||||
F: 'static + Fn(&mut Cursive),
|
||||
{
|
||||
let label = label.into();
|
||||
let cb = Callback::from_fn(cb);
|
||||
let enabled = true;
|
||||
Item::Leaf { label, cb, enabled }
|
||||
}
|
||||
|
||||
/// Create a new subtree menu item.
|
||||
pub fn subtree<S>(label: S, tree: Tree) -> Self
|
||||
where
|
||||
S: Into<String>,
|
||||
{
|
||||
let label = label.into();
|
||||
let tree = Rc::new(tree);
|
||||
let enabled = true;
|
||||
Item::Subtree {
|
||||
label,
|
||||
tree,
|
||||
enabled,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the label for this item.
|
||||
///
|
||||
/// Returns an empty string if `self` is a delimiter.
|
||||
/// Returns a vertical bar string if `self` is a delimiter.
|
||||
pub fn label(&self) -> &str {
|
||||
match *self {
|
||||
MenuItem::Delimiter => "│",
|
||||
MenuItem::Leaf(ref label, _) | MenuItem::Subtree(ref label, _) => {
|
||||
Item::Delimiter => "│",
|
||||
Item::Leaf { ref label, .. } | Item::Subtree { ref label, .. } => {
|
||||
label
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if this item is enabled.
|
||||
///
|
||||
/// Only labels and subtrees can be enabled. Delimiters
|
||||
pub fn is_enabled(&self) -> bool {
|
||||
match *self {
|
||||
Item::Leaf { enabled, .. } | Item::Subtree { enabled, .. } => {
|
||||
enabled
|
||||
}
|
||||
Item::Delimiter => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Return a disabled version of this item.
|
||||
pub fn disabled(self) -> Self {
|
||||
self.with(Self::disable)
|
||||
}
|
||||
|
||||
/// Disable this item.
|
||||
///
|
||||
/// Disabled items cannot be selected and are shown grayed out.
|
||||
///
|
||||
/// Does not affect delimiters.
|
||||
pub fn disable(&mut self) {
|
||||
if let Item::Leaf {
|
||||
ref mut enabled, ..
|
||||
}
|
||||
| Item::Subtree {
|
||||
ref mut enabled, ..
|
||||
} = self
|
||||
{
|
||||
*enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if `self` is a delimiter.
|
||||
pub fn is_delimiter(&self) -> bool {
|
||||
matches!(*self, MenuItem::Delimiter)
|
||||
matches!(*self, Item::Delimiter)
|
||||
}
|
||||
|
||||
/// Returns `true` if `self` is a leaf node.
|
||||
pub fn is_leaf(&self) -> bool {
|
||||
matches!(*self, MenuItem::Leaf(_, _))
|
||||
matches!(*self, Item::Leaf { .. })
|
||||
}
|
||||
|
||||
/// Returns `true` if `self` is a subtree.
|
||||
pub fn is_subtree(&self) -> bool {
|
||||
matches!(*self, MenuItem::Subtree(_, _))
|
||||
matches!(*self, Item::Subtree { .. })
|
||||
}
|
||||
|
||||
/// Return a mutable reference to the subtree, if applicable.
|
||||
///
|
||||
/// Returns `None` if `self` is not a `MenuItem::Subtree`.
|
||||
pub fn as_subtree(&mut self) -> Option<&mut MenuTree> {
|
||||
/// Returns `None` if `self` is not a `Item::Subtree`.
|
||||
pub fn as_subtree(&mut self) -> Option<&mut Tree> {
|
||||
match *self {
|
||||
MenuItem::Subtree(_, ref mut tree) => Some(Rc::make_mut(tree)),
|
||||
Item::Subtree { ref mut tree, .. } => Some(Rc::make_mut(tree)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MenuTree {
|
||||
impl Tree {
|
||||
/// Creates a new, empty tree.
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
@ -87,13 +166,13 @@ impl MenuTree {
|
||||
}
|
||||
|
||||
/// Inserts an item at the given position.
|
||||
pub fn insert(&mut self, i: usize, item: MenuItem) {
|
||||
pub fn insert(&mut self, i: usize, item: Item) {
|
||||
self.children.insert(i, item);
|
||||
}
|
||||
|
||||
/// Inserts a delimiter at the given position.
|
||||
pub fn insert_delimiter(&mut self, i: usize) {
|
||||
self.insert(i, MenuItem::Delimiter);
|
||||
self.insert(i, Item::Delimiter);
|
||||
}
|
||||
|
||||
/// Adds a delimiter to the end of this tree.
|
||||
@ -108,100 +187,123 @@ impl MenuTree {
|
||||
}
|
||||
|
||||
/// Adds a actionnable leaf to the end of this tree.
|
||||
pub fn add_leaf<S, F>(&mut self, title: S, cb: F)
|
||||
pub fn add_leaf<S, F>(&mut self, label: S, cb: F)
|
||||
where
|
||||
S: Into<String>,
|
||||
F: 'static + Fn(&mut Cursive),
|
||||
{
|
||||
let i = self.children.len();
|
||||
self.insert_leaf(i, title, cb);
|
||||
self.insert_leaf(i, label, cb);
|
||||
}
|
||||
|
||||
/// Inserts a leaf at the given position.
|
||||
pub fn insert_leaf<S, F>(&mut self, i: usize, title: S, cb: F)
|
||||
pub fn insert_leaf<S, F>(&mut self, i: usize, label: S, cb: F)
|
||||
where
|
||||
S: Into<String>,
|
||||
F: 'static + Fn(&mut Cursive),
|
||||
{
|
||||
let title = title.into();
|
||||
self.insert(i, MenuItem::Leaf(title, Callback::from_fn(cb)));
|
||||
let label = label.into();
|
||||
self.insert(
|
||||
i,
|
||||
Item::Leaf {
|
||||
label,
|
||||
cb: Callback::from_fn(cb),
|
||||
enabled: true,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// Adds a actionnable leaf to the end of this tree - chainable variant.
|
||||
pub fn leaf<S, F>(self, title: S, cb: F) -> Self
|
||||
pub fn leaf<S, F>(self, label: S, cb: F) -> Self
|
||||
where
|
||||
S: Into<String>,
|
||||
F: 'static + Fn(&mut Cursive),
|
||||
{
|
||||
self.with(|menu| menu.add_leaf(title, cb))
|
||||
self.with(|menu| menu.add_leaf(label, cb))
|
||||
}
|
||||
|
||||
/// Inserts a subtree at the given position.
|
||||
pub fn insert_subtree<S>(&mut self, i: usize, title: S, tree: MenuTree)
|
||||
pub fn insert_subtree<S>(&mut self, i: usize, label: S, tree: Tree)
|
||||
where
|
||||
S: Into<String>,
|
||||
{
|
||||
let title = title.into();
|
||||
let tree = MenuItem::Subtree(title, Rc::new(tree));
|
||||
let label = label.into();
|
||||
let tree = Item::Subtree {
|
||||
label,
|
||||
tree: Rc::new(tree),
|
||||
enabled: true,
|
||||
};
|
||||
self.insert(i, tree);
|
||||
}
|
||||
|
||||
/// Adds an item to the end of this tree.
|
||||
///
|
||||
/// Chainable variant.
|
||||
pub fn item(self, item: Item) -> Self {
|
||||
self.with(|s| s.add_item(item))
|
||||
}
|
||||
|
||||
/// Adds an item to the end of this tree.
|
||||
pub fn add_item(&mut self, item: Item) {
|
||||
let i = self.children.len();
|
||||
self.insert(i, item);
|
||||
}
|
||||
|
||||
/// Adds a submenu to the end of this tree.
|
||||
pub fn add_subtree<S>(&mut self, title: S, tree: MenuTree)
|
||||
pub fn add_subtree<S>(&mut self, label: S, tree: Tree)
|
||||
where
|
||||
S: Into<String>,
|
||||
{
|
||||
let i = self.children.len();
|
||||
self.insert_subtree(i, title, tree);
|
||||
self.insert_subtree(i, label, tree);
|
||||
}
|
||||
|
||||
/// Adds a submenu to the end of this tree - chainable variant.
|
||||
pub fn subtree<S>(self, title: S, tree: MenuTree) -> Self
|
||||
pub fn subtree<S>(self, label: S, tree: Tree) -> Self
|
||||
where
|
||||
S: Into<String>,
|
||||
{
|
||||
self.with(|menu| menu.add_subtree(title, tree))
|
||||
self.with(|menu| menu.add_subtree(label, tree))
|
||||
}
|
||||
|
||||
/// Looks for the child at the given position.
|
||||
///
|
||||
/// Returns `None` if `i >= self.len()`.
|
||||
pub fn get_mut(&mut self, i: usize) -> Option<&mut MenuItem> {
|
||||
pub fn get_mut(&mut self, i: usize) -> Option<&mut Item> {
|
||||
self.children.get_mut(i)
|
||||
}
|
||||
|
||||
/// Returns the item at the given position.
|
||||
///
|
||||
/// Returns `None` if `i > self.len()` or if the item is not a subtree.
|
||||
pub fn get_subtree(&mut self, i: usize) -> Option<&mut MenuTree> {
|
||||
self.get_mut(i).and_then(MenuItem::as_subtree)
|
||||
pub fn get_subtree(&mut self, i: usize) -> Option<&mut Tree> {
|
||||
self.get_mut(i).and_then(Item::as_subtree)
|
||||
}
|
||||
|
||||
/// Looks for a child with the given title.
|
||||
/// Looks for a child with the given label.
|
||||
///
|
||||
/// Returns `None` if no such label was found.
|
||||
pub fn find_item(&mut self, title: &str) -> Option<&mut MenuItem> {
|
||||
pub fn find_item(&mut self, label: &str) -> Option<&mut Item> {
|
||||
self.children
|
||||
.iter_mut()
|
||||
.find(|child| child.label() == title)
|
||||
.find(|child| child.label() == label)
|
||||
}
|
||||
|
||||
/// Looks for a subtree with the given title.
|
||||
pub fn find_subtree(&mut self, title: &str) -> Option<&mut MenuTree> {
|
||||
/// Looks for a subtree with the given label.
|
||||
pub fn find_subtree(&mut self, label: &str) -> Option<&mut Tree> {
|
||||
self.children
|
||||
.iter_mut()
|
||||
.filter(|child| child.label() == title)
|
||||
.filter_map(MenuItem::as_subtree)
|
||||
.next()
|
||||
.filter(|child| child.label() == label)
|
||||
.find_map(Item::as_subtree)
|
||||
}
|
||||
|
||||
/// Returns the position of a child with the given label.
|
||||
///
|
||||
/// Returns `None` if no such label was found.
|
||||
pub fn find_position(&mut self, title: &str) -> Option<usize> {
|
||||
pub fn find_position(&mut self, label: &str) -> Option<usize> {
|
||||
self.children
|
||||
.iter()
|
||||
.position(|child| child.label() == title)
|
||||
.position(|child| child.label() == label)
|
||||
}
|
||||
|
||||
/// Removes the item at the given position.
|
||||
|
@ -1,16 +1,14 @@
|
||||
use crate::align::Align;
|
||||
use crate::event::{
|
||||
Callback, Event, EventResult, Key, MouseButton, MouseEvent,
|
||||
use crate::{
|
||||
align::Align,
|
||||
event::{Callback, Event, EventResult, Key, MouseButton, MouseEvent},
|
||||
menu,
|
||||
rect::Rect,
|
||||
theme::ColorStyle,
|
||||
view::scroll,
|
||||
view::{Position, View},
|
||||
views::OnEventView,
|
||||
Cursive, Printer, Vec2, With,
|
||||
};
|
||||
use crate::menu::{MenuItem, MenuTree};
|
||||
use crate::rect::Rect;
|
||||
use crate::view::scroll;
|
||||
use crate::view::{Position, View};
|
||||
use crate::views::OnEventView;
|
||||
use crate::Cursive;
|
||||
use crate::Printer;
|
||||
use crate::Vec2;
|
||||
use crate::With;
|
||||
use std::cmp::min;
|
||||
use std::rc::Rc;
|
||||
use unicode_width::UnicodeWidthStr;
|
||||
@ -23,7 +21,7 @@ use unicode_width::UnicodeWidthStr;
|
||||
/// [1]: crate::views::SelectView::popup()
|
||||
/// [2]: crate::Cursive::menubar()
|
||||
pub struct MenuPopup {
|
||||
menu: Rc<MenuTree>,
|
||||
menu: Rc<menu::Tree>,
|
||||
focus: usize,
|
||||
scroll_core: scroll::Core,
|
||||
align: Align,
|
||||
@ -38,7 +36,7 @@ impl_scroller!(MenuPopup::scroll_core);
|
||||
|
||||
impl MenuPopup {
|
||||
/// Creates a new `MenuPopup` using the given menu tree.
|
||||
pub fn new(menu: Rc<MenuTree>) -> Self {
|
||||
pub fn new(menu: Rc<menu::Tree>) -> Self {
|
||||
MenuPopup {
|
||||
menu,
|
||||
focus: 0,
|
||||
@ -66,11 +64,11 @@ impl MenuPopup {
|
||||
self.focus
|
||||
}
|
||||
|
||||
fn item_width(item: &MenuItem) -> usize {
|
||||
fn item_width(item: &menu::Item) -> usize {
|
||||
match *item {
|
||||
MenuItem::Delimiter => 1,
|
||||
MenuItem::Leaf(ref title, _) => title.width(),
|
||||
MenuItem::Subtree(ref title, _) => title.width() + 3,
|
||||
menu::Item::Delimiter => 1,
|
||||
menu::Item::Leaf { ref label, .. } => label.width(),
|
||||
menu::Item::Subtree { ref label, .. } => label.width() + 3,
|
||||
}
|
||||
}
|
||||
|
||||
@ -132,7 +130,7 @@ impl MenuPopup {
|
||||
break;
|
||||
}
|
||||
|
||||
if !self.menu.children[self.focus].is_delimiter() {
|
||||
if self.menu.children[self.focus].is_enabled() {
|
||||
n -= 1;
|
||||
}
|
||||
}
|
||||
@ -149,7 +147,7 @@ impl MenuPopup {
|
||||
break;
|
||||
}
|
||||
|
||||
if !self.menu.children[self.focus].is_delimiter() {
|
||||
if self.menu.children[self.focus].is_enabled() {
|
||||
n -= 1;
|
||||
}
|
||||
}
|
||||
@ -157,7 +155,7 @@ impl MenuPopup {
|
||||
|
||||
fn submit(&mut self) -> EventResult {
|
||||
match self.menu.children[self.focus] {
|
||||
MenuItem::Leaf(_, ref cb) => {
|
||||
menu::Item::Leaf { ref cb, .. } => {
|
||||
let cb = cb.clone();
|
||||
let action_cb = self.on_action.clone();
|
||||
EventResult::with_cb(move |s| {
|
||||
@ -171,7 +169,7 @@ impl MenuPopup {
|
||||
cb.clone()(s);
|
||||
})
|
||||
}
|
||||
MenuItem::Subtree(_, ref tree) => self.make_subtree_cb(tree),
|
||||
menu::Item::Subtree { ref tree, .. } => self.make_subtree_cb(tree),
|
||||
_ => unreachable!("Delimiters cannot be submitted."),
|
||||
}
|
||||
}
|
||||
@ -186,7 +184,7 @@ impl MenuPopup {
|
||||
})
|
||||
}
|
||||
|
||||
fn make_subtree_cb(&self, tree: &Rc<MenuTree>) -> EventResult {
|
||||
fn make_subtree_cb(&self, tree: &Rc<menu::Tree>) -> EventResult {
|
||||
let tree = Rc::clone(tree);
|
||||
let max_width = 4 + self
|
||||
.menu
|
||||
@ -239,14 +237,14 @@ impl MenuPopup {
|
||||
if self.menu.children[self.focus].is_subtree() =>
|
||||
{
|
||||
return match self.menu.children[self.focus] {
|
||||
MenuItem::Subtree(_, ref tree) => {
|
||||
menu::Item::Subtree { ref tree, .. } => {
|
||||
self.make_subtree_cb(tree)
|
||||
}
|
||||
_ => unreachable!("Child is a subtree"),
|
||||
};
|
||||
}
|
||||
Event::Key(Key::Enter)
|
||||
if !self.menu.children[self.focus].is_delimiter() =>
|
||||
if self.menu.children[self.focus].is_enabled() =>
|
||||
{
|
||||
return self.submit();
|
||||
}
|
||||
@ -260,7 +258,7 @@ impl MenuPopup {
|
||||
// Now `position` is relative to the top-left of the content.
|
||||
let focus = position.y;
|
||||
if focus < self.menu.len()
|
||||
&& !self.menu.children[focus].is_delimiter()
|
||||
&& self.menu.children[focus].is_enabled()
|
||||
{
|
||||
self.focus = focus;
|
||||
}
|
||||
@ -270,7 +268,7 @@ impl MenuPopup {
|
||||
event: MouseEvent::Release(MouseButton::Left),
|
||||
position,
|
||||
offset,
|
||||
} if !self.menu.children[self.focus].is_delimiter()
|
||||
} if self.menu.children[self.focus].is_enabled()
|
||||
&& position
|
||||
.checked_sub(offset)
|
||||
.map(|position| position.y == self.focus)
|
||||
@ -335,14 +333,23 @@ impl View for MenuPopup {
|
||||
let printer = printer.shrinked_centered((2, 2));
|
||||
|
||||
scroll::draw_lines(self, &printer, |s, printer, i| {
|
||||
printer.with_selection(i == s.focus, |printer| {
|
||||
let item = &s.menu.children[i];
|
||||
let enabled =
|
||||
printer.enabled && (item.is_enabled() || item.is_delimiter());
|
||||
let color = if !enabled {
|
||||
ColorStyle::secondary()
|
||||
} else if i == s.focus {
|
||||
ColorStyle::highlight()
|
||||
} else {
|
||||
ColorStyle::primary()
|
||||
};
|
||||
printer.with_style(color, |printer| {
|
||||
match *item {
|
||||
MenuItem::Delimiter => {
|
||||
menu::Item::Delimiter => {
|
||||
// printer.print_hdelim((0, 0), printer.size.x)
|
||||
printer.print_hline((0, 0), printer.size.x, "─");
|
||||
}
|
||||
MenuItem::Subtree(ref label, _) => {
|
||||
menu::Item::Subtree { ref label, .. } => {
|
||||
if printer.size.x < 4 {
|
||||
return;
|
||||
}
|
||||
@ -351,7 +358,7 @@ impl View for MenuPopup {
|
||||
let x = printer.size.x.saturating_sub(3);
|
||||
printer.print((x, 0), ">>");
|
||||
}
|
||||
MenuItem::Leaf(ref label, _) => {
|
||||
menu::Item::Leaf { ref label, .. } => {
|
||||
if printer.size.x < 2 {
|
||||
return;
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::direction;
|
||||
use crate::event::*;
|
||||
use crate::menu::{MenuItem, MenuTree};
|
||||
use crate::menu;
|
||||
use crate::rect::Rect;
|
||||
use crate::theme::ColorStyle;
|
||||
use crate::view::{Position, View};
|
||||
@ -34,7 +34,7 @@ enum State {
|
||||
/// [`Cursive`]: crate::Cursive::menubar
|
||||
pub struct Menubar {
|
||||
/// Menu items in this menubar.
|
||||
root: MenuTree,
|
||||
root: menu::Tree,
|
||||
|
||||
/// TODO: move this out of this view.
|
||||
pub autohide: bool,
|
||||
@ -50,7 +50,7 @@ impl Menubar {
|
||||
/// Creates a new, empty menubar.
|
||||
pub fn new() -> Self {
|
||||
Menubar {
|
||||
root: MenuTree::new(),
|
||||
root: menu::Tree::new(),
|
||||
autohide: true,
|
||||
state: State::Inactive,
|
||||
focus: 0,
|
||||
@ -77,11 +77,23 @@ impl Menubar {
|
||||
!self.autohide || self.state != State::Inactive
|
||||
}
|
||||
|
||||
/// Adds a new item to the menubar.
|
||||
pub fn insert(&mut self, i: usize, item: menu::Item) -> &mut Self {
|
||||
self.root.insert(i, item);
|
||||
self
|
||||
}
|
||||
|
||||
/// Adds a new item to the menubar.
|
||||
pub fn item(&mut self, item: menu::Item) -> &mut Self {
|
||||
let i = self.root.len();
|
||||
self.insert(i, item)
|
||||
}
|
||||
|
||||
/// Adds a new item to the menubar.
|
||||
///
|
||||
/// The item will use the given title, and on selection, will open a
|
||||
/// popup-menu with the given menu tree.
|
||||
pub fn add_subtree<S>(&mut self, title: S, menu: MenuTree) -> &mut Self
|
||||
pub fn add_subtree<S>(&mut self, title: S, menu: menu::Tree) -> &mut Self
|
||||
where
|
||||
S: Into<String>,
|
||||
{
|
||||
@ -110,7 +122,7 @@ impl Menubar {
|
||||
&mut self,
|
||||
i: usize,
|
||||
title: S,
|
||||
menu: MenuTree,
|
||||
menu: menu::Tree,
|
||||
) -> &mut Self
|
||||
where
|
||||
S: Into<String>,
|
||||
@ -158,12 +170,12 @@ impl Menubar {
|
||||
/// Returns the item at the given position.
|
||||
///
|
||||
/// Returns `None` if `i > self.len()`
|
||||
pub fn get_subtree(&mut self, i: usize) -> Option<&mut MenuTree> {
|
||||
pub fn get_subtree(&mut self, i: usize) -> Option<&mut menu::Tree> {
|
||||
self.root.get_subtree(i)
|
||||
}
|
||||
|
||||
/// Looks for an item with the given label.
|
||||
pub fn find_subtree(&mut self, label: &str) -> Option<&mut MenuTree> {
|
||||
pub fn find_subtree(&mut self, label: &str) -> Option<&mut menu::Tree> {
|
||||
self.root.find_subtree(label)
|
||||
}
|
||||
|
||||
@ -197,12 +209,12 @@ impl Menubar {
|
||||
|
||||
fn select_child(&mut self, open_only: bool) -> EventResult {
|
||||
match self.root.children[self.focus] {
|
||||
MenuItem::Leaf(_, ref cb) if !open_only => {
|
||||
menu::Item::Leaf { ref cb, .. } if !open_only => {
|
||||
// Go inactive after an action.
|
||||
self.state = State::Inactive;
|
||||
EventResult::Consumed(Some(cb.clone()))
|
||||
}
|
||||
MenuItem::Subtree(_, ref tree) => {
|
||||
menu::Item::Subtree { ref tree, .. } => {
|
||||
// First, we need a new Rc to send the callback,
|
||||
// since we don't know when it will be called.
|
||||
let menu = Rc::clone(tree);
|
||||
@ -226,7 +238,7 @@ impl Menubar {
|
||||
}
|
||||
}
|
||||
|
||||
fn show_child(s: &mut Cursive, offset: Vec2, menu: Rc<MenuTree>) {
|
||||
fn show_child(s: &mut Cursive, offset: Vec2, menu: Rc<menu::Tree>) {
|
||||
// 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.
|
||||
@ -275,16 +287,30 @@ impl View for Menubar {
|
||||
// TODO: draw the rest
|
||||
let mut offset = 1;
|
||||
for (i, item) in self.root.children.iter().enumerate() {
|
||||
let title = item.label();
|
||||
let label = item.label();
|
||||
|
||||
// We print disabled items differently, except delimiters,
|
||||
// which are still white.
|
||||
let enabled =
|
||||
printer.enabled && (item.is_enabled() || item.is_delimiter());
|
||||
|
||||
// We don't want to show HighlightInactive when we're not selected,
|
||||
// because it's ugly on the menubar.
|
||||
let selected =
|
||||
(self.state != State::Inactive) && (i == self.focus);
|
||||
printer.with_selection(selected, |printer| {
|
||||
printer.print((offset, 0), &format!(" {} ", title));
|
||||
|
||||
let color = if !enabled {
|
||||
ColorStyle::secondary()
|
||||
} else if selected {
|
||||
ColorStyle::highlight()
|
||||
} else {
|
||||
ColorStyle::primary()
|
||||
};
|
||||
|
||||
printer.with_style(color, |printer| {
|
||||
printer.print((offset, 0), &format!(" {} ", label));
|
||||
});
|
||||
offset += title.width() + 2;
|
||||
offset += label.width() + 2;
|
||||
}
|
||||
}
|
||||
|
||||
@ -295,12 +321,13 @@ impl View for Menubar {
|
||||
return EventResult::with_cb(Cursive::clear);
|
||||
}
|
||||
Event::Key(Key::Left) => loop {
|
||||
// TODO: fix endless loop if nothing is enabled?
|
||||
if self.focus > 0 {
|
||||
self.focus -= 1;
|
||||
} else {
|
||||
self.focus = self.root.len() - 1;
|
||||
}
|
||||
if !self.root.children[self.focus].is_delimiter() {
|
||||
if self.root.children[self.focus].is_enabled() {
|
||||
break;
|
||||
}
|
||||
},
|
||||
@ -310,7 +337,7 @@ impl View for Menubar {
|
||||
} else {
|
||||
self.focus = 0;
|
||||
}
|
||||
if !self.root.children[self.focus].is_delimiter() {
|
||||
if self.root.children[self.focus].is_enabled() {
|
||||
break;
|
||||
}
|
||||
},
|
||||
@ -329,7 +356,7 @@ impl View for Menubar {
|
||||
.checked_sub(offset)
|
||||
.and_then(|pos| self.child_at(pos.x))
|
||||
{
|
||||
if !self.root.children[child].is_delimiter() {
|
||||
if self.root.children[child].is_enabled() {
|
||||
self.focus = child;
|
||||
if btn == MouseButton::Left {
|
||||
return self.select_child(true);
|
||||
|
@ -3,7 +3,7 @@ use crate::direction::Direction;
|
||||
use crate::event::{
|
||||
Callback, Event, EventResult, Key, MouseButton, MouseEvent,
|
||||
};
|
||||
use crate::menu::MenuTree;
|
||||
use crate::menu;
|
||||
use crate::rect::Rect;
|
||||
use crate::theme::ColorStyle;
|
||||
use crate::utils::markup::StyledString;
|
||||
@ -713,7 +713,7 @@ impl<T: 'static> SelectView<T> {
|
||||
fn open_popup(&mut self) -> EventResult {
|
||||
// Build a shallow menu tree to mimick the items array.
|
||||
// TODO: cache it?
|
||||
let mut tree = MenuTree::new();
|
||||
let mut tree = menu::Tree::new();
|
||||
for (i, item) in self.items.iter().enumerate() {
|
||||
let focus = Rc::clone(&self.focus);
|
||||
let on_submit = self.on_submit.as_ref().cloned();
|
||||
|
@ -1,7 +1,4 @@
|
||||
use cursive::event::Key;
|
||||
use cursive::menu::MenuTree;
|
||||
use cursive::traits::*;
|
||||
use cursive::views::Dialog;
|
||||
use cursive::{event::Key, menu, traits::*, views::Dialog};
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
// This examples shows how to configure and use a menubar at the top of the
|
||||
@ -18,7 +15,7 @@ fn main() {
|
||||
// We add a new "File" tree
|
||||
.add_subtree(
|
||||
"File",
|
||||
MenuTree::new()
|
||||
menu::Tree::new()
|
||||
// Trees are made of leaves, with are directly actionable...
|
||||
.leaf("New", move |s| {
|
||||
// Here we use the counter to add an entry
|
||||
@ -39,11 +36,13 @@ fn main() {
|
||||
"Recent",
|
||||
// The `.with()` method can help when running loops
|
||||
// within builder patterns.
|
||||
MenuTree::new().with(|tree| {
|
||||
menu::Tree::new().with(|tree| {
|
||||
for i in 1..100 {
|
||||
// We don't actually do anything here,
|
||||
// but you could!
|
||||
tree.add_leaf(format!("Item {}", i), |_| ())
|
||||
tree.add_item(menu::Item::leaf(format!("Item {}", i), |_| ()).with(|s| {
|
||||
if i % 5 == 0 { s.disable(); }
|
||||
}))
|
||||
}
|
||||
}),
|
||||
)
|
||||
@ -58,10 +57,10 @@ fn main() {
|
||||
)
|
||||
.add_subtree(
|
||||
"Help",
|
||||
MenuTree::new()
|
||||
menu::Tree::new()
|
||||
.subtree(
|
||||
"Help",
|
||||
MenuTree::new()
|
||||
menu::Tree::new()
|
||||
.leaf("General", |s| {
|
||||
s.add_layer(Dialog::info("Help message!"))
|
||||
})
|
||||
|
Loading…
Reference in New Issue
Block a user