tauri_plugin_polygon/
desktop.rs

1use log::{error, trace};
2use portable_atomic::AtomicPtr;
3use serde::de::DeserializeOwned;
4use std::fmt::Debug;
5use std::sync::atomic::Ordering;
6use tauri::Manager;
7use tauri::{plugin::PluginApi, AppHandle, Runtime};
8
9use crate::error::Result;
10use crate::utils::Convert;
11use crate::view;
12
13pub(crate) fn init<R: Runtime, C: DeserializeOwned>(
14    app: &AppHandle<R>,
15    _api: PluginApi<R, C>,
16    f: PolygonCallback<R>,
17) -> crate::Result<Polygon<R>> {
18    Ok(Polygon {
19        app_handle: app.clone(),
20        callback: AtomicPtr::new(Box::into_raw(Box::new(f))),
21    })
22}
23
24pub(crate) type PolygonCallback<R> =
25    Box<dyn FnMut(&AppHandle<R>, crate::Event) + Send + Sync + 'static>;
26
27/// Access to the Polygon APIs.
28pub struct Polygon<R: Runtime> {
29    pub app_handle: AppHandle<R>,
30    callback: AtomicPtr<PolygonCallback<R>>,
31}
32
33impl<R: Runtime> Polygon<R> {
34    pub(crate) fn emit(&self, app_handle: &AppHandle<R>, event: crate::Event) {
35        let ptr = self.callback.load(Ordering::SeqCst);
36        let mut callback = unsafe { Box::from_raw(ptr) };
37        callback(app_handle, event);
38        self.callback
39            .store(Box::into_raw(callback), Ordering::SeqCst);
40    }
41    /// Register a default polygon with given id.
42    ///
43    /// Frequent calls to this function may cause performance issues.
44    /// It is recommended to use `register_all` to register multiple polygons at once.
45    ///
46    /// # Errors
47    /// This function will return an error if the `id` provided has already been registered.
48    ///
49    /// # Example
50    /// ```no_run
51    /// // backend with rust
52    /// app.polygon().register("my-polygon")?;
53    /// ```
54    /// ```javascript
55    /// // frontend with js
56    /// import { register } from 'tauri-plugin-polygon-api';
57    /// await register('my-polygon');
58    /// ```
59    pub fn register(&self, id: &str) -> Result<()> {
60        trace!("register: {id}");
61        match view::register(id.into()) {
62            Ok(_) => Ok(()),
63            Err(e) => {
64                error!("register: {e}");
65                self.emit(&self.app_handle, crate::Event::Error(e.clone()));
66                Err(e)
67            }
68        }
69    }
70    /// Register multiple polygons.
71    ///
72    /// # Errors
73    /// This function will `not` return errors even if the `id` provided has already been registered.
74    ///
75    /// # Example
76    /// ```no_run
77    /// // backend with rust
78    /// app.polygon().register(Vec::from(["my-polygon", "another-polygon"]))?;
79    /// ```
80    /// ```javascript
81    /// // frontend with js
82    /// import { registerAll } from 'tauri-plugin-polygon-api';
83    /// await registerAll(['my-polygon', 'another-polygon']);
84    /// ```
85    pub fn register_all<S: AsRef<str> + Debug>(&self, ids: Vec<S>) -> Result<()> {
86        trace!("register_all: {ids:?}");
87        match view::register_all(ids.iter().map(|id| id.as_ref().to_string()).collect()) {
88            Ok(_) => Ok(()),
89            Err(e) => {
90                error!("register_all: {e}");
91                self.emit(&self.app_handle, crate::Event::Error(e.clone()));
92                Err(e)
93            }
94        }
95    }
96    /// Remove a polygon physically.
97    ///
98    /// After this function call ends, the specified polygon will be deleted physically,
99    /// and needs to be re-registered before it can be used again.
100    ///
101    /// Frequent calls to this function may cause performance issues.
102    /// It is recommended to use `hide` to disable the polygon logically.
103    ///
104    /// # Errors
105    /// This function will return an error if the `id` provided can not be found.
106    ///
107    /// # Example
108    /// ```no_run
109    /// // backend with rust
110    /// app.polygon().remove("my-polygon")?;
111    /// ```
112    /// ```javascript
113    /// // frontend with js
114    /// import { remove } from 'tauri-plugin-polygon-api';
115    /// await remove('my-polygon');
116    /// ```
117    pub fn remove(&self, id: &str) -> Result<()> {
118        trace!("remove: {id}");
119        match view::remove(&id) {
120            Ok(_) => Ok(()),
121            Err(e) => {
122                error!("remove: {e}");
123                self.emit(&self.app_handle, crate::Event::Error(e.clone()));
124                Err(e)
125            }
126        }
127    }
128    /// Enable the polygon by given id.
129    ///
130    /// # Errors
131    /// This function will return an error if the `id` provided can not be found.
132    ///
133    /// # Example
134    /// ```no_run
135    /// // backend with rust
136    /// app.polygon().show("my-polygon")?;
137    /// ```
138    /// ```javascript
139    /// // frontend with js
140    /// import { show } from 'tauri-plugin-polygon-api';
141    /// await show('my-polygon');
142    /// ```
143    pub fn show(&self, id: &str) -> Result<()> {
144        trace!("show: {id}");
145        match view::show(&id) {
146            Ok(_) => Ok(()),
147            Err(e) => {
148                error!("show: {e}");
149                self.emit(&self.app_handle, crate::Event::Error(e.clone()));
150                Err(e)
151            }
152        }
153    }
154    /// Disable the polygon logically by given id.
155    ///
156    /// # Errors
157    /// This function will return an error if the `id` provided can not be found.
158    ///
159    /// # Example
160    /// ```no_run
161    /// // backend with rust
162    /// app.polygon().hide("my-polygon")?;
163    /// ```
164    /// ```javascript
165    /// // frontend with js
166    /// import { hide } from 'tauri-plugin-polygon-api';
167    /// await hide('my-polygon');
168    /// ```
169    pub fn hide(&self, id: &str) -> Result<()> {
170        trace!("hide: {id}");
171
172        self.app_handle
173            .get_webview_window("main")
174            .unwrap()
175            .set_ignore_cursor_events(true)
176            .unwrap();
177
178        match view::hide(&id) {
179            Ok(_) => Ok(()),
180            Err(e) => {
181                error!("hide: {e}");
182                self.emit(&self.app_handle, crate::Event::Error(e.clone()));
183                Err(e)
184            }
185        }
186    }
187    /// Update vertices of the polygon by given id.
188    /// Within these points, mouse events will not go through.
189    ///
190    /// # Notice
191    /// 1. All positions should be converted to a `percentage based on the screen width`.
192    ///    Position from 0 to 1, 0.1 means 10% of the `screen width`.
193    /// 2. At least `3` points are required.
194    /// 3. The order in which you define the points matters and can result in different shapes.
195    ///
196    /// # Errors
197    /// This function will return an error if the `id` provided can not be found.
198    ///
199    /// # Example
200    /// ```no_run
201    /// // backend with rust
202    /// app.polygon().update("my-polygon", vec![(0.0, 0.0), (0.1, 0.0), (0.1, 0.1), (0.0, 0.1)])?;
203    /// ```
204    /// ```javascript
205    /// // frontend with js
206    /// import { update } from 'tauri-plugin-polygon-api';
207    ///
208    /// await update('my-polygon', {
209    ///     id: "EXAMPLE",
210    ///     polygon: [
211    ///       [0, 0],
212    ///       [0.1, 0],
213    ///       [0.1, 0.1],
214    ///       [0, 0.1]
215    ///     ]
216    /// })
217    /// ```
218    pub fn update(&self, id: &str, points: Vec<(f64, f64)>) -> Result<()> {
219        trace!("update: {id} - {points:?}");
220        match view::update(
221            &id,
222            &points
223                .iter()
224                .map(|(x, y)| Convert::from_viewport(*x, *y))
225                .collect::<Vec<(f64, f64)>>(),
226        ) {
227            Ok(_) => Ok(()),
228            Err(e) => {
229                error!("update: {e}");
230                self.emit(&self.app_handle, crate::Event::Error(e.clone()));
231                Err(e)
232            }
233        }
234    }
235    /// Clear all polygons physically.
236    ///
237    /// # Example
238    /// ```no_run
239    /// // backend with rust
240    /// app.polygon().clear()?;
241    /// ```
242    /// ```javascript
243    /// // frontend with js
244    /// import { clear } from 'tauri-plugin-polygon-api';
245    /// await clear();
246    /// ```
247    pub fn clear(&self) -> Result<()> {
248        trace!("clear");
249        match view::clear() {
250            Ok(_) => Ok(()),
251            Err(e) => {
252                error!("clear: {e}");
253                self.emit(&self.app_handle, crate::Event::Error(e.clone()));
254                Err(e)
255            }
256        }
257    }
258    pub(crate) fn destroy(&self) -> Result<()> {
259        let ptr = self.callback.load(Ordering::SeqCst);
260        self.callback.store(std::ptr::null_mut(), Ordering::SeqCst);
261        drop(unsafe { Box::from_raw(ptr) });
262        Ok(())
263    }
264}