Platform: Add a Resized event and use that to convey the changes in size (#2759)

This commit is contained in:
Olivier Goffart 2023-05-21 12:12:30 +02:00 committed by GitHub
parent 846c48b81d
commit dd5ef9993f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 121 additions and 47 deletions

View file

@ -61,6 +61,9 @@ public:
/// do that in the next iteration of the event loop, or in a callback from the window manager.
virtual void request_redraw() const { }
/// Returns the actual physical size of the window
virtual slint::PhysicalSize physical_size() const = 0;
private:
friend class Platform;
virtual cbindgen_private::WindowAdapterRcOpaque initialize() = 0;
@ -94,7 +97,11 @@ private:
},
[](void *wa) { reinterpret_cast<const WA *>(wa)->show(); },
[](void *wa) { reinterpret_cast<const WA *>(wa)->hide(); },
[](void *wa) { reinterpret_cast<const WA *>(wa)->request_redraw(); }, &self);
[](void *wa) { reinterpret_cast<const WA *>(wa)->request_redraw(); },
[](void *wa) -> cbindgen_private::IntSize {
return reinterpret_cast<const WA *>(wa)->physical_size();
},
&self);
m_renderer.init(&self);
was_initialized = true;
return self;
@ -143,6 +150,16 @@ public:
}
}
/// Set the logical size of this window after a resize event
// Note: in rust, this is an event on the Window
void dispatch_resize_event(slint::LogicalSize s)
{
private_api::assert_main_thread();
if (was_initialized) {
cbindgen_private::slint_windowrc_dispatch_resize_event(&self, s.width, s.height);
}
}
/// Returns true if the window is currently animating
bool has_active_animations() const
{

View file

@ -30,6 +30,7 @@ pub struct CppWindowAdapter {
show: unsafe extern "C" fn(WindowAdapterUserData),
hide: unsafe extern "C" fn(WindowAdapterUserData),
request_redraw: unsafe extern "C" fn(WindowAdapterUserData),
size: unsafe extern "C" fn(WindowAdapterUserData) -> IntSize,
}
impl Drop for CppWindowAdapter {
@ -61,6 +62,11 @@ impl WindowAdapterSealed for CppWindowAdapter {
fn request_redraw(&self) {
unsafe { (self.request_redraw)(self.user_data) }
}
fn size(&self) -> PhysicalSize {
let s = unsafe { (self.size)(self.user_data) };
PhysicalSize::new(s.width, s.height)
}
}
#[no_mangle]
@ -71,6 +77,7 @@ pub unsafe extern "C" fn slint_window_adapter_new(
show: unsafe extern "C" fn(WindowAdapterUserData),
hide: unsafe extern "C" fn(WindowAdapterUserData),
request_redraw: unsafe extern "C" fn(WindowAdapterUserData),
size: unsafe extern "C" fn(WindowAdapterUserData) -> IntSize,
target: *mut WindowAdapterRcOpaque,
) {
let window = Rc::<CppWindowAdapter>::new_cyclic(|w| CppWindowAdapter {
@ -81,6 +88,7 @@ pub unsafe extern "C" fn slint_window_adapter_new(
show,
request_redraw,
hide,
size,
});
core::ptr::write(target as *mut Rc<dyn WindowAdapter>, window);
@ -131,6 +139,19 @@ pub unsafe extern "C" fn slint_windowrc_has_active_animations(
window_adapter.window().has_active_animations()
}
/// Dispatch a key pressed or release event
#[no_mangle]
pub unsafe extern "C" fn slint_windowrc_dispatch_resize_event(
handle: *const WindowAdapterRcOpaque,
width: f32,
height: f32,
) {
let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
window_adapter.window().dispatch_event(i_slint_core::platform::WindowEvent::Resized {
size: i_slint_core::api::LogicalSize { width, height },
});
}
#[no_mangle]
pub extern "C" fn slint_platform_update_timers_and_animations() {
i_slint_core::platform::update_timers_and_animations()

View file

@ -38,7 +38,7 @@ struct MyWindowAdapter : NativeWindowHandle,
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)this);
}
slint::PhysicalSize windowSize() const
slint::PhysicalSize physical_size() const override
{
RECT r;
GetWindowRect(hwnd, &r);
@ -61,7 +61,7 @@ struct MyWindowAdapter : NativeWindowHandle,
void render()
{
renderer().render(windowSize());
renderer().render(physical_size());
if (has_active_animations())
request_redraw();
}
@ -70,7 +70,7 @@ struct MyWindowAdapter : NativeWindowHandle,
{
slint::PhysicalSize windowSize({ width, height });
renderer().resize(windowSize);
window().set_size(windowSize);
dispatch_resize_event(slint::LogicalSize({ (float)width, (float)height }));
}
void setGeometry(int x, int y, int width, int height)

View file

@ -99,6 +99,11 @@ public:
renderer().hide();
const_cast<MyWindow *>(this)->QWindow::hide();
}
slint::PhysicalSize physical_size() const override
{
auto s = size();
return slint::PhysicalSize({ uint32_t(s.width()), uint32_t(s.height()) });
}
void request_redraw() const override { const_cast<MyWindow *>(this)->requestUpdate(); }
@ -107,7 +112,10 @@ public:
auto windowSize = slint::PhysicalSize(
{ uint32_t(ev->size().width()), uint32_t(ev->size().height()) });
renderer().resize(windowSize);
slint_platform::WindowAdapter<slint_platform::SkiaRenderer>::window().set_size(windowSize);
float scale_factor = devicePixelRatio();
WindowAdapter<slint_platform::SkiaRenderer>::dispatch_resize_event(
slint::LogicalSize({ float(windowSize.width) / scale_factor,
float(windowSize.height) / scale_factor }));
}
void mousePressEvent(QMouseEvent *event) override

View file

@ -1459,7 +1459,9 @@ impl QtWindow {
}
fn resize_event(&self, size: qttypes::QSize) {
self.window.set_size(i_slint_core::api::PhysicalSize::new(size.width, size.height));
self.window().dispatch_event(WindowEvent::Resized {
size: i_slint_core::api::LogicalSize::new(size.width as _, size.height as _),
});
}
fn mouse_event(&self, event: MouseEvent) {
@ -1790,6 +1792,14 @@ impl WindowAdapterSealed for QtWindow {
}};
}
fn size(&self) -> i_slint_core::api::PhysicalSize {
let widget_ptr = self.widget_ptr();
let s = cpp! {unsafe [widget_ptr as "QWidget*"] -> qttypes::QSize as "QSize" {
return widget_ptr->size();
}};
i_slint_core::api::PhysicalSize::new(s.width as _, s.height as _)
}
fn dark_color_scheme(&self) -> bool {
let ds = self.dark_color_scheme.get_or_init(|| {
Box::pin(Property::new(cpp! {unsafe [] -> bool as "bool" {

View file

@ -27,6 +27,7 @@ impl i_slint_core::platform::Platform for TestingBackend {
Ok(Rc::new_cyclic(|self_weak| TestingWindow {
window: i_slint_core::api::Window::new(self_weak.clone() as _),
shown: false.into(),
size: Default::default(),
}))
}
@ -53,6 +54,7 @@ impl i_slint_core::platform::Platform for TestingBackend {
pub struct TestingWindow {
window: i_slint_core::api::Window,
shown: core::cell::Cell<bool>,
size: core::cell::Cell<PhysicalSize>,
}
impl WindowAdapterSealed for TestingWindow {
@ -82,6 +84,14 @@ impl WindowAdapterSealed for TestingWindow {
unimplemented!()
}
fn size(&self) -> PhysicalSize {
self.size.get()
}
fn set_size(&self, size: i_slint_core::api::WindowSize) {
self.size.set(size.to_physical(1.))
}
fn is_visible(&self) -> bool {
self.shown.get()
}
@ -226,4 +236,5 @@ mod for_unit_test {
}
pub use for_unit_test::*;
use i_slint_core::api::PhysicalSize;
use i_slint_core::platform::PlatformError;

View file

@ -10,7 +10,6 @@
use copypasta::ClipboardProvider;
use corelib::items::PointerEventButton;
use corelib::lengths::LogicalPoint;
use corelib::lengths::LogicalSize;
use corelib::SharedString;
use i_slint_core as corelib;
@ -441,10 +440,8 @@ fn process_window_event(
}
WindowEvent::ScaleFactorChanged { scale_factor, new_inner_size } => {
if std::env::var("SLINT_SCALE_FACTOR").is_err() {
let size = new_inner_size.to_logical(scale_factor);
runtime_window.set_window_item_geometry(LogicalSize::new(size.width, size.height));
window.window().dispatch_event(
i_slint_core::platform::WindowEvent::ScaleFactorChanged {
corelib::platform::WindowEvent::ScaleFactorChanged {
scale_factor: scale_factor as f32,
},
);

View file

@ -106,7 +106,6 @@ pub struct WinitWindowAdapter {
self_weak: Weak<Self>,
currently_pressed_key_code: std::cell::Cell<Option<winit::event::VirtualKeyCode>>,
pending_redraw: Cell<bool>,
in_resize_event: Cell<bool>,
dark_color_scheme: OnceCell<Pin<Box<Property<bool>>>>,
constraints: Cell<(corelib::layout::LayoutInfo, corelib::layout::LayoutInfo)>,
shown: Cell<bool>,
@ -126,7 +125,6 @@ impl Default for WinitWindowAdapter {
self_weak: Default::default(),
currently_pressed_key_code: Default::default(),
pending_redraw: Default::default(),
in_resize_event: Default::default(),
dark_color_scheme: Default::default(),
constraints: Default::default(),
shown: Default::default(),
@ -312,22 +310,15 @@ impl WinitWindowAdapter {
}
pub fn resize_event(&self, size: winit::dpi::PhysicalSize<u32>) -> Result<(), PlatformError> {
// slint::Window::set_size will call set_size() on this type, which would call
// set_inner_size on the winit Window. On Windows that triggers an new resize event
// in the next event loop iteration for mysterious reasons, with slightly different sizes.
// I suspect a bug in the way the frame decorations are added and subtracted from the size
// we provide.
// Work around it with this guard that prevents us from calling set_inner_size again.
assert!(!self.in_resize_event.get());
self.in_resize_event.set(true);
scopeguard::defer! { self.in_resize_event.set(false); }
// When a window is minimized on Windows, we get a move event to an off-screen position
// and a resize even with a zero size. Don't forward that, especially not to the renderer,
// which might panic when trying to create a zero-sized surface.
if size.width > 0 && size.height > 0 {
let physical_size = physical_size_to_slint(&size);
self.window().set_size(physical_size);
let scale_factor = WindowInner::from_pub(self.window()).scale_factor();
self.window().dispatch_event(WindowEvent::Resized {
size: physical_size.to_logical(scale_factor),
});
self.renderer().resize_event(physical_size)
} else {
Ok(())
@ -399,7 +390,9 @@ impl WindowAdapterSealed for WinitWindowAdapter {
}
if must_resize {
self.window().set_size(i_slint_core::api::LogicalSize::new(width, height));
self.window().dispatch_event(WindowEvent::Resized {
size: i_slint_core::api::LogicalSize::new(width, height),
});
}
}
@ -546,11 +539,6 @@ impl WindowAdapterSealed for WinitWindowAdapter {
self_.renderer().show()?;
winit_window.set_visible(true);
let s = winit_window.inner_size().to_logical(scale_factor);
// Make sure that the window's inner size is in sync with the root window item's
// width/height.
runtime_window.set_window_item_geometry(LogicalSize::new(s.width, s.height));
// Make sure the dark color scheme property is up-to-date, as it may have been queried earlier when
// the window wasn't mapped yet.
if let Some(dark_color_scheme_prop) = self_.dark_color_scheme.get() {
@ -680,12 +668,13 @@ impl WindowAdapterSealed for WinitWindowAdapter {
}
fn set_size(&self, size: corelib::api::WindowSize) {
if self.in_resize_event.get() {
return;
}
self.winit_window().set_inner_size(window_size_to_slint(&size))
}
fn size(&self) -> corelib::api::PhysicalSize {
physical_size_to_slint(&self.winit_window().inner_size())
}
fn dark_color_scheme(&self) -> bool {
self.dark_color_scheme
.get_or_init(|| {

View file

@ -328,6 +328,7 @@ impl Window {
/// //...
/// }
/// # impl i_slint_core::window::WindowAdapterSealed for MyWindowAdapter {
/// # fn size(&self) -> i_slint_core::api::PhysicalSize { unimplemented!() }
/// # fn renderer(&self) -> &dyn i_slint_core::renderer::Renderer { unimplemented!() }
/// # }
///
@ -398,20 +399,14 @@ impl Window {
/// Returns the size of the window on the screen, in physical screen coordinates and excluding
/// a window frame (if present).
pub fn size(&self) -> PhysicalSize {
self.0.inner_size.get()
self.0.window_adapter().size()
}
/// Resizes the window to the specified size on the screen, in physical pixels and excluding
/// a window frame (if present).
pub fn set_size(&self, size: impl Into<WindowSize>) {
let size = size.into();
let l = size.to_logical(self.scale_factor()).to_euclid();
let p = size.to_physical(self.scale_factor());
self.0.set_window_item_geometry(l);
if self.0.inner_size.replace(p) != p {
self.0.window_adapter().set_size(size);
}
self.0.window_adapter().set_size(size);
}
/// Dispatch a window event to the scene.
@ -469,6 +464,9 @@ impl Window {
crate::platform::WindowEvent::ScaleFactorChanged { scale_factor } => {
self.0.set_scale_factor(scale_factor);
}
crate::platform::WindowEvent::Resized { size } => {
self.0.set_window_item_geometry(size.to_euclid());
}
}
}

View file

@ -7,8 +7,8 @@ The backend is the abstraction for crates that need to do the actual drawing and
#![warn(missing_docs)]
use crate::api::LogicalPosition;
pub use crate::api::PlatformError;
use crate::api::{LogicalPosition, LogicalSize};
pub use crate::software_renderer;
#[cfg(all(not(feature = "std"), feature = "unsafe-single-threaded"))]
use crate::unsafe_single_threaded::{thread_local, OnceCell};
@ -278,6 +278,14 @@ pub enum WindowEvent {
/// The window system provided scale factor to map logical pixels to physical pixels.
scale_factor: f32,
},
/// The window was resized.
///
/// The backend must send this event to ensure that the `width` and `height` property of the root Window
/// element are properly set.
Resized {
/// The new logical size of the window
size: LogicalSize,
},
}
impl WindowEvent {

View file

@ -1923,6 +1923,7 @@ pub struct MinimalSoftwareWindow {
window: Window,
renderer: SoftwareRenderer,
needs_redraw: Cell<bool>,
size: Cell<crate::api::PhysicalSize>,
}
impl MinimalSoftwareWindow {
@ -1934,6 +1935,7 @@ impl MinimalSoftwareWindow {
window: Window::new(w.clone()),
renderer: SoftwareRenderer::new(repaint_buffer_type, w.clone()),
needs_redraw: Default::default(),
size: Default::default(),
})
}
/// If the window needs to be redrawn, the callback will be called with the
@ -1967,6 +1969,15 @@ impl crate::window::WindowAdapterSealed for MinimalSoftwareWindow {
_items: &mut dyn Iterator<Item = Pin<crate::items::ItemRef<'a>>>,
) {
}
fn size(&self) -> crate::api::PhysicalSize {
self.size.get()
}
fn set_size(&self, size: crate::api::WindowSize) {
self.size.set(size.to_physical(1.));
self.window
.dispatch_event(crate::platform::WindowEvent::Resized { size: size.to_logical(1.) })
}
}
impl WindowAdapter for MinimalSoftwareWindow {

View file

@ -171,6 +171,8 @@ pub trait WindowAdapterSealed {
/// [`Window::set_size`] will call this function again, so you must be prepared to get recursions.
// FIXME: before making that public, we need to add a WindowEvent::Resized to avoid the above recursion
fn set_size(&self, _size: WindowSize) {}
/// Return the size of the Window on the screen
fn size(&self) -> PhysicalSize;
/// returns wether a dark theme is used
fn dark_color_scheme(&self) -> bool {
@ -287,9 +289,6 @@ pub struct WindowInner {
active_popup: RefCell<Option<PopupWindow>>,
close_requested: Callback<(), CloseRequestResponse>,
click_state: ClickState,
/// This is a cache of the size set by the set_inner_size setter.
/// It should be mapping with the WindowItem::width and height (only in physical)
pub(crate) inner_size: Cell<PhysicalSize>,
}
impl Drop for WindowInner {
@ -340,7 +339,6 @@ impl WindowInner {
cursor_blinker: Default::default(),
active_popup: Default::default(),
close_requested: Default::default(),
inner_size: Default::default(),
click_state: ClickState::default(),
}
}
@ -703,6 +701,12 @@ impl WindowInner {
pub fn show(&self) -> Result<(), PlatformError> {
self.update_window_properties();
self.window_adapter().show()?;
// Make sure that the window's inner size is in sync with the root window item's
// width/height.
self.set_window_item_geometry(
self.window_adapter().size().to_logical(self.scale_factor()).to_euclid(),
);
Ok(())
}
@ -822,7 +826,7 @@ impl WindowInner {
/// Sets the size of the window item. This method is typically called in response to receiving a
/// window resize event from the windowing system.
pub fn set_window_item_geometry(&self, size: LogicalSize) {
pub(crate) fn set_window_item_geometry(&self, size: LogicalSize) {
if let Some(component_rc) = self.try_component() {
let component = ComponentRc::borrow_pin(&component_rc);
let root_item = component.as_ref().get_item_ref(0);
@ -1153,7 +1157,7 @@ pub mod ffi {
#[no_mangle]
pub unsafe extern "C" fn slint_windowrc_size(handle: *const WindowAdapterRcOpaque) -> IntSize {
let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
WindowInner::from_pub(window_adapter.window()).inner_size.get().to_euclid().cast()
window_adapter.size().to_euclid().cast()
}
/// Resizes the window to the specified size on the screen, in physical pixels and excluding