feat(context_menu): report open/close and reset on compositor dismissal
This commit is contained in:
parent
aac833d37d
commit
fd20f09729
4 changed files with 85 additions and 0 deletions
|
|
@ -25,6 +25,7 @@ pub enum Button {
|
|||
IconVertical,
|
||||
Image,
|
||||
Link,
|
||||
LinkActive,
|
||||
ListItem([f32; 4]),
|
||||
MenuFolder,
|
||||
MenuItem,
|
||||
|
|
@ -121,6 +122,13 @@ pub fn appearance(
|
|||
corner_radii = &cosmic.corner_radii.radius_0;
|
||||
}
|
||||
|
||||
Button::LinkActive => {
|
||||
appearance.background = Some(Background::Color(cosmic.text_button.hover.into()));
|
||||
appearance.icon_color = Some(cosmic.accent_text_color().into());
|
||||
appearance.text_color = Some(cosmic.accent_text_color().into());
|
||||
corner_radii = &cosmic.corner_radii.radius_xs;
|
||||
}
|
||||
|
||||
Button::Custom { .. } => (),
|
||||
Button::AppletMenu => {
|
||||
let (background, _, _) = color(&cosmic.text_button);
|
||||
|
|
|
|||
|
|
@ -34,6 +34,8 @@ pub fn context_menu<'a, Message: 'static + Clone>(
|
|||
close_on_escape: true,
|
||||
window_id: window::Id::RESERVED,
|
||||
item_width: ItemWidth::Uniform(240),
|
||||
on_open: None,
|
||||
on_close: None,
|
||||
on_surface_action: None,
|
||||
};
|
||||
|
||||
|
|
@ -56,12 +58,31 @@ pub struct ContextMenu<'a, Message> {
|
|||
pub close_on_escape: bool,
|
||||
/// Width of each menu item, and therefore of the menu.
|
||||
pub item_width: ItemWidth,
|
||||
/// Emitted when the menu opens, so the application can mark what was right-clicked.
|
||||
#[setters(strip_option)]
|
||||
pub on_open: Option<Message>,
|
||||
/// Emitted when the menu closes by any path, including the compositor dismissing it.
|
||||
#[setters(strip_option)]
|
||||
pub on_close: Option<Message>,
|
||||
#[setters(skip)]
|
||||
pub(crate) on_surface_action:
|
||||
Option<Arc<dyn Fn(crate::surface::Action) -> Message + Send + Sync + 'static>>,
|
||||
}
|
||||
|
||||
impl<Message: Clone + 'static> ContextMenu<'_, Message> {
|
||||
/// Publish `on_open`/`on_close` when the open state changed since the last report.
|
||||
fn report_open_state(&self, state: &mut LocalState, shell: &mut iced_core::Shell<'_, Message>) {
|
||||
let open = state.menu_bar_state.inner.with_data(|d| d.open);
|
||||
if open == state.reported_open {
|
||||
return;
|
||||
}
|
||||
state.reported_open = open;
|
||||
let message = if open { &self.on_open } else { &self.on_close };
|
||||
if let Some(message) = message.clone() {
|
||||
shell.publish(message);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(wayland_platform)]
|
||||
#[allow(clippy::too_many_lines)]
|
||||
fn create_popup(
|
||||
|
|
@ -238,6 +259,7 @@ impl<Message: 'static + Clone> Widget<Message, crate::Theme, crate::Renderer>
|
|||
context_cursor: Point::default(),
|
||||
fingers_pressed: Default::default(),
|
||||
menu_bar_state: Default::default(),
|
||||
reported_open: false,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -351,6 +373,20 @@ impl<Message: 'static + Clone> Widget<Message, crate::Theme, crate::Renderer>
|
|||
let state = tree.state.downcast_mut::<LocalState>();
|
||||
let bounds = layout.bounds();
|
||||
|
||||
// The compositor dismissed our popup: nothing else tells this state about it.
|
||||
#[cfg(wayland_platform)]
|
||||
if let iced::Event::PlatformSpecific(iced::event::PlatformSpecific::Wayland(
|
||||
iced::event::wayland::Event::Popup(iced::event::wayland::PopupEvent::Done, _, popup),
|
||||
)) = event
|
||||
{
|
||||
state.menu_bar_state.inner.with_data_mut(|d| {
|
||||
if d.popup_id.get(&self.window_id) == Some(popup) {
|
||||
d.popup_id.remove(&self.window_id);
|
||||
d.reset();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 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
|
||||
&& state
|
||||
|
|
@ -432,6 +468,7 @@ impl<Message: 'static + Clone> Widget<Message, crate::Theme, crate::Renderer>
|
|||
|
||||
shell.request_redraw();
|
||||
shell.capture_event();
|
||||
self.report_open_state(tree.state.downcast_mut::<LocalState>(), shell);
|
||||
return;
|
||||
} else if !was_open && right_button_released(event)
|
||||
|| (touch_lifted(event))
|
||||
|
|
@ -467,6 +504,7 @@ impl<Message: 'static + Clone> Widget<Message, crate::Theme, crate::Renderer>
|
|||
shell,
|
||||
viewport,
|
||||
);
|
||||
self.report_open_state(tree.state.downcast_mut::<LocalState>(), shell);
|
||||
}
|
||||
|
||||
fn overlay<'b>(
|
||||
|
|
@ -565,4 +603,5 @@ pub struct LocalState {
|
|||
context_cursor: Point,
|
||||
fingers_pressed: HashSet<Finger>,
|
||||
menu_bar_state: MenuBarState,
|
||||
reported_open: bool,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -593,6 +593,21 @@ where
|
|||
|
||||
let my_state = tree.state.downcast_mut::<MenuBarState>();
|
||||
|
||||
// The compositor dismissed our popup: nothing else tells this state about it.
|
||||
#[cfg(wayland_platform)]
|
||||
if let iced::Event::PlatformSpecific(iced::event::PlatformSpecific::Wayland(
|
||||
iced::event::wayland::Event::Popup(iced::event::wayland::PopupEvent::Done, _, popup),
|
||||
)) = event
|
||||
{
|
||||
my_state.inner.with_data_mut(|d| {
|
||||
if d.popup_id.get(&self.window_id) == Some(popup) {
|
||||
// submenus were dismissed with it
|
||||
d.popup_id.clear();
|
||||
d.reset();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 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
|
||||
|
|
|
|||
|
|
@ -1244,6 +1244,29 @@ where
|
|||
let my_bounds = layout.bounds();
|
||||
let state = tree.state.downcast_mut::<LocalState>();
|
||||
|
||||
// The compositor dismissed our context menu popup: nothing else tells this state about it.
|
||||
#[cfg(wayland_platform)]
|
||||
if let iced::Event::PlatformSpecific(iced::event::PlatformSpecific::Wayland(
|
||||
iced::event::wayland::Event::Popup(iced::event::wayland::PopupEvent::Done, _, popup),
|
||||
)) = &event
|
||||
{
|
||||
let dismissed = state.menu_state.inner.with_data_mut(|data| {
|
||||
if data.popup_id.get(&self.window_id) == Some(popup) {
|
||||
data.popup_id.clear();
|
||||
data.reset();
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
});
|
||||
if dismissed {
|
||||
state.show_context = None;
|
||||
for key in self.model.order.iter().copied() {
|
||||
self.update_entity_paragraph(state, key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let hovered_before = state.hovered;
|
||||
|
||||
let my_id = self.get_drag_id();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue