Skip to content

Commit c9a9246

Browse files
feat: move window plugin back to core (#8007)
Co-authored-by: Lucas Nogueira <lucas@tauri.studio>
1 parent f12306a commit c9a9246

File tree

20 files changed

+3388
-17
lines changed

20 files changed

+3388
-17
lines changed

.changes/api-window.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@tauri-apps/api": 'minor:feat'
3+
---
4+
5+
Add the `window` module back

.changes/window-plugin-core.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"tauri": patch:changes
3+
---
4+
5+
Added the `window` plugin back into core.

core/tauri/scripts/bundle.global.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

core/tauri/src/app.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -807,6 +807,7 @@ impl<R: Runtime> App<R> {
807807
fn register_core_plugins(&self) -> crate::Result<()> {
808808
self.handle.plugin(crate::path::init())?;
809809
self.handle.plugin(crate::event::init())?;
810+
self.handle.plugin(crate::window::plugin::init())?;
810811
Ok(())
811812
}
812813

core/tauri/src/error.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,4 +107,7 @@ pub enum Error {
107107
#[cfg(all(desktop, feature = "tray-icon"))]
108108
#[cfg_attr(doc_cfg, doc(cfg(all(desktop, feature = "tray-icon"))))]
109109
BadTrayIcon(#[from] tray_icon::BadIcon),
110+
/// window not found.
111+
#[error("window not found")]
112+
WindowNotFound,
110113
}
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
//! The Tauri window types and functions.
66
7+
pub(crate) mod plugin;
8+
79
use http::HeaderMap;
810
pub use tauri_utils::{config::Color, WindowEffect as Effect, WindowEffectState as EffectState};
911
use url::Url;
@@ -2461,7 +2463,7 @@ impl<R: Runtime> Window<R> {
24612463
///
24622464
/// This listener only receives events that are triggered using the
24632465
/// [`trigger`](Window#method.trigger) and [`emit_and_trigger`](Window#method.emit_and_trigger) methods or
2464-
/// the `emit` function from the window plugin (`@tauri-apps/plugin-window` package).
2466+
/// the `emit` function from the window plugin (`@tauri-apps/api/window` package).
24652467
///
24662468
/// # Examples
24672469
/// ```

core/tauri/src/window/plugin.rs

Lines changed: 322 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,322 @@
1+
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
2+
// SPDX-License-Identifier: Apache-2.0
3+
// SPDX-License-Identifier: MIT
4+
5+
//! The tauri plugin to create and manipulate windows from JS.
6+
7+
use crate::{
8+
plugin::{Builder, TauriPlugin},
9+
Runtime,
10+
};
11+
12+
#[cfg(desktop)]
13+
mod desktop_commands {
14+
use serde::Deserialize;
15+
16+
use super::*;
17+
use crate::{
18+
command,
19+
utils::config::{WindowConfig, WindowEffectsConfig},
20+
AppHandle, CursorIcon, Icon, Manager, Monitor, PhysicalPosition, PhysicalSize, Position, Size,
21+
Theme, UserAttentionType, Window, WindowBuilder,
22+
};
23+
24+
#[derive(Deserialize)]
25+
#[serde(untagged)]
26+
pub enum IconDto {
27+
#[cfg(any(feature = "icon-png", feature = "icon-ico"))]
28+
File(std::path::PathBuf),
29+
#[cfg(any(feature = "icon-png", feature = "icon-ico"))]
30+
Raw(Vec<u8>),
31+
Rgba {
32+
rgba: Vec<u8>,
33+
width: u32,
34+
height: u32,
35+
},
36+
}
37+
38+
impl From<IconDto> for Icon {
39+
fn from(icon: IconDto) -> Self {
40+
match icon {
41+
#[cfg(any(feature = "icon-png", feature = "icon-ico"))]
42+
IconDto::File(path) => Self::File(path),
43+
#[cfg(any(feature = "icon-png", feature = "icon-ico"))]
44+
IconDto::Raw(raw) => Self::Raw(raw),
45+
IconDto::Rgba {
46+
rgba,
47+
width,
48+
height,
49+
} => Self::Rgba {
50+
rgba,
51+
width,
52+
height,
53+
},
54+
}
55+
}
56+
}
57+
58+
#[command(root = "crate")]
59+
pub async fn create<R: Runtime>(app: AppHandle<R>, options: WindowConfig) -> crate::Result<()> {
60+
WindowBuilder::from_config(&app, options).build()?;
61+
Ok(())
62+
}
63+
64+
fn get_window<R: Runtime>(window: Window<R>, label: Option<String>) -> crate::Result<Window<R>> {
65+
match label {
66+
Some(l) if !l.is_empty() => window.get_window(&l).ok_or(crate::Error::WindowNotFound),
67+
_ => Ok(window),
68+
}
69+
}
70+
71+
macro_rules! getter {
72+
($cmd: ident, $ret: ty) => {
73+
#[command(root = "crate")]
74+
pub async fn $cmd<R: Runtime>(
75+
window: Window<R>,
76+
label: Option<String>,
77+
) -> crate::Result<$ret> {
78+
get_window(window, label)?.$cmd().map_err(Into::into)
79+
}
80+
};
81+
}
82+
83+
macro_rules! setter {
84+
($cmd: ident) => {
85+
#[command(root = "crate")]
86+
pub async fn $cmd<R: Runtime>(window: Window<R>, label: Option<String>) -> crate::Result<()> {
87+
get_window(window, label)?.$cmd().map_err(Into::into)
88+
}
89+
};
90+
91+
($cmd: ident, $input: ty) => {
92+
#[command(root = "crate")]
93+
pub async fn $cmd<R: Runtime>(
94+
window: Window<R>,
95+
label: Option<String>,
96+
value: $input,
97+
) -> crate::Result<()> {
98+
get_window(window, label)?.$cmd(value).map_err(Into::into)
99+
}
100+
};
101+
}
102+
103+
getter!(scale_factor, f64);
104+
getter!(inner_position, PhysicalPosition<i32>);
105+
getter!(outer_position, PhysicalPosition<i32>);
106+
getter!(inner_size, PhysicalSize<u32>);
107+
getter!(outer_size, PhysicalSize<u32>);
108+
getter!(is_fullscreen, bool);
109+
getter!(is_minimized, bool);
110+
getter!(is_maximized, bool);
111+
getter!(is_focused, bool);
112+
getter!(is_decorated, bool);
113+
getter!(is_resizable, bool);
114+
getter!(is_maximizable, bool);
115+
getter!(is_minimizable, bool);
116+
getter!(is_closable, bool);
117+
getter!(is_visible, bool);
118+
getter!(title, String);
119+
getter!(current_monitor, Option<Monitor>);
120+
getter!(primary_monitor, Option<Monitor>);
121+
getter!(available_monitors, Vec<Monitor>);
122+
getter!(theme, Theme);
123+
124+
setter!(center);
125+
setter!(request_user_attention, Option<UserAttentionType>);
126+
setter!(set_resizable, bool);
127+
setter!(set_maximizable, bool);
128+
setter!(set_minimizable, bool);
129+
setter!(set_closable, bool);
130+
setter!(set_title, &str);
131+
setter!(maximize);
132+
setter!(unmaximize);
133+
setter!(minimize);
134+
setter!(unminimize);
135+
setter!(show);
136+
setter!(hide);
137+
setter!(close);
138+
setter!(set_decorations, bool);
139+
setter!(set_shadow, bool);
140+
setter!(set_effects, Option<WindowEffectsConfig>);
141+
setter!(set_always_on_top, bool);
142+
setter!(set_content_protected, bool);
143+
setter!(set_size, Size);
144+
setter!(set_min_size, Option<Size>);
145+
setter!(set_max_size, Option<Size>);
146+
setter!(set_position, Position);
147+
setter!(set_fullscreen, bool);
148+
setter!(set_focus);
149+
setter!(set_skip_taskbar, bool);
150+
setter!(set_cursor_grab, bool);
151+
setter!(set_cursor_visible, bool);
152+
setter!(set_cursor_icon, CursorIcon);
153+
setter!(set_cursor_position, Position);
154+
setter!(set_ignore_cursor_events, bool);
155+
setter!(start_dragging);
156+
setter!(print);
157+
158+
#[command(root = "crate")]
159+
pub async fn set_icon<R: Runtime>(
160+
window: Window<R>,
161+
label: Option<String>,
162+
value: IconDto,
163+
) -> crate::Result<()> {
164+
get_window(window, label)?
165+
.set_icon(value.into())
166+
.map_err(Into::into)
167+
}
168+
169+
#[command(root = "crate")]
170+
pub async fn toggle_maximize<R: Runtime>(
171+
window: Window<R>,
172+
label: Option<String>,
173+
) -> crate::Result<()> {
174+
let window = get_window(window, label)?;
175+
match window.is_maximized()? {
176+
true => window.unmaximize()?,
177+
false => window.maximize()?,
178+
};
179+
Ok(())
180+
}
181+
182+
#[command(root = "crate")]
183+
pub async fn internal_toggle_maximize<R: Runtime>(
184+
window: Window<R>,
185+
label: Option<String>,
186+
) -> crate::Result<()> {
187+
let window = get_window(window, label)?;
188+
if window.is_resizable()? {
189+
match window.is_maximized()? {
190+
true => window.unmaximize()?,
191+
false => window.maximize()?,
192+
};
193+
}
194+
Ok(())
195+
}
196+
197+
#[cfg(any(debug_assertions, feature = "devtools"))]
198+
#[command(root = "crate")]
199+
pub async fn internal_toggle_devtools<R: Runtime>(
200+
window: Window<R>,
201+
label: Option<String>,
202+
) -> crate::Result<()> {
203+
let window = get_window(window, label)?;
204+
if window.is_devtools_open() {
205+
window.close_devtools();
206+
} else {
207+
window.open_devtools();
208+
}
209+
Ok(())
210+
}
211+
}
212+
213+
/// Initializes the plugin.
214+
pub fn init<R: Runtime>() -> TauriPlugin<R> {
215+
let mut init_script = String::new();
216+
// window.print works on Linux/Windows; need to use the API on macOS
217+
#[cfg(any(target_os = "macos", target_os = "ios"))]
218+
{
219+
init_script.push_str(include_str!("./scripts/print.js"));
220+
}
221+
init_script.push_str(include_str!("./scripts/drag.js"));
222+
223+
#[cfg(any(debug_assertions, feature = "devtools"))]
224+
{
225+
use serialize_to_javascript::{default_template, DefaultTemplate, Template};
226+
227+
#[derive(Template)]
228+
#[default_template("./scripts/toggle-devtools.js")]
229+
struct Devtools<'a> {
230+
os_name: &'a str,
231+
}
232+
233+
init_script.push_str(
234+
&Devtools {
235+
os_name: std::env::consts::OS,
236+
}
237+
.render_default(&Default::default())
238+
.unwrap()
239+
.into_string(),
240+
);
241+
}
242+
243+
Builder::new("window")
244+
.js_init_script(init_script)
245+
.invoke_handler(|invoke| {
246+
#[cfg(desktop)]
247+
{
248+
let handler: Box<dyn Fn(crate::ipc::Invoke<R>) -> bool> =
249+
Box::new(crate::generate_handler![
250+
desktop_commands::create,
251+
// getters
252+
desktop_commands::scale_factor,
253+
desktop_commands::inner_position,
254+
desktop_commands::outer_position,
255+
desktop_commands::inner_size,
256+
desktop_commands::outer_size,
257+
desktop_commands::is_fullscreen,
258+
desktop_commands::is_minimized,
259+
desktop_commands::is_maximized,
260+
desktop_commands::is_focused,
261+
desktop_commands::is_decorated,
262+
desktop_commands::is_resizable,
263+
desktop_commands::is_maximizable,
264+
desktop_commands::is_minimizable,
265+
desktop_commands::is_closable,
266+
desktop_commands::is_visible,
267+
desktop_commands::title,
268+
desktop_commands::current_monitor,
269+
desktop_commands::primary_monitor,
270+
desktop_commands::available_monitors,
271+
desktop_commands::theme,
272+
// setters
273+
desktop_commands::center,
274+
desktop_commands::request_user_attention,
275+
desktop_commands::set_resizable,
276+
desktop_commands::set_maximizable,
277+
desktop_commands::set_minimizable,
278+
desktop_commands::set_closable,
279+
desktop_commands::set_title,
280+
desktop_commands::maximize,
281+
desktop_commands::unmaximize,
282+
desktop_commands::minimize,
283+
desktop_commands::unminimize,
284+
desktop_commands::show,
285+
desktop_commands::hide,
286+
desktop_commands::close,
287+
desktop_commands::set_decorations,
288+
desktop_commands::set_shadow,
289+
desktop_commands::set_effects,
290+
desktop_commands::set_always_on_top,
291+
desktop_commands::set_content_protected,
292+
desktop_commands::set_size,
293+
desktop_commands::set_min_size,
294+
desktop_commands::set_max_size,
295+
desktop_commands::set_position,
296+
desktop_commands::set_fullscreen,
297+
desktop_commands::set_focus,
298+
desktop_commands::set_skip_taskbar,
299+
desktop_commands::set_cursor_grab,
300+
desktop_commands::set_cursor_visible,
301+
desktop_commands::set_cursor_icon,
302+
desktop_commands::set_cursor_position,
303+
desktop_commands::set_ignore_cursor_events,
304+
desktop_commands::start_dragging,
305+
desktop_commands::print,
306+
desktop_commands::set_icon,
307+
desktop_commands::toggle_maximize,
308+
desktop_commands::internal_toggle_maximize,
309+
#[cfg(any(debug_assertions, feature = "devtools"))]
310+
desktop_commands::internal_toggle_devtools,
311+
]);
312+
#[allow(clippy::needless_return)]
313+
return handler(invoke);
314+
}
315+
#[cfg(mobile)]
316+
{
317+
invoke.resolver.reject("Window API not available on mobile");
318+
return true;
319+
}
320+
})
321+
.build()
322+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
2+
// SPDX-License-Identifier: Apache-2.0
3+
// SPDX-License-Identifier: MIT
4+
5+
document.addEventListener("mousedown", (e) => {
6+
if (e.target.hasAttribute("data-tauri-drag-region") && e.button === 0) {
7+
// prevents text cursor
8+
e.preventDefault();
9+
// fix #2549: double click on drag region edge causes content to maximize without window sizing change
10+
// https://github.com/tauri-apps/tauri/issues/2549#issuecomment-1250036908
11+
e.stopImmediatePropagation();
12+
13+
// start dragging if the element has a `tauri-drag-region` data attribute and maximize on double-clicking it
14+
const cmd = e.detail === 2 ? "internal_toggle_maximize" : "start_dragging";
15+
window.__TAURI_INVOKE__("plugin:window|" + cmd);
16+
}
17+
});
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
2+
// SPDX-License-Identifier: Apache-2.0
3+
// SPDX-License-Identifier: MIT
4+
5+
window.print = function () {
6+
return window.__TAURI_INVOKE__("plugin:window|print");
7+
};

0 commit comments

Comments
 (0)