Skip to content

Commit

Permalink
feat: expose window cursor APIs, closes #3888 #3890 (#3935)
Browse files Browse the repository at this point in the history
  • Loading branch information
lucasfernog committed Apr 21, 2022
1 parent 76950e9 commit c54ddfe
Show file tree
Hide file tree
Showing 19 changed files with 743 additions and 128 deletions.
5 changes: 5 additions & 0 deletions .changes/api-window-cursor.md
@@ -0,0 +1,5 @@
---
"api": patch
---

Added the `setCursorGrab`, `setCursorVisible`, `setCursorIcon` and `setCursorPosition` methods to the `WebviewWindow` class.
7 changes: 7 additions & 0 deletions .changes/cursor-apis.md
@@ -0,0 +1,7 @@
---
"tauri": patch
"tauri-runtime": patch
"tauri-runtime-wry": patch
---

Expose Window cursor APIs `set_cursor_grab`, `set_cursor_visible`, `set_cursor_icon` and `set_cursor_position`.
110 changes: 105 additions & 5 deletions core/tauri-runtime-wry/src/lib.rs
Expand Up @@ -14,7 +14,7 @@ use tauri_runtime::{
webview::{WebviewIpcHandler, WindowBuilder, WindowBuilderBase},
window::{
dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size},
DetachedWindow, FileDropEvent, JsEventListenerKey, PendingWindow, WindowEvent,
CursorIcon, DetachedWindow, FileDropEvent, JsEventListenerKey, PendingWindow, WindowEvent,
},
ClipboardManager, Dispatch, Error, EventLoopProxy, ExitRequestedEventAction,
GlobalShortcutManager, Result, RunEvent, RunIteration, Runtime, RuntimeHandle, UserAttentionType,
Expand Down Expand Up @@ -63,7 +63,7 @@ use wry::{
},
monitor::MonitorHandle,
window::{
Fullscreen, Icon as WryWindowIcon, Theme as WryTheme,
CursorIcon as WryCursorIcon, Fullscreen, Icon as WryWindowIcon, Theme as WryTheme,
UserAttentionType as WryUserAttentionType,
},
},
Expand Down Expand Up @@ -752,7 +752,7 @@ impl From<Position> for PositionWrapper {
pub struct UserAttentionTypeWrapper(WryUserAttentionType);

impl From<UserAttentionType> for UserAttentionTypeWrapper {
fn from(request_type: UserAttentionType) -> UserAttentionTypeWrapper {
fn from(request_type: UserAttentionType) -> Self {
let o = match request_type {
UserAttentionType::Critical => WryUserAttentionType::Critical,
UserAttentionType::Informational => WryUserAttentionType::Informational,
Expand All @@ -761,6 +761,54 @@ impl From<UserAttentionType> for UserAttentionTypeWrapper {
}
}

#[derive(Debug)]
pub struct CursorIconWrapper(WryCursorIcon);

impl From<CursorIcon> for CursorIconWrapper {
fn from(icon: CursorIcon) -> Self {
use CursorIcon::*;
let i = match icon {
Default => WryCursorIcon::Default,
Crosshair => WryCursorIcon::Crosshair,
Hand => WryCursorIcon::Hand,
Arrow => WryCursorIcon::Arrow,
Move => WryCursorIcon::Move,
Text => WryCursorIcon::Text,
Wait => WryCursorIcon::Wait,
Help => WryCursorIcon::Help,
Progress => WryCursorIcon::Progress,
NotAllowed => WryCursorIcon::NotAllowed,
ContextMenu => WryCursorIcon::ContextMenu,
Cell => WryCursorIcon::Cell,
VerticalText => WryCursorIcon::VerticalText,
Alias => WryCursorIcon::Alias,
Copy => WryCursorIcon::Copy,
NoDrop => WryCursorIcon::NoDrop,
Grab => WryCursorIcon::Grab,
Grabbing => WryCursorIcon::Grabbing,
AllScroll => WryCursorIcon::AllScroll,
ZoomIn => WryCursorIcon::ZoomIn,
ZoomOut => WryCursorIcon::ZoomOut,
EResize => WryCursorIcon::EResize,
NResize => WryCursorIcon::NResize,
NeResize => WryCursorIcon::NeResize,
NwResize => WryCursorIcon::NwResize,
SResize => WryCursorIcon::SResize,
SeResize => WryCursorIcon::SeResize,
SwResize => WryCursorIcon::SwResize,
WResize => WryCursorIcon::WResize,
EwResize => WryCursorIcon::EwResize,
NsResize => WryCursorIcon::NsResize,
NeswResize => WryCursorIcon::NeswResize,
NwseResize => WryCursorIcon::NwseResize,
ColResize => WryCursorIcon::ColResize,
RowResize => WryCursorIcon::RowResize,
_ => WryCursorIcon::Default,
};
Self(i)
}
}

#[derive(Debug, Clone, Default)]
pub struct WindowBuilderWrapper {
inner: WryWindowBuilder,
Expand Down Expand Up @@ -1079,6 +1127,10 @@ pub enum WindowMessage {
SetFocus,
SetIcon(WryWindowIcon),
SetSkipTaskbar(bool),
SetCursorGrab(bool),
SetCursorVisible(bool),
SetCursorIcon(CursorIcon),
SetCursorPosition(Position),
DragWindow,
UpdateMenuItem(u16, MenuUpdate),
RequestRedraw,
Expand Down Expand Up @@ -1505,6 +1557,37 @@ impl<T: UserEvent> Dispatch<T> for WryDispatcher<T> {
)
}

fn set_cursor_grab(&self, grab: bool) -> crate::Result<()> {
send_user_message(
&self.context,
Message::Window(self.window_id, WindowMessage::SetCursorGrab(grab)),
)
}

fn set_cursor_visible(&self, visible: bool) -> crate::Result<()> {
send_user_message(
&self.context,
Message::Window(self.window_id, WindowMessage::SetCursorVisible(visible)),
)
}

fn set_cursor_icon(&self, icon: CursorIcon) -> crate::Result<()> {
send_user_message(
&self.context,
Message::Window(self.window_id, WindowMessage::SetCursorIcon(icon)),
)
}

fn set_cursor_position<Pos: Into<Position>>(&self, position: Pos) -> crate::Result<()> {
send_user_message(
&self.context,
Message::Window(
self.window_id,
WindowMessage::SetCursorPosition(position.into()),
),
)
}

fn start_dragging(&self) -> Result<()> {
send_user_message(
&self.context,
Expand Down Expand Up @@ -2176,9 +2259,26 @@ fn handle_user_message<T: UserEvent>(
WindowMessage::SetIcon(icon) => {
window.set_window_icon(Some(icon));
}
WindowMessage::SetSkipTaskbar(_skip) => {
#[allow(unused_variables)]
WindowMessage::SetSkipTaskbar(skip) => {
#[cfg(any(windows, target_os = "linux"))]
window.set_skip_taskbar(_skip);
window.set_skip_taskbar(skip);
}
#[allow(unused_variables)]
WindowMessage::SetCursorGrab(grab) => {
#[cfg(any(windows, target_os = "macos"))]
let _ = window.set_cursor_grab(grab);
}
WindowMessage::SetCursorVisible(visible) => {
window.set_cursor_visible(visible);
}
WindowMessage::SetCursorIcon(icon) => {
window.set_cursor_icon(CursorIconWrapper::from(icon).0);
}
#[allow(unused_variables)]
WindowMessage::SetCursorPosition(position) => {
#[cfg(any(windows, target_os = "macos"))]
let _ = window.set_cursor_position(PositionWrapper::from(position).0);
}
WindowMessage::DragWindow => {
let _ = window.drag_window();
Expand Down
19 changes: 18 additions & 1 deletion core/tauri-runtime/src/lib.rs
Expand Up @@ -26,7 +26,7 @@ use monitor::Monitor;
use webview::WindowBuilder;
use window::{
dpi::{PhysicalPosition, PhysicalSize, Position, Size},
DetachedWindow, PendingWindow, WindowEvent,
CursorIcon, DetachedWindow, PendingWindow, WindowEvent,
};

use crate::http::{
Expand Down Expand Up @@ -565,6 +565,23 @@ pub trait Dispatch<T: UserEvent>: Debug + Clone + Send + Sync + Sized + 'static
/// Whether to show the window icon in the task bar or not.
fn set_skip_taskbar(&self, skip: bool) -> Result<()>;

/// Grabs the cursor, preventing it from leaving the window.
///
/// There's no guarantee that the cursor will be hidden. You should
/// hide it by yourself if you want so.
fn set_cursor_grab(&self, grab: bool) -> Result<()>;

/// Modifies the cursor's visibility.
///
/// If `false`, this will hide the cursor. If `true`, this will show the cursor.
fn set_cursor_visible(&self, visible: bool) -> Result<()>;

// Modifies the cursor icon of the window.
fn set_cursor_icon(&self, icon: CursorIcon) -> Result<()>;

/// Changes the position of the cursor in window coordinates.
fn set_cursor_position<Pos: Into<Position>>(&self, position: Pos) -> Result<()>;

/// Starts dragging the window.
fn start_dragging(&self) -> Result<()>;

Expand Down
114 changes: 113 additions & 1 deletion core/tauri-runtime/src/window.rs
Expand Up @@ -10,7 +10,7 @@ use crate::{
webview::{WebviewAttributes, WebviewIpcHandler},
Dispatch, Runtime, UserEvent, WindowBuilder,
};
use serde::Serialize;
use serde::{Deserialize, Deserializer, Serialize};
use tauri_utils::{config::WindowConfig, Theme};

use std::{
Expand Down Expand Up @@ -98,6 +98,118 @@ fn get_menu_ids(map: &mut HashMap<MenuHash, MenuId>, menu: &Menu) {
}
}

/// Describes the appearance of the mouse cursor.
#[non_exhaustive]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum CursorIcon {
/// The platform-dependent default cursor.
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,
}

impl<'de> Deserialize<'de> for CursorIcon {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
Ok(match s.to_lowercase().as_str() {
"default" => CursorIcon::Default,
"crosshair" => CursorIcon::Crosshair,
"hand" => CursorIcon::Hand,
"arrow" => CursorIcon::Arrow,
"move" => CursorIcon::Move,
"text" => CursorIcon::Text,
"wait" => CursorIcon::Wait,
"help" => CursorIcon::Help,
"progress" => CursorIcon::Progress,
"notallowed" => CursorIcon::NotAllowed,
"contextmenu" => CursorIcon::ContextMenu,
"cell" => CursorIcon::Cell,
"verticaltext" => CursorIcon::VerticalText,
"alias" => CursorIcon::Alias,
"copy" => CursorIcon::Copy,
"nodrop" => CursorIcon::NoDrop,
"grab" => CursorIcon::Grab,
"grabbing" => CursorIcon::Grabbing,
"allscroll" => CursorIcon::AllScroll,
"zoomun" => CursorIcon::ZoomIn,
"zoomout" => CursorIcon::ZoomOut,
"eresize" => CursorIcon::EResize,
"nresize" => CursorIcon::NResize,
"neresize" => CursorIcon::NeResize,
"nwresize" => CursorIcon::NwResize,
"sresize" => CursorIcon::SResize,
"seresize" => CursorIcon::SeResize,
"swresize" => CursorIcon::SwResize,
"wresize" => CursorIcon::WResize,
"ewresize" => CursorIcon::EwResize,
"nsresize" => CursorIcon::NsResize,
"neswresize" => CursorIcon::NeswResize,
"nwseresize" => CursorIcon::NwseResize,
"colresize" => CursorIcon::ColResize,
"rowresize" => CursorIcon::RowResize,
_ => CursorIcon::Default,
})
}
}

impl Default for CursorIcon {
fn default() -> Self {
CursorIcon::Default
}
}

/// A webview window that has yet to be built.
pub struct PendingWindow<T: UserEvent, R: Runtime<T>> {
/// The label that the window will be named.
Expand Down
8 changes: 8 additions & 0 deletions core/tauri/Cargo.toml
Expand Up @@ -230,6 +230,10 @@ window-all = [
"window-set-focus",
"window-set-icon",
"window-set-skip-taskbar",
"window-set-cursor-grab",
"window-set-cursor-visible",
"window-set-cursor-icon",
"window-set-cursor-position",
"window-start-dragging",
"window-print"
]
Expand All @@ -255,6 +259,10 @@ window-set-fullscreen = [ ]
window-set-focus = [ ]
window-set-icon = [ ]
window-set-skip-taskbar = [ ]
window-set-cursor-grab = [ ]
window-set-cursor-visible = [ ]
window-set-cursor-icon = [ ]
window-set-cursor-position = [ ]
window-start-dragging = [ ]
window-print = [ ]
config-json5 = [ "tauri-macros/config-json5" ]
Expand Down
6 changes: 5 additions & 1 deletion core/tauri/build.rs
Expand Up @@ -29,7 +29,7 @@ fn main() {
window_create: { any(window_all, feature = "window-create") },
window_center: { any(window_all, feature = "window-center") },
window_request_user_attention: { any(window_all, feature = "window-request-user-attention") },
window_set_izable: { any(window_all, feature = "window-set-resizable") },
window_set_resizable: { any(window_all, feature = "window-set-resizable") },
window_set_title: { any(window_all, feature = "window-set-title") },
window_maximize: { any(window_all, feature = "window-maximize") },
window_unmaximize: { any(window_all, feature = "window-unmaximize") },
Expand All @@ -48,6 +48,10 @@ fn main() {
window_set_focus: { any(window_all, feature = "window-set-focus") },
window_set_icon: { any(window_all, feature = "window-set-icon") },
window_set_skip_taskbar: { any(window_all, feature = "window-set-skip-taskbar") },
window_set_cursor_grab: { any(window_all, feature = "window-set-cursor-grab") },
window_set_cursor_visible: { any(window_all, feature = "window-set-cursor-visible") },
window_set_cursor_icon: { any(window_all, feature = "window-set-cursor-icon") },
window_set_cursor_position: { any(window_all, feature = "window-set-cursor-position") },
window_start_dragging: { any(window_all, feature = "window-start-dragging") },
window_print: { any(window_all, feature = "window-print") },

Expand Down
2 changes: 1 addition & 1 deletion core/tauri/scripts/bundle.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion core/tauri/src/app.rs
Expand Up @@ -776,7 +776,7 @@ impl<R: Runtime> Builder<R> {
///
/// ## Platform-specific
///
/// - **macOS**: on macOS the application *must* be executed on the main thread, so this function is not exposed.
/// - **macOS:** on macOS the application *must* be executed on the main thread, so this function is not exposed.
#[cfg(any(windows, target_os = "linux"))]
#[cfg_attr(doc_cfg, doc(cfg(any(windows, target_os = "linux"))))]
#[must_use]
Expand Down

0 comments on commit c54ddfe

Please sign in to comment.