Skip to content

Commit f02f834

Browse files
Desktop: Add support for UI scaling (#3310)
* desktop support ui scaling * fix some warnings * use browser zoom if needed * fix infinite footprint size * fix web canvas scale * always set zoom * use only zoom for scaling * prevent user zoom * remove mouse position scaling
1 parent 7254c47 commit f02f834

25 files changed

+286
-162
lines changed

desktop/src/app.rs

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use std::thread;
88
use std::time::Duration;
99
use std::time::Instant;
1010
use winit::application::ApplicationHandler;
11+
use winit::dpi::PhysicalSize;
1112
use winit::event::WindowEvent;
1213
use winit::event_loop::ActiveEventLoop;
1314
use winit::event_loop::ControlFlow;
@@ -25,8 +26,9 @@ use crate::wrapper::{DesktopWrapper, NodeGraphExecutionResult, WgpuContext, seri
2526
pub(crate) struct App {
2627
cef_context: Box<dyn cef::CefContext>,
2728
window: Option<Window>,
29+
window_scale: f64,
2830
cef_schedule: Option<Instant>,
29-
cef_window_size_sender: Sender<cef::WindowSize>,
31+
cef_view_info_sender: Sender<cef::ViewInfoUpdate>,
3032
graphics_state: Option<GraphicsState>,
3133
wgpu_context: WgpuContext,
3234
app_event_receiver: Receiver<AppEvent>,
@@ -44,7 +46,7 @@ pub(crate) struct App {
4446
impl App {
4547
pub(crate) fn new(
4648
cef_context: Box<dyn cef::CefContext>,
47-
window_size_sender: Sender<cef::WindowSize>,
49+
cef_view_info_sender: Sender<cef::ViewInfoUpdate>,
4850
wgpu_context: WgpuContext,
4951
app_event_receiver: Receiver<AppEvent>,
5052
app_event_scheduler: AppEventScheduler,
@@ -66,9 +68,10 @@ impl App {
6668
Self {
6769
cef_context,
6870
window: None,
71+
window_scale: 1.0,
6972
cef_schedule: Some(Instant::now()),
7073
graphics_state: None,
71-
cef_window_size_sender: window_size_sender,
74+
cef_view_info_sender,
7275
wgpu_context,
7376
app_event_receiver,
7477
app_event_scheduler,
@@ -147,19 +150,19 @@ impl App {
147150
}
148151
});
149152
}
150-
DesktopFrontendMessage::UpdateViewportBounds { x, y, width, height } => {
153+
DesktopFrontendMessage::UpdateViewportPhysicalBounds { x, y, width, height } => {
151154
if let Some(graphics_state) = &mut self.graphics_state
152155
&& let Some(window) = &self.window
153156
{
154157
let window_size = window.surface_size();
155158

156-
let viewport_offset_x = x / window_size.width as f32;
157-
let viewport_offset_y = y / window_size.height as f32;
158-
graphics_state.set_viewport_offset([viewport_offset_x, viewport_offset_y]);
159+
let viewport_offset_x = x / window_size.width as f64;
160+
let viewport_offset_y = y / window_size.height as f64;
161+
graphics_state.set_viewport_offset([viewport_offset_x as f32, viewport_offset_y as f32]);
159162

160-
let viewport_scale_x = if width != 0.0 { window_size.width as f32 / width } else { 1.0 };
161-
let viewport_scale_y = if height != 0.0 { window_size.height as f32 / height } else { 1.0 };
162-
graphics_state.set_viewport_scale([viewport_scale_x, viewport_scale_y]);
163+
let viewport_scale_x = if width != 0.0 { window_size.width as f64 / width } else { 1.0 };
164+
let viewport_scale_y = if height != 0.0 { window_size.height as f64 / height } else { 1.0 };
165+
graphics_state.set_viewport_scale([viewport_scale_x as f32, viewport_scale_y as f32]);
163166
}
164167
}
165168
DesktopFrontendMessage::UpdateOverlays(scene) => {
@@ -179,7 +182,7 @@ impl App {
179182
}
180183
DesktopFrontendMessage::DragWindow => {
181184
if let Some(window) = &self.window {
182-
let _ = window.start_drag();
185+
window.start_drag();
183186
}
184187
}
185188
DesktopFrontendMessage::CloseWindow => {
@@ -352,14 +355,17 @@ impl App {
352355
impl ApplicationHandler for App {
353356
fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) {
354357
let window = Window::new(event_loop, self.app_event_scheduler.clone());
358+
359+
self.window_scale = window.scale_factor();
360+
let _ = self.cef_view_info_sender.send(cef::ViewInfoUpdate::Scale(self.window_scale));
361+
self.cef_context.notify_view_info_changed();
362+
355363
self.window = Some(window);
356364

357365
let graphics_state = GraphicsState::new(self.window.as_ref().unwrap(), self.wgpu_context.clone());
358366

359367
self.graphics_state = Some(graphics_state);
360368

361-
tracing::info!("Winit window created and ready");
362-
363369
self.desktop_wrapper.init(self.wgpu_context.clone());
364370

365371
#[cfg(target_os = "windows")]
@@ -384,14 +390,22 @@ impl ApplicationHandler for App {
384390
WindowEvent::CloseRequested => {
385391
self.app_event_scheduler.schedule(AppEvent::CloseWindow);
386392
}
387-
WindowEvent::SurfaceResized(size) => {
388-
let _ = self.cef_window_size_sender.send(size.into());
389-
self.cef_context.notify_of_resize();
393+
WindowEvent::SurfaceResized(PhysicalSize { width, height }) => {
394+
let _ = self.cef_view_info_sender.send(cef::ViewInfoUpdate::Size {
395+
width: width as usize,
396+
height: height as usize,
397+
});
398+
self.cef_context.notify_view_info_changed();
390399
if let Some(window) = &self.window {
391400
let maximized = window.is_maximized();
392401
self.app_event_scheduler.schedule(AppEvent::DesktopWrapperMessage(DesktopWrapperMessage::UpdateMaximized { maximized }));
393402
}
394403
}
404+
WindowEvent::ScaleFactorChanged { scale_factor, .. } => {
405+
self.window_scale = scale_factor;
406+
let _ = self.cef_view_info_sender.send(cef::ViewInfoUpdate::Scale(self.window_scale));
407+
self.cef_context.notify_view_info_changed();
408+
}
395409
WindowEvent::RedrawRequested => {
396410
let Some(ref mut graphics_state) = self.graphics_state else { return };
397411
// Only rerender once we have a new UI texture to display

desktop/src/cef.rs

Lines changed: 69 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@ use texture_import::SharedTextureHandle;
3838

3939
pub(crate) use context::{CefContext, CefContextBuilder, InitError};
4040

41-
pub(crate) trait CefEventHandler: Clone + Send + Sync + 'static {
42-
fn window_size(&self) -> WindowSize;
41+
pub(crate) trait CefEventHandler: Send + Sync + 'static {
42+
fn view_info(&self) -> ViewInfo;
4343
fn draw<'a>(&self, frame_buffer: FrameBufferRef<'a>);
4444
#[cfg(feature = "accelerated_paint")]
4545
fn draw_gpu(&self, shared_texture: SharedTextureHandle);
@@ -50,24 +50,54 @@ pub(crate) trait CefEventHandler: Clone + Send + Sync + 'static {
5050
fn schedule_cef_message_loop_work(&self, scheduled_time: Instant);
5151
fn initialized_web_communication(&self);
5252
fn receive_web_message(&self, message: &[u8]);
53+
fn duplicate(&self) -> Self
54+
where
55+
Self: Sized;
5356
}
5457

5558
#[derive(Clone, Copy)]
56-
pub(crate) struct WindowSize {
57-
pub(crate) width: usize,
58-
pub(crate) height: usize,
59+
pub(crate) struct ViewInfo {
60+
width: usize,
61+
height: usize,
62+
scale: f64,
5963
}
60-
impl WindowSize {
61-
pub(crate) fn new(width: usize, height: usize) -> Self {
62-
Self { width, height }
64+
impl ViewInfo {
65+
pub(crate) fn new() -> Self {
66+
Self { width: 1, height: 1, scale: 1. }
67+
}
68+
pub(crate) fn apply_update(&mut self, update: ViewInfoUpdate) {
69+
match update {
70+
ViewInfoUpdate::Size { width, height } if width > 0 && height > 0 => {
71+
self.width = width;
72+
self.height = height;
73+
}
74+
ViewInfoUpdate::Scale(scale) if scale > 0. => {
75+
self.scale = scale;
76+
}
77+
_ => {}
78+
}
79+
}
80+
pub(crate) fn zoom(&self) -> f64 {
81+
self.scale.ln() / 1.2_f64.ln()
82+
}
83+
pub(crate) fn width(&self) -> usize {
84+
self.width
85+
}
86+
pub(crate) fn height(&self) -> usize {
87+
self.height
6388
}
6489
}
65-
impl From<winit::dpi::PhysicalSize<u32>> for WindowSize {
66-
fn from(size: winit::dpi::PhysicalSize<u32>) -> Self {
67-
Self::new(size.width as usize, size.height as usize)
90+
impl Default for ViewInfo {
91+
fn default() -> Self {
92+
Self::new()
6893
}
6994
}
7095

96+
pub(crate) enum ViewInfoUpdate {
97+
Size { width: usize, height: usize },
98+
Scale(f64),
99+
}
100+
71101
#[derive(Clone)]
72102
pub(crate) struct Resource {
73103
pub(crate) reader: ResourceReader,
@@ -89,34 +119,33 @@ impl Read for ResourceReader {
89119
}
90120
}
91121

92-
#[derive(Clone)]
93122
pub(crate) struct CefHandler {
94123
wgpu_context: WgpuContext,
95124
app_event_scheduler: AppEventScheduler,
96-
window_size_receiver: Arc<Mutex<WindowSizeReceiver>>,
125+
view_info_receiver: Arc<Mutex<ViewInfoReceiver>>,
97126
}
98127

99128
impl CefHandler {
100-
pub(crate) fn new(wgpu_context: WgpuContext, app_event_scheduler: AppEventScheduler, window_size_receiver: Receiver<WindowSize>) -> Self {
129+
pub(crate) fn new(wgpu_context: WgpuContext, app_event_scheduler: AppEventScheduler, view_info_receiver: Receiver<ViewInfoUpdate>) -> Self {
101130
Self {
102131
wgpu_context,
103132
app_event_scheduler,
104-
window_size_receiver: Arc::new(Mutex::new(WindowSizeReceiver::new(window_size_receiver))),
133+
view_info_receiver: Arc::new(Mutex::new(ViewInfoReceiver::new(view_info_receiver))),
105134
}
106135
}
107136
}
108137

109138
impl CefEventHandler for CefHandler {
110-
fn window_size(&self) -> WindowSize {
111-
let Ok(mut guard) = self.window_size_receiver.lock() else {
112-
tracing::error!("Failed to lock window_size_receiver");
113-
return WindowSize::new(1, 1);
139+
fn view_info(&self) -> ViewInfo {
140+
let Ok(mut guard) = self.view_info_receiver.lock() else {
141+
tracing::error!("Failed to lock view_info_receiver");
142+
return ViewInfo::new();
114143
};
115-
let WindowSizeReceiver { receiver, window_size } = &mut *guard;
116-
for new_window_size in receiver.try_iter() {
117-
*window_size = new_window_size;
144+
let ViewInfoReceiver { receiver, view_info } = &mut *guard;
145+
for update in receiver.try_iter() {
146+
view_info.apply_update(update);
118147
}
119-
*window_size
148+
*view_info
120149
}
121150
fn draw<'a>(&self, frame_buffer: FrameBufferRef<'a>) {
122151
let width = frame_buffer.width() as u32;
@@ -244,17 +273,25 @@ impl CefEventHandler for CefHandler {
244273
};
245274
self.app_event_scheduler.schedule(AppEvent::DesktopWrapperMessage(desktop_wrapper_message));
246275
}
247-
}
248276

249-
struct WindowSizeReceiver {
250-
window_size: WindowSize,
251-
receiver: Receiver<WindowSize>,
252-
}
253-
impl WindowSizeReceiver {
254-
fn new(window_size_receiver: Receiver<WindowSize>) -> Self {
277+
fn duplicate(&self) -> Self
278+
where
279+
Self: Sized,
280+
{
255281
Self {
256-
window_size: WindowSize { width: 1, height: 1 },
257-
receiver: window_size_receiver,
282+
wgpu_context: self.wgpu_context.clone(),
283+
app_event_scheduler: self.app_event_scheduler.clone(),
284+
view_info_receiver: self.view_info_receiver.clone(),
258285
}
259286
}
260287
}
288+
289+
struct ViewInfoReceiver {
290+
view_info: ViewInfo,
291+
receiver: Receiver<ViewInfoUpdate>,
292+
}
293+
impl ViewInfoReceiver {
294+
fn new(receiver: Receiver<ViewInfoUpdate>) -> Self {
295+
Self { view_info: ViewInfo::new(), receiver }
296+
}
297+
}

desktop/src/cef/context.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ pub(crate) trait CefContext {
1010

1111
fn handle_window_event(&mut self, event: &winit::event::WindowEvent);
1212

13-
fn notify_of_resize(&self);
13+
fn notify_view_info_changed(&self);
1414

1515
fn send_web_message(&self, message: Vec<u8>);
1616
}

desktop/src/cef/context/builder.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use std::path::{Path, PathBuf};
33
use cef::args::Args;
44
use cef::sys::{CEF_API_VERSION_LAST, cef_resultcode_t};
55
use cef::{
6-
App, BrowserSettings, CefString, Client, DictionaryValue, ImplCommandLine, ImplRequestContext, RenderHandler, RequestContextSettings, SchemeHandlerFactory, Settings, WindowInfo, api_hash,
6+
App, BrowserSettings, CefString, Client, DictionaryValue, ImplCommandLine, ImplRequestContext, RequestContextSettings, SchemeHandlerFactory, Settings, WindowInfo, api_hash,
77
browser_host_create_browser_sync, execute_process,
88
};
99

@@ -13,7 +13,7 @@ use crate::cef::CefEventHandler;
1313
use crate::cef::consts::{RESOURCE_DOMAIN, RESOURCE_SCHEME};
1414
use crate::cef::dirs::create_instance_dir;
1515
use crate::cef::input::InputState;
16-
use crate::cef::internal::{BrowserProcessAppImpl, BrowserProcessClientImpl, RenderHandlerImpl, RenderProcessAppImpl, SchemeHandlerFactoryImpl};
16+
use crate::cef::internal::{BrowserProcessAppImpl, BrowserProcessClientImpl, RenderProcessAppImpl, SchemeHandlerFactoryImpl};
1717

1818
pub(crate) struct CefContextBuilder<H: CefEventHandler> {
1919
pub(crate) args: Args,
@@ -131,7 +131,7 @@ impl<H: CefEventHandler> CefContextBuilder<H> {
131131

132132
fn initialize_inner(self, event_handler: &H, settings: Settings) -> Result<(), InitError> {
133133
// Attention! Wrapping this in an extra App is necessary, otherwise the program still compiles but segfaults
134-
let mut cef_app = App::new(BrowserProcessAppImpl::new(event_handler.clone()));
134+
let mut cef_app = App::new(BrowserProcessAppImpl::new(event_handler.duplicate()));
135135

136136
let result = cef::initialize(Some(self.args.as_main_args()), Some(&settings), Some(&mut cef_app), std::ptr::null_mut());
137137
if result != 1 {
@@ -146,8 +146,7 @@ impl<H: CefEventHandler> CefContextBuilder<H> {
146146
}
147147

148148
fn create_browser<H: CefEventHandler>(event_handler: H, instance_dir: PathBuf, disable_gpu_acceleration: bool) -> Result<SingleThreadedCefContext, InitError> {
149-
let render_handler = RenderHandler::new(RenderHandlerImpl::new(event_handler.clone()));
150-
let mut client = Client::new(BrowserProcessClientImpl::new(render_handler, event_handler.clone()));
149+
let mut client = Client::new(BrowserProcessClientImpl::new(&event_handler));
151150

152151
#[cfg(feature = "accelerated_paint")]
153152
let use_accelerated_paint = if disable_gpu_acceleration {
@@ -180,7 +179,7 @@ fn create_browser<H: CefEventHandler>(event_handler: H, instance_dir: PathBuf, d
180179
return Err(InitError::RequestContextCreationFailed);
181180
};
182181

183-
let mut scheme_handler_factory = SchemeHandlerFactory::new(SchemeHandlerFactoryImpl::new(event_handler.clone()));
182+
let mut scheme_handler_factory = SchemeHandlerFactory::new(SchemeHandlerFactoryImpl::new(event_handler.duplicate()));
184183
incognito_request_context.clear_scheme_handler_factories();
185184
incognito_request_context.register_scheme_handler_factory(Some(&CefString::from(RESOURCE_SCHEME)), Some(&CefString::from(RESOURCE_DOMAIN)), Some(&mut scheme_handler_factory));
186185

@@ -197,6 +196,7 @@ fn create_browser<H: CefEventHandler>(event_handler: H, instance_dir: PathBuf, d
197196

198197
if let Some(browser) = browser {
199198
Ok(SingleThreadedCefContext {
199+
event_handler: Box::new(event_handler),
200200
browser,
201201
input_state: InputState::default(),
202202
instance_dir,

desktop/src/cef/context/multithreaded.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,11 @@ impl CefContext for MultiThreadedCefContextProxy {
3030
});
3131
}
3232

33-
fn notify_of_resize(&self) {
33+
fn notify_view_info_changed(&self) {
3434
run_on_ui_thread(move || {
3535
CONTEXT.with(|b| {
3636
if let Some(context) = b.borrow_mut().as_mut() {
37-
context.notify_of_resize();
37+
context.notify_view_info_changed();
3838
}
3939
});
4040
});

desktop/src/cef/context/singlethreaded.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
use cef::{Browser, ImplBrowser, ImplBrowserHost};
22
use winit::event::WindowEvent;
33

4-
use crate::cef::input;
54
use crate::cef::input::InputState;
65
use crate::cef::ipc::{MessageType, SendMessage};
6+
use crate::cef::{CefEventHandler, input};
77

88
use super::CefContext;
99

1010
pub(super) struct SingleThreadedCefContext {
11+
pub(super) event_handler: Box<dyn CefEventHandler>,
1112
pub(super) browser: Browser,
1213
pub(super) input_state: InputState,
1314
pub(super) instance_dir: std::path::PathBuf,
@@ -19,11 +20,14 @@ impl CefContext for SingleThreadedCefContext {
1920
}
2021

2122
fn handle_window_event(&mut self, event: &WindowEvent) {
22-
input::handle_window_event(&self.browser, &mut self.input_state, event)
23+
input::handle_window_event(&self.browser, &mut self.input_state, event);
2324
}
2425

25-
fn notify_of_resize(&self) {
26-
self.browser.host().unwrap().was_resized();
26+
fn notify_view_info_changed(&self) {
27+
let view_info = self.event_handler.view_info();
28+
let host = self.browser.host().unwrap();
29+
host.set_zoom_level(view_info.zoom());
30+
host.was_resized();
2731
}
2832

2933
fn send_web_message(&self, message: Vec<u8>) {

0 commit comments

Comments
 (0)