Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Custom Cursor #6923

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
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
38 changes: 36 additions & 2 deletions internal/backends/winit/event_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ use std::rc::{Rc, Weak};
use winit::event::WindowEvent;
use winit::event_loop::ActiveEventLoop;
use winit::event_loop::ControlFlow;
use winit::window::ResizeDirection;
use winit::window::{CustomCursor, ResizeDirection};
pub(crate) struct NotRunningEventLoop {
#[cfg(not(target_arch = "wasm32"))]
pub(crate) clipboard: Rc<std::cell::RefCell<crate::clipboard::ClipboardPair>>,
Expand Down Expand Up @@ -320,6 +320,8 @@ impl winit::application::ApplicationHandler<SlintUserEvent> for EventLoopState {
}

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

match event {
WindowEvent::RedrawRequested => {
self.loop_error = window.draw().err();
Expand Down Expand Up @@ -348,7 +350,6 @@ impl winit::application::ApplicationHandler<SlintUserEvent> for EventLoopState {
);
}
}

WindowEvent::KeyboardInput { event, is_synthetic, .. } => {
let key_code = event.logical_key;
// For now: Match Qt's behavior of mapping command to control and control to meta (LWin/RWin).
Expand Down Expand Up @@ -720,6 +721,39 @@ impl EventLoopState {
}
}

pub fn maybe_set_custom_cursor(
&self,
runtime_window: &WindowInner,
window: &WinitWindowAdapter,
event_loop: &ActiveEventLoop,
) {
let window_item = runtime_window.window_item();
let window_ref = window_item.as_ref().unwrap().as_pin_ref();
let cursor = window_ref.cursor();
runtime_window.set_has_custom_cursor(false);

// TODO: Only change the cursor if the cursor value becomes dirty
// `window_ref.cursor.is_dirty()` doesn't seem to work.
if cursor.size().width > 0 && cursor.size().height > 0 {
let hotspot_x = window_ref.cursor_hotspot_x().get();
let hotspot_y = window_ref.cursor_hotspot_y().get();
let rgba_vec = cursor.to_rgba8().unwrap().make_mut_slice().to_vec();
let rgba =
rgba_vec.iter().map(|c| vec![c.r, c.g, c.b, c.a]).flatten().collect::<Vec<u8>>();
let size = cursor.size();
let source = CustomCursor::from_rgba(
rgba,
size.width as u16,
size.height as u16,
hotspot_x as u16,
hotspot_y as u16,
);
let custom_cursor = event_loop.create_custom_cursor(source.unwrap());
window.winit_window().unwrap().set_cursor(custom_cursor.clone());
runtime_window.set_has_custom_cursor(true);
}
}

/// Runs the event loop and renders the items in the provided `component` in its
/// own window.
#[cfg(not(target_arch = "wasm32"))]
Expand Down
5 changes: 5 additions & 0 deletions internal/backends/winit/winitwindowadapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -939,6 +939,11 @@ impl WindowAdapter for WinitWindowAdapter {

impl WindowAdapterInternal for WinitWindowAdapter {
fn set_mouse_cursor(&self, cursor: MouseCursor) {
let runtime_window = WindowInner::from_pub(self.window());
if runtime_window.has_custom_cursor() {
return;
}

let winit_cursor = match cursor {
MouseCursor::Default => winit::window::CursorIcon::Default,
MouseCursor::None => winit::window::CursorIcon::Default,
Expand Down
3 changes: 3 additions & 0 deletions internal/compiler/builtins.slint
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,9 @@ component WindowItem {
in-out property <length> default-font-size; // <=> StyleMetrics.default-font-size set in apply_default_properties_from_style
in property <int> default-font-weight;
in property <image> icon;
in property <image> cursor;
in property <length> cursor-hotspot-x;
in property <length> cursor-hotspot-y;
}

export component Window inherits WindowItem {
Expand Down
3 changes: 3 additions & 0 deletions internal/core/items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -960,6 +960,9 @@ pub struct WindowItem {
pub default_font_size: Property<LogicalLength>,
pub default_font_weight: Property<i32>,
pub cached_rendering_data: CachedRenderingData,
pub cursor: Property<crate::graphics::Image>,
pub cursor_hotspot_x: Property<LogicalLength>,
pub cursor_hotspot_y: Property<LogicalLength>,
}

impl Item for WindowItem {
Expand Down
13 changes: 13 additions & 0 deletions internal/core/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,8 @@ pub struct WindowInner {
maximized: Cell<bool>,
minimized: Cell<bool>,

has_custom_cursor: Cell<bool>,

/// Stack of currently active popups
active_popups: RefCell<Vec<PopupWindow>>,
next_popup_id: Cell<NonZeroU32>,
Expand Down Expand Up @@ -503,6 +505,7 @@ impl WindowInner {
close_requested: Default::default(),
click_state: ClickState::default(),
prevent_focus_change: Default::default(),
has_custom_cursor: Cell::new(false),
// The ctx is lazy so that a Window can be initialized before the backend.
// (for example in test_empty_window)
ctx: once_cell::unsync::Lazy::new(|| {
Expand Down Expand Up @@ -1263,6 +1266,16 @@ impl WindowInner {
pub fn from_pub(window: &crate::api::Window) -> &Self {
&window.0
}

/// Sets whether the window has a custom cursor set.
pub fn set_has_custom_cursor(&self, is_custom_cursor: bool) {
self.has_custom_cursor.set(is_custom_cursor);
}

/// Returns whether the window has a custom cursor set.
pub fn has_custom_cursor(&self) -> bool {
self.has_custom_cursor.get()
}
}

/// Internal alias for `Rc<dyn WindowAdapter>`.
Expand Down
Loading