mirror of
https://github.com/slint-ui/slint.git
synced 2025-12-23 09:19:32 +00:00
servo: Improve Code (#10119)
This commit is contained in:
parent
a119b714a5
commit
164b58ab46
7 changed files with 75 additions and 126 deletions
1
examples/servo/Cargo.lock
generated
1
examples/servo/Cargo.lock
generated
|
|
@ -8242,6 +8242,7 @@ dependencies = [
|
|||
"i-slint-backend-qt",
|
||||
"i-slint-backend-selector",
|
||||
"i-slint-backend-winit",
|
||||
"i-slint-common",
|
||||
"i-slint-core",
|
||||
"i-slint-core-macros",
|
||||
"i-slint-renderer-femtovg",
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@ pub fn upgrade_adapter(weak_ref: &Weak<SlintServoAdapter>) -> Rc<SlintServoAdapt
|
|||
/// Bridge between Slint UI and Servo browser engine.
|
||||
/// Manages the lifecycle and communication between the UI and browser components.
|
||||
pub struct SlintServoAdapter {
|
||||
app: slint::Weak<MyApp>,
|
||||
/// Channel sender to wake the event loop
|
||||
waker_sender: Sender<()>,
|
||||
/// Channel receiver for event loop wake signals
|
||||
|
|
@ -33,7 +32,6 @@ pub struct SlintServoAdapter {
|
|||
}
|
||||
|
||||
pub struct SlintServoAdapterInner {
|
||||
scale_factor: f32,
|
||||
webview: Option<WebView>,
|
||||
rendering_adapter: Option<Rc<Box<dyn ServoRenderingAdapter>>>,
|
||||
#[cfg(not(target_os = "android"))]
|
||||
|
|
@ -44,20 +42,17 @@ pub struct SlintServoAdapterInner {
|
|||
|
||||
impl SlintServoAdapter {
|
||||
pub fn new(
|
||||
app: slint::Weak<MyApp>,
|
||||
waker_sender: Sender<()>,
|
||||
waker_receiver: Receiver<()>,
|
||||
#[cfg(not(target_os = "android"))] device: wgpu::Device,
|
||||
#[cfg(not(target_os = "android"))] queue: wgpu::Queue,
|
||||
) -> Self {
|
||||
Self {
|
||||
app,
|
||||
waker_sender,
|
||||
waker_receiver,
|
||||
servo: RefCell::new(None),
|
||||
inner: RefCell::new(SlintServoAdapterInner {
|
||||
webview: None,
|
||||
scale_factor: 1.0,
|
||||
rendering_adapter: None,
|
||||
#[cfg(not(target_os = "android"))]
|
||||
device: device,
|
||||
|
|
@ -75,10 +70,6 @@ impl SlintServoAdapter {
|
|||
self.inner.borrow_mut()
|
||||
}
|
||||
|
||||
pub fn app(&self) -> MyApp {
|
||||
self.app.upgrade().expect("Failed to upgrade MyApp")
|
||||
}
|
||||
|
||||
pub fn waker_sender(&self) -> Sender<()> {
|
||||
self.waker_sender.clone()
|
||||
}
|
||||
|
|
@ -87,10 +78,6 @@ impl SlintServoAdapter {
|
|||
self.waker_receiver.clone()
|
||||
}
|
||||
|
||||
pub fn scale_factor(&self) -> f32 {
|
||||
self.inner().scale_factor
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "android"))]
|
||||
pub fn wgpu_device(&self) -> wgpu::Device {
|
||||
self.inner().device.clone()
|
||||
|
|
@ -109,30 +96,23 @@ impl SlintServoAdapter {
|
|||
&self,
|
||||
servo: Servo,
|
||||
webview: WebView,
|
||||
scale_factor: f32,
|
||||
rendering_adapter: Rc<Box<dyn ServoRenderingAdapter>>,
|
||||
) {
|
||||
*self.servo.borrow_mut() = Some(servo);
|
||||
let mut inner = self.inner_mut();
|
||||
inner.webview = Some(webview);
|
||||
inner.scale_factor = scale_factor;
|
||||
inner.rendering_adapter = Some(rendering_adapter);
|
||||
}
|
||||
|
||||
/// Captures the current Servo framebuffer and updates the Slint UI with the rendered content.
|
||||
/// This bridges the rendering output from Servo to the Slint display surface.
|
||||
pub fn update_web_content_with_latest_frame(&self) {
|
||||
pub fn update_web_content_with_latest_frame(&self, app: &MyApp) {
|
||||
let inner = self.inner();
|
||||
let rendering_adapter = inner.rendering_adapter.as_ref().unwrap();
|
||||
|
||||
// Convert framebuffer to Slint image format
|
||||
let slint_image = rendering_adapter.current_framebuffer_as_image();
|
||||
|
||||
let app = self
|
||||
.app
|
||||
.upgrade()
|
||||
.expect("Application reference is no longer valid - UI may have been destroyed");
|
||||
|
||||
app.global::<WebviewLogic>().set_web_content(slint_image);
|
||||
app.window().request_redraw();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,15 +5,16 @@ use std::rc::Rc;
|
|||
|
||||
use servo::{WebView, WebViewDelegate};
|
||||
|
||||
use crate::adapter::SlintServoAdapter;
|
||||
use crate::{MyApp, adapter::SlintServoAdapter};
|
||||
|
||||
pub struct AppDelegate {
|
||||
pub state: Rc<SlintServoAdapter>,
|
||||
pub app: slint::Weak<MyApp>,
|
||||
}
|
||||
|
||||
impl AppDelegate {
|
||||
pub fn new(state: Rc<SlintServoAdapter>) -> Self {
|
||||
Self { state }
|
||||
pub fn new(state: Rc<SlintServoAdapter>, app: slint::Weak<MyApp>) -> Self {
|
||||
Self { state, app }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -22,6 +23,8 @@ impl WebViewDelegate for AppDelegate {
|
|||
/// Triggers painting and updates the Slint UI with the new frame.
|
||||
fn notify_new_frame_ready(&self, webview: WebView) {
|
||||
webview.paint();
|
||||
self.state.update_web_content_with_latest_frame();
|
||||
if let Some(app) = self.app.upgrade() {
|
||||
self.state.update_web_content_with_latest_frame(&app);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,10 +62,14 @@ pub fn main() {
|
|||
let url = "https://slint.dev";
|
||||
|
||||
#[cfg(not(target_os = "android"))]
|
||||
let _adapter = init_servo(app.clone_strong(), url.into(), device, queue);
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
let _adapter = init_servo(app.clone_strong(), url.into());
|
||||
let _adapter = init_servo(
|
||||
app.clone_strong(),
|
||||
url.into(),
|
||||
#[cfg(not(target_os = "android"))]
|
||||
device,
|
||||
#[cfg(not(target_os = "android"))]
|
||||
queue,
|
||||
);
|
||||
|
||||
app.run().expect("Application failed to run - check for runtime errors");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ use std::rc::Rc;
|
|||
use url::Url;
|
||||
use winit::dpi::PhysicalSize;
|
||||
|
||||
use euclid::{Box2D, Point2D, Size2D};
|
||||
use euclid::{Box2D, Point2D, Scale, Size2D};
|
||||
|
||||
use i_slint_core::items::{ColorScheme, PointerEvent, PointerEventKind};
|
||||
use slint::{ComponentHandle, platform::PointerEventButton};
|
||||
|
|
@ -18,24 +18,21 @@ use servo::{
|
|||
};
|
||||
|
||||
use crate::{
|
||||
WebviewLogic,
|
||||
MyApp, WebviewLogic,
|
||||
adapter::{SlintServoAdapter, upgrade_adapter},
|
||||
};
|
||||
|
||||
pub fn on_app_callbacks(adapter: Rc<SlintServoAdapter>) {
|
||||
on_url(adapter.clone());
|
||||
on_theme(adapter.clone());
|
||||
on_resize(adapter.clone());
|
||||
on_scroll(adapter.clone());
|
||||
on_buttons(adapter.clone());
|
||||
on_pointer(adapter.clone());
|
||||
pub fn on_app_callbacks(app: &MyApp, adapter: Rc<SlintServoAdapter>) {
|
||||
on_url(app, adapter.clone());
|
||||
on_theme(app, adapter.clone());
|
||||
on_resize(app, adapter.clone());
|
||||
on_scroll(app, adapter.clone());
|
||||
on_buttons(app, adapter.clone());
|
||||
on_pointer(app, adapter.clone());
|
||||
}
|
||||
|
||||
fn on_url(adapter: Rc<SlintServoAdapter>) {
|
||||
let app = adapter.app();
|
||||
|
||||
fn on_url(app: &MyApp, adapter: Rc<SlintServoAdapter>) {
|
||||
let adapter_weak = Rc::downgrade(&adapter);
|
||||
|
||||
app.global::<WebviewLogic>().on_loadUrl(move |url| {
|
||||
let adapter = upgrade_adapter(&adapter_weak);
|
||||
let webview = adapter.webview();
|
||||
|
|
@ -44,11 +41,8 @@ fn on_url(adapter: Rc<SlintServoAdapter>) {
|
|||
});
|
||||
}
|
||||
|
||||
fn on_theme(adapter: Rc<SlintServoAdapter>) {
|
||||
let app = adapter.app();
|
||||
|
||||
fn on_theme(app: &MyApp, adapter: Rc<SlintServoAdapter>) {
|
||||
let adapter_weak = Rc::downgrade(&adapter);
|
||||
|
||||
app.global::<WebviewLogic>().on_theme(move |color_scheme| {
|
||||
let theme = if color_scheme == ColorScheme::Dark { Theme::Dark } else { Theme::Light };
|
||||
|
||||
|
|
@ -56,24 +50,29 @@ fn on_theme(adapter: Rc<SlintServoAdapter>) {
|
|||
|
||||
let webview = adapter.webview();
|
||||
|
||||
// Theme not updating its the issue with servo itself until mouse move over it
|
||||
// Theme not updating until mouse move over it
|
||||
// https://github.com/servo/servo/issues/40268
|
||||
webview.notify_theme_change(theme);
|
||||
});
|
||||
}
|
||||
|
||||
fn on_resize(adapter: Rc<SlintServoAdapter>) {
|
||||
let app = adapter.app();
|
||||
|
||||
// This will always called when slint window show first time and when resize so set scale factor here
|
||||
fn on_resize(app: &MyApp, adapter: Rc<SlintServoAdapter>) {
|
||||
let adapter_weak = Rc::downgrade(&adapter);
|
||||
let app_weak = app.as_weak();
|
||||
app.global::<WebviewLogic>().on_resize(move |width, height| {
|
||||
let adapter = upgrade_adapter(&adapter_weak);
|
||||
|
||||
let webview = adapter.webview();
|
||||
|
||||
let scale_factor = adapter.scale_factor();
|
||||
let scale_factor =
|
||||
app_weak.upgrade().expect("Failed to upgrade app").window().scale_factor();
|
||||
|
||||
let size = Size2D::new(width, height) * scale_factor;
|
||||
let scale = Scale::new(scale_factor);
|
||||
|
||||
webview.set_hidpi_scale_factor(scale);
|
||||
|
||||
let size = Size2D::new(width, height);
|
||||
|
||||
let physical_size = PhysicalSize::new(size.width as u32, size.height as u32);
|
||||
|
||||
|
|
@ -84,18 +83,14 @@ fn on_resize(adapter: Rc<SlintServoAdapter>) {
|
|||
});
|
||||
}
|
||||
|
||||
fn on_scroll(adapter: Rc<SlintServoAdapter>) {
|
||||
let app = adapter.app();
|
||||
|
||||
fn on_scroll(app: &MyApp, adapter: Rc<SlintServoAdapter>) {
|
||||
let adapter_weak = Rc::downgrade(&adapter);
|
||||
app.global::<WebviewLogic>().on_scroll(move |initial_x, initial_y, delta_x, delta_y| {
|
||||
let adapter = upgrade_adapter(&adapter_weak);
|
||||
|
||||
let webview = adapter.webview();
|
||||
|
||||
let scale_factor = adapter.scale_factor();
|
||||
|
||||
let point = DevicePoint::new(initial_x * scale_factor, initial_y * scale_factor);
|
||||
let point = DevicePoint::new(initial_x, initial_y);
|
||||
|
||||
let moved_by = DeviceVector2D::new(delta_x, delta_y);
|
||||
|
||||
|
|
@ -106,9 +101,7 @@ fn on_scroll(adapter: Rc<SlintServoAdapter>) {
|
|||
});
|
||||
}
|
||||
|
||||
fn on_buttons(adapter: Rc<SlintServoAdapter>) {
|
||||
let app = adapter.app();
|
||||
|
||||
fn on_buttons(app: &MyApp, adapter: Rc<SlintServoAdapter>) {
|
||||
let adapter_weak = Rc::downgrade(&adapter);
|
||||
app.on_back(move || {
|
||||
let adapter = upgrade_adapter(&adapter_weak);
|
||||
|
|
@ -137,18 +130,14 @@ fn on_buttons(adapter: Rc<SlintServoAdapter>) {
|
|||
});
|
||||
}
|
||||
|
||||
fn on_pointer(adapter: Rc<SlintServoAdapter>) {
|
||||
let app = adapter.app();
|
||||
|
||||
fn on_pointer(app: &MyApp, adapter: Rc<SlintServoAdapter>) {
|
||||
let adapter_weak = Rc::downgrade(&adapter);
|
||||
app.global::<WebviewLogic>().on_pointer(move |pointer_event, x, y| {
|
||||
let adapter = upgrade_adapter(&adapter_weak);
|
||||
|
||||
let webview = adapter.webview();
|
||||
|
||||
let scale_factor = adapter.scale_factor();
|
||||
|
||||
let point = DevicePoint::new(x * scale_factor, y * scale_factor);
|
||||
let point = DevicePoint::new(x, y);
|
||||
|
||||
let input_event =
|
||||
convert_slint_pointer_event_to_servo_input_event(&pointer_event, point.into());
|
||||
|
|
@ -166,7 +155,7 @@ fn convert_slint_pointer_event_to_servo_input_event(
|
|||
if pointer_event.is_touch {
|
||||
handle_touch_events(pointer_event, point)
|
||||
} else {
|
||||
_handle_mouse_events(pointer_event, point)
|
||||
handle_mouse_events(pointer_event, point)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -180,8 +169,8 @@ fn handle_touch_events(pointer_event: &PointerEvent, point: WebViewPoint) -> Inp
|
|||
InputEvent::Touch(touch_event)
|
||||
}
|
||||
|
||||
fn _handle_mouse_events(pointer_event: &PointerEvent, point: WebViewPoint) -> InputEvent {
|
||||
let button = _get_mouse_button(pointer_event);
|
||||
fn handle_mouse_events(pointer_event: &PointerEvent, point: WebViewPoint) -> InputEvent {
|
||||
let button = get_mouse_button(pointer_event);
|
||||
match pointer_event.kind {
|
||||
PointerEventKind::Down => {
|
||||
let mouse_event = MouseButtonEvent::new(MouseButtonAction::Down, button, point);
|
||||
|
|
@ -195,7 +184,7 @@ fn _handle_mouse_events(pointer_event: &PointerEvent, point: WebViewPoint) -> In
|
|||
}
|
||||
}
|
||||
|
||||
fn _get_mouse_button(point_event: &PointerEvent) -> MouseButton {
|
||||
fn get_mouse_button(point_event: &PointerEvent) -> MouseButton {
|
||||
match point_event.button {
|
||||
PointerEventButton::Left => MouseButton::Left,
|
||||
PointerEventButton::Right => MouseButton::Right,
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ use smol::channel;
|
|||
use url::Url;
|
||||
use winit::dpi::PhysicalSize;
|
||||
|
||||
use euclid::{Scale, Size2D};
|
||||
use euclid::Size2D;
|
||||
|
||||
use i_slint_core::items::ColorScheme;
|
||||
use slint::{ComponentHandle, SharedString};
|
||||
|
|
@ -29,78 +29,58 @@ pub fn init_servo(
|
|||
#[cfg(not(target_os = "android"))] device: slint::wgpu_27::wgpu::Device,
|
||||
#[cfg(not(target_os = "android"))] queue: slint::wgpu_27::wgpu::Queue,
|
||||
) -> Rc<SlintServoAdapter> {
|
||||
let app_weak = app.as_weak();
|
||||
let (waker_sender, waker_receiver) = channel::unbounded::<()>();
|
||||
|
||||
#[cfg(not(target_os = "android"))]
|
||||
let adapter = Rc::new(SlintServoAdapter::new(
|
||||
app_weak,
|
||||
waker_sender.clone(),
|
||||
waker_receiver.clone(),
|
||||
#[cfg(not(target_os = "android"))]
|
||||
device,
|
||||
#[cfg(not(target_os = "android"))]
|
||||
queue,
|
||||
));
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
let adapter = Rc::new(SlintServoAdapter::new(
|
||||
app_weak,
|
||||
waker_sender.clone(),
|
||||
waker_receiver.clone(),
|
||||
));
|
||||
|
||||
let state_weak = Rc::downgrade(&adapter);
|
||||
|
||||
slint::spawn_local({
|
||||
async move {
|
||||
let state = upgrade_adapter(&state_weak);
|
||||
|
||||
let state = upgrade_adapter(&state_weak);
|
||||
let (rendering_adapter, physical_size) = init_rendering_adpater(&app, state.clone());
|
||||
|
||||
let (rendering_adapter, physical_size, scale_factor) =
|
||||
init_rendering_adpater(state.clone());
|
||||
let servo = init_servo_builder(state.clone(), rendering_adapter.clone());
|
||||
|
||||
let servo = init_servo_builder(state.clone(), rendering_adapter.clone());
|
||||
|
||||
init_webview(scale_factor, physical_size, initial_url, state, servo, rendering_adapter);
|
||||
}
|
||||
})
|
||||
.unwrap();
|
||||
init_webview(&app, physical_size, initial_url, state, servo, rendering_adapter);
|
||||
|
||||
spin_servo_event_loop(adapter.clone());
|
||||
|
||||
on_app_callbacks(adapter.clone());
|
||||
on_app_callbacks(&app, adapter.clone());
|
||||
|
||||
adapter
|
||||
|
||||
}
|
||||
|
||||
fn init_rendering_adpater(
|
||||
state: Rc<SlintServoAdapter>,
|
||||
) -> (Rc<Box<dyn ServoRenderingAdapter>>, PhysicalSize<u32>, f32) {
|
||||
let app = state.app();
|
||||
|
||||
let scale_factor = app.window().scale_factor() as f32;
|
||||
|
||||
app: &MyApp,
|
||||
adapter: Rc<SlintServoAdapter>,
|
||||
) -> (Rc<Box<dyn ServoRenderingAdapter>>, PhysicalSize<u32>) {
|
||||
let width = app.global::<WebviewLogic>().get_viewport_width();
|
||||
let height = app.global::<WebviewLogic>().get_viewport_height();
|
||||
|
||||
let size: Size2D<f32, DevicePixel> = Size2D::new(width, height) * scale_factor;
|
||||
|
||||
let size: Size2D<f32, DevicePixel> = Size2D::new(width, height);
|
||||
let physical_size = PhysicalSize::new(size.width as u32, size.height as u32);
|
||||
|
||||
#[cfg(not(target_os = "android"))]
|
||||
let rendering_adapter = {
|
||||
let wgpu_device = state.wgpu_device();
|
||||
let wgpu_queue = state.wgpu_queue();
|
||||
crate::rendering_context::try_create_gpu_context(wgpu_device, wgpu_queue, physical_size)
|
||||
.unwrap()
|
||||
};
|
||||
let rendering_adapter = crate::rendering_context::try_create_gpu_context(
|
||||
adapter.wgpu_device(),
|
||||
adapter.wgpu_queue(),
|
||||
physical_size,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
let rendering_adapter = crate::rendering_context::create_software_context(physical_size);
|
||||
|
||||
let rendering_adapter_rc = Rc::new(rendering_adapter);
|
||||
|
||||
(rendering_adapter_rc, physical_size, scale_factor)
|
||||
(rendering_adapter_rc, physical_size)
|
||||
}
|
||||
|
||||
fn init_servo_builder(
|
||||
|
|
@ -117,29 +97,21 @@ fn init_servo_builder(
|
|||
}
|
||||
|
||||
fn init_webview(
|
||||
scale_factor: f32,
|
||||
app: &MyApp,
|
||||
physical_size: PhysicalSize<u32>,
|
||||
initial_url: SharedString,
|
||||
adapter: Rc<SlintServoAdapter>,
|
||||
servo: Servo,
|
||||
rendering_adapter: Rc<Box<dyn ServoRenderingAdapter>>,
|
||||
) {
|
||||
let scale = Scale::new(scale_factor);
|
||||
|
||||
let app = adapter.app();
|
||||
|
||||
app.global::<WebviewLogic>().set_current_url(initial_url.clone());
|
||||
|
||||
let url = Url::parse(&initial_url).expect("Failed to parse url");
|
||||
|
||||
let delegate = Rc::new(AppDelegate::new(adapter.clone()));
|
||||
let delegate = Rc::new(AppDelegate::new(adapter.clone(), app.as_weak()));
|
||||
|
||||
let webview = WebViewBuilder::new(&servo)
|
||||
.url(url)
|
||||
.size(physical_size)
|
||||
.delegate(delegate)
|
||||
.hidpi_scale_factor(scale)
|
||||
.build();
|
||||
let webview =
|
||||
WebViewBuilder::new(&servo).url(url).size(physical_size).delegate(delegate).build();
|
||||
|
||||
webview.show(true);
|
||||
|
||||
|
|
@ -149,7 +121,7 @@ fn init_webview(
|
|||
|
||||
webview.notify_theme_change(theme);
|
||||
|
||||
adapter.set_inner(servo, webview, scale_factor, rendering_adapter);
|
||||
adapter.set_inner(servo, webview, rendering_adapter);
|
||||
}
|
||||
|
||||
fn spin_servo_event_loop(state: Rc<SlintServoAdapter>) {
|
||||
|
|
|
|||
|
|
@ -7,9 +7,9 @@ export global WebviewLogic {
|
|||
|
||||
callback loadUrl(url: string);
|
||||
callback theme(scheme: ColorScheme);
|
||||
callback resize(width: length, height: length);
|
||||
callback pointer(event: PointerEvent, x: length, y: length);
|
||||
callback scroll(initital_x: length, initital_y: length, delta_x: length, delta_y: length);
|
||||
callback resize(width: physical-length, height: physical-length);
|
||||
callback pointer(event: PointerEvent, x: physical-length, y: physical-length);
|
||||
callback scroll(initital_x: physical-length, initital_y: physical-length, delta_x: length, delta_y: length);
|
||||
|
||||
in property <image> web_content;
|
||||
in-out property <string> current_url;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue