feat(window): add setEffects API (#392)

This commit is contained in:
Lucas Fernandes Nogueira authored and GitHub committed 2023-06-18 07:21:27 -03:00
1 parent 2fc420ba37
commit c8c3191565
7 files changed
+349 -4

No files matched your search

+6
View File
@@ -0,0 +1,6 @@
---
"window": "patch"
"window-js": "patch"
---
Added the `setEffects` and `clearEffects` API.
+6 -1
View File
@@ -56,7 +56,7 @@ pub fn run() {
#[cfg(desktop)]
{
window_builder = window_builder
.user_agent("Tauri API")
.user_agent(&format!("Tauri API - {}", std::env::consts::OS))
.title("Tauri API Validation")
.inner_size(1000., 800.)
.min_inner_size(600., 400.)
@@ -71,6 +71,11 @@ pub fn run() {
.decorations(false);
}
#[cfg(target_os = "macos")]
{
window_builder = window_builder.transparent(true);
}
let window = window_builder.build().unwrap();
#[cfg(debug_assertions)]
+136
View File
@@ -6,6 +6,8 @@
UserAttentionType,
PhysicalSize,
PhysicalPosition,
Effect,
EffectState,
} from "@tauri-apps/plugin-window";
import { open as openDialog } from "@tauri-apps/plugin-dialog";
import { open } from "@tauri-apps/plugin-shell";
@@ -57,7 +59,20 @@
"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;
@@ -91,6 +106,12 @@
let cursorIgnoreEvents = false;
let windowTitle = "Awesome Tauri Example!";
let effects = [];
let selectedEffect;
let effectState;
let effectRadius;
let effectR, effectG, effectB, effectA;
function openUrl() {
open(urlValue);
}
@@ -172,6 +193,38 @@
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();
@@ -455,5 +508,88 @@
<button class="btn" id="open-url"> Open URL </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>
+195
View File
@@ -950,6 +950,30 @@ class WindowManager extends WebviewWindowHandle {
});
}
/**
* Set window effects.
*
* @since 2.0
*/
async setEffects(effects: Effects): Promise<void> {
return window.__TAURI_INVOKE__("plugin:window|set_effects", {
label: this.label,
value: effects,
});
}
/**
* Clear any applied effects if possible.
*
* @since 2.0
*/
async clearEffects(): Promise<void> {
return window.__TAURI_INVOKE__("plugin:window|set_effects", {
label: this.label,
value: null,
});
}
/**
* Whether the window should always be on top of other windows.
* @example
@@ -1777,6 +1801,174 @@ if ("__TAURI_METADATA__" in window) {
});
}
/**
* an array RGBA colors. Each value has minimum of 0 and maximum of 255.
*
* @since 2.0
*/
type Color = [number, number, number, number];
/**
* Platform-specific window effects
*
* @since 2.0
*/
enum Effect {
/**
* A default material appropriate for the view's effectiveAppearance. **macOS 10.14-**
*
* @deprecated since macOS 10.14. You should instead choose an appropriate semantic material.
*/
AppearanceBased = "appearanceBased",
/**
* **macOS 10.14-**
*
* @deprecated since macOS 10.14. Use a semantic material instead.
*/
Light = "light",
/**
* **macOS 10.14-**
*
* @deprecated since macOS 10.14. Use a semantic material instead.
*/
Dark = "dark",
/**
* **macOS 10.14-**
*
* @deprecated since macOS 10.14. Use a semantic material instead.
*/
MediumLight = "mediumLight",
/**
* **macOS 10.14-**
*
* @deprecated since macOS 10.14. Use a semantic material instead.
*/
UltraDark = "ultraDark",
/**
* **macOS 10.10+**
*/
Titlebar = "titlebar",
/**
* **macOS 10.10+**
*/
Selection = "selection",
/**
* **macOS 10.11+**
*/
Menu = "menu",
/**
* **macOS 10.11+**
*/
Popover = "popover",
/**
* **macOS 10.11+**
*/
Sidebar = "sidebar",
/**
* **macOS 10.14+**
*/
HeaderView = "headerView",
/**
* **macOS 10.14+**
*/
Sheet = "sheet",
/**
* **macOS 10.14+**
*/
WindowBackground = "windowBackground",
/**
* **macOS 10.14+**
*/
HudWindow = "hudWindow",
/**
* **macOS 10.14+**
*/
FullScreenUI = "fullScreenUI",
/**
* **macOS 10.14+**
*/
Tooltip = "tooltip",
/**
* **macOS 10.14+**
*/
ContentBackground = "contentBackground",
/**
* **macOS 10.14+**
*/
UnderWindowBackground = "underWindowBackground",
/**
* **macOS 10.14+**
*/
UnderPageBackground = "underPageBackground",
/**
* **Windows 11 Only**
*/
Mica = "mica",
/**
* **Windows 7/10/11(22H1) Only**
*
* ## Notes
*
* This effect has bad performance when resizing/dragging the window on Windows 11 build 22621.
*/
Blur = "blur",
/**
* **Windows 10/11**
*
* ## Notes
*
* This effect has bad performance when resizing/dragging the window on Windows 10 v1903+ and Windows 11 build 22000.
*/
Acrylic = "acrylic",
}
/**
* Window effect state **macOS only**
*
* @see https://developer.apple.com/documentation/appkit/nsvisualeffectview/state
*
* @since 2.0
*/
enum EffectState {
/**
* Make window effect state follow the window's active state **macOS only**
*/
FollowsWindowActiveState = "followsWindowActiveState",
/**
* Make window effect state always active **macOS only**
*/
Active = "active",
/**
* Make window effect state always inactive **macOS only**
*/
Inactive = "inactive",
}
/** The window effects configuration object
*
* @since 2.0
*/
interface Effects {
/**
* List of Window effects to apply to the Window.
* Conflicting effects will apply the first one and ignore the rest.
*/
effects: Effect[];
/**
* Window effect state **macOS Only**
*/
state?: EffectState;
/**
* Window effect corner radius **macOS Only**
*/
radius?: number;
/**
* Window effect color. Affects {@link Effects.Blur} and {@link Effects.Acrylic} only
* on Windows 10 v1903+. Doesn't have any effect on Windows 7 or Windows 11.
*/
color?: Color;
}
/**
* Configuration for the window to create.
*
@@ -1976,6 +2168,8 @@ export {
LogicalPosition,
PhysicalPosition,
UserAttentionType,
Effect,
EffectState,
currentMonitor,
primaryMonitor,
availableMonitors,
@@ -1988,4 +2182,5 @@ export type {
ScaleFactorChanged,
FileDropEvent,
WindowOptions,
Color,
};
+1 -1
View File
@@ -1 +1 @@
if("__TAURI__"in window){var __TAURI_WINDOW__=function(e){"use strict";var i=Object.defineProperty,n=(e,n)=>{for(var t in n)i(e,t,{get:n[t],enumerable:!0})},t=(e,i,n)=>{if(!i.has(e))throw TypeError("Cannot "+n)},l=(e,i,n)=>(t(e,i,"read from private field"),n?n.call(e):i.get(e)),a=(e,i,n,l)=>(t(e,i,"write to private field"),l?l.call(e,n):i.set(e,n),n);function s(e,i=!1){let n=window.crypto.getRandomValues(new Uint32Array(1))[0],t=`_${n}`;return Object.defineProperty(window,t,{value:n=>(i&&Reflect.deleteProperty(window,t),e?.(n)),writable:!1,configurable:!0}),n}n({},{Channel:()=>o,PluginListener:()=>_,addPluginListener:()=>w,convertFileSrc:()=>c,invoke:()=>u,transformCallback:()=>s});var r,o=class{constructor(){this.__TAURI_CHANNEL_MARKER__=!0,((e,i,n)=>{if(i.has(e))throw TypeError("Cannot add the same private member more than once");i instanceof WeakSet?i.add(e):i.set(e,n)})(this,r,(()=>{})),this.id=s((e=>{l(this,r).call(this,e)}))}set onmessage(e){a(this,r,e)}get onmessage(){return l(this,r)}toJSON(){return`__CHANNEL__:${this.id}`}};r=new WeakMap;var _=class{constructor(e,i,n){this.plugin=e,this.event=i,this.channelId=n}async unregister(){return u(`plugin:${this.plugin}|remove_listener`,{event:this.event,channelId:this.channelId})}};async function w(e,i,n){let t=new o;return t.onmessage=n,u(`plugin:${e}|register_listener`,{event:i,handler:t}).then((()=>new _(e,i,t.id)))}async function u(e,i={}){return new Promise(((n,t)=>{let l=s((e=>{n(e),Reflect.deleteProperty(window,`_${a}`)}),!0),a=s((e=>{t(e),Reflect.deleteProperty(window,`_${l}`)}),!0);window.__TAURI_IPC__({cmd:e,callback:l,error:a,...i})}))}function c(e,i="asset"){let n=encodeURIComponent(e);return navigator.userAgent.includes("Windows")?`https://${i}.localhost/${n}`:`${i}://localhost/${n}`}n({},{TauriEvent:()=>h,emit:()=>b,listen:()=>y,once:()=>I});var d,h=(e=>(e.WINDOW_RESIZED="tauri://resize",e.WINDOW_MOVED="tauri://move",e.WINDOW_CLOSE_REQUESTED="tauri://close-requested",e.WINDOW_CREATED="tauri://window-created",e.WINDOW_DESTROYED="tauri://destroyed",e.WINDOW_FOCUS="tauri://focus",e.WINDOW_BLUR="tauri://blur",e.WINDOW_SCALE_FACTOR_CHANGED="tauri://scale-change",e.WINDOW_THEME_CHANGED="tauri://theme-changed",e.WINDOW_FILE_DROP="tauri://file-drop",e.WINDOW_FILE_DROP_HOVER="tauri://file-drop-hover",e.WINDOW_FILE_DROP_CANCELLED="tauri://file-drop-cancelled",e.MENU="tauri://menu",e))(h||{});async function p(e,i){await u("plugin:event|unlisten",{event:e,eventId:i})}async function y(e,i,n){return u("plugin:event|listen",{event:e,windowLabel:n?.target,handler:s(i)}).then((i=>async()=>p(e,i)))}async function I(e,i,n){return y(e,(n=>{i(n),p(e,n.id).catch((()=>{}))}),n)}async function b(e,i,n){await u("plugin:event|emit",{event:e,windowLabel:n?.target,payload:i})}class g{constructor(e,i){this.type="Logical",this.width=e,this.height=i}}class E{constructor(e,i){this.type="Physical",this.width=e,this.height=i}toLogical(e){return new g(this.width/e,this.height/e)}}class A{constructor(e,i){this.type="Logical",this.x=e,this.y=i}}class O{constructor(e,i){this.type="Physical",this.x=e,this.y=i}toLogical(e){return new A(this.x/e,this.y/e)}}function T(){return window.__TAURI_METADATA__.__windows.map((e=>new U(e.label,{skip:!0})))}e.UserAttentionType=void 0,(d=e.UserAttentionType||(e.UserAttentionType={}))[d.Critical=1]="Critical",d[d.Informational=2]="Informational";const R=["tauri://created","tauri://error"];class N{constructor(e){this.label=e,this.listeners=Object.create(null)}async listen(e,i){return this._handleTauriEvent(e,i)?Promise.resolve((()=>{const n=this.listeners[e];n.splice(n.indexOf(i),1)})):y(e,i,{target:this.label})}async once(e,i){return this._handleTauriEvent(e,i)?Promise.resolve((()=>{const n=this.listeners[e];n.splice(n.indexOf(i),1)})):I(e,i,{target:this.label})}async emit(e,i){if(R.includes(e)){for(const n of this.listeners[e]||[])n({event:e,id:-1,windowLabel:this.label,payload:i});return Promise.resolve()}return b(e,i,{target:this.label})}_handleTauriEvent(e,i){return!!R.includes(e)&&(e in this.listeners?this.listeners[e].push(i):this.listeners[e]=[i],!0)}}class m extends N{async scaleFactor(){return window.__TAURI_INVOKE__("plugin:window|scale_factor",{label:this.label})}async innerPosition(){return window.__TAURI_INVOKE__("plugin:window|inner_position",{label:this.label}).then((({x:e,y:i})=>new O(e,i)))}async outerPosition(){return window.__TAURI_INVOKE__("plugin:window|outer_position",{label:this.label}).then((({x:e,y:i})=>new O(e,i)))}async innerSize(){return window.__TAURI_INVOKE__("plugin:window|inner_size",{label:this.label}).then((({width:e,height:i})=>new E(e,i)))}async outerSize(){return window.__TAURI_INVOKE__("plugin:window|outer_size",{label:this.label}).then((({width:e,height:i})=>new E(e,i)))}async isFullscreen(){return window.__TAURI_INVOKE__("plugin:window|is_fullscreen",{label:this.label})}async isMinimized(){return window.__TAURI_INVOKE__("plugin:window|is_minimized",{label:this.label})}async isMaximized(){return window.__TALine truncated
if("__TAURI__"in window){var __TAURI_WINDOW__=function(e){"use strict";var i=Object.defineProperty,n=(e,n)=>{for(var t in n)i(e,t,{get:n[t],enumerable:!0})},t=(e,i,n)=>{if(!i.has(e))throw TypeError("Cannot "+n)},a=(e,i,n)=>(t(e,i,"read from private field"),n?n.call(e):i.get(e)),l=(e,i,n,a)=>(t(e,i,"write to private field"),a?a.call(e,n):i.set(e,n),n);function s(e,i=!1){let n=window.crypto.getRandomValues(new Uint32Array(1))[0],t=`_${n}`;return Object.defineProperty(window,t,{value:n=>(i&&Reflect.deleteProperty(window,t),e?.(n)),writable:!1,configurable:!0}),n}n({},{Channel:()=>o,PluginListener:()=>_,addPluginListener:()=>w,convertFileSrc:()=>c,invoke:()=>u,transformCallback:()=>s});var r,o=class{constructor(){this.__TAURI_CHANNEL_MARKER__=!0,((e,i,n)=>{if(i.has(e))throw TypeError("Cannot add the same private member more than once");i instanceof WeakSet?i.add(e):i.set(e,n)})(this,r,(()=>{})),this.id=s((e=>{a(this,r).call(this,e)}))}set onmessage(e){l(this,r,e)}get onmessage(){return a(this,r)}toJSON(){return`__CHANNEL__:${this.id}`}};r=new WeakMap;var _=class{constructor(e,i,n){this.plugin=e,this.event=i,this.channelId=n}async unregister(){return u(`plugin:${this.plugin}|remove_listener`,{event:this.event,channelId:this.channelId})}};async function w(e,i,n){let t=new o;return t.onmessage=n,u(`plugin:${e}|register_listener`,{event:i,handler:t}).then((()=>new _(e,i,t.id)))}async function u(e,i={}){return new Promise(((n,t)=>{let a=s((e=>{n(e),Reflect.deleteProperty(window,`_${l}`)}),!0),l=s((e=>{t(e),Reflect.deleteProperty(window,`_${a}`)}),!0);window.__TAURI_IPC__({cmd:e,callback:a,error:l,...i})}))}function c(e,i="asset"){let n=encodeURIComponent(e);return navigator.userAgent.includes("Windows")?`https://${i}.localhost/${n}`:`${i}://localhost/${n}`}n({},{TauriEvent:()=>h,emit:()=>g,listen:()=>y,once:()=>I});var d,h=(e=>(e.WINDOW_RESIZED="tauri://resize",e.WINDOW_MOVED="tauri://move",e.WINDOW_CLOSE_REQUESTED="tauri://close-requested",e.WINDOW_CREATED="tauri://window-created",e.WINDOW_DESTROYED="tauri://destroyed",e.WINDOW_FOCUS="tauri://focus",e.WINDOW_BLUR="tauri://blur",e.WINDOW_SCALE_FACTOR_CHANGED="tauri://scale-change",e.WINDOW_THEME_CHANGED="tauri://theme-changed",e.WINDOW_FILE_DROP="tauri://file-drop",e.WINDOW_FILE_DROP_HOVER="tauri://file-drop-hover",e.WINDOW_FILE_DROP_CANCELLED="tauri://file-drop-cancelled",e.MENU="tauri://menu",e))(h||{});async function p(e,i){await u("plugin:event|unlisten",{event:e,eventId:i})}async function y(e,i,n){return u("plugin:event|listen",{event:e,windowLabel:n?.target,handler:s(i)}).then((i=>async()=>p(e,i)))}async function I(e,i,n){return y(e,(n=>{i(n),p(e,n.id).catch((()=>{}))}),n)}async function g(e,i,n){await u("plugin:event|emit",{event:e,windowLabel:n?.target,payload:i})}class b{constructor(e,i){this.type="Logical",this.width=e,this.height=i}}class E{constructor(e,i){this.type="Physical",this.width=e,this.height=i}toLogical(e){return new b(this.width/e,this.height/e)}}class A{constructor(e,i){this.type="Logical",this.x=e,this.y=i}}class O{constructor(e,i){this.type="Physical",this.x=e,this.y=i}toLogical(e){return new A(this.x/e,this.y/e)}}function T(){return window.__TAURI_METADATA__.__windows.map((e=>new N(e.label,{skip:!0})))}e.UserAttentionType=void 0,(d=e.UserAttentionType||(e.UserAttentionType={}))[d.Critical=1]="Critical",d[d.Informational=2]="Informational";const v=["tauri://created","tauri://error"];class f{constructor(e){this.label=e,this.listeners=Object.create(null)}async listen(e,i){return this._handleTauriEvent(e,i)?Promise.resolve((()=>{const n=this.listeners[e];n.splice(n.indexOf(i),1)})):y(e,i,{target:this.label})}async once(e,i){return this._handleTauriEvent(e,i)?Promise.resolve((()=>{const n=this.listeners[e];n.splice(n.indexOf(i),1)})):I(e,i,{target:this.label})}async emit(e,i){if(v.includes(e)){for(const n of this.listeners[e]||[])n({event:e,id:-1,windowLabel:this.label,payload:i});return Promise.resolve()}return g(e,i,{target:this.label})}_handleTauriEvent(e,i){return!!v.includes(e)&&(e in this.listeners?this.listeners[e].push(i):this.listeners[e]=[i],!0)}}class R extends f{async scaleFactor(){return window.__TAURI_INVOKE__("plugin:window|scale_factor",{label:this.label})}async innerPosition(){return window.__TAURI_INVOKE__("plugin:window|inner_position",{label:this.label}).then((({x:e,y:i})=>new O(e,i)))}async outerPosition(){return window.__TAURI_INVOKE__("plugin:window|outer_position",{label:this.label}).then((({x:e,y:i})=>new O(e,i)))}async innerSize(){return window.__TAURI_INVOKE__("plugin:window|inner_size",{label:this.label}).then((({width:e,height:i})=>new E(e,i)))}async outerSize(){return window.__TAURI_INVOKE__("plugin:window|outer_size",{label:this.label}).then((({width:e,height:i})=>new E(e,i)))}async isFullscreen(){return window.__TAURI_INVOKE__("plugin:window|is_fullscreen",{label:this.label})}async isMinimized(){return window.__TAURI_INVOKE__("plugin:window|is_minimized",{label:this.label})}async isMaximized(){return window.__TALine truncated
+4 -2
View File
@@ -4,8 +4,9 @@
use serde::{Deserialize, Serialize, Serializer};
use tauri::{
utils::config::WindowConfig, AppHandle, CursorIcon, Icon, Manager, Monitor, PhysicalPosition,
PhysicalSize, Position, Runtime, Size, Theme, UserAttentionType, Window,
utils::config::{WindowConfig, WindowEffectsConfig},
AppHandle, CursorIcon, Icon, Manager, Monitor, PhysicalPosition, PhysicalSize, Position,
Runtime, Size, Theme, UserAttentionType, Window,
};
#[derive(Debug, thiserror::Error)]
@@ -133,6 +134,7 @@ 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);
+1
View File
@@ -62,6 +62,7 @@ pub fn init<R: Runtime>() -> TauriPlugin<R> {
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,