Bridge web surface keyboard input
This commit is contained in:
@@ -86,6 +86,9 @@ impl ServoSidecarClient {
|
|||||||
.arg("--click-y")
|
.arg("--click-y")
|
||||||
.arg(click_point.y.to_string());
|
.arg(click_point.y.to_string());
|
||||||
}
|
}
|
||||||
|
if let Some(typed_text) = request.typed_text.as_deref() {
|
||||||
|
command.arg("--type-text").arg(typed_text);
|
||||||
|
}
|
||||||
|
|
||||||
let mut child = command
|
let mut child = command
|
||||||
.stdout(Stdio::piped())
|
.stdout(Stdio::piped())
|
||||||
@@ -120,12 +123,13 @@ pub struct SidecarSnapshotRequest {
|
|||||||
scroll_x: i32,
|
scroll_x: i32,
|
||||||
scroll_y: i32,
|
scroll_y: i32,
|
||||||
click_point: Option<SidecarClickPoint>,
|
click_point: Option<SidecarClickPoint>,
|
||||||
|
typed_text: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SidecarSnapshotRequest {
|
impl SidecarSnapshotRequest {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(url: UrlText, width: u32, height: u32) -> Self {
|
pub fn new(url: UrlText, width: u32, height: u32) -> Self {
|
||||||
Self { url, width, height, scroll_x: 0, scroll_y: 0, click_point: None }
|
Self { url, width, height, scroll_x: 0, scroll_y: 0, click_point: None, typed_text: None }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
@@ -140,6 +144,17 @@ impl SidecarSnapshotRequest {
|
|||||||
self.click_point = Some(SidecarClickPoint { x, y });
|
self.click_point = Some(SidecarClickPoint { x, y });
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn with_typed_text(mut self, typed_text: String) -> Self {
|
||||||
|
self.typed_text = Some(typed_text);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub(crate) fn typed_text_for_test(&self) -> Option<&str> {
|
||||||
|
self.typed_text.as_deref()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
|||||||
@@ -15,9 +15,11 @@ mod splits;
|
|||||||
mod tab_groups;
|
mod tab_groups;
|
||||||
mod tab_lifecycle;
|
mod tab_lifecycle;
|
||||||
mod web_surface;
|
mod web_surface;
|
||||||
|
mod web_surface_controller;
|
||||||
mod web_surface_frame;
|
mod web_surface_frame;
|
||||||
mod web_surface_geometry;
|
mod web_surface_geometry;
|
||||||
mod web_surface_image;
|
mod web_surface_image;
|
||||||
|
mod web_surface_keyboard;
|
||||||
mod web_surface_state;
|
mod web_surface_state;
|
||||||
mod web_surface_view;
|
mod web_surface_view;
|
||||||
|
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ impl ElyShell {
|
|||||||
div()
|
div()
|
||||||
.size_full()
|
.size_full()
|
||||||
.track_focus(&self.focus_handle)
|
.track_focus(&self.focus_handle)
|
||||||
|
.capture_key_down(cx.listener(Self::on_external_web_key_down))
|
||||||
.on_action(cx.listener(Self::on_close_current_tab))
|
.on_action(cx.listener(Self::on_close_current_tab))
|
||||||
.on_action(cx.listener(Self::on_focus_address_bar))
|
.on_action(cx.listener(Self::on_focus_address_bar))
|
||||||
.on_action(cx.listener(Self::on_focus_command_mode))
|
.on_action(cx.listener(Self::on_focus_command_mode))
|
||||||
|
|||||||
@@ -1,22 +1,18 @@
|
|||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
use ely_domain::{BrowserTab, TabId};
|
use ely_domain::{BrowserTab, TabId};
|
||||||
use gpui::{AnyElement, Bounds, Context, Pixels, Point};
|
use gpui::{Bounds, Pixels, Point};
|
||||||
|
|
||||||
use crate::services::servo_sidecar::{ServoSidecarError, SidecarSnapshot, SidecarSnapshotRequest};
|
use crate::services::servo_sidecar::SidecarSnapshotRequest;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
ElyShell,
|
|
||||||
web_surface_frame::WebSurfaceFrame,
|
web_surface_frame::WebSurfaceFrame,
|
||||||
web_surface_geometry::{
|
web_surface_geometry::{
|
||||||
WebSurfaceClickPoint, WebSurfaceScrollDelta, WebSurfaceScrollOffset, WebSurfaceSize,
|
WebSurfaceClickPoint, WebSurfaceScrollDelta, WebSurfaceScrollOffset, WebSurfaceSize,
|
||||||
},
|
},
|
||||||
web_surface_state::{
|
web_surface_state::{
|
||||||
WebSurfaceClickState, WebSurfaceClient, WebSurfaceRequest, WebSurfaceScrollState,
|
WebSurfaceClickState, WebSurfaceClient, WebSurfaceKeyboardFocusState, WebSurfaceRequest,
|
||||||
WebSurfaceState,
|
WebSurfaceScrollState, WebSurfaceState, WebSurfaceTextInputState,
|
||||||
},
|
|
||||||
web_surface_view::{
|
|
||||||
render_failed_web_surface, render_loading_web_surface, render_ready_web_surface,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -24,7 +20,9 @@ pub(super) struct WebSurfaceStore {
|
|||||||
client: WebSurfaceClient,
|
client: WebSurfaceClient,
|
||||||
pending_viewport_sizes: BTreeMap<TabId, WebSurfaceSize>,
|
pending_viewport_sizes: BTreeMap<TabId, WebSurfaceSize>,
|
||||||
click_points: BTreeMap<TabId, WebSurfaceClickState>,
|
click_points: BTreeMap<TabId, WebSurfaceClickState>,
|
||||||
|
keyboard_focus: Option<WebSurfaceKeyboardFocusState>,
|
||||||
scroll_offsets: BTreeMap<TabId, WebSurfaceScrollState>,
|
scroll_offsets: BTreeMap<TabId, WebSurfaceScrollState>,
|
||||||
|
typed_texts: BTreeMap<TabId, WebSurfaceTextInputState>,
|
||||||
viewport_bounds: BTreeMap<TabId, Bounds<Pixels>>,
|
viewport_bounds: BTreeMap<TabId, Bounds<Pixels>>,
|
||||||
viewport_sizes: BTreeMap<TabId, WebSurfaceSize>,
|
viewport_sizes: BTreeMap<TabId, WebSurfaceSize>,
|
||||||
states: BTreeMap<TabId, WebSurfaceState>,
|
states: BTreeMap<TabId, WebSurfaceState>,
|
||||||
@@ -36,18 +34,20 @@ impl WebSurfaceStore {
|
|||||||
client: WebSurfaceClient::new(),
|
client: WebSurfaceClient::new(),
|
||||||
pending_viewport_sizes: BTreeMap::new(),
|
pending_viewport_sizes: BTreeMap::new(),
|
||||||
click_points: BTreeMap::new(),
|
click_points: BTreeMap::new(),
|
||||||
|
keyboard_focus: None,
|
||||||
scroll_offsets: BTreeMap::new(),
|
scroll_offsets: BTreeMap::new(),
|
||||||
|
typed_texts: BTreeMap::new(),
|
||||||
viewport_bounds: BTreeMap::new(),
|
viewport_bounds: BTreeMap::new(),
|
||||||
viewport_sizes: BTreeMap::new(),
|
viewport_sizes: BTreeMap::new(),
|
||||||
states: BTreeMap::new(),
|
states: BTreeMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn state(&self, tab_id: &TabId) -> Option<&WebSurfaceState> {
|
pub(super) fn state(&self, tab_id: &TabId) -> Option<&WebSurfaceState> {
|
||||||
self.states.get(tab_id)
|
self.states.get(tab_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prepare_request(&mut self, tab: &BrowserTab) -> Option<WebSurfaceRequest> {
|
pub(super) fn prepare_request(&mut self, tab: &BrowserTab) -> Option<WebSurfaceRequest> {
|
||||||
if !is_external_web_url(tab.url().as_str()) {
|
if !is_external_web_url(tab.url().as_str()) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
@@ -56,10 +56,19 @@ impl WebSurfaceStore {
|
|||||||
let requested_url = tab.url().as_str().to_string();
|
let requested_url = tab.url().as_str().to_string();
|
||||||
let scroll_offset = self.scroll_offset_for(tab.id(), requested_url.as_str());
|
let scroll_offset = self.scroll_offset_for(tab.id(), requested_url.as_str());
|
||||||
let click_point = self.click_point_for(tab.id(), requested_url.as_str(), scroll_offset);
|
let click_point = self.click_point_for(tab.id(), requested_url.as_str(), scroll_offset);
|
||||||
|
let typed_text =
|
||||||
|
self.typed_text_for(tab.id(), requested_url.as_str(), scroll_offset, click_point);
|
||||||
if self.is_loading_requested_url(tab.id(), requested_url.as_str()) {
|
if self.is_loading_requested_url(tab.id(), requested_url.as_str()) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
if self.has_current_state(tab.id(), &requested_url, size, scroll_offset, click_point) {
|
if self.has_current_state(
|
||||||
|
tab.id(),
|
||||||
|
&requested_url,
|
||||||
|
size,
|
||||||
|
scroll_offset,
|
||||||
|
click_point,
|
||||||
|
typed_text.as_deref(),
|
||||||
|
) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,6 +82,7 @@ impl WebSurfaceStore {
|
|||||||
size,
|
size,
|
||||||
scroll_offset,
|
scroll_offset,
|
||||||
click_point,
|
click_point,
|
||||||
|
typed_text: typed_text.clone(),
|
||||||
message: message.clone(),
|
message: message.clone(),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@@ -87,6 +97,7 @@ impl WebSurfaceStore {
|
|||||||
size,
|
size,
|
||||||
scroll_offset,
|
scroll_offset,
|
||||||
click_point,
|
click_point,
|
||||||
|
typed_text: typed_text.clone(),
|
||||||
previous_frame: self.previous_ready_frame(tab.id(), requested_url.as_str()),
|
previous_frame: self.previous_ready_frame(tab.id(), requested_url.as_str()),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@@ -97,6 +108,9 @@ impl WebSurfaceStore {
|
|||||||
if let Some(click_point) = click_point {
|
if let Some(click_point) = click_point {
|
||||||
snapshot_request = snapshot_request.with_click_point(click_point.x(), click_point.y());
|
snapshot_request = snapshot_request.with_click_point(click_point.x(), click_point.y());
|
||||||
}
|
}
|
||||||
|
if let Some(typed_text) = typed_text.clone() {
|
||||||
|
snapshot_request = snapshot_request.with_typed_text(typed_text);
|
||||||
|
}
|
||||||
|
|
||||||
Some(WebSurfaceRequest {
|
Some(WebSurfaceRequest {
|
||||||
tab_id: tab.id().clone(),
|
tab_id: tab.id().clone(),
|
||||||
@@ -104,6 +118,7 @@ impl WebSurfaceStore {
|
|||||||
size,
|
size,
|
||||||
scroll_offset,
|
scroll_offset,
|
||||||
click_point,
|
click_point,
|
||||||
|
typed_text,
|
||||||
client,
|
client,
|
||||||
snapshot_request,
|
snapshot_request,
|
||||||
})
|
})
|
||||||
@@ -116,6 +131,7 @@ impl WebSurfaceStore {
|
|||||||
size: WebSurfaceSize,
|
size: WebSurfaceSize,
|
||||||
scroll_offset: WebSurfaceScrollOffset,
|
scroll_offset: WebSurfaceScrollOffset,
|
||||||
click_point: Option<WebSurfaceClickPoint>,
|
click_point: Option<WebSurfaceClickPoint>,
|
||||||
|
typed_text: Option<&str>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
match self.states.get(tab_id) {
|
match self.states.get(tab_id) {
|
||||||
Some(WebSurfaceState::Loading {
|
Some(WebSurfaceState::Loading {
|
||||||
@@ -123,42 +139,48 @@ impl WebSurfaceStore {
|
|||||||
size: current_size,
|
size: current_size,
|
||||||
scroll_offset: current_scroll_offset,
|
scroll_offset: current_scroll_offset,
|
||||||
click_point: current_click_point,
|
click_point: current_click_point,
|
||||||
|
typed_text: current_typed_text,
|
||||||
..
|
..
|
||||||
}) => {
|
}) => {
|
||||||
current_url == requested_url
|
current_url == requested_url
|
||||||
&& *current_size == size
|
&& *current_size == size
|
||||||
&& *current_scroll_offset == scroll_offset
|
&& *current_scroll_offset == scroll_offset
|
||||||
&& *current_click_point == click_point
|
&& *current_click_point == click_point
|
||||||
|
&& current_typed_text.as_deref() == typed_text
|
||||||
}
|
}
|
||||||
Some(WebSurfaceState::Ready(frame)) => {
|
Some(WebSurfaceState::Ready(frame)) => {
|
||||||
frame.requested_url == requested_url
|
frame.requested_url == requested_url
|
||||||
&& frame.size() == size
|
&& frame.size() == size
|
||||||
&& frame.scroll_offset() == scroll_offset
|
&& frame.scroll_offset() == scroll_offset
|
||||||
&& frame.click_point() == click_point
|
&& frame.click_point() == click_point
|
||||||
|
&& frame.typed_text() == typed_text
|
||||||
}
|
}
|
||||||
Some(WebSurfaceState::Failed {
|
Some(WebSurfaceState::Failed {
|
||||||
requested_url: current_url,
|
requested_url: current_url,
|
||||||
size: current_size,
|
size: current_size,
|
||||||
scroll_offset: current_scroll_offset,
|
scroll_offset: current_scroll_offset,
|
||||||
click_point: current_click_point,
|
click_point: current_click_point,
|
||||||
|
typed_text: current_typed_text,
|
||||||
..
|
..
|
||||||
}) => {
|
}) => {
|
||||||
current_url == requested_url
|
current_url == requested_url
|
||||||
&& *current_size == size
|
&& *current_size == size
|
||||||
&& *current_scroll_offset == scroll_offset
|
&& *current_scroll_offset == scroll_offset
|
||||||
&& *current_click_point == click_point
|
&& *current_click_point == click_point
|
||||||
|
&& current_typed_text.as_deref() == typed_text
|
||||||
}
|
}
|
||||||
None => false,
|
None => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_loading(
|
pub(super) fn is_loading(
|
||||||
&self,
|
&self,
|
||||||
tab_id: &TabId,
|
tab_id: &TabId,
|
||||||
requested_url: &str,
|
requested_url: &str,
|
||||||
size: WebSurfaceSize,
|
size: WebSurfaceSize,
|
||||||
scroll_offset: WebSurfaceScrollOffset,
|
scroll_offset: WebSurfaceScrollOffset,
|
||||||
click_point: Option<WebSurfaceClickPoint>,
|
click_point: Option<WebSurfaceClickPoint>,
|
||||||
|
typed_text: Option<&str>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
matches!(
|
matches!(
|
||||||
self.states.get(tab_id),
|
self.states.get(tab_id),
|
||||||
@@ -167,12 +189,14 @@ impl WebSurfaceStore {
|
|||||||
size: current_size,
|
size: current_size,
|
||||||
scroll_offset: current_scroll_offset,
|
scroll_offset: current_scroll_offset,
|
||||||
click_point: current_click_point,
|
click_point: current_click_point,
|
||||||
|
typed_text: current_typed_text,
|
||||||
..
|
..
|
||||||
})
|
})
|
||||||
if current_url == requested_url
|
if current_url == requested_url
|
||||||
&& *current_size == size
|
&& *current_size == size
|
||||||
&& *current_scroll_offset == scroll_offset
|
&& *current_scroll_offset == scroll_offset
|
||||||
&& *current_click_point == click_point
|
&& *current_click_point == click_point
|
||||||
|
&& current_typed_text.as_deref() == typed_text
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -196,11 +220,11 @@ impl WebSurfaceStore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finish(&mut self, tab_id: TabId, state: WebSurfaceState) {
|
pub(super) fn finish(&mut self, tab_id: TabId, state: WebSurfaceState) {
|
||||||
self.states.insert(tab_id, state);
|
self.states.insert(tab_id, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn record_scroll_delta(
|
pub(super) fn record_scroll_delta(
|
||||||
&mut self,
|
&mut self,
|
||||||
tab_id: &TabId,
|
tab_id: &TabId,
|
||||||
requested_url: &str,
|
requested_url: &str,
|
||||||
@@ -225,10 +249,12 @@ impl WebSurfaceStore {
|
|||||||
|
|
||||||
state.offset = next_offset;
|
state.offset = next_offset;
|
||||||
self.click_points.remove(tab_id);
|
self.click_points.remove(tab_id);
|
||||||
|
self.typed_texts.remove(tab_id);
|
||||||
|
self.keyboard_focus = None;
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
fn record_viewport_size(&mut self, tab_id: &TabId, bounds: Bounds<Pixels>) -> bool {
|
pub(super) fn record_viewport_size(&mut self, tab_id: &TabId, bounds: Bounds<Pixels>) -> bool {
|
||||||
let Some(size) = WebSurfaceSize::from_bounds(bounds) else {
|
let Some(size) = WebSurfaceSize::from_bounds(bounds) else {
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
@@ -255,7 +281,7 @@ impl WebSurfaceStore {
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
fn record_click_point(
|
pub(super) fn record_click_point(
|
||||||
&mut self,
|
&mut self,
|
||||||
tab_id: &TabId,
|
tab_id: &TabId,
|
||||||
requested_url: &str,
|
requested_url: &str,
|
||||||
@@ -273,14 +299,60 @@ impl WebSurfaceStore {
|
|||||||
scroll_offset: self.scroll_offset_for(tab_id, requested_url),
|
scroll_offset: self.scroll_offset_for(tab_id, requested_url),
|
||||||
point,
|
point,
|
||||||
};
|
};
|
||||||
|
self.keyboard_focus = Some(WebSurfaceKeyboardFocusState {
|
||||||
|
tab_id: tab_id.clone(),
|
||||||
|
requested_url: requested_url.to_string(),
|
||||||
|
scroll_offset: state.scroll_offset,
|
||||||
|
click_point: state.point,
|
||||||
|
});
|
||||||
if self.click_points.get(tab_id) == Some(&state) {
|
if self.click_points.get(tab_id) == Some(&state) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.typed_texts.remove(tab_id);
|
||||||
self.click_points.insert(tab_id.clone(), state);
|
self.click_points.insert(tab_id.clone(), state);
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) fn record_typed_text(
|
||||||
|
&mut self,
|
||||||
|
tab_id: &TabId,
|
||||||
|
requested_url: &str,
|
||||||
|
text: &str,
|
||||||
|
) -> bool {
|
||||||
|
if text.is_empty() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let Some(focus) = self.keyboard_focus.as_ref() else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
if focus.tab_id != *tab_id || focus.requested_url != requested_url {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let entry =
|
||||||
|
self.typed_texts.entry(tab_id.clone()).or_insert_with(|| WebSurfaceTextInputState {
|
||||||
|
requested_url: requested_url.to_string(),
|
||||||
|
scroll_offset: focus.scroll_offset,
|
||||||
|
click_point: focus.click_point,
|
||||||
|
text: String::new(),
|
||||||
|
});
|
||||||
|
if entry.requested_url != requested_url
|
||||||
|
|| entry.scroll_offset != focus.scroll_offset
|
||||||
|
|| entry.click_point != focus.click_point
|
||||||
|
{
|
||||||
|
*entry = WebSurfaceTextInputState {
|
||||||
|
requested_url: requested_url.to_string(),
|
||||||
|
scroll_offset: focus.scroll_offset,
|
||||||
|
click_point: focus.click_point,
|
||||||
|
text: String::new(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
entry.text.push_str(text);
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
fn scroll_offset_for(&self, tab_id: &TabId, requested_url: &str) -> WebSurfaceScrollOffset {
|
fn scroll_offset_for(&self, tab_id: &TabId, requested_url: &str) -> WebSurfaceScrollOffset {
|
||||||
self.scroll_offsets
|
self.scroll_offsets
|
||||||
.get(tab_id)
|
.get(tab_id)
|
||||||
@@ -302,150 +374,69 @@ impl WebSurfaceStore {
|
|||||||
})
|
})
|
||||||
.map(|state| state.point)
|
.map(|state| state.point)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl ElyShell {
|
fn typed_text_for(
|
||||||
pub(super) fn render_external_web_canvas(
|
&self,
|
||||||
&mut self,
|
tab_id: &TabId,
|
||||||
tab: &BrowserTab,
|
requested_url: &str,
|
||||||
cx: &mut Context<Self>,
|
|
||||||
) -> AnyElement {
|
|
||||||
self.ensure_external_web_frame(tab, cx);
|
|
||||||
|
|
||||||
let state_entity = cx.entity().clone();
|
|
||||||
match self.web_surfaces.state(tab.id()) {
|
|
||||||
Some(WebSurfaceState::Ready(frame)) => {
|
|
||||||
render_ready_web_surface(frame, tab, state_entity)
|
|
||||||
}
|
|
||||||
Some(WebSurfaceState::Failed { message, .. }) => {
|
|
||||||
render_failed_web_surface(tab, message.as_str(), state_entity)
|
|
||||||
}
|
|
||||||
Some(WebSurfaceState::Loading { previous_frame: Some(frame), .. }) => {
|
|
||||||
render_ready_web_surface(frame, tab, state_entity)
|
|
||||||
}
|
|
||||||
Some(WebSurfaceState::Loading { previous_frame: None, .. }) | None => {
|
|
||||||
render_loading_web_surface(tab, state_entity)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ensure_external_web_frame(&mut self, tab: &BrowserTab, cx: &mut Context<Self>) {
|
|
||||||
let Some(request) = self.web_surfaces.prepare_request(tab) else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
let WebSurfaceRequest {
|
|
||||||
tab_id,
|
|
||||||
requested_url,
|
|
||||||
size,
|
|
||||||
scroll_offset,
|
|
||||||
click_point,
|
|
||||||
client,
|
|
||||||
snapshot_request,
|
|
||||||
} = request;
|
|
||||||
cx.spawn(async move |shell, cx| {
|
|
||||||
let result = cx
|
|
||||||
.background_executor()
|
|
||||||
.spawn(async move { client.snapshot(snapshot_request) })
|
|
||||||
.await;
|
|
||||||
|
|
||||||
_ = shell.update(cx, |shell, cx| {
|
|
||||||
shell.handle_external_web_frame_result(
|
|
||||||
tab_id,
|
|
||||||
requested_url,
|
|
||||||
size,
|
|
||||||
scroll_offset,
|
|
||||||
click_point,
|
|
||||||
result,
|
|
||||||
);
|
|
||||||
cx.notify();
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.detach();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handle_external_web_frame_result(
|
|
||||||
&mut self,
|
|
||||||
tab_id: TabId,
|
|
||||||
requested_url: String,
|
|
||||||
size: WebSurfaceSize,
|
|
||||||
scroll_offset: WebSurfaceScrollOffset,
|
scroll_offset: WebSurfaceScrollOffset,
|
||||||
click_point: Option<WebSurfaceClickPoint>,
|
click_point: Option<WebSurfaceClickPoint>,
|
||||||
result: Result<SidecarSnapshot, ServoSidecarError>,
|
) -> Option<String> {
|
||||||
) {
|
let click_point = click_point?;
|
||||||
if !self.web_surfaces.is_loading(
|
self.typed_texts
|
||||||
&tab_id,
|
.get(tab_id)
|
||||||
requested_url.as_str(),
|
.filter(|state| {
|
||||||
size,
|
state.requested_url == requested_url
|
||||||
scroll_offset,
|
&& state.scroll_offset == scroll_offset
|
||||||
click_point,
|
&& state.click_point == click_point
|
||||||
) {
|
&& !state.text.is_empty()
|
||||||
return;
|
})
|
||||||
}
|
.map(|state| state.text.clone())
|
||||||
|
|
||||||
let state = match result {
|
|
||||||
Ok(snapshot) => match WebSurfaceFrame::from_snapshot(
|
|
||||||
requested_url.clone(),
|
|
||||||
scroll_offset,
|
|
||||||
click_point,
|
|
||||||
snapshot,
|
|
||||||
) {
|
|
||||||
Ok(frame) => WebSurfaceState::Ready(frame),
|
|
||||||
Err(error) => WebSurfaceState::Failed {
|
|
||||||
requested_url,
|
|
||||||
size,
|
|
||||||
scroll_offset,
|
|
||||||
click_point,
|
|
||||||
message: error.to_string(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Err(error) => WebSurfaceState::Failed {
|
|
||||||
requested_url,
|
|
||||||
size,
|
|
||||||
scroll_offset,
|
|
||||||
click_point,
|
|
||||||
message: error.to_string(),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
self.web_surfaces.finish(tab_id, state);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn record_external_web_viewport(
|
|
||||||
&mut self,
|
|
||||||
tab_id: TabId,
|
|
||||||
bounds: Bounds<Pixels>,
|
|
||||||
cx: &mut Context<Self>,
|
|
||||||
) {
|
|
||||||
if self.web_surfaces.record_viewport_size(&tab_id, bounds) {
|
|
||||||
cx.notify();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn scroll_external_web_viewport(
|
|
||||||
&mut self,
|
|
||||||
tab_id: TabId,
|
|
||||||
requested_url: String,
|
|
||||||
delta: gpui::Point<Pixels>,
|
|
||||||
cx: &mut Context<Self>,
|
|
||||||
) {
|
|
||||||
if self.web_surfaces.record_scroll_delta(&tab_id, requested_url.as_str(), delta) {
|
|
||||||
cx.notify();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn click_external_web_viewport(
|
|
||||||
&mut self,
|
|
||||||
tab_id: TabId,
|
|
||||||
requested_url: String,
|
|
||||||
position: Point<Pixels>,
|
|
||||||
cx: &mut Context<Self>,
|
|
||||||
) {
|
|
||||||
if self.web_surfaces.record_click_point(&tab_id, requested_url.as_str(), position) {
|
|
||||||
cx.notify();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn is_external_web_url(url: &str) -> bool {
|
pub(super) fn is_external_web_url(url: &str) -> bool {
|
||||||
url.starts_with("https://") || url.starts_with("http://")
|
url.starts_with("https://") || url.starts_with("http://")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use std::error::Error;
|
||||||
|
|
||||||
|
use ely_domain::{BrowserTab, ProfileId, SpaceId, TabId, UrlText};
|
||||||
|
use gpui::{Bounds, point, px, size};
|
||||||
|
|
||||||
|
use super::WebSurfaceStore;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn typed_text_enters_snapshot_request_after_clicked_viewport() -> Result<(), Box<dyn Error>> {
|
||||||
|
let mut store = WebSurfaceStore::new();
|
||||||
|
let tab = web_tab("https://example.com/form")?;
|
||||||
|
|
||||||
|
let bounds = Bounds::new(point(px(0.0), px(0.0)), size(px(640.0), px(480.0)));
|
||||||
|
assert!(store.record_viewport_size(tab.id(), bounds));
|
||||||
|
assert!(store.record_click_point(
|
||||||
|
tab.id(),
|
||||||
|
tab.url().as_str(),
|
||||||
|
point(px(160.0), px(120.0))
|
||||||
|
));
|
||||||
|
assert!(store.record_typed_text(tab.id(), tab.url().as_str(), "e"));
|
||||||
|
assert!(store.record_typed_text(tab.id(), tab.url().as_str(), "l"));
|
||||||
|
|
||||||
|
let request = store.prepare_request(&tab).ok_or("missing web surface request")?;
|
||||||
|
|
||||||
|
assert_eq!(request.typed_text.as_deref(), Some("el"));
|
||||||
|
assert_eq!(request.snapshot_request.typed_text_for_test(), Some("el"));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn web_tab(url: &str) -> Result<BrowserTab, Box<dyn Error>> {
|
||||||
|
Ok(BrowserTab::new(
|
||||||
|
TabId::new(),
|
||||||
|
SpaceId::new(),
|
||||||
|
ProfileId::new(),
|
||||||
|
"Web",
|
||||||
|
UrlText::parse(url)?,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,192 @@
|
|||||||
|
use ely_domain::{BrowserTab, TabId};
|
||||||
|
use gpui::{AnyElement, Bounds, Context, Pixels, Point};
|
||||||
|
|
||||||
|
use crate::services::servo_sidecar::{ServoSidecarError, SidecarSnapshot};
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
ElyShell,
|
||||||
|
web_surface_frame::WebSurfaceFrame,
|
||||||
|
web_surface_geometry::{WebSurfaceClickPoint, WebSurfaceScrollOffset, WebSurfaceSize},
|
||||||
|
web_surface_state::{WebSurfaceRequest, WebSurfaceState},
|
||||||
|
web_surface_view::{
|
||||||
|
render_failed_web_surface, render_loading_web_surface, render_ready_web_surface,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PendingWebSurfaceFrame {
|
||||||
|
tab_id: TabId,
|
||||||
|
requested_url: String,
|
||||||
|
size: WebSurfaceSize,
|
||||||
|
scroll_offset: WebSurfaceScrollOffset,
|
||||||
|
click_point: Option<WebSurfaceClickPoint>,
|
||||||
|
typed_text: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ElyShell {
|
||||||
|
pub(super) fn render_external_web_canvas(
|
||||||
|
&mut self,
|
||||||
|
tab: &BrowserTab,
|
||||||
|
cx: &mut Context<Self>,
|
||||||
|
) -> AnyElement {
|
||||||
|
self.ensure_external_web_frame(tab, cx);
|
||||||
|
|
||||||
|
let state_entity = cx.entity().clone();
|
||||||
|
match self.web_surfaces.state(tab.id()) {
|
||||||
|
Some(WebSurfaceState::Ready(frame)) => {
|
||||||
|
render_ready_web_surface(frame, tab, state_entity)
|
||||||
|
}
|
||||||
|
Some(WebSurfaceState::Failed { message, .. }) => {
|
||||||
|
render_failed_web_surface(tab, message.as_str(), state_entity)
|
||||||
|
}
|
||||||
|
Some(WebSurfaceState::Loading { previous_frame: Some(frame), .. }) => {
|
||||||
|
render_ready_web_surface(frame, tab, state_entity)
|
||||||
|
}
|
||||||
|
Some(WebSurfaceState::Loading { previous_frame: None, .. }) | None => {
|
||||||
|
render_loading_web_surface(tab, state_entity)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ensure_external_web_frame(&mut self, tab: &BrowserTab, cx: &mut Context<Self>) {
|
||||||
|
let Some(request) = self.web_surfaces.prepare_request(tab) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let WebSurfaceRequest {
|
||||||
|
tab_id,
|
||||||
|
requested_url,
|
||||||
|
size,
|
||||||
|
scroll_offset,
|
||||||
|
click_point,
|
||||||
|
typed_text,
|
||||||
|
client,
|
||||||
|
snapshot_request,
|
||||||
|
} = request;
|
||||||
|
let pending_frame = PendingWebSurfaceFrame {
|
||||||
|
tab_id,
|
||||||
|
requested_url,
|
||||||
|
size,
|
||||||
|
scroll_offset,
|
||||||
|
click_point,
|
||||||
|
typed_text,
|
||||||
|
};
|
||||||
|
cx.spawn(async move |shell, cx| {
|
||||||
|
let result = cx
|
||||||
|
.background_executor()
|
||||||
|
.spawn(async move { client.snapshot(snapshot_request) })
|
||||||
|
.await;
|
||||||
|
|
||||||
|
_ = shell.update(cx, |shell, cx| {
|
||||||
|
shell.handle_external_web_frame_result(pending_frame, result);
|
||||||
|
cx.notify();
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.detach();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_external_web_frame_result(
|
||||||
|
&mut self,
|
||||||
|
pending_frame: PendingWebSurfaceFrame,
|
||||||
|
result: Result<SidecarSnapshot, ServoSidecarError>,
|
||||||
|
) {
|
||||||
|
let PendingWebSurfaceFrame {
|
||||||
|
tab_id,
|
||||||
|
requested_url,
|
||||||
|
size,
|
||||||
|
scroll_offset,
|
||||||
|
click_point,
|
||||||
|
typed_text,
|
||||||
|
} = pending_frame;
|
||||||
|
if !self.web_surfaces.is_loading(
|
||||||
|
&tab_id,
|
||||||
|
requested_url.as_str(),
|
||||||
|
size,
|
||||||
|
scroll_offset,
|
||||||
|
click_point,
|
||||||
|
typed_text.as_deref(),
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let state = match result {
|
||||||
|
Ok(snapshot) => match WebSurfaceFrame::from_snapshot(
|
||||||
|
requested_url.clone(),
|
||||||
|
scroll_offset,
|
||||||
|
click_point,
|
||||||
|
typed_text.clone(),
|
||||||
|
snapshot,
|
||||||
|
) {
|
||||||
|
Ok(frame) => WebSurfaceState::Ready(frame),
|
||||||
|
Err(error) => WebSurfaceState::Failed {
|
||||||
|
requested_url,
|
||||||
|
size,
|
||||||
|
scroll_offset,
|
||||||
|
click_point,
|
||||||
|
typed_text,
|
||||||
|
message: error.to_string(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Err(error) => WebSurfaceState::Failed {
|
||||||
|
requested_url,
|
||||||
|
size,
|
||||||
|
scroll_offset,
|
||||||
|
click_point,
|
||||||
|
typed_text,
|
||||||
|
message: error.to_string(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
self.web_surfaces.finish(tab_id, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn record_external_web_viewport(
|
||||||
|
&mut self,
|
||||||
|
tab_id: TabId,
|
||||||
|
bounds: Bounds<Pixels>,
|
||||||
|
cx: &mut Context<Self>,
|
||||||
|
) {
|
||||||
|
if self.web_surfaces.record_viewport_size(&tab_id, bounds) {
|
||||||
|
cx.notify();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn scroll_external_web_viewport(
|
||||||
|
&mut self,
|
||||||
|
tab_id: TabId,
|
||||||
|
requested_url: String,
|
||||||
|
delta: gpui::Point<Pixels>,
|
||||||
|
cx: &mut Context<Self>,
|
||||||
|
) {
|
||||||
|
if self.web_surfaces.record_scroll_delta(&tab_id, requested_url.as_str(), delta) {
|
||||||
|
cx.notify();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn click_external_web_viewport(
|
||||||
|
&mut self,
|
||||||
|
tab_id: TabId,
|
||||||
|
requested_url: String,
|
||||||
|
position: Point<Pixels>,
|
||||||
|
window: &mut gpui::Window,
|
||||||
|
cx: &mut Context<Self>,
|
||||||
|
) {
|
||||||
|
self.focus_handle.focus(window);
|
||||||
|
if self.web_surfaces.record_click_point(&tab_id, requested_url.as_str(), position) {
|
||||||
|
cx.notify();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn type_text_in_external_web_viewport(
|
||||||
|
&mut self,
|
||||||
|
tab_id: TabId,
|
||||||
|
requested_url: String,
|
||||||
|
text: &str,
|
||||||
|
cx: &mut Context<Self>,
|
||||||
|
) -> bool {
|
||||||
|
if self.web_surfaces.record_typed_text(&tab_id, requested_url.as_str(), text) {
|
||||||
|
cx.notify();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,6 +20,7 @@ pub(super) struct WebSurfaceFrame {
|
|||||||
height: u32,
|
height: u32,
|
||||||
scroll_offset: WebSurfaceScrollOffset,
|
scroll_offset: WebSurfaceScrollOffset,
|
||||||
click_point: Option<WebSurfaceClickPoint>,
|
click_point: Option<WebSurfaceClickPoint>,
|
||||||
|
typed_text: Option<String>,
|
||||||
pub(super) image: Arc<RenderImage>,
|
pub(super) image: Arc<RenderImage>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,6 +29,7 @@ impl WebSurfaceFrame {
|
|||||||
requested_url: String,
|
requested_url: String,
|
||||||
scroll_offset: WebSurfaceScrollOffset,
|
scroll_offset: WebSurfaceScrollOffset,
|
||||||
click_point: Option<WebSurfaceClickPoint>,
|
click_point: Option<WebSurfaceClickPoint>,
|
||||||
|
typed_text: Option<String>,
|
||||||
snapshot: SidecarSnapshot,
|
snapshot: SidecarSnapshot,
|
||||||
) -> Result<Self, WebSurfaceError> {
|
) -> Result<Self, WebSurfaceError> {
|
||||||
let width = snapshot.width();
|
let width = snapshot.width();
|
||||||
@@ -50,6 +52,7 @@ impl WebSurfaceFrame {
|
|||||||
height,
|
height,
|
||||||
scroll_offset,
|
scroll_offset,
|
||||||
click_point,
|
click_point,
|
||||||
|
typed_text,
|
||||||
image: Arc::new(RenderImage::new([image::Frame::new(image_buffer)])),
|
image: Arc::new(RenderImage::new([image::Frame::new(image_buffer)])),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -63,9 +66,12 @@ impl WebSurfaceFrame {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn detail_label(&self) -> String {
|
pub(super) fn detail_label(&self) -> String {
|
||||||
let detail = self.scroll_offset.detail_label(self.size());
|
let mut detail = self.scroll_offset.detail_label(self.size());
|
||||||
match self.click_point {
|
if let Some(click_point) = self.click_point {
|
||||||
Some(click_point) => format!("{detail} {}", click_point.detail_label()),
|
detail = format!("{detail} {}", click_point.detail_label());
|
||||||
|
}
|
||||||
|
match self.typed_text.as_ref() {
|
||||||
|
Some(typed_text) => format!("{detail} text={}b", typed_text.len()),
|
||||||
None => detail,
|
None => detail,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -81,6 +87,10 @@ impl WebSurfaceFrame {
|
|||||||
pub(super) fn click_point(&self) -> Option<WebSurfaceClickPoint> {
|
pub(super) fn click_point(&self) -> Option<WebSurfaceClickPoint> {
|
||||||
self.click_point
|
self.click_point
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) fn typed_text(&self) -> Option<&str> {
|
||||||
|
self.typed_text.as_deref()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
|
|||||||
@@ -0,0 +1,96 @@
|
|||||||
|
use ely_domain::TabId;
|
||||||
|
use gpui::{Context, KeyDownEvent, Window};
|
||||||
|
|
||||||
|
use super::{ElyShell, ShellState, web_surface::is_external_web_url};
|
||||||
|
|
||||||
|
impl ElyShell {
|
||||||
|
pub(super) fn on_external_web_key_down(
|
||||||
|
&mut self,
|
||||||
|
event: &KeyDownEvent,
|
||||||
|
_window: &mut Window,
|
||||||
|
cx: &mut Context<Self>,
|
||||||
|
) {
|
||||||
|
let Some(text) = typed_text_from_key_down(event) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let Some((tab_id, requested_url)) = self.active_external_tab_target() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
if self.type_text_in_external_web_viewport(tab_id, requested_url, text, cx) {
|
||||||
|
cx.stop_propagation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn active_external_tab_target(&self) -> Option<(TabId, String)> {
|
||||||
|
let ShellState::Ready(core) = &self.state else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
let tab = core.active_tab().ok()?;
|
||||||
|
let requested_url = tab.url().as_str();
|
||||||
|
if !is_external_web_url(requested_url) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some((tab.id().clone(), requested_url.to_string()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn typed_text_from_key_down(event: &KeyDownEvent) -> Option<&str> {
|
||||||
|
let modifiers = &event.keystroke.modifiers;
|
||||||
|
if modifiers.control || modifiers.platform || modifiers.function {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let text = event.keystroke.key_char.as_deref()?;
|
||||||
|
let mut chars = text.chars();
|
||||||
|
let character = chars.next()?;
|
||||||
|
if chars.next().is_some() || character.is_control() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(text)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use gpui::{KeyDownEvent, Keystroke, Modifiers};
|
||||||
|
|
||||||
|
use super::typed_text_from_key_down;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn typed_text_uses_printable_key_char() {
|
||||||
|
let event = key_down("e", Some("e"), Modifiers::none());
|
||||||
|
|
||||||
|
assert_eq!(typed_text_from_key_down(&event), Some("e"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn typed_text_keeps_shifted_characters() {
|
||||||
|
let mut modifiers = Modifiers::none();
|
||||||
|
modifiers.shift = true;
|
||||||
|
let event = key_down("1", Some("!"), modifiers);
|
||||||
|
|
||||||
|
assert_eq!(typed_text_from_key_down(&event), Some("!"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn typed_text_ignores_browser_shortcuts() {
|
||||||
|
let mut modifiers = Modifiers::none();
|
||||||
|
modifiers.platform = true;
|
||||||
|
let event = key_down("l", None, modifiers);
|
||||||
|
|
||||||
|
assert_eq!(typed_text_from_key_down(&event), None);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn key_down(key: &str, key_char: Option<&str>, modifiers: Modifiers) -> KeyDownEvent {
|
||||||
|
KeyDownEvent {
|
||||||
|
keystroke: Keystroke {
|
||||||
|
modifiers,
|
||||||
|
key: key.to_string(),
|
||||||
|
key_char: key_char.map(str::to_string),
|
||||||
|
},
|
||||||
|
is_held: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -25,6 +25,21 @@ pub(super) struct WebSurfaceClickState {
|
|||||||
pub(super) point: WebSurfaceClickPoint,
|
pub(super) point: WebSurfaceClickPoint,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub(super) struct WebSurfaceKeyboardFocusState {
|
||||||
|
pub(super) tab_id: TabId,
|
||||||
|
pub(super) requested_url: String,
|
||||||
|
pub(super) scroll_offset: WebSurfaceScrollOffset,
|
||||||
|
pub(super) click_point: WebSurfaceClickPoint,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) struct WebSurfaceTextInputState {
|
||||||
|
pub(super) requested_url: String,
|
||||||
|
pub(super) scroll_offset: WebSurfaceScrollOffset,
|
||||||
|
pub(super) click_point: WebSurfaceClickPoint,
|
||||||
|
pub(super) text: String,
|
||||||
|
}
|
||||||
|
|
||||||
pub(super) enum WebSurfaceClient {
|
pub(super) enum WebSurfaceClient {
|
||||||
Ready(ServoSidecarClient),
|
Ready(ServoSidecarClient),
|
||||||
Unavailable(String),
|
Unavailable(String),
|
||||||
@@ -45,6 +60,7 @@ pub(super) struct WebSurfaceRequest {
|
|||||||
pub(super) size: WebSurfaceSize,
|
pub(super) size: WebSurfaceSize,
|
||||||
pub(super) scroll_offset: WebSurfaceScrollOffset,
|
pub(super) scroll_offset: WebSurfaceScrollOffset,
|
||||||
pub(super) click_point: Option<WebSurfaceClickPoint>,
|
pub(super) click_point: Option<WebSurfaceClickPoint>,
|
||||||
|
pub(super) typed_text: Option<String>,
|
||||||
pub(super) client: ServoSidecarClient,
|
pub(super) client: ServoSidecarClient,
|
||||||
pub(super) snapshot_request: SidecarSnapshotRequest,
|
pub(super) snapshot_request: SidecarSnapshotRequest,
|
||||||
}
|
}
|
||||||
@@ -55,6 +71,7 @@ pub(super) enum WebSurfaceState {
|
|||||||
size: WebSurfaceSize,
|
size: WebSurfaceSize,
|
||||||
scroll_offset: WebSurfaceScrollOffset,
|
scroll_offset: WebSurfaceScrollOffset,
|
||||||
click_point: Option<WebSurfaceClickPoint>,
|
click_point: Option<WebSurfaceClickPoint>,
|
||||||
|
typed_text: Option<String>,
|
||||||
previous_frame: Option<WebSurfaceFrame>,
|
previous_frame: Option<WebSurfaceFrame>,
|
||||||
},
|
},
|
||||||
Ready(WebSurfaceFrame),
|
Ready(WebSurfaceFrame),
|
||||||
@@ -63,6 +80,7 @@ pub(super) enum WebSurfaceState {
|
|||||||
size: WebSurfaceSize,
|
size: WebSurfaceSize,
|
||||||
scroll_offset: WebSurfaceScrollOffset,
|
scroll_offset: WebSurfaceScrollOffset,
|
||||||
click_point: Option<WebSurfaceClickPoint>,
|
click_point: Option<WebSurfaceClickPoint>,
|
||||||
|
typed_text: Option<String>,
|
||||||
message: String,
|
message: String,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -156,7 +156,7 @@ fn render_input_overlay(
|
|||||||
.absolute()
|
.absolute()
|
||||||
.size_full()
|
.size_full()
|
||||||
.occlude()
|
.occlude()
|
||||||
.capture_any_mouse_up(move |event, _window, cx| {
|
.capture_any_mouse_up(move |event, window, cx| {
|
||||||
if event.button != MouseButton::Left {
|
if event.button != MouseButton::Left {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -165,6 +165,7 @@ fn render_input_overlay(
|
|||||||
click_tab_id.clone(),
|
click_tab_id.clone(),
|
||||||
click_url.clone(),
|
click_url.clone(),
|
||||||
event.position,
|
event.position,
|
||||||
|
window,
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user