1use iced_core::{
4 Clipboard, Element, Event, Layout, Length, Point, Rectangle, Shell, Vector, Widget,
5 layout::{Limits, Node},
6 mouse::{self, Button, Cursor},
7 overlay, renderer,
8 widget::{Operation, Tree, tree},
9};
10
11pub use crate::style::{
12 context_menu::{Catalog, Style},
13 status::{Status, StyleFn},
14};
15
16use crate::widget::overlay::ContextMenuOverlay;
17
18#[allow(missing_debug_implementations)]
39pub struct ContextMenu<
40 'a,
41 Overlay,
42 Message,
43 Theme = iced_widget::Theme,
44 Renderer = iced_widget::Renderer,
45> where
46 Overlay: Fn() -> Element<'a, Message, Theme, Renderer>,
47 Message: Clone,
48 Renderer: renderer::Renderer,
49 Theme: Catalog,
50{
51 underlay: Element<'a, Message, Theme, Renderer>,
53 overlay: Overlay,
55 class: Theme::Class<'a>,
57}
58
59impl<'a, Overlay, Message, Theme, Renderer> ContextMenu<'a, Overlay, Message, Theme, Renderer>
60where
61 Overlay: Fn() -> Element<'a, Message, Theme, Renderer>,
62 Message: Clone,
63 Renderer: renderer::Renderer,
64 Theme: Catalog,
65{
66 pub fn new<U>(underlay: U, overlay: Overlay) -> Self
72 where
73 U: Into<Element<'a, Message, Theme, Renderer>>,
74 {
75 ContextMenu {
76 underlay: underlay.into(),
77 overlay,
78 class: Theme::default(),
79 }
80 }
81
82 #[must_use]
84 pub fn style(mut self, style: impl Fn(&Theme, Status) -> Style + 'a) -> Self
85 where
86 Theme::Class<'a>: From<StyleFn<'a, Theme, Style>>,
87 {
88 self.class = (Box::new(style) as StyleFn<'a, Theme, Style>).into();
89 self
90 }
91
92 #[must_use]
94 pub fn class(mut self, class: impl Into<Theme::Class<'a>>) -> Self {
95 self.class = class.into();
96 self
97 }
98}
99
100impl<'a, Content, Message, Theme, Renderer> Widget<Message, Theme, Renderer>
101 for ContextMenu<'a, Content, Message, Theme, Renderer>
102where
103 Content: 'a + Fn() -> Element<'a, Message, Theme, Renderer>,
104 Message: 'a + Clone,
105 Renderer: 'a + renderer::Renderer,
106 Theme: Catalog,
107{
108 fn size(&self) -> iced_core::Size<Length> {
109 self.underlay.as_widget().size()
110 }
111
112 fn layout(&mut self, tree: &mut Tree, renderer: &Renderer, limits: &Limits) -> Node {
113 self.underlay
114 .as_widget_mut()
115 .layout(&mut tree.children[0], renderer, limits)
116 }
117
118 fn draw(
119 &self,
120 state: &Tree,
121 renderer: &mut Renderer,
122 theme: &Theme,
123 style: &renderer::Style,
124 layout: Layout<'_>,
125 cursor: Cursor,
126 viewport: &Rectangle,
127 ) {
128 self.underlay.as_widget().draw(
129 &state.children[0],
130 renderer,
131 theme,
132 style,
133 layout,
134 cursor,
135 viewport,
136 );
137 }
138
139 fn tag(&self) -> tree::Tag {
140 tree::Tag::of::<State>()
141 }
142
143 fn state(&self) -> tree::State {
144 tree::State::new(State::new())
145 }
146
147 fn children(&self) -> Vec<Tree> {
148 vec![Tree::new(&self.underlay), Tree::new((self.overlay)())]
149 }
150
151 fn diff(&self, tree: &mut Tree) {
152 tree.diff_children(&[&self.underlay, &(self.overlay)()]);
153 }
154
155 fn operate<'b>(
156 &'b mut self,
157 state: &'b mut Tree,
158 layout: Layout<'_>,
159 renderer: &Renderer,
160 operation: &mut dyn Operation<()>,
161 ) {
162 let s: &mut State = state.state.downcast_mut();
163
164 if s.show {
165 let mut content = (self.overlay)();
166 content.as_widget_mut().diff(&mut state.children[1]);
167
168 content
169 .as_widget_mut()
170 .operate(&mut state.children[1], layout, renderer, operation);
171 } else {
172 self.underlay.as_widget_mut().operate(
173 &mut state.children[0],
174 layout,
175 renderer,
176 operation,
177 );
178 }
179 }
180
181 fn update(
182 &mut self,
183 state: &mut Tree,
184 event: &Event,
185 layout: Layout<'_>,
186 cursor: Cursor,
187 renderer: &Renderer,
188 clipboard: &mut dyn Clipboard,
189 shell: &mut Shell<'_, Message>,
190 viewport: &Rectangle,
191 ) {
192 if *event == Event::Mouse(mouse::Event::ButtonPressed(Button::Right)) {
193 let bounds = layout.bounds();
194
195 if cursor.is_over(bounds) {
196 let s: &mut State = state.state.downcast_mut();
197 s.cursor_position = cursor.position().unwrap_or_default();
198 s.show = !s.show;
199 shell.capture_event();
200 }
201 }
202
203 self.underlay.as_widget_mut().update(
204 &mut state.children[0],
205 event,
206 layout,
207 cursor,
208 renderer,
209 clipboard,
210 shell,
211 viewport,
212 );
213 }
214
215 fn mouse_interaction(
216 &self,
217 state: &Tree,
218 layout: Layout<'_>,
219 cursor: Cursor,
220 viewport: &Rectangle,
221 renderer: &Renderer,
222 ) -> mouse::Interaction {
223 self.underlay.as_widget().mouse_interaction(
224 &state.children[0],
225 layout,
226 cursor,
227 viewport,
228 renderer,
229 )
230 }
231
232 fn overlay<'b>(
233 &'b mut self,
234 tree: &'b mut Tree,
235 layout: Layout<'b>,
236 renderer: &Renderer,
237 viewport: &Rectangle,
238 translation: Vector,
239 ) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
240 let s: &mut State = tree.state.downcast_mut();
241
242 if !s.show {
243 return self.underlay.as_widget_mut().overlay(
244 &mut tree.children[0],
245 layout,
246 renderer,
247 viewport,
248 translation,
249 );
250 }
251
252 let position = s.cursor_position;
253 let mut content = (self.overlay)();
254 content.as_widget_mut().diff(&mut tree.children[1]);
255 Some(
256 ContextMenuOverlay::new(
257 position + translation,
258 &mut tree.children[1],
259 content,
260 &self.class,
261 s,
262 )
263 .overlay(),
264 )
265 }
266}
267
268impl<'a, Content, Message, Theme, Renderer> From<ContextMenu<'a, Content, Message, Theme, Renderer>>
269 for Element<'a, Message, Theme, Renderer>
270where
271 Content: 'a + Fn() -> Self,
272 Message: 'a + Clone,
273 Renderer: 'a + renderer::Renderer,
274 Theme: 'a + Catalog,
275{
276 fn from(modal: ContextMenu<'a, Content, Message, Theme, Renderer>) -> Self {
277 Element::new(modal)
278 }
279}
280
281#[derive(Debug, Default)]
283pub(crate) struct State {
284 pub show: bool,
286 pub cursor_position: Point,
288}
289
290impl State {
291 pub const fn new() -> Self {
293 Self {
294 show: false,
295 cursor_position: Point::ORIGIN,
296 }
297 }
298}