Incorporate suggestions.

Co-authored-by: Jonathan Schwender <55576758+jschwe@users.noreply.github.com>
Signed-off-by: Narfinger <Narfinger@users.noreply.github.com>
This commit is contained in:
Narfinger 2025-05-08 17:40:02 +02:00
parent 7de9716dc0
commit d1389378ad
6 changed files with 163 additions and 126 deletions

View file

@ -381,84 +381,83 @@ impl TouchHandler {
// As `TouchHandler` is per `WebViewRenderer` which is per `WebView` we might get a Touch Sequence Move that
// started with a down on a different webview. As the touch_sequence id is only changed on touch_down this
// move event gets a touch id which is already cleaned up.
if let Some(touch_sequence) = self.try_get_current_touch_sequence_mut() {
let idx = match touch_sequence
.active_touch_points
.iter_mut()
.position(|t| t.id == id)
{
Some(i) => i,
None => {
error!("Got a touchmove event for a non-active touch point");
return TouchMoveAction::NoAction;
},
};
let old_point = touch_sequence.active_touch_points[idx].point;
let delta = point - old_point;
let Some(touch_sequence) = self.try_get_current_touch_sequence_mut() else {
return TouchMoveAction::NoAction;
};
let idx = match touch_sequence
.active_touch_points
.iter_mut()
.position(|t| t.id == id)
{
Some(i) => i,
None => {
error!("Got a touchmove event for a non-active touch point");
return TouchMoveAction::NoAction;
},
};
let old_point = touch_sequence.active_touch_points[idx].point;
let delta = point - old_point;
let action = match touch_sequence.touch_count() {
1 => {
if let Panning { ref mut velocity } = touch_sequence.state {
// TODO: Probably we should track 1-3 more points and use a smarter algorithm
*velocity += delta;
*velocity /= 2.0;
// update the touch point every time when panning.
touch_sequence.active_touch_points[idx].point = point;
TouchMoveAction::Scroll(delta, point)
} else if delta.x.abs() > TOUCH_PAN_MIN_SCREEN_PX ||
delta.y.abs() > TOUCH_PAN_MIN_SCREEN_PX
{
touch_sequence.state = Panning {
velocity: Vector2D::new(delta.x, delta.y),
};
// No clicks should be issued after we transitioned to move.
touch_sequence.prevent_click = true;
// update the touch point
touch_sequence.active_touch_points[idx].point = point;
TouchMoveAction::Scroll(delta, point)
} else {
// We don't update the touchpoint, so multiple small moves can
// accumulate and merge into a larger move.
TouchMoveAction::NoAction
}
},
2 => {
if touch_sequence.state == Pinching ||
delta.x.abs() > TOUCH_PAN_MIN_SCREEN_PX ||
delta.y.abs() > TOUCH_PAN_MIN_SCREEN_PX
{
touch_sequence.state = Pinching;
let (d0, c0) = touch_sequence.pinch_distance_and_center();
// update the touch point with the enough distance or pinching.
touch_sequence.active_touch_points[idx].point = point;
let (d1, c1) = touch_sequence.pinch_distance_and_center();
let magnification = d1 / d0;
let scroll_delta = c1 - c0 * Scale::new(magnification);
TouchMoveAction::Zoom(magnification, scroll_delta)
} else {
// We don't update the touchpoint, so multiple small moves can
// accumulate and merge into a larger move.
TouchMoveAction::NoAction
}
},
_ => {
let action = match touch_sequence.touch_count() {
1 => {
if let Panning { ref mut velocity } = touch_sequence.state {
// TODO: Probably we should track 1-3 more points and use a smarter algorithm
*velocity += delta;
*velocity /= 2.0;
// update the touch point every time when panning.
touch_sequence.active_touch_points[idx].point = point;
touch_sequence.state = MultiTouch;
TouchMoveAction::Scroll(delta, point)
} else if delta.x.abs() > TOUCH_PAN_MIN_SCREEN_PX ||
delta.y.abs() > TOUCH_PAN_MIN_SCREEN_PX
{
touch_sequence.state = Panning {
velocity: Vector2D::new(delta.x, delta.y),
};
// No clicks should be issued after we transitioned to move.
touch_sequence.prevent_click = true;
// update the touch point
touch_sequence.active_touch_points[idx].point = point;
TouchMoveAction::Scroll(delta, point)
} else {
// We don't update the touchpoint, so multiple small moves can
// accumulate and merge into a larger move.
TouchMoveAction::NoAction
},
};
// If the touch action is not `NoAction` and the first move has not been processed,
// set pending_touch_move_action.
if TouchMoveAction::NoAction != action &&
touch_sequence.prevent_move == TouchMoveAllowed::Pending
{
touch_sequence.update_pending_touch_move_action(action);
}
action
} else {
TouchMoveAction::NoAction
}
},
2 => {
if touch_sequence.state == Pinching ||
delta.x.abs() > TOUCH_PAN_MIN_SCREEN_PX ||
delta.y.abs() > TOUCH_PAN_MIN_SCREEN_PX
{
touch_sequence.state = Pinching;
let (d0, c0) = touch_sequence.pinch_distance_and_center();
// update the touch point with the enough distance or pinching.
touch_sequence.active_touch_points[idx].point = point;
let (d1, c1) = touch_sequence.pinch_distance_and_center();
let magnification = d1 / d0;
let scroll_delta = c1 - c0 * Scale::new(magnification);
TouchMoveAction::Zoom(magnification, scroll_delta)
} else {
// We don't update the touchpoint, so multiple small moves can
// accumulate and merge into a larger move.
TouchMoveAction::NoAction
}
},
_ => {
touch_sequence.active_touch_points[idx].point = point;
touch_sequence.state = MultiTouch;
TouchMoveAction::NoAction
},
};
// If the touch action is not `NoAction` and the first move has not been processed,
// set pending_touch_move_action.
if TouchMoveAction::NoAction != action &&
touch_sequence.prevent_move == TouchMoveAllowed::Pending
{
touch_sequence.update_pending_touch_move_action(action);
}
action
}
pub fn on_touch_up(&mut self, id: TouchId, point: Point2D<f32, DevicePixel>) {
@ -540,23 +539,24 @@ impl TouchHandler {
pub fn on_touch_cancel(&mut self, id: TouchId, _point: Point2D<f32, DevicePixel>) {
// A similar thing with touch move can happen here where the event is coming from a different webview.
if let Some(touch_sequence) = self.try_get_current_touch_sequence_mut() {
match touch_sequence
.active_touch_points
.iter()
.position(|t| t.id == id)
{
Some(i) => {
touch_sequence.active_touch_points.swap_remove(i);
},
None => {
warn!("Got a touchcancel event for a non-active touch point");
return;
},
}
if touch_sequence.active_touch_points.is_empty() {
touch_sequence.state = Finished;
}
let Some(touch_sequence) = self.try_get_current_touch_sequence_mut() else {
return;
};
match touch_sequence
.active_touch_points
.iter()
.position(|t| t.id == id)
{
Some(i) => {
touch_sequence.active_touch_points.swap_remove(i);
},
None => {
warn!("Got a touchcancel event for a non-active touch point");
return;
},
}
if touch_sequence.active_touch_points.is_empty() {
touch_sequence.state = Finished;
}
}
}

View file

@ -404,7 +404,8 @@ impl WebViewRenderer {
self.dispatch_input_event(event);
}
/// Send touch event to the pipeline. Returns true if the event was send
/// Send a [`TouchEvent`] to the Constellation for this [`WebViewRenderer`].
/// Returns true if the event was send
fn send_touch_event(&self, mut event: TouchEvent) -> bool {
let get_pipeline_details = |pipeline_id| self.pipelines.get(&pipeline_id);
let Some(result) = self

View file

@ -124,7 +124,7 @@ pub enum TouchEventType {
pub struct TouchId(pub i32);
/// An ID for a sequence of touch events between a `Down` and the `Up` or `Cancel` event.
/// The ID is the same for all events between `Down`` and `Up`` or `Cancel``
/// The ID is the same for all events between `Down` and `Up` or `Cancel`
#[repr(transparent)]
#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub struct TouchSequenceId(u32);

View file

@ -337,13 +337,9 @@ impl RunningAppState {
self.inner_mut().webviews.insert(webview.id(), webview);
}
pub(crate) fn activate_webview(&self, id: u32) {
let inner = self.inner();
let webview = inner
.creation_order
.get(id as usize)
.and_then(|id| inner.webviews.get(id));
if let Some(webview) = webview {
/// The focused webview will not be immediately valid via `active_webview()`
pub(crate) fn focus_webview(&self, id: WebViewId) {
if let Some(webview) = self.inner().webviews.get(&id) {
webview.focus();
} else {
error!("We could not find the webview with this id {id}");
@ -366,14 +362,14 @@ impl RunningAppState {
Ok(webview_id)
}
fn newest_webview(&self) -> Option<WebView> {
pub(crate) fn newest_webview(&self) -> Option<WebView> {
self.inner()
.creation_order
.last()
.and_then(|id| self.inner().webviews.get(id).cloned())
}
fn active_webview(&self) -> WebView {
pub(crate) fn active_webview(&self) -> WebView {
self.inner()
.focused_webview_id
.and_then(|id| self.inner().webviews.get(&id).cloned())

View file

@ -24,7 +24,7 @@ use ohos_ime_sys::types::InputMethod_EnterKeyType;
use servo::style::Zero;
use servo::{
AlertResponse, EventLoopWaker, InputMethodType, LoadStatus, MediaSessionPlaybackState,
PermissionRequest, SimpleDialog, WebView,
PermissionRequest, SimpleDialog, WebView, WebViewId,
};
use xcomponent_sys::{
OH_NativeXComponent, OH_NativeXComponent_Callback, OH_NativeXComponent_GetKeyEvent,
@ -131,10 +131,19 @@ static PROMPT_TOAST: OnceLock<
ThreadsafeFunction<String, (), String, false, false, PROMPT_QUEUE_SIZE>,
> = OnceLock::new();
/// Storing webview related items
struct NativeWebViewComponents {
/// The id of the related webview
id: WebViewId,
/// The XComponentWrapper for the above webview
xcomponent: XComponentWrapper,
/// The WindowWrapper for the above webview
window: WindowWrapper,
}
/// Currently we do not support different contexts for different windows but we might want to change tabs.
/// For this we store the window context for every tab and change the compositor by hand.
static WEBVIEW_TO_RAW_HANDLE: Mutex<Vec<(XComponentWrapper, WindowWrapper)>> =
Mutex::new(Vec::new());
static NATIVE_WEBVIEWS: Mutex<Vec<NativeWebViewComponents>> = Mutex::new(Vec::new());
impl ServoAction {
fn dispatch_touch_event(
@ -194,28 +203,55 @@ impl ServoAction {
servo.present_if_needed();
},
Resize { width, height } => servo.resize(Coordinates::new(0, 0, *width, *height)),
FocusWebview(id) => {
servo.activate_webview(id.clone());
servo.pause_compositor();
let webview_lock = WEBVIEW_TO_RAW_HANDLE.lock().unwrap();
let (xcomponent_wrapper, window_wrapper) = webview_lock
.get(*id as usize)
.clone()
.expect("Could not find window handle to webview");
let (window_handle, _, coordinates) =
simpleservo::get_raw_window_handle(xcomponent_wrapper.0, window_wrapper.0);
servo.resume_compositor(window_handle, coordinates);
FocusWebview(arkts_id) => {
if let Some(native_webview_components) =
NATIVE_WEBVIEWS.lock().unwrap().get(*arkts_id as usize)
{
if (servo.active_webview().id() != native_webview_components.id) {
servo.focus_webview(native_webview_components.id);
servo.pause_compositor();
let (window_handle, _, coordinates) = simpleservo::get_raw_window_handle(
native_webview_components.xcomponent.0,
native_webview_components.window.0,
);
servo.resume_compositor(window_handle, coordinates);
let url = servo
.active_webview()
.url()
.map(|u| u.to_string())
.unwrap_or(String::from("about:blank"));
SET_URL_BAR_CB
.get()
.map(|f| f.call(url, ThreadsafeFunctionCallMode::Blocking));
}
} else {
error!("Could not find webview to focus");
}
},
NewWebview(xcomponent, window) => {
servo.pause_compositor();
servo.new_toplevel_webview("about:blank".parse().unwrap());
let (window_handle, _, coordinates) =
simpleservo::get_raw_window_handle(xcomponent.0, window.0);
WEBVIEW_TO_RAW_HANDLE
servo.resume_compositor(window_handle, coordinates);
let webview = servo.newest_webview().expect("There should always be one");
let id = webview.id();
NATIVE_WEBVIEWS
.lock()
.unwrap()
.push((xcomponent.clone(), window.clone()));
servo.resume_compositor(window_handle, coordinates);
.push(NativeWebViewComponents {
id: id,
xcomponent: xcomponent.clone(),
window: window.clone(),
});
let url = webview
.url()
.map(|u| u.to_string())
.unwrap_or(String::from("about:blank"));
SET_URL_BAR_CB
.get()
.map(|f| f.call(url, ThreadsafeFunctionCallMode::Blocking));
},
};
}
@ -273,11 +309,6 @@ extern "C" fn on_surface_created_cb(xcomponent: *mut OH_NativeXComponent, window
let xc = xc_wrapper;
let window = window_wrapper;
WEBVIEW_TO_RAW_HANDLE
.lock()
.unwrap()
.push((xc.clone(), window.clone()));
let init_opts = if let Ok(ServoAction::Initialize(init_opts)) = rx.recv() {
init_opts
} else {
@ -286,6 +317,15 @@ extern "C" fn on_surface_created_cb(xcomponent: *mut OH_NativeXComponent, window
let servo = simpleservo::init(*init_opts, window.0, xc.0, wakeup, callbacks)
.expect("Servo initialization failed");
NATIVE_WEBVIEWS
.lock()
.unwrap()
.push(NativeWebViewComponents {
id: servo.active_webview().id(),
xcomponent: xc,
window,
});
info!("Surface created!");
let native_vsync =
ohos_vsync::NativeVsync::new("ServoVsync").expect("Failed to create NativeVsync");

View file

@ -29,11 +29,11 @@ pub(crate) fn get_raw_window_handle(
) -> (RawWindowHandle, euclid::default::Size2D<i32>, Coordinates) {
let window_size = unsafe { super::get_xcomponent_size(xcomponent, window) }
.expect("Could not get native window size");
let (x, y) = unsafe { super::get_xcomponent_offset(xcomponent, window)}.expect("Could not get native window offset");
let (x, y) = unsafe { super::get_xcomponent_offset(xcomponent, window) }
.expect("Could not get native window offset");
let coordinates = Coordinates::new(x, y, window_size.width, window_size.height);
let native_window = NonNull::new(window).expect("Could not get native window");
let window_handle = RawWindowHandle::OhosNdk(OhosNdkWindowHandle::new(native_window));
let coordinates = Coordinates::new(0, 0, window_size.width, window_size.height);
(window_handle, window_size, coordinates)
}