mirror of
https://github.com/servo/servo.git
synced 2025-08-03 12:40:06 +01:00
Context Menu: API
This commit is contained in:
parent
ecdbea518f
commit
ed46f5985c
6 changed files with 86 additions and 3 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -5386,6 +5386,7 @@ dependencies = [
|
||||||
"core-foundation 0.6.4",
|
"core-foundation 0.6.4",
|
||||||
"getopts",
|
"getopts",
|
||||||
"gl_generator 0.11.0",
|
"gl_generator 0.11.0",
|
||||||
|
"ipc-channel",
|
||||||
"libc",
|
"libc",
|
||||||
"libloading",
|
"libloading",
|
||||||
"libservo",
|
"libservo",
|
||||||
|
|
|
@ -106,6 +106,13 @@ impl EmbedderReceiver {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize)]
|
||||||
|
pub enum ContextMenuResult {
|
||||||
|
Dismissed,
|
||||||
|
Ignored,
|
||||||
|
Selected(usize),
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize)]
|
#[derive(Deserialize, Serialize)]
|
||||||
pub enum PromptDefinition {
|
pub enum PromptDefinition {
|
||||||
/// Show a message.
|
/// Show a message.
|
||||||
|
@ -149,6 +156,8 @@ pub enum EmbedderMsg {
|
||||||
ResizeTo(DeviceIntSize),
|
ResizeTo(DeviceIntSize),
|
||||||
/// Show dialog to user
|
/// Show dialog to user
|
||||||
Prompt(PromptDefinition, PromptOrigin),
|
Prompt(PromptDefinition, PromptOrigin),
|
||||||
|
/// Show a context menu to the user
|
||||||
|
ShowContextMenu(IpcSender<ContextMenuResult>, Vec<String>),
|
||||||
/// Whether or not to allow a pipeline to load a url.
|
/// Whether or not to allow a pipeline to load a url.
|
||||||
AllowNavigationRequest(PipelineId, ServoUrl),
|
AllowNavigationRequest(PipelineId, ServoUrl),
|
||||||
/// Whether or not to allow script to open a new tab/browser
|
/// Whether or not to allow script to open a new tab/browser
|
||||||
|
@ -235,6 +244,7 @@ impl Debug for EmbedderMsg {
|
||||||
EmbedderMsg::ReportProfile(..) => write!(f, "ReportProfile"),
|
EmbedderMsg::ReportProfile(..) => write!(f, "ReportProfile"),
|
||||||
EmbedderMsg::MediaSessionEvent(..) => write!(f, "MediaSessionEvent"),
|
EmbedderMsg::MediaSessionEvent(..) => write!(f, "MediaSessionEvent"),
|
||||||
EmbedderMsg::OnDevtoolsStarted(..) => write!(f, "OnDevtoolsStarted"),
|
EmbedderMsg::OnDevtoolsStarted(..) => write!(f, "OnDevtoolsStarted"),
|
||||||
|
EmbedderMsg::ShowContextMenu(..) => write!(f, "ShowContextMenu"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ use euclid::{Point2D, Vector2D};
|
||||||
use keyboard_types::{Key, KeyboardEvent, Modifiers, ShortcutMatcher};
|
use keyboard_types::{Key, KeyboardEvent, Modifiers, ShortcutMatcher};
|
||||||
use servo::compositing::windowing::{WebRenderDebugOption, WindowEvent};
|
use servo::compositing::windowing::{WebRenderDebugOption, WindowEvent};
|
||||||
use servo::embedder_traits::{
|
use servo::embedder_traits::{
|
||||||
EmbedderMsg, FilterPattern, PermissionRequest, PromptDefinition, PromptOrigin, PromptResult,
|
ContextMenuResult, EmbedderMsg, FilterPattern, PermissionRequest, PromptDefinition, PromptOrigin, PromptResult,
|
||||||
PermissionPrompt,
|
PermissionPrompt,
|
||||||
};
|
};
|
||||||
use servo::msg::constellation_msg::TopLevelBrowsingContextId as BrowserId;
|
use servo::msg::constellation_msg::TopLevelBrowsingContextId as BrowserId;
|
||||||
|
@ -521,6 +521,9 @@ where
|
||||||
Err(()) => error!("Error running devtools server"),
|
Err(()) => error!("Error running devtools server"),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
EmbedderMsg::ShowContextMenu(sender, _) => {
|
||||||
|
let _ = sender.send(ContextMenuResult::Ignored);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ publish = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
getopts = "0.2.11"
|
getopts = "0.2.11"
|
||||||
|
ipc-channel = "0.14"
|
||||||
libservo = { path = "../../../components/servo" }
|
libservo = { path = "../../../components/servo" }
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
servo-media = { git = "https://github.com/servo/media" }
|
servo-media = { git = "https://github.com/servo/media" }
|
||||||
|
|
|
@ -8,11 +8,12 @@ extern crate log;
|
||||||
pub mod gl_glue;
|
pub mod gl_glue;
|
||||||
|
|
||||||
pub use servo::embedder_traits::{
|
pub use servo::embedder_traits::{
|
||||||
MediaSessionPlaybackState, PermissionPrompt, PermissionRequest, PromptResult,
|
ContextMenuResult, MediaSessionPlaybackState, PermissionPrompt, PermissionRequest, PromptResult,
|
||||||
};
|
};
|
||||||
pub use servo::script_traits::{MediaSessionActionType, MouseButton};
|
pub use servo::script_traits::{MediaSessionActionType, MouseButton};
|
||||||
|
|
||||||
use getopts::Options;
|
use getopts::Options;
|
||||||
|
use ipc_channel::ipc::IpcSender;
|
||||||
use servo::canvas::{SurfaceProviders, WebGlExecutor};
|
use servo::canvas::{SurfaceProviders, WebGlExecutor};
|
||||||
use servo::compositing::windowing::{
|
use servo::compositing::windowing::{
|
||||||
AnimationState, EmbedderCoordinates, EmbedderMethods, MouseWindowEvent, WindowEvent,
|
AnimationState, EmbedderCoordinates, EmbedderMethods, MouseWindowEvent, WindowEvent,
|
||||||
|
@ -103,6 +104,8 @@ pub trait HostTrait {
|
||||||
fn prompt_ok_cancel(&self, msg: String, trusted: bool) -> PromptResult;
|
fn prompt_ok_cancel(&self, msg: String, trusted: bool) -> PromptResult;
|
||||||
/// Ask for string
|
/// Ask for string
|
||||||
fn prompt_input(&self, msg: String, default: String, trusted: bool) -> Option<String>;
|
fn prompt_input(&self, msg: String, default: String, trusted: bool) -> Option<String>;
|
||||||
|
/// Show context menu
|
||||||
|
fn show_context_menu(&self, items: Vec<String>);
|
||||||
/// Page starts loading.
|
/// Page starts loading.
|
||||||
/// "Reload button" should be disabled.
|
/// "Reload button" should be disabled.
|
||||||
/// "Stop button" should be enabled.
|
/// "Stop button" should be enabled.
|
||||||
|
@ -164,6 +167,7 @@ pub struct ServoGlue {
|
||||||
browsers: Vec<BrowserId>,
|
browsers: Vec<BrowserId>,
|
||||||
events: Vec<WindowEvent>,
|
events: Vec<WindowEvent>,
|
||||||
current_url: Option<ServoUrl>,
|
current_url: Option<ServoUrl>,
|
||||||
|
context_menu_sender: Option<IpcSender<ContextMenuResult>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn servo_version() -> String {
|
pub fn servo_version() -> String {
|
||||||
|
@ -240,6 +244,7 @@ pub fn init(
|
||||||
browsers: vec![],
|
browsers: vec![],
|
||||||
events: vec![],
|
events: vec![],
|
||||||
current_url: Some(url.clone()),
|
current_url: Some(url.clone()),
|
||||||
|
context_menu_sender: None,
|
||||||
};
|
};
|
||||||
let browser_id = BrowserId::new();
|
let browser_id = BrowserId::new();
|
||||||
let _ = servo_glue.process_event(WindowEvent::NewBrowser(url, browser_id));
|
let _ = servo_glue.process_event(WindowEvent::NewBrowser(url, browser_id));
|
||||||
|
@ -503,6 +508,18 @@ impl ServoGlue {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn on_context_menu_closed(
|
||||||
|
&mut self,
|
||||||
|
result: ContextMenuResult,
|
||||||
|
) -> Result<(), &'static str> {
|
||||||
|
if let Some(sender) = self.context_menu_sender.take() {
|
||||||
|
let _ = sender.send(result);
|
||||||
|
} else {
|
||||||
|
warn!("Trying to close a context menu when no context menu is active");
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn process_event(&mut self, event: WindowEvent) -> Result<(), &'static str> {
|
fn process_event(&mut self, event: WindowEvent) -> Result<(), &'static str> {
|
||||||
self.events.push(event);
|
self.events.push(event);
|
||||||
if !self.batch_mode {
|
if !self.batch_mode {
|
||||||
|
@ -562,6 +579,17 @@ impl ServoGlue {
|
||||||
EmbedderMsg::AllowUnload(sender) => {
|
EmbedderMsg::AllowUnload(sender) => {
|
||||||
let _ = sender.send(true);
|
let _ = sender.send(true);
|
||||||
},
|
},
|
||||||
|
EmbedderMsg::ShowContextMenu(sender, items) => {
|
||||||
|
if self.context_menu_sender.is_some() {
|
||||||
|
warn!(
|
||||||
|
"Trying to show a context menu when a context menu is already active"
|
||||||
|
);
|
||||||
|
let _ = sender.send(ContextMenuResult::Ignored);
|
||||||
|
} else {
|
||||||
|
self.context_menu_sender = Some(sender);
|
||||||
|
self.callbacks.host_callbacks.show_context_menu(items);
|
||||||
|
}
|
||||||
|
},
|
||||||
EmbedderMsg::Prompt(definition, origin) => {
|
EmbedderMsg::Prompt(definition, origin) => {
|
||||||
let cb = &self.callbacks.host_callbacks;
|
let cb = &self.callbacks.host_callbacks;
|
||||||
let trusted = origin == PromptOrigin::Trusted;
|
let trusted = origin == PromptOrigin::Trusted;
|
||||||
|
|
|
@ -17,7 +17,7 @@ use env_logger;
|
||||||
use log::LevelFilter;
|
use log::LevelFilter;
|
||||||
use simpleservo::{self, gl_glue, ServoGlue, SERVO};
|
use simpleservo::{self, gl_glue, ServoGlue, SERVO};
|
||||||
use simpleservo::{
|
use simpleservo::{
|
||||||
Coordinates, EventLoopWaker, HostTrait, InitOptions, MediaSessionActionType,
|
ContextMenuResult, Coordinates, EventLoopWaker, HostTrait, InitOptions, MediaSessionActionType,
|
||||||
MediaSessionPlaybackState, MouseButton, PromptResult, VRInitOptions,
|
MediaSessionPlaybackState, MouseButton, PromptResult, VRInitOptions,
|
||||||
};
|
};
|
||||||
use std::ffi::{CStr, CString};
|
use std::ffi::{CStr, CString};
|
||||||
|
@ -230,6 +230,7 @@ pub struct CHostCallbacks {
|
||||||
trusted: bool,
|
trusted: bool,
|
||||||
) -> *const c_char,
|
) -> *const c_char,
|
||||||
pub on_devtools_started: extern "C" fn(result: CDevtoolsServerState, port: c_uint),
|
pub on_devtools_started: extern "C" fn(result: CDevtoolsServerState, port: c_uint),
|
||||||
|
pub show_context_menu: extern "C" fn(items_list: *const *const c_char, items_size: u32),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Servo options
|
/// Servo options
|
||||||
|
@ -332,6 +333,25 @@ impl CMediaSessionActionType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub enum CContextMenuResult {
|
||||||
|
Ignored,
|
||||||
|
Selected,
|
||||||
|
// Can't use Dismissed. Already used by PromptResult. See:
|
||||||
|
// https://github.com/servo/servo/issues/25986
|
||||||
|
Dismissed_,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CContextMenuResult {
|
||||||
|
pub fn convert(&self, idx: u32) -> ContextMenuResult {
|
||||||
|
match self {
|
||||||
|
CContextMenuResult::Ignored => ContextMenuResult::Ignored,
|
||||||
|
CContextMenuResult::Dismissed_ => ContextMenuResult::Dismissed,
|
||||||
|
CContextMenuResult::Selected => ContextMenuResult::Selected(idx as usize),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The returned string is not freed. This will leak.
|
/// The returned string is not freed. This will leak.
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn servo_version() -> *const c_char {
|
pub extern "C" fn servo_version() -> *const c_char {
|
||||||
|
@ -494,6 +514,14 @@ pub extern "C" fn set_batch_mode(batch: bool) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn on_context_menu_closed(result: CContextMenuResult, item: u32) {
|
||||||
|
catch_any_panic(|| {
|
||||||
|
debug!("on_context_menu_closed");
|
||||||
|
call(|s| s.on_context_menu_closed(result.convert(item)));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn resize(width: i32, height: i32) {
|
pub extern "C" fn resize(width: i32, height: i32) {
|
||||||
catch_any_panic(|| {
|
catch_any_panic(|| {
|
||||||
|
@ -874,4 +902,16 @@ impl HostTrait for HostCallbacks {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn show_context_menu(&self, items: Vec<String>) {
|
||||||
|
debug!("show_context_menu");
|
||||||
|
let size = items.len() as u32;
|
||||||
|
let cstrs: Vec<CString> = items
|
||||||
|
.into_iter()
|
||||||
|
.map(|i| CString::new(i).expect("Can't create string"))
|
||||||
|
.collect();
|
||||||
|
let ptrs: Vec<*const c_char> = cstrs.iter().map(|cstr| cstr.as_ptr()).collect();
|
||||||
|
(self.0.show_context_menu)(ptrs.as_ptr(), size);
|
||||||
|
// let _ = cstrs; // Don't drop these too early
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue