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

feat: add cursor_position getter #9297

Merged
merged 11 commits into from Apr 29, 2024
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changes/cursor_position.md
@@ -0,0 +1,5 @@
---
'tauri': 'patch:feat'
---

Add `App/AppHandle/Window/Webview/WebviewWindow::cursor_position` getter to get the current cursor position.
5 changes: 5 additions & 0 deletions .changes/cursor_position_js.md
@@ -0,0 +1,5 @@
---
'@tauri-apps/api': 'patch:feat'
---

Add `cursorPosition` function in `window` module to get the current cursor position.
22 changes: 22 additions & 0 deletions core/tauri-runtime-wry/src/lib.rs
Expand Up @@ -2147,6 +2147,17 @@ impl<T: UserEvent> RuntimeHandle<T> for WryHandle<T> {
.collect()
}

fn cursor_position(&self) -> Result<PhysicalPosition<f64>> {
self
.context
.main_thread
.window_target
.cursor_position()
.map(PhysicalPositionWrapper)
.map(Into::into)
.map_err(|_| Error::FailedToGetCursorPosition)
}

#[cfg(target_os = "macos")]
fn show(&self) -> tauri_runtime::Result<()> {
send_user_message(
Expand Down Expand Up @@ -2394,6 +2405,17 @@ impl<T: UserEvent> Runtime<T> for Wry<T> {
.collect()
}

fn cursor_position(&self) -> Result<PhysicalPosition<f64>> {
self
.context
.main_thread
.window_target
.cursor_position()
.map(PhysicalPositionWrapper)
.map(Into::into)
.map_err(|_| Error::FailedToGetCursorPosition)
}

#[cfg(target_os = "macos")]
fn set_activation_policy(&mut self, activation_policy: ActivationPolicy) {
self
Expand Down
7 changes: 7 additions & 0 deletions core/tauri-runtime/src/lib.rs
Expand Up @@ -158,6 +158,9 @@ pub enum Error {
/// Failed to get monitor on window operation.
#[error("failed to get monitor")]
FailedToGetMonitor,
/// Failed to get cursor position.
#[error("failed to get cursor position")]
FailedToGetCursorPosition,
#[error("Invalid header name: {0}")]
InvalidHeaderName(#[from] InvalidHeaderName),
#[error("Invalid header value: {0}")]
Expand Down Expand Up @@ -293,6 +296,8 @@ pub trait RuntimeHandle<T: UserEvent>: Debug + Clone + Send + Sync + Sized + 'st
fn primary_monitor(&self) -> Option<Monitor>;
fn available_monitors(&self) -> Vec<Monitor>;

fn cursor_position(&self) -> Result<PhysicalPosition<f64>>;

/// Shows the application, but does not automatically focus it.
#[cfg(target_os = "macos")]
#[cfg_attr(docsrs, doc(cfg(target_os = "macos")))]
Expand Down Expand Up @@ -373,6 +378,8 @@ pub trait Runtime<T: UserEvent>: Debug + Sized + 'static {
fn primary_monitor(&self) -> Option<Monitor>;
fn available_monitors(&self) -> Vec<Monitor>;

fn cursor_position(&self) -> Result<PhysicalPosition<f64>>;

/// Sets the activation policy for the application.
#[cfg(target_os = "macos")]
#[cfg_attr(docsrs, doc(cfg(target_os = "macos")))]
Expand Down
1 change: 1 addition & 0 deletions core/tauri/build.rs
Expand Up @@ -64,6 +64,7 @@ const PLUGINS: &[(&str, &[(&str, bool)])] = &[
("current_monitor", true),
("primary_monitor", true),
("available_monitors", true),
("cursor_position", true),
("theme", true),
// setters
("center", false),
Expand Down
2 changes: 2 additions & 0 deletions core/tauri/permissions/window/autogenerated/reference.md
Expand Up @@ -10,6 +10,8 @@
|`deny-create`|Denies the create command without any pre-configured scope.|
|`allow-current-monitor`|Enables the current_monitor command without any pre-configured scope.|
|`deny-current-monitor`|Denies the current_monitor command without any pre-configured scope.|
|`allow-cursor-position`|Enables the cursor_position command without any pre-configured scope.|
|`deny-cursor-position`|Denies the cursor_position command without any pre-configured scope.|
|`allow-destroy`|Enables the destroy command without any pre-configured scope.|
|`deny-destroy`|Denies the destroy command without any pre-configured scope.|
|`allow-hide`|Enables the hide command without any pre-configured scope.|
Expand Down
2 changes: 1 addition & 1 deletion core/tauri/scripts/bundle.global.js

Large diffs are not rendered by default.

10 changes: 10 additions & 0 deletions core/tauri/src/app.rs
Expand Up @@ -585,6 +585,16 @@ macro_rules! shared_app_impl {
_ => unreachable!(),
})
}

/// Get the cursor position relative to the top-left hand corner of the main monitor.
pub fn cursor_position(&self) -> crate::Result<PhysicalPosition<f64>> {
Ok(match self.runtime() {
RuntimeOrDispatch::Runtime(h) => h.cursor_position()?,
RuntimeOrDispatch::RuntimeHandle(h) => h.cursor_position()?,
_ => unreachable!(),
})
}

/// Returns the default window icon.
pub fn default_window_icon(&self) -> Option<&Image<'_>> {
self.manager.window.default_icon.as_ref()
Expand Down
8 changes: 8 additions & 0 deletions core/tauri/src/test/mock_runtime.rs
Expand Up @@ -268,6 +268,10 @@ impl<T: UserEvent> RuntimeHandle<T> for MockRuntimeHandle {
{
todo!()
}

fn cursor_position(&self) -> Result<PhysicalPosition<f64>> {
Ok(PhysicalPosition::new(0.0, 0.0))
}
}

#[derive(Debug, Clone)]
Expand Down Expand Up @@ -1148,4 +1152,8 @@ impl<T: UserEvent> Runtime<T> for MockRuntime {

callback(RunEvent::Exit);
}

fn cursor_position(&self) -> Result<PhysicalPosition<f64>> {
Ok(PhysicalPosition::new(0.0, 0.0))
}
}
5 changes: 5 additions & 0 deletions core/tauri/src/webview/mod.rs
Expand Up @@ -877,6 +877,11 @@ impl<R: Runtime> Webview<R> {
self.webview.dispatcher.print().map_err(Into::into)
}

/// Get the cursor position relative to the top-left hand corner of the main monitor.
pub fn cursor_position(&self) -> crate::Result<PhysicalPosition<f64>> {
self.app_handle.cursor_position()
}

/// Closes this webview.
pub fn close(&self) -> crate::Result<()> {
self.webview.dispatcher.close()?;
Expand Down
9 changes: 9 additions & 0 deletions core/tauri/src/webview/webview_window.rs
Expand Up @@ -1206,6 +1206,15 @@ impl<R: Runtime> WebviewWindow<R> {
}
}

/// Desktop window getters.
#[cfg(desktop)]
impl<R: Runtime> WebviewWindow<R> {
/// Get the cursor position relative to the top-left hand corner of the main monitor.
pub fn cursor_position(&self) -> crate::Result<PhysicalPosition<f64>> {
self.webview.cursor_position()
}
}

/// Desktop window setters and actions.
#[cfg(desktop)]
impl<R: Runtime> WebviewWindow<R> {
Expand Down
9 changes: 9 additions & 0 deletions core/tauri/src/window/mod.rs
Expand Up @@ -1539,6 +1539,15 @@ impl<R: Runtime> Window<R> {
}
}

/// Desktop window getters.
#[cfg(desktop)]
impl<R: Runtime> Window<R> {
/// Get the cursor position relative to the top-left hand corner of the main monitor.
pub fn cursor_position(&self) -> crate::Result<PhysicalPosition<f64>> {
self.app_handle.cursor_position()
}
}

/// Desktop window setters and actions.
#[cfg(desktop)]
impl<R: Runtime> Window<R> {
Expand Down
2 changes: 2 additions & 0 deletions core/tauri/src/window/plugin.rs
Expand Up @@ -90,6 +90,7 @@ mod desktop_commands {
getter!(current_monitor, Option<Monitor>);
getter!(primary_monitor, Option<Monitor>);
getter!(available_monitors, Vec<Monitor>);
getter!(cursor_position, PhysicalPosition<f64>);
getter!(theme, Theme);

setter!(center);
Expand Down Expand Up @@ -220,6 +221,7 @@ pub fn init<R: Runtime>() -> TauriPlugin<R> {
desktop_commands::current_monitor,
desktop_commands::primary_monitor,
desktop_commands::available_monitors,
desktop_commands::cursor_position,
desktop_commands::theme,
// setters
desktop_commands::center,
Expand Down
17 changes: 16 additions & 1 deletion tooling/api/src/window.ts
Expand Up @@ -2254,6 +2254,20 @@ async function availableMonitors(): Promise<Monitor[]> {
)
}

/** Get the cursor position relative to the top-left hand corner of the desktop.
*
* Note that the top-left hand corner of the desktop is not necessarily the same as the screen.
* If the user uses a desktop with multiple monitors,
* the top-left hand corner of the desktop is the top-left hand corner of the monitor at the top-left of the desktop.
*
* The coordinates can be negative if the top-left hand corner of the window is outside of the visible screen region.
*/
async function cursorPosition(): Promise<PhysicalPosition> {
return invoke<PhysicalPosition>('plugin:window|cursor_position').then(
mapPhysicalPosition
)
}

export {
Window,
CloseRequestedEvent,
Expand All @@ -2268,7 +2282,8 @@ export {
EffectState,
currentMonitor,
primaryMonitor,
availableMonitors
availableMonitors,
cursorPosition
}

export type {
Expand Down