graphix_package_gui/widgets/
context_menu_widget.rs1use iced_core::{
2 keyboard, layout, mouse, overlay, renderer, touch, widget, Clipboard, Element, Event,
3 Layout, Length, Point, Rectangle, Shell, Size, Vector, Widget,
4};
5
6use super::menu_bar_widget::{MenuGroupDesc, MenuItemDesc, MenuOverlay};
7use super::{Message, Renderer};
8use crate::theme::GraphixTheme;
9
10#[derive(Default)]
11struct State {
12 open: bool,
13 position: Point,
14}
15
16pub(crate) struct OwnedContextMenu<'a> {
19 child: Element<'a, Message, GraphixTheme, Renderer>,
20 desc: MenuGroupDesc,
21}
22
23impl<'a> OwnedContextMenu<'a> {
24 pub fn new(
25 child: Element<'a, Message, GraphixTheme, Renderer>,
26 items: Vec<MenuItemDesc>,
27 ) -> Self {
28 Self {
29 child,
30 desc: MenuGroupDesc { label: String::new(), items },
31 }
32 }
33}
34
35impl<'a> Widget<Message, GraphixTheme, Renderer> for OwnedContextMenu<'a> {
36 fn tag(&self) -> widget::tree::Tag {
37 widget::tree::Tag::of::<State>()
38 }
39
40 fn state(&self) -> widget::tree::State {
41 widget::tree::State::new(State::default())
42 }
43
44 fn children(&self) -> Vec<widget::Tree> {
45 vec![widget::Tree::new(&self.child)]
46 }
47
48 fn diff(&self, tree: &mut widget::Tree) {
49 tree.diff_children(std::slice::from_ref(&self.child));
50 }
51
52 fn size(&self) -> Size<Length> {
53 self.child.as_widget().size()
54 }
55
56 fn layout(
57 &mut self,
58 tree: &mut widget::Tree,
59 renderer: &Renderer,
60 limits: &layout::Limits,
61 ) -> layout::Node {
62 self.child.as_widget_mut().layout(
63 &mut tree.children[0],
64 renderer,
65 limits,
66 )
67 }
68
69 fn draw(
70 &self,
71 tree: &widget::Tree,
72 renderer: &mut Renderer,
73 theme: &GraphixTheme,
74 style: &renderer::Style,
75 layout: Layout<'_>,
76 cursor: mouse::Cursor,
77 viewport: &Rectangle,
78 ) {
79 self.child.as_widget().draw(
80 &tree.children[0],
81 renderer,
82 theme,
83 style,
84 layout,
85 cursor,
86 viewport,
87 );
88 }
89
90 fn update(
91 &mut self,
92 tree: &mut widget::Tree,
93 event: &Event,
94 layout: Layout<'_>,
95 cursor: mouse::Cursor,
96 renderer: &Renderer,
97 clipboard: &mut dyn Clipboard,
98 shell: &mut Shell<'_, Message>,
99 viewport: &Rectangle,
100 ) {
101 self.child.as_widget_mut().update(
103 &mut tree.children[0],
104 event,
105 layout,
106 cursor,
107 renderer,
108 clipboard,
109 shell,
110 viewport,
111 );
112 let state = tree.state.downcast_mut::<State>();
113 match event {
114 Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Right)) => {
115 if cursor.is_over(layout.bounds()) {
116 if let Some(pos) = cursor.position() {
117 state.open = true;
118 state.position = pos;
119 shell.capture_event();
120 }
121 }
122 }
123 Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) => {
124 if state.open {
125 state.open = false;
128 }
129 }
130 Event::Touch(touch::Event::FingerPressed { .. }) => {
131 if state.open {
132 state.open = false;
133 }
134 }
135 Event::Keyboard(keyboard::Event::KeyPressed {
136 key: keyboard::Key::Named(keyboard::key::Named::Escape),
137 ..
138 }) => {
139 if state.open {
140 state.open = false;
141 shell.capture_event();
142 }
143 }
144 _ => {}
145 }
146 }
147
148 fn overlay<'b>(
149 &'b mut self,
150 tree: &'b mut widget::Tree,
151 layout: Layout<'b>,
152 renderer: &Renderer,
153 viewport: &Rectangle,
154 translation: Vector,
155 ) -> Option<overlay::Element<'b, Message, GraphixTheme, Renderer>> {
156 let child_overlay = self.child.as_widget_mut().overlay(
158 &mut tree.children[0],
159 layout,
160 renderer,
161 viewport,
162 translation,
163 );
164 if child_overlay.is_some() {
165 return child_overlay;
166 }
167 let state = tree.state.downcast_mut::<State>();
168 if !state.open || self.desc.items.is_empty() {
169 return None;
170 }
171 Some(overlay::Element::new(Box::new(MenuOverlay {
172 menu: &self.desc,
173 position: state.position,
174 open: Some(&mut state.open),
175 })))
176 }
177
178 fn mouse_interaction(
179 &self,
180 tree: &widget::Tree,
181 layout: Layout<'_>,
182 cursor: mouse::Cursor,
183 viewport: &Rectangle,
184 renderer: &Renderer,
185 ) -> mouse::Interaction {
186 self.child.as_widget().mouse_interaction(
187 &tree.children[0],
188 layout,
189 cursor,
190 viewport,
191 renderer,
192 )
193 }
194}
195
196impl<'a> From<OwnedContextMenu<'a>>
197 for Element<'a, Message, GraphixTheme, Renderer>
198{
199 fn from(w: OwnedContextMenu<'a>) -> Self {
200 Self::new(w)
201 }
202}