This commit is contained in:
Ashley Wulber 2025-05-12 10:16:19 -04:00
parent 2cfef8814e
commit 8642a92270
No known key found for this signature in database
GPG key ID: 5216D4F46A90A820
3 changed files with 124 additions and 120 deletions

View file

@ -7,11 +7,11 @@ use std::collections::HashMap;
use std::sync::LazyLock; use std::sync::LazyLock;
use cosmic::app::{Core, Settings, Task}; use cosmic::app::{Core, Settings, Task};
use cosmic::iced::Length;
use cosmic::iced::alignment::{Horizontal, Vertical}; use cosmic::iced::alignment::{Horizontal, Vertical};
use cosmic::iced::widget::column; use cosmic::iced::widget::column;
use cosmic::iced::Length;
use cosmic::iced_core::Size; use cosmic::iced_core::Size;
use cosmic::widget::icon::{from_name, Handle}; use cosmic::widget::icon::{Handle, from_name};
use cosmic::widget::menu::KeyBind; use cosmic::widget::menu::KeyBind;
use cosmic::widget::{button, text}; use cosmic::widget::{button, text};
use cosmic::widget::{ use cosmic::widget::{
@ -20,7 +20,7 @@ use cosmic::widget::{
menu::{self, action::MenuAction}, menu::{self, action::MenuAction},
nav_bar, responsive, nav_bar, responsive,
}; };
use cosmic::{executor, iced, ApplicationExt, Element}; use cosmic::{ApplicationExt, Element, executor, iced};
static MENU_ID: LazyLock<iced::id::Id> = LazyLock::new(|| iced::id::Id::new("menu_id")); static MENU_ID: LazyLock<iced::id::Id> = LazyLock::new(|| iced::id::Id::new("menu_id"));
@ -224,61 +224,63 @@ impl cosmic::Application for App {
use cosmic::widget::menu::Tree; use cosmic::widget::menu::Tree;
#[cfg(not(feature = "wayland"))] #[cfg(not(feature = "wayland"))]
{ {
vec![cosmic::widget::menu::bar(vec![ vec![
Tree::with_children( cosmic::widget::menu::bar(vec![
menu::root("hiiiiiiiiiiiiiiiiiii 1"), Tree::with_children(
menu::items( menu::root("hiiiiiiiiiiiiiiiiiii 1"),
&self.keybinds, menu::items(
vec![menu::Item::Button("hi", None, Action::Hi)], &self.keybinds,
vec![menu::Item::Button("hi", None, Action::Hi)],
),
), ),
), Tree::with_children(
Tree::with_children( menu::root("hiiiiiiiiiiiiiiiiii 2"),
menu::root("hiiiiiiiiiiiiiiiiii 2"), menu::items(
menu::items( &self.keybinds,
&self.keybinds, vec![menu::Item::Button("hi 2", None, Action::Hi)],
vec![menu::Item::Button("hi 2", None, Action::Hi)], ),
), ),
), Tree::with_children(
Tree::with_children( menu::root("hiiiiiiiiiiiiiiiiiiiii 3"),
menu::root("hiiiiiiiiiiiiiiiiiiiii 3"), menu::items(
menu::items( &self.keybinds,
&self.keybinds, vec![
vec![ menu::Item::Button("hi 3", None, Action::Hi),
menu::Item::Button("hi 3", None, Action::Hi), menu::Item::Button("hi 3 #2", None, Action::Hi),
menu::Item::Button("hi 3 #2", None, Action::Hi), ],
], ),
), ),
), Tree::with_children(
Tree::with_children( menu::root("hi 3"),
menu::root("hi 3"), menu::items(
menu::items( &self.keybinds,
&self.keybinds, vec![
vec![ menu::Item::Button("hi 3", None, Action::Hi),
menu::Item::Button("hi 3", None, Action::Hi), menu::Item::Button("hi 3 #2", None, Action::Hi),
menu::Item::Button("hi 3 #2", None, Action::Hi), menu::Item::Button("hi 3 #3", None, Action::Hi),
menu::Item::Button("hi 3 #3", None, Action::Hi), ],
], ),
), ),
), Tree::with_children(
Tree::with_children( menu::root("hi 4"),
menu::root("hi 4"), menu::items(
menu::items( &self.keybinds,
&self.keybinds, vec![
vec![ menu::Item::Folder(
menu::Item::Folder( "hi 41 extra root",
"hi 41 extra root", vec![menu::Item::Button("hi 3", None, Action::Hi)],
vec![menu::Item::Button("hi 3", None, Action::Hi)], ),
), menu::Item::Button("hi 42", None, Action::Hi),
menu::Item::Button("hi 42", None, Action::Hi), menu::Item::Button("hi 43", None, Action::Hi),
menu::Item::Button("hi 43", None, Action::Hi), menu::Item::Button("hi 44", None, Action::Hi),
menu::Item::Button("hi 44", None, Action::Hi), menu::Item::Button("hi 45", None, Action::Hi),
menu::Item::Button("hi 45", None, Action::Hi), menu::Item::Button("hi 46", None, Action::Hi),
menu::Item::Button("hi 46", None, Action::Hi), ],
], ),
), ),
), ])
]) .into(),
.into()] ]
} }
#[cfg(feature = "wayland")] #[cfg(feature = "wayland")]
{ {
@ -289,18 +291,21 @@ impl cosmic::Application for App {
Message::Surface, Message::Surface,
vec![ vec![
( (
"hiiiiiiiiiiiiiiiiiii 1", "hi 1".into(),
vec![menu::Item::Button("hi 1", None, Action::Hi)], vec![
menu::Item::Button("hi 1".into(), None, Action::Hi),
menu::Item::Button("hi 1 2".into(), None, Action::Hi),
],
), ),
( (
"hiiiiiiiiiiiiiiiiiii 2".into(), "hi 2".into(),
vec![ vec![
menu::Item::Button("hi 2", None, Action::Hi), menu::Item::Button("hi 2", None, Action::Hi),
menu::Item::Button("hi 22", None, Action::Hi), menu::Item::Button("hi 22", None, Action::Hi),
], ],
), ),
( (
"hiiiiiiiiiiiiiiiiiii 3".into(), "hi 3".into(),
vec![ vec![
menu::Item::Button("hi 3", None, Action::Hi), menu::Item::Button("hi 3", None, Action::Hi),
menu::Item::Button("hi 33", None, Action::Hi), menu::Item::Button("hi 33", None, Action::Hi),

View file

@ -53,6 +53,7 @@ pub(crate) struct MenuBarStateInner {
pub(crate) active_root: Vec<Vec<usize>>, pub(crate) active_root: Vec<Vec<usize>>,
pub(crate) horizontal_direction: Direction, pub(crate) horizontal_direction: Direction,
pub(crate) vertical_direction: Direction, pub(crate) vertical_direction: Direction,
/// List of all menu states
pub(crate) menu_states: Vec<Vec<MenuState>>, pub(crate) menu_states: Vec<Vec<MenuState>>,
} }
impl MenuBarStateInner { impl MenuBarStateInner {
@ -559,7 +560,7 @@ where
path_highlight: self.path_highlight, path_highlight: self.path_highlight,
style: std::borrow::Cow::Borrowed(&self.style), style: std::borrow::Cow::Borrowed(&self.style),
position: Point::new(translation.x, translation.y), position: Point::new(translation.x, translation.y),
is_overlay: false, is_overlay: true,
window_id: window::Id::NONE, window_id: window::Id::NONE,
depth: 0, depth: 0,
} }

View file

@ -296,6 +296,7 @@ impl MenuBounds {
#[derive(Clone)] #[derive(Clone)]
pub(crate) struct MenuState { pub(crate) struct MenuState {
/// 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, menu_bounds: MenuBounds,
@ -320,12 +321,7 @@ impl MenuState {
// menu_tree.children.len(), // menu_tree.children.len(),
// self.menu_bounds.child_positions.len() // self.menu_bounds.child_positions.len()
// ); // );
// dbg!(
// overlay_offset,
// menu_tree.index,
// menu_tree.width,
// menu_tree.height
// );
// for mt in &menu_tree.children[start_index..=end_index] { // for mt in &menu_tree.children[start_index..=end_index] {
// dbg!(mt.index); // dbg!(mt.index);
// } // }
@ -360,13 +356,16 @@ impl MenuState {
} }
mt.item mt.item
.layout(&mut tree[i], renderer, &limits) .layout(&mut tree[mt.index], renderer, &limits)
.move_to(Point::new(0.0, position + self.scroll_offset)) .move_to(Point::new(0.0, position + self.scroll_offset))
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
// dbg!(children_bounds.size()); // dbg!(children_bounds.size());
Node::with_children(children_bounds.size(), child_nodes).move_to(children_bounds.position()) let node = Node::with_children(children_bounds.size(), child_nodes)
.move_to(children_bounds.position());
// dbg!(&node);
node
} }
fn layout_single<Message>( fn layout_single<Message>(
@ -390,6 +389,7 @@ impl MenuState {
)) ))
} }
/// returns a slice of the menu items that are inside the viewport
pub(super) fn slice( pub(super) fn slice(
&self, &self,
viewport_size: Size, viewport_size: Size,
@ -446,6 +446,7 @@ impl MenuState {
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
pub(crate) menu_roots: Cow<'b, Vec<MenuTree<Message>>>, pub(crate) menu_roots: Cow<'b, Vec<MenuTree<Message>>>,
pub(crate) bounds_expand: u16, pub(crate) bounds_expand: u16,
/// Allows menu overlay items to overlap the parent /// Allows menu overlay items to overlap the parent
@ -489,17 +490,13 @@ impl<'b, Message: Clone + 'static> Menu<'b, Message> {
if active_root.is_empty() || self.menu_roots.is_empty() { if active_root.is_empty() || self.menu_roots.is_empty() {
return (&empty, vec![]); return (&empty, vec![]);
} }
let (active_tree, roots) = if self.depth > 0 { let (active_tree, roots) = active_root.iter().take(self.depth).fold(
active_root.iter().take(self.depth).fold( (
( &mut tree_children[active_root[0]].children,
&mut tree_children[active_root[0]].children, &self.menu_roots[active_root[0]].children,
&self.menu_roots[active_root[0]].children, ),
), |(tree, mt), next_active_root| (tree, &mt[*next_active_root].children),
|(tree, mt), next_active_root| (tree, &mt[*next_active_root].children), );
)
} else {
(tree_children, &self.menu_roots[active_root[0]].children)
};
dbg!(roots.len()); dbg!(roots.len());
dbg!(&active_root); dbg!(&active_root);
@ -519,7 +516,7 @@ impl<'b, Message: Clone + 'static> Menu<'b, Message> {
// } // }
// } // }
// } // }
if let Some(ms) = data.menu_states.get(self.depth + 1) { if let Some(ms) = data.menu_states.get(self.depth) {
ms.iter() ms.iter()
.enumerate() .enumerate()
.filter(|ms| self.is_overlay || ms.0 < active_root.len()) .filter(|ms| self.is_overlay || ms.0 < active_root.len())
@ -667,7 +664,7 @@ impl<'b, Message: Clone + 'static> Menu<'b, Message> {
.distance(view_cursor.position().unwrap_or_default()) .distance(view_cursor.position().unwrap_or_default())
< 2.0 < 2.0
{ {
let is_inside = state.menu_states[self.depth + 1] let is_inside = state.menu_states[self.depth]
.iter() .iter()
.any(|ms| ms.menu_bounds.check_bounds.contains(overlay_cursor)); .any(|ms| ms.menu_bounds.check_bounds.contains(overlay_cursor));
@ -717,6 +714,7 @@ impl<'b, Message: Clone + 'static> Menu<'b, Message> {
// dbg!(self.window_id); // dbg!(self.window_id);
return; return;
}; };
dbg!(active_root);
let viewport = layout.bounds(); let viewport = layout.bounds();
let viewport_size = viewport.size(); let viewport_size = viewport.size();
let overlay_offset = Point::ORIGIN - viewport.position(); let overlay_offset = Point::ORIGIN - viewport.position();
@ -729,29 +727,33 @@ impl<'b, Message: Clone + 'static> Menu<'b, Message> {
// dbg!(self.window_id, &active_root); // dbg!(self.window_id, &active_root);
let styling = theme.appearance(&self.style); let styling = theme.appearance(&self.style);
let (active_tree, roots) = if self.depth > 0 { let (active_tree, roots) = active_root.iter().take(self.depth).fold(
active_root.iter().take(self.depth).fold( (
( &state.tree.children[active_root[0]].children,
&state.tree.children[active_root[0]].children, &self.menu_roots[active_root[0]].children,
&self.menu_roots[active_root[0]].children, ),
), |(tree, mt), next_active_root| (tree, &mt[*next_active_root].children),
|(tree, mt), next_active_root| (tree, &mt[*next_active_root].children), );
)
} else { dbg!(
(&state.tree.children, self.menu_roots.as_ref()) state.tree.children.len(),
}; state.tree.children[active_root[0]].children.len()
);
// let tree = &state.tree.children[active_root].children; // let tree = &state.tree.children[active_root].children;
// let root = &self.menu_roots[active_root]; // let root = &self.menu_roots[active_root];
dbg!(&active_root.len());
let indices = state.get_trimmed_indices(self.depth).collect::<Vec<_>>(); let indices = state.get_trimmed_indices(self.depth).collect::<Vec<_>>();
// dbg!( dbg!(self.depth);
// self.window_id,
// state.menu_states[&self.window_id].len(), dbg!(
// layout.children().count(), self.window_id,
// root.children.len(), state.menu_states.len(),
// layout.bounds() state.menu_states[self.depth].len(),
// ); layout.children().count(),
state.menu_states[self.depth + 1] layout.bounds()
);
state.menu_states[self.depth]
.iter() .iter()
.zip(layout.children()) .zip(layout.children())
.enumerate() .enumerate()
@ -819,6 +821,7 @@ impl<'b, Message: Clone + 'static> Menu<'b, Message> {
} }
} }
if start_index < menu_roots.len() { if start_index < menu_roots.len() {
dbg!(start_index, end_index, menu_roots.len());
// draw item // draw item
menu_roots[start_index..=end_index] menu_roots[start_index..=end_index]
.iter() .iter()
@ -908,7 +911,7 @@ pub(super) fn init_root_menu<Message: Clone>(
menu.tree.inner.with_data_mut(|state| { menu.tree.inner.with_data_mut(|state| {
if !(state if !(state
.menu_states .menu_states
.get(menu.depth + 1) .get(menu.depth)
.is_none_or(|s| s.is_empty()) .is_none_or(|s| s.is_empty())
&& (!menu.is_overlay || bar_bounds.contains(overlay_cursor))) && (!menu.is_overlay || bar_bounds.contains(overlay_cursor)))
{ {
@ -923,7 +926,7 @@ pub(super) fn init_root_menu<Message: Clone>(
dbg!( dbg!(
state state
.menu_states .menu_states
.get(menu.depth + 1) .get(menu.depth)
.is_none_or(|s| s.is_empty()) .is_none_or(|s| s.is_empty())
); );
dbg!( dbg!(
@ -1026,7 +1029,7 @@ pub(super) fn init_root_menu<Message: Clone>(
menu_bounds, menu_bounds,
}; };
dbg!("pushing to menu states..."); dbg!("pushing to menu states...");
let v = super::menu_bar::get_mut_or_default(&mut state.menu_states, menu.depth + 1); let v = super::menu_bar::get_mut_or_default(&mut state.menu_states, menu.depth);
v.push(ms); v.push(ms);
// Hack to ensure menu opens properly // Hack to ensure menu opens properly
@ -1057,7 +1060,7 @@ pub(super) fn init_root_popup_menu<Message>(
menu.tree.inner.with_data_mut(|state| { menu.tree.inner.with_data_mut(|state| {
if !(state if !(state
.menu_states .menu_states
.get(menu.depth + 1) .get(menu.depth)
.is_none_or(|s| s.is_empty()) .is_none_or(|s| s.is_empty())
&& (!menu.is_overlay || bar_bounds.contains(overlay_cursor))) && (!menu.is_overlay || bar_bounds.contains(overlay_cursor)))
{ {
@ -1156,7 +1159,7 @@ pub(super) fn init_root_popup_menu<Message>(
scroll_offset: 0.0, scroll_offset: 0.0,
menu_bounds, menu_bounds,
}; };
let v = super::menu_bar::get_mut_or_default(&mut state.menu_states, menu.depth + 1); let v = super::menu_bar::get_mut_or_default(&mut state.menu_states, menu.depth);
v.push(ms); v.push(ms);
// Hack to ensure menu opens properly // Hack to ensure menu opens properly
@ -1221,7 +1224,7 @@ fn process_menu_events<'b, Message: std::clone::Clone>(
let tree = &mut tree[mt.index]; let tree = &mut tree[mt.index];
// get layout // get layout
let last_ms = &state.menu_states[menu.depth + 1][indices.len() - 1]; let last_ms = &state.menu_states[menu.depth][indices.len() - 1];
let child_node = last_ms.layout_single( let child_node = last_ms.layout_single(
overlay_offset, overlay_offset,
last_ms.index.expect("missing index within menu state."), last_ms.index.expect("missing index within menu state."),
@ -1245,7 +1248,7 @@ fn process_menu_events<'b, Message: std::clone::Clone>(
}) })
} }
#[allow(unused_results)] #[allow(unused_results, clippy::too_many_lines, clippy::too_many_arguments)]
fn process_overlay_events<Message>( fn process_overlay_events<Message>(
menu: &mut Menu<Message>, menu: &mut Menu<Message>,
renderer: &crate::Renderer, renderer: &crate::Renderer,
@ -1290,13 +1293,12 @@ where
// * remove invalid menus // * remove invalid menus
let mut prev_bounds = std::iter::once(menu.bar_bounds) let mut prev_bounds = std::iter::once(menu.bar_bounds)
.chain( .chain(
state.menu_states[menu.depth + 1] state.menu_states[menu.depth][..state.menu_states.len().saturating_sub(1).min(1)]
[..state.menu_states.len().saturating_sub(1).min(1)]
.iter() .iter()
.map(|ms| ms.menu_bounds.children_bounds), .map(|ms| ms.menu_bounds.children_bounds),
) )
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let menu_states = state.menu_states.get_mut(menu.depth + 1).unwrap(); let menu_states = state.menu_states.get_mut(menu.depth).unwrap();
if menu.close_condition.leave { if menu.close_condition.leave {
for i in (0..menu_states.len()).rev() { for i in (0..menu_states.len()).rev() {
@ -1374,17 +1376,13 @@ where
// ), // ),
// |(tree, mr), next_active_root| (tree, &mr.children[*next_active_root]), // |(tree, mr), next_active_root| (tree, &mr.children[*next_active_root]),
// ); // );
let (active_tree, roots) = if menu.depth > 0 { let (active_tree, roots) = active_root.iter().take(menu.depth).fold(
active_root.iter().take(menu.depth).fold( (
( &mut state.tree.children[active_root[0]].children,
&mut state.tree.children[active_root[0]].children, &menu.menu_roots[active_root[0]].children,
&menu.menu_roots[active_root[0]].children, ),
), |(tree, mt), next_active_root| (tree, &mt[*next_active_root].children),
|(tree, mt), next_active_root| (tree, &mt[*next_active_root].children), );
)
} else {
(&mut state.tree.children, menu.menu_roots.as_ref())
};
let active_menu = if is_overlay { let active_menu = if is_overlay {
indices[0..indices.len().saturating_sub(1)] indices[0..indices.len().saturating_sub(1)]
.iter() .iter()
@ -1511,7 +1509,7 @@ where
(viewport_size.height - (children_bounds.y + children_bounds.height)).min(0.0); (viewport_size.height - (children_bounds.y + children_bounds.height)).min(0.0);
(max_offset, min_offset) (max_offset, min_offset)
}; };
let menu_states = state.menu_states.get_mut(menu.depth + 1).unwrap(); let menu_states = state.menu_states.get_mut(menu.depth).unwrap();
// update // update
if menu_states.is_empty() { if menu_states.is_empty() {