mirror of
https://github.com/slint-ui/slint.git
synced 2025-08-04 18:58:36 +00:00
Platform: Add a Resized event and use that to convey the changes in size (#2759)
This commit is contained in:
parent
846c48b81d
commit
dd5ef9993f
12 changed files with 121 additions and 47 deletions
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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" {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
);
|
||||
|
|
|
@ -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(|| {
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue