This commit is contained in:
Ashley Wulber 2025-04-25 00:28:08 -04:00
parent 4fcd09d690
commit 9e9449d302
No known key found for this signature in database
GPG key ID: 5216D4F46A90A820
5 changed files with 649 additions and 191 deletions

View file

@ -8,7 +8,7 @@ use crate::widget::menu::{
};
use derive_setters::Setters;
use iced::touch::Finger;
use iced::{Event, Vector};
use iced::{Event, Vector, window};
use iced_core::widget::{Tree, Widget, tree};
use iced_core::{Length, Point, Size, event, mouse, touch};
use std::collections::HashSet;
@ -257,6 +257,9 @@ impl<Message: 'static + Clone> Widget<Message, crate::Theme, crate::Renderer>
path_highlight: Some(PathHighlight::MenuActive),
style: std::borrow::Cow::Borrowed(&crate::theme::menu_bar::MenuBarStyle::Default),
position: Point::new(translation.x, translation.y),
is_overlay: true,
window_id: window::Id::NONE,
depth: 0,
}
.overlay(),
)

View file

@ -1,6 +1,8 @@
// From iced_aw, license MIT
//! A widget that handles menu trees
use std::collections::HashMap;
use super::{
menu_inner::{
CloseCondition, Direction, ItemHeight, ItemWidth, Menu, MenuState, PathHighlight,
@ -43,25 +45,30 @@ pub(crate) struct MenuBarState {
pub(crate) struct MenuBarStateInner {
pub(crate) tree: Tree,
pub(crate) popup_id: HashMap<window::Id, window::Id>,
pub(crate) pressed: bool,
pub(crate) bar_pressed: bool,
pub(crate) view_cursor: Cursor,
pub(crate) open: bool,
pub(crate) active_root: Option<usize>,
pub(crate) active_root: HashMap<window::Id, Vec<usize>>,
pub(crate) horizontal_direction: Direction,
pub(crate) vertical_direction: Direction,
pub(crate) menu_states: Vec<MenuState>,
pub(crate) menu_states: HashMap<window::Id, Vec<MenuState>>,
}
impl MenuBarStateInner {
pub(super) fn get_trimmed_indices(&self) -> impl Iterator<Item = usize> + '_ {
pub(super) fn get_trimmed_indices(&self, id: &window::Id) -> impl Iterator<Item = usize> + '_ {
self.menu_states
.iter()
.get(id)
.into_iter()
.map(|v| v.iter())
.flatten()
.take_while(|ms| ms.index.is_some())
.map(|ms| ms.index.expect("No indices were found in the menu state."))
}
pub(super) fn reset(&mut self) {
self.open = false;
self.active_root = None;
self.active_root = HashMap::new();
self.menu_states.clear();
}
}
@ -72,10 +79,12 @@ impl Default for MenuBarStateInner {
pressed: false,
view_cursor: Cursor::Available([-0.5, -0.5].into()),
open: false,
active_root: None,
active_root: HashMap::new(),
horizontal_direction: Direction::Positive,
vertical_direction: Direction::Positive,
menu_states: Vec::new(),
menu_states: HashMap::new(),
popup_id: HashMap::new(),
bar_pressed: false,
}
}
}
@ -171,6 +180,7 @@ pub struct MenuBar<Message> {
window_id: window::Id,
#[cfg(feature = "wayland")]
positioner: iced_runtime::platform_specific::wayland::popup::SctkPositioner,
pub(crate) on_surface_action: Option<Box<dyn Fn(crate::surface::Action) -> Message>>,
}
impl<Message> MenuBar<Message>
@ -182,7 +192,13 @@ where
pub fn new(menu_roots: Vec<MenuTree<Message>>) -> Self {
let mut menu_roots = menu_roots;
menu_roots.iter_mut().for_each(MenuTree::set_index);
// println!("======================================================");
// for menu_root in &menu_roots {
// dbg!(menu_root.index);
// for inner_root in &menu_root.children {
// dbg!(inner_root.index);
// }
// }
Self {
width: Length::Shrink,
height: Length::Shrink,
@ -204,6 +220,7 @@ where
window_id: window::Id::NONE,
#[cfg(feature = "wayland")]
positioner: iced_runtime::platform_specific::wayland::popup::SctkPositioner::default(),
on_surface_action: None,
}
}
@ -315,6 +332,14 @@ where
}
self
}
pub fn on_surface_action(
mut self,
handler: impl Fn(crate::surface::Action) -> Message + 'static,
) -> Self {
self.on_surface_action = Some(Box::new(handler));
self
}
}
impl<Message> Widget<Message, crate::Theme, Renderer> for MenuBar<Message>
where
@ -325,7 +350,10 @@ where
}
fn diff(&mut self, tree: &mut Tree) {
menu_roots_diff(&mut self.menu_roots, tree);
let state = tree.state.downcast_mut::<MenuBarState>();
state
.inner
.with_data_mut(|inner| menu_roots_diff(&mut self.menu_roots, &mut inner.tree))
}
fn tag(&self) -> tree::Tag {
@ -394,21 +422,38 @@ where
viewport,
);
let state = tree.state.downcast_mut::<MenuBarState>();
let my_state = tree.state.downcast_mut::<MenuBarState>();
match event {
Mouse(ButtonReleased(Left)) | Touch(FingerLifted { .. } | FingerLost { .. }) => {
state.inner.with_data_mut(|state| {
// XXX this should reset the state if there are no other copies of the state, which implies no dropdown menus open.
let reset = self.window_id != window::Id::NONE && my_state.inner.with_data(|d| d.open);
my_state.inner.with_data_mut(|state| {
if reset {
if let Some(popup_id) = state.popup_id.get(&self.window_id).copied() {
dbg!("reset destroy");
// TODO emit message
if let Some(handler) = self.on_surface_action.as_ref() {
shell.publish((handler)(crate::surface::Action::DestroyPopup(popup_id)));
state.reset();
}
}
}
let tree = &mut state.tree;
match event {
Mouse(ButtonReleased(Left)) | Touch(FingerLifted { .. } | FingerLost { .. }) => {
if state.menu_states.is_empty() && view_cursor.is_over(layout.bounds()) {
state.view_cursor = view_cursor;
state.open = true;
// #[cfg(feature = "wayland")]
// TODO emit Message to open menu
}
});
}
_ => (),
}
_ => (),
}
});
root_status
}
@ -434,10 +479,14 @@ where
// draw path highlight
if self.path_highlight.is_some() {
let styling = theme.appearance(&self.style);
if let Some(active) = state.active_root {
if let Some(active) = state
.active_root
.get(&self.window_id)
.and_then(|active| active.get(0))
{
let active_bounds = layout
.children()
.nth(active)
.nth(*active)
.expect("Active child not found in menu?")
.bounds();
let path_quad = renderer::Quad {
@ -489,7 +538,7 @@ where
Some(
Menu {
tree: state.clone(),
menu_roots: std::borrow::Cow::Borrowed(&mut self.menu_roots),
menu_roots: std::borrow::Cow::Owned(self.menu_roots.clone()),
bounds_expand: self.bounds_expand,
menu_overlays_parent: false,
close_condition: self.close_condition,
@ -502,6 +551,9 @@ where
path_highlight: self.path_highlight,
style: std::borrow::Cow::Borrowed(&self.style),
position: Point::new(translation.x, translation.y),
is_overlay: false,
window_id: window::Id::NONE,
depth: 0,
}
.overlay(),
)

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,7 @@
use std::collections::HashMap;
use apply::Apply;
use iced::window;
use crate::{
Core, Element,
@ -80,17 +81,23 @@ impl ResponsiveMenuBar {
menu::bar(
trees
.into_iter()
.map(|mt| {
menu::Tree::<_>::with_children(
menu::root(mt.0),
menu::items(key_binds, mt.1.into()),
)
})
.map(
|mt: (
std::borrow::Cow<'_, str>,
Vec<menu::Item<A, std::borrow::Cow<'_, str>>>,
)| {
menu::Tree::<_>::with_children(
menu::root(mt.0),
menu::items(key_binds, mt.1.into()),
)
},
)
.collect(),
)
.item_width(self.item_width)
.item_height(self.item_height)
.spacing(self.spacing),
.spacing(self.spacing)
.window_id_maybe(core.main_window_id()),
crate::widget::Id::new(format!("menu_bar_expanded_{id}")),
),
id,
@ -123,7 +130,8 @@ impl ResponsiveMenuBar {
)])
.item_height(self.item_height)
.item_width(self.collapsed_item_width)
.spacing(self.spacing),
.spacing(self.spacing)
.window_id_maybe(core.main_window_id()),
crate::widget::Id::new(format!("menu_bar_collapsed_{id}")),
),
id,

View file

@ -17,7 +17,7 @@ use iced::clipboard::mime::AllowedMimeTypes;
use iced::touch::Finger;
use iced::{
Alignment, Background, Color, Event, Length, Padding, Rectangle, Size, Task, Vector, alignment,
event, keyboard, mouse, touch,
event, keyboard, mouse, touch, window,
};
use iced_core::mouse::ScrollDelta;
use iced_core::text::{LineHeight, Renderer as TextRenderer, Shaping, Wrapping};
@ -1615,6 +1615,9 @@ where
path_highlight: Some(PathHighlight::MenuActive),
style: std::borrow::Cow::Borrowed(&crate::theme::menu_bar::MenuBarStyle::Default),
position: Point::new(translation.x, translation.y),
is_overlay: true,
window_id: window::Id::NONE,
depth: 0,
}
.overlay(),
)