refactor: remove menu tree changes

This commit is contained in:
Ashley Wulber 2025-02-17 16:40:11 -05:00
parent 93e3d4fd42
commit 5a5670eda5
No known key found for this signature in database
GPG key ID: 5216D4F46A90A820
9 changed files with 221 additions and 197 deletions

View file

@ -390,7 +390,7 @@ impl Core {
pub fn responsive_menu_bar<'a, Message: Clone + From<SurfaceMessage> + 'static>( pub fn responsive_menu_bar<'a, Message: Clone + From<SurfaceMessage> + 'static>(
&self, &self,
id: crate::widget::Id, id: crate::widget::Id,
roots: Vec<Tree<'a, Message, Renderer>>, roots: Vec<Tree<'a, Message>>,
) -> crate::Element<'a, Message> { ) -> crate::Element<'a, Message> {
let menu_bar_size = self.menu_bars.get(&id); let menu_bar_size = self.menu_bars.get(&id);
if !menu_bar_size.is_some_and(|(limits, size)| { if !menu_bar_size.is_some_and(|(limits, size)| {
@ -405,10 +405,12 @@ impl Core {
} else { } else {
crate::Element::from( crate::Element::from(
responsive_container::responsive_container( responsive_container::responsive_container(
menu::bar(vec![menu::Tree::<'a, _, _>::with_children( menu::bar(vec![menu::Tree::<_>::with_children(
Element::from(
button::icon(icon::from_name("open-menu-symbolic")) button::icon(icon::from_name("open-menu-symbolic"))
.padding([4, 12]) .padding([4, 12])
.class(crate::theme::Button::MenuRoot), .class(crate::theme::Button::MenuRoot),
),
roots, roots,
)]), )]),
id, id,

View file

@ -6,30 +6,24 @@
use crate::widget::menu::{ use crate::widget::menu::{
self, CloseCondition, ItemHeight, ItemWidth, MenuBarState, PathHighlight, self, CloseCondition, ItemHeight, ItemWidth, MenuBarState, PathHighlight,
}; };
use crate::widget::ArcElementWrapper;
use derive_setters::Setters; use derive_setters::Setters;
use iced::touch::Finger; use iced::touch::Finger;
use iced::{Event, Vector}; use iced::{Event, Vector};
use iced_core::widget::{tree, Tree, Widget}; use iced_core::widget::{tree, Tree, Widget};
use iced_core::{event, mouse, touch, Length, Point, Size}; use iced_core::{event, mouse, touch, Length, Point, Size};
use std::collections::HashSet; use std::collections::HashSet;
use std::sync::{Arc, Mutex};
use super::dropdown;
/// A context menu is a menu in a graphical user interface that appears upon user interaction, such as a right-click mouse operation. /// 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<Message: Clone + 'static>( pub fn context_menu<'a, Message: 'a>(
content: impl Into<crate::Element<'static, Message>> + 'static, content: impl Into<crate::Element<'a, Message>> + 'a,
// on_context: Message, // on_context: Message,
context_menu: Option<Vec<menu::Tree<Message>>>, context_menu: Option<Vec<menu::Tree<'a, Message>>>,
) -> ContextMenu<'static, Message> { ) -> ContextMenu<'a, Message> {
let mut this = ContextMenu { let mut this = ContextMenu {
content: content.into(), content: content.into(),
context_menu: context_menu.map(|menus| { context_menu: context_menu.map(|menus| {
vec![menu::Tree::with_children( vec![menu::Tree::with_children(
ArcElementWrapper(Arc::new(Mutex::new(crate::Element::from(
crate::widget::row::<'static, Message>(), crate::widget::row::<'static, Message>(),
)))),
menus, menus,
)] )]
}), }),
@ -49,10 +43,10 @@ pub struct ContextMenu<'a, Message> {
#[setters(skip)] #[setters(skip)]
content: crate::Element<'a, Message>, content: crate::Element<'a, Message>,
#[setters(skip)] #[setters(skip)]
context_menu: Option<Vec<menu::Tree<Message>>>, context_menu: Option<Vec<menu::Tree<'a, Message>>>,
} }
impl<'a, Message: Clone + 'static> Widget<Message, crate::Theme, crate::Renderer> impl<'a, Message: Clone> Widget<Message, crate::Theme, crate::Renderer>
for ContextMenu<'a, Message> for ContextMenu<'a, Message>
{ {
fn tag(&self) -> tree::Tag { fn tag(&self) -> tree::Tag {
@ -64,7 +58,6 @@ impl<'a, Message: Clone + 'static> Widget<Message, crate::Theme, crate::Renderer
tree::State::new(LocalState { tree::State::new(LocalState {
context_cursor: Point::default(), context_cursor: Point::default(),
fingers_pressed: Default::default(), fingers_pressed: Default::default(),
menu_state: Default::default(),
}) })
} }
@ -82,12 +75,9 @@ impl<'a, Message: Clone + 'static> Widget<Message, crate::Theme, crate::Renderer
.map(|root| { .map(|root| {
let mut tree = Tree::empty(); let mut tree = Tree::empty();
let flat = root let flat = root
.flatten() .flattern()
.iter() .iter()
.map(|mt| { .map(|mt| Tree::new(mt.item.as_widget()))
let guard = mt.item.0.lock().unwrap();
Tree::new(guard.as_widget())
})
.collect(); .collect();
tree.children = flat; tree.children = flat;
tree tree
@ -224,7 +214,7 @@ impl<'a, Message: Clone + 'static> Widget<Message, crate::Theme, crate::Renderer
translation: Vector, translation: Vector,
) -> Option<iced_core::overlay::Element<'b, Message, crate::Theme, crate::Renderer>> { ) -> Option<iced_core::overlay::Element<'b, Message, crate::Theme, crate::Renderer>> {
let state = tree.state.downcast_ref::<LocalState>(); let state = tree.state.downcast_ref::<LocalState>();
let menu_state = state.menu_state.clone();
let Some(context_menu) = self.context_menu.as_mut() else { let Some(context_menu) = self.context_menu.as_mut() else {
return None; return None;
}; };
@ -239,8 +229,8 @@ impl<'a, Message: Clone + 'static> Widget<Message, crate::Theme, crate::Renderer
Some( Some(
crate::widget::menu::Menu { crate::widget::menu::Menu {
tree: menu_state, tree: &mut tree.children[1],
menu_roots: std::borrow::Cow::Borrowed(context_menu), menu_roots: context_menu,
bounds_expand: 16, bounds_expand: 16,
menu_overlays_parent: true, menu_overlays_parent: true,
close_condition: CloseCondition { close_condition: CloseCondition {
@ -255,7 +245,7 @@ impl<'a, Message: Clone + 'static> Widget<Message, crate::Theme, crate::Renderer
cross_offset: 0, cross_offset: 0,
root_bounds_list: vec![bounds], root_bounds_list: vec![bounds],
path_highlight: Some(PathHighlight::MenuActive), path_highlight: Some(PathHighlight::MenuActive),
style: std::borrow::Cow::Owned(crate::theme::menu_bar::MenuBarStyle::Default), style: &crate::theme::menu_bar::MenuBarStyle::Default,
position: Point::new(translation.x, translation.y), position: Point::new(translation.x, translation.y),
} }
.overlay(), .overlay(),
@ -275,10 +265,8 @@ impl<'a, Message: Clone + 'static> Widget<Message, crate::Theme, crate::Renderer
} }
} }
impl<Message: Clone + 'static> From<ContextMenu<'static, Message>> impl<'a, Message: Clone + 'a> From<ContextMenu<'a, Message>> for crate::Element<'a, Message> {
for crate::Element<'static, Message> fn from(widget: ContextMenu<'a, Message>) -> Self {
{
fn from(widget: ContextMenu<'static, Message>) -> Self {
Self::new(widget) Self::new(widget)
} }
} }
@ -297,5 +285,4 @@ fn touch_lifted(event: &Event) -> bool {
pub struct LocalState { pub struct LocalState {
context_cursor: Point, context_cursor: Point,
fingers_pressed: HashSet<Finger>, fingers_pressed: HashSet<Finger>,
menu_state: dropdown::menu::State,
} }

View file

@ -130,6 +130,7 @@ pub struct State {
pub(crate) tree: Arc<Mutex<Tree>>, pub(crate) tree: Arc<Mutex<Tree>>,
} }
// TODO use the Rc wrapper instead
// XXX The tree is only used on one thread, but this is needed to make a message // XXX The tree is only used on one thread, but this is needed to make a message
unsafe impl Send for State {} unsafe impl Send for State {}
unsafe impl Sync for State {} unsafe impl Sync for State {}

View file

@ -84,7 +84,7 @@ where
.map(|root| { .map(|root| {
let mut tree = Tree::empty(); let mut tree = Tree::empty();
let flat = root let flat = root
.flatten() .flattern()
.iter() .iter()
.map(|mt| Tree::new(mt.item.as_widget())) .map(|mt| Tree::new(mt.item.as_widget()))
.collect(); .collect();
@ -110,7 +110,7 @@ pub(crate) fn menu_roots_diff<'a, Message, Renderer>(
.zip(menu_roots.iter()) .zip(menu_roots.iter())
.for_each(|(t, root)| { .for_each(|(t, root)| {
let mut flat = root let mut flat = root
.flatten() .flattern()
.iter() .iter()
.map(|mt| { .map(|mt| {
let widget = mt.item.as_widget(); let widget = mt.item.as_widget();
@ -129,7 +129,7 @@ pub(crate) fn menu_roots_diff<'a, Message, Renderer>(
let extended = menu_roots[tree.children.len()..].iter().map(|root| { let extended = menu_roots[tree.children.len()..].iter().map(|root| {
let mut tree = Tree::empty(); let mut tree = Tree::empty();
let flat = root let flat = root
.flatten() .flattern()
.iter() .iter()
.map(|mt| Tree::new(mt.item.as_widget())) .map(|mt| Tree::new(mt.item.as_widget()))
.collect(); .collect();

View file

@ -1,13 +1,8 @@
// From iced_aw, license MIT // From iced_aw, license MIT
//! Menu tree overlay //! Menu tree overlay
use std::{
borrow::Cow,
sync::{Arc, Mutex},
};
use super::{menu_bar::MenuBarState, menu_tree::MenuTree}; use super::{menu_bar::MenuBarState, menu_tree::MenuTree};
use crate::{style::menu_bar::StyleSheet, widget::dropdown}; use crate::style::menu_bar::StyleSheet;
use iced_core::{Border, Shadow}; use iced_core::{Border, Shadow};
use iced_widget::core::{ use iced_widget::core::{
@ -245,7 +240,7 @@ struct MenuBounds {
impl MenuBounds { impl MenuBounds {
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
fn new<Message, Renderer>( fn new<Message, Renderer>(
menu_tree: &MenuTree<Message>, menu_tree: &MenuTree<'_, Message, Renderer>,
renderer: &Renderer, renderer: &Renderer,
item_width: ItemWidth, item_width: ItemWidth,
item_height: ItemHeight, item_height: ItemHeight,
@ -305,7 +300,7 @@ impl MenuState {
overlay_offset: Vector, overlay_offset: Vector,
slice: MenuSlice, slice: MenuSlice,
renderer: &Renderer, renderer: &Renderer,
menu_tree: &MenuTree<Message>, menu_tree: &MenuTree<'_, Message, Renderer>,
tree: &mut [Tree], tree: &mut [Tree],
) -> Node ) -> Node
where where
@ -359,7 +354,7 @@ impl MenuState {
overlay_offset: Vector, overlay_offset: Vector,
index: usize, index: usize,
renderer: &Renderer, renderer: &Renderer,
menu_tree: &MenuTree<Message>, menu_tree: &MenuTree<'_, Message, Renderer>,
tree: &mut Tree, tree: &mut Tree,
) -> Node ) -> Node
where where
@ -432,9 +427,12 @@ impl MenuState {
} }
} }
pub(crate) struct Menu<'b, Message: std::clone::Clone> { pub(crate) struct Menu<'a, 'b, Message, Renderer>
pub(crate) tree: dropdown::menu::State, where
pub(crate) menu_roots: Cow<'b, Vec<MenuTree<Message>>>, Renderer: renderer::Renderer,
{
pub(crate) tree: &'b mut Tree,
pub(crate) menu_roots: &'b mut Vec<MenuTree<'a, Message, Renderer>>,
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
pub(crate) menu_overlays_parent: bool, pub(crate) menu_overlays_parent: bool,
@ -446,10 +444,10 @@ pub(crate) struct Menu<'b, Message: std::clone::Clone> {
pub(crate) cross_offset: i32, pub(crate) cross_offset: i32,
pub(crate) root_bounds_list: Vec<Rectangle>, pub(crate) root_bounds_list: Vec<Rectangle>,
pub(crate) path_highlight: Option<PathHighlight>, pub(crate) path_highlight: Option<PathHighlight>,
pub(crate) style: Cow<'b, <crate::Theme as StyleSheet>::Style>, pub(crate) style: &'b <crate::Theme as StyleSheet>::Style,
pub(crate) position: Point, pub(crate) position: Point,
} }
impl<'b, Message: Clone + 'static, Renderer> Menu<'b, Message> impl<'a, 'b, Message, Renderer> Menu<'a, 'b, Message, Renderer>
where where
Renderer: renderer::Renderer, Renderer: renderer::Renderer,
{ {
@ -753,7 +751,7 @@ fn pad_rectangle(rect: Rectangle, padding: Padding) -> Rectangle {
} }
pub(super) fn init_root_menu<Message, Renderer>( pub(super) fn init_root_menu<Message, Renderer>(
menu: &mut Menu<'_, Message>, menu: &mut Menu<'_, '_, Message, Renderer>,
renderer: &Renderer, renderer: &Renderer,
shell: &mut Shell<'_, Message>, shell: &mut Shell<'_, Message>,
overlay_cursor: Point, overlay_cursor: Point,
@ -831,7 +829,7 @@ pub(super) fn init_root_menu<Message, Renderer>(
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
fn process_menu_events<'b, Message, Renderer>( fn process_menu_events<'b, Message, Renderer>(
tree: &'b mut Tree, tree: &'b mut Tree,
menu_roots: &'b mut [MenuTree<Message>], menu_roots: &'b mut [MenuTree<'_, Message, Renderer>],
event: event::Event, event: event::Event,
view_cursor: Cursor, view_cursor: Cursor,
renderer: &Renderer, renderer: &Renderer,
@ -889,7 +887,7 @@ where
#[allow(unused_results)] #[allow(unused_results)]
fn process_overlay_events<Message, Renderer>( fn process_overlay_events<Message, Renderer>(
menu: &mut Menu<'_, Message>, menu: &mut Menu<'_, '_, Message, Renderer>,
renderer: &Renderer, renderer: &Renderer,
viewport_size: Size, viewport_size: Size,
overlay_offset: Vector, overlay_offset: Vector,
@ -899,7 +897,6 @@ fn process_overlay_events<Message, Renderer>(
) -> event::Status ) -> event::Status
where where
Renderer: renderer::Renderer, Renderer: renderer::Renderer,
Message: std::clone::Clone,
{ {
use event::Status::{Captured, Ignored}; use event::Status::{Captured, Ignored};
/* /*
@ -1080,13 +1077,16 @@ where
Captured Captured
} }
fn process_scroll_events<Message: Clone>( fn process_scroll_events<Message, Renderer>(
menu: &mut Menu<'_, Message>, menu: &mut Menu<'_, '_, Message, Renderer>,
delta: mouse::ScrollDelta, delta: mouse::ScrollDelta,
overlay_cursor: Point, overlay_cursor: Point,
viewport_size: Size, viewport_size: Size,
overlay_offset: Vector, overlay_offset: Vector,
) -> event::Status { ) -> event::Status
where
Renderer: renderer::Renderer,
{
use event::Status::{Captured, Ignored}; use event::Status::{Captured, Ignored};
use mouse::ScrollDelta; use mouse::ScrollDelta;
@ -1155,13 +1155,16 @@ fn process_scroll_events<Message: Clone>(
#[allow(clippy::pedantic)] #[allow(clippy::pedantic)]
/// Returns (children_size, child_positions, child_sizes) /// Returns (children_size, child_positions, child_sizes)
fn get_children_layout<Message>( fn get_children_layout<Message, Renderer>(
menu_tree: &MenuTree<Message>, menu_tree: &MenuTree<'_, Message, Renderer>,
renderer: &crate::Renderer, renderer: &Renderer,
item_width: ItemWidth, item_width: ItemWidth,
item_height: ItemHeight, item_height: ItemHeight,
tree: &mut [Tree], tree: &mut [Tree],
) -> (Size, Vec<f32>, Vec<Size>) { ) -> (Size, Vec<f32>, Vec<Size>)
where
Renderer: renderer::Renderer,
{
let width = match item_width { let width = match item_width {
ItemWidth::Uniform(u) => f32::from(u), ItemWidth::Uniform(u) => f32::from(u),
ItemWidth::Static(s) => f32::from(menu_tree.width.unwrap_or(s)), ItemWidth::Static(s) => f32::from(menu_tree.width.unwrap_or(s)),
@ -1181,8 +1184,7 @@ fn get_children_layout<Message>(
.children .children
.iter() .iter()
.map(|mt| { .map(|mt| {
let guard = mt.item.0.lock().unwrap(); let w = mt.item.as_widget();
let w = guard.as_widget();
match w.size().height { match w.size().height {
Length::Fixed(f) => Size::new(width, f), Length::Fixed(f) => Size::new(width, f),
Length::Shrink => { Length::Shrink => {

View file

@ -5,14 +5,13 @@
use std::borrow::Cow; use std::borrow::Cow;
use std::collections::HashMap; use std::collections::HashMap;
use std::rc::Rc; use std::rc::Rc;
use std::sync::{Arc, Mutex};
use iced_widget::core::{renderer, Element}; use iced_widget::core::{renderer, Element};
use crate::iced_core::{Alignment, Length}; use crate::iced_core::{Alignment, Length};
use crate::widget::icon;
use crate::widget::menu::action::MenuAction; use crate::widget::menu::action::MenuAction;
use crate::widget::menu::key_bind::KeyBind; use crate::widget::menu::key_bind::KeyBind;
use crate::widget::{icon, ArcElementWrapper};
use crate::{theme, widget}; use crate::{theme, widget};
/// Nested menu is essentially a tree of items, a menu is a collection of items /// Nested menu is essentially a tree of items, a menu is a collection of items
@ -24,27 +23,27 @@ use crate::{theme, widget};
/// but there's no need to explicitly distinguish them here, if a menu tree /// but there's no need to explicitly distinguish them here, if a menu tree
/// has children, it's a menu, otherwise it's an item /// has children, it's a menu, otherwise it's an item
#[allow(missing_debug_implementations)] #[allow(missing_debug_implementations)]
#[derive(Clone)] pub struct MenuTree<'a, Message, Renderer = crate::Renderer> {
pub struct MenuTree<Message> {
/// The menu tree will be flatten into a vector to build a linear widget tree, /// The menu tree will be flatten into a vector to build a linear widget tree,
/// the `index` field is the index of the item in that vector /// the `index` field is the index of the item in that vector
pub(crate) index: usize, pub(crate) index: usize,
// TODO needs to be wrapped in Arc<Mutex kinda like in cosmic-files. Check the widget there for details
/// The item of the menu tree /// The item of the menu tree
pub(crate) item: ArcElementWrapper<Message>, pub(crate) item: Element<'a, Message, crate::Theme, Renderer>,
/// The children of the menu tree /// The children of the menu tree
pub(crate) children: Vec<MenuTree<Message>>, pub(crate) children: Vec<MenuTree<'a, Message, Renderer>>,
/// The width of the menu tree /// The width of the menu tree
pub(crate) width: Option<u16>, pub(crate) width: Option<u16>,
/// The height of the menu tree /// The height of the menu tree
pub(crate) height: Option<u16>, pub(crate) height: Option<u16>,
} }
impl<Message: Clone + 'static> MenuTree<Message> { impl<'a, Message, Renderer> MenuTree<'a, Message, Renderer>
where
Renderer: renderer::Renderer,
{
/// Create a new menu tree from a widget /// Create a new menu tree from a widget
pub fn new(item: impl Into<ArcElementWrapper<Message>>) -> Self { pub fn new(item: impl Into<Element<'a, Message, crate::Theme, Renderer>>) -> Self {
Self { Self {
index: 0, index: 0,
item: item.into(), item: item.into(),
@ -56,8 +55,8 @@ impl<Message: Clone + 'static> MenuTree<Message> {
/// Create a menu tree from a widget and a vector of sub trees /// Create a menu tree from a widget and a vector of sub trees
pub fn with_children( pub fn with_children(
item: impl Into<ArcElementWrapper<Message>>, item: impl Into<Element<'a, Message, crate::Theme, Renderer>>,
children: Vec<impl Into<MenuTree<Message>>>, children: Vec<impl Into<MenuTree<'a, Message, Renderer>>>,
) -> Self { ) -> Self {
Self { Self {
index: 0, index: 0,
@ -93,7 +92,7 @@ impl<Message: Clone + 'static> MenuTree<Message> {
/// Set the index of each item /// Set the index of each item
pub(crate) fn set_index(&mut self) { pub(crate) fn set_index(&mut self) {
/// inner counting function. /// inner counting function.
fn rec<Message: Clone + 'static>(mt: &mut MenuTree<Message>, count: &mut usize) { fn rec<Message, Renderer>(mt: &mut MenuTree<'_, Message, Renderer>, count: &mut usize) {
// keep items under the same menu line up // keep items under the same menu line up
mt.children.iter_mut().for_each(|c| { mt.children.iter_mut().for_each(|c| {
c.index = *count; c.index = *count;
@ -110,32 +109,36 @@ impl<Message: Clone + 'static> MenuTree<Message> {
} }
/// Flatten the menu tree /// Flatten the menu tree
pub(crate) fn flatten(&self) -> Vec<Self> { pub(crate) fn flattern(&'a self) -> Vec<&Self> {
/// Inner flattening function /// Inner flattening function
fn rec<Message: Clone + 'static>( fn rec<'a, Message, Renderer>(
mt: &MenuTree<Message>, mt: &'a MenuTree<'a, Message, Renderer>,
flat: &mut Vec<MenuTree<Message>>, flat: &mut Vec<&MenuTree<'a, Message, Renderer>>,
) { ) {
mt.children.clone().into_iter().for_each(|c| { mt.children.iter().for_each(|c| {
flat.push(c); flat.push(c);
}); });
mt.children.iter().for_each(|c| { mt.children.iter().for_each(|c| {
rec(&c, flat); rec(c, flat);
}); });
} }
let mut flat = Vec::new(); let mut flat = Vec::new();
flat.push(self.clone()); flat.push(self);
rec(self, &mut flat); rec(self, &mut flat);
flat flat
} }
} }
impl<Message: Clone + 'static> From<crate::Element<'static, Message>> for MenuTree<Message> { impl<'a, Message, Renderer> From<Element<'a, Message, crate::Theme, Renderer>>
fn from(value: crate::Element<'static, Message>) -> Self { for MenuTree<'a, Message, Renderer>
Self::new(ArcElementWrapper(Arc::new(Mutex::new(value)))) where
Renderer: renderer::Renderer,
{
fn from(value: Element<'a, Message, crate::Theme, Renderer>) -> Self {
Self::new(value)
} }
} }
@ -154,7 +157,6 @@ pub fn menu_button<'a, Message: 'a>(
.class(theme::Button::MenuItem) .class(theme::Button::MenuItem)
} }
#[derive(Clone)]
/// Represents a menu item that performs an action when selected or a separator between menu items. /// Represents a menu item that performs an action when selected or a separator between menu items.
/// ///
/// - `Action` - Represents a menu item that performs an action when selected. /// - `Action` - Represents a menu item that performs an action when selected.
@ -211,13 +213,18 @@ where
/// # Returns /// # Returns
/// - A vector of `MenuTree`. /// - A vector of `MenuTree`.
pub fn menu_items< pub fn menu_items<
'a,
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: 'a,
Renderer: renderer::Renderer + 'a,
>( >(
key_binds: &HashMap<KeyBind, A>, key_binds: &HashMap<KeyBind, A>,
children: Vec<MenuItem<A, L>>, children: Vec<MenuItem<A, L>>,
) -> Vec<MenuTree<Message>> { ) -> Vec<MenuTree<'a, Message, Renderer>>
where
Element<'a, Message, crate::Theme, Renderer>: From<widget::button::Button<'a, Message>>,
{
fn find_key<A: MenuAction>(action: &A, key_binds: &HashMap<KeyBind, A>) -> String { fn find_key<A: MenuAction>(action: &A, key_binds: &HashMap<KeyBind, A>) -> String {
for (key_bind, key_action) in key_binds { for (key_bind, key_action) in key_binds {
if action == key_action { if action == key_action {
@ -252,7 +259,7 @@ pub fn menu_items<
let menu_button = menu_button(items).on_press(action.message()); let menu_button = menu_button(items).on_press(action.message());
trees.push(MenuTree::<Message>::from(Element::from(menu_button))); trees.push(MenuTree::<Message, Renderer>::new(menu_button));
} }
MenuItem::ButtonDisabled(label, icon, action) => { MenuItem::ButtonDisabled(label, icon, action) => {
let key = find_key(&action, key_binds); let key = find_key(&action, key_binds);
@ -270,7 +277,7 @@ pub fn menu_items<
let menu_button = menu_button(items); let menu_button = menu_button(items);
trees.push(MenuTree::<Message>::from(Element::from(menu_button))); trees.push(MenuTree::<Message, Renderer>::new(menu_button));
} }
MenuItem::CheckBox(label, icon, value, action) => { MenuItem::CheckBox(label, icon, value, action) => {
let key = find_key(&action, key_binds); let key = find_key(&action, key_binds);
@ -300,14 +307,11 @@ pub fn menu_items<
items.insert(2, widget::icon::icon(icon).size(14).into()); items.insert(2, widget::icon::icon(icon).size(14).into());
} }
trees.push(MenuTree::from(Element::from( trees.push(MenuTree::new(menu_button(items).on_press(action.message())));
menu_button(items).on_press(action.message()),
)));
} }
MenuItem::Folder(label, children) => { MenuItem::Folder(label, children) => {
trees.push(MenuTree::<Message>::with_children( trees.push(MenuTree::<Message, Renderer>::with_children(
ArcElementWrapper(Arc::new(Mutex::new(crate::Element::from( menu_button(vec![
menu_button::<'static, _>(vec![
widget::text(label).into(), widget::text(label).into(),
widget::horizontal_space().into(), widget::horizontal_space().into(),
widget::icon::from_name("pan-end-symbolic") widget::icon::from_name("pan-end-symbolic")
@ -325,15 +329,14 @@ pub fn menu_items<
theme::Button::MenuFolder theme::Button::MenuFolder
}, },
), ),
)))),
menu_items(key_binds, children), menu_items(key_binds, children),
)); ));
} }
MenuItem::Divider => { MenuItem::Divider => {
if i != size - 1 { if i != size - 1 {
trees.push(MenuTree::<Message>::from(Element::from( trees.push(MenuTree::<Message, Renderer>::new(
widget::divider::horizontal::light(), widget::divider::horizontal::light(),
))); ));
} }
} }
} }

View file

@ -67,7 +67,7 @@ impl<'a, Message: Clone + 'static> NavBar<'a, Message> {
self self
} }
pub fn context_menu(mut self, context_menu: Option<Vec<menu::Tree<Message>>>) -> Self { pub fn context_menu(mut self, context_menu: Option<Vec<menu::Tree<'a, Message>>>) -> Self {
self.segmented_button = self.segmented_button.context_menu(context_menu); self.segmented_button = self.segmented_button.context_menu(context_menu);
self self
} }

View file

@ -9,7 +9,7 @@ use crate::widget::menu::{
self, menu_roots_children, menu_roots_diff, CloseCondition, ItemHeight, ItemWidth, self, menu_roots_children, menu_roots_diff, CloseCondition, ItemHeight, ItemWidth,
MenuBarState, PathHighlight, MenuBarState, PathHighlight,
}; };
use crate::widget::{dropdown, icon, Icon}; use crate::widget::{icon, Icon};
use crate::{Element, Renderer}; use crate::{Element, Renderer};
use derive_setters::Setters; use derive_setters::Setters;
use iced::clipboard::dnd::{self, DndAction, DndDestinationRectangle, DndEvent, OfferEvent}; use iced::clipboard::dnd::{self, DndAction, DndDestinationRectangle, DndEvent, OfferEvent};
@ -127,7 +127,7 @@ where
pub(super) style: Style, pub(super) style: Style,
/// The context menu to display when a context is activated /// The context menu to display when a context is activated
#[setters(skip)] #[setters(skip)]
pub(super) context_menu: Option<Vec<menu::Tree<Message>>>, pub(super) context_menu: Option<Vec<menu::Tree<'a, Message, crate::Renderer>>>,
/// Emits the ID of the item that was activated. /// Emits the ID of the item that was activated.
#[setters(skip)] #[setters(skip)]
pub(super) on_activate: Option<Box<dyn Fn(Entity) -> Message + 'static>>, pub(super) on_activate: Option<Box<dyn Fn(Entity) -> Message + 'static>>,
@ -197,13 +197,13 @@ where
} }
} }
pub fn context_menu(mut self, context_menu: Option<Vec<menu::Tree<Message>>>) -> Self pub fn context_menu(mut self, context_menu: Option<Vec<menu::Tree<'a, Message>>>) -> Self
where where
Message: Clone + 'static, Message: 'static,
{ {
self.context_menu = context_menu.map(|menus| { self.context_menu = context_menu.map(|menus| {
vec![menu::Tree::with_children( vec![menu::Tree::with_children(
crate::Element::from(crate::widget::row::<'static, Message>()), crate::widget::row::<'static, Message>(),
menus, menus,
)] )]
}); });
@ -593,7 +593,6 @@ where
dnd_state: Default::default(), dnd_state: Default::default(),
fingers_pressed: Default::default(), fingers_pressed: Default::default(),
pressed_item: None, pressed_item: None,
menu_state: Default::default(),
}) })
} }
@ -1553,7 +1552,7 @@ where
translation: Vector, translation: Vector,
) -> Option<iced_core::overlay::Element<'b, Message, crate::Theme, Renderer>> { ) -> Option<iced_core::overlay::Element<'b, Message, crate::Theme, Renderer>> {
let state = tree.state.downcast_ref::<LocalState>(); let state = tree.state.downcast_ref::<LocalState>();
let menu_state = state.menu_state.clone();
let Some(entity) = state.show_context else { let Some(entity) = state.show_context else {
return None; return None;
}; };
@ -1581,8 +1580,8 @@ where
Some( Some(
crate::widget::menu::Menu { crate::widget::menu::Menu {
tree: menu_state, tree: &mut tree.children[0],
menu_roots: Cow::Borrowed(context_menu), menu_roots: context_menu,
bounds_expand: 16, bounds_expand: 16,
menu_overlays_parent: true, menu_overlays_parent: true,
close_condition: CloseCondition { close_condition: CloseCondition {
@ -1597,7 +1596,7 @@ where
cross_offset: 0, cross_offset: 0,
root_bounds_list: vec![bounds], root_bounds_list: vec![bounds],
path_highlight: Some(PathHighlight::MenuActive), path_highlight: Some(PathHighlight::MenuActive),
style: Cow::Owned(crate::theme::menu_bar::MenuBarStyle::Default), style: &crate::theme::menu_bar::MenuBarStyle::Default,
position: Point::new(translation.x, translation.y), position: Point::new(translation.x, translation.y),
} }
.overlay(), .overlay(),
@ -1684,8 +1683,6 @@ pub struct LocalState {
fingers_pressed: HashSet<Finger>, fingers_pressed: HashSet<Finger>,
/// The currently pressed item /// The currently pressed item
pressed_item: Option<Item>, pressed_item: Option<Item>,
/// Menu state
pub(crate) menu_state: dropdown::menu::State,
} }
#[derive(Debug, Default, PartialEq)] #[derive(Debug, Default, PartialEq)]

View file

@ -1,19 +1,55 @@
use std::sync::{Arc, Mutex}; use std::{
cell::RefCell,
rc::Rc,
thread::{self, ThreadId},
};
use crate::Element; use crate::Element;
use iced::{event, Length, Rectangle, Size}; use iced::{event, Length, Rectangle, Size};
use iced_core::{id::Id, widget, widget::tree, Widget}; use iced_core::{id::Id, widget, widget::tree, Widget};
#[derive(Clone)] #[derive(Clone)]
pub struct ArcElementWrapper<M>(pub Arc<Mutex<Element<'static, M>>>); pub struct RcElementWrapper<M> {
pub(crate) element: Rc<RefCell<Element<'static, M>>>,
pub(crate) thread_id: ThreadId,
}
impl<M> Widget<M, crate::Theme, crate::Renderer> for ArcElementWrapper<M> { impl<M> RcElementWrapper<M> {
pub fn new(element: Element<'static, M>) -> Self {
Self {
element: Rc::new(RefCell::new(element)),
thread_id: thread::current().id(),
}
}
pub fn with_element<T>(&self, f: impl FnOnce(&Element<'static, M>) -> T) -> T {
assert_eq!(self.thread_id, thread::current().id());
let my_ref: &Element<'static, M> = &RefCell::borrow(self.element.as_ref());
f(my_ref)
}
pub fn with_element_mut<T>(&self, f: impl FnOnce(&mut Element<'static, M>) -> T) -> T {
assert_eq!(self.thread_id, thread::current().id());
let my_refmut: &mut Element<'static, M> = &mut RefCell::borrow_mut(self.element.as_ref());
f(my_refmut)
}
pub(crate) unsafe fn as_ptr(&self) -> *mut Element<'static, M> {
assert_eq!(self.thread_id, thread::current().id());
RefCell::as_ptr(self.element.as_ref())
}
}
unsafe impl<M: 'static> Send for RcElementWrapper<M> {}
unsafe impl<M: 'static> Sync for RcElementWrapper<M> {}
impl<M> Widget<M, crate::Theme, crate::Renderer> for RcElementWrapper<M> {
fn size(&self) -> Size<Length> { fn size(&self) -> Size<Length> {
self.0.lock().unwrap().as_widget().size() self.with_element(|e| e.as_widget().size())
} }
fn size_hint(&self) -> Size<Length> { fn size_hint(&self) -> Size<Length> {
self.0.lock().unwrap().as_widget().size_hint() self.element.borrow_mut().as_widget().size_hint()
} }
fn layout( fn layout(
@ -22,11 +58,7 @@ impl<M> Widget<M, crate::Theme, crate::Renderer> for ArcElementWrapper<M> {
renderer: &crate::Renderer, renderer: &crate::Renderer,
limits: &crate::iced_core::layout::Limits, limits: &crate::iced_core::layout::Limits,
) -> crate::iced_core::layout::Node { ) -> crate::iced_core::layout::Node {
self.0 self.with_element_mut(|e| e.as_widget_mut().layout(tree, renderer, limits))
.lock()
.unwrap()
.as_widget_mut()
.layout(tree, renderer, limits)
} }
fn draw( fn draw(
@ -39,27 +71,26 @@ impl<M> Widget<M, crate::Theme, crate::Renderer> for ArcElementWrapper<M> {
cursor: crate::iced_core::mouse::Cursor, cursor: crate::iced_core::mouse::Cursor,
viewport: &Rectangle, viewport: &Rectangle,
) { ) {
self.0 self.with_element(move |e| {
.lock() e.as_widget()
.unwrap()
.as_widget()
.draw(tree, renderer, theme, style, layout, cursor, viewport) .draw(tree, renderer, theme, style, layout, cursor, viewport)
})
} }
fn tag(&self) -> tree::Tag { fn tag(&self) -> tree::Tag {
self.0.lock().unwrap().as_widget().tag() self.with_element(|e| e.as_widget().tag())
} }
fn state(&self) -> tree::State { fn state(&self) -> tree::State {
self.0.lock().unwrap().as_widget().state() self.with_element(|e| e.as_widget().state())
} }
fn children(&self) -> Vec<tree::Tree> { fn children(&self) -> Vec<tree::Tree> {
self.0.lock().unwrap().as_widget().children() self.with_element(|e| e.as_widget().children())
} }
fn diff(&mut self, tree: &mut tree::Tree) { fn diff(&mut self, tree: &mut tree::Tree) {
self.0.lock().unwrap().as_widget_mut().diff(tree) self.with_element_mut(|e| e.as_widget_mut().diff(tree))
} }
fn operate( fn operate(
@ -69,87 +100,88 @@ impl<M> Widget<M, crate::Theme, crate::Renderer> for ArcElementWrapper<M> {
renderer: &crate::Renderer, renderer: &crate::Renderer,
operation: &mut dyn widget::Operation, operation: &mut dyn widget::Operation,
) { ) {
self.0 self.with_element(|e| {
.lock() e.as_widget().operate(state, layout, renderer, operation);
.unwrap() })
.as_widget()
.operate(state, layout, renderer, operation)
} }
fn on_event( fn on_event(
&mut self, &mut self,
_state: &mut tree::Tree, state: &mut tree::Tree,
_event: crate::iced::Event, event: crate::iced::Event,
_layout: crate::iced_core::Layout<'_>, layout: crate::iced_core::Layout<'_>,
_cursor: crate::iced_core::mouse::Cursor, cursor: crate::iced_core::mouse::Cursor,
_renderer: &crate::Renderer, renderer: &crate::Renderer,
_clipboard: &mut dyn crate::iced_core::Clipboard, clipboard: &mut dyn crate::iced_core::Clipboard,
_shell: &mut crate::iced_core::Shell<'_, M>, shell: &mut crate::iced_core::Shell<'_, M>,
_viewport: &Rectangle, viewport: &Rectangle,
) -> event::Status { ) -> event::Status {
self.0.lock().unwrap().as_widget_mut().on_event( self.with_element_mut(|e| {
_state, _event, _layout, _cursor, _renderer, _clipboard, _shell, _viewport, e.as_widget_mut().on_event(
state, event, layout, cursor, renderer, clipboard, shell, viewport,
) )
})
} }
fn mouse_interaction( fn mouse_interaction(
&self, &self,
_state: &tree::Tree, state: &tree::Tree,
_layout: crate::iced_core::Layout<'_>, layout: crate::iced_core::Layout<'_>,
_cursor: crate::iced_core::mouse::Cursor, cursor: crate::iced_core::mouse::Cursor,
_viewport: &Rectangle, viewport: &Rectangle,
_renderer: &crate::Renderer, renderer: &crate::Renderer,
) -> crate::iced_core::mouse::Interaction { ) -> crate::iced_core::mouse::Interaction {
self.0 self.with_element(|e| {
.lock() e.as_widget()
.unwrap() .mouse_interaction(state, layout, cursor, viewport, renderer)
.as_widget() })
.mouse_interaction(_state, _layout, _cursor, _viewport, _renderer)
} }
fn overlay<'a>( fn overlay<'a>(
&'a mut self, &'a mut self,
_state: &'a mut tree::Tree, state: &'a mut tree::Tree,
_layout: crate::iced_core::Layout<'_>, layout: crate::iced_core::Layout<'_>,
_renderer: &crate::Renderer, renderer: &crate::Renderer,
_translation: crate::iced_core::Vector, translation: crate::iced_core::Vector,
) -> Option<crate::iced_core::overlay::Element<'a, M, crate::Theme, crate::Renderer>> { ) -> Option<crate::iced_core::overlay::Element<'a, M, crate::Theme, crate::Renderer>> {
// TODO assert_eq!(self.thread_id, thread::current().id());
None Rc::get_mut(&mut self.element).and_then(|e| {
e.get_mut()
.as_widget_mut()
.overlay(state, layout, renderer, translation)
})
} }
fn id(&self) -> Option<Id> { fn id(&self) -> Option<Id> {
self.0.lock().unwrap().as_widget().id() self.with_element_mut(|e| e.as_widget_mut().id())
} }
fn set_id(&mut self, _id: Id) { fn set_id(&mut self, id: Id) {
self.0.lock().unwrap().as_widget_mut().set_id(_id) self.with_element_mut(|e| e.as_widget_mut().set_id(id))
} }
fn drag_destinations( fn drag_destinations(
&self, &self,
_state: &tree::Tree, state: &tree::Tree,
_layout: crate::iced_core::Layout<'_>, layout: crate::iced_core::Layout<'_>,
renderer: &crate::Renderer, renderer: &crate::Renderer,
_dnd_rectangles: &mut crate::iced_core::clipboard::DndDestinationRectangles, dnd_rectangles: &mut crate::iced_core::clipboard::DndDestinationRectangles,
) { ) {
self.0.lock().unwrap().as_widget().drag_destinations( self.with_element_mut(|e| {
_state, e.as_widget_mut()
_layout, .drag_destinations(state, layout, renderer, dnd_rectangles)
renderer, })
_dnd_rectangles,
)
} }
} }
impl<Message: 'static> From<ArcElementWrapper<Message>> for Element<'static, Message> { impl<Message: 'static> From<RcElementWrapper<Message>> for Element<'static, Message> {
fn from(wrapper: ArcElementWrapper<Message>) -> Self { fn from(wrapper: RcElementWrapper<Message>) -> Self {
Element::new(wrapper) Element::new(wrapper)
} }
} }
impl<Message: 'static> From<Element<'static, Message>> for ArcElementWrapper<Message> { impl<Message: 'static> From<Element<'static, Message>> for RcElementWrapper<Message> {
fn from(e: Element<'static, Message>) -> Self { fn from(e: Element<'static, Message>) -> Self {
ArcElementWrapper(Arc::new(Mutex::new(e))) RcElementWrapper::new(e)
} }
} }