orinium_browser/browser/core/ui/context_menu.rs
1//! Abstraction for the context menu shown when the user right-clicks the web
2//! content.
3//!
4//! The browser core (`BrowserUi` / `BrowserRenderer`) detects right-clicks on
5//! the web view and hands them to any [`ContextMenu`] implementation through
6//! [`ClickContext`]: click position, the link under the cursor, and the
7//! document URL. The implementation draws itself as a window-space overlay
8//! (on top of the chrome) and reports selected items back as
9//! [`ChromeAction`]s, exactly like a [`Chrome`](super::Chrome) does, so it can
10//! be replaced by any user-designed menu without touching the core.
11//!
12//! All coordinates are logical pixels in window space.
13
14use crate::browser::core::ui::chrome::ChromeAction;
15use crate::engine::renderer_model::DrawCommand;
16use crate::engine::ui::PointerEvent;
17
18/// Information about the right-click that requested the context menu.
19#[derive(Debug, Clone, PartialEq)]
20pub struct ClickContext {
21 /// Click position in window logical coordinates.
22 pub window_pos: (f32, f32),
23 /// Click position relative to the web content area origin (page space).
24 pub page_pos: (f32, f32),
25 /// URL of the link under the cursor, if any.
26 pub link_url: Option<String>,
27 /// URL of the document shown in the web view, if any.
28 pub document_url: Option<String>,
29}
30
31/// The outcome of dispatching a pointer event to an open context menu.
32#[derive(Debug, Clone, PartialEq)]
33pub struct MenuEventResult {
34 /// `true` when the event hit the menu and must not reach the chrome or
35 /// the page.
36 pub consumed: bool,
37 /// Action the browser core should perform.
38 pub action: ChromeAction,
39}
40
41impl MenuEventResult {
42 /// A result that consumes nothing and requests nothing.
43 pub const fn none() -> Self {
44 Self {
45 consumed: false,
46 action: ChromeAction::None,
47 }
48 }
49
50 /// A result that consumes the event and requests `action`.
51 pub const fn consumed(action: ChromeAction) -> Self {
52 Self {
53 consumed: true,
54 action,
55 }
56 }
57}
58
59/// The context menu opened by right-clicking the web view.
60///
61/// The core opens the menu with [`open`](ContextMenu::open) when the web
62/// content is right-pressed; while the menu reports
63/// [`is_open`](ContextMenu::is_open), every pointer event over the window is
64/// routed to it before the chrome or the page. The menu renders above all
65/// other UI via [`draw`](ContextMenu::draw).
66pub trait ContextMenu: std::fmt::Debug {
67 /// Requests the menu at the given click position.
68 ///
69 /// Returns `true` when the menu opened (and consumed the click);
70 /// implementations may return `false` to decline (e.g. no items apply).
71 fn open(&mut self, ctx: &ClickContext) -> bool;
72
73 /// Closes the menu without running an action.
74 fn close(&mut self);
75
76 /// Whether the menu is currently open.
77 fn is_open(&self) -> bool;
78
79 /// Draws the open menu into `cmd_buf` in window coordinates.
80 ///
81 /// Called every frame after the chrome so the menu paints on top of
82 /// everything else. Implementations should emit nothing while closed.
83 fn draw(&self, cmd_buf: &mut Vec<DrawCommand>, width: f32, height: f32);
84
85 /// Dispatches a pointer event to the open menu.
86 ///
87 /// Only called while the menu is open. Returning
88 /// [`consumed`](MenuEventResult.consumed) keeps the event away from the
89 /// chrome and the page.
90 fn pointer_event(&mut self, width: f32, height: f32, event: PointerEvent) -> MenuEventResult;
91
92 /// Whether the menu changed its visual state since the last check.
93 ///
94 /// Consumes the flag, like [`crate::engine::ui::custom_node::CustomNode::needs_repaint`].
95 fn needs_repaint(&self) -> bool;
96}