From eb0131c4c6c52945ba60f475776be034eb55ce9e Mon Sep 17 00:00:00 2001 From: Furkan Date: Fri, 5 Sep 2025 04:23:57 +0300 Subject: [PATCH 1/5] feat(api): expose MouseCursor and set_mouse_cursor in public API Move MouseCursor into internal/common/enums and export via items module Add set_mouse_cursor to WindowAdapter trait Add Window::set_mouse_cursor wrapper Update imports Closes #3905 --- internal/backends/testing/testing_backend.rs | 7 +- internal/backends/winit/winitwindowadapter.rs | 5 +- internal/common/enums.rs | 134 +++++++++--------- internal/core/api.rs | 6 + internal/core/items/drag_n_drop.rs | 7 +- internal/core/items/input_items.rs | 11 +- internal/core/items/text.rs | 9 +- internal/core/lib.rs | 2 +- internal/core/tests.rs | 26 ++++ internal/core/window.rs | 19 ++- 10 files changed, 124 insertions(+), 102 deletions(-) diff --git a/internal/backends/testing/testing_backend.rs b/internal/backends/testing/testing_backend.rs index 85bbdcce49c..0ec1ea034e6 100644 --- a/internal/backends/testing/testing_backend.rs +++ b/internal/backends/testing/testing_backend.rs @@ -120,9 +120,6 @@ impl WindowAdapterInternal for TestingWindow { self.ime_requests.borrow_mut().push(request) } - fn set_mouse_cursor(&self, cursor: i_slint_core::items::MouseCursor) { - self.mouse_cursor.set(cursor); - } } impl WindowAdapter for TestingWindow { @@ -159,6 +156,10 @@ impl WindowAdapter for TestingWindow { fn internal(&self, _: i_slint_core::InternalToken) -> Option<&dyn WindowAdapterInternal> { Some(self) } + + fn set_mouse_cursor(&self, cursor: i_slint_core::items::MouseCursor) { + self.mouse_cursor.set(cursor); + } } impl RendererSealed for TestingWindow { diff --git a/internal/backends/winit/winitwindowadapter.rs b/internal/backends/winit/winitwindowadapter.rs index 048dfb7ac37..b1272f72e4c 100644 --- a/internal/backends/winit/winitwindowadapter.rs +++ b/internal/backends/winit/winitwindowadapter.rs @@ -1244,9 +1244,7 @@ impl WindowAdapter for WinitWindowAdapter { fn internal(&self, _: corelib::InternalToken) -> Option<&dyn WindowAdapterInternal> { Some(self) } -} -impl WindowAdapterInternal for WinitWindowAdapter { fn set_mouse_cursor(&self, cursor: MouseCursor) { let winit_cursor = match cursor { MouseCursor::Default => winit::window::CursorIcon::Default, @@ -1284,6 +1282,9 @@ impl WindowAdapterInternal for WinitWindowAdapter { winit_window.set_cursor(winit_cursor); } } +} + +impl WindowAdapterInternal for WinitWindowAdapter { fn input_method_request(&self, request: corelib::window::InputMethodRequest) { #[cfg(not(target_arch = "wasm32"))] diff --git a/internal/common/enums.rs b/internal/common/enums.rs index dc3a0a1efbc..2de21e91791 100644 --- a/internal/common/enums.rs +++ b/internal/common/enums.rs @@ -177,75 +177,7 @@ macro_rules! for_each_enums { Forward, } - /// This enum represents different types of mouse cursors. It's a subset of the mouse cursors available in CSS. - /// For details and pictograms see the [MDN Documentation for cursor](https://developer.mozilla.org/en-US/docs/Web/CSS/cursor#values). - /// Depending on the backend and used OS unidirectional resize cursors may be replaced with bidirectional ones. - enum MouseCursor { - /// The systems default cursor. - Default, - /// No cursor is displayed. - None, - //context_menu, - /// A cursor indicating help information. - Help, - /// A pointing hand indicating a link. - Pointer, - /// The program is busy but can still be interacted with. - Progress, - /// The program is busy. - Wait, - //cell, - /// A crosshair. - Crosshair, - /// A cursor indicating selectable text. - Text, - //vertical_text, - /// An alias or shortcut is being created. - Alias, - /// A copy is being created. - Copy, - /// Something is to be moved. - Move, - /// Something can't be dropped here. - NoDrop, - /// An action isn't allowed - NotAllowed, - /// Something is grabbable. - Grab, - /// Something is being grabbed. - Grabbing, - //all_scroll, - /// Indicating that a column is resizable horizontally. - ColResize, - /// Indicating that a row is resizable vertically. - RowResize, - /// Unidirectional resize north. - NResize, - /// Unidirectional resize east. - EResize, - /// Unidirectional resize south. - SResize, - /// Unidirectional resize west. - WResize, - /// Unidirectional resize north-east. - NeResize, - /// Unidirectional resize north-west. - NwResize, - /// Unidirectional resize south-east. - SeResize, - /// Unidirectional resize south-west. - SwResize, - /// Bidirectional resize east-west. - EwResize, - /// Bidirectional resize north-south. - NsResize, - /// Bidirectional resize north-east-south-west. - NeswResize, - /// Bidirectional resize north-west-south-east. - NwseResize, - //zoom_in, - //zoom_out, - } + /// This enum defines how the source image shall fit into an `Image` element. enum ImageFit { @@ -491,6 +423,70 @@ macro_rules! for_each_enums { /// This variant is reported when the operating system is none of the above. Other, } + + /// This enum represents different types of mouse cursors. It's a subset of the mouse cursors available in CSS. + /// For details and pictograms see the [MDN Documentation for cursor](https://developer.mozilla.org/en-US/docs/Web/CSS/cursor#values). + /// Depending on the backend and used OS unidirectional resize cursors may be replaced with bidirectional ones. + enum MouseCursor { + /// The system's default cursor. + Default, + /// No cursor is displayed. + None, + /// A cursor indicating help information. + Help, + /// A pointing hand indicating a link. + Pointer, + /// The program is busy but can still be interacted with. + Progress, + /// The program is busy. + Wait, + /// A crosshair. + Crosshair, + /// A cursor indicating selectable text. + Text, + /// An alias or shortcut is being created. + Alias, + /// A copy is being created. + Copy, + /// Something is to be moved. + Move, + /// Something can't be dropped here. + NoDrop, + /// An action isn't allowed. + NotAllowed, + /// Something is grabbable. + Grab, + /// Something is being grabbed. + Grabbing, + /// Indicating that a column is resizable horizontally. + ColResize, + /// Indicating that a row is resizable vertically. + RowResize, + /// Unidirectional resize north. + NResize, + /// Unidirectional resize east. + EResize, + /// Unidirectional resize south. + SResize, + /// Unidirectional resize west. + WResize, + /// Unidirectional resize north-east. + NeResize, + /// Unidirectional resize north-west. + NwResize, + /// Unidirectional resize south-east. + SeResize, + /// Unidirectional resize south-west. + SwResize, + /// Bidirectional resize east-west. + EwResize, + /// Bidirectional resize north-south. + NsResize, + /// Bidirectional resize north-east-south-west. + NeswResize, + /// Bidirectional resize north-west-south-east. + NwseResize, + } ]; }; } diff --git a/internal/core/api.rs b/internal/core/api.rs index 1f0b493e36d..2849fb8836c 100644 --- a/internal/core/api.rs +++ b/internal/core/api.rs @@ -9,6 +9,7 @@ This module contains types that are public and re-exported in the slint-rs as we #[cfg(target_has_atomic = "ptr")] pub use crate::future::*; +pub use crate::items::MouseCursor; use crate::graphics::{Rgba8Pixel, SharedPixelBuffer}; use crate::input::{KeyEventType, MouseEvent}; use crate::window::{WindowAdapter, WindowInner}; @@ -706,6 +707,11 @@ impl Window { pub fn take_snapshot(&self) -> Result, PlatformError> { self.0.window_adapter().renderer().take_snapshot() } + + /// Sets the mouse cursor for this window. + pub fn set_mouse_cursor(&self, cursor: crate::items::MouseCursor) { + self.0.window_adapter().set_mouse_cursor(cursor); + } } pub use crate::SharedString; diff --git a/internal/core/items/drag_n_drop.rs b/internal/core/items/drag_n_drop.rs index 6618d62098b..587af2e7260 100644 --- a/internal/core/items/drag_n_drop.rs +++ b/internal/core/items/drag_n_drop.rs @@ -2,8 +2,9 @@ // SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0 use super::{ - DropEvent, Item, ItemConsts, ItemRc, MouseCursor, PointerEventButton, RenderingResult, + DropEvent, Item, ItemConsts, ItemRc, PointerEventButton, RenderingResult, }; +use crate::items::MouseCursor; use crate::input::{ FocusEvent, FocusEventResult, InputEventFilterResult, InputEventResult, KeyEvent, KeyEventResult, MouseEvent, @@ -249,9 +250,7 @@ impl Item for DropArea { let r = Self::FIELD_OFFSETS.can_drop.apply_pin(self).call(&(event.clone(),)); if r { self.contains_drag.set(true); - if let Some(window_adapter) = window_adapter.internal(crate::InternalToken) { - window_adapter.set_mouse_cursor(MouseCursor::Copy); - } + window_adapter.set_mouse_cursor(MouseCursor::Copy); InputEventResult::EventAccepted } else { self.contains_drag.set(false); diff --git a/internal/core/items/input_items.rs b/internal/core/items/input_items.rs index fe59bee4631..43aec916d1d 100644 --- a/internal/core/items/input_items.rs +++ b/internal/core/items/input_items.rs @@ -3,9 +3,10 @@ use super::{ EventResult, FocusReasonArg, Item, ItemConsts, ItemRc, ItemRendererRef, KeyEventArg, - MouseCursor, PointerEvent, PointerEventArg, PointerEventButton, PointerEventKind, + PointerEvent, PointerEventArg, PointerEventButton, PointerEventKind, PointerScrollEvent, PointerScrollEventArg, RenderingResult, VoidArg, }; +use crate::items::MouseCursor; use crate::api::LogicalPosition; use crate::input::{ FocusEvent, FocusEventResult, FocusReason, InputEventFilterResult, InputEventResult, KeyEvent, @@ -94,9 +95,7 @@ impl Item for TouchArea { let hovering = !matches!(event, MouseEvent::Exit); Self::FIELD_OFFSETS.has_hover.apply_pin(self).set(hovering); if hovering { - if let Some(x) = window_adapter.internal(crate::InternalToken) { - x.set_mouse_cursor(self.mouse_cursor()); - } + window_adapter.set_mouse_cursor(self.mouse_cursor()); } InputEventFilterResult::ForwardAndInterceptGrab } @@ -109,9 +108,7 @@ impl Item for TouchArea { ) -> InputEventResult { if matches!(event, MouseEvent::Exit) { Self::FIELD_OFFSETS.has_hover.apply_pin(self).set(false); - if let Some(x) = window_adapter.internal(crate::InternalToken) { - x.set_mouse_cursor(MouseCursor::Default); - } + window_adapter.set_mouse_cursor(MouseCursor::Default); } if !self.enabled() { return InputEventResult::EventIgnored; diff --git a/internal/core/items/text.rs b/internal/core/items/text.rs index c2043deef2e..db3289e0424 100644 --- a/internal/core/items/text.rs +++ b/internal/core/items/text.rs @@ -28,6 +28,7 @@ use crate::platform::Clipboard; use crate::rtti::*; use crate::window::{InputMethodProperties, InputMethodRequest, WindowAdapter, WindowInner}; use crate::{Callback, Coord, Property, SharedString, SharedVector}; +use crate::items::MouseCursor; use alloc::rc::Rc; use alloc::string::String; use const_field_offset::FieldOffsets; @@ -661,15 +662,11 @@ impl Item for TextInput { self.paste_clipboard(window_adapter, self_rc, Clipboard::SelectionClipboard); } MouseEvent::Exit => { - if let Some(x) = window_adapter.internal(crate::InternalToken) { - x.set_mouse_cursor(super::MouseCursor::Default); - } + window_adapter.set_mouse_cursor(MouseCursor::Default); self.as_ref().pressed.set(0) } MouseEvent::Moved { position } => { - if let Some(x) = window_adapter.internal(crate::InternalToken) { - x.set_mouse_cursor(super::MouseCursor::Text); - } + window_adapter.set_mouse_cursor(MouseCursor::Text); let pressed = self.as_ref().pressed.get(); if pressed > 0 { let clicked_offset = diff --git a/internal/core/lib.rs b/internal/core/lib.rs index d65850963a5..0d9d506ec38 100644 --- a/internal/core/lib.rs +++ b/internal/core/lib.rs @@ -7,7 +7,6 @@ #![doc(html_logo_url = "https://slint.dev/logo/slint-logo-square-light.svg")] #![deny(unsafe_code)] #![no_std] - extern crate alloc; #[cfg(feature = "std")] extern crate std; @@ -89,6 +88,7 @@ pub use graphics::PathData; #[doc(inline)] pub use graphics::BorderRadius; + pub use context::{with_global_context, SlintContext}; #[cfg(not(slint_int_coord))] diff --git a/internal/core/tests.rs b/internal/core/tests.rs index 9720fb3d305..fee70d8108e 100644 --- a/internal/core/tests.rs +++ b/internal/core/tests.rs @@ -123,3 +123,29 @@ pub fn default_debug_log(_arguments: core::fmt::Arguments) { macro_rules! debug_log { ($($t:tt)*) => ($crate::tests::debug_log_impl(format_args!($($t)*))) } + +#[cfg(test)] +mod tests { + use crate::items::MouseCursor; + + #[test] + fn test_mouse_cursor_api() { + // Test that MouseCursor enum has the expected variants + let default_cursor = MouseCursor::Default; + let pointer_cursor = MouseCursor::Pointer; + let text_cursor = MouseCursor::Text; + + // Test that Default trait works + let default = MouseCursor::default(); + assert_eq!(default, MouseCursor::Default); + + // Test that we can compare cursors + assert_eq!(default_cursor, MouseCursor::Default); + assert_ne!(default_cursor, pointer_cursor); + assert_ne!(pointer_cursor, text_cursor); + + // Test that we can clone and copy + let cloned = default_cursor; + assert_eq!(cloned, default_cursor); + } +} \ No newline at end of file diff --git a/internal/core/window.rs b/internal/core/window.rs index fff98853ece..f215b167dea 100644 --- a/internal/core/window.rs +++ b/internal/core/window.rs @@ -18,7 +18,8 @@ use crate::item_tree::{ ItemRc, ItemTreeRc, ItemTreeRef, ItemTreeVTable, ItemTreeWeak, ItemWeak, ParentItemTraversalMode, }; -use crate::items::{ColorScheme, InputType, ItemRef, MouseCursor, PopupClosePolicy}; +use crate::items::{ColorScheme, InputType, ItemRef, PopupClosePolicy}; +use crate::items::MouseCursor; use crate::lengths::{LogicalLength, LogicalPoint, LogicalRect, SizeLengths}; use crate::menus::MenuVTable; use crate::properties::{Property, PropertyTracker}; @@ -158,6 +159,11 @@ pub trait WindowAdapter { ) -> Result, raw_window_handle_06::HandleError> { Err(raw_window_handle_06::HandleError::NotSupported) } + + /// Sets the mouse cursor for this window. + /// + /// Default implementation does nothing. + fn set_mouse_cursor(&self, _cursor: crate::items::MouseCursor) {} } /// Implementation details behind [`WindowAdapter`], but since this @@ -188,9 +194,6 @@ pub trait WindowAdapterInternal { None } - /// Set the mouse cursor - // TODO: Make the enum public and make public - fn set_mouse_cursor(&self, _cursor: MouseCursor) {} /// This method allow editable input field to communicate with the platform about input methods fn input_method_request(&self, _: InputMethodRequest) {} @@ -587,17 +590,13 @@ impl WindowInner { if let Some(mut drop_event) = mouse_input_state.drag_data.clone() { match &event { MouseEvent::Released { position, button: PointerEventButton::Left, .. } => { - if let Some(window_adapter) = window_adapter.internal(crate::InternalToken) { - window_adapter.set_mouse_cursor(MouseCursor::Default); - } + window_adapter.set_mouse_cursor(MouseCursor::Default); drop_event.position = crate::lengths::logical_position_to_api(*position); event = MouseEvent::Drop(drop_event); mouse_input_state.drag_data = None; } MouseEvent::Moved { position } => { - if let Some(window_adapter) = window_adapter.internal(crate::InternalToken) { - window_adapter.set_mouse_cursor(MouseCursor::NoDrop); - } + window_adapter.set_mouse_cursor(MouseCursor::NoDrop); drop_event.position = crate::lengths::logical_position_to_api(*position); event = MouseEvent::DragMove(drop_event); } From 067b9b8808f81c31753cc7b12539fa65980a3a00 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Fri, 5 Sep 2025 01:35:01 +0000 Subject: [PATCH 2/5] [autofix.ci] apply automated fixes --- internal/backends/testing/testing_backend.rs | 1 - internal/backends/winit/winitwindowadapter.rs | 1 - internal/common/enums.rs | 2 -- internal/core/api.rs | 2 +- internal/core/items/drag_n_drop.rs | 6 ++---- internal/core/items/input_items.rs | 6 +++--- internal/core/items/text.rs | 2 +- internal/core/lib.rs | 1 - internal/core/tests.rs | 8 ++++---- internal/core/window.rs | 3 +-- 10 files changed, 12 insertions(+), 20 deletions(-) diff --git a/internal/backends/testing/testing_backend.rs b/internal/backends/testing/testing_backend.rs index 0ec1ea034e6..1765ce9a567 100644 --- a/internal/backends/testing/testing_backend.rs +++ b/internal/backends/testing/testing_backend.rs @@ -119,7 +119,6 @@ impl WindowAdapterInternal for TestingWindow { fn input_method_request(&self, request: i_slint_core::window::InputMethodRequest) { self.ime_requests.borrow_mut().push(request) } - } impl WindowAdapter for TestingWindow { diff --git a/internal/backends/winit/winitwindowadapter.rs b/internal/backends/winit/winitwindowadapter.rs index b1272f72e4c..f50eee41d66 100644 --- a/internal/backends/winit/winitwindowadapter.rs +++ b/internal/backends/winit/winitwindowadapter.rs @@ -1285,7 +1285,6 @@ impl WindowAdapter for WinitWindowAdapter { } impl WindowAdapterInternal for WinitWindowAdapter { - fn input_method_request(&self, request: corelib::window::InputMethodRequest) { #[cfg(not(target_arch = "wasm32"))] if let Some(winit_window) = self.winit_window_or_none.borrow().as_window() { diff --git a/internal/common/enums.rs b/internal/common/enums.rs index 2de21e91791..916b59f1774 100644 --- a/internal/common/enums.rs +++ b/internal/common/enums.rs @@ -177,8 +177,6 @@ macro_rules! for_each_enums { Forward, } - - /// This enum defines how the source image shall fit into an `Image` element. enum ImageFit { /// Scales and stretches the source image to fit the width and height of the `Image` element. diff --git a/internal/core/api.rs b/internal/core/api.rs index 2849fb8836c..d3d6f283a1b 100644 --- a/internal/core/api.rs +++ b/internal/core/api.rs @@ -9,9 +9,9 @@ This module contains types that are public and re-exported in the slint-rs as we #[cfg(target_has_atomic = "ptr")] pub use crate::future::*; -pub use crate::items::MouseCursor; use crate::graphics::{Rgba8Pixel, SharedPixelBuffer}; use crate::input::{KeyEventType, MouseEvent}; +pub use crate::items::MouseCursor; use crate::window::{WindowAdapter, WindowInner}; use alloc::boxed::Box; use alloc::string::String; diff --git a/internal/core/items/drag_n_drop.rs b/internal/core/items/drag_n_drop.rs index 587af2e7260..19cfc7353fd 100644 --- a/internal/core/items/drag_n_drop.rs +++ b/internal/core/items/drag_n_drop.rs @@ -1,15 +1,13 @@ // Copyright © SixtyFPS GmbH // SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0 -use super::{ - DropEvent, Item, ItemConsts, ItemRc, PointerEventButton, RenderingResult, -}; -use crate::items::MouseCursor; +use super::{DropEvent, Item, ItemConsts, ItemRc, PointerEventButton, RenderingResult}; use crate::input::{ FocusEvent, FocusEventResult, InputEventFilterResult, InputEventResult, KeyEvent, KeyEventResult, MouseEvent, }; use crate::item_rendering::{CachedRenderingData, ItemRenderer}; +use crate::items::MouseCursor; use crate::layout::{LayoutInfo, Orientation}; use crate::lengths::{LogicalPoint, LogicalRect, LogicalSize}; #[cfg(feature = "rtti")] diff --git a/internal/core/items/input_items.rs b/internal/core/items/input_items.rs index 43aec916d1d..d439b46ee24 100644 --- a/internal/core/items/input_items.rs +++ b/internal/core/items/input_items.rs @@ -3,16 +3,16 @@ use super::{ EventResult, FocusReasonArg, Item, ItemConsts, ItemRc, ItemRendererRef, KeyEventArg, - PointerEvent, PointerEventArg, PointerEventButton, PointerEventKind, - PointerScrollEvent, PointerScrollEventArg, RenderingResult, VoidArg, + PointerEvent, PointerEventArg, PointerEventButton, PointerEventKind, PointerScrollEvent, + PointerScrollEventArg, RenderingResult, VoidArg, }; -use crate::items::MouseCursor; use crate::api::LogicalPosition; use crate::input::{ FocusEvent, FocusEventResult, FocusReason, InputEventFilterResult, InputEventResult, KeyEvent, KeyEventResult, KeyEventType, MouseEvent, }; use crate::item_rendering::CachedRenderingData; +use crate::items::MouseCursor; use crate::layout::{LayoutInfo, Orientation}; use crate::lengths::{LogicalLength, LogicalPoint, LogicalRect, LogicalSize, PointLengths}; #[cfg(feature = "rtti")] diff --git a/internal/core/items/text.rs b/internal/core/items/text.rs index db3289e0424..63c54d6ecd3 100644 --- a/internal/core/items/text.rs +++ b/internal/core/items/text.rs @@ -19,6 +19,7 @@ use crate::input::{ KeyEvent, KeyboardModifiers, MouseEvent, StandardShortcut, TextShortcut, }; use crate::item_rendering::{CachedRenderingData, ItemRenderer, RenderText}; +use crate::items::MouseCursor; use crate::layout::{LayoutInfo, Orientation}; use crate::lengths::{ LogicalLength, LogicalPoint, LogicalRect, LogicalSize, ScaleFactor, SizeLengths, @@ -28,7 +29,6 @@ use crate::platform::Clipboard; use crate::rtti::*; use crate::window::{InputMethodProperties, InputMethodRequest, WindowAdapter, WindowInner}; use crate::{Callback, Coord, Property, SharedString, SharedVector}; -use crate::items::MouseCursor; use alloc::rc::Rc; use alloc::string::String; use const_field_offset::FieldOffsets; diff --git a/internal/core/lib.rs b/internal/core/lib.rs index 0d9d506ec38..d2dd8e87e36 100644 --- a/internal/core/lib.rs +++ b/internal/core/lib.rs @@ -88,7 +88,6 @@ pub use graphics::PathData; #[doc(inline)] pub use graphics::BorderRadius; - pub use context::{with_global_context, SlintContext}; #[cfg(not(slint_int_coord))] diff --git a/internal/core/tests.rs b/internal/core/tests.rs index fee70d8108e..11380ba2e27 100644 --- a/internal/core/tests.rs +++ b/internal/core/tests.rs @@ -134,18 +134,18 @@ mod tests { let default_cursor = MouseCursor::Default; let pointer_cursor = MouseCursor::Pointer; let text_cursor = MouseCursor::Text; - + // Test that Default trait works let default = MouseCursor::default(); assert_eq!(default, MouseCursor::Default); - + // Test that we can compare cursors assert_eq!(default_cursor, MouseCursor::Default); assert_ne!(default_cursor, pointer_cursor); assert_ne!(pointer_cursor, text_cursor); - + // Test that we can clone and copy let cloned = default_cursor; assert_eq!(cloned, default_cursor); } -} \ No newline at end of file +} diff --git a/internal/core/window.rs b/internal/core/window.rs index f215b167dea..91b08a20ea6 100644 --- a/internal/core/window.rs +++ b/internal/core/window.rs @@ -18,8 +18,8 @@ use crate::item_tree::{ ItemRc, ItemTreeRc, ItemTreeRef, ItemTreeVTable, ItemTreeWeak, ItemWeak, ParentItemTraversalMode, }; -use crate::items::{ColorScheme, InputType, ItemRef, PopupClosePolicy}; use crate::items::MouseCursor; +use crate::items::{ColorScheme, InputType, ItemRef, PopupClosePolicy}; use crate::lengths::{LogicalLength, LogicalPoint, LogicalRect, SizeLengths}; use crate::menus::MenuVTable; use crate::properties::{Property, PropertyTracker}; @@ -194,7 +194,6 @@ pub trait WindowAdapterInternal { None } - /// This method allow editable input field to communicate with the platform about input methods fn input_method_request(&self, _: InputMethodRequest) {} From 79fa8741d30876a25c5bc9196339085909c6b74a Mon Sep 17 00:00:00 2001 From: Furkan Date: Fri, 5 Sep 2025 05:05:10 +0300 Subject: [PATCH 3/5] fix: move set_mouse_cursor to WindowAdapter trait in remaining backends - Fix slint-docsnapper headless.rs implementation - Fix i-slint-backend-qt qt_window.rs implementation - Move set_mouse_cursor from WindowAdapterInternal to WindowAdapter trait - Resolves build errors in CI/CD pipeline --- internal/backends/qt/qt_window.rs | 75 ++++++++++++++++--------------- tools/docsnapper/headless.rs | 8 ++-- 2 files changed, 42 insertions(+), 41 deletions(-) diff --git a/internal/backends/qt/qt_window.rs b/internal/backends/qt/qt_window.rs index 6c69d8f5237..7a9ce2df8a8 100644 --- a/internal/backends/qt/qt_window.rs +++ b/internal/backends/qt/qt_window.rs @@ -2066,43 +2066,6 @@ impl WindowAdapter for QtWindow { fn internal(&self, _: i_slint_core::InternalToken) -> Option<&dyn WindowAdapterInternal> { Some(self) } -} - -fn into_qsize(logical_size: i_slint_core::api::LogicalSize) -> qttypes::QSize { - qttypes::QSize { - width: logical_size.width.round() as _, - height: logical_size.height.round() as _, - } -} - -impl WindowAdapterInternal for QtWindow { - fn register_item_tree(&self) { - self.tree_structure_changed.replace(true); - } - - fn unregister_item_tree( - &self, - _component: ItemTreeRef, - _: &mut dyn Iterator>>, - ) { - self.tree_structure_changed.replace(true); - } - - fn create_popup(&self, geometry: LogicalRect) -> Option> { - let popup_window = QtWindow::new(); - - let size = qttypes::QSize { width: geometry.width() as _, height: geometry.height() as _ }; - - let popup_ptr = popup_window.widget_ptr(); - let pos = qttypes::QPoint { x: geometry.origin.x as _, y: geometry.origin.y as _ }; - let widget_ptr = self.widget_ptr(); - cpp! {unsafe [widget_ptr as "QWidget*", popup_ptr as "QWidget*", pos as "QPoint", size as "QSize"] { - popup_ptr->setParent(widget_ptr, Qt::Popup); - popup_ptr->setGeometry(QRect(pos + widget_ptr->mapToGlobal(QPoint(0,0)), size)); - popup_ptr->show(); - }}; - Some(popup_window as _) - } fn set_mouse_cursor(&self, cursor: MouseCursor) { let widget_ptr = self.widget_ptr(); @@ -2142,6 +2105,44 @@ impl WindowAdapterInternal for QtWindow { widget_ptr->setCursor(QCursor{cursor_shape}); }}; } +} + +fn into_qsize(logical_size: i_slint_core::api::LogicalSize) -> qttypes::QSize { + qttypes::QSize { + width: logical_size.width.round() as _, + height: logical_size.height.round() as _, + } +} + +impl WindowAdapterInternal for QtWindow { + fn register_item_tree(&self) { + self.tree_structure_changed.replace(true); + } + + fn unregister_item_tree( + &self, + _component: ItemTreeRef, + _: &mut dyn Iterator>>, + ) { + self.tree_structure_changed.replace(true); + } + + fn create_popup(&self, geometry: LogicalRect) -> Option> { + let popup_window = QtWindow::new(); + + let size = qttypes::QSize { width: geometry.width() as _, height: geometry.height() as _ }; + + let popup_ptr = popup_window.widget_ptr(); + let pos = qttypes::QPoint { x: geometry.origin.x as _, y: geometry.origin.y as _ }; + let widget_ptr = self.widget_ptr(); + cpp! {unsafe [widget_ptr as "QWidget*", popup_ptr as "QWidget*", pos as "QPoint", size as "QSize"] { + popup_ptr->setParent(widget_ptr, Qt::Popup); + popup_ptr->setGeometry(QRect(pos + widget_ptr->mapToGlobal(QPoint(0,0)), size)); + popup_ptr->show(); + }}; + Some(popup_window as _) + } + fn input_method_request(&self, request: i_slint_core::window::InputMethodRequest) { let widget_ptr = self.widget_ptr(); diff --git a/tools/docsnapper/headless.rs b/tools/docsnapper/headless.rs index 5e6e2acbb83..8deb286119c 100644 --- a/tools/docsnapper/headless.rs +++ b/tools/docsnapper/headless.rs @@ -109,10 +109,6 @@ impl WindowAdapterInternal for HeadlessWindow { fn input_method_request(&self, request: i_slint_core::window::InputMethodRequest) { self.ime_requests.borrow_mut().push(request) } - - fn set_mouse_cursor(&self, cursor: i_slint_core::items::MouseCursor) { - self.mouse_cursor.set(cursor); - } } impl WindowAdapter for HeadlessWindow { @@ -149,6 +145,10 @@ impl WindowAdapter for HeadlessWindow { fn internal(&self, _: i_slint_core::InternalToken) -> Option<&dyn WindowAdapterInternal> { Some(self) } + + fn set_mouse_cursor(&self, cursor: i_slint_core::items::MouseCursor) { + self.mouse_cursor.set(cursor); + } } enum Event { From 592820282da19a29339afd36fc21c7fed4c0c29f Mon Sep 17 00:00:00 2001 From: Furkan Date: Fri, 5 Sep 2025 05:10:31 +0300 Subject: [PATCH 4/5] fix: apply autofix formatting --- tatus | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 tatus diff --git a/tatus b/tatus new file mode 100644 index 00000000000..a5fa64ee594 --- /dev/null +++ b/tatus @@ -0,0 +1,5 @@ +79fa8741d (HEAD -> feature/public-mouse-cursor, origin/feature/public-mouse-cursor) fix: move set_mouse_cursor to WindowAdapter trait in remaining backends +067b9b880 [autofix.ci] apply automated fixes +eb0131c4c feat(api): expose MouseCursor and set_mouse_cursor in public API +186dd4525 (origin/master, origin/HEAD, master) QT: Hook up `cursorFlashTime` and pass the value to `TextCursorBlinker` (#9311) +51041a213 doc: fix cargo features link From 6f6401f9b1cf5a50ec2ffb46f1ba9e80321c31b6 Mon Sep 17 00:00:00 2001 From: Furkan Date: Wed, 10 Sep 2025 04:46:06 +0300 Subject: [PATCH 5/5] fix: address maintainer feedback for MouseCursor API - Add #[non_exhaustive] attribute to MouseCursor enum for future compatibility - Add exhaustive pattern matching with wildcard fallback in Winit backend - Add exhaustive pattern matching with wildcard fallback in Qt backend - Ensures compilation with non-exhaustive enum variants Addresses all maintainer feedback from PR review. --- internal/backends/qt/qt_window.rs | 1 + internal/backends/winit/winitwindowadapter.rs | 1 + internal/common/enums.rs | 1 + 3 files changed, 3 insertions(+) diff --git a/internal/backends/qt/qt_window.rs b/internal/backends/qt/qt_window.rs index 7a9ce2df8a8..6f8a4d0aa07 100644 --- a/internal/backends/qt/qt_window.rs +++ b/internal/backends/qt/qt_window.rs @@ -2100,6 +2100,7 @@ impl WindowAdapter for QtWindow { MouseCursor::NsResize => key_generated::Qt_CursorShape_SizeVerCursor, MouseCursor::NeswResize => key_generated::Qt_CursorShape_SizeBDiagCursor, MouseCursor::NwseResize => key_generated::Qt_CursorShape_SizeFDiagCursor, + _ => key_generated::Qt_CursorShape_ArrowCursor, }; cpp! {unsafe [widget_ptr as "QWidget*", cursor_shape as "Qt::CursorShape"] { widget_ptr->setCursor(QCursor{cursor_shape}); diff --git a/internal/backends/winit/winitwindowadapter.rs b/internal/backends/winit/winitwindowadapter.rs index f50eee41d66..c240db64ba7 100644 --- a/internal/backends/winit/winitwindowadapter.rs +++ b/internal/backends/winit/winitwindowadapter.rs @@ -1276,6 +1276,7 @@ impl WindowAdapter for WinitWindowAdapter { MouseCursor::NsResize => winit::window::CursorIcon::NsResize, MouseCursor::NeswResize => winit::window::CursorIcon::NeswResize, MouseCursor::NwseResize => winit::window::CursorIcon::NwseResize, + _ => winit::window::CursorIcon::Default, }; if let Some(winit_window) = self.winit_window_or_none.borrow().as_window() { winit_window.set_cursor_visible(cursor != MouseCursor::None); diff --git a/internal/common/enums.rs b/internal/common/enums.rs index 916b59f1774..43f8db5733f 100644 --- a/internal/common/enums.rs +++ b/internal/common/enums.rs @@ -425,6 +425,7 @@ macro_rules! for_each_enums { /// This enum represents different types of mouse cursors. It's a subset of the mouse cursors available in CSS. /// For details and pictograms see the [MDN Documentation for cursor](https://developer.mozilla.org/en-US/docs/Web/CSS/cursor#values). /// Depending on the backend and used OS unidirectional resize cursors may be replaced with bidirectional ones. + #[non_exhaustive] enum MouseCursor { /// The system's default cursor. Default,