1use crate::context_menu;
5pub use crate::style::{
6 context_menu::{Catalog, Style},
7 status::{self, StyleFn},
8};
9
10use iced_core::{
11 Border, Clipboard, Color, Element, Event, Layout, Point, Shell, Size, keyboard,
12 layout::{Limits, Node},
13 mouse::{self, Cursor},
14 overlay, renderer, touch,
15 widget::Tree,
16 window,
17};
18
19#[allow(missing_debug_implementations)]
21pub struct ContextMenuOverlay<
22 'a,
23 'b,
24 Message,
25 Theme = iced_widget::Theme,
26 Renderer = iced_widget::Renderer,
27> where
28 Message: 'a + Clone,
29 Renderer: 'a + renderer::Renderer,
30 Theme: Catalog,
31 'b: 'a,
32{
33 position: Point,
35 tree: &'a mut Tree,
37 content: Element<'a, Message, Theme, Renderer>,
39 class: &'a Theme::Class<'b>,
41 state: &'a mut context_menu::State,
43}
44
45impl<'a, 'b, Message, Theme, Renderer> ContextMenuOverlay<'a, 'b, Message, Theme, Renderer>
46where
47 Message: Clone,
48 Renderer: renderer::Renderer,
49 Theme: 'a + Catalog,
50 'b: 'a,
51{
52 pub(crate) fn new<C>(
54 position: Point,
55 tree: &'a mut Tree,
56 content: C,
57 class: &'a <Theme as Catalog>::Class<'b>,
58 state: &'a mut context_menu::State,
59 ) -> Self
60 where
61 C: Into<Element<'a, Message, Theme, Renderer>>,
62 {
63 ContextMenuOverlay {
64 position,
65 tree,
66 content: content.into(),
67 class,
68 state,
69 }
70 }
71
72 #[must_use]
74 pub fn overlay(self) -> overlay::Element<'a, Message, Theme, Renderer> {
75 overlay::Element::new(Box::new(self))
76 }
77}
78
79impl<'a, 'b, Message, Theme, Renderer> overlay::Overlay<Message, Theme, Renderer>
80 for ContextMenuOverlay<'a, 'b, Message, Theme, Renderer>
81where
82 Message: 'a + Clone,
83 Renderer: 'a + renderer::Renderer,
84 Theme: 'a + Catalog,
85 'b: 'a,
86{
87 fn layout(&mut self, renderer: &Renderer, bounds: Size) -> Node {
88 let limits = Limits::new(Size::ZERO, bounds);
89 let max_size = limits.max();
90
91 let mut content = self
92 .content
93 .as_widget_mut()
94 .layout(self.tree, renderer, &limits);
95
96 let mut position = self.position;
98 if position.x + content.size().width > bounds.width {
99 position.x = f32::max(0.0, position.x - content.size().width);
100 }
101 if position.y + content.size().height > bounds.height {
102 position.y = f32::max(0.0, position.y - content.size().height);
103 }
104
105 content.move_to_mut(position);
106
107 Node::with_children(max_size, vec![content])
108 }
109
110 fn draw(
111 &self,
112 renderer: &mut Renderer,
113 theme: &Theme,
114 style: &renderer::Style,
115 layout: Layout<'_>,
116 cursor: Cursor,
117 ) {
118 let bounds = layout.bounds();
119
120 let style_sheet = theme.style(self.class, status::Status::Active);
121
122 renderer.fill_quad(
125 renderer::Quad {
126 bounds,
127 border: Border {
128 radius: (0.0).into(),
129 width: 0.0,
130 color: Color::TRANSPARENT,
131 },
132 ..Default::default()
133 },
134 style_sheet.background,
135 );
136
137 let content_layout = layout
138 .children()
139 .next()
140 .expect("widget: Layout should have a content layout.");
141
142 self.content.as_widget().draw(
144 self.tree,
145 renderer,
146 theme,
147 style,
148 content_layout,
149 cursor,
150 &bounds,
151 );
152 }
153
154 fn update(
155 &mut self,
156 event: &Event,
157 layout: Layout<'_>,
158 cursor: iced_core::mouse::Cursor,
159 renderer: &Renderer,
160 clipboard: &mut dyn Clipboard,
161 shell: &mut Shell<'_, Message>,
162 ) {
163 let layout_children = layout
164 .children()
165 .next()
166 .expect("widget: Layout should have a content layout.");
167
168 let mut forward_event_to_children = true;
169 let mut capture_event = false;
170
171 match &event {
172 Event::Keyboard(keyboard::Event::KeyPressed { key, .. }) => {
173 if *key == keyboard::Key::Named(keyboard::key::Named::Escape) {
174 self.state.show = false;
175 forward_event_to_children = false;
176 shell.capture_event();
177 }
178 }
179
180 Event::Mouse(mouse::Event::ButtonPressed(
181 mouse::Button::Left | mouse::Button::Right,
182 ))
183 | Event::Touch(touch::Event::FingerPressed { .. }) => {
184 if cursor.is_over(layout_children.bounds()) {
185 capture_event = true;
186 } else {
187 self.state.show = false;
188 forward_event_to_children = false;
189 shell.request_redraw();
190 }
191 }
192
193 Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left)) => {
194 self.state.show = false;
196
197 capture_event = true;
198 }
199
200 Event::Window(window::Event::Resized { .. }) => {
201 self.state.show = false;
202 forward_event_to_children = false;
203 capture_event = true;
204 }
205
206 _ => {}
207 }
208
209 if forward_event_to_children {
210 self.content.as_widget_mut().update(
211 self.tree,
212 event,
213 layout_children,
214 cursor,
215 renderer,
216 clipboard,
217 shell,
218 &layout.bounds(),
219 );
220 }
221 if capture_event {
222 shell.capture_event();
223 }
224 }
225
226 fn mouse_interaction(
227 &self,
228 layout: Layout<'_>,
229
230 cursor: mouse::Cursor,
231 renderer: &Renderer,
232 ) -> mouse::Interaction {
233 let bounds = layout.bounds();
234
235 self.content.as_widget().mouse_interaction(
236 self.tree,
237 layout
238 .children()
239 .next()
240 .expect("widget: Layout should have a content layout."),
241 cursor,
242 &bounds,
243 renderer,
244 )
245 }
246}