Use cursor-icon crate for CursorIcon

This crate is aimed to simplify handling of cursor icon across
various crates and be used in the public API.
This commit is contained in:
Kirill Chibisov 2023-05-09 20:19:35 +03:00 committed by GitHub
parent 596c0edf0f
commit bd9cc2a9da
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 54 additions and 250 deletions

View file

@ -8,6 +8,9 @@ And please only add new entries to the top of this list, right below the `# Unre
# Unreleased
- **Breaking:** `CursorIcon` is now used from the `cursor-icon` crate.
- **Breaking:** `CursorIcon::Hand` is now named `CursorIcon::Pointer`.
- **Breaking:** `CursorIcon::Arrow` was removed.
- On macOS, fixed memory leak when getting monitor handle.
- On Wayland, fix maximized startup not taking full size on GNOME.
- On Wayland, fix initial window size not restored for maximized/fullscreened on startup window.

View file

@ -44,12 +44,14 @@ wayland-csd-adwaita-crossfont = ["sctk-adwaita", "sctk-adwaita/crossfont"]
wayland-csd-adwaita-notitle = ["sctk-adwaita"]
android-native-activity = ["android-activity/native-activity"]
android-game-activity = ["android-activity/game-activity"]
serde = ["dep:serde", "cursor-icon/serde"]
[build-dependencies]
cfg_aliases = "0.1.1"
[dependencies]
bitflags = "1"
cursor-icon = "1.0.0"
instant = { version = "0.1", features = ["wasm-bindgen"] }
log = "0.4"
mint = { version = "0.5.6", optional = true }

View file

@ -54,8 +54,7 @@ fn main() {
const CURSORS: &[CursorIcon] = &[
CursorIcon::Default,
CursorIcon::Crosshair,
CursorIcon::Hand,
CursorIcon::Arrow,
CursorIcon::Pointer,
CursorIcon::Move,
CursorIcon::Text,
CursorIcon::Wait,

View file

@ -547,63 +547,22 @@ impl WindowState {
return;
}
let cursors: &[&str] = match cursor_icon {
CursorIcon::Alias => &["link"],
CursorIcon::Arrow => &["arrow"],
CursorIcon::Cell => &["plus"],
CursorIcon::Copy => &["copy"],
CursorIcon::Crosshair => &["crosshair"],
CursorIcon::Default => &["left_ptr"],
CursorIcon::Hand => &["hand2", "hand1"],
CursorIcon::Help => &["question_arrow"],
CursorIcon::Move => &["move"],
CursorIcon::Grab => &["openhand", "grab"],
CursorIcon::Grabbing => &["closedhand", "grabbing"],
CursorIcon::Progress => &["progress"],
CursorIcon::AllScroll => &["all-scroll"],
CursorIcon::ContextMenu => &["context-menu"],
CursorIcon::NoDrop => &["no-drop", "circle"],
CursorIcon::NotAllowed => &["crossed_circle"],
// Resize cursors
CursorIcon::EResize => &["right_side"],
CursorIcon::NResize => &["top_side"],
CursorIcon::NeResize => &["top_right_corner"],
CursorIcon::NwResize => &["top_left_corner"],
CursorIcon::SResize => &["bottom_side"],
CursorIcon::SeResize => &["bottom_right_corner"],
CursorIcon::SwResize => &["bottom_left_corner"],
CursorIcon::WResize => &["left_side"],
CursorIcon::EwResize => &["h_double_arrow"],
CursorIcon::NsResize => &["v_double_arrow"],
CursorIcon::NwseResize => &["bd_double_arrow", "size_fdiag"],
CursorIcon::NeswResize => &["fd_double_arrow", "size_bdiag"],
CursorIcon::ColResize => &["split_h", "h_double_arrow"],
CursorIcon::RowResize => &["split_v", "v_double_arrow"],
CursorIcon::Text => &["text", "xterm"],
CursorIcon::VerticalText => &["vertical-text"],
CursorIcon::Wait => &["watch"],
CursorIcon::ZoomIn => &["zoom-in"],
CursorIcon::ZoomOut => &["zoom-out"],
};
self.apply_on_poiner(|pointer, data| {
let surface = data.cursor_surface();
let scale_factor = surface.data::<SurfaceData>().unwrap().scale_factor();
for cursor in cursors {
if pointer
.set_cursor(&self.connection, cursor, &self.shm, surface, scale_factor)
.is_ok()
{
return;
}
if pointer
.set_cursor(
&self.connection,
cursor_icon.name(),
&self.shm,
surface,
scale_factor,
)
.is_err()
{
warn!("Failed to set cursor to {:?}", cursor_icon);
}
warn!("Failed to set cursor to {:?}", cursor_icon);
})
}

View file

@ -1,3 +1,5 @@
use std::ffi::CString;
use crate::window::CursorIcon;
use super::*;
@ -45,78 +47,15 @@ impl XConnection {
}
}
fn load_cursor(&self, name: &[u8]) -> ffi::Cursor {
unsafe {
(self.xcursor.XcursorLibraryLoadCursor)(self.display, name.as_ptr() as *const c_char)
}
}
fn load_first_existing_cursor(&self, names: &[&[u8]]) -> ffi::Cursor {
for name in names.iter() {
let xcursor = self.load_cursor(name);
if xcursor != 0 {
return xcursor;
}
}
0
}
fn get_cursor(&self, cursor: Option<CursorIcon>) -> ffi::Cursor {
let cursor = match cursor {
Some(cursor) => cursor,
None => return self.create_empty_cursor(),
};
let load = |name: &[u8]| self.load_cursor(name);
let loadn = |names: &[&[u8]]| self.load_first_existing_cursor(names);
// Try multiple names in some cases where the name
// differs on the desktop environments or themes.
//
// Try the better looking (or more suiting) names first.
match cursor {
CursorIcon::Alias => load(b"link\0"),
CursorIcon::Arrow => load(b"arrow\0"),
CursorIcon::Cell => load(b"plus\0"),
CursorIcon::Copy => load(b"copy\0"),
CursorIcon::Crosshair => load(b"crosshair\0"),
CursorIcon::Default => load(b"left_ptr\0"),
CursorIcon::Hand => loadn(&[b"hand2\0", b"hand1\0"]),
CursorIcon::Help => load(b"question_arrow\0"),
CursorIcon::Move => load(b"move\0"),
CursorIcon::Grab => loadn(&[b"openhand\0", b"grab\0"]),
CursorIcon::Grabbing => loadn(&[b"closedhand\0", b"grabbing\0"]),
CursorIcon::Progress => load(b"left_ptr_watch\0"),
CursorIcon::AllScroll => load(b"all-scroll\0"),
CursorIcon::ContextMenu => load(b"context-menu\0"),
CursorIcon::NoDrop => loadn(&[b"no-drop\0", b"circle\0"]),
CursorIcon::NotAllowed => load(b"crossed_circle\0"),
// Resize cursors
CursorIcon::EResize => load(b"right_side\0"),
CursorIcon::NResize => load(b"top_side\0"),
CursorIcon::NeResize => load(b"top_right_corner\0"),
CursorIcon::NwResize => load(b"top_left_corner\0"),
CursorIcon::SResize => load(b"bottom_side\0"),
CursorIcon::SeResize => load(b"bottom_right_corner\0"),
CursorIcon::SwResize => load(b"bottom_left_corner\0"),
CursorIcon::WResize => load(b"left_side\0"),
CursorIcon::EwResize => load(b"h_double_arrow\0"),
CursorIcon::NsResize => load(b"v_double_arrow\0"),
CursorIcon::NwseResize => loadn(&[b"bd_double_arrow\0", b"size_fdiag\0"]),
CursorIcon::NeswResize => loadn(&[b"fd_double_arrow\0", b"size_bdiag\0"]),
CursorIcon::ColResize => loadn(&[b"split_h\0", b"h_double_arrow\0"]),
CursorIcon::RowResize => loadn(&[b"split_v\0", b"v_double_arrow\0"]),
CursorIcon::Text => loadn(&[b"text\0", b"xterm\0"]),
CursorIcon::VerticalText => load(b"vertical-text\0"),
CursorIcon::Wait => load(b"watch\0"),
CursorIcon::ZoomIn => load(b"zoom-in\0"),
CursorIcon::ZoomOut => load(b"zoom-out\0"),
let name = CString::new(cursor.name()).unwrap();
unsafe {
(self.xcursor.XcursorLibraryLoadCursor)(self.display, name.as_ptr() as *const c_char)
}
}

View file

@ -197,8 +197,7 @@ impl NSCursor {
pub fn from_icon(icon: CursorIcon) -> Id<Self, Shared> {
match icon {
CursorIcon::Default => Default::default(),
CursorIcon::Arrow => Self::arrowCursor(),
CursorIcon::Hand => Self::pointingHandCursor(),
CursorIcon::Pointer => Self::pointingHandCursor(),
CursorIcon::Grab => Self::openHandCursor(),
CursorIcon::Grabbing => Self::closedHandCursor(),
CursorIcon::Text => Self::IBeamCursor(),
@ -228,6 +227,7 @@ impl NSCursor {
CursorIcon::Wait | CursorIcon::Progress => Self::busyButClickableCursor(),
CursorIcon::Move | CursorIcon::AllScroll => Self::moveCursor(),
CursorIcon::Cell => Self::cellCursor(),
_ => Default::default(),
}
}
}

View file

@ -192,47 +192,8 @@ impl Window {
#[inline]
pub fn set_cursor_icon(&self, cursor: CursorIcon) {
let text = match cursor {
CursorIcon::Default => "auto",
CursorIcon::Crosshair => "crosshair",
CursorIcon::Hand => "pointer",
CursorIcon::Arrow => "default",
CursorIcon::Move => "move",
CursorIcon::Text => "text",
CursorIcon::Wait => "wait",
CursorIcon::Help => "help",
CursorIcon::Progress => "progress",
CursorIcon::NotAllowed => "not-allowed",
CursorIcon::ContextMenu => "context-menu",
CursorIcon::Cell => "cell",
CursorIcon::VerticalText => "vertical-text",
CursorIcon::Alias => "alias",
CursorIcon::Copy => "copy",
CursorIcon::NoDrop => "no-drop",
CursorIcon::Grab => "grab",
CursorIcon::Grabbing => "grabbing",
CursorIcon::AllScroll => "all-scroll",
CursorIcon::ZoomIn => "zoom-in",
CursorIcon::ZoomOut => "zoom-out",
CursorIcon::EResize => "e-resize",
CursorIcon::NResize => "n-resize",
CursorIcon::NeResize => "ne-resize",
CursorIcon::NwResize => "nw-resize",
CursorIcon::SResize => "s-resize",
CursorIcon::SeResize => "se-resize",
CursorIcon::SwResize => "sw-resize",
CursorIcon::WResize => "w-resize",
CursorIcon::EwResize => "ew-resize",
CursorIcon::NsResize => "ns-resize",
CursorIcon::NeswResize => "nesw-resize",
CursorIcon::NwseResize => "nwse-resize",
CursorIcon::ColResize => "col-resize",
CursorIcon::RowResize => "row-resize",
};
*self.previous_pointer.borrow_mut() = text;
backend::set_canvas_style_property(self.canvas.borrow().raw(), "cursor", text);
*self.previous_pointer.borrow_mut() = cursor.name();
backend::set_canvas_style_property(self.canvas.borrow().raw(), "cursor", cursor.name());
}
#[inline]

View file

@ -2026,7 +2026,7 @@ unsafe fn public_window_callback_inner<T: 'static>(
match set_cursor_to {
Some(cursor) => {
let cursor = LoadCursorW(0, cursor.to_windows_cursor());
let cursor = LoadCursorW(0, util::to_windows_cursor(cursor));
SetCursor(cursor);
0
}

View file

@ -164,32 +164,30 @@ pub fn get_instance_handle() -> HINSTANCE {
unsafe { &__ImageBase as *const _ as _ }
}
impl CursorIcon {
pub(crate) fn to_windows_cursor(self) -> PCWSTR {
match self {
CursorIcon::Arrow | CursorIcon::Default => IDC_ARROW,
CursorIcon::Hand => IDC_HAND,
CursorIcon::Crosshair => IDC_CROSS,
CursorIcon::Text | CursorIcon::VerticalText => IDC_IBEAM,
CursorIcon::NotAllowed | CursorIcon::NoDrop => IDC_NO,
CursorIcon::Grab | CursorIcon::Grabbing | CursorIcon::Move | CursorIcon::AllScroll => {
IDC_SIZEALL
}
CursorIcon::EResize
| CursorIcon::WResize
| CursorIcon::EwResize
| CursorIcon::ColResize => IDC_SIZEWE,
CursorIcon::NResize
| CursorIcon::SResize
| CursorIcon::NsResize
| CursorIcon::RowResize => IDC_SIZENS,
CursorIcon::NeResize | CursorIcon::SwResize | CursorIcon::NeswResize => IDC_SIZENESW,
CursorIcon::NwResize | CursorIcon::SeResize | CursorIcon::NwseResize => IDC_SIZENWSE,
CursorIcon::Wait => IDC_WAIT,
CursorIcon::Progress => IDC_APPSTARTING,
CursorIcon::Help => IDC_HELP,
_ => IDC_ARROW, // use arrow for the missing cases.
pub(crate) fn to_windows_cursor(cursor: CursorIcon) -> PCWSTR {
match cursor {
CursorIcon::Default => IDC_ARROW,
CursorIcon::Pointer => IDC_HAND,
CursorIcon::Crosshair => IDC_CROSS,
CursorIcon::Text | CursorIcon::VerticalText => IDC_IBEAM,
CursorIcon::NotAllowed | CursorIcon::NoDrop => IDC_NO,
CursorIcon::Grab | CursorIcon::Grabbing | CursorIcon::Move | CursorIcon::AllScroll => {
IDC_SIZEALL
}
CursorIcon::EResize
| CursorIcon::WResize
| CursorIcon::EwResize
| CursorIcon::ColResize => IDC_SIZEWE,
CursorIcon::NResize
| CursorIcon::SResize
| CursorIcon::NsResize
| CursorIcon::RowResize => IDC_SIZENS,
CursorIcon::NeResize | CursorIcon::SwResize | CursorIcon::NeswResize => IDC_SIZENESW,
CursorIcon::NwResize | CursorIcon::SeResize | CursorIcon::NwseResize => IDC_SIZENWSE,
CursorIcon::Wait => IDC_WAIT,
CursorIcon::Progress => IDC_APPSTARTING,
CursorIcon::Help => IDC_HELP,
_ => IDC_ARROW, // use arrow for the missing cases.
}
}

View file

@ -333,7 +333,7 @@ impl Window {
pub fn set_cursor_icon(&self, cursor: CursorIcon) {
self.window_state_lock().mouse.cursor = cursor;
self.thread_executor.execute_in_thread(move || unsafe {
let cursor = LoadCursorW(0, cursor.to_windows_cursor());
let cursor = LoadCursorW(0, util::to_windows_cursor(cursor));
SetCursor(cursor);
});
}

View file

@ -15,6 +15,9 @@ use crate::{
pub use crate::icon::{BadIcon, Icon};
#[doc(inline)]
pub use cursor_icon::{CursorIcon, ParseError as CursorIconParseError};
/// Represents a window.
///
/// # Example
@ -1388,66 +1391,6 @@ pub enum CursorGrabMode {
Locked,
}
/// Describes the appearance of the mouse cursor.
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum CursorIcon {
/// The platform-dependent default cursor.
#[default]
Default,
/// A simple crosshair.
Crosshair,
/// A hand (often used to indicate links in web browsers).
Hand,
/// Self explanatory.
Arrow,
/// Indicates something is to be moved.
Move,
/// Indicates text that may be selected or edited.
Text,
/// Program busy indicator.
Wait,
/// Help indicator (often rendered as a "?")
Help,
/// Progress indicator. Shows that processing is being done. But in contrast
/// with "Wait" the user may still interact with the program. Often rendered
/// as a spinning beach ball, or an arrow with a watch or hourglass.
Progress,
/// Cursor showing that something cannot be done.
NotAllowed,
ContextMenu,
Cell,
VerticalText,
Alias,
Copy,
NoDrop,
/// Indicates something can be grabbed.
Grab,
/// Indicates something is grabbed.
Grabbing,
AllScroll,
ZoomIn,
ZoomOut,
/// Indicate that some edge is to be moved. For example, the 'SeResize' cursor
/// is used when the movement starts from the south-east corner of the box.
EResize,
NResize,
NeResize,
NwResize,
SResize,
SeResize,
SwResize,
WResize,
EwResize,
NsResize,
NeswResize,
NwseResize,
ColResize,
RowResize,
}
/// Defines the orientation that a window resize will be performed.
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum ResizeDirection {