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}