diff --git a/resources/context_menu.html b/resources/context_menu.html index 702d1709..765b5f92 100644 --- a/resources/context_menu.html +++ b/resources/context_menu.html @@ -13,6 +13,7 @@ justify-content: start; } .menu-item { + cursor: pointer; display: inline-block; height: 30px; width: 100%; @@ -27,6 +28,7 @@ border-radius: 5px; } .menu-item.disabled { + cursor: default; background: #dfdfdf; color: #505050; cursor: pointer; @@ -61,7 +63,7 @@ } else { menuItem.onclick = (ev) => { // accept left click only - if (ev.buttons != 1) { + if (ev.buttons !== 1) { return; } const msg = JSON.stringify({ diff --git a/src/context_menu.rs b/src/context_menu.rs index bd3c5aeb..869915c9 100644 --- a/src/context_menu.rs +++ b/src/context_menu.rs @@ -9,13 +9,19 @@ use raw_window_handle::{HasWindowHandle, RawWindowHandle}; /* Wayland Implementation */ #[cfg(linux)] -use crate::webview::WebView; +use crate::{verso::send_to_constellation, webview::WebView, window::Window}; +#[cfg(linux)] +use compositing_traits::ConstellationMsg; +#[cfg(linux)] +use crossbeam_channel::Sender; #[cfg(linux)] use serde::{Deserialize, Serialize}; #[cfg(linux)] -use webrender_api::units::DeviceIntPoint; +use servo_url::ServoUrl; #[cfg(linux)] use webrender_api::units::DeviceIntRect; +#[cfg(linux)] +use winit::dpi::PhysicalPosition; /// Context Menu #[cfg(any(target_os = "macos", target_os = "windows"))] @@ -65,7 +71,7 @@ impl ContextMenu { pub struct ContextMenu { menu_items: Vec, /// The webview that the context menu is attached to - pub webview: Option, + webview: WebView, } #[cfg(linux)] @@ -74,19 +80,48 @@ impl ContextMenu { /// /// Often used by calling window.alert() or window.confirm() in the web page. pub fn new_with_menu(menu_items: Vec) -> Self { + let webview_id = WebViewId::new(); + let webview = WebView::new(webview_id, DeviceIntRect::zero()); + Self { menu_items, - webview: None, + webview, } } - /// Set the context menu's options - pub fn set_menu_items(&mut self, menu_items: Vec) { - self.menu_items = menu_items; + + /// Show the context menu to current cursor position + pub fn show( + &mut self, + sender: &Sender, + window: &mut Window, + position: PhysicalPosition, + ) { + let scale_factor = window.scale_factor(); + self.set_position(position, scale_factor); + + send_to_constellation( + sender, + ConstellationMsg::NewWebView(self.resource_url(), self.webview.webview_id), + ); + window.append_dialog_webview(self.webview.clone()); + } + + /// Get webview of the context menu + pub fn webview(&self) -> &WebView { + &self.webview + } + + /// Get resource URL of the context menu + fn resource_url(&self) -> ServoUrl { + let items_json: String = self.to_items_json(); + let url_str = format!("verso://context_menu.html?items={}", items_json); + ServoUrl::parse(&url_str).unwrap() } - /// Show the context menu on position - pub fn create_webview(&mut self, position: DeviceIntPoint, scale_factor: f64) -> WebView { + + /// Set the position of the context menu + fn set_position(&mut self, position: PhysicalPosition, scale_factor: f64) { // Translate position to origin - let origin = Point2D::new(position.x, position.y); + let origin = Point2D::new(position.x as i32, position.y as i32); // Calculate menu size // Each menu item is 30px height @@ -96,17 +131,11 @@ impl ContextMenu { let size = Size2D::new(menu_width as i32, menu_height as i32); let rect = DeviceIntRect::from_origin_and_size(origin, size); - let webview_id = WebViewId::new(); - let webview = WebView::new(webview_id, rect); - // let url = ServoUrl::parse("https://example.com").unwrap(); - - self.webview = Some(webview.clone()); - - webview + self.webview.set_size(rect); } /// get item json - pub fn to_items_json(&self) -> String { + fn to_items_json(&self) -> String { serde_json::to_string(&self.menu_items).unwrap() } } @@ -115,8 +144,10 @@ impl ContextMenu { #[derive(Debug, Clone, Serialize)] pub struct MenuItem { id: String, - label: String, - enabled: bool, + /// label of the menu item + pub label: String, + /// Whether the menu item is enabled + pub enabled: bool, } impl MenuItem { @@ -133,27 +164,13 @@ impl MenuItem { pub fn id(&self) -> &str { &self.id } - /// Get the label of the menu item - pub fn label(&self) -> &str { - &self.label - } - /// Set the label of the menu item - pub fn set_label(&mut self, label: &str) -> &Self { - self.label = label.to_string(); - self - } - /// Enable or disable menu item - pub fn set_enabled(&mut self, enabled: bool) -> &Self { - self.enabled = enabled; - self - } } /// Context Menu Click Result #[cfg(linux)] #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ContextMenuClickResult { - /// The id of the menu item + /// The id of the menu ite /// Get the label of the menu item pub id: String, /// Close the context menu pub close: bool, diff --git a/src/window.rs b/src/window.rs index e068d7f8..a83cf8e0 100644 --- a/src/window.rs +++ b/src/window.rs @@ -312,8 +312,7 @@ impl Window { // Close old context menu self.close_context_menu(sender); // Create new context menu - self.context_menu = Some(self.create_context_menu()); - self.show_context_menu(sender); + self.context_menu = Some(self.show_context_menu(sender)); return; } _ => {} @@ -447,19 +446,6 @@ impl Window { } } - /// Close the context menu - /// - /// If context menu exists, return true. - pub(crate) fn close_context_menu(&mut self, sender: &Sender) -> bool { - if let Some(context_menu) = self.context_menu.take() { - if let Some(webview) = context_menu.webview { - send_to_constellation(sender, ConstellationMsg::CloseWebView(webview.webview_id)); - return true; - } - } - false - } - /// Handle servo messages. Return true if it requests a new window pub fn handle_servo_message( &mut self, @@ -478,13 +464,11 @@ impl Window { } } if let Some(context_menu) = &self.context_menu { - if let Some(webview) = &context_menu.webview { - if webview.webview_id == webview_id { - self.handle_servo_messages_with_context_menu( - webview_id, message, sender, clipboard, compositor, - ); - return false; - } + if context_menu.webview().webview_id == webview_id { + self.handle_servo_messages_with_context_menu( + webview_id, message, sender, clipboard, compositor, + ); + return false; } } // Handle message in Verso WebView @@ -514,8 +498,8 @@ impl Window { } /// Append a dialog webview to the window. - pub fn append_dialog_webview(&mut self, webview: &WebView) { - self.dialog_webviews.push(webview.clone()); + pub fn append_dialog_webview(&mut self, webview: WebView) { + self.dialog_webviews.push(webview); } /// Remove a dialog webview from the window. @@ -660,9 +644,42 @@ impl Window { context_menu.show(self.window.window_handle().unwrap()); } + #[cfg(any(target_os = "macos", target_os = "windows"))] + fn handle_context_menu_event(&self, sender: &Sender, event: MenuEvent) { + // TODO: should be more flexible to handle different menu items + match event.id().0.as_str() { + "back" => { + send_to_constellation( + sender, + ConstellationMsg::TraverseHistory( + self.webview.as_ref().unwrap().webview_id, + TraversalDirection::Back(1), + ), + ); + } + "forward" => { + send_to_constellation( + sender, + ConstellationMsg::TraverseHistory( + self.webview.as_ref().unwrap().webview_id, + TraversalDirection::Forward(1), + ), + ); + } + "reload" => { + send_to_constellation( + sender, + ConstellationMsg::Reload(self.webview.as_ref().unwrap().webview_id), + ); + } + _ => {} + } + } + #[cfg(linux)] - pub(crate) fn create_context_menu(&mut self) -> ContextMenu { + pub(crate) fn show_context_menu(&mut self, sender: &Sender) -> ContextMenu { use crate::context_menu::MenuItem; + let history_len = self.history.len(); // items @@ -674,27 +691,27 @@ impl Window { ); let reload = MenuItem::new(Some("reload"), "Reload", true); - ContextMenu::new_with_menu([back, forward, reload].to_vec()) + let mut context_menu = ContextMenu::new_with_menu([back, forward, reload].to_vec()); + + let position = self.mouse_position.get().unwrap(); + context_menu.show(sender, self, position); + + context_menu } + /// Close the context menu + /// + /// If context menu exists, return true. #[cfg(linux)] - pub(crate) fn show_context_menu(&mut self, sender: &Sender) { - let scale_factor = self.scale_factor(); - - if let Some(ref mut context_menu) = self.context_menu { - let mouse_position = self.mouse_position.get().unwrap(); - let position = Point2D::new(mouse_position.x as i32, mouse_position.y as i32); - let items_json = context_menu.to_items_json(); - let url_str = format!("verso://context_menu.html?items={}", items_json); - let url = ServoUrl::parse(&url_str).unwrap(); - let menu_webview = context_menu.create_webview(position, scale_factor); + pub(crate) fn close_context_menu(&mut self, sender: &Sender) -> bool { + if let Some(context_menu) = self.context_menu.take() { send_to_constellation( sender, - ConstellationMsg::NewWebView(url, menu_webview.webview_id), + ConstellationMsg::CloseWebView(context_menu.webview().webview_id), ); - - self.append_dialog_webview(&menu_webview); + return true; } + false } #[cfg(linux)] @@ -711,9 +728,8 @@ impl Window { return self .context_menu .as_ref() - .and_then(|context_menu| context_menu.webview.as_ref()) - .and_then(|webview| { - if webview.webview_id == webview_id { + .and_then(|context_menu| { + if context_menu.webview().webview_id == webview_id { return Some(true); } None @@ -766,38 +782,6 @@ impl Window { _ => {} } } - - #[cfg(any(target_os = "macos", target_os = "windows"))] - fn handle_context_menu_event(&self, sender: &Sender, event: MenuEvent) { - // TODO: should be more flexible to handle different menu items - match event.id().0.as_str() { - "back" => { - send_to_constellation( - sender, - ConstellationMsg::TraverseHistory( - self.webview.as_ref().unwrap().webview_id, - TraversalDirection::Back(1), - ), - ); - } - "forward" => { - send_to_constellation( - sender, - ConstellationMsg::TraverseHistory( - self.webview.as_ref().unwrap().webview_id, - TraversalDirection::Forward(1), - ), - ); - } - "reload" => { - send_to_constellation( - sender, - ConstellationMsg::Reload(self.webview.as_ref().unwrap().webview_id), - ); - } - _ => {} - } - } } // Non-decorated window resizing for Windows and Linux.