Skip to content

Commit 8fced5f

Browse files
committed
refactor(context-menu): accept action map for popups
1 parent 8c4cb2e commit 8fced5f

File tree

1 file changed

+74
-15
lines changed

1 file changed

+74
-15
lines changed

src/widget/context_menu.rs

Lines changed: 74 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
66
#[cfg(all(feature = "wayland", feature = "winit", feature = "surface-message"))]
77
use crate::app::cosmic::{WINDOWING_SYSTEM, WindowingSystem};
8+
use crate::surface;
89
use crate::widget::menu::{
910
self, CloseCondition, Direction, ItemHeight, ItemWidth, MenuBarState, PathHighlight,
1011
init_root_menu, menu_roots_diff,
@@ -22,7 +23,7 @@ pub fn context_menu<Message: 'static + Clone>(
2223
content: impl Into<crate::Element<'static, Message>> + 'static,
2324
// on_context: Message,
2425
context_menu: Option<Vec<menu::Tree<Message>>>,
25-
) -> ContextMenu<'static, Message> {
26+
) -> ContextMenu<'static, Message, Message> {
2627
let mut this = ContextMenu {
2728
content: content.into(),
2829
context_menu: context_menu.map(|menus| {
@@ -33,6 +34,7 @@ pub fn context_menu<Message: 'static + Clone>(
3334
}),
3435
window_id: window::Id::RESERVED,
3536
on_surface_action: None,
37+
action_map: None,
3638
};
3739

3840
if let Some(ref mut context_menu) = this.context_menu {
@@ -42,10 +44,42 @@ pub fn context_menu<Message: 'static + Clone>(
4244
this
4345
}
4446

47+
/// A context menu is a menu in a graphical user interface that appears upon user interaction, such as a right-click mouse operation.
48+
pub fn context_menu_popup<Message: 'static + Clone, AppMessage: 'static + Clone>(
49+
content: impl Into<crate::Element<'static, Message>> + 'static,
50+
// on_context: Message,
51+
context_menu: Option<Vec<menu::Tree<Message>>>,
52+
_parent_id: window::Id,
53+
_on_surface_action: impl Fn(surface::Action) -> Message + Send + Sync + 'static,
54+
_map_action: impl Fn(Message) -> AppMessage + Send + Sync + 'static,
55+
) -> ContextMenu<'static, Message, AppMessage> {
56+
let this: ContextMenu<'_, Message, AppMessage> = ContextMenu {
57+
content: content.into(),
58+
context_menu: context_menu.map(|menus| {
59+
vec![menu::Tree::with_children(
60+
crate::Element::from(crate::widget::row::<'static, Message>()),
61+
menus,
62+
)]
63+
}),
64+
window_id: window::Id::RESERVED,
65+
on_surface_action: None,
66+
action_map: None,
67+
};
68+
69+
#[cfg(all(feature = "winit", feature = "wayland"))]
70+
let mut this = this.with_popup(_parent_id, _on_surface_action, _map_action);
71+
72+
if let Some(ref mut context_menu) = this.context_menu {
73+
context_menu.iter_mut().for_each(menu::Tree::set_index);
74+
}
75+
76+
this
77+
}
78+
4579
/// A context menu is a menu in a graphical user interface that appears upon user interaction, such as a right-click mouse operation.
4680
#[derive(Setters)]
4781
#[must_use]
48-
pub struct ContextMenu<'a, Message> {
82+
pub struct ContextMenu<'a, Message, AppMessage> {
4983
#[setters(skip)]
5084
content: crate::Element<'a, Message>,
5185
#[setters(skip)]
@@ -54,9 +88,13 @@ pub struct ContextMenu<'a, Message> {
5488
#[setters(skip)]
5589
pub(crate) on_surface_action:
5690
Option<Arc<dyn Fn(crate::surface::Action) -> Message + Send + Sync + 'static>>,
91+
#[setters(skip)]
92+
pub action_map: Option<Arc<dyn Fn(Message) -> AppMessage + 'static + Send + Sync>>,
5793
}
5894

59-
impl<Message: Clone + 'static> ContextMenu<'_, Message> {
95+
impl<'a, Message: Clone + 'static, AppMessage: Clone + 'static>
96+
ContextMenu<'a, Message, AppMessage>
97+
{
6098
#[cfg(all(feature = "wayland", feature = "winit", feature = "surface-message"))]
6199
#[allow(clippy::too_many_lines)]
62100
fn create_popup(
@@ -68,7 +106,10 @@ impl<Message: Clone + 'static> ContextMenu<'_, Message> {
68106
viewport: &iced::Rectangle,
69107
my_state: &mut LocalState,
70108
) {
71-
if self.window_id != window::Id::NONE && self.on_surface_action.is_some() {
109+
if self.window_id != window::Id::NONE
110+
&& self.on_surface_action.is_some()
111+
&& self.action_map.is_some()
112+
{
72113
use crate::{surface::action::destroy_popup, widget::menu::Menu};
73114
use iced_runtime::platform_specific::wayland::popup::{
74115
SctkPopupSettings, SctkPositioner,
@@ -171,6 +212,7 @@ impl<Message: Clone + 'static> ContextMenu<'_, Message> {
171212
..Default::default()
172213
};
173214
let parent = self.window_id;
215+
let action_map = self.action_map.clone().unwrap();
174216
shell.publish((self.on_surface_action.as_ref().unwrap())(
175217
crate::surface::action::simple_popup(
176218
move || SctkPopupSettings {
@@ -183,27 +225,44 @@ impl<Message: Clone + 'static> ContextMenu<'_, Message> {
183225
input_zone: None,
184226
},
185227
Some(move || {
228+
let action_map = action_map.clone();
186229
crate::Element::from(
187230
crate::widget::container(popup_menu.clone()).center(Length::Fill),
188231
)
189-
.map(crate::action::app)
232+
.map(move |m| crate::Action::App(action_map.clone()(m)))
190233
}),
191234
),
192235
));
193236
}
194237
}
195238

196-
pub fn on_surface_action(
239+
#[cfg(all(feature = "winit", feature = "wayland"))]
240+
/// Handle dropdown requests for popup creation.
241+
/// Intended to be used with [`crate::app::message::get_popup`]
242+
pub fn with_popup<NewAppMessage>(
197243
mut self,
198-
handler: impl Fn(crate::surface::Action) -> Message + Send + Sync + 'static,
199-
) -> Self {
200-
self.on_surface_action = Some(Arc::new(handler));
201-
self
244+
parent_id: window::Id,
245+
on_surface_action: impl Fn(surface::Action) -> Message + Send + Sync + 'static,
246+
action_map: impl Fn(Message) -> NewAppMessage + Send + Sync + 'static,
247+
) -> ContextMenu<'a, Message, NewAppMessage> {
248+
let Self {
249+
content,
250+
context_menu,
251+
..
252+
} = self;
253+
let new = ContextMenu::<'a, Message, NewAppMessage> {
254+
content,
255+
context_menu,
256+
on_surface_action: Some(Arc::new(on_surface_action)),
257+
action_map: Some(Arc::new(action_map)),
258+
window_id: parent_id,
259+
};
260+
new
202261
}
203262
}
204263

205-
impl<Message: 'static + Clone> Widget<Message, crate::Theme, crate::Renderer>
206-
for ContextMenu<'_, Message>
264+
impl<Message: 'static + Clone, AppMessage: 'static + Clone>
265+
Widget<Message, crate::Theme, crate::Renderer> for ContextMenu<'_, Message, AppMessage>
207266
{
208267
fn tag(&self) -> tree::Tag {
209268
tree::Tag::of::<LocalState>()
@@ -539,10 +598,10 @@ impl<Message: 'static + Clone> Widget<Message, crate::Theme, crate::Renderer>
539598
}
540599
}
541600

542-
impl<'a, Message: Clone + 'static> From<ContextMenu<'static, Message>>
543-
for crate::Element<'static, Message>
601+
impl<'a, Message: Clone + 'static, AppMessage: Clone + 'static>
602+
From<ContextMenu<'static, Message, AppMessage>> for crate::Element<'static, Message>
544603
{
545-
fn from(widget: ContextMenu<'static, Message>) -> Self {
604+
fn from(widget: ContextMenu<'static, Message, AppMessage>) -> Self {
546605
Self::new(widget)
547606
}
548607
}

0 commit comments

Comments
 (0)