Added initial window module functionality. (#57)

This commit is contained in:
bicarlsen 2024-07-23 09:35:07 +02:00 committed by GitHub
parent 125136ae79
commit 9c3cd94275
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 931 additions and 29 deletions

View file

@ -24,9 +24,11 @@ wasm-bindgen-test = "0.3.42"
all-features = true all-features = true
[features] [features]
all = ["core", "event"] all = ["core", "dpi", "event", "window"]
core = [] core = []
dpi = []
event = ["dep:futures"] event = ["dep:futures"]
window = ["dpi", "event"]
[workspace] [workspace]
members = ["examples/test", "examples/test/src-tauri"] members = ["examples/test", "examples/test/src-tauri"]

View file

@ -53,6 +53,7 @@ All modules are gated by accordingly named Cargo features. It is recommended you
- **all**: Enables all modules. - **all**: Enables all modules.
- **core**: Enables the `core` module. (Only `invoke` and `convertFileSrc` currently implemented.) - **core**: Enables the `core` module. (Only `invoke` and `convertFileSrc` currently implemented.)
- **event**: Enables the `event` module. - **event**: Enables the `event` module.
- **window**: Enables the `windows` module. (~20% implemented)
## Are we Tauri yet? ## Are we Tauri yet?
@ -60,7 +61,7 @@ These API bindings are not completely on-par with `@tauri-apps/api` yet, but her
- [ ] `app` - [ ] `app`
- [x] `core` (partial implementation) - [x] `core` (partial implementation)
- [ ] `dpi` - [x] `dpi`
- [x] `event` - [x] `event`
- [ ] `image` - [ ] `image`
- [ ] `menu` - [ ] `menu`
@ -69,7 +70,7 @@ These API bindings are not completely on-par with `@tauri-apps/api` yet, but her
- [ ] `tray` - [ ] `tray`
- [ ] `webview` - [ ] `webview`
- [ ] `webviewWindow` - [ ] `webviewWindow`
- [ ] `window` - [x] `window` (partial implementation)
The current API also very closely mirrors the JS API even though that might not be the most ergonomic choice, ideas for improving the API with quality-of-life features beyond the regular JS API interface are very welcome. The current API also very closely mirrors the JS API even though that might not be the most ergonomic choice, ideas for improving the API with quality-of-life features beyond the regular JS API interface are very welcome.

View file

@ -43,7 +43,6 @@ mod inner {
#[wasm_bindgen(module = "/src/core.js")] #[wasm_bindgen(module = "/src/core.js")]
extern "C" { extern "C" {
#[wasm_bindgen]
pub async fn invoke(cmd: &str, args: JsValue) -> JsValue; pub async fn invoke(cmd: &str, args: JsValue) -> JsValue;
#[wasm_bindgen(js_name = "invoke", catch)] #[wasm_bindgen(js_name = "invoke", catch)]
pub async fn invoke_result(cmd: &str, args: JsValue) -> Result<JsValue, JsValue>; pub async fn invoke_result(cmd: &str, args: JsValue) -> Result<JsValue, JsValue>;

110
src/dpi.rs Normal file
View file

@ -0,0 +1,110 @@
use serde::Deserialize;
pub type ScaleFactor = f64;
pub type PixelCount = isize;
#[derive(Debug)]
pub enum Kind {
Logical,
Physical,
}
/// A size represented in logical pixels.
#[derive(Deserialize, Clone, Debug)]
pub struct LogicalSize {
width: PixelCount,
height: PixelCount,
}
impl LogicalSize {
pub fn kind() -> Kind {
Kind::Logical
}
pub fn width(&self) -> PixelCount {
self.width
}
pub fn height(&self) -> PixelCount {
self.height
}
}
/// A size represented in physical pixels.
#[derive(Deserialize, Clone, Debug)]
pub struct PhysicalSize {
width: PixelCount,
height: PixelCount,
}
impl PhysicalSize {
pub fn kind() -> Kind {
Kind::Physical
}
pub fn width(&self) -> PixelCount {
self.width
}
pub fn height(&self) -> PixelCount {
self.height
}
/// Converts the physical size to a logical one.
pub fn as_logical(&self, scale_factor: ScaleFactor) -> LogicalSize {
LogicalSize {
width: (self.width as f64 / scale_factor) as PixelCount,
height: (self.height as f64 / scale_factor) as PixelCount,
}
}
}
/// A position represented in logical pixels.
#[derive(Deserialize, Clone, Debug)]
pub struct LogicalPosition {
x: PixelCount,
y: PixelCount,
}
impl LogicalPosition {
pub fn kind() -> Kind {
Kind::Logical
}
pub fn x(&self) -> PixelCount {
self.x
}
pub fn y(&self) -> PixelCount {
self.y
}
}
/// A position represented in physical pixels.
#[derive(Deserialize, Clone, Debug)]
pub struct PhysicalPosition {
x: PixelCount,
y: PixelCount,
}
impl PhysicalPosition {
pub fn kind() -> Kind {
Kind::Physical
}
pub fn x(&self) -> PixelCount {
self.x
}
pub fn y(&self) -> PixelCount {
self.y
}
/// Converts the physical position to a logical one.
pub fn as_logical(&self, scale_factor: ScaleFactor) -> LogicalPosition {
LogicalPosition {
x: (self.x as f64 / scale_factor) as PixelCount,
y: (self.y as f64 / scale_factor) as PixelCount,
}
}
}

View file

@ -55,28 +55,9 @@ async function once(event, handler, options) {
) )
} }
// tauri/tooling/api/src/event.ts
var TauriEvent = /* @__PURE__ */ ((TauriEvent2) => {
TauriEvent2["WINDOW_RESIZED"] = 'tauri://resize';
TauriEvent2["WINDOW_MOVED"] = 'tauri://move';
TauriEvent2["WINDOW_CLOSE_REQUESTED"] = 'tauri://close-requested';
TauriEvent2["WINDOW_DESTROYED"] = 'tauri://destroyed';
TauriEvent2["WINDOW_FOCUS"] = 'tauri://focus';
TauriEvent2["WINDOW_BLUR"] = 'tauri://blur';
TauriEvent2["WINDOW_SCALE_FACTOR_CHANGED"] = 'tauri://scale-change';
TauriEvent2["WINDOW_THEME_CHANGED"] = 'tauri://theme-changed';
TauriEvent2["WINDOW_CREATED"] = 'tauri://window-created';
TauriEvent2["WEBVIEW_CREATED"] = 'tauri://webview-created';
TauriEvent2["DRAG"] = 'tauri://drag';
TauriEvent2["DROP"] = 'tauri://drop';
TauriEvent2["DROP_OVER"] = 'tauri://drop-over';
TauriEvent2["DROP_CANCELLED"] = 'tauri://drag-cancelled';
return TauriEvent2;
})(TauriEvent || {});
export { export {
TauriEvent,
emit, emit,
emitTo, emitTo,
listen, listen,
once once,
}; };

View file

@ -1,5 +1,4 @@
//! The event system allows you to emit events to the backend and listen to events from it. //! The event system allows you to emit events to the backend and listen to events from it.
use futures::{ use futures::{
channel::{mpsc, oneshot}, channel::{mpsc, oneshot},
Future, FutureExt, Stream, StreamExt, Future, FutureExt, Stream, StreamExt,
@ -8,13 +7,28 @@ use serde::{de::DeserializeOwned, Deserialize, Serialize};
use std::fmt::Debug; use std::fmt::Debug;
use wasm_bindgen::{prelude::Closure, JsValue}; use wasm_bindgen::{prelude::Closure, JsValue};
pub const WINDOW_RESIZED: &str = "tauri://resize";
pub const WINDOW_MOVED: &str = "tauri://move";
pub const WINDOW_CLOSE_REQUESTED: &str = "tauri://close-requested";
pub const WINDOW_DESTROYED: &str = "tauri://destroyed";
pub const WINDOW_FOCUS: &str = "tauri://focus";
pub const WINDOW_BLUR: &str = "tauri://blur";
pub const WINDOW_SCALE_FACTOR_CHANGED: &str = "tauri://scale-change";
pub const WINDOW_THEME_CHANGED: &str = "tauri://theme-changed";
pub const WINDOW_CREATED: &str = "tauri://window-created";
pub const WEBVIEW_CREATED: &str = "tauri://webview-created";
pub const DRAG: &str = "tauri://drag";
pub const DROP: &str = "tauri://drop";
pub const DROP_OVER: &str = "tauri://drop-over";
pub const DROP_CANCELLED: &str = "tauri://drag-cancelled";
#[derive(Debug, Clone, PartialEq, Deserialize)] #[derive(Debug, Clone, PartialEq, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct Event<T> { pub struct Event<T> {
/// Event name /// Event name
pub event: String, pub event: String,
/// Event identifier used to unlisten /// Event identifier used to unlisten
pub id: f32, pub id: isize,
/// Event payload /// Event payload
pub payload: T, pub payload: T,
} }
@ -31,8 +45,8 @@ pub enum EventTarget {
} }
#[derive(Debug, Clone, PartialEq, Serialize)] #[derive(Debug, Clone, PartialEq, Serialize)]
struct Options { pub(crate) struct Options {
target: EventTarget, pub target: EventTarget,
} }
/// Emits an event to the backend. /// Emits an event to the backend.
@ -334,7 +348,7 @@ impl<T> Future for Once<T> {
} }
} }
mod inner { pub(crate) mod inner {
use wasm_bindgen::{ use wasm_bindgen::{
prelude::{wasm_bindgen, Closure}, prelude::{wasm_bindgen, Closure},
JsValue, JsValue,

View file

@ -92,6 +92,12 @@ pub mod event;
#[cfg(feature = "core")] #[cfg(feature = "core")]
pub mod core; pub mod core;
#[cfg(feature = "dpi")]
pub mod dpi;
#[cfg(feature = "window")]
pub mod window;
pub use error::Error; pub use error::Error;
pub(crate) type Result<T> = std::result::Result<T, Error>; pub(crate) type Result<T> = std::result::Result<T, Error>;

38
src/window.js Normal file
View file

@ -0,0 +1,38 @@
// tauri/tooling/api/src/core.ts
async function invoke(cmd, args = {}) {
// NB: `options` ignored as not used here.
return window.__TAURI_INTERNALS__.invoke(cmd, args)
}
// tauri/tooling/api/src/window.ts
function getCurrent() {
return window.__TAURI_INTERNALS__.metadata.currentWindow
}
function getAll() {
return window.__TAURI_INTERNALS__.metadata.windows
}
async function currentMonitor() {
return invoke('plugin:window|current_monitor')
}
async function primaryMonitor() {
return invoke('plugin:window|primary_monitor')
}
async function monitorFromPoint(x, y) {
return invoke('plugin:window|monitor_from_point', { x, y })
}
async function availableMonitors() {
return invoke('plugin:window|available_monitors')
}
async function cursorPosition() {
return invoke('plugin:window|cursor_position')
}
export {
getCurrent,
getAll,
currentMonitor,
primaryMonitor,
monitorFromPoint,
availableMonitors,
cursorPosition,
}

751
src/window.rs Normal file
View file

@ -0,0 +1,751 @@
//! Provides APIs to create windows, communicate with other windows and manipulate the current window.
//!
//! ## Window events
//! Events can be listened to using [`Window::listen`].
use crate::{
dpi,
event::{self, Event},
};
use futures::{
channel::{
mpsc::{self, UnboundedSender},
oneshot,
},
Future, FutureExt, Stream, StreamExt,
};
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use std::{any::Any, collections::HashMap};
use wasm_bindgen::{prelude::Closure, JsValue};
/// Events that are emitted right here instead of by the created window.
const LOCAL_TAURI_EVENTS: &'static [&'static str; 2] = &["tauri://created", "tauri://error"];
trait SenderVec: Any {
fn as_any(&self) -> &dyn std::any::Any;
fn as_any_mut(&mut self) -> &mut dyn std::any::Any;
}
impl<T> SenderVec for Vec<mpsc::UnboundedSender<T>>
where
T: DeserializeOwned + 'static,
{
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self as &mut dyn Any
}
}
pub(crate) struct Listen<T> {
pub rx: mpsc::UnboundedReceiver<T>,
}
impl<T> Stream for Listen<T> {
type Item = T;
fn poll_next(
mut self: std::pin::Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
) -> std::task::Poll<Option<Self::Item>> {
self.rx.poll_next_unpin(cx)
}
}
pub(crate) struct DragDropListen {
pub rx: mpsc::UnboundedReceiver<Event<DragDropEvent>>,
pub unlisten_drag: js_sys::Function,
pub unlisten_drop: js_sys::Function,
pub unlisten_drag_over: js_sys::Function,
pub unlisten_cancel: js_sys::Function,
}
impl Drop for DragDropListen {
fn drop(&mut self) {
log::debug!("Calling unlisten for listen callback");
self.unlisten_drag
.call0(&wasm_bindgen::JsValue::NULL)
.unwrap();
self.unlisten_drop
.call0(&wasm_bindgen::JsValue::NULL)
.unwrap();
self.unlisten_drag_over
.call0(&wasm_bindgen::JsValue::NULL)
.unwrap();
self.unlisten_cancel
.call0(&wasm_bindgen::JsValue::NULL)
.unwrap();
}
}
impl Stream for DragDropListen {
type Item = Event<DragDropEvent>;
fn poll_next(
mut self: std::pin::Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
) -> std::task::Poll<Option<Self::Item>> {
self.rx.poll_next_unpin(cx)
}
}
#[derive(Deserialize)]
struct WindowLabel {
label: String,
}
#[derive(Deserialize)]
pub enum DragDropEvent {
Dragged(DragDropPayload),
DragOver(DragOverPayload),
Dropped(DragDropPayload),
Cancelled,
}
#[derive(Deserialize)]
pub struct DragDropPayload {
paths: Vec<String>,
position: dpi::PhysicalPosition,
}
impl DragDropPayload {
pub fn paths(&self) -> &Vec<String> {
&self.paths
}
pub fn position(&self) -> &dpi::PhysicalPosition {
&self.position
}
}
#[derive(Deserialize)]
pub struct DragOverPayload {
position: dpi::PhysicalPosition,
}
impl DragOverPayload {
pub fn position(&self) -> &dpi::PhysicalPosition {
&self.position
}
}
pub struct Window {
label: String,
listeners: HashMap<String, Box<dyn SenderVec>>,
}
impl Window {
/// Create a new Window.
///
/// # Arguments
/// + `label`: Unique window label. Must be alphanumberic: `a-zA-Z-/:_`.
pub fn new(label: impl Into<String>) -> Self {
Self {
label: label.into(),
listeners: HashMap::new(),
}
}
/// Gets the Window associated with the given label.
pub fn get_by_label(label: impl AsRef<str>) -> Option<Self> {
js_sys::try_iter(&inner::get_all())
.unwrap()
.unwrap()
.into_iter()
.find_map(|value| {
let window_label = value.unwrap().as_string().unwrap();
if window_label == label.as_ref() {
Some(Window::new(window_label))
} else {
None
}
})
}
/// Get an instance of `Window` for the current window.
pub fn get_current() -> Self {
get_current()
}
/// Gets a list of instances of `Window` for all available windows.
pub fn get_all() -> Vec<Self> {
get_all()
}
}
impl Window {
pub fn label(&self) -> &String {
&self.label
}
fn handle_tauri_event<T>(&mut self, event: String) -> Option<impl Stream<Item = Event<T>>>
where
T: DeserializeOwned + 'static,
{
if LOCAL_TAURI_EVENTS.contains(&event.as_str()) {
let (tx, rx) = mpsc::unbounded::<Event<T>>();
let entry = self
.listeners
.entry(event)
.or_insert(Box::new(Vec::<mpsc::UnboundedSender<Event<T>>>::new()));
let senders = entry
.as_any_mut()
.downcast_mut::<Vec<mpsc::UnboundedSender<Event<T>>>>()
.unwrap();
senders.push(tx);
Some(Listen { rx })
} else {
None
}
}
}
impl Window {
/// Listen to an emitted event on this window.
///
/// # Arguments
/// + `event`: Event name. Must include only alphanumeric characters, `-`, `/`, `:` and `_`.
/// + `handler`: Event handler.
///
/// # Returns
/// A function to unlisten to the event.
/// Note that removing the listener is required if your listener goes out of scope e.g. the component is unmounted.
pub async fn listen<T>(
&mut self,
event: impl Into<String>,
) -> crate::Result<impl Stream<Item = Event<T>>>
where
T: DeserializeOwned + 'static,
{
use futures::future::Either;
let event = event.into();
if let Some(listener) = self.handle_tauri_event(event.clone()) {
Ok(Either::Left(listener))
} else {
let listener =
event::listen_to(&event, event::EventTarget::Window(self.label.clone())).await?;
Ok(Either::Right(listener))
}
}
/// Listen to an emitted event on this window only once.
///
/// # Arguments
/// + `event`: Event name. Must include only alphanumeric characters, `-`, `/`, `:` and `_`.
/// + `handler`: Event handler.
///
/// # Returns
/// A promise resolving to a function to unlisten to the event.
/// Note that removing the listener is required if your listener goes out of scope e.g. the component is unmounted.
pub async fn once(&self, event: impl Into<String>, handler: Closure<dyn FnMut(JsValue)>) {
todo!();
}
/// Emits an event to all {@link EventTarget|targets}.
///
/// # Arguments
/// + `event`: Event name. Must include only alphanumeric characters, `-`, `/`, `:` and `_`.
/// + `payload`: Event payload.
pub async fn emit<T: Serialize + Clone + 'static>(
&self,
event: impl Into<String>,
payload: T,
) -> crate::Result<()> {
let event: String = event.into();
if LOCAL_TAURI_EVENTS.contains(&event.as_str()) {
if let Some(listeners) = self.listeners.get(&event) {
let listeners = listeners
.as_any()
.downcast_ref::<Vec<UnboundedSender<Event<T>>>>()
.unwrap();
for listener in listeners {
listener
.unbounded_send(event::Event {
event: event.clone(),
id: -1,
payload: payload.clone(),
})
.unwrap();
}
}
Ok(())
} else {
event::emit(event.as_str(), &payload).await
}
}
/// Emits an event to all {@link EventTarget|targets} matching the given target.
///
/// # Arguments
/// + `target`: Label of the target Window/Webview/WebviewWindow or raw {@link EventTarget} object.
/// + `event`: Event name. Must include only alphanumeric characters, `-`, `/`, `:` and `_`.
/// + `payload`: Event payload.
pub async fn emit_to<T: Serialize + Clone + 'static>(
&self,
target: &event::EventTarget,
event: impl Into<String>,
payload: T,
) -> crate::Result<()> {
let event: String = event.into();
if LOCAL_TAURI_EVENTS.contains(&event.as_str()) {
if let Some(listeners) = self.listeners.get(&event) {
let listeners = listeners
.as_any()
.downcast_ref::<Vec<UnboundedSender<Event<T>>>>()
.unwrap();
for listener in listeners {
listener
.unbounded_send(event::Event {
event: event.clone(),
id: -1,
payload: payload.clone(),
})
.unwrap();
}
}
Ok(())
} else {
event::emit_to(target, event.as_str(), &payload).await
}
}
}
impl Window {
/// Listen to a file drop event.
/// The listener is triggered when the user hovers the selected files on the webview,
/// drops the files or cancels the operation.
///
/// You need to call unlisten if your handler goes out of scope e.g. the component is unmounted
/// unlisten();
///
/// # Returns
/// Function to unlisten to the event.
/// Note that removing the listener is required if your listener goes out of scope e.g. the component is unmounted.
pub async fn on_drag_drop_event(
&mut self,
) -> crate::Result<impl Stream<Item = Event<DragDropEvent>>> {
let (tx, rx) = mpsc::unbounded::<Event<DragDropEvent>>();
let closure = {
let tx = tx.clone();
Closure::<dyn FnMut(JsValue)>::new(move |raw| {
let Event { event, id, payload } =
serde_wasm_bindgen::from_value::<Event<DragDropPayload>>(raw).unwrap();
let _ = tx.unbounded_send(Event {
event,
id,
payload: DragDropEvent::Dragged(payload),
});
})
};
let unlisten = event::inner::listen(
event::DRAG,
&closure,
serde_wasm_bindgen::to_value(&event::Options {
target: event::EventTarget::Window(self.label.clone()),
})?,
)
.await?;
closure.forget();
let unlisten_drag = js_sys::Function::from(unlisten);
let closure = {
let tx = tx.clone();
Closure::<dyn FnMut(JsValue)>::new(move |raw| {
let Event { event, id, payload } =
serde_wasm_bindgen::from_value::<Event<DragDropPayload>>(raw).unwrap();
let _ = tx.unbounded_send(Event {
event,
id,
payload: DragDropEvent::Dropped(payload),
});
})
};
let unlisten = event::inner::listen(
event::DROP,
&closure,
serde_wasm_bindgen::to_value(&event::Options {
target: event::EventTarget::Window(self.label.clone()),
})?,
)
.await?;
closure.forget();
let unlisten_drop = js_sys::Function::from(unlisten);
let closure = {
let tx = tx.clone();
Closure::<dyn FnMut(JsValue)>::new(move |raw| {
let Event { event, id, payload } =
serde_wasm_bindgen::from_value::<Event<DragOverPayload>>(raw).unwrap();
let _ = tx.unbounded_send(Event {
event,
id,
payload: DragDropEvent::DragOver(payload),
});
})
};
let unlisten = event::inner::listen(
event::DROP_OVER,
&closure,
serde_wasm_bindgen::to_value(&event::Options {
target: event::EventTarget::Window(self.label.clone()),
})?,
)
.await?;
closure.forget();
let unlisten_drag_over = js_sys::Function::from(unlisten);
let closure = {
let tx = tx.clone();
Closure::<dyn FnMut(JsValue)>::new(move |raw| {
let Event { event, id, .. } =
serde_wasm_bindgen::from_value::<Event<()>>(raw).unwrap();
let _ = tx.unbounded_send(Event {
event,
id,
payload: DragDropEvent::Cancelled,
});
})
};
let unlisten = event::inner::listen(
event::DROP_CANCELLED,
&closure,
serde_wasm_bindgen::to_value(&event::Options {
target: event::EventTarget::Window(self.label.clone()),
})?,
)
.await?;
closure.forget();
let unlisten_cancel = js_sys::Function::from(unlisten);
Ok(DragDropListen {
rx,
unlisten_drag,
unlisten_drop,
unlisten_drag_over,
unlisten_cancel,
})
}
}
#[derive(Deserialize, Clone, Debug)]
#[serde(rename_all = "camelCase")]
pub struct Monitor {
/// Human-readable name of the monitor.
name: Option<String>,
/// The monitor's resolution.
size: dpi::PhysicalSize,
/// the Top-left corner position of the monitor relative to the larger full screen area.
position: dpi::PhysicalPosition,
/// The scale factor that can be used to map physical pixels to logical pixels.
scale_factor: f64,
}
impl Monitor {
pub fn name(&self) -> &Option<String> {
&self.name
}
pub fn size(&self) -> &dpi::PhysicalSize {
&self.size
}
pub fn position(&self) -> &dpi::PhysicalPosition {
&self.position
}
pub fn scale_factor(&self) -> f64 {
self.scale_factor
}
}
pub fn get_current() -> Window {
let WindowLabel { label } = serde_wasm_bindgen::from_value(inner::get_current()).unwrap();
Window::new(label)
}
pub fn get_all() -> Vec<Window> {
js_sys::try_iter(&inner::get_all())
.unwrap()
.unwrap()
.into_iter()
.map(|value| {
let WindowLabel { label } = serde_wasm_bindgen::from_value(value.unwrap()).unwrap();
Window::new(label)
})
.collect()
}
/// # Returns
/// Monitor on which the window currently resides.
pub async fn current_monitor() -> Option<Monitor> {
let value = inner::current_monitor().await;
if value.is_null() {
None
} else {
Some(serde_wasm_bindgen::from_value(value).unwrap())
}
}
/// # Returns
/// Primary monitor of the system.
pub async fn primary_monitor() -> Option<Monitor> {
let value = inner::primary_monitor().await;
if value.is_null() {
None
} else {
Some(serde_wasm_bindgen::from_value(value).unwrap())
}
}
/// # Returns
/// Monitor that contains the given point.
pub async fn monitor_from_point(x: isize, y: isize) -> Option<Monitor> {
let value = inner::monitor_from_point(x, y).await;
if value.is_null() {
None
} else {
Some(serde_wasm_bindgen::from_value(value).unwrap())
}
}
/// # Returns
/// All the monitors available on the system.
pub async fn available_monitors() -> Vec<Monitor> {
let value = inner::available_monitors().await;
js_sys::try_iter(&value)
.unwrap()
.unwrap()
.into_iter()
.map(|value| serde_wasm_bindgen::from_value(value.unwrap()).unwrap())
.collect()
}
// TODO: Issue with cursorPosition in Tauri.
// See: https://github.com/tauri-apps/tauri/issues/10340
///// 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 main monitor on Windows and macOS
///// or the top-left of the leftmost monitor on X11.
/////
///// The coordinates can be negative if the top-left hand corner of the window is outside of the visible screen region.
//pub async fn cursor_position() -> PhysicalPosition {
// serde_wasm_bindgen::from_value(inner::cursor_position().await).unwrap()
//}
mod inner {
use wasm_bindgen::{prelude::wasm_bindgen, JsValue};
#[wasm_bindgen(module = "/src/window.js")]
extern "C" {
#[wasm_bindgen(js_name = "getCurrent")]
pub fn get_current() -> JsValue;
#[wasm_bindgen(js_name = "getAll")]
pub fn get_all() -> JsValue;
#[wasm_bindgen(js_name = "currentMonitor")]
pub async fn current_monitor() -> JsValue;
#[wasm_bindgen(js_name = "primaryMonitor")]
pub async fn primary_monitor() -> JsValue;
#[wasm_bindgen(js_name = "monitorFromPoint")]
pub async fn monitor_from_point(x: isize, y: isize) -> JsValue;
#[wasm_bindgen(js_name = "availableMonitors")]
pub async fn available_monitors() -> JsValue;
#[wasm_bindgen(js_name = "cursorPosition")]
pub async fn cursor_position() -> JsValue;
}
}
// partial mocks
/*
pub enum Theme {
Light,
Dark,
}
/// Attention type to request on a window.
pub enum UserAttentionType {
/// # Platform-specific
/// - **macOS:** Bounces the dock icon until the application is in focus.
/// - **Windows:** Flashes both the window and the taskbar button until the application is in focus.
Critical = 1,
/// # Platform-specific
/// - **macOS:** Bounces the dock icon once.
/// - **Windows:** Flashes the taskbar button until the application is in focus.
Informational,
}
impl Window {
/// The scale factor that can be used to map physical pixels to logical pixels.
pub async fn scale_factor(&self) -> dpi::ScaleFactor {
todo!();
}
/// The position of the top-left hand corner of the window's client area relative to the top-left hand corner of the desktop.
pub async fn inner_position(&self) -> dpi::PhysicalPosition {
todo!();
}
/// The position of the top-left hand corner of the window relative to the top-left hand corner of the desktop.
pub async fn outer_position(&self) -> dpi::PhysicalPosition {
todo!();
}
/// The physical size of the window's client area.
/// The client area is the content of the window, excluding the title bar and borders.
pub async fn inner_size(&self) -> dpi::PhysicalSize {
todo!();
}
/// The physical size of the entire window.
/// These dimensions include the title bar and borders. If you don't want that (and you usually don't), use inner_size instead.
pub async fn outer_size(&self) -> dpi::PhysicalSize {
todo!();
}
/// Gets the window's current fullscreen state.
pub async fn is_fullscreen(&self) -> bool {
todo!();
}
/// Gets the window's current minimized state.
pub async fn is_minimized(&self) -> bool {
todo!();
}
/// Gets the window's current maximized state.
pub async fn is_maximized(&self) -> bool {
todo!();
}
/// Gets the window's current focused state.
pub async fn is_focused(&self) -> bool {
todo!();
}
/// Gets the window's current decorated state.
pub async fn is_decorated(&self) -> bool {
todo!();
}
/// Gets the window's current resizable state.
pub async fn is_resizable(&self) -> bool {
todo!();
}
/// Gets the window's current visible state.
pub async fn is_visible(&self) -> bool {
todo!();
}
/// Gets the window's current title.
pub async fn title(&self) -> String {
todo!();
}
/// Gets the window's current theme.
pub async fn theme(&self) -> Option<Theme> {
todo!();
}
}
/// # Platform-specific
/// - **Linux / iOS / Android:** Unsupported.
impl Window {
/// Gets the window's native maximize button state.
///
/// # Platform-specific
/// - **Linux / iOS / Android:** Unsupported.
pub async fn is_maximizable(&self) -> bool {
todo!();
}
/// Gets the window's native minimize button state.
///
/// # Platform-specific
/// - **Linux / iOS / Android:** Unsupported.
pub async fn is_minimizable(&self) -> bool {
todo!();
}
/// Gets the window's native close button state.
///
/// # Platform-specific
/// - **Linux / iOS / Android:** Unsupported.
pub async fn is_closable(&self) -> bool {
todo!();
}
}
impl Window {
/// Centers the window.
///
/// # Returns
/// The success or failure of the operation.
pub async fn center(&self) -> Result<(), ()> {
todo!();
}
/// Requests user attention to the window, this has no effect if the application
/// is already focused. How requesting for user attention manifests is platform dependent,
/// see `UserAttentionType` for details.
///
/// Providing `null` will unset the request for user attention. Unsetting the request for
/// user attention might not be done automatically by the WM when the window receives input.
///
/// # Platform-specific
/// - **macOS:** `null` has no effect.
/// - **Linux:** Urgency levels have the same effect.
///
/// # Returns
/// The success or failure of the operation.
pub async fn request_user_attention() -> Result<(), ()> {
todo!();
}
/// Requests user attention to the window, this has no effect if the application
/// is already focused. How requesting for user attention manifests is platform dependent,
/// see `UserAttentionType` for details.
///
/// Providing `null` will unset the request for user attention. Unsetting the request for
/// user attention might not be done automatically by the WM when the window receives input.
///
/// # Platform-specific
/// - **macOS:** `null` has no effect.
/// - **Linux:** Urgency levels have the same effect.
///
/// # Returns
/// The success or failure of the operation.
pub async fn request_user_attention_with_type(
request_type: UserAttentionType,
) -> Result<(), ()> {
todo!();
}
/// Updates the window resizable flag.
///
/// # Returns
/// The success or failure of the operation.
pub async fn set_resizable(&self, resizable: bool) -> Result<(), ()> {
todo!();
}
}
*/