Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions api/cpp/cbindgen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -722,6 +722,14 @@ fn gen_corelib(
.body
.insert("Flickable".to_owned(), " inline Flickable(); inline ~Flickable();".into());
config.export.pre_body.insert("FlickableDataBox".to_owned(), "struct FlickableData;".into());
config.export.body.insert(
"MouseCursor".to_owned(),
" constexpr MouseCursor(MouseCursor::Tag tag = Tag::Default) : tag(tag) {}
MouseCursor(MouseCursor::Tag tag, Image image, int hotspot_x, int hotspot_y) : tag(tag), custom_cursor(image, hotspot_x, hotspot_y) {}
MouseCursor& operator=(const MouseCursor &other) { tag = other.tag; custom_cursor = other.custom_cursor; return *this; }
~MouseCursor() {}
".into()
);

cbindgen::Builder::new()
.with_config(config)
Expand Down
11 changes: 11 additions & 0 deletions api/cpp/include/slint.h
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,17 @@ inline bool operator==(const EasingCurve &a, const EasingCurve &b)
}
return true;
}
inline bool operator==(const MouseCursor &a, const MouseCursor &b)
{
if (a.tag != b.tag) {
return false;
} else if (a.tag == MouseCursor::Tag::CustomCursor) {
return a.custom_cursor.image == b.custom_cursor.image
&& a.custom_cursor.hotspot_x == b.custom_cursor.hotspot_x
&& a.custom_cursor.hotspot_y == b.custom_cursor.hotspot_y;
}
return true;
}
}

namespace private_api {
Expand Down
2 changes: 1 addition & 1 deletion api/node/rust/interpreter/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,7 @@ pub fn to_value(env: &Env, unknown: JsUnknown, typ: &Type) -> Result<Value> {
| Type::LayoutCache
| Type::ArrayOfU16
| Type::ElementReference
| Type::StyledText => Err(napi::Error::from_reason("reason")),
| Type::StyledText => Err(napi::Error::from_reason("reason")) | Type::Cursor,
}
}

Expand Down
9 changes: 9 additions & 0 deletions internal/backends/qt/qt_window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1984,6 +1984,15 @@ impl WindowAdapterInternal for QtWindow {

fn set_mouse_cursor(&self, cursor: MouseCursor) {
let widget_ptr = self.widget_ptr();
if let MouseCursor::CustomCursor { image, hotspot_x, hotspot_y } = cursor {
let pixmap: qttypes::QPixmap =
crate::qt_window::image_to_pixmap((&image).into(), None).unwrap_or_default();
cpp! {unsafe [widget_ptr as "QWidget*", pixmap as "QPixmap", hotspot_x as "int", hotspot_y as "int"] {
widget_ptr->setCursor(QCursor{pixmap, hotspot_x, hotspot_y});
}};
return;
}

//unidirectional resize cursors are replaced with bidirectional ones
let cursor_shape = match cursor {
MouseCursor::Default => key_generated::Qt_CursorShape_ArrowCursor,
Expand Down
4 changes: 2 additions & 2 deletions internal/backends/testing/testing_backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ pub struct TestingWindow {
window: i_slint_core::api::Window,
size: Cell<PhysicalSize>,
pub ime_requests: RefCell<Vec<InputMethodRequest>>,
pub mouse_cursor: Cell<i_slint_core::items::MouseCursor>,
pub mouse_cursor: RefCell<i_slint_core::items::MouseCursor>,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not happy about this change (and the sibling one in headless) but it's the only way I got it to work, since MouseCursor is no longer Copy.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is just for tests anyway so it's fine.
Maybe the tests would be a bit nicer if there was a pub fn mouse_cursor(&self)->MouseCursor { self.mouse_cursor.borrow().clone() } getter function.

(And in headless this isn't even used, so we could just remove it)

}

impl WindowAdapterInternal for TestingWindow {
Expand All @@ -117,7 +117,7 @@ impl WindowAdapterInternal for TestingWindow {
}

fn set_mouse_cursor(&self, cursor: i_slint_core::items::MouseCursor) {
self.mouse_cursor.set(cursor);
self.mouse_cursor.replace(cursor);
}
}

Expand Down
18 changes: 17 additions & 1 deletion internal/backends/winit/event_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
*/
use crate::EventResult;
use crate::drag_resize_window::{handle_cursor_move_for_resize, handle_resize};
use crate::winitwindowadapter::WindowVisibility;
use crate::winitwindowadapter::{WindowVisibility, WinitWindowAdapter};
use crate::{SharedBackendData, SlintEvent};
use corelib::graphics::euclid;
use corelib::input::{KeyEvent, KeyEventType, MouseEvent};
Expand Down Expand Up @@ -175,6 +175,8 @@ impl winit::application::ApplicationHandler<SlintEvent> for EventLoopState {
}

let runtime_window = WindowInner::from_pub(window.window());
self.maybe_set_custom_cursor(&window, &event_loop);

match event {
WindowEvent::RedrawRequested => {
self.loop_error = window.draw().err();
Expand Down Expand Up @@ -603,6 +605,20 @@ impl EventLoopState {
}
}

/// Sets the cursor to a custom source, if it needs to be set.
pub fn maybe_set_custom_cursor(
&self,
window: &WinitWindowAdapter,
event_loop: &ActiveEventLoop,
) {
// If there is a new custom cursor, update it.
let custom_cursor_source = window.custom_cursor_source.take();
if let Some(source) = custom_cursor_source {
let custom_cursor = event_loop.create_custom_cursor(source);
window.winit_window().unwrap().set_cursor(custom_cursor);
}
}

/// Runs the event loop and renders the items in the provided `component` in its
/// own window.
#[cfg(all(not(target_arch = "wasm32"), not(ios_and_friends)))]
Expand Down
94 changes: 61 additions & 33 deletions internal/backends/winit/winitwindowadapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ use i_slint_core::{self as corelib};
use std::cell::OnceCell;
#[cfg(any(enable_accesskit, muda))]
use winit::event_loop::EventLoopProxy;
use winit::window::{WindowAttributes, WindowButtons};
use winit::window::{CustomCursor, CustomCursorSource, WindowAttributes, WindowButtons};

pub(crate) fn position_to_winit(pos: &corelib::api::WindowPosition) -> winit::dpi::Position {
match pos {
Expand Down Expand Up @@ -368,6 +368,8 @@ pub struct WinitWindowAdapter {
window_icon_cache_key: RefCell<Option<ImageCacheKey>>,

frame_throttle: Box<dyn crate::frame_throttle::FrameThrottle>,

pub(crate) custom_cursor_source: Cell<Option<CustomCursorSource>>,
}

impl WinitWindowAdapter {
Expand Down Expand Up @@ -416,6 +418,7 @@ impl WinitWindowAdapter {
self_weak.clone(),
shared_backend_data.is_wayland,
),
custom_cursor_source: Cell::new(None),
});

self_rc.shared_backend_data.register_inactive_window((self_rc.clone()) as _);
Expand Down Expand Up @@ -1324,41 +1327,66 @@ impl WindowAdapter for WinitWindowAdapter {

impl WindowAdapterInternal for WinitWindowAdapter {
fn set_mouse_cursor(&self, cursor: MouseCursor) {
let winit_cursor = match cursor {
MouseCursor::Default => winit::window::CursorIcon::Default,
MouseCursor::None => winit::window::CursorIcon::Default,
MouseCursor::Help => winit::window::CursorIcon::Help,
MouseCursor::Pointer => winit::window::CursorIcon::Pointer,
MouseCursor::Progress => winit::window::CursorIcon::Progress,
MouseCursor::Wait => winit::window::CursorIcon::Wait,
MouseCursor::Crosshair => winit::window::CursorIcon::Crosshair,
MouseCursor::Text => winit::window::CursorIcon::Text,
MouseCursor::Alias => winit::window::CursorIcon::Alias,
MouseCursor::Copy => winit::window::CursorIcon::Copy,
MouseCursor::Move => winit::window::CursorIcon::Move,
MouseCursor::NoDrop => winit::window::CursorIcon::NoDrop,
MouseCursor::NotAllowed => winit::window::CursorIcon::NotAllowed,
MouseCursor::Grab => winit::window::CursorIcon::Grab,
MouseCursor::Grabbing => winit::window::CursorIcon::Grabbing,
MouseCursor::ColResize => winit::window::CursorIcon::ColResize,
MouseCursor::RowResize => winit::window::CursorIcon::RowResize,
MouseCursor::NResize => winit::window::CursorIcon::NResize,
MouseCursor::EResize => winit::window::CursorIcon::EResize,
MouseCursor::SResize => winit::window::CursorIcon::SResize,
MouseCursor::WResize => winit::window::CursorIcon::WResize,
MouseCursor::NeResize => winit::window::CursorIcon::NeResize,
MouseCursor::NwResize => winit::window::CursorIcon::NwResize,
MouseCursor::SeResize => winit::window::CursorIcon::SeResize,
MouseCursor::SwResize => winit::window::CursorIcon::SwResize,
MouseCursor::EwResize => winit::window::CursorIcon::EwResize,
MouseCursor::NsResize => winit::window::CursorIcon::NsResize,
MouseCursor::NeswResize => winit::window::CursorIcon::NeswResize,
MouseCursor::NwseResize => winit::window::CursorIcon::NwseResize,
_ => winit::window::CursorIcon::Default,
let winit_cursor = match &cursor {
MouseCursor::Default => Some(winit::window::CursorIcon::Default),
MouseCursor::None => Some(winit::window::CursorIcon::Default),
MouseCursor::Help => Some(winit::window::CursorIcon::Help),
MouseCursor::Pointer => Some(winit::window::CursorIcon::Pointer),
MouseCursor::Progress => Some(winit::window::CursorIcon::Progress),
MouseCursor::Wait => Some(winit::window::CursorIcon::Wait),
MouseCursor::Crosshair => Some(winit::window::CursorIcon::Crosshair),
MouseCursor::Text => Some(winit::window::CursorIcon::Text),
MouseCursor::Alias => Some(winit::window::CursorIcon::Alias),
MouseCursor::Copy => Some(winit::window::CursorIcon::Copy),
MouseCursor::Move => Some(winit::window::CursorIcon::Move),
MouseCursor::NoDrop => Some(winit::window::CursorIcon::NoDrop),
MouseCursor::NotAllowed => Some(winit::window::CursorIcon::NotAllowed),
MouseCursor::Grab => Some(winit::window::CursorIcon::Grab),
MouseCursor::Grabbing => Some(winit::window::CursorIcon::Grabbing),
MouseCursor::ColResize => Some(winit::window::CursorIcon::ColResize),
MouseCursor::RowResize => Some(winit::window::CursorIcon::RowResize),
MouseCursor::NResize => Some(winit::window::CursorIcon::NResize),
MouseCursor::EResize => Some(winit::window::CursorIcon::EResize),
MouseCursor::SResize => Some(winit::window::CursorIcon::SResize),
MouseCursor::WResize => Some(winit::window::CursorIcon::WResize),
MouseCursor::NeResize => Some(winit::window::CursorIcon::NeResize),
MouseCursor::NwResize => Some(winit::window::CursorIcon::NwResize),
MouseCursor::SeResize => Some(winit::window::CursorIcon::SeResize),
MouseCursor::SwResize => Some(winit::window::CursorIcon::SwResize),
MouseCursor::EwResize => Some(winit::window::CursorIcon::EwResize),
MouseCursor::NsResize => Some(winit::window::CursorIcon::NsResize),
MouseCursor::NeswResize => Some(winit::window::CursorIcon::NeswResize),
MouseCursor::NwseResize => Some(winit::window::CursorIcon::NwseResize),
MouseCursor::CustomCursor { image, hotspot_x, hotspot_y } => {
if let Some(rgba8) = image.to_rgba8() {
let rgba_vec = rgba8.as_slice().to_vec();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why allocate a vector if we're going to allocate another vector in the next line?

let rgba = rgba_vec
.iter()
.map(|c| vec![c.r, c.g, c.b, c.a])
.flatten()
.collect::<Vec<u8>>();
let size = image.size();
let source = CustomCursor::from_rgba(
rgba,
size.width as u16,
size.height as u16,
*hotspot_x as u16,
*hotspot_y as u16,
);

// Custom cursors have to be set during the event loop
self.custom_cursor_source.set(source.ok());
}
None
}
_ => None,
};
if let Some(winit_window) = self.winit_window_or_none.borrow().as_window() {
winit_window.set_cursor_visible(cursor != MouseCursor::None);
winit_window.set_cursor(winit_cursor);

if let Some(cursor) = winit_cursor {
winit_window.set_cursor(cursor);
}
}
}

Expand Down
71 changes: 0 additions & 71 deletions internal/common/enums.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,77 +187,6 @@ 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.
#[non_exhaustive]
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.
#[non_exhaustive]
enum ImageFit {
Expand Down
Loading
Loading