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)] #[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Action { pub enum Action {
Hi, Hi,
Hi2,
Hi3,
} }
impl MenuAction for Action { impl MenuAction for Action {
type Message = Message; type Message = Message;
fn message(&self) -> 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, ToggleHide,
Surface(cosmic::surface::Action), Surface(cosmic::surface::Action),
Hi, Hi,
Hi2,
Hi3,
} }
/// The [`App`] stores application-specific state. /// The [`App`] stores application-specific state.
@ -176,6 +184,12 @@ impl cosmic::Application for App {
Message::Hi => { Message::Hi => {
dbg!("hi"); dbg!("hi");
} }
Message::Hi2 => {
dbg!("hi 2");
}
Message::Hi3 => {
dbg!("hi 3");
}
} }
Task::none() Task::none()
} }
@ -222,123 +236,60 @@ impl cosmic::Application for App {
fn header_start(&self) -> Vec<Element<Self::Message>> { fn header_start(&self) -> Vec<Element<Self::Message>> {
use cosmic::widget::menu::Tree; 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![ vec![
cosmic::widget::menu::bar(vec![ (
Tree::with_children( "hi 1".into(),
menu::root("hiiiiiiiiiiiiiiiiiii 1"), vec![
menu::items( menu::Item::Button("hi 12".into(), None, Action::Hi),
&self.keybinds, menu::Item::Button("hi 13".into(), None, Action::Hi2),
vec![menu::Item::Button("hi", None, Action::Hi)], ],
), ),
), (
Tree::with_children( "hi 2".into(),
menu::root("hiiiiiiiiiiiiiiiiii 2"), vec![
menu::items( menu::Item::Button("hi 21".into(), None, Action::Hi),
&self.keybinds, menu::Item::Button("hi 22".into(), None, Action::Hi2),
vec![menu::Item::Button("hi 2", None, Action::Hi)], ],
), ),
), (
Tree::with_children( "hi 3".into(),
menu::root("hiiiiiiiiiiiiiiiiiiiii 3"), vec![
menu::items( menu::Item::Button("hi 33".into(), None, Action::Hi),
&self.keybinds, menu::Item::Button("hi 333".into(), None, Action::Hi2),
vec![ menu::Item::Button("hi 3333".into(), None, Action::Hi3),
menu::Item::Button("hi 3", None, Action::Hi), ],
menu::Item::Button("hi 3 #2", None, Action::Hi), ),
], (
), "hiiiiiiiiiiiiiiiiiii 4".into(),
), vec![
Tree::with_children( menu::Item::Button("hi 4".into(), None, Action::Hi),
menu::root("hi 3"), menu::Item::Button("hi 44".into(), None, Action::Hi2),
menu::items( menu::Item::Button("hi 444".into(), None, Action::Hi3),
&self.keybinds, menu::Item::Folder(
vec![ "nest 4".into(),
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,
vec![ 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( menu::Item::Folder(
"hi 41 extra root", "nest 2 4".into(),
vec![menu::Item::Button("hi 3", None, Action::Hi)], 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. /// Used to create a popup message from within a widget.
#[cfg(all(feature = "wayland", feature = "winit"))] #[cfg(all(feature = "wayland", feature = "winit"))]
#[must_use] #[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 settings: impl Fn() -> iced_runtime::platform_specific::wayland::popup::SctkPopupSettings
+ Send + Send
+ Sync + Sync

View file

@ -42,7 +42,7 @@ pub enum MenuBarStyle {
#[default] #[default]
Default, Default,
/// A [`Theme`] that uses a `Custom` palette. /// 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 { 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_close = surface::action::destroy_popup(id);
let on_surface_action_clone = on_surface_action.clone(); let on_surface_action_clone = on_surface_action.clone();
let translation = layout.virtual_offset(); let translation = layout.virtual_offset();
let get_popup_action = surface::action::simple_popup::< let get_popup_action = surface::action::simple_popup::<AppMessage>(
AppMessage,
Box<
dyn Fn() -> Element<'static, crate::Action<AppMessage>>
+ Send
+ Sync
+ 'static,
>,
>(
move || { move || {
SctkPopupSettings { SctkPopupSettings {
parent, parent,

View file

@ -15,6 +15,7 @@ use crate::{
widget::{ widget::{
RcWrapper, RcWrapper,
dropdown::menu::{self, State}, 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>>, pub(crate) menu_states: Vec<Vec<MenuState>>,
} }
impl MenuBarStateInner { impl MenuBarStateInner {
/// get the list of indices hovered for the menu
pub(super) fn get_trimmed_indices(&self, index: usize) -> impl Iterator<Item = usize> + '_ { pub(super) fn get_trimmed_indices(&self, index: usize) -> impl Iterator<Item = usize> + '_ {
self.menu_states self.menu_states
.get(index) .get(index)
@ -188,7 +190,7 @@ pub struct MenuBar<Message> {
menu_roots: Vec<MenuTree<Message>>, menu_roots: Vec<MenuTree<Message>>,
style: <crate::Theme as StyleSheet>::Style, style: <crate::Theme as StyleSheet>::Style,
window_id: window::Id, window_id: window::Id,
#[cfg(feature = "wayland")] #[cfg(all(feature = "wayland", feature = "winit"))]
positioner: iced_runtime::platform_specific::wayland::popup::SctkPositioner, positioner: iced_runtime::platform_specific::wayland::popup::SctkPositioner,
pub(crate) on_surface_action: Option<Box<dyn Fn(crate::surface::Action) -> Message>>, pub(crate) on_surface_action: Option<Box<dyn Fn(crate::surface::Action) -> Message>>,
} }
@ -228,7 +230,7 @@ where
menu_roots, menu_roots,
style: <crate::Theme as StyleSheet>::Style::default(), style: <crate::Theme as StyleSheet>::Style::default(),
window_id: window::Id::NONE, window_id: window::Id::NONE,
#[cfg(feature = "wayland")] #[cfg(all(feature = "wayland", feature = "winit"))]
positioner: iced_runtime::platform_specific::wayland::popup::SctkPositioner::default(), positioner: iced_runtime::platform_specific::wayland::popup::SctkPositioner::default(),
on_surface_action: None, on_surface_action: None,
} }
@ -322,7 +324,8 @@ where
self self
} }
#[cfg(feature = "wayland")] #[cfg(all(feature = "wayland", feature = "winit"))]
pub fn with_positioner( pub fn with_positioner(
mut self, mut self,
positioner: iced_runtime::platform_specific::wayland::popup::SctkPositioner, positioner: iced_runtime::platform_specific::wayland::popup::SctkPositioner,
@ -405,6 +408,7 @@ where
) )
} }
#[allow(clippy::too_many_lines)]
fn on_event( fn on_event(
&mut self, &mut self,
tree: &mut Tree, tree: &mut Tree,
@ -441,28 +445,170 @@ where
if reset { if reset {
if let Some(popup_id) = state.popup_id.get(&self.window_id).copied() { if let Some(popup_id) = state.popup_id.get(&self.window_id).copied() {
dbg!("reset destroy"); dbg!("reset destroy");
// TODO emit message
if let Some(handler) = self.on_surface_action.as_ref() { if let Some(handler) = self.on_surface_action.as_ref() {
shell.publish((handler)(crate::surface::Action::DestroyPopup(popup_id))); shell.publish((handler)(crate::surface::Action::DestroyPopup(popup_id)));
state.reset(); state.reset();
} }
} }
} }
});
let tree = &mut state.tree; // let tree: &mut _ = &mut state.tree;
match event { match event {
Mouse(ButtonReleased(Left)) | Touch(FingerLifted { .. } | FingerLost { .. }) => { 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()) { if state.menu_states.is_empty() && view_cursor.is_over(layout.bounds()) {
state.view_cursor = view_cursor; state.view_cursor = view_cursor;
state.open = true; 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 root_status
} }
@ -533,8 +679,8 @@ where
_renderer: &Renderer, _renderer: &Renderer,
translation: Vector, translation: Vector,
) -> Option<overlay::Element<'b, Message, crate::Theme, Renderer>> { ) -> Option<overlay::Element<'b, Message, crate::Theme, Renderer>> {
//#[cfg(feature = "wayland")] #[cfg(all(feature = "wayland", feature = "winit"))]
//return None; return None;
let state = tree.state.downcast_ref::<MenuBarState>(); let state = tree.state.downcast_ref::<MenuBarState>();
if state if state

View file

@ -232,11 +232,11 @@ pub(super) struct MenuSlice {
#[derive(Clone)] #[derive(Clone)]
/// Menu bounds in overlay space /// Menu bounds in overlay space
struct MenuBounds { pub struct MenuBounds {
child_positions: Vec<f32>, child_positions: Vec<f32>,
child_sizes: Vec<Size>, child_sizes: Vec<Size>,
children_bounds: Rectangle, children_bounds: Rectangle,
parent_bounds: Rectangle, pub parent_bounds: Rectangle,
check_bounds: Rectangle, check_bounds: Rectangle,
offset_bounds: Rectangle, offset_bounds: Rectangle,
} }
@ -299,7 +299,7 @@ pub(crate) struct MenuState {
/// The index of the active menu item /// The index of the active menu item
pub(super) index: Option<usize>, pub(super) index: Option<usize>,
scroll_offset: f32, scroll_offset: f32,
menu_bounds: MenuBounds, pub menu_bounds: MenuBounds,
} }
impl MenuState { impl MenuState {
pub(super) fn layout<Message>( pub(super) fn layout<Message>(
@ -444,6 +444,7 @@ impl MenuState {
} }
} }
#[derive(Clone)]
pub(crate) struct Menu<'b, Message: std::clone::Clone> { pub(crate) struct Menu<'b, Message: std::clone::Clone> {
pub(crate) tree: MenuBarState, pub(crate) tree: MenuBarState,
// Flattened menu tree // Flattened menu tree
@ -471,7 +472,7 @@ impl<'b, Message: Clone + 'static> Menu<'b, Message> {
overlay::Element::new(Box::new(self)) 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; // layout children;
let position = self.position; let position = self.position;
let mut intrinsic_size = Size::ZERO; 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 { fn pad_rectangle(rect: Rectangle, padding: Padding) -> Rectangle {
Rectangle { Rectangle {
x: rect.x - padding.left, 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(), Cow::Owned(o) => o.as_mut_slice(),
}; };
my_state.inner.with_data_mut(|state| { my_state.inner.with_data_mut(|state| {
dbg!(&state.active_root);
let Some(active_root) = state.active_root.get(menu.depth).cloned() else { let Some(active_root) = state.active_root.get(menu.depth).cloned() else {
return Status::Ignored; return Status::Ignored;
}; };
dbg!("got the active root");
let indices = state.get_trimmed_indices(menu.depth); let indices = state.get_trimmed_indices(menu.depth);
let indices = if is_overlay { let indices = indices.collect::<Vec<_>>();
indices.collect::<Vec<_>>() // if is_overlay {
} else { // indices.collect::<Vec<_>>()
indices.take(1).collect::<Vec<_>>() // } else {
}; // indices.take(1).collect::<Vec<_>>()
// };
if indices.is_empty() { if indices.is_empty() {
return Status::Ignored; return Status::Ignored;
} }
dbg!(&active_root, &indices);
// get active item // get active item
// let mt = indices.iter().fold(root | mt, &i | &mut mt.children[i]); // 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 state.tree.children[active_root[0]].children,
&mut menu_roots[active_root[0]], &mut menu_roots[active_root[0]],
), ),
|(tree, mt), next_active_root| (tree, &mut mt.children[*next_active_root]), |(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 // widget tree
let tree = &mut tree[mt.index]; let tree = &mut tree[mt.index];
dbg!(tree.children.len());
// get layout // get layout
let last_ms = &state.menu_states[menu.depth][indices.len() - 1]; 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); let child_layout = Layout::new(&child_node);
dbg!("item on event handler...");
// process only the last widget // process only the last widget
dbg!(&event);
mt.item.on_event( mt.item.on_event(
tree, tree,
event, event,
@ -1369,13 +1587,7 @@ where
- (last_children_bounds.y + last_menu_state.scroll_offset)) - (last_children_bounds.y + last_menu_state.scroll_offset))
.clamp(0.0, last_children_bounds.height - 0.001); .clamp(0.0, last_children_bounds.height - 0.001);
// dbg!(height_diff); // 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( let (active_tree, roots) = active_root.iter().take(menu.depth).fold(
( (
&mut state.tree.children[active_root[0]].children, &mut state.tree.children[active_root[0]].children,

View file

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

View file

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