Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(windows): add tabbed effect #7794

Merged
merged 12 commits into from
Oct 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changes/api-tabbed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@tauri-apps/api": "patch:feat"
---

On Windows, add `Effect.Tabbed`,`Effect.TabbedDark` and `Effect.TabbedLight` effects.
5 changes: 5 additions & 0 deletions .changes/tauri-tabbed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"tauri": "patch:feat"
---

On Windows, add `Effect::Tabbed`,`Effect::TabbedDark` and `Effect::TabbedLight` effects.
6 changes: 6 additions & 0 deletions .changes/tauri-utils-tabbed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"tauri-utils": "patch:feat"
---

On Windows, add `WindowEffect::Tabbed`,`WindowEffect::TabbedDark` and `WindowEffect::TabbedLight`

21 changes: 21 additions & 0 deletions core/tauri-config-schema/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -796,6 +796,27 @@
"micaLight"
]
},
{
"description": "Tabbed effect that matches the system dark perefence **Windows 11 Only**",
"type": "string",
"enum": [
"tabbed"
]
},
{
"description": "Tabbed effect with dark mode but only if dark mode is enabled on the system **Windows 11 Only**",
"type": "string",
"enum": [
"tabbedDark"
]
},
{
"description": "Tabbed effect with light mode **Windows 11 Only**",
"type": "string",
"enum": [
"tabbedLight"
]
},
{
"description": "**Windows 7/10/11(22H1) Only**\n\n## Notes\n\nThis effect has bad performance when resizing/dragging the window on Windows 11 build 22621.",
"type": "string",
Expand Down
9 changes: 9 additions & 0 deletions core/tauri-utils/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -842,6 +842,12 @@ pub struct BundleConfig {
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct Color(pub u8, pub u8, pub u8, pub u8);

impl From<Color> for (u8, u8, u8, u8) {
fn from(value: Color) -> Self {
(value.0, value.1, value.2, value.3)
}
}

/// The window effects configuration object
#[skip_serializing_none]
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize, Default)]
Expand Down Expand Up @@ -2197,6 +2203,9 @@ mod build {
WindowEffect::MicaLight => quote! { #prefix::MicaLight},
WindowEffect::Blur => quote! { #prefix::Blur},
WindowEffect::Acrylic => quote! { #prefix::Acrylic},
WindowEffect::Tabbed => quote! { #prefix::Tabbed },
WindowEffect::TabbedDark => quote! { #prefix::TabbedDark },
WindowEffect::TabbedLight => quote! { #prefix::TabbedLight },
})
}
}
Expand Down
6 changes: 6 additions & 0 deletions core/tauri-utils/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,12 @@ mod window_effects {
MicaDark,
/// Mica effect with light mode **Windows 11 Only**
MicaLight,
/// Tabbed effect that matches the system dark perefence **Windows 11 Only**
Tabbed,
/// Tabbed effect with dark mode but only if dark mode is enabled on the system **Windows 11 Only**
TabbedDark,
/// Tabbed effect with light mode **Windows 11 Only**
TabbedLight,
/// **Windows 7/10/11(22H1) Only**
///
/// ## Notes
Expand Down
1 change: 1 addition & 0 deletions core/tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ infer = { version = "0.15", optional = true }
png = { version = "0.17", optional = true }
ico = { version = "0.3.0", optional = true }
http-range = { version = "0.1.4", optional = true }
window-vibrancy = "0.4"

[target."cfg(any(target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\", target_os = \"windows\", target_os = \"macos\"))".dependencies]
muda = { version = "0.8", default-features = false }
Expand Down
2 changes: 1 addition & 1 deletion core/tauri/scripts/bundle.global.js

Large diffs are not rendered by default.

245 changes: 13 additions & 232 deletions core/tauri/src/vibrancy/macos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,17 @@

use crate::utils::config::WindowEffectsConfig;
use crate::window::{Effect, EffectState};
use cocoa::{
appkit::{
NSAppKitVersionNumber, NSAppKitVersionNumber10_10, NSAppKitVersionNumber10_11,
NSAutoresizingMaskOptions, NSView, NSViewHeightSizable, NSViewWidthSizable, NSWindow,
NSWindowOrderingMode,
},
base::{id, nil, BOOL},
foundation::{NSAutoreleasePool, NSPoint, NSRect, NSSize},
};
use objc::{class, msg_send, sel, sel_impl};
use raw_window_handle::HasRawWindowHandle;
use window_vibrancy::{NSVisualEffectMaterial, NSVisualEffectState};

pub fn apply_effects(window: id, effects: WindowEffectsConfig) {
pub fn apply_effects(window: impl HasRawWindowHandle, effects: WindowEffectsConfig) {
let WindowEffectsConfig {
effects,
radius,
state,
..
} = effects;
let mut appearance: NSVisualEffectMaterial = if let Some(effect) = effects.into_iter().find(|e| {
let effect = if let Some(effect) = effects.into_iter().find(|e| {
matches!(
e,
Effect::AppearanceBased
Expand All @@ -49,221 +41,14 @@ pub fn apply_effects(window: id, effects: WindowEffectsConfig) {
| Effect::UnderPageBackground
)
}) {
effect.into()
effect
} else {
return;
};

unsafe {
if NSAppKitVersionNumber < NSAppKitVersionNumber10_10 {
return;
}

if !msg_send![class!(NSThread), isMainThread] {
return;
}

if appearance as u32 > 4 && NSAppKitVersionNumber < NSAppKitVersionNumber10_11 {
appearance = NSVisualEffectMaterial::AppearanceBased;
}

if appearance as u32 > 9 && NSAppKitVersionNumber < NSAppKitVersionNumber10_14 {
appearance = NSVisualEffectMaterial::AppearanceBased;
}

let ns_view: id = window.contentView();
let bounds = NSView::bounds(ns_view);

let blurred_view = NSVisualEffectView::initWithFrame_(NSVisualEffectView::alloc(nil), bounds);
blurred_view.autorelease();

blurred_view.setMaterial_(appearance);
blurred_view.setCornerRadius_(radius.unwrap_or(0.0));
blurred_view.setBlendingMode_(NSVisualEffectBlendingMode::BehindWindow);
blurred_view.setState_(
state
.map(Into::into)
.unwrap_or(NSVisualEffectState::FollowsWindowActiveState),
);
NSVisualEffectView::setAutoresizingMask_(
blurred_view,
NSViewWidthSizable | NSViewHeightSizable,
);

let _: () = msg_send![ns_view, addSubview: blurred_view positioned: NSWindowOrderingMode::NSWindowBelow relativeTo: 0];
}
}

#[allow(non_upper_case_globals)]
const NSAppKitVersionNumber10_14: f64 = 1671.0;

// https://developer.apple.com/documentation/appkit/nsvisualeffectview/blendingmode
#[allow(dead_code)]
#[repr(u64)]
#[derive(Clone, Copy, Debug, PartialEq)]
enum NSVisualEffectBlendingMode {
BehindWindow = 0,
WithinWindow = 1,
}

// macos 10.10+
// https://developer.apple.com/documentation/appkit/nsvisualeffectview
#[allow(non_snake_case)]
trait NSVisualEffectView: Sized {
unsafe fn alloc(_: Self) -> id {
msg_send![class!(NSVisualEffectView), alloc]
}

unsafe fn init(self) -> id;
unsafe fn initWithFrame_(self, frameRect: NSRect) -> id;
unsafe fn bounds(self) -> NSRect;
unsafe fn frame(self) -> NSRect;
unsafe fn setFrameSize(self, frameSize: NSSize);
unsafe fn setFrameOrigin(self, frameOrigin: NSPoint);

unsafe fn superview(self) -> id;
unsafe fn removeFromSuperview(self);
unsafe fn setAutoresizingMask_(self, autoresizingMask: NSAutoresizingMaskOptions);

// API_AVAILABLE(macos(10.12));
unsafe fn isEmphasized(self) -> BOOL;
// API_AVAILABLE(macos(10.12));
unsafe fn setEmphasized_(self, emphasized: BOOL);

unsafe fn setMaterial_(self, material: NSVisualEffectMaterial);
unsafe fn setCornerRadius_(self, radius: f64);
unsafe fn setState_(self, state: NSVisualEffectState);
unsafe fn setBlendingMode_(self, mode: NSVisualEffectBlendingMode);
}

#[allow(non_snake_case)]
impl NSVisualEffectView for id {
unsafe fn init(self) -> id {
msg_send![self, init]
}

unsafe fn initWithFrame_(self, frameRect: NSRect) -> id {
msg_send![self, initWithFrame: frameRect]
}

unsafe fn bounds(self) -> NSRect {
msg_send![self, bounds]
}

unsafe fn frame(self) -> NSRect {
msg_send![self, frame]
}

unsafe fn setFrameSize(self, frameSize: NSSize) {
msg_send![self, setFrameSize: frameSize]
}

unsafe fn setFrameOrigin(self, frameOrigin: NSPoint) {
msg_send![self, setFrameOrigin: frameOrigin]
}

unsafe fn superview(self) -> id {
msg_send![self, superview]
}

unsafe fn removeFromSuperview(self) {
msg_send![self, removeFromSuperview]
}

unsafe fn setAutoresizingMask_(self, autoresizingMask: NSAutoresizingMaskOptions) {
msg_send![self, setAutoresizingMask: autoresizingMask]
}

// API_AVAILABLE(macos(10.12));
unsafe fn isEmphasized(self) -> BOOL {
msg_send![self, isEmphasized]
}

// API_AVAILABLE(macos(10.12));
unsafe fn setEmphasized_(self, emphasized: BOOL) {
msg_send![self, setEmphasized: emphasized]
}

unsafe fn setMaterial_(self, material: NSVisualEffectMaterial) {
msg_send![self, setMaterial: material]
}

unsafe fn setCornerRadius_(self, radius: f64) {
msg_send![self, setCornerRadius: radius]
}

unsafe fn setState_(self, state: NSVisualEffectState) {
msg_send![self, setState: state]
}

unsafe fn setBlendingMode_(self, mode: NSVisualEffectBlendingMode) {
msg_send![self, setBlendingMode: mode]
}
}

/// <https://developer.apple.com/documentation/appkit/nsvisualeffectview/material>
#[repr(u64)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum NSVisualEffectMaterial {
#[deprecated = "Since macOS 10.14 a default material appropriate for the view's effectiveAppearance. You should instead choose an appropriate semantic material."]
AppearanceBased = 0,
#[deprecated = "Since macOS 10.14 use a semantic material instead."]
Light = 1,
#[deprecated = "Since macOS 10.14 use a semantic material instead."]
Dark = 2,
#[deprecated = "Since macOS 10.14 use a semantic material instead."]
MediumLight = 8,
#[deprecated = "Since macOS 10.14 use a semantic material instead."]
UltraDark = 9,

/// macOS 10.10+
Titlebar = 3,
/// macOS 10.10+
Selection = 4,

/// macOS 10.11+
Menu = 5,
/// macOS 10.11+
Popover = 6,
/// macOS 10.11+
Sidebar = 7,

/// macOS 10.14+
HeaderView = 10,
/// macOS 10.14+
Sheet = 11,
/// macOS 10.14+
WindowBackground = 12,
/// macOS 10.14+
HudWindow = 13,
/// macOS 10.14+
FullScreenUI = 15,
/// macOS 10.14+
Tooltip = 17,
/// macOS 10.14+
ContentBackground = 18,
/// macOS 10.14+
UnderWindowBackground = 21,
/// macOS 10.14+
UnderPageBackground = 22,
}

/// <https://developer.apple.com/documentation/appkit/nsvisualeffectview/state>
#[allow(dead_code)]
#[repr(u64)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum NSVisualEffectState {
/// Make window vibrancy state follow the window's active state
FollowsWindowActiveState = 0,
/// Make window vibrancy state always active
Active = 1,
/// Make window vibrancy state always inactive
Inactive = 2,
}

impl From<crate::window::Effect> for NSVisualEffectMaterial {
fn from(value: crate::window::Effect) -> Self {
match value {
window_vibrancy::apply_vibrancy(
window,
match effect {
Effect::AppearanceBased => NSVisualEffectMaterial::AppearanceBased,
Effect::Light => NSVisualEffectMaterial::Light,
Effect::Dark => NSVisualEffectMaterial::Dark,
Expand All @@ -284,16 +69,12 @@ impl From<crate::window::Effect> for NSVisualEffectMaterial {
Effect::UnderWindowBackground => NSVisualEffectMaterial::UnderWindowBackground,
Effect::UnderPageBackground => NSVisualEffectMaterial::UnderPageBackground,
_ => unreachable!(),
}
}
}

impl From<crate::window::EffectState> for NSVisualEffectState {
fn from(value: crate::window::EffectState) -> Self {
match value {
},
state.map(|s| match s {
EffectState::FollowsWindowActiveState => NSVisualEffectState::FollowsWindowActiveState,
EffectState::Active => NSVisualEffectState::Active,
EffectState::Inactive => NSVisualEffectState::Inactive,
}
}
}),
radius,
);
}
15 changes: 3 additions & 12 deletions core/tauri/src/vibrancy/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,12 @@ pub fn set_window_effects<R: Runtime>(
) -> crate::Result<()> {
if let Some(_effects) = effects {
#[cfg(windows)]
{
let hwnd = window.hwnd()?;
windows::apply_effects(hwnd, _effects);
}
windows::apply_effects(window, _effects);
#[cfg(target_os = "macos")]
{
let ns_window = window.ns_window()?;
macos::apply_effects(ns_window as _, _effects);
}
macos::apply_effects(window, _effects);
} else {
#[cfg(windows)]
{
let hwnd = window.hwnd()?;
windows::clear_effects(hwnd);
}
windows::clear_effects(window);
}
Ok(())
}
Loading