fix: close inner most menu on esc
This commit is contained in:
parent
03c8f93b29
commit
f0eed6e0c3
2 changed files with 112 additions and 21 deletions
|
|
@ -6,6 +6,7 @@ use std::sync::Arc;
|
|||
|
||||
use super::menu_inner::{
|
||||
CloseCondition, Direction, ItemHeight, ItemWidth, Menu, MenuState, PathHighlight,
|
||||
close_innermost,
|
||||
};
|
||||
use super::menu_tree::MenuTree;
|
||||
use crate::Renderer;
|
||||
|
|
@ -626,6 +627,23 @@ where
|
|||
});
|
||||
|
||||
match event {
|
||||
event::Event::Keyboard(iced_widget::core::keyboard::Event::KeyPressed {
|
||||
key:
|
||||
iced_widget::core::keyboard::Key::Named(
|
||||
iced_widget::core::keyboard::key::Named::Escape,
|
||||
),
|
||||
..
|
||||
}) if open => {
|
||||
my_state.inner.with_data_mut(|state| {
|
||||
let handler = self.on_surface_action.as_ref();
|
||||
close_innermost(state, &mut |id| {
|
||||
if let Some(handler) = handler {
|
||||
shell.publish((handler)(crate::surface::Action::DestroyPopup(id)));
|
||||
}
|
||||
});
|
||||
});
|
||||
shell.capture_event();
|
||||
}
|
||||
Mouse(mouse::Event::ButtonPressed(Left))
|
||||
| Touch(touch::Event::FingerPressed { .. })
|
||||
if view_cursor.is_over(layout.bounds()) =>
|
||||
|
|
|
|||
|
|
@ -546,7 +546,48 @@ impl<'b, Message: Clone + 'static> Menu<'b, Message> {
|
|||
})
|
||||
}
|
||||
|
||||
/// Close every menu and, on Wayland, destroy the root popup this menu belongs to.
|
||||
/// Used when clicking outside a menu.
|
||||
#[allow(clippy::too_many_lines)]
|
||||
fn close_all(
|
||||
&self,
|
||||
state: &mut super::menu_bar::MenuBarStateInner,
|
||||
shell: &mut Shell<'_, Message>,
|
||||
) {
|
||||
#[cfg(wayland_platform)]
|
||||
if matches!(WINDOWING_SYSTEM.get(), Some(WindowingSystem::Wayland))
|
||||
&& let Some(handler) = self.on_surface_action.as_ref()
|
||||
{
|
||||
let mut root = self.window_id;
|
||||
let mut depth = self.depth;
|
||||
while let Some(parent) = state.popup_id.iter().find(|(_, v)| **v == root) {
|
||||
if depth == 0 {
|
||||
break;
|
||||
}
|
||||
root = *parent.0;
|
||||
depth = depth.saturating_sub(1);
|
||||
}
|
||||
shell.publish((handler)(crate::surface::Action::DestroyPopup(root)));
|
||||
}
|
||||
|
||||
state.reset();
|
||||
}
|
||||
|
||||
/// Close the innermost open menu, see [`close_innermost`].
|
||||
/// Used when pressing `esc`.
|
||||
fn close(
|
||||
&self,
|
||||
state: &mut super::menu_bar::MenuBarStateInner,
|
||||
shell: &mut Shell<'_, Message>,
|
||||
) {
|
||||
let handler = self.on_surface_action.as_ref();
|
||||
close_innermost(state, &mut |id| {
|
||||
if let Some(handler) = handler {
|
||||
shell.publish((handler)(crate::surface::Action::DestroyPopup(id)));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn update(
|
||||
&mut self,
|
||||
event: &event::Event,
|
||||
|
|
@ -601,6 +642,19 @@ impl<'b, Message: Clone + 'static> Menu<'b, Message> {
|
|||
);
|
||||
|
||||
match event {
|
||||
event::Event::Keyboard(iced_widget::core::keyboard::Event::KeyPressed {
|
||||
key:
|
||||
iced_widget::core::keyboard::Key::Named(
|
||||
iced_widget::core::keyboard::key::Named::Escape,
|
||||
),
|
||||
..
|
||||
}) if !self.is_overlay => {
|
||||
self.tree
|
||||
.inner
|
||||
.with_data_mut(|state| self.close(state, shell));
|
||||
shell.capture_event();
|
||||
}
|
||||
|
||||
Mouse(WheelScrolled { delta }) => process_scroll_events(
|
||||
self,
|
||||
shell,
|
||||
|
|
@ -676,27 +730,7 @@ impl<'b, Message: Clone + 'static> Menu<'b, Message> {
|
|||
needs_reset |= self.close_condition.click_outside && !is_inside;
|
||||
|
||||
if needs_reset {
|
||||
#[cfg(wayland_platform)]
|
||||
if matches!(WINDOWING_SYSTEM.get(), Some(WindowingSystem::Wayland))
|
||||
&& let Some(handler) = self.on_surface_action.as_ref()
|
||||
{
|
||||
let mut root = self.window_id;
|
||||
let mut depth = self.depth;
|
||||
while let Some(parent) =
|
||||
state.popup_id.iter().find(|(_, v)| **v == root)
|
||||
{
|
||||
// parent of root popup is the window, so we stop.
|
||||
if depth == 0 {
|
||||
break;
|
||||
}
|
||||
root = *parent.0;
|
||||
depth = depth.saturating_sub(1);
|
||||
}
|
||||
shell
|
||||
.publish((handler)(crate::surface::Action::DestroyPopup(root)));
|
||||
}
|
||||
|
||||
state.reset();
|
||||
self.close_all(state, shell);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1782,6 +1816,45 @@ fn get_children_layout<Message>(
|
|||
(Size::new(width, height), child_positions, child_sizes)
|
||||
}
|
||||
|
||||
/// Close the innermost open menu level
|
||||
pub(super) fn close_innermost(
|
||||
state: &mut super::menu_bar::MenuBarStateInner,
|
||||
destroy: &mut dyn FnMut(window::Id),
|
||||
) {
|
||||
let outermost = state
|
||||
.popup_id
|
||||
.iter()
|
||||
.find(|(parent, _)| !state.popup_id.values().any(|id| id == *parent))
|
||||
.map(|(_, id)| *id);
|
||||
let innermost = state
|
||||
.popup_id
|
||||
.values()
|
||||
.find(|id| !state.popup_id.contains_key(id))
|
||||
.copied();
|
||||
|
||||
let levels = state.menu_states.len();
|
||||
if levels <= 1 {
|
||||
if let Some(id) = outermost {
|
||||
destroy(id);
|
||||
}
|
||||
state.popup_id.clear();
|
||||
state.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
state.active_root.truncate(levels - 1);
|
||||
state.menu_states.truncate(levels - 1);
|
||||
|
||||
// Forget the parent's hovered item, so hovering it again reopens the submenu.
|
||||
if let Some(parent) = state.menu_states.last_mut() {
|
||||
parent.index = None;
|
||||
}
|
||||
if let Some(id) = innermost {
|
||||
state.popup_id.retain(|_, popup| *popup != id);
|
||||
destroy(id);
|
||||
}
|
||||
}
|
||||
|
||||
fn search_bound(
|
||||
default: usize,
|
||||
default_left: usize,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue