servo: Improve Code (#10119)

This commit is contained in:
Burhan Khanzada 2025-11-19 17:38:17 +01:00 committed by GitHub
parent a119b714a5
commit 164b58ab46
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 75 additions and 126 deletions

View file

@ -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",

View file

@ -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();
}

View file

@ -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);
}
}
}

View file

@ -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");
}

View file

@ -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,

View file

@ -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>) {

View file

@ -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;