wip: popup

This commit is contained in:
Ashley Wulber 2025-05-28 17:01:24 -04:00
parent 8642a92270
commit a372ce800f
No known key found for this signature in database
GPG key ID: 5216D4F46A90A820
8 changed files with 469 additions and 159 deletions

View file

@ -46,13 +46,19 @@ impl Page {
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Action {
Hi,
Hi2,
Hi3,
}
impl MenuAction for Action {
type Message = Message;
fn message(&self) -> Message {
Message::Hi
match self {
Action::Hi => Message::Hi,
Action::Hi2 => Message::Hi2,
Action::Hi3 => Message::Hi3,
}
}
}
@ -86,6 +92,8 @@ pub enum Message {
ToggleHide,
Surface(cosmic::surface::Action),
Hi,
Hi2,
Hi3,
}
/// The [`App`] stores application-specific state.
@ -176,6 +184,12 @@ impl cosmic::Application for App {
Message::Hi => {
dbg!("hi");
}
Message::Hi2 => {
dbg!("hi 2");
}
Message::Hi3 => {
dbg!("hi 3");
}
}
Task::none()
}
@ -222,123 +236,60 @@ impl cosmic::Application for App {
fn header_start(&self) -> Vec<Element<Self::Message>> {
use cosmic::widget::menu::Tree;
#[cfg(not(feature = "wayland"))]
{
vec![cosmic::widget::responsive_menu_bar(
self.core(),
&self.keybinds,
MENU_ID.clone(),
Message::Surface,
vec![
cosmic::widget::menu::bar(vec![
Tree::with_children(
menu::root("hiiiiiiiiiiiiiiiiiii 1"),
menu::items(
&self.keybinds,
vec![menu::Item::Button("hi", None, Action::Hi)],
),
),
Tree::with_children(
menu::root("hiiiiiiiiiiiiiiiiii 2"),
menu::items(
&self.keybinds,
vec![menu::Item::Button("hi 2", None, Action::Hi)],
),
),
Tree::with_children(
menu::root("hiiiiiiiiiiiiiiiiiiiii 3"),
menu::items(
&self.keybinds,
vec![
menu::Item::Button("hi 3", None, Action::Hi),
menu::Item::Button("hi 3 #2", None, Action::Hi),
],
),
),
Tree::with_children(
menu::root("hi 3"),
menu::items(
&self.keybinds,
vec![
menu::Item::Button("hi 3", None, Action::Hi),
menu::Item::Button("hi 3 #2", None, Action::Hi),
menu::Item::Button("hi 3 #3", None, Action::Hi),
],
),
),
Tree::with_children(
menu::root("hi 4"),
menu::items(
&self.keybinds,
(
"hi 1".into(),
vec![
menu::Item::Button("hi 12".into(), None, Action::Hi),
menu::Item::Button("hi 13".into(), None, Action::Hi2),
],
),
(
"hi 2".into(),
vec![
menu::Item::Button("hi 21".into(), None, Action::Hi),
menu::Item::Button("hi 22".into(), None, Action::Hi2),
],
),
(
"hi 3".into(),
vec![
menu::Item::Button("hi 33".into(), None, Action::Hi),
menu::Item::Button("hi 333".into(), None, Action::Hi2),
menu::Item::Button("hi 3333".into(), None, Action::Hi3),
],
),
(
"hiiiiiiiiiiiiiiiiiii 4".into(),
vec![
menu::Item::Button("hi 4".into(), None, Action::Hi),
menu::Item::Button("hi 44".into(), None, Action::Hi2),
menu::Item::Button("hi 444".into(), None, Action::Hi3),
menu::Item::Folder(
"nest 4".into(),
vec![
menu::Item::Button("hi 41".into(), None, Action::Hi),
menu::Item::Button("hi 442".into(), None, Action::Hi2),
menu::Item::Button("hi 4443".into(), None, Action::Hi3),
menu::Item::Folder(
"hi 41 extra root",
vec![menu::Item::Button("hi 3", None, Action::Hi)],
"nest 2 4".into(),
vec![
menu::Item::Button("hi 443".into(), None, Action::Hi2),
menu::Item::Button("hi 4444".into(), None, Action::Hi),
],
),
menu::Item::Button("hi 42", None, Action::Hi),
menu::Item::Button("hi 43", None, Action::Hi),
menu::Item::Button("hi 44", None, Action::Hi),
menu::Item::Button("hi 45", None, Action::Hi),
menu::Item::Button("hi 46", None, Action::Hi),
],
),
),
])
.into(),
]
}
#[cfg(feature = "wayland")]
{
vec![cosmic::widget::responsive_menu_bar().into_element(
self.core(),
&self.keybinds,
MENU_ID.clone(),
Message::Surface,
vec![
(
"hi 1".into(),
vec![
menu::Item::Button("hi 1".into(), None, Action::Hi),
menu::Item::Button("hi 1 2".into(), None, Action::Hi),
],
),
(
"hi 2".into(),
vec![
menu::Item::Button("hi 2", None, Action::Hi),
menu::Item::Button("hi 22", None, Action::Hi),
],
),
(
"hi 3".into(),
vec![
menu::Item::Button("hi 3", None, Action::Hi),
menu::Item::Button("hi 33", None, Action::Hi),
menu::Item::Button("hi 333", None, Action::Hi),
],
),
(
"hiiiiiiiiiiiiiiiiiii 4".into(),
vec![
menu::Item::Button("hi 4", None, Action::Hi),
menu::Item::Button("hi 44", None, Action::Hi),
menu::Item::Button("hi 444", None, Action::Hi),
menu::Item::Folder(
"nest 4".into(),
vec![
menu::Item::Button("hi 4", None, Action::Hi),
menu::Item::Button("hi 44", None, Action::Hi),
menu::Item::Button("hi 444", None, Action::Hi),
menu::Item::Folder(
"nest 2 4".into(),
vec![
menu::Item::Button("hi 4", None, Action::Hi),
menu::Item::Button("hi 44", None, Action::Hi),
menu::Item::Button("hi 444", None, Action::Hi),
],
),
],
),
],
),
],
)]
}
],
),
],
)]
}
}

View file

@ -85,7 +85,7 @@ pub fn simple_subsurface<Message: 'static, V>(
/// Used to create a popup message from within a widget.
#[cfg(all(feature = "wayland", feature = "winit"))]
#[must_use]
pub fn simple_popup<Message: 'static, V>(
pub fn simple_popup<Message: 'static>(
settings: impl Fn() -> iced_runtime::platform_specific::wayland::popup::SctkPopupSettings
+ Send
+ Sync

View file

@ -42,7 +42,7 @@ pub enum MenuBarStyle {
#[default]
Default,
/// A [`Theme`] that uses a `Custom` palette.
Custom(Arc<dyn StyleSheet<Style = Theme>>),
Custom(Arc<dyn StyleSheet<Style = Theme> + Send + Sync>),
}
impl From<fn(&Theme) -> Appearance> for MenuBarStyle {

View file

@ -536,15 +536,7 @@ pub fn update<
let on_close = surface::action::destroy_popup(id);
let on_surface_action_clone = on_surface_action.clone();
let translation = layout.virtual_offset();
let get_popup_action = surface::action::simple_popup::<
AppMessage,
Box<
dyn Fn() -> Element<'static, crate::Action<AppMessage>>
+ Send
+ Sync
+ 'static,
>,
>(
let get_popup_action = surface::action::simple_popup::<AppMessage>(
move || {
SctkPopupSettings {
parent,

View file

@ -15,6 +15,7 @@ use crate::{
widget::{
RcWrapper,
dropdown::menu::{self, State},
menu::menu_inner::init_root_menu,
},
};
@ -57,6 +58,7 @@ pub(crate) struct MenuBarStateInner {
pub(crate) menu_states: Vec<Vec<MenuState>>,
}
impl MenuBarStateInner {
/// get the list of indices hovered for the menu
pub(super) fn get_trimmed_indices(&self, index: usize) -> impl Iterator<Item = usize> + '_ {
self.menu_states
.get(index)
@ -188,7 +190,7 @@ pub struct MenuBar<Message> {
menu_roots: Vec<MenuTree<Message>>,
style: <crate::Theme as StyleSheet>::Style,
window_id: window::Id,
#[cfg(feature = "wayland")]
#[cfg(all(feature = "wayland", feature = "winit"))]
positioner: iced_runtime::platform_specific::wayland::popup::SctkPositioner,
pub(crate) on_surface_action: Option<Box<dyn Fn(crate::surface::Action) -> Message>>,
}
@ -228,7 +230,7 @@ where
menu_roots,
style: <crate::Theme as StyleSheet>::Style::default(),
window_id: window::Id::NONE,
#[cfg(feature = "wayland")]
#[cfg(all(feature = "wayland", feature = "winit"))]
positioner: iced_runtime::platform_specific::wayland::popup::SctkPositioner::default(),
on_surface_action: None,
}
@ -322,7 +324,8 @@ where
self
}
#[cfg(feature = "wayland")]
#[cfg(all(feature = "wayland", feature = "winit"))]
pub fn with_positioner(
mut self,
positioner: iced_runtime::platform_specific::wayland::popup::SctkPositioner,
@ -405,6 +408,7 @@ where
)
}
#[allow(clippy::too_many_lines)]
fn on_event(
&mut self,
tree: &mut Tree,
@ -441,28 +445,170 @@ where
if reset {
if let Some(popup_id) = state.popup_id.get(&self.window_id).copied() {
dbg!("reset destroy");
// TODO emit message
if let Some(handler) = self.on_surface_action.as_ref() {
shell.publish((handler)(crate::surface::Action::DestroyPopup(popup_id)));
state.reset();
}
}
}
});
let tree = &mut state.tree;
// let tree: &mut _ = &mut state.tree;
match event {
Mouse(ButtonReleased(Left)) | Touch(FingerLifted { .. } | FingerLost { .. }) => {
match event {
Mouse(ButtonReleased(Left)) | Touch(FingerLifted { .. } | FingerLost { .. }) => {
my_state.inner.with_data_mut(|state| {
if state.menu_states.is_empty() && view_cursor.is_over(layout.bounds()) {
state.view_cursor = view_cursor;
state.open = true;
// #[cfg(feature = "wayland")]
// TODO emit Message to open menu
}
});
#[cfg(all(feature = "wayland", feature = "winit"))]
dbg!(
self.window_id != window::Id::NONE,
self.on_surface_action.is_some()
);
// TODO emit Message to open menu
if self.window_id != window::Id::NONE && self.on_surface_action.is_some() {
use crate::surface::action::destroy_popup;
use iced_runtime::platform_specific::wayland::popup::{
SctkPopupSettings, SctkPositioner,
};
let surface_action = self.on_surface_action.as_ref().unwrap();
let old_active_root = my_state.inner.with_data(|state| {
state
.active_root
.get(0)
.filter(|r| r.len() == 1)
.map(|r| r[0])
});
// if position is not on menu bar button skip.
let position = view_cursor.position();
let hovered_root = layout
.children()
.position(|lo| view_cursor.is_over(lo.bounds()));
dbg!(old_active_root);
// TODO why exit here?
// if hovered_root
// .zip(old_active_root.as_ref())
// .is_none_or(|r| r.0 != *r.1)
// {
// panic!();
// }
let (id, root_list) = my_state.inner.with_data_mut(|state| {
if let Some(id) = state.popup_id.get(&self.window_id).copied() {
// close existing popups
state.menu_states.clear();
state.active_root.clear();
shell.publish(surface_action(destroy_popup(id)));
state.view_cursor = view_cursor;
(id, layout.children().map(|lo| lo.bounds()).collect())
} else {
(
window::Id::unique(),
layout.children().map(|lo| lo.bounds()).collect(),
)
}
});
let mut popup_menu: Menu<'static, _> = Menu {
tree: my_state.clone(),
menu_roots: std::borrow::Cow::Owned(self.menu_roots.clone()),
bounds_expand: self.bounds_expand,
menu_overlays_parent: false,
close_condition: self.close_condition,
item_width: self.item_width,
item_height: self.item_height,
bar_bounds: layout.bounds(),
main_offset: self.main_offset,
cross_offset: self.cross_offset,
root_bounds_list: root_list,
path_highlight: self.path_highlight,
style: std::borrow::Cow::Owned(self.style.clone()),
position: Point::new(0., 0.),
is_overlay: false,
window_id: id,
depth: 0,
};
init_root_menu(
&mut popup_menu,
renderer,
shell,
view_cursor.position().unwrap(),
viewport.size(),
Vector::new(0., 0.),
layout.bounds(),
self.main_offset as f32,
);
let anchor_rect = my_state.inner.with_data(|state| {
state.menu_states[0]
.iter()
.find(|s| s.index.is_none())
.map(|s| s.menu_bounds.parent_bounds)
.map_or_else(
|| {
let bounds = layout.bounds();
Rectangle {
x: bounds.x as i32,
y: bounds.y as i32,
width: bounds.width as i32,
height: bounds.height as i32,
}
},
|r| Rectangle {
x: r.x as i32,
y: r.y as i32,
width: r.width as i32,
height: r.height as i32,
},
)
});
let menu_node =
popup_menu.layout(renderer, Limits::NONE.min_width(1.).min_height(1.));
let popup_size = menu_node.size();
let positioner = SctkPositioner {
size: Some((popup_size.width.ceil() as u32 + 2, popup_size.height.ceil() as u32 + 2)),
anchor_rect,
anchor: cctk::wayland_protocols::xdg::shell::client::xdg_positioner::Anchor::BottomLeft,
gravity:cctk::wayland_protocols::xdg::shell::client::xdg_positioner::Gravity::BottomRight,
reactive: true,
..Default::default()
};
let parent = self.window_id;
shell.publish((surface_action)(crate::surface::action::simple_popup(
move || SctkPopupSettings {
parent,
id,
positioner: positioner.clone(),
parent_size: None,
grab: true,
close_with_children: true,
input_zone: None,
},
Some(move || {
Element::from(
crate::widget::container(popup_menu.clone()).center(Length::Fill),
)
.map(crate::action::app)
}),
)));
}
_ => (),
}
});
// Window(Focused) => {
// if let Some(popup_id) = state.popup_id.get(&self.window_id).copied() {
// dbg!("window focused");
// shell.publish(Message::from(SurfaceMessage::DestroyPopup(popup_id)));
// }
// state.reset();
// }
_ => (),
}
root_status
}
@ -533,8 +679,8 @@ where
_renderer: &Renderer,
translation: Vector,
) -> Option<overlay::Element<'b, Message, crate::Theme, Renderer>> {
//#[cfg(feature = "wayland")]
//return None;
#[cfg(all(feature = "wayland", feature = "winit"))]
return None;
let state = tree.state.downcast_ref::<MenuBarState>();
if state

View file

@ -232,11 +232,11 @@ pub(super) struct MenuSlice {
#[derive(Clone)]
/// Menu bounds in overlay space
struct MenuBounds {
pub struct MenuBounds {
child_positions: Vec<f32>,
child_sizes: Vec<Size>,
children_bounds: Rectangle,
parent_bounds: Rectangle,
pub parent_bounds: Rectangle,
check_bounds: Rectangle,
offset_bounds: Rectangle,
}
@ -299,7 +299,7 @@ pub(crate) struct MenuState {
/// The index of the active menu item
pub(super) index: Option<usize>,
scroll_offset: f32,
menu_bounds: MenuBounds,
pub menu_bounds: MenuBounds,
}
impl MenuState {
pub(super) fn layout<Message>(
@ -444,6 +444,7 @@ impl MenuState {
}
}
#[derive(Clone)]
pub(crate) struct Menu<'b, Message: std::clone::Clone> {
pub(crate) tree: MenuBarState,
// Flattened menu tree
@ -471,7 +472,7 @@ impl<'b, Message: Clone + 'static> Menu<'b, Message> {
overlay::Element::new(Box::new(self))
}
fn layout(&mut self, renderer: &crate::Renderer, limits: Limits) -> Node {
pub(crate) fn layout(&self, renderer: &crate::Renderer, limits: Limits) -> Node {
// layout children;
let position = self.position;
let mut intrinsic_size = Size::ZERO;
@ -889,6 +890,207 @@ impl<Message: Clone + 'static> overlay::Overlay<Message, crate::Theme, crate::Re
}
}
impl<'a, Message: std::clone::Clone + 'static> Widget<Message, crate::Theme, crate::Renderer>
for Menu<'a, Message>
{
fn size(&self) -> Size<Length> {
Size {
width: Length::Shrink,
height: Length::Shrink,
}
}
fn layout(
&self,
_tree: &mut Tree,
renderer: &crate::Renderer,
limits: &iced_core::layout::Limits,
) -> iced_core::layout::Node {
// dbg!(self.window_id, limits);
Menu::layout(self, renderer, *limits)
}
fn draw(
&self,
_tree: &Tree,
renderer: &mut crate::Renderer,
theme: &crate::Theme,
style: &renderer::Style,
layout: Layout<'_>,
cursor: mouse::Cursor,
_viewport: &Rectangle,
) {
Menu::draw(self, renderer, theme, style, layout, cursor);
}
fn on_event(
&mut self,
_tree: &mut Tree,
event: iced::Event,
layout: Layout<'_>,
cursor: mouse::Cursor,
renderer: &crate::Renderer,
clipboard: &mut dyn Clipboard,
shell: &mut Shell<'_, Message>,
_viewport: &Rectangle,
) -> event::Status {
let (new_root, status) = self.on_event(event, layout, cursor, renderer, clipboard, shell);
// #[cfg(feature = "wayland")]
// if let Some((new_root, new_ms)) = new_root {
// use iced_runtime::platform_specific::wayland::popup::{
// SctkPopupSettings, SctkPositioner,
// };
// let mut guard = self.tree.inner.lock().unwrap();
// let popup_id = *guard
// .popup_id
// .entry(self.window_id)
// .or_insert_with(window::Id::unique);
// let active_roots = &guard.active_root[&self.window_id];
// // dbg!(active_roots);
// let root_bounds_list = active_roots
// .into_iter()
// .fold(layout, |l, active_root| {
// // dbg!(active_root);
// l.children().nth(*active_root).unwrap()
// })
// .children()
// .map(|c| c.bounds())
// .collect();
// drop(guard);
// // dbg!(&root_bounds_list);
// // dbg!(self.menu_roots.clone().len());
// // dbg!(self
// // .menu_roots
// // .clone()
// // .iter()
// // .map(|r| r.index)
// // .collect::<Vec<_>>());
// let mut popup_menu = Menu {
// tree: self.tree.clone(),
// menu_roots: Cow::Owned(Cow::into_owned(self.menu_roots.clone())),
// bounds_expand: self.bounds_expand,
// menu_overlays_parent: false,
// close_condition: self.close_condition,
// item_width: self.item_width,
// item_height: self.item_height,
// bar_bounds: layout.bounds(),
// main_offset: self.main_offset,
// cross_offset: self.cross_offset,
// root_bounds_list,
// path_highlight: self.path_highlight,
// style: Cow::Owned(Cow::into_owned(self.style.clone())),
// position: Point::new(0., 0.),
// is_overlay: false,
// window_id: popup_id,
// depth: self.depth + 1,
// };
// let mut guard = self.tree.inner.lock().unwrap();
// // dbg!(guard.menu_states.keys());
// let Some(parent_root) = guard.active_root.get(&self.window_id) else {
// // TODO log warning
// return status;
// };
// let mut roots = parent_root.clone();
// roots.push(new_root);
// // dbg!(&roots);
// guard.active_root.insert(popup_id, roots);
// _ = guard.menu_states.remove(&popup_id);
// drop(guard);
// init_root_popup_menu(
// &mut popup_menu,
// renderer,
// shell,
// cursor.position().unwrap(),
// layout.bounds().size(),
// Vector::new(0., 0.),
// layout.bounds(),
// self.main_offset as f32,
// );
// let mut guard = self.tree.inner.lock().unwrap();
// guard
// .menu_states
// .get_mut(&self.window_id)
// .unwrap()
// .push(new_ms);
// let anchor_rect = guard.menu_states[&self.window_id]
// .iter()
// .find(|s| s.index.is_none())
// .map(|s| s.menu_bounds.parent_bounds)
// .map_or_else(
// || {
// let bounds = layout.bounds();
// Rectangle {
// x: bounds.x as i32,
// y: bounds.y as i32,
// width: bounds.width as i32,
// height: bounds.height as i32,
// }
// },
// |r| Rectangle {
// x: r.x as i32,
// y: r.y as i32,
// width: r.width as i32,
// height: r.height as i32,
// },
// );
// // dbg!(&anchor_rect);
// drop(guard);
// let menu_node = Widget::layout(
// &popup_menu,
// &mut Tree::empty(),
// renderer,
// &Limits::NONE.min_width(1.).min_height(1.),
// );
// // dbg!(menu_node.size());
// // dbg!(&menu_node);
// let popup_size = menu_node.size();
// let positioner = SctkPositioner {
// size: Some((popup_size.width.ceil() as u32 + 2, popup_size.height.ceil() as u32 + 2)),
// anchor_rect,
// anchor: cctk::wayland_protocols::xdg::shell::client::xdg_positioner::Anchor::TopRight,
// gravity:cctk::wayland_protocols::xdg::shell::client::xdg_positioner::Gravity::BottomRight,
// reactive: true,
// ..Default::default()
// };
// let parent = self.window_id;
// // dbg!(&positioner);
// shell.publish(crate::app::message::simple_popup(
// move || SctkPopupSettings {
// parent,
// id: popup_id,
// positioner: positioner.clone(),
// parent_size: None,
// grab: true,
// close_with_children: true,
// },
// Some(move || {
// crate::Element::from(
// crate::widget::container(popup_menu.clone()).center(Length::Fill),
// )
// .map(crate::app::Message::App)
// }),
// ));
// }
status
}
}
impl<'a, Message> From<Menu<'a, Message>>
for iced::Element<'a, Message, crate::Theme, crate::Renderer>
where
Message: std::clone::Clone + 'static,
{
fn from(value: Menu<'a, Message>) -> Self {
Self::new(value)
}
}
fn pad_rectangle(rect: Rectangle, padding: Padding) -> Rectangle {
Rectangle {
x: rect.x - padding.left,
@ -1194,34 +1396,48 @@ fn process_menu_events<'b, Message: std::clone::Clone>(
Cow::Owned(o) => o.as_mut_slice(),
};
my_state.inner.with_data_mut(|state| {
dbg!(&state.active_root);
let Some(active_root) = state.active_root.get(menu.depth).cloned() else {
return Status::Ignored;
};
dbg!("got the active root");
let indices = state.get_trimmed_indices(menu.depth);
let indices = if is_overlay {
indices.collect::<Vec<_>>()
} else {
indices.take(1).collect::<Vec<_>>()
};
let indices = indices.collect::<Vec<_>>();
// if is_overlay {
// indices.collect::<Vec<_>>()
// } else {
// indices.take(1).collect::<Vec<_>>()
// };
if indices.is_empty() {
return Status::Ignored;
}
dbg!(&active_root, &indices);
// get active item
// let mt = indices.iter().fold(root | mt, &i | &mut mt.children[i]);
let (tree, mt) = active_root.iter().take(menu.depth).fold(
let (tree, mt) = indices.iter().take(indices.len()).fold(
(
&mut state.tree.children[active_root[0]].children,
&mut menu_roots[active_root[0]],
),
|(tree, mt), next_active_root| (tree, &mut mt.children[*next_active_root]),
);
dbg!(mt.children.len(), mt.index,);
// let Some(i) = state.menu_states[menu.depth].iter().position(|ms| {
// ms.menu_bounds
// .check_bounds
// .contains(view_cursor.position().unwrap_or(Point { x: -1., y: -1. }))
// }) else {
// return Status::Ignored;
// };
// let mt = &mut mt.children[indices[indices.len() - 1]];
// widget tree
let tree = &mut tree[mt.index];
dbg!(tree.children.len());
// get layout
let last_ms = &state.menu_states[menu.depth][indices.len() - 1];
@ -1234,7 +1450,9 @@ fn process_menu_events<'b, Message: std::clone::Clone>(
);
let child_layout = Layout::new(&child_node);
dbg!("item on event handler...");
// process only the last widget
dbg!(&event);
mt.item.on_event(
tree,
event,
@ -1369,13 +1587,7 @@ where
- (last_children_bounds.y + last_menu_state.scroll_offset))
.clamp(0.0, last_children_bounds.height - 0.001);
// dbg!(height_diff);
// let (tree, active_menu_root) = active_root.iter().skip(1).fold(
// (
// &mut state.tree.children[active_root[0]].children,
// &menu.menu_roots[active_root[0]],
// ),
// |(tree, mr), next_active_root| (tree, &mr.children[*next_active_root]),
// );
let (active_tree, roots) = active_root.iter().take(menu.depth).fold(
(
&mut state.tree.children[active_root[0]].children,

View file

@ -213,7 +213,7 @@ where
pub fn menu_items<
A: MenuAction<Message = Message>,
L: Into<Cow<'static, str>> + 'static,
Message: 'static + std::clone::Clone,
Message: 'static + std::clone::Clone + std::fmt::Debug,
>(
key_binds: &HashMap<KeyBind, A>,
children: Vec<MenuItem<A, L>>,
@ -238,9 +238,10 @@ pub fn menu_items<
match item {
MenuItem::Button(label, icon, action) => {
let l: Cow<'static, str> = label.into();
let key = find_key(&action, key_binds);
let mut items = vec![
widget::text(label).into(),
widget::text(l.clone()).into(),
widget::horizontal_space().into(),
widget::text(key).into(),
];
@ -250,15 +251,18 @@ pub fn menu_items<
items.insert(1, widget::Space::with_width(spacing.space_xxs).into());
}
let menu_button = menu_button(items).on_press(action.message());
// dbg!("button with action...", action.message());
let menu_button = menu_button(items).on_press(action.message()).description(l);
trees.push(MenuTree::<Message>::from(Element::from(menu_button)));
}
MenuItem::ButtonDisabled(label, icon, action) => {
let l: Cow<'static, str> = label.into();
let key = find_key(&action, key_binds);
let mut items = vec![
widget::text(label).into(),
widget::text(l.clone()).into(),
widget::horizontal_space().into(),
widget::text(key).into(),
];
@ -268,7 +272,7 @@ pub fn menu_items<
items.insert(1, widget::Space::with_width(spacing.space_xxs).into());
}
let menu_button = menu_button(items);
let menu_button = menu_button(items).description((l.clone()));
trees.push(MenuTree::<Message>::from(Element::from(menu_button)));
}
@ -305,16 +309,19 @@ pub fn menu_items<
)));
}
MenuItem::Folder(label, children) => {
let l: Cow<'static, str> = label.into();
trees.push(MenuTree::<Message>::with_children(
RcElementWrapper::new(crate::Element::from(
menu_button::<'static, _>(vec![
widget::text(label).into(),
widget::text(l.clone()).into(),
widget::horizontal_space().into(),
widget::icon::from_name("pan-end-symbolic")
.size(16)
.icon()
.into(),
])
.description(l.clone())
.class(
// Menu folders have no on_press so they take on the disabled style by default
if children.is_empty() {

View file

@ -97,6 +97,7 @@ impl ResponsiveMenuBar {
.item_width(self.item_width)
.item_height(self.item_height)
.spacing(self.spacing)
.on_surface_action(action_message.clone())
.window_id_maybe(core.main_window_id()),
crate::widget::Id::new(format!("menu_bar_expanded_{id}")),
),
@ -131,6 +132,7 @@ impl ResponsiveMenuBar {
.item_height(self.item_height)
.item_width(self.collapsed_item_width)
.spacing(self.spacing)
.on_surface_action(action_message.clone())
.window_id_maybe(core.main_window_id()),
crate::widget::Id::new(format!("menu_bar_collapsed_{id}")),
),