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::{
|
use super::menu_inner::{
|
||||||
CloseCondition, Direction, ItemHeight, ItemWidth, Menu, MenuState, PathHighlight,
|
CloseCondition, Direction, ItemHeight, ItemWidth, Menu, MenuState, PathHighlight,
|
||||||
|
close_innermost,
|
||||||
};
|
};
|
||||||
use super::menu_tree::MenuTree;
|
use super::menu_tree::MenuTree;
|
||||||
use crate::Renderer;
|
use crate::Renderer;
|
||||||
|
|
@ -626,6 +627,23 @@ where
|
||||||
});
|
});
|
||||||
|
|
||||||
match event {
|
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))
|
Mouse(mouse::Event::ButtonPressed(Left))
|
||||||
| Touch(touch::Event::FingerPressed { .. })
|
| Touch(touch::Event::FingerPressed { .. })
|
||||||
if view_cursor.is_over(layout.bounds()) =>
|
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)]
|
#[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(
|
fn update(
|
||||||
&mut self,
|
&mut self,
|
||||||
event: &event::Event,
|
event: &event::Event,
|
||||||
|
|
@ -601,6 +642,19 @@ impl<'b, Message: Clone + 'static> Menu<'b, Message> {
|
||||||
);
|
);
|
||||||
|
|
||||||
match event {
|
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(
|
Mouse(WheelScrolled { delta }) => process_scroll_events(
|
||||||
self,
|
self,
|
||||||
shell,
|
shell,
|
||||||
|
|
@ -676,27 +730,7 @@ impl<'b, Message: Clone + 'static> Menu<'b, Message> {
|
||||||
needs_reset |= self.close_condition.click_outside && !is_inside;
|
needs_reset |= self.close_condition.click_outside && !is_inside;
|
||||||
|
|
||||||
if needs_reset {
|
if needs_reset {
|
||||||
#[cfg(wayland_platform)]
|
self.close_all(state, shell);
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1782,6 +1816,45 @@ fn get_children_layout<Message>(
|
||||||
(Size::new(width, height), child_positions, child_sizes)
|
(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(
|
fn search_bound(
|
||||||
default: usize,
|
default: usize,
|
||||||
default_left: usize,
|
default_left: usize,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue