mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00:00
Merge d1389378ad
into 5ef66ce386
This commit is contained in:
commit
70162c7406
7 changed files with 258 additions and 99 deletions
|
@ -292,6 +292,10 @@ impl TouchHandler {
|
||||||
.expect("Current Touch sequence does not exist")
|
.expect("Current Touch sequence does not exist")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn try_get_current_touch_sequence_mut(&mut self) -> Option<&mut TouchSequenceInfo> {
|
||||||
|
self.touch_sequence_map.get_mut(&self.current_sequence_id)
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn get_touch_sequence(&self, sequence_id: TouchSequenceId) -> &TouchSequenceInfo {
|
pub(crate) fn get_touch_sequence(&self, sequence_id: TouchSequenceId) -> &TouchSequenceInfo {
|
||||||
self.touch_sequence_map
|
self.touch_sequence_map
|
||||||
.get(&sequence_id)
|
.get(&sequence_id)
|
||||||
|
@ -374,7 +378,12 @@ impl TouchHandler {
|
||||||
id: TouchId,
|
id: TouchId,
|
||||||
point: Point2D<f32, DevicePixel>,
|
point: Point2D<f32, DevicePixel>,
|
||||||
) -> TouchMoveAction {
|
) -> TouchMoveAction {
|
||||||
let touch_sequence = self.get_current_touch_sequence_mut();
|
// 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.
|
||||||
|
let Some(touch_sequence) = self.try_get_current_touch_sequence_mut() else {
|
||||||
|
return TouchMoveAction::NoAction;
|
||||||
|
};
|
||||||
let idx = match touch_sequence
|
let idx = match touch_sequence
|
||||||
.active_touch_points
|
.active_touch_points
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
|
@ -529,7 +538,10 @@ impl TouchHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn on_touch_cancel(&mut self, id: TouchId, _point: Point2D<f32, DevicePixel>) {
|
pub fn on_touch_cancel(&mut self, id: TouchId, _point: Point2D<f32, DevicePixel>) {
|
||||||
let touch_sequence = self.get_current_touch_sequence_mut();
|
// A similar thing with touch move can happen here where the event is coming from a different webview.
|
||||||
|
let Some(touch_sequence) = self.try_get_current_touch_sequence_mut() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
match touch_sequence
|
match touch_sequence
|
||||||
.active_touch_points
|
.active_touch_points
|
||||||
.iter()
|
.iter()
|
||||||
|
|
|
@ -404,6 +404,8 @@ impl WebViewRenderer {
|
||||||
self.dispatch_input_event(event);
|
self.dispatch_input_event(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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 {
|
fn send_touch_event(&self, mut event: TouchEvent) -> bool {
|
||||||
let get_pipeline_details = |pipeline_id| self.pipelines.get(&pipeline_id);
|
let get_pipeline_details = |pipeline_id| self.pipelines.get(&pipeline_id);
|
||||||
let Some(result) = self
|
let Some(result) = self
|
||||||
|
|
|
@ -180,6 +180,7 @@ pub enum TouchEventType {
|
||||||
pub struct TouchId(pub i32);
|
pub struct TouchId(pub i32);
|
||||||
|
|
||||||
/// An ID for a sequence of touch events between a `Down` and the `Up` or `Cancel` event.
|
/// 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`
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, Hash, PartialEq, Serialize)]
|
#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, Hash, PartialEq, Serialize)]
|
||||||
pub struct TouchSequenceId(u32);
|
pub struct TouchSequenceId(u32);
|
||||||
|
|
|
@ -355,6 +355,15 @@ impl RunningAppState {
|
||||||
self.inner_mut().webviews.insert(webview.id(), webview);
|
self.inner_mut().webviews.insert(webview.id(), 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}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn inner(&self) -> Ref<RunningAppStateInner> {
|
fn inner(&self) -> Ref<RunningAppStateInner> {
|
||||||
self.inner.borrow()
|
self.inner.borrow()
|
||||||
}
|
}
|
||||||
|
@ -371,14 +380,14 @@ impl RunningAppState {
|
||||||
Ok(webview_id)
|
Ok(webview_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn newest_webview(&self) -> Option<WebView> {
|
pub(crate) fn newest_webview(&self) -> Option<WebView> {
|
||||||
self.inner()
|
self.inner()
|
||||||
.creation_order
|
.creation_order
|
||||||
.last()
|
.last()
|
||||||
.and_then(|id| self.inner().webviews.get(id).cloned())
|
.and_then(|id| self.inner().webviews.get(id).cloned())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn active_webview(&self) -> WebView {
|
pub(crate) fn active_webview(&self) -> WebView {
|
||||||
self.inner()
|
self.inner()
|
||||||
.focused_webview_id
|
.focused_webview_id
|
||||||
.and_then(|id| self.inner().webviews.get(&id).cloned())
|
.and_then(|id| self.inner().webviews.get(&id).cloned())
|
||||||
|
|
|
@ -6,8 +6,9 @@
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::mem::MaybeUninit;
|
use std::mem::MaybeUninit;
|
||||||
use std::os::raw::c_void;
|
use std::os::raw::c_void;
|
||||||
|
use std::rc::Rc;
|
||||||
use std::sync::mpsc::{Receiver, Sender};
|
use std::sync::mpsc::{Receiver, Sender};
|
||||||
use std::sync::{LazyLock, Once, OnceLock, mpsc};
|
use std::sync::{LazyLock, Mutex, Once, OnceLock, mpsc};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::thread::sleep;
|
use std::thread::sleep;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
@ -23,7 +24,7 @@ use ohos_ime_sys::types::InputMethod_EnterKeyType;
|
||||||
use servo::style::Zero;
|
use servo::style::Zero;
|
||||||
use servo::{
|
use servo::{
|
||||||
AlertResponse, EventLoopWaker, InputMethodType, LoadStatus, MediaSessionPlaybackState,
|
AlertResponse, EventLoopWaker, InputMethodType, LoadStatus, MediaSessionPlaybackState,
|
||||||
PermissionRequest, SimpleDialog, WebView,
|
PermissionRequest, SimpleDialog, WebView, WebViewId,
|
||||||
};
|
};
|
||||||
use xcomponent_sys::{
|
use xcomponent_sys::{
|
||||||
OH_NativeXComponent, OH_NativeXComponent_Callback, OH_NativeXComponent_GetKeyEvent,
|
OH_NativeXComponent, OH_NativeXComponent_Callback, OH_NativeXComponent_GetKeyEvent,
|
||||||
|
@ -69,9 +70,11 @@ fn call(action: ServoAction) -> Result<(), CallError> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
struct XComponentWrapper(*mut OH_NativeXComponent);
|
#[derive(Clone)]
|
||||||
|
pub(crate) struct XComponentWrapper(*mut OH_NativeXComponent);
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
struct WindowWrapper(*mut c_void);
|
#[derive(Clone)]
|
||||||
|
pub(crate) struct WindowWrapper(*mut c_void);
|
||||||
unsafe impl Send for XComponentWrapper {}
|
unsafe impl Send for XComponentWrapper {}
|
||||||
unsafe impl Send for WindowWrapper {}
|
unsafe impl Send for WindowWrapper {}
|
||||||
|
|
||||||
|
@ -84,7 +87,6 @@ pub(super) enum TouchEventType {
|
||||||
Unknown,
|
Unknown,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub(super) enum ServoAction {
|
pub(super) enum ServoAction {
|
||||||
WakeUp,
|
WakeUp,
|
||||||
LoadUrl(String),
|
LoadUrl(String),
|
||||||
|
@ -108,6 +110,8 @@ pub(super) enum ServoAction {
|
||||||
width: i32,
|
width: i32,
|
||||||
height: i32,
|
height: i32,
|
||||||
},
|
},
|
||||||
|
FocusWebview(u32),
|
||||||
|
NewWebview(XComponentWrapper, WindowWrapper),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Queue length for the thread-safe function to submit URL updates to ArkTS
|
/// Queue length for the thread-safe function to submit URL updates to ArkTS
|
||||||
|
@ -127,6 +131,20 @@ static PROMPT_TOAST: OnceLock<
|
||||||
ThreadsafeFunction<String, (), String, false, false, PROMPT_QUEUE_SIZE>,
|
ThreadsafeFunction<String, (), String, false, false, PROMPT_QUEUE_SIZE>,
|
||||||
> = OnceLock::new();
|
> = 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 NATIVE_WEBVIEWS: Mutex<Vec<NativeWebViewComponents>> = Mutex::new(Vec::new());
|
||||||
|
|
||||||
impl ServoAction {
|
impl ServoAction {
|
||||||
fn dispatch_touch_event(
|
fn dispatch_touch_event(
|
||||||
servo: &RunningAppState,
|
servo: &RunningAppState,
|
||||||
|
@ -145,7 +163,7 @@ impl ServoAction {
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo: consider making this take `self`, so we don't need to needlessly clone.
|
// todo: consider making this take `self`, so we don't need to needlessly clone.
|
||||||
fn do_action(&self, servo: &RunningAppState) {
|
fn do_action(&self, servo: &Rc<RunningAppState>) {
|
||||||
use ServoAction::*;
|
use ServoAction::*;
|
||||||
match self {
|
match self {
|
||||||
WakeUp => servo.perform_updates(),
|
WakeUp => servo.perform_updates(),
|
||||||
|
@ -157,7 +175,7 @@ impl ServoAction {
|
||||||
x,
|
x,
|
||||||
y,
|
y,
|
||||||
pointer_id,
|
pointer_id,
|
||||||
} => Self::dispatch_touch_event(servo, *kind, *x, *y, *pointer_id),
|
} => Self::dispatch_touch_event(&servo, *kind, *x, *y, *pointer_id),
|
||||||
KeyUp(k) => servo.key_up(k.clone()),
|
KeyUp(k) => servo.key_up(k.clone()),
|
||||||
KeyDown(k) => servo.key_down(k.clone()),
|
KeyDown(k) => servo.key_down(k.clone()),
|
||||||
InsertText(text) => servo.ime_insert_text(text.clone()),
|
InsertText(text) => servo.ime_insert_text(text.clone()),
|
||||||
|
@ -185,6 +203,56 @@ impl ServoAction {
|
||||||
servo.present_if_needed();
|
servo.present_if_needed();
|
||||||
},
|
},
|
||||||
Resize { width, height } => servo.resize(Coordinates::new(0, 0, *width, *height)),
|
Resize { width, height } => servo.resize(Coordinates::new(0, 0, *width, *height)),
|
||||||
|
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);
|
||||||
|
|
||||||
|
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(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));
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -223,50 +291,64 @@ extern "C" fn on_surface_created_cb(xcomponent: *mut OH_NativeXComponent, window
|
||||||
let xc_wrapper = XComponentWrapper(xcomponent);
|
let xc_wrapper = XComponentWrapper(xcomponent);
|
||||||
let window_wrapper = WindowWrapper(window);
|
let window_wrapper = WindowWrapper(window);
|
||||||
|
|
||||||
// Todo: Perhaps it would be better to move this thread into the vsync signal thread.
|
if !SERVO_CHANNEL.get().is_some() {
|
||||||
// This would allow us to save one thread and the IPC for the vsync signal.
|
// Todo: Perhaps it would be better to move this thread into the vsync signal thread.
|
||||||
//
|
// This would allow us to save one thread and the IPC for the vsync signal.
|
||||||
// Each thread will send its id via the channel
|
//
|
||||||
let _main_surface_thread = thread::spawn(move || {
|
// Each thread will send its id via the channel
|
||||||
let (tx, rx): (Sender<ServoAction>, Receiver<ServoAction>) = mpsc::channel();
|
let _main_surface_thread = thread::spawn(move || {
|
||||||
|
let (tx, rx): (Sender<ServoAction>, Receiver<ServoAction>) = mpsc::channel();
|
||||||
|
|
||||||
SERVO_CHANNEL
|
SERVO_CHANNEL
|
||||||
.set(tx.clone())
|
.set(tx.clone())
|
||||||
.expect("Servo channel already initialized");
|
.expect("Servo channel already initialized");
|
||||||
|
|
||||||
let wakeup = Box::new(WakeupCallback::new(tx));
|
let wakeup = Box::new(WakeupCallback::new(tx));
|
||||||
let callbacks = Box::new(HostCallbacks::new());
|
let callbacks = Box::new(HostCallbacks::new());
|
||||||
|
|
||||||
let xc = xc_wrapper;
|
let xc = xc_wrapper;
|
||||||
let window = window_wrapper;
|
let window = window_wrapper;
|
||||||
let init_opts = if let Ok(ServoAction::Initialize(init_opts)) = rx.recv() {
|
|
||||||
init_opts
|
|
||||||
} else {
|
|
||||||
panic!("Servos GL thread received another event before it was initialized")
|
|
||||||
};
|
|
||||||
let servo = simpleservo::init(*init_opts, window.0, xc.0, wakeup, callbacks)
|
|
||||||
.expect("Servo initialization failed");
|
|
||||||
|
|
||||||
info!("Surface created!");
|
let init_opts = if let Ok(ServoAction::Initialize(init_opts)) = rx.recv() {
|
||||||
let native_vsync =
|
init_opts
|
||||||
ohos_vsync::NativeVsync::new("ServoVsync").expect("Failed to create NativeVsync");
|
} else {
|
||||||
// get_period() returns an error - perhaps we need to wait until the first callback?
|
panic!("Servos GL thread received another event before it was initialized")
|
||||||
// info!("Native vsync period is {} nanoseconds", native_vsync.get_period().unwrap());
|
};
|
||||||
unsafe {
|
let servo = simpleservo::init(*init_opts, window.0, xc.0, wakeup, callbacks)
|
||||||
native_vsync
|
.expect("Servo initialization failed");
|
||||||
.request_raw_callback_with_self(Some(on_vsync_cb))
|
|
||||||
.expect("Failed to request vsync callback")
|
|
||||||
}
|
|
||||||
info!("Enabled Vsync!");
|
|
||||||
|
|
||||||
while let Ok(action) = rx.recv() {
|
NATIVE_WEBVIEWS
|
||||||
trace!("Wakeup message received!");
|
.lock()
|
||||||
action.do_action(&servo);
|
.unwrap()
|
||||||
}
|
.push(NativeWebViewComponents {
|
||||||
|
id: servo.active_webview().id(),
|
||||||
|
xcomponent: xc,
|
||||||
|
window,
|
||||||
|
});
|
||||||
|
|
||||||
info!("Sender disconnected - Terminating main surface thread");
|
info!("Surface created!");
|
||||||
});
|
let native_vsync =
|
||||||
|
ohos_vsync::NativeVsync::new("ServoVsync").expect("Failed to create NativeVsync");
|
||||||
|
// get_period() returns an error - perhaps we need to wait until the first callback?
|
||||||
|
// info!("Native vsync period is {} nanoseconds", native_vsync.get_period().unwrap());
|
||||||
|
unsafe {
|
||||||
|
native_vsync
|
||||||
|
.request_raw_callback_with_self(Some(on_vsync_cb))
|
||||||
|
.expect("Failed to request vsync callback")
|
||||||
|
}
|
||||||
|
info!("Enabled Vsync!");
|
||||||
|
|
||||||
|
while let Ok(action) = rx.recv() {
|
||||||
|
trace!("Wakeup message received!");
|
||||||
|
action.do_action(&servo);
|
||||||
|
}
|
||||||
|
|
||||||
|
info!("Sender disconnected - Terminating main surface thread");
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
call(ServoAction::NewWebview(xc_wrapper, window_wrapper))
|
||||||
|
.expect("Could not create new webview");
|
||||||
|
}
|
||||||
info!("Returning from on_surface_created_cb");
|
info!("Returning from on_surface_created_cb");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -644,6 +726,12 @@ pub fn init_servo(init_opts: InitOpts) -> napi_ohos::Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
fn focus_webview(id: u32) {
|
||||||
|
debug!("Focusing webview {id} from napi");
|
||||||
|
call(ServoAction::FocusWebview(id)).expect("Could not focus webview");
|
||||||
|
}
|
||||||
|
|
||||||
struct OhosImeOptions {
|
struct OhosImeOptions {
|
||||||
input_type: ohos_ime_sys::types::InputMethod_TextInputType,
|
input_type: ohos_ime_sys::types::InputMethod_TextInputType,
|
||||||
enterkey_type: InputMethod_EnterKeyType,
|
enterkey_type: InputMethod_EnterKeyType,
|
||||||
|
|
|
@ -23,6 +23,20 @@ use crate::egl::ohos::InitOpts;
|
||||||
use crate::egl::ohos::resources::ResourceReaderInstance;
|
use crate::egl::ohos::resources::ResourceReaderInstance;
|
||||||
use crate::prefs::{ArgumentParsingResult, parse_command_line_arguments};
|
use crate::prefs::{ArgumentParsingResult, parse_command_line_arguments};
|
||||||
|
|
||||||
|
pub(crate) fn get_raw_window_handle(
|
||||||
|
xcomponent: *mut OH_NativeXComponent,
|
||||||
|
window: *mut c_void,
|
||||||
|
) -> (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 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));
|
||||||
|
(window_handle, window_size, coordinates)
|
||||||
|
}
|
||||||
|
|
||||||
/// Initialize Servo. At that point, we need a valid GL context.
|
/// Initialize Servo. At that point, we need a valid GL context.
|
||||||
/// In the future, this will be done in multiple steps.
|
/// In the future, this will be done in multiple steps.
|
||||||
pub fn init(
|
pub fn init(
|
||||||
|
@ -94,19 +108,12 @@ pub fn init(
|
||||||
#[cfg(target_env = "ohos")]
|
#[cfg(target_env = "ohos")]
|
||||||
crate::egl::ohos::set_log_filter(servoshell_preferences.log_filter.as_deref());
|
crate::egl::ohos::set_log_filter(servoshell_preferences.log_filter.as_deref());
|
||||||
|
|
||||||
let Ok(window_size) = (unsafe { super::get_xcomponent_size(xcomponent, native_window) }) else {
|
let (window_handle, window_size, coordinates) =
|
||||||
return Err("Failed to get xcomponent size");
|
get_raw_window_handle(xcomponent, native_window);
|
||||||
};
|
|
||||||
let Ok((x, y)) = (unsafe { super::get_xcomponent_offset(xcomponent, native_window) }) else {
|
|
||||||
return Err("Failed to get xcomponent offset");
|
|
||||||
};
|
|
||||||
let coordinates = Coordinates::new(x, y, window_size.width, window_size.height);
|
|
||||||
|
|
||||||
let display_handle = RawDisplayHandle::Ohos(OhosDisplayHandle::new());
|
let display_handle = RawDisplayHandle::Ohos(OhosDisplayHandle::new());
|
||||||
let display_handle = unsafe { DisplayHandle::borrow_raw(display_handle) };
|
let display_handle = unsafe { DisplayHandle::borrow_raw(display_handle) };
|
||||||
|
|
||||||
let native_window = NonNull::new(native_window).expect("Could not get native window");
|
|
||||||
let window_handle = RawWindowHandle::OhosNdk(OhosNdkWindowHandle::new(native_window));
|
|
||||||
let window_handle = unsafe { WindowHandle::borrow_raw(window_handle) };
|
let window_handle = unsafe { WindowHandle::borrow_raw(window_handle) };
|
||||||
|
|
||||||
let rendering_context = Rc::new(
|
let rendering_context = Rc::new(
|
||||||
|
|
|
@ -5,10 +5,17 @@ import promptAction from '@ohos.promptAction';
|
||||||
|
|
||||||
interface ServoXComponentInterface {
|
interface ServoXComponentInterface {
|
||||||
loadURL(url: string): void;
|
loadURL(url: string): void;
|
||||||
|
|
||||||
goBack(): void;
|
goBack(): void;
|
||||||
|
|
||||||
goForward(): void;
|
goForward(): void;
|
||||||
|
|
||||||
registerURLcallback(callback: (url: string) => void): void;
|
registerURLcallback(callback: (url: string) => void): void;
|
||||||
|
|
||||||
registerPromptToastCallback(callback: (msg: string) => void): void
|
registerPromptToastCallback(callback: (msg: string) => void): void
|
||||||
|
|
||||||
|
focusWebview(index: number):void;
|
||||||
|
|
||||||
initServo(options: InitOpts): void;
|
initServo(options: InitOpts): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,20 +30,20 @@ interface InitOpts {
|
||||||
}
|
}
|
||||||
|
|
||||||
function get_density(): number {
|
function get_density(): number {
|
||||||
try {
|
try {
|
||||||
let displayClass = display.getDefaultDisplaySync();
|
let displayClass = display.getDefaultDisplaySync();
|
||||||
console.info('Test densityDPI:' + JSON.stringify(displayClass.densityDPI));
|
console.info('Test densityDPI:' + JSON.stringify(displayClass.densityDPI));
|
||||||
return displayClass.densityDPI / 160;
|
return displayClass.densityDPI / 160;
|
||||||
} catch (exception) {
|
} catch (exception) {
|
||||||
console.error('Failed to obtain the default display object. Code: ' + JSON.stringify(exception));
|
console.error('Failed to obtain the default display object. Code: ' + JSON.stringify(exception));
|
||||||
return 3;
|
return 3;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function get_device_type(): string {
|
function get_device_type(): string {
|
||||||
let device_type: string = deviceInfo.deviceType;
|
let device_type: string = deviceInfo.deviceType;
|
||||||
if (device_type == "") {
|
if (device_type == "") {
|
||||||
console.error("deviceInfo.deviceType is empty string!")
|
console.error("deviceInfo.deviceType is empty string!")
|
||||||
} else {
|
} else {
|
||||||
console.info("Device type is " + device_type)
|
console.info("Device type is " + device_type)
|
||||||
}
|
}
|
||||||
|
@ -44,10 +51,10 @@ function get_device_type(): string {
|
||||||
}
|
}
|
||||||
|
|
||||||
function prompt_toast(msg: string) {
|
function prompt_toast(msg: string) {
|
||||||
promptAction.showToast({
|
promptAction.showToast({
|
||||||
message: msg,
|
message: msg,
|
||||||
duration: 2000
|
duration: 2000
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use the getShared API to obtain the LocalStorage instance shared by stage.
|
// Use the getShared API to obtain the LocalStorage instance shared by stage.
|
||||||
|
@ -62,11 +69,12 @@ struct Index {
|
||||||
type: XComponentType.SURFACE,
|
type: XComponentType.SURFACE,
|
||||||
libraryname: 'servoshell',
|
libraryname: 'servoshell',
|
||||||
}
|
}
|
||||||
|
|
||||||
private context = getContext(this) as common.UIAbilityContext;
|
private context = getContext(this) as common.UIAbilityContext;
|
||||||
@LocalStorageProp('InitialURI') InitialURI: string = "unused"
|
@LocalStorageProp('InitialURI') InitialURI: string = "unused"
|
||||||
@LocalStorageProp('CommandlineArgs') CommandlineArgs: string = ""
|
@LocalStorageProp('CommandlineArgs') CommandlineArgs: string = ""
|
||||||
@State urlToLoad: string = this.InitialURI
|
@State urlToLoad: string = this.InitialURI
|
||||||
|
@State tablist: Array<number> = [];
|
||||||
|
@State currentIndex: number = 0;
|
||||||
|
|
||||||
// Called when the user swipes from the right or left edge to the middle
|
// Called when the user swipes from the right or left edge to the middle
|
||||||
// Default behavior is bringing the app to the background.
|
// Default behavior is bringing the app to the background.
|
||||||
|
@ -81,57 +89,89 @@ struct Index {
|
||||||
// Flex.
|
// Flex.
|
||||||
Flex({ direction: FlexDirection.Column}) {
|
Flex({ direction: FlexDirection.Column}) {
|
||||||
Row() {
|
Row() {
|
||||||
Button('⇦').backgroundColor(Color.White)
|
Button('+')
|
||||||
|
.backgroundColor(Color.White)
|
||||||
|
.fontColor(Color.Black)
|
||||||
|
.fontWeight(FontWeight.Bolder)
|
||||||
|
.fontSize(22)
|
||||||
|
.width('12%')
|
||||||
|
.onClick((event) => {
|
||||||
|
if (this.tablist.length==0) {
|
||||||
|
this.tablist.push(2);
|
||||||
|
} else {
|
||||||
|
this.tablist.push(this.tablist[this.tablist.length-1]+1);
|
||||||
|
}
|
||||||
|
// yes this is correct as we always have one tab extra
|
||||||
|
// The tab extra is seperate for the initialization and will always exist.
|
||||||
|
// It is not in the tablist.
|
||||||
|
this.currentIndex = this.tablist.length;
|
||||||
|
})
|
||||||
|
Button('⇦')
|
||||||
|
.backgroundColor(Color.White)
|
||||||
.fontColor(Color.Black)
|
.fontColor(Color.Black)
|
||||||
.fontWeight(FontWeight.Bolder)
|
.fontWeight(FontWeight.Bolder)
|
||||||
.width('12%')
|
.width('12%')
|
||||||
.fontSize(12)
|
.fontSize(12)
|
||||||
.onClick(()=>{
|
.onClick(() => {
|
||||||
this.onBackPress()
|
this.onBackPress()
|
||||||
})
|
})
|
||||||
Button('⇨').backgroundColor(Color.White)
|
Button('⇨')
|
||||||
|
.backgroundColor(Color.White)
|
||||||
.fontColor(Color.Black)
|
.fontColor(Color.Black)
|
||||||
.fontWeight(FontWeight.Bolder)
|
.fontWeight(FontWeight.Bolder)
|
||||||
.fontSize(12)
|
.fontSize(12)
|
||||||
.width('12%')
|
.width('12%')
|
||||||
.onClick(()=> {
|
.onClick(() => {
|
||||||
this.xComponentContext?.goForward()
|
this.xComponentContext?.goForward()
|
||||||
})
|
})
|
||||||
TextInput({placeholder:'URL',text: $$this.urlToLoad})
|
TextInput({ placeholder: 'URL', text: $$this.urlToLoad })
|
||||||
.type(InputType.Normal)
|
.type(InputType.Normal)
|
||||||
.width('76%')
|
.width('76%')
|
||||||
.onChange((value) => {
|
.onChange((value) => {
|
||||||
this.urlToLoad = value
|
this.urlToLoad = value
|
||||||
})
|
})
|
||||||
.onSubmit((EnterKeyType)=>{
|
.onSubmit((EnterKeyType) => {
|
||||||
this.xComponentContext?.loadURL(this.urlToLoad)
|
this.xComponentContext?.loadURL(this.urlToLoad)
|
||||||
console.info('Load URL: ', this.urlToLoad)
|
console.info('Load URL: ', this.urlToLoad)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
XComponent(this.xComponentAttrs)
|
|
||||||
.focusable(true)
|
Tabs({ barPosition: BarPosition.Start, index: this.currentIndex}) {
|
||||||
.onLoad((xComponentContext) => {
|
TabContent() {
|
||||||
this.xComponentContext = xComponentContext as ServoXComponentInterface;
|
XComponent(this.xComponentAttrs)
|
||||||
let resource_dir: string = this.context.resourceDir;
|
.focusable(true)
|
||||||
let cache_dir: string = this.context.cacheDir;
|
.onLoad((xComponentContext) => {
|
||||||
console.debug("resourceDir: ", resource_dir);
|
this.xComponentContext = xComponentContext as ServoXComponentInterface;
|
||||||
console.debug("cacheDir: ", cache_dir);
|
let resource_dir: string = this.context.resourceDir;
|
||||||
let init_options: InitOpts = {
|
let cache_dir: string = this.context.cacheDir;
|
||||||
url: this.urlToLoad,
|
console.debug("resourceDir: ", resource_dir);
|
||||||
deviceType: get_device_type(),
|
console.debug("cacheDir: ", cache_dir);
|
||||||
osFullName: deviceInfo.osFullName,
|
let init_options: InitOpts = {
|
||||||
displayDensity: get_density(),
|
url: this.urlToLoad,
|
||||||
resourceDir: resource_dir,
|
deviceType: get_device_type(),
|
||||||
cacheDir: cache_dir,
|
osFullName: deviceInfo.osFullName,
|
||||||
commandlineArgs: this.CommandlineArgs
|
displayDensity: get_density(),
|
||||||
}
|
resourceDir: resource_dir,
|
||||||
this.xComponentContext.initServo(init_options)
|
cacheDir: cache_dir,
|
||||||
this.xComponentContext.registerURLcallback((new_url) => {
|
commandlineArgs: this.CommandlineArgs
|
||||||
console.info('New URL from native: ', new_url)
|
}
|
||||||
this.urlToLoad = new_url
|
this.xComponentContext.initServo(init_options)
|
||||||
})
|
this.xComponentContext.registerURLcallback((new_url) => {
|
||||||
this.xComponentContext.registerPromptToastCallback(prompt_toast)
|
console.info('New URL from native: ', new_url)
|
||||||
|
this.urlToLoad = new_url
|
||||||
|
})
|
||||||
|
this.xComponentContext.registerPromptToastCallback(prompt_toast)
|
||||||
|
})
|
||||||
|
}.tabBar('1')
|
||||||
|
ForEach(this.tablist, (item: number) => {
|
||||||
|
TabContent() {
|
||||||
|
XComponent(this.xComponentAttrs)
|
||||||
|
.focusable(true)
|
||||||
|
}.tabBar(String(item))
|
||||||
})
|
})
|
||||||
|
}.onChange((index: number) => {
|
||||||
|
this.xComponentContext?.focusWebview(index);
|
||||||
|
})
|
||||||
}
|
}
|
||||||
.width('100%')
|
.width('100%')
|
||||||
}
|
}
|
||||||
|
@ -141,4 +181,4 @@ interface XComponentAttrs {
|
||||||
id: string;
|
id: string;
|
||||||
type: number;
|
type: number;
|
||||||
libraryname: string;
|
libraryname: string;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue