menu_bar: allow custom width on menu items

This commit is contained in:
Bryan Hyland 2026-01-19 14:30:45 -08:00
parent 097c76f0e5
commit 5ae1b961dc
2 changed files with 99 additions and 30 deletions

View file

@ -69,7 +69,8 @@ pub use menu_bar::{MenuBar, menu_bar as bar};
mod menu_inner;
mod menu_tree;
pub use menu_tree::{
MenuItem as Item, MenuTree as Tree, menu_button, menu_items as items, menu_root as root,
MenuItem as Item, MenuItemKind as ItemKind, MenuTree as Tree, menu_button, menu_items as items,
menu_root as root,
};
pub use crate::style::menu_bar::{Appearance, StyleSheet};

View file

@ -155,24 +155,12 @@ where
.class(theme::Button::MenuItem)
}
/// The type of menu item
#[derive(Clone)]
/// Represents a menu item that performs an action when selected or a separator between menu items.
///
/// - `Action` - Represents a menu item that performs an action when selected.
/// - `L` - The label of the menu item.
/// - `A` - The action to perform when the menu item is selected, the action must implement the `MenuAction` trait.
/// - `CheckBox` - Represents a checkbox menu item.
/// - `L` - The label of the menu item.
/// - `bool` - The state of the checkbox.
/// - `A` - The action to perform when the menu item is selected, the action must implement the `MenuAction` trait.
/// - `Folder` - Represents a folder menu item.
/// - `L` - The label of the menu item.
/// - `Vec<MenuItem<A, L>>` - A vector of menu items.
/// - `Divider` - Represents a divider between menu items.
pub enum MenuItem<A: MenuAction, L: Into<Cow<'static, str>>> {
pub enum MenuItemKind<A: MenuAction, L: Into<Cow<'static, str>>> {
/// Represents a button menu item.
Button(L, Option<icon::Handle>, A),
/// Represents a button menu item that is disabled.
/// Represents a button menu item that's disabled.
ButtonDisabled(L, Option<icon::Handle>, A),
/// Represents a checkbox menu item.
CheckBox(L, Option<icon::Handle>, bool, A),
@ -182,6 +170,54 @@ pub enum MenuItem<A: MenuAction, L: Into<Cow<'static, str>>> {
Divider,
}
#[derive(Clone)]
/// A menu item with optional width configuration
pub struct MenuItem<A: MenuAction, L: Into<Cow<'static, str>>> {
/// Kind of menu item.
kind: MenuItemKind<A, L>,
/// Optional width override for this item's submenu.
width: Option<u16>,
}
impl<A: MenuAction, L: Into<Cow<'static, str>>> MenuItem<A, L> {
/// Create from a kind with no width set
pub fn new(kind: MenuItemKind<A, L>) -> Self {
Self { kind, width: None }
}
/// Builder method to set width
pub fn width(mut self, width: u16) -> Self {
self.width = Some(width);
self
}
pub fn button(label: L, icon: Option<icon::Handle>, action: A) -> Self {
Self::new(MenuItemKind::Button(label, icon, action))
}
pub fn button_disabled(label: L, icon: Option<icon::Handle>, action: A) -> Self {
Self::new(MenuItemKind::ButtonDisabled(label, icon, action))
}
pub fn checkbox(label: L, icon: Option<icon::Handle>, checked: bool, action: A) -> Self {
Self::new(MenuItemKind::CheckBox(label, icon, checked, action))
}
pub fn folder(label: L, children: Vec<MenuItem<A, L>>) -> Self {
Self::new(MenuItemKind::Folder(label, children))
}
pub fn divider() -> Self {
Self::new(MenuItemKind::Divider)
}
}
impl<A: MenuAction, L: Into<Cow<'static, str>>> From<MenuItemKind<A, L>> for MenuItem<A, L> {
fn from(kind: MenuItemKind<A, L>) -> Self {
Self::new(kind)
}
}
/// Create a root menu item.
///
/// # Arguments
@ -246,9 +282,10 @@ pub fn menu_items<
.flat_map(|(i, item)| {
let mut trees = vec![];
let spacing = crate::theme::spacing();
let item_width = item.width;
match item {
MenuItem::Button(label, icon, action) => {
match item.kind {
MenuItemKind::Button(label, icon, action) => {
let l: Cow<'static, str> = label.into();
let key = find_key(&action, key_binds);
let mut items = vec![
@ -264,9 +301,16 @@ pub fn menu_items<
let menu_button = menu_button(items).on_press(action.message());
trees.push(MenuTree::<Message>::from(Element::from(menu_button)));
// Add a user designated width
let mut tree = MenuTree::<Message>::from(Element::from(menu_button));
if let Some(width) = item_width {
tree = tree.width(width);
}
trees.push(tree);
}
MenuItem::ButtonDisabled(label, icon, action) => {
MenuItemKind::ButtonDisabled(label, icon, action) => {
let l: Cow<'static, str> = label.into();
let key = find_key(&action, key_binds);
@ -284,9 +328,15 @@ pub fn menu_items<
let menu_button = menu_button(items);
trees.push(MenuTree::<Message>::from(Element::from(menu_button)));
let mut tree = MenuTree::<Message>::from(Element::from(menu_button));
if let Some(width) = item_width {
tree = tree.width(width);
}
trees.push(tree);
}
MenuItem::CheckBox(label, icon, value, action) => {
MenuItemKind::CheckBox(label, icon, value, action) => {
let key = find_key(&action, key_binds);
let mut items = vec![
if value {
@ -314,14 +364,20 @@ pub fn menu_items<
items.insert(2, widget::icon::icon(icon).size(14).into());
}
trees.push(MenuTree::from(Element::from(
let mut tree = MenuTree::from(Element::from(
menu_button(items).on_press(action.message()),
)));
));
if let Some(width) = item_width {
tree = tree.width(width);
}
trees.push(tree);
}
MenuItem::Folder(label, children) => {
MenuItemKind::Folder(label, children) => {
let l: Cow<'static, str> = label.into();
trees.push(MenuTree::<Message>::with_children(
let mut tree = MenuTree::<Message>::with_children(
RcElementWrapper::new(crate::Element::from(
menu_button::<'static, _>(vec![
widget::text(l.clone()).into(),
@ -343,13 +399,25 @@ pub fn menu_items<
),
)),
menu_items(key_binds, children),
));
);
if let Some(width) = item_width {
tree = tree.width(width);
}
trees.push(tree);
}
MenuItem::Divider => {
MenuItemKind::Divider => {
if i != size - 1 {
trees.push(MenuTree::<Message>::from(Element::from(
let mut tree = MenuTree::<Message>::from(Element::from(
widget::divider::horizontal::light(),
)));
));
if let Some(width) = item_width {
tree = tree.width(width);
}
trees.push(tree);
}
}
}