mirror of
https://github.com/tauri-apps/tauri.git
synced 2024-12-01 03:02:28 +03:00
feat: move window
plugin back to core (#8007)
Co-authored-by: Lucas Nogueira <lucas@tauri.studio>
This commit is contained in:
parent
f12306af5f
commit
c9a9246c37
5
.changes/api-window.md
Normal file
5
.changes/api-window.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
"@tauri-apps/api": 'minor:feat'
|
||||
---
|
||||
|
||||
Add the `window` module back
|
5
.changes/window-plugin-core.md
Normal file
5
.changes/window-plugin-core.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
"tauri": patch:changes
|
||||
---
|
||||
|
||||
Added the `window` plugin back into core.
|
File diff suppressed because one or more lines are too long
@ -807,6 +807,7 @@ impl<R: Runtime> App<R> {
|
||||
fn register_core_plugins(&self) -> crate::Result<()> {
|
||||
self.handle.plugin(crate::path::init())?;
|
||||
self.handle.plugin(crate::event::init())?;
|
||||
self.handle.plugin(crate::window::plugin::init())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -107,4 +107,7 @@ pub enum Error {
|
||||
#[cfg(all(desktop, feature = "tray-icon"))]
|
||||
#[cfg_attr(doc_cfg, doc(cfg(all(desktop, feature = "tray-icon"))))]
|
||||
BadTrayIcon(#[from] tray_icon::BadIcon),
|
||||
/// window not found.
|
||||
#[error("window not found")]
|
||||
WindowNotFound,
|
||||
}
|
||||
|
@ -4,6 +4,8 @@
|
||||
|
||||
//! The Tauri window types and functions.
|
||||
|
||||
pub(crate) mod plugin;
|
||||
|
||||
use http::HeaderMap;
|
||||
pub use tauri_utils::{config::Color, WindowEffect as Effect, WindowEffectState as EffectState};
|
||||
use url::Url;
|
||||
@ -2461,7 +2463,7 @@ impl<R: Runtime> Window<R> {
|
||||
///
|
||||
/// This listener only receives events that are triggered using the
|
||||
/// [`trigger`](Window#method.trigger) and [`emit_and_trigger`](Window#method.emit_and_trigger) methods or
|
||||
/// the `emit` function from the window plugin (`@tauri-apps/plugin-window` package).
|
||||
/// the `emit` function from the window plugin (`@tauri-apps/api/window` package).
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
322
core/tauri/src/window/plugin.rs
Normal file
322
core/tauri/src/window/plugin.rs
Normal file
@ -0,0 +1,322 @@
|
||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
//! The tauri plugin to create and manipulate windows from JS.
|
||||
|
||||
use crate::{
|
||||
plugin::{Builder, TauriPlugin},
|
||||
Runtime,
|
||||
};
|
||||
|
||||
#[cfg(desktop)]
|
||||
mod desktop_commands {
|
||||
use serde::Deserialize;
|
||||
|
||||
use super::*;
|
||||
use crate::{
|
||||
command,
|
||||
utils::config::{WindowConfig, WindowEffectsConfig},
|
||||
AppHandle, CursorIcon, Icon, Manager, Monitor, PhysicalPosition, PhysicalSize, Position, Size,
|
||||
Theme, UserAttentionType, Window, WindowBuilder,
|
||||
};
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum IconDto {
|
||||
#[cfg(any(feature = "icon-png", feature = "icon-ico"))]
|
||||
File(std::path::PathBuf),
|
||||
#[cfg(any(feature = "icon-png", feature = "icon-ico"))]
|
||||
Raw(Vec<u8>),
|
||||
Rgba {
|
||||
rgba: Vec<u8>,
|
||||
width: u32,
|
||||
height: u32,
|
||||
},
|
||||
}
|
||||
|
||||
impl From<IconDto> for Icon {
|
||||
fn from(icon: IconDto) -> Self {
|
||||
match icon {
|
||||
#[cfg(any(feature = "icon-png", feature = "icon-ico"))]
|
||||
IconDto::File(path) => Self::File(path),
|
||||
#[cfg(any(feature = "icon-png", feature = "icon-ico"))]
|
||||
IconDto::Raw(raw) => Self::Raw(raw),
|
||||
IconDto::Rgba {
|
||||
rgba,
|
||||
width,
|
||||
height,
|
||||
} => Self::Rgba {
|
||||
rgba,
|
||||
width,
|
||||
height,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[command(root = "crate")]
|
||||
pub async fn create<R: Runtime>(app: AppHandle<R>, options: WindowConfig) -> crate::Result<()> {
|
||||
WindowBuilder::from_config(&app, options).build()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_window<R: Runtime>(window: Window<R>, label: Option<String>) -> crate::Result<Window<R>> {
|
||||
match label {
|
||||
Some(l) if !l.is_empty() => window.get_window(&l).ok_or(crate::Error::WindowNotFound),
|
||||
_ => Ok(window),
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! getter {
|
||||
($cmd: ident, $ret: ty) => {
|
||||
#[command(root = "crate")]
|
||||
pub async fn $cmd<R: Runtime>(
|
||||
window: Window<R>,
|
||||
label: Option<String>,
|
||||
) -> crate::Result<$ret> {
|
||||
get_window(window, label)?.$cmd().map_err(Into::into)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! setter {
|
||||
($cmd: ident) => {
|
||||
#[command(root = "crate")]
|
||||
pub async fn $cmd<R: Runtime>(window: Window<R>, label: Option<String>) -> crate::Result<()> {
|
||||
get_window(window, label)?.$cmd().map_err(Into::into)
|
||||
}
|
||||
};
|
||||
|
||||
($cmd: ident, $input: ty) => {
|
||||
#[command(root = "crate")]
|
||||
pub async fn $cmd<R: Runtime>(
|
||||
window: Window<R>,
|
||||
label: Option<String>,
|
||||
value: $input,
|
||||
) -> crate::Result<()> {
|
||||
get_window(window, label)?.$cmd(value).map_err(Into::into)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
getter!(scale_factor, f64);
|
||||
getter!(inner_position, PhysicalPosition<i32>);
|
||||
getter!(outer_position, PhysicalPosition<i32>);
|
||||
getter!(inner_size, PhysicalSize<u32>);
|
||||
getter!(outer_size, PhysicalSize<u32>);
|
||||
getter!(is_fullscreen, bool);
|
||||
getter!(is_minimized, bool);
|
||||
getter!(is_maximized, bool);
|
||||
getter!(is_focused, bool);
|
||||
getter!(is_decorated, bool);
|
||||
getter!(is_resizable, bool);
|
||||
getter!(is_maximizable, bool);
|
||||
getter!(is_minimizable, bool);
|
||||
getter!(is_closable, bool);
|
||||
getter!(is_visible, bool);
|
||||
getter!(title, String);
|
||||
getter!(current_monitor, Option<Monitor>);
|
||||
getter!(primary_monitor, Option<Monitor>);
|
||||
getter!(available_monitors, Vec<Monitor>);
|
||||
getter!(theme, Theme);
|
||||
|
||||
setter!(center);
|
||||
setter!(request_user_attention, Option<UserAttentionType>);
|
||||
setter!(set_resizable, bool);
|
||||
setter!(set_maximizable, bool);
|
||||
setter!(set_minimizable, bool);
|
||||
setter!(set_closable, bool);
|
||||
setter!(set_title, &str);
|
||||
setter!(maximize);
|
||||
setter!(unmaximize);
|
||||
setter!(minimize);
|
||||
setter!(unminimize);
|
||||
setter!(show);
|
||||
setter!(hide);
|
||||
setter!(close);
|
||||
setter!(set_decorations, bool);
|
||||
setter!(set_shadow, bool);
|
||||
setter!(set_effects, Option<WindowEffectsConfig>);
|
||||
setter!(set_always_on_top, bool);
|
||||
setter!(set_content_protected, bool);
|
||||
setter!(set_size, Size);
|
||||
setter!(set_min_size, Option<Size>);
|
||||
setter!(set_max_size, Option<Size>);
|
||||
setter!(set_position, Position);
|
||||
setter!(set_fullscreen, bool);
|
||||
setter!(set_focus);
|
||||
setter!(set_skip_taskbar, bool);
|
||||
setter!(set_cursor_grab, bool);
|
||||
setter!(set_cursor_visible, bool);
|
||||
setter!(set_cursor_icon, CursorIcon);
|
||||
setter!(set_cursor_position, Position);
|
||||
setter!(set_ignore_cursor_events, bool);
|
||||
setter!(start_dragging);
|
||||
setter!(print);
|
||||
|
||||
#[command(root = "crate")]
|
||||
pub async fn set_icon<R: Runtime>(
|
||||
window: Window<R>,
|
||||
label: Option<String>,
|
||||
value: IconDto,
|
||||
) -> crate::Result<()> {
|
||||
get_window(window, label)?
|
||||
.set_icon(value.into())
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
#[command(root = "crate")]
|
||||
pub async fn toggle_maximize<R: Runtime>(
|
||||
window: Window<R>,
|
||||
label: Option<String>,
|
||||
) -> crate::Result<()> {
|
||||
let window = get_window(window, label)?;
|
||||
match window.is_maximized()? {
|
||||
true => window.unmaximize()?,
|
||||
false => window.maximize()?,
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[command(root = "crate")]
|
||||
pub async fn internal_toggle_maximize<R: Runtime>(
|
||||
window: Window<R>,
|
||||
label: Option<String>,
|
||||
) -> crate::Result<()> {
|
||||
let window = get_window(window, label)?;
|
||||
if window.is_resizable()? {
|
||||
match window.is_maximized()? {
|
||||
true => window.unmaximize()?,
|
||||
false => window.maximize()?,
|
||||
};
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(any(debug_assertions, feature = "devtools"))]
|
||||
#[command(root = "crate")]
|
||||
pub async fn internal_toggle_devtools<R: Runtime>(
|
||||
window: Window<R>,
|
||||
label: Option<String>,
|
||||
) -> crate::Result<()> {
|
||||
let window = get_window(window, label)?;
|
||||
if window.is_devtools_open() {
|
||||
window.close_devtools();
|
||||
} else {
|
||||
window.open_devtools();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Initializes the plugin.
|
||||
pub fn init<R: Runtime>() -> TauriPlugin<R> {
|
||||
let mut init_script = String::new();
|
||||
// window.print works on Linux/Windows; need to use the API on macOS
|
||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||
{
|
||||
init_script.push_str(include_str!("./scripts/print.js"));
|
||||
}
|
||||
init_script.push_str(include_str!("./scripts/drag.js"));
|
||||
|
||||
#[cfg(any(debug_assertions, feature = "devtools"))]
|
||||
{
|
||||
use serialize_to_javascript::{default_template, DefaultTemplate, Template};
|
||||
|
||||
#[derive(Template)]
|
||||
#[default_template("./scripts/toggle-devtools.js")]
|
||||
struct Devtools<'a> {
|
||||
os_name: &'a str,
|
||||
}
|
||||
|
||||
init_script.push_str(
|
||||
&Devtools {
|
||||
os_name: std::env::consts::OS,
|
||||
}
|
||||
.render_default(&Default::default())
|
||||
.unwrap()
|
||||
.into_string(),
|
||||
);
|
||||
}
|
||||
|
||||
Builder::new("window")
|
||||
.js_init_script(init_script)
|
||||
.invoke_handler(|invoke| {
|
||||
#[cfg(desktop)]
|
||||
{
|
||||
let handler: Box<dyn Fn(crate::ipc::Invoke<R>) -> bool> =
|
||||
Box::new(crate::generate_handler![
|
||||
desktop_commands::create,
|
||||
// getters
|
||||
desktop_commands::scale_factor,
|
||||
desktop_commands::inner_position,
|
||||
desktop_commands::outer_position,
|
||||
desktop_commands::inner_size,
|
||||
desktop_commands::outer_size,
|
||||
desktop_commands::is_fullscreen,
|
||||
desktop_commands::is_minimized,
|
||||
desktop_commands::is_maximized,
|
||||
desktop_commands::is_focused,
|
||||
desktop_commands::is_decorated,
|
||||
desktop_commands::is_resizable,
|
||||
desktop_commands::is_maximizable,
|
||||
desktop_commands::is_minimizable,
|
||||
desktop_commands::is_closable,
|
||||
desktop_commands::is_visible,
|
||||
desktop_commands::title,
|
||||
desktop_commands::current_monitor,
|
||||
desktop_commands::primary_monitor,
|
||||
desktop_commands::available_monitors,
|
||||
desktop_commands::theme,
|
||||
// setters
|
||||
desktop_commands::center,
|
||||
desktop_commands::request_user_attention,
|
||||
desktop_commands::set_resizable,
|
||||
desktop_commands::set_maximizable,
|
||||
desktop_commands::set_minimizable,
|
||||
desktop_commands::set_closable,
|
||||
desktop_commands::set_title,
|
||||
desktop_commands::maximize,
|
||||
desktop_commands::unmaximize,
|
||||
desktop_commands::minimize,
|
||||
desktop_commands::unminimize,
|
||||
desktop_commands::show,
|
||||
desktop_commands::hide,
|
||||
desktop_commands::close,
|
||||
desktop_commands::set_decorations,
|
||||
desktop_commands::set_shadow,
|
||||
desktop_commands::set_effects,
|
||||
desktop_commands::set_always_on_top,
|
||||
desktop_commands::set_content_protected,
|
||||
desktop_commands::set_size,
|
||||
desktop_commands::set_min_size,
|
||||
desktop_commands::set_max_size,
|
||||
desktop_commands::set_position,
|
||||
desktop_commands::set_fullscreen,
|
||||
desktop_commands::set_focus,
|
||||
desktop_commands::set_skip_taskbar,
|
||||
desktop_commands::set_cursor_grab,
|
||||
desktop_commands::set_cursor_visible,
|
||||
desktop_commands::set_cursor_icon,
|
||||
desktop_commands::set_cursor_position,
|
||||
desktop_commands::set_ignore_cursor_events,
|
||||
desktop_commands::start_dragging,
|
||||
desktop_commands::print,
|
||||
desktop_commands::set_icon,
|
||||
desktop_commands::toggle_maximize,
|
||||
desktop_commands::internal_toggle_maximize,
|
||||
#[cfg(any(debug_assertions, feature = "devtools"))]
|
||||
desktop_commands::internal_toggle_devtools,
|
||||
]);
|
||||
#[allow(clippy::needless_return)]
|
||||
return handler(invoke);
|
||||
}
|
||||
#[cfg(mobile)]
|
||||
{
|
||||
invoke.resolver.reject("Window API not available on mobile");
|
||||
return true;
|
||||
}
|
||||
})
|
||||
.build()
|
||||
}
|
17
core/tauri/src/window/scripts/drag.js
Normal file
17
core/tauri/src/window/scripts/drag.js
Normal file
@ -0,0 +1,17 @@
|
||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
document.addEventListener("mousedown", (e) => {
|
||||
if (e.target.hasAttribute("data-tauri-drag-region") && e.button === 0) {
|
||||
// prevents text cursor
|
||||
e.preventDefault();
|
||||
// fix #2549: double click on drag region edge causes content to maximize without window sizing change
|
||||
// https://github.com/tauri-apps/tauri/issues/2549#issuecomment-1250036908
|
||||
e.stopImmediatePropagation();
|
||||
|
||||
// start dragging if the element has a `tauri-drag-region` data attribute and maximize on double-clicking it
|
||||
const cmd = e.detail === 2 ? "internal_toggle_maximize" : "start_dragging";
|
||||
window.__TAURI_INVOKE__("plugin:window|" + cmd);
|
||||
}
|
||||
});
|
7
core/tauri/src/window/scripts/print.js
Normal file
7
core/tauri/src/window/scripts/print.js
Normal file
@ -0,0 +1,7 @@
|
||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
window.print = function () {
|
||||
return window.__TAURI_INVOKE__("plugin:window|print");
|
||||
};
|
29
core/tauri/src/window/scripts/toggle-devtools.js
Normal file
29
core/tauri/src/window/scripts/toggle-devtools.js
Normal file
@ -0,0 +1,29 @@
|
||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
;(function () {
|
||||
function toggleDevtoolsHotkey() {
|
||||
const osName = __TEMPLATE_os_name__
|
||||
|
||||
const isHotkey =
|
||||
osName === 'macos'
|
||||
? (event) => event.metaKey && event.altKey && event.key === 'I'
|
||||
: (event) => event.ctrlKey && event.shiftKey && event.key === 'I'
|
||||
|
||||
document.addEventListener('keydown', (event) => {
|
||||
if (isHotkey(event)) {
|
||||
window.__TAURI_INVOKE__('plugin:window|internal_toggle_devtools')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (
|
||||
document.readyState === 'complete' ||
|
||||
document.readyState === 'interactive'
|
||||
) {
|
||||
toggleDevtoolsHotkey()
|
||||
} else {
|
||||
window.addEventListener('DOMContentLoaded', toggleDevtoolsHotkey, true)
|
||||
}
|
||||
})()
|
2
examples/api/dist/assets/index.css
vendored
2
examples/api/dist/assets/index.css
vendored
File diff suppressed because one or more lines are too long
43
examples/api/dist/assets/index.js
vendored
43
examples/api/dist/assets/index.js
vendored
File diff suppressed because one or more lines are too long
4
examples/api/src-tauri/Cargo.lock
generated
4
examples/api/src-tauri/Cargo.lock
generated
@ -3333,9 +3333,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tao"
|
||||
version = "0.22.0"
|
||||
version = "0.22.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "60279ecb16c33a6cef45cd37a9602455c190942d20e360bd8499bff49f2a48f3"
|
||||
checksum = "f5d30690a6746dfbb26d4f41522757da1ebfd277514b58e27a80b7e55e511e52"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"cairo-rs",
|
||||
|
@ -5,6 +5,7 @@
|
||||
|
||||
import Welcome from './views/Welcome.svelte'
|
||||
import Communication from './views/Communication.svelte'
|
||||
import Window from './views/Window.svelte'
|
||||
import WebRTC from './views/WebRTC.svelte'
|
||||
|
||||
document.addEventListener('keydown', (event) => {
|
||||
@ -24,6 +25,11 @@
|
||||
component: Communication,
|
||||
icon: 'i-codicon-radio-tower'
|
||||
},
|
||||
{
|
||||
label: 'Window',
|
||||
component: Window,
|
||||
icon: 'i-codicon-window'
|
||||
},
|
||||
{
|
||||
label: 'WebRTC',
|
||||
component: WebRTC,
|
||||
|
611
examples/api/src/views/Window.svelte
Normal file
611
examples/api/src/views/Window.svelte
Normal file
@ -0,0 +1,611 @@
|
||||
<script>
|
||||
import {
|
||||
getCurrent,
|
||||
LogicalSize,
|
||||
UserAttentionType,
|
||||
PhysicalSize,
|
||||
PhysicalPosition,
|
||||
Effect,
|
||||
EffectState,
|
||||
Window
|
||||
} from "@tauri-apps/api/window";
|
||||
import { invoke } from "@tauri-apps/api/tauri";
|
||||
|
||||
const appWindow = getCurrent();
|
||||
|
||||
let selectedWindow = appWindow.label;
|
||||
const windowMap = {
|
||||
[appWindow.label]: appWindow,
|
||||
};
|
||||
|
||||
const cursorIconOptions = [
|
||||
"default",
|
||||
"crosshair",
|
||||
"hand",
|
||||
"arrow",
|
||||
"move",
|
||||
"text",
|
||||
"wait",
|
||||
"help",
|
||||
"progress",
|
||||
// something cannot be done
|
||||
"notAllowed",
|
||||
"contextMenu",
|
||||
"cell",
|
||||
"verticalText",
|
||||
"alias",
|
||||
"copy",
|
||||
"noDrop",
|
||||
// something can be grabbed
|
||||
"grab",
|
||||
/// something is grabbed
|
||||
"grabbing",
|
||||
"allScroll",
|
||||
"zoomIn",
|
||||
"zoomOut",
|
||||
// edge is to be moved
|
||||
"eResize",
|
||||
"nResize",
|
||||
"neResize",
|
||||
"nwResize",
|
||||
"sResize",
|
||||
"seResize",
|
||||
"swResize",
|
||||
"wResize",
|
||||
"ewResize",
|
||||
"nsResize",
|
||||
"neswResize",
|
||||
"nwseResize",
|
||||
"colResize",
|
||||
"rowResize",
|
||||
];
|
||||
|
||||
const windowsEffects = ["mica", "blur", "acrylic"];
|
||||
const isWindows = navigator.appVersion.includes("windows");
|
||||
const isMacOS = navigator.appVersion.includes("macos");
|
||||
let effectOptions = isWindows
|
||||
? windowsEffects
|
||||
: Object.keys(Effect)
|
||||
.map((effect) => Effect[effect])
|
||||
.filter((e) => !windowsEffects.includes(e));
|
||||
const effectStateOptions = Object.keys(EffectState).map(
|
||||
(state) => EffectState[state]
|
||||
);
|
||||
|
||||
export let onMessage;
|
||||
const mainEl = document.querySelector("main");
|
||||
|
||||
let newWindowLabel;
|
||||
|
||||
let urlValue = "https://tauri.app";
|
||||
let resizable = true;
|
||||
let maximizable = true;
|
||||
let minimizable = true;
|
||||
let closable = true;
|
||||
let maximized = false;
|
||||
let decorations = true;
|
||||
let alwaysOnTop = false;
|
||||
let contentProtected = true;
|
||||
let fullscreen = false;
|
||||
let width = null;
|
||||
let height = null;
|
||||
let minWidth = null;
|
||||
let minHeight = null;
|
||||
let maxWidth = null;
|
||||
let maxHeight = null;
|
||||
let x = null;
|
||||
let y = null;
|
||||
let scaleFactor = 1;
|
||||
let innerPosition = new PhysicalPosition(x, y);
|
||||
let outerPosition = new PhysicalPosition(x, y);
|
||||
let innerSize = new PhysicalSize(width, height);
|
||||
let outerSize = new PhysicalSize(width, height);
|
||||
let resizeEventUnlisten;
|
||||
let moveEventUnlisten;
|
||||
let cursorGrab = false;
|
||||
let cursorVisible = true;
|
||||
let cursorX = null;
|
||||
let cursorY = null;
|
||||
let cursorIcon = "default";
|
||||
let cursorIgnoreEvents = false;
|
||||
let windowTitle = "Awesome Tauri Example!";
|
||||
|
||||
let effects = [];
|
||||
let selectedEffect;
|
||||
let effectState;
|
||||
let effectRadius;
|
||||
let effectR, effectG, effectB, effectA;
|
||||
|
||||
let windowIconPath;
|
||||
|
||||
function setTitle_() {
|
||||
windowMap[selectedWindow].setTitle(windowTitle);
|
||||
}
|
||||
|
||||
function hide_() {
|
||||
windowMap[selectedWindow].hide();
|
||||
setTimeout(windowMap[selectedWindow].show, 2000);
|
||||
}
|
||||
|
||||
function minimize_() {
|
||||
windowMap[selectedWindow].minimize();
|
||||
setTimeout(windowMap[selectedWindow].unminimize, 2000);
|
||||
}
|
||||
|
||||
function changeIcon() {
|
||||
windowMap[selectedWindow].setIcon(path);
|
||||
}
|
||||
|
||||
function createWindow() {
|
||||
if (!newWindowLabel) return;
|
||||
|
||||
const webview = new Window(newWindowLabel);
|
||||
windowMap[newWindowLabel] = webview;
|
||||
webview.once("tauri://error", function () {
|
||||
onMessage("Error creating new webview");
|
||||
});
|
||||
}
|
||||
|
||||
function loadWindowSize() {
|
||||
windowMap[selectedWindow].innerSize().then((response) => {
|
||||
innerSize = response;
|
||||
width = innerSize.width;
|
||||
height = innerSize.height;
|
||||
});
|
||||
windowMap[selectedWindow].outerSize().then((response) => {
|
||||
outerSize = response;
|
||||
});
|
||||
}
|
||||
|
||||
function loadWindowPosition() {
|
||||
windowMap[selectedWindow].innerPosition().then((response) => {
|
||||
innerPosition = response;
|
||||
});
|
||||
windowMap[selectedWindow].outerPosition().then((response) => {
|
||||
outerPosition = response;
|
||||
x = outerPosition.x;
|
||||
y = outerPosition.y;
|
||||
});
|
||||
}
|
||||
|
||||
async function addWindowEventListeners(window) {
|
||||
if (!window) return;
|
||||
if (resizeEventUnlisten) {
|
||||
resizeEventUnlisten();
|
||||
}
|
||||
if (moveEventUnlisten) {
|
||||
moveEventUnlisten();
|
||||
}
|
||||
moveEventUnlisten = await window.listen("tauri://move", loadWindowPosition);
|
||||
resizeEventUnlisten = await window.listen("tauri://resize", loadWindowSize);
|
||||
}
|
||||
|
||||
async function requestUserAttention_() {
|
||||
await windowMap[selectedWindow].minimize();
|
||||
await windowMap[selectedWindow].requestUserAttention(
|
||||
UserAttentionType.Critical
|
||||
);
|
||||
await new Promise((resolve) => setTimeout(resolve, 3000));
|
||||
await windowMap[selectedWindow].requestUserAttention(null);
|
||||
}
|
||||
|
||||
async function addEffect() {
|
||||
if (!effects.includes(selectedEffect)) {
|
||||
effects = [...effects, selectedEffect];
|
||||
}
|
||||
|
||||
const payload = {
|
||||
effects,
|
||||
state: effectState,
|
||||
radius: effectRadius,
|
||||
};
|
||||
if (
|
||||
Number.isInteger(effectR) &&
|
||||
Number.isInteger(effectG) &&
|
||||
Number.isInteger(effectB) &&
|
||||
Number.isInteger(effectA)
|
||||
) {
|
||||
payload.color = [effectR, effectG, effectB, effectA];
|
||||
}
|
||||
|
||||
mainEl.classList.remove("bg-primary");
|
||||
mainEl.classList.remove("dark:bg-darkPrimary");
|
||||
await windowMap[selectedWindow].clearEffects();
|
||||
await windowMap[selectedWindow].setEffects(payload);
|
||||
}
|
||||
|
||||
async function clearEffects() {
|
||||
effects = [];
|
||||
await windowMap[selectedWindow].clearEffects();
|
||||
mainEl.classList.add("bg-primary");
|
||||
mainEl.classList.add("dark:bg-darkPrimary");
|
||||
}
|
||||
|
||||
$: {
|
||||
windowMap[selectedWindow];
|
||||
loadWindowPosition();
|
||||
loadWindowSize();
|
||||
}
|
||||
$: windowMap[selectedWindow]?.setResizable(resizable);
|
||||
$: windowMap[selectedWindow]?.setMaximizable(maximizable);
|
||||
$: windowMap[selectedWindow]?.setMinimizable(minimizable);
|
||||
$: windowMap[selectedWindow]?.setClosable(closable);
|
||||
$: maximized
|
||||
? windowMap[selectedWindow]?.maximize()
|
||||
: windowMap[selectedWindow]?.unmaximize();
|
||||
$: windowMap[selectedWindow]?.setDecorations(decorations);
|
||||
$: windowMap[selectedWindow]?.setAlwaysOnTop(alwaysOnTop);
|
||||
$: windowMap[selectedWindow]?.setContentProtected(contentProtected);
|
||||
$: windowMap[selectedWindow]?.setFullscreen(fullscreen);
|
||||
|
||||
$: width &&
|
||||
height &&
|
||||
windowMap[selectedWindow]?.setSize(new PhysicalSize(width, height));
|
||||
$: minWidth && minHeight
|
||||
? windowMap[selectedWindow]?.setMinSize(
|
||||
new LogicalSize(minWidth, minHeight)
|
||||
)
|
||||
: windowMap[selectedWindow]?.setMinSize(null);
|
||||
$: maxWidth > 800 && maxHeight > 400
|
||||
? windowMap[selectedWindow]?.setMaxSize(
|
||||
new LogicalSize(maxWidth, maxHeight)
|
||||
)
|
||||
: windowMap[selectedWindow]?.setMaxSize(null);
|
||||
$: x !== null &&
|
||||
y !== null &&
|
||||
windowMap[selectedWindow]?.setPosition(new PhysicalPosition(x, y));
|
||||
$: windowMap[selectedWindow]
|
||||
?.scaleFactor()
|
||||
.then((factor) => (scaleFactor = factor));
|
||||
$: addWindowEventListeners(windowMap[selectedWindow]);
|
||||
|
||||
$: windowMap[selectedWindow]?.setCursorGrab(cursorGrab);
|
||||
$: windowMap[selectedWindow]?.setCursorVisible(cursorVisible);
|
||||
$: windowMap[selectedWindow]?.setCursorIcon(cursorIcon);
|
||||
$: cursorX !== null &&
|
||||
cursorY !== null &&
|
||||
windowMap[selectedWindow]?.setCursorPosition(
|
||||
new PhysicalPosition(cursorX, cursorY)
|
||||
);
|
||||
$: windowMap[selectedWindow]?.setIgnoreCursorEvents(cursorIgnoreEvents);
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col children:grow gap-2">
|
||||
<div class="flex gap-1">
|
||||
<input
|
||||
class="input grow"
|
||||
type="text"
|
||||
placeholder="New Window label.."
|
||||
bind:value={newWindowLabel}
|
||||
/>
|
||||
<button class="btn" on:click={createWindow}>New window</button>
|
||||
</div>
|
||||
<br />
|
||||
{#if Object.keys(windowMap).length >= 1}
|
||||
<span class="font-700 text-sm">Selected window:</span>
|
||||
<select class="input" bind:value={selectedWindow}>
|
||||
<option value="" disabled selected>Choose a window...</option>
|
||||
{#each Object.keys(windowMap) as label}
|
||||
<option value={label}>{label}</option>
|
||||
{/each}
|
||||
</select>
|
||||
{/if}
|
||||
{#if windowMap[selectedWindow]}
|
||||
<br />
|
||||
<div class="flex gap-1 items-center">
|
||||
<label>
|
||||
Icon path
|
||||
</label>
|
||||
<form class="flex gap-1 grow" on:submit|preventDefault={setTitle_}>
|
||||
<input class="input grow" bind:value={windowIconPath} />
|
||||
<button class="btn" type="submit"> Change window icon </button>
|
||||
</form>
|
||||
</div>
|
||||
<br />
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<button
|
||||
class="btn"
|
||||
title="Unminimizes after 2 seconds"
|
||||
on:click={() => windowMap[selectedWindow].center()}
|
||||
>
|
||||
Center
|
||||
</button>
|
||||
<button
|
||||
class="btn"
|
||||
title="Unminimizes after 2 seconds"
|
||||
on:click={minimize_}
|
||||
>
|
||||
Minimize
|
||||
</button>
|
||||
<button
|
||||
class="btn"
|
||||
title="Visible again after 2 seconds"
|
||||
on:click={hide_}
|
||||
>
|
||||
Hide
|
||||
</button>
|
||||
<button
|
||||
class="btn"
|
||||
on:click={requestUserAttention_}
|
||||
title="Minimizes the window, requests attention for 3s and then resets it"
|
||||
>Request attention</button
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<label>
|
||||
Maximized
|
||||
<input type="checkbox" bind:checked={maximized} />
|
||||
</label>
|
||||
<label>
|
||||
Resizable
|
||||
<input type="checkbox" bind:checked={resizable} />
|
||||
</label>
|
||||
<label>
|
||||
Maximizable
|
||||
<input type="checkbox" bind:checked={maximizable} />
|
||||
</label>
|
||||
<label>
|
||||
Minimizable
|
||||
<input type="checkbox" bind:checked={minimizable} />
|
||||
</label>
|
||||
<label>
|
||||
Closable
|
||||
<input type="checkbox" bind:checked={closable} />
|
||||
</label>
|
||||
<label>
|
||||
Has decorations
|
||||
<input type="checkbox" bind:checked={decorations} />
|
||||
</label>
|
||||
<label>
|
||||
Always on top
|
||||
<input type="checkbox" bind:checked={alwaysOnTop} />
|
||||
</label>
|
||||
<label>
|
||||
Content protected
|
||||
<input type="checkbox" bind:checked={contentProtected} />
|
||||
</label>
|
||||
<label>
|
||||
Fullscreen
|
||||
<input type="checkbox" bind:checked={fullscreen} />
|
||||
</label>
|
||||
</div>
|
||||
<br />
|
||||
<div class="flex flex-row gap-2 flex-wrap">
|
||||
<div class="flex children:grow flex-col">
|
||||
<div>
|
||||
X
|
||||
<input class="input" type="number" bind:value={x} min="0" />
|
||||
</div>
|
||||
<div>
|
||||
Y
|
||||
<input class="input" type="number" bind:value={y} min="0" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex children:grow flex-col">
|
||||
<div>
|
||||
Width
|
||||
<input class="input" type="number" bind:value={width} min="400" />
|
||||
</div>
|
||||
<div>
|
||||
Height
|
||||
<input class="input" type="number" bind:value={height} min="400" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex children:grow flex-col">
|
||||
<div>
|
||||
Min width
|
||||
<input class="input" type="number" bind:value={minWidth} />
|
||||
</div>
|
||||
<div>
|
||||
Min height
|
||||
<input class="input" type="number" bind:value={minHeight} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex children:grow flex-col">
|
||||
<div>
|
||||
Max width
|
||||
<input class="input" type="number" bind:value={maxWidth} min="800" />
|
||||
</div>
|
||||
<div>
|
||||
Max height
|
||||
<input class="input" type="number" bind:value={maxHeight} min="400" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<div>
|
||||
<div class="flex">
|
||||
<div class="grow">
|
||||
<div class="text-accent dark:text-darkAccent font-700">
|
||||
Inner Size
|
||||
</div>
|
||||
<span>Width: {innerSize.width}</span>
|
||||
<span>Height: {innerSize.height}</span>
|
||||
</div>
|
||||
<div class="grow">
|
||||
<div class="text-accent dark:text-darkAccent font-700">
|
||||
Outer Size
|
||||
</div>
|
||||
<span>Width: {outerSize.width}</span>
|
||||
<span>Height: {outerSize.height}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<div class="grow">
|
||||
<div class="text-accent dark:text-darkAccent font-700">
|
||||
Inner Logical Size
|
||||
</div>
|
||||
<span>Width: {innerSize.toLogical(scaleFactor).width}</span>
|
||||
<span>Height: {innerSize.toLogical(scaleFactor).height}</span>
|
||||
</div>
|
||||
<div class="grow">
|
||||
<div class="text-accent dark:text-darkAccent font-700">
|
||||
Outer Logical Size
|
||||
</div>
|
||||
<span>Width: {outerSize.toLogical(scaleFactor).width}</span>
|
||||
<span>Height: {outerSize.toLogical(scaleFactor).height}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<div class="grow">
|
||||
<div class="text-accent dark:text-darkAccent font-700">
|
||||
Inner Position
|
||||
</div>
|
||||
<span>x: {innerPosition.x}</span>
|
||||
<span>y: {innerPosition.y}</span>
|
||||
</div>
|
||||
<div class="grow">
|
||||
<div class="text-accent dark:text-darkAccent font-700">
|
||||
Outer Position
|
||||
</div>
|
||||
<span>x: {outerPosition.x}</span>
|
||||
<span>y: {outerPosition.y}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<div class="grow">
|
||||
<div class="text-accent dark:text-darkAccent font-700">
|
||||
Inner Logical Position
|
||||
</div>
|
||||
<span>x: {innerPosition.toLogical(scaleFactor).x}</span>
|
||||
<span>y: {innerPosition.toLogical(scaleFactor).y}</span>
|
||||
</div>
|
||||
<div class="grow">
|
||||
<div class="text-accent dark:text-darkAccent font-700">
|
||||
Outer Logical Position
|
||||
</div>
|
||||
<span>x: {outerPosition.toLogical(scaleFactor).x}</span>
|
||||
<span>y: {outerPosition.toLogical(scaleFactor).y}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<h4 class="mb-2">Cursor</h4>
|
||||
<div class="flex gap-2">
|
||||
<label>
|
||||
<input type="checkbox" bind:checked={cursorGrab} />
|
||||
Grab
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" bind:checked={cursorVisible} />
|
||||
Visible
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" bind:checked={cursorIgnoreEvents} />
|
||||
Ignore events
|
||||
</label>
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<label>
|
||||
Icon
|
||||
<select class="input" bind:value={cursorIcon}>
|
||||
{#each cursorIconOptions as kind}
|
||||
<option value={kind}>{kind}</option>
|
||||
{/each}
|
||||
</select>
|
||||
</label>
|
||||
<label>
|
||||
X position
|
||||
<input class="input" type="number" bind:value={cursorX} />
|
||||
</label>
|
||||
<label>
|
||||
Y position
|
||||
<input class="input" type="number" bind:value={cursorY} />
|
||||
</label>
|
||||
</div>
|
||||
<br />
|
||||
<div class="flex flex-col gap-1">
|
||||
<form class="flex gap-1" on:submit|preventDefault={setTitle_}>
|
||||
<input class="input grow" id="title" bind:value={windowTitle} />
|
||||
<button class="btn" type="submit">Set title</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<br />
|
||||
|
||||
{#if isWindows || isMacOS}
|
||||
<div class="flex flex-col gap-1">
|
||||
<div class="flex">
|
||||
<label>
|
||||
Effect
|
||||
<select class="input" bind:value={selectedEffect}>
|
||||
{#each effectOptions as effect}
|
||||
<option value={effect}>{effect}</option>
|
||||
{/each}
|
||||
</select>
|
||||
</label>
|
||||
|
||||
<label>
|
||||
State
|
||||
<select class="input" bind:value={effectState}>
|
||||
{#each effectStateOptions as state}
|
||||
<option value={state}>{state}</option>
|
||||
{/each}
|
||||
</select>
|
||||
</label>
|
||||
|
||||
<label>
|
||||
Radius
|
||||
<input class="input" type="number" bind:value={effectRadius} />
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="flex">
|
||||
<label>
|
||||
Color
|
||||
<div class="flex">
|
||||
<input
|
||||
style="max-width: 120px;"
|
||||
class="input"
|
||||
type="number"
|
||||
placeholder="R"
|
||||
bind:value={effectR}
|
||||
/>
|
||||
<input
|
||||
style="max-width: 120px;"
|
||||
class="input"
|
||||
type="number"
|
||||
placeholder="G"
|
||||
bind:value={effectG}
|
||||
/>
|
||||
<input
|
||||
style="max-width: 120px;"
|
||||
class="input"
|
||||
type="number"
|
||||
placeholder="B"
|
||||
bind:value={effectB}
|
||||
/>
|
||||
<input
|
||||
style="max-width: 120px;"
|
||||
class="input"
|
||||
type="number"
|
||||
placeholder="A"
|
||||
bind:value={effectA}
|
||||
/>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="flex">
|
||||
<button class="btn" style="width: 80px;" on:click={addEffect}
|
||||
>Add</button
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="flex">
|
||||
<div>
|
||||
Applied effects: {effects.length ? effects.join(",") : "None"}
|
||||
</div>
|
||||
|
||||
<button class="btn" style="width: 80px;" on:click={clearEffects}
|
||||
>Clear</button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
File diff suppressed because one or more lines are too long
99
tooling/api/src/dpi.ts
Normal file
99
tooling/api/src/dpi.ts
Normal file
@ -0,0 +1,99 @@
|
||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
/**
|
||||
* A size represented in logical pixels.
|
||||
*
|
||||
* @since 2.0.0
|
||||
*/
|
||||
class LogicalSize {
|
||||
type = 'Logical'
|
||||
width: number
|
||||
height: number
|
||||
|
||||
constructor(width: number, height: number) {
|
||||
this.width = width
|
||||
this.height = height
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A size represented in physical pixels.
|
||||
*
|
||||
* @since 2.0.0
|
||||
*/
|
||||
class PhysicalSize {
|
||||
type = 'Physical'
|
||||
width: number
|
||||
height: number
|
||||
|
||||
constructor(width: number, height: number) {
|
||||
this.width = width
|
||||
this.height = height
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the physical size to a logical one.
|
||||
* @example
|
||||
* ```typescript
|
||||
* import { getCurrent } from '@tauri-apps/api/window';
|
||||
* const appWindow = getCurrent();
|
||||
* const factor = await appWindow.scaleFactor();
|
||||
* const size = await appWindow.innerSize();
|
||||
* const logical = size.toLogical(factor);
|
||||
* ```
|
||||
* */
|
||||
toLogical(scaleFactor: number): LogicalSize {
|
||||
return new LogicalSize(this.width / scaleFactor, this.height / scaleFactor)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A position represented in logical pixels.
|
||||
*
|
||||
* @since 2.0.0
|
||||
*/
|
||||
class LogicalPosition {
|
||||
type = 'Logical'
|
||||
x: number
|
||||
y: number
|
||||
|
||||
constructor(x: number, y: number) {
|
||||
this.x = x
|
||||
this.y = y
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A position represented in physical pixels.
|
||||
*
|
||||
* @since 2.0.0
|
||||
*/
|
||||
class PhysicalPosition {
|
||||
type = 'Physical'
|
||||
x: number
|
||||
y: number
|
||||
|
||||
constructor(x: number, y: number) {
|
||||
this.x = x
|
||||
this.y = y
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the physical position to a logical one.
|
||||
* @example
|
||||
* ```typescript
|
||||
* import { getCurrent } from '@tauri-apps/api/window';
|
||||
* const appWindow = getCurrent();
|
||||
* const factor = await appWindow.scaleFactor();
|
||||
* const position = await appWindow.innerPosition();
|
||||
* const logical = position.toLogical(factor);
|
||||
* ```
|
||||
* */
|
||||
toLogical(scaleFactor: number): LogicalPosition {
|
||||
return new LogicalPosition(this.x / scaleFactor, this.y / scaleFactor)
|
||||
}
|
||||
}
|
||||
|
||||
export { LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize }
|
@ -8,16 +8,18 @@
|
||||
* This module exposes all other modules as an object where the key is the module name, and the value is the module exports.
|
||||
* @example
|
||||
* ```typescript
|
||||
* import { app, dialog, event, fs, globalShortcut } from '@tauri-apps/api'
|
||||
* import { event, window, path } from '@tauri-apps/api'
|
||||
* ```
|
||||
* @module
|
||||
*/
|
||||
|
||||
import * as event from './event'
|
||||
import * as tauri from './tauri'
|
||||
import * as window from './window'
|
||||
import * as path from './path'
|
||||
import * as dpi from './dpi'
|
||||
|
||||
/** @ignore */
|
||||
const invoke = tauri.invoke
|
||||
|
||||
export { invoke, event, path, tauri }
|
||||
export { invoke, event, path, tauri, window, dpi }
|
||||
|
2234
tooling/api/src/window.ts
Normal file
2234
tooling/api/src/window.ts
Normal file
File diff suppressed because it is too large
Load Diff
@ -3,7 +3,8 @@
|
||||
"src/event.ts",
|
||||
"src/mocks.ts",
|
||||
"src/path.ts",
|
||||
"src/tauri.ts"
|
||||
"src/tauri.ts",
|
||||
"src/window.ts"
|
||||
],
|
||||
"githubPages": false,
|
||||
"readme": "none",
|
||||
|
Loading…
Reference in New Issue
Block a user