diff --git a/examples/application/src/main.rs b/examples/application/src/main.rs index c1aba67f..c70a9d30 100644 --- a/examples/application/src/main.rs +++ b/examples/application/src/main.rs @@ -177,7 +177,6 @@ impl cosmic::Application for App { self.hidden = !self.hidden; } Message::Surface(a) => { - dbg!("got action..."); return cosmic::task::message(cosmic::Action::Cosmic( cosmic::app::Action::Surface(a), )); @@ -236,8 +235,6 @@ impl cosmic::Application for App { } fn header_start(&self) -> Vec> { - use cosmic::widget::menu::Tree; - vec![cosmic::widget::responsive_menu_bar().into_element( self.core(), &self.keybinds, @@ -300,6 +297,10 @@ impl cosmic::Application for App { vec![ menu::Item::Button("hi 443", None, Action::Hi2), menu::Item::Button("hi 4444", None, Action::Hi), + menu::Item::Button("hi 44444", None, Action::Hi3), + menu::Item::Button("hi 444445", None, Action::Hi3), + menu::Item::Button("hi 4444446", None, Action::Hi3), + menu::Item::Button("hi 44444447", None, Action::Hi3), ], ), ], diff --git a/examples/calendar/src/main.rs b/examples/calendar/src/main.rs index c73c4da7..47549a70 100644 --- a/examples/calendar/src/main.rs +++ b/examples/calendar/src/main.rs @@ -92,6 +92,7 @@ impl cosmic::Application for App { |date| Message::DateSelected(date), || Message::PrevMonth, || Message::NextMonth, + chrono::Weekday::Sun, ); content = content.push(calendar); diff --git a/examples/context-menu/Cargo.toml b/examples/context-menu/Cargo.toml index 5b9ad020..9a24a1c8 100644 --- a/examples/context-menu/Cargo.toml +++ b/examples/context-menu/Cargo.toml @@ -11,4 +11,12 @@ tracing-log = "0.2.0" [dependencies.libcosmic] path = "../../" default-features = false -features = ["debug", "winit", "tokio", "xdg-portal", "multi-window"] +features = [ + "debug", + "winit", + "tokio", + "xdg-portal", + "multi-window", + "surface-message", + "wayland", +] diff --git a/examples/context-menu/src/main.rs b/examples/context-menu/src/main.rs index 840cf865..4a307840 100644 --- a/examples/context-menu/src/main.rs +++ b/examples/context-menu/src/main.rs @@ -93,7 +93,7 @@ impl cosmic::Application for App { /// Creates a view after each update. fn view(&self) -> Element { let widget = cosmic::widget::context_menu( - cosmic::widget::button::text(&self.button_label).on_press(Message::Clicked), + cosmic::widget::button::text(self.button_label.to_string()).on_press(Message::Clicked), self.context_menu(), ); diff --git a/examples/menu/src/main.rs b/examples/menu/src/main.rs index 5b65732e..7037a62c 100644 --- a/examples/menu/src/main.rs +++ b/examples/menu/src/main.rs @@ -15,6 +15,7 @@ use cosmic::widget::menu::action::MenuAction; use cosmic::widget::menu::key_bind::KeyBind; use cosmic::widget::menu::key_bind::Modifier; use cosmic::widget::menu::{self, ItemHeight, ItemWidth}; +use cosmic::widget::RcElementWrapper; use cosmic::{executor, Element}; /// Runs application with these settings @@ -155,7 +156,7 @@ impl cosmic::Application for App { pub fn menu_bar<'a>(config: &Config, key_binds: &HashMap) -> Element<'a, Message> { menu::bar(vec![menu::Tree::with_children( - menu::root("File"), + RcElementWrapper::new(Element::from(menu::root("File"))), menu::items( key_binds, vec![ diff --git a/examples/table-view/src/main.rs b/examples/table-view/src/main.rs index 8b2d4f62..6bd773bc 100644 --- a/examples/table-view/src/main.rs +++ b/examples/table-view/src/main.rs @@ -209,11 +209,11 @@ impl cosmic::Application for App { if size.width < 600.0 { widget::compact_table(&self.table_model) .on_item_left_click(Message::ItemSelect) - .item_context(|item| { + .item_context(move |item| { Some(widget::menu::items( &HashMap::new(), vec![widget::menu::Item::Button( - format!("Action on {}", item.name), + format!("Action on {}", item.name.to_string()), None, Action::None, )], diff --git a/src/widget/context_menu.rs b/src/widget/context_menu.rs index dc46da01..6769dff2 100644 --- a/src/widget/context_menu.rs +++ b/src/widget/context_menu.rs @@ -4,7 +4,7 @@ //! A context menu is a menu in a graphical user interface that appears upon user interaction, such as a right-click mouse operation. use crate::widget::menu::{ - self, CloseCondition, ItemHeight, ItemWidth, MenuBarState, PathHighlight, + self, CloseCondition, ItemHeight, ItemWidth, MenuBarState, PathHighlight, menu_roots_diff, }; use derive_setters::Setters; use iced::touch::Finger; @@ -13,8 +13,6 @@ use iced_core::widget::{Tree, Widget, tree}; use iced_core::{Length, Point, Size, event, mouse, touch}; use std::collections::HashSet; -use super::dropdown::menu::State; - /// A context menu is a menu in a graphical user interface that appears upon user interaction, such as a right-click mouse operation. pub fn context_menu( content: impl Into> + 'static, @@ -60,7 +58,7 @@ impl Widget tree::State::new(LocalState { context_cursor: Point::default(), fingers_pressed: Default::default(), - menu_state: Default::default(), + menu_bar_state: Default::default(), }) } @@ -72,7 +70,6 @@ impl Widget // Assign the context menu's elements as this widget's children. if let Some(ref context_menu) = self.context_menu { let mut tree = Tree::empty(); - tree.state = tree::State::new(MenuBarState::default()); tree.children = context_menu .iter() .map(|root| { @@ -95,6 +92,10 @@ impl Widget fn diff(&mut self, tree: &mut Tree) { tree.children[0].diff(self.content.as_widget_mut()); + let state = tree.state.downcast_mut::(); + state.menu_bar_state.inner.with_data_mut(|inner| { + menu_roots_diff(self.context_menu.as_mut().unwrap(), &mut inner.tree); + }); // if let Some(ref mut context_menus) = self.context_menu { // for (menu, tree) in context_menus @@ -188,9 +189,9 @@ impl Widget && (right_button_released(&event) || (touch_lifted(&event) && fingers_pressed == 2)) { state.context_cursor = cursor.position().unwrap_or_default(); - let menu_state = tree.children[1].state.downcast_mut::(); + let state = tree.state.downcast_mut::(); - menu_state.inner.with_data_mut(|state| { + state.menu_bar_state.inner.with_data_mut(|state| { state.open = true; state.view_cursor = cursor; }); @@ -219,18 +220,10 @@ impl Widget translation: Vector, ) -> Option> { let state = tree.state.downcast_ref::(); - let menu_state = state.menu_state.clone(); - let Some(context_menu) = self.context_menu.as_mut() else { - return None; - }; + let context_menu = self.context_menu.as_mut()?; - if !tree.children[1] - .state - .downcast_ref::() - .inner - .with_data(|state| state.open) - { + if !state.menu_bar_state.inner.with_data(|state| state.open) { return None; } @@ -239,8 +232,8 @@ impl Widget bounds.y = state.context_cursor.y; Some( crate::widget::menu::Menu { - tree: menu_state, - menu_roots: std::borrow::Cow::Borrowed(context_menu), + tree: state.menu_bar_state.clone(), + menu_roots: std::borrow::Cow::Owned(context_menu.clone()), bounds_expand: 16, menu_overlays_parent: true, close_condition: CloseCondition { @@ -301,5 +294,5 @@ fn touch_lifted(event: &Event) -> bool { pub struct LocalState { context_cursor: Point, fingers_pressed: HashSet, - menu_state: MenuBarState, + menu_bar_state: MenuBarState, } diff --git a/src/widget/menu/menu_bar.rs b/src/widget/menu/menu_bar.rs index 8658f29a..eddc4f3a 100644 --- a/src/widget/menu/menu_bar.rs +++ b/src/widget/menu/menu_bar.rs @@ -19,7 +19,7 @@ use crate::{ }, }; -use iced::{Point, Vector, window}; +use iced::{Point, Shadow, Vector, window}; use iced_core::Border; use iced_widget::core::{ Alignment, Clipboard, Element, Layout, Length, Padding, Rectangle, Shell, Widget, event, @@ -429,9 +429,9 @@ where layout.bounds(), self.main_offset as f32, ); - let anchor_rect = my_state.inner.with_data_mut(|state| { + let (anchor_rect, gravity) = my_state.inner.with_data_mut(|state| { state.popup_id.insert(self.window_id, id); - state + (state .menu_states .iter() .find(|s| s.index.is_none()) @@ -452,19 +452,28 @@ where width: r.width as i32, height: r.height as i32, }, - ) + ), match (state.horizontal_direction, state.vertical_direction) { + (Direction::Positive, Direction::Positive) => cctk::wayland_protocols::xdg::shell::client::xdg_positioner::Gravity::BottomRight, + (Direction::Positive, Direction::Negative) => cctk::wayland_protocols::xdg::shell::client::xdg_positioner::Gravity::TopRight, + (Direction::Negative, Direction::Positive) => cctk::wayland_protocols::xdg::shell::client::xdg_positioner::Gravity::BottomLeft, + (Direction::Negative, Direction::Negative) => cctk::wayland_protocols::xdg::shell::client::xdg_positioner::Gravity::TopLeft, + }) }); 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() - }; + 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, + reactive: true, + ..Default::default() + }; let parent = self.window_id; shell.publish((surface_action)(crate::surface::action::simple_popup( move || SctkPopupSettings { @@ -496,7 +505,7 @@ where let state = tree.state.downcast_mut::(); state .inner - .with_data_mut(|inner| menu_roots_diff(&mut self.menu_roots, &mut inner.tree)) + .with_data_mut(|inner| menu_roots_diff(&mut self.menu_roots, &mut inner.tree)); } fn tag(&self) -> tree::Tag { @@ -550,10 +559,9 @@ where shell: &mut Shell<'_, Message>, viewport: &Rectangle, ) -> event::Status { - use event::Event::{Mouse, Touch, Window}; + use event::Event::{Mouse, Touch}; use mouse::{Button::Left, Event::ButtonReleased}; use touch::Event::{FingerLifted, FingerLost}; - use window::Event::Focused; let root_status = process_root_events( &mut self.menu_roots, @@ -656,7 +664,7 @@ where // draw path highlight if self.path_highlight.is_some() { let styling = theme.appearance(&self.style); - if let Some(active) = state.active_root.get(0) { + if let Some(active) = state.active_root.first() { let active_bounds = layout .children() .nth(*active) @@ -668,7 +676,7 @@ where radius: styling.bar_border_radius.into(), ..Default::default() }, - shadow: Default::default(), + shadow: Shadow::default(), }; renderer.fill_quad(path_quad, styling.path); @@ -734,7 +742,7 @@ where } } -impl<'a, Message> From> for Element<'a, Message, crate::Theme, Renderer> +impl From> for Element<'_, Message, crate::Theme, Renderer> where Message: Clone + 'static, { diff --git a/src/widget/menu/menu_inner.rs b/src/widget/menu/menu_inner.rs index 3b783af0..f410905e 100644 --- a/src/widget/menu/menu_inner.rs +++ b/src/widget/menu/menu_inner.rs @@ -322,15 +322,13 @@ impl MenuState { // self.menu_bounds.child_positions.len() // ); - // viewport space children bounds let children_bounds = self.menu_bounds.children_bounds + overlay_offset; let child_nodes = self.menu_bounds.child_positions[start_index..=end_index] .iter() .zip(self.menu_bounds.child_sizes[start_index..=end_index].iter()) .zip(menu_tree[start_index..=end_index].iter()) - .enumerate() - .map(|(i, ((cp, size), mt))| { + .map(|((cp, size), mt)| { let mut position = *cp; let mut size = *size; @@ -343,7 +341,6 @@ impl MenuState { } let limits = Limits::new(size, size); - mt.item .layout(&mut tree[mt.index], renderer, &limits) @@ -351,8 +348,7 @@ impl MenuState { }) .collect::>(); - Node::with_children(children_bounds.size(), child_nodes) - .move_to(children_bounds.position()) + Node::with_children(children_bounds.size(), child_nodes).move_to(children_bounds.position()) } fn layout_single( @@ -468,12 +464,18 @@ impl<'b, Message: Clone + 'static> Menu<'b, Message> { let empty = Vec::new(); self.tree.inner.with_data_mut(|data| { - if data.active_root.len() < self.depth + 1 || data.menu_states.len() < self.depth + 1 { - return Node::new(Size::ZERO); + if data.active_root.len() < self.depth + 1 || data.menu_states.len() < self.depth + 1 { + return Node::new(limits.min()); } + let overlay_offset = Point::ORIGIN - position; - let tree_children = &mut data.tree.children; - let children = (if self.is_overlay {0} else {self.depth}..=self.depth) + let tree_children: &mut Vec = &mut data.tree.children; + + let children = (if self.is_overlay { 0 } else { self.depth }..=if self.is_overlay { + data.active_root.len() - 1 + } else { + self.depth + }) .map(|active_root| { if self.menu_roots.is_empty() { return (&empty, vec![]); @@ -491,7 +493,7 @@ impl<'b, Message: Clone + 'static> Menu<'b, Message> { }, ); - data.menu_states[if self.is_overlay {0} else {self.depth}..=self.depth].iter() + data.menu_states[if self.is_overlay {0} else {self.depth}..=if self.is_overlay{data.active_root.len() - 1} else {self.depth}].iter() .enumerate() .filter(|ms| self.is_overlay || ms.0 < 1) .fold((roots, Vec::new()), |(menu_root, mut nodes), (_i, ms)| { @@ -509,6 +511,7 @@ impl<'b, Message: Clone + 'static> Menu<'b, Message> { let node_size = children_node.size(); intrinsic_size.height += node_size.height; intrinsic_size.width = intrinsic_size.width.max(node_size.width); + nodes.push(children_node); // if popup just use len 1? // only the last menu can have a None active index @@ -556,9 +559,10 @@ impl<'b, Message: Clone + 'static> Menu<'b, Message> { .with_data(|data| data.open || data.active_root.len() <= self.depth) { return (None, Ignored); - }; + } let viewport = layout.bounds(); + let viewport_size = viewport.size(); let overlay_offset = Point::ORIGIN - viewport.position(); let overlay_cursor = view_cursor.position().unwrap_or_default() - overlay_offset; @@ -634,7 +638,11 @@ impl<'b, Message: Clone + 'static> Menu<'b, Message> { .distance(view_cursor.position().unwrap_or_default()) < 2.0 { - let is_inside = state.menu_states[..=self.depth] + let is_inside = state.menu_states[..=if self.is_overlay { + state.active_root.len().saturating_sub(1) + } else { + self.depth + }] .iter() .any(|ms| ms.menu_bounds.check_bounds.contains(overlay_cursor)); let mut needs_reset = false; @@ -648,6 +656,11 @@ impl<'b, Message: Clone + 'static> Menu<'b, Message> { needs_reset |= self.close_condition.click_outside && !is_inside; if needs_reset { + #[cfg(all( + feature = "wayland", + feature = "winit", + feature = "surface-message" + ))] if let Some(handler) = self.on_surface_action.as_ref() { let mut root = self.window_id; let mut depth = self.depth; @@ -658,7 +671,7 @@ impl<'b, Message: Clone + 'static> Menu<'b, Message> { if depth == 0 { break; } - root = parent.0.clone(); + root = *parent.0; depth = depth.saturating_sub(1); } shell @@ -698,7 +711,7 @@ impl<'b, Message: Clone + 'static> Menu<'b, Message> { if !state.open || state.active_root.len() <= self.depth { return; } - let active_root = &state.active_root[..=self.depth]; + let active_root = &state.active_root[..=if self.is_overlay { 0 } else { self.depth }]; let viewport = layout.bounds(); let viewport_size = viewport.size(); let overlay_offset = Point::ORIGIN - viewport.position(); @@ -710,18 +723,16 @@ impl<'b, Message: Clone + 'static> Menu<'b, Message> { }; let styling = theme.appearance(&self.style); - let (active_tree, roots) = active_root - .iter() - .skip(1) - .fold( - ( - &state.tree.children[active_root[0]].children, - &self.menu_roots[active_root[0]].children, - ), - |(tree, mt), next_active_root| (tree, &mt[*next_active_root].children), - ); + let roots = active_root.iter().skip(1).fold( + &self.menu_roots[active_root[0]].children, + |mt, next_active_root| (&mt[*next_active_root].children), + ); let indices = state.get_trimmed_indices(self.depth).collect::>(); - state.menu_states[if self.is_overlay {0} else {self.depth}..=self.depth] + state.menu_states[if self.is_overlay { 0 } else { self.depth }..=if self.is_overlay { + state.menu_states.len() - 1 + } else { + self.depth + }] .iter() .zip(layout.children()) .enumerate() @@ -729,7 +740,7 @@ impl<'b, Message: Clone + 'static> Menu<'b, Message> { .fold( roots, |menu_roots: &Vec>, (i, (ms, children_layout))| { - let draw_path = self.path_highlight.as_ref().map_or(false, |ph| match ph { + let draw_path = self.path_highlight.as_ref().is_some_and(|ph| match ph { PathHighlight::Full => true, PathHighlight::OmitActive => { !indices.is_empty() && i < indices.len() - 1 @@ -738,7 +749,9 @@ impl<'b, Message: Clone + 'static> Menu<'b, Message> { }); // react only to the last menu - if self.depth == state.active_root.len() - 1 { + let view_cursor = if self.depth == state.active_root.len() - 1 + || i == state.menu_states.len() - 1 + { view_cursor } else { Cursor::Available([-1.0; 2].into()) @@ -796,7 +809,7 @@ impl<'b, Message: Clone + 'static> Menu<'b, Message> { .zip(children_layout.children()) .for_each(|(mt, clo)| { mt.item.draw( - &active_tree[mt.index], + &state.tree.children[active_root[0]].children[mt.index], r, theme, style, @@ -858,8 +871,8 @@ impl overlay::Overlay Widget - for Menu<'a, Message> +impl Widget + for Menu<'_, Message> { fn size(&self) -> Size { Size { @@ -923,14 +936,7 @@ impl<'a, Message: std::clone::Clone + 'static> Widget Widget Widget Widget cctk::wayland_protocols::xdg::shell::client::xdg_positioner::Gravity::BottomRight, + (Direction::Positive, Direction::Negative) => cctk::wayland_protocols::xdg::shell::client::xdg_positioner::Gravity::TopRight, + (Direction::Negative, Direction::Positive) => cctk::wayland_protocols::xdg::shell::client::xdg_positioner::Gravity::BottomLeft, + (Direction::Negative, Direction::Negative) => cctk::wayland_protocols::xdg::shell::client::xdg_positioner::Gravity::TopLeft, + }) }); let menu_node = Widget::layout( @@ -1013,7 +1025,7 @@ impl<'a, Message: std::clone::Clone + 'static> Widget Rectangle { } } +#[allow(clippy::too_many_arguments)] pub(super) fn init_root_menu( menu: &mut Menu<'_, Message>, renderer: &crate::Renderer, @@ -1074,10 +1087,8 @@ pub(super) fn init_root_menu( main_offset: f32, ) { menu.tree.inner.with_data_mut(|state| { - if !(state - .menu_states - .get(menu.depth) - .is_none() + + if !(state.menu_states.get(menu.depth).is_none() && (!menu.is_overlay || bar_bounds.contains(overlay_cursor))) || menu.depth > 0 || !state.open @@ -1130,8 +1141,7 @@ pub(super) fn init_root_menu( menu.is_overlay, ); set = true; - - state.active_root.insert(menu.depth, i); + state.active_root.push(i); let ms = MenuState { index: None, scroll_offset: 0.0, @@ -1145,9 +1155,7 @@ pub(super) fn init_root_menu( break; } } - if !set { - panic!("huh"); - } + debug_assert!(set, "Root not set"); }); } @@ -1165,20 +1173,17 @@ pub(super) fn init_root_popup_menu( Message: std::clone::Clone, { menu.tree.inner.with_data_mut(|state| { - if !(state - .menu_states - .get(menu.depth) - .is_none() + if !(state.menu_states.get(menu.depth).is_none() && (!menu.is_overlay || bar_bounds.contains(overlay_cursor))) { return; } - let active_roots = &state - .active_root[..=menu.depth]; + let active_roots = &state.active_root[..=menu.depth]; let mut set = false; - let mt = active_roots.iter() + let mt = active_roots + .iter() .skip(1) .fold(&menu.menu_roots[active_roots[0]], |mt, next_active_root| { &mt.children[*next_active_root] @@ -1232,7 +1237,7 @@ pub(super) fn init_root_popup_menu( // Hack to ensure menu opens properly shell.invalidate_layout(); // non tree buttons arent active? - assert!(set, "oops"); + debug_assert!(set, "Root popup menu state was not set."); }); } @@ -1264,23 +1269,16 @@ fn process_menu_events( let Some(hover_index) = hover.index else { return Status::Ignored; - }; + }; - let (tree, mt) = state.active_root - .iter() - .skip(1) - .fold( - // then use menu states for each open menu - ( - &mut state.tree.children[state.active_root[0]].children, - &mut menu_roots[state.active_root[0]], - ), - |(tree, mt), next_active_root| (tree, &mut mt.children[*next_active_root]), - ); - + let mt = state.active_root.iter().skip(1).fold( + // then use menu states for each open menu + &mut menu_roots[state.active_root[0]], + |mt, next_active_root| &mut mt.children[*next_active_root], + ); let mt = &mut mt.children[hover_index]; - let tree = &mut tree[mt.index]; + let tree = &mut state.tree.children[state.active_root[0]].children[mt.index]; // get layout let child_node = hover.layout_single( @@ -1333,18 +1331,14 @@ where let mut new_menu_root = None; menu.tree.inner.with_data_mut(|state| { - let active_root = &state.active_root[..=menu.depth]; - - if state.pressed { - return (new_menu_root, Ignored); - } + /* When overlay is running, cursor_position in any widget method will go negative but I still want Widget::draw() to react to cursor movement */ state.view_cursor = view_cursor; // * remove invalid menus - + let mut prev_bounds = std::iter::once(menu.bar_bounds) .chain( if menu.is_overlay { @@ -1355,7 +1349,7 @@ where .map(|s| s.menu_bounds.children_bounds), ) .collect::>(); - + if menu.is_overlay && menu.close_condition.leave { for i in (0..state.menu_states.len()).rev() { let mb = &state.menu_states[i].menu_bounds; @@ -1369,22 +1363,37 @@ where break; } prev_bounds.pop(); + state.active_root.pop(); + state.menu_states.pop(); + } + } else if menu.is_overlay { + for i in (0..state.menu_states.len()).rev() { + let mb = &state.menu_states[i].menu_bounds; + if mb.parent_bounds.contains(overlay_cursor) + || mb.children_bounds.contains(overlay_cursor) + || prev_bounds.iter().all(|pvb| !pvb.contains(overlay_cursor)) + { + break; + } + prev_bounds.pop(); + state.active_root.pop(); state.menu_states.pop(); } } // * update active item let menu_states_len = state.menu_states.len(); + let Some(last_menu_state) = state.menu_states.get_mut(if menu.is_overlay { - menu_states_len - 1 + menu_states_len.saturating_sub(1) } else { menu.depth }) else { if menu.is_overlay { // no menus left // TODO do we want to avoid this for popups? - state.active_root.remove(menu.depth); + // state.active_root.remove(menu.depth); // keep state.open when the cursor is still inside the menu bar // this allows the overlay to keep drawing when the cursor is @@ -1406,31 +1415,32 @@ where || menu.is_overlay && !last_children_bounds.contains(overlay_cursor) // cursor is outside { + last_menu_state.index = None; return (new_menu_root, Captured); } - // cursor is in the children part - - // TODO set active root here even when not a tree. - // ensure that the // calc new index let height_diff = (overlay_cursor.y - (last_children_bounds.y + last_menu_state.scroll_offset)) .clamp(0.0, last_children_bounds.height - 0.001); - let (active_tree, roots) = active_root - .iter() - .skip(1) - .fold( - ( - &mut state.tree.children[active_root[0]].children, - &menu.menu_roots[active_root[0]].children, - ), - |(tree, mt), next_active_root| (tree, &mt[*next_active_root].children), - ); + let active_root = if menu.is_overlay { + &state.active_root + } else { + &state.active_root[..=menu.depth] + }; - let active_menu = roots; + if state.pressed { + return (new_menu_root, Ignored); + } + let roots = active_root.iter().skip(1).fold( + &menu.menu_roots[active_root[0]].children, + |mt, next_active_root| &mt[*next_active_root].children, + ); + let tree = &mut state.tree.children[active_root[0]].children; + + let active_menu: &Vec> = roots; let new_index = match menu.item_height { ItemHeight::Uniform(u) => (height_diff / f32::from(u)).floor() as usize, ItemHeight::Static(_) | ItemHeight::Dynamic(_) => { @@ -1446,12 +1456,10 @@ where } }; - - let remove = !menu.is_overlay - && last_menu_state - .index - .as_ref() - .is_some_and(|i| *i != new_index && !active_menu[*i].children.is_empty()); + let remove = last_menu_state + .index + .as_ref() + .is_some_and(|i| *i != new_index && !active_menu[*i].children.is_empty()); #[cfg(all(feature = "wayland", feature = "winit", feature = "surface-message"))] { @@ -1465,9 +1473,9 @@ where } } let item = &active_menu[new_index]; - // set new index let old_index = last_menu_state.index.replace(new_index); + // get new active item // * add new menu if the new item is a menu if !item.children.is_empty() && old_index.is_none_or(|i| i != new_index) { @@ -1504,18 +1512,22 @@ where &aod, menu.bounds_expand, item_bounds, - active_tree, + tree, menu.is_overlay, ), }; - + new_menu_root = Some((new_index, ms.clone())); - state.menu_states.truncate(menu.depth + 1); + if menu.is_overlay { + state.active_root.push(new_index); + } else { + state.menu_states.truncate(menu.depth + 1); + } state.menu_states.push(ms); - } else if remove { + } else if !menu.is_overlay && remove { state.menu_states.truncate(menu.depth + 1); } - + (new_menu_root, Captured) }) } @@ -1534,7 +1546,6 @@ where use mouse::ScrollDelta; menu.tree.inner.with_data_mut(|state| { - let delta_y = match delta { ScrollDelta::Lines { y, .. } => y * 60.0, ScrollDelta::Pixels { y, .. } => y, diff --git a/src/widget/menu/menu_tree.rs b/src/widget/menu/menu_tree.rs index 3a875fa1..d02b2b27 100644 --- a/src/widget/menu/menu_tree.rs +++ b/src/widget/menu/menu_tree.rs @@ -210,10 +210,11 @@ where /// /// # Returns /// - A vector of `MenuTree`. +#[must_use] pub fn menu_items< A: MenuAction, L: Into> + 'static, - Message: 'static + std::clone::Clone + std::fmt::Debug, + Message: 'static + std::clone::Clone, >( key_binds: &HashMap, children: Vec>, diff --git a/src/widget/responsive_menu_bar.rs b/src/widget/responsive_menu_bar.rs index 41b4e00a..3d9557d0 100644 --- a/src/widget/responsive_menu_bar.rs +++ b/src/widget/responsive_menu_bar.rs @@ -1,7 +1,6 @@ use std::collections::HashMap; use apply::Apply; -use iced::window; use crate::{ Core, Element, @@ -10,6 +9,7 @@ use crate::{ use super::menu::{self, ItemHeight, ItemWidth}; +#[must_use] pub fn responsive_menu_bar() -> ResponsiveMenuBar { ResponsiveMenuBar::default() } @@ -34,18 +34,21 @@ impl Default for ResponsiveMenuBar { impl ResponsiveMenuBar { /// Set the item width + #[must_use] pub fn item_width(mut self, item_width: ItemWidth) -> Self { self.item_width = item_width; self } /// Set the item height + #[must_use] pub fn item_height(mut self, item_height: ItemHeight) -> Self { self.item_height = item_height; self } /// Set the spacing + #[must_use] pub fn spacing(mut self, spacing: f32) -> Self { self.spacing = spacing; self @@ -56,7 +59,7 @@ impl ResponsiveMenuBar { /// Will panic if the menu bar collapses without tracking the size pub fn into_element< 'a, - Message: std::fmt::Debug + Clone + 'static, + Message: Clone + 'static, A: menu::Action + Clone, S: Into> + 'static, >( diff --git a/src/widget/table/mod.rs b/src/widget/table/mod.rs index 7063dc8e..c546383c 100644 --- a/src/widget/table/mod.rs +++ b/src/widget/table/mod.rs @@ -20,9 +20,9 @@ pub type MultiSelectTableView<'a, Item, Category, Message> = TableView<'a, MultiSelect, Item, Category, Message>; pub type MultiSelectModel = Model; -pub fn table<'a, SelectionMode, Item, Category, Message>( - model: &'a Model, -) -> TableView<'a, SelectionMode, Item, Category, Message> +pub fn table( + model: &Model, +) -> TableView<'_, SelectionMode, Item, Category, Message> where Message: Clone, SelectionMode: Default, @@ -33,9 +33,9 @@ where TableView::new(model) } -pub fn compact_table<'a, SelectionMode, Item, Category, Message>( - model: &'a Model, -) -> CompactTableView<'a, SelectionMode, Item, Category, Message> +pub fn compact_table( + model: &Model, +) -> CompactTableView<'_, SelectionMode, Item, Category, Message> where Message: Clone, SelectionMode: Default, diff --git a/src/widget/table/widget/compact.rs b/src/widget/table/widget/compact.rs index 2d060ca2..47864f6d 100644 --- a/src/widget/table/widget/compact.rs +++ b/src/widget/table/widget/compact.rs @@ -47,9 +47,8 @@ where pub(super) item_context_builder: Box Option>>>, } -impl - From> - for Element<'static, Message> +impl<'a, SelectionMode, Item, Category, Message> + From> for Element<'a, Message> where Category: ItemCategory, Item: ItemInterface, @@ -57,7 +56,7 @@ where SelectionMode: Default, Message: Clone + 'static, { - fn from(val: CompactTableView<'static, SelectionMode, Item, Category, Message>) -> Self { + fn from(val: CompactTableView<'a, SelectionMode, Item, Category, Message>) -> Self { let cosmic_theme::Spacing { space_xxxs, .. } = theme::spacing(); val.model .iter() @@ -172,7 +171,7 @@ where ) .apply(Element::from) }) - .collect::>>() + .collect::>>() .apply(widget::column::with_children) .spacing(val.item_spacing) .padding(val.element_padding) diff --git a/src/widget/table/widget/standard.rs b/src/widget/table/widget/standard.rs index 112d673f..eb9ba7a4 100644 --- a/src/widget/table/widget/standard.rs +++ b/src/widget/table/widget/standard.rs @@ -67,8 +67,8 @@ where pub(super) category_context_builder: Box Option>>>, } -impl - From> for Element<'static, Message> +impl<'a, SelectionMode, Item, Category, Message> + From> for Element<'a, Message> where Category: ItemCategory, Item: ItemInterface, @@ -76,7 +76,7 @@ where SelectionMode: Default, Message: Clone + 'static, { - fn from(val: TableView<'static, SelectionMode, Item, Category, Message>) -> Self { + fn from(val: TableView<'a, SelectionMode, Item, Category, Message>) -> Self { // Header row let header_row = val .model @@ -125,7 +125,7 @@ where .apply(|mouse_area| widget::context_menu(mouse_area, cat_context_tree)) .apply(Element::from) }) - .collect::>>() + .collect::>>() .apply(widget::row::with_children) .apply(Element::from); // Build the items @@ -234,12 +234,12 @@ where ] }) .flatten() - .collect::>>() + .collect::>>() }; vec![vec![header_row], items_full] .into_iter() .flatten() - .collect::>>() + .collect::>>() .apply(widget::column::with_children) .width(val.width) .height(val.height)