Skip to content

Commit 930d6ba

Browse files
committed
Add cross-platform window visual effect support
Introduces a VisualEffect enum and related APIs to set and get window background effects (blur, acrylic, mica) across all supported platforms. Implements platform-specific handling for macOS and Windows, with stubs or warnings for unsupported platforms. Updates C API and documentation accordingly.
1 parent ed5eb99 commit 930d6ba

File tree

9 files changed

+219
-6
lines changed

9 files changed

+219
-6
lines changed

src/capi/window_c.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -592,6 +592,22 @@ float native_window_get_opacity(native_window_t window) {
592592
return win->GetOpacity();
593593
}
594594

595+
FFI_PLUGIN_EXPORT
596+
void native_window_set_visual_effect(native_window_t window, native_visual_effect_t effect) {
597+
if (!window)
598+
return;
599+
auto* win = static_cast<nativeapi::Window*>(window);
600+
win->SetVisualEffect(static_cast<nativeapi::VisualEffect>(effect));
601+
}
602+
603+
FFI_PLUGIN_EXPORT
604+
native_visual_effect_t native_window_get_visual_effect(native_window_t window) {
605+
if (!window)
606+
return NATIVE_VISUAL_EFFECT_NONE;
607+
auto* win = static_cast<nativeapi::Window*>(window);
608+
return static_cast<native_visual_effect_t>(win->GetVisualEffect());
609+
}
610+
595611
FFI_PLUGIN_EXPORT
596612
void native_window_set_visible_on_all_workspaces(native_window_t window, bool visible) {
597613
if (!window)

src/capi/window_c.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,16 @@ typedef enum {
4141
NATIVE_TITLE_BAR_STYLE_HIDDEN = 1
4242
} native_title_bar_style_t;
4343

44+
/**
45+
* Visual effect styles for window background
46+
*/
47+
typedef enum {
48+
NATIVE_VISUAL_EFFECT_NONE = 0,
49+
NATIVE_VISUAL_EFFECT_BLUR = 1,
50+
NATIVE_VISUAL_EFFECT_ACRYLIC = 2,
51+
NATIVE_VISUAL_EFFECT_MICA = 3
52+
} native_visual_effect_t;
53+
4454
// Window creation and destruction
4555
FFI_PLUGIN_EXPORT
4656
native_window_t native_window_create(void);
@@ -220,6 +230,12 @@ void native_window_set_opacity(native_window_t window, float opacity);
220230
FFI_PLUGIN_EXPORT
221231
float native_window_get_opacity(native_window_t window);
222232

233+
FFI_PLUGIN_EXPORT
234+
void native_window_set_visual_effect(native_window_t window, native_visual_effect_t effect);
235+
236+
FFI_PLUGIN_EXPORT
237+
native_visual_effect_t native_window_get_visual_effect(native_window_t window);
238+
223239
FFI_PLUGIN_EXPORT
224240
void native_window_set_visible_on_all_workspaces(native_window_t window, bool visible);
225241

src/platform/android/window_android.cpp

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,9 @@ namespace nativeapi {
1414
// Private implementation class
1515
class Window::Impl {
1616
public:
17-
Impl(ANativeWindow* window) : native_window_(window) {}
17+
Impl(ANativeWindow* window) : native_window_(window), visual_effect_(VisualEffect::None) {}
1818
ANativeWindow* native_window_;
19+
VisualEffect visual_effect_;
1920
};
2021

2122
Window::Window() : pimpl_(std::make_unique<Impl>(nullptr)) {}
@@ -325,6 +326,15 @@ float Window::GetOpacity() const {
325326
return 1.0f;
326327
}
327328

329+
void Window::SetVisualEffect(VisualEffect effect) {
330+
pimpl_->visual_effect_ = effect;
331+
ALOGW("SetVisualEffect not supported on Android");
332+
}
333+
334+
VisualEffect Window::GetVisualEffect() const {
335+
return pimpl_->visual_effect_;
336+
}
337+
328338
void Window::SetVisibleOnAllWorkspaces(bool is_visible_on_all_workspaces) {
329339
ALOGW("SetVisibleOnAllWorkspaces not supported on Android");
330340
}

src/platform/ios/window_ios.mm

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@
99
// Private implementation class
1010
class Window::Impl {
1111
public:
12-
Impl(UIWindow* window) : ui_window_(window) {}
12+
Impl(UIWindow* window) : ui_window_(window), visual_effect_(VisualEffect::None) {}
1313
UIWindow* ui_window_;
14+
VisualEffect visual_effect_;
1415
};
1516

1617
Window::Window() : pimpl_(std::make_unique<Impl>(nil)) {}
@@ -368,6 +369,15 @@
368369
return pimpl_->ui_window_ ? pimpl_->ui_window_.alpha : 1.0f;
369370
}
370371

372+
void Window::SetVisualEffect(VisualEffect effect) {
373+
pimpl_->visual_effect_ = effect;
374+
NSLog(@"SetVisualEffect not supported on iOS");
375+
}
376+
377+
VisualEffect Window::GetVisualEffect() const {
378+
return pimpl_->visual_effect_;
379+
}
380+
371381
void Window::SetVisibleOnAllWorkspaces(bool is_visible_on_all_workspaces) {
372382
// Not applicable to iOS
373383
}

src/platform/linux/window_linux.cpp

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,14 @@ static GtkWidget* FindHeaderBar(GtkWidget* widget) {
4545
class Window::Impl {
4646
public:
4747
Impl(GtkWidget* widget, GdkWindow* gdk_window)
48-
: widget_(widget), gdk_window_(gdk_window), title_bar_style_(TitleBarStyle::Normal) {}
48+
: widget_(widget),
49+
gdk_window_(gdk_window),
50+
title_bar_style_(TitleBarStyle::Normal),
51+
visual_effect_(VisualEffect::None) {}
4952
GtkWidget* widget_;
5053
GdkWindow* gdk_window_;
5154
TitleBarStyle title_bar_style_;
55+
VisualEffect visual_effect_;
5256
};
5357

5458
Window::Window() {
@@ -546,6 +550,16 @@ float Window::GetOpacity() const {
546550
return 1.0f; // Default assumption
547551
}
548552

553+
void Window::SetVisualEffect(VisualEffect effect) {
554+
pimpl_->visual_effect_ = effect;
555+
// TODO: Implement background blur for Linux (GTK/GDK)
556+
// This typically requires compositor support or specific GTK CSS
557+
}
558+
559+
VisualEffect Window::GetVisualEffect() const {
560+
return pimpl_->visual_effect_;
561+
}
562+
549563
void Window::SetVisibleOnAllWorkspaces(bool is_visible_on_all_workspaces) {
550564
if (pimpl_->gdk_window_) {
551565
gdk_window_stick(pimpl_->gdk_window_);

src/platform/macos/window_macos.mm

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,16 @@
1818
class Window::Impl {
1919
public:
2020
Impl(WindowId id, NSWindow* window)
21-
: id_(id), ns_window_(window), title_bar_style_(TitleBarStyle::Normal) {}
21+
: id_(id),
22+
ns_window_(window),
23+
title_bar_style_(TitleBarStyle::Normal),
24+
visual_effect_(VisualEffect::None),
25+
visual_effect_view_(nil) {}
2226
WindowId id_;
2327
NSWindow* ns_window_;
2428
TitleBarStyle title_bar_style_;
29+
VisualEffect visual_effect_;
30+
NSVisualEffectView* visual_effect_view_;
2531
};
2632

2733
Window::Window() : Window(nullptr) {}
@@ -414,6 +420,55 @@
414420
return [pimpl_->ns_window_ alphaValue];
415421
}
416422

423+
void Window::SetVisualEffect(VisualEffect effect) {
424+
if (pimpl_->visual_effect_ == effect)
425+
return;
426+
427+
pimpl_->visual_effect_ = effect;
428+
NSWindow* window = pimpl_->ns_window_;
429+
430+
if (effect == VisualEffect::None) {
431+
if (pimpl_->visual_effect_view_) {
432+
[pimpl_->visual_effect_view_ removeFromSuperview];
433+
pimpl_->visual_effect_view_ = nil;
434+
}
435+
[window setOpaque:YES];
436+
[window setBackgroundColor:[NSColor windowBackgroundColor]];
437+
return;
438+
}
439+
440+
if (!pimpl_->visual_effect_view_) {
441+
NSView* contentView = [window contentView];
442+
pimpl_->visual_effect_view_ = [[NSVisualEffectView alloc] initWithFrame:[contentView bounds]];
443+
[pimpl_->visual_effect_view_ setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
444+
[pimpl_->visual_effect_view_ setBlendingMode:NSVisualEffectBlendingModeBehindWindow];
445+
[contentView addSubview:pimpl_->visual_effect_view_ positioned:NSWindowBelow relativeTo:nil];
446+
}
447+
448+
[window setOpaque:NO];
449+
[window setBackgroundColor:[NSColor clearColor]];
450+
451+
switch (effect) {
452+
case VisualEffect::Blur:
453+
[pimpl_->visual_effect_view_ setMaterial:NSVisualEffectMaterialSidebar];
454+
break;
455+
case VisualEffect::Acrylic:
456+
[pimpl_->visual_effect_view_ setMaterial:NSVisualEffectMaterialUnderWindowBackground];
457+
break;
458+
case VisualEffect::Mica:
459+
[pimpl_->visual_effect_view_ setMaterial:NSVisualEffectMaterialWindowBackground];
460+
break;
461+
default:
462+
break;
463+
}
464+
465+
[pimpl_->visual_effect_view_ setState:NSVisualEffectStateActive];
466+
}
467+
468+
VisualEffect Window::GetVisualEffect() const {
469+
return pimpl_->visual_effect_;
470+
}
471+
417472
void Window::SetVisibleOnAllWorkspaces(bool is_visible_on_all_workspaces) {
418473
[pimpl_->ns_window_ setCollectionBehavior:is_visible_on_all_workspaces
419474
? NSWindowCollectionBehaviorCanJoinAllSpaces

src/platform/ohos/window_ohos.cpp

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,9 @@ namespace nativeapi {
1414
// Private implementation class
1515
class Window::Impl {
1616
public:
17-
Impl(void* window) : native_window_(window) {}
17+
Impl(void* window) : native_window_(window), visual_effect_(VisualEffect::None) {}
1818
void* native_window_;
19+
VisualEffect visual_effect_;
1920
};
2021

2122
Window::Window() : pimpl_(std::make_unique<Impl>(nullptr)) {}
@@ -309,6 +310,15 @@ float Window::GetOpacity() const {
309310
return 1.0f;
310311
}
311312

313+
void Window::SetVisualEffect(VisualEffect effect) {
314+
pimpl_->visual_effect_ = effect;
315+
// SetVisualEffect not supported on OpenHarmony
316+
}
317+
318+
VisualEffect Window::GetVisualEffect() const {
319+
return pimpl_->visual_effect_;
320+
}
321+
312322
void Window::SetVisibleOnAllWorkspaces(bool is_visible_on_all_workspaces) {
313323
// SetVisibleOnAllWorkspaces not supported on OpenHarmony
314324
}

src/platform/windows/window_windows.cpp

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,14 @@ static LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM l
2222
class Window::Impl {
2323
public:
2424
Impl(HWND hwnd, WindowId id)
25-
: hwnd_(hwnd), window_id_(id), title_bar_style_(TitleBarStyle::Normal) {}
25+
: hwnd_(hwnd),
26+
window_id_(id),
27+
title_bar_style_(TitleBarStyle::Normal),
28+
visual_effect_(VisualEffect::None) {}
2629
HWND hwnd_;
2730
WindowId window_id_;
2831
TitleBarStyle title_bar_style_;
32+
VisualEffect visual_effect_;
2933
};
3034

3135
// Custom window procedure to handle window messages
@@ -721,6 +725,36 @@ float Window::GetOpacity() const {
721725
return 1.0f;
722726
}
723727

728+
void Window::SetVisualEffect(VisualEffect effect) {
729+
if (!pimpl_->hwnd_ || pimpl_->visual_effect_ == effect)
730+
return;
731+
732+
pimpl_->visual_effect_ = effect;
733+
734+
// DWM_SYSTEMBACKDROP_TYPE is available in Windows 11 Build 22621+
735+
// DWMWA_SYSTEMBACKDROP_TYPE = 38
736+
int backdrop_type = 1; // DWMSBT_NONE
737+
738+
switch (effect) {
739+
case VisualEffect::None:
740+
backdrop_type = 1; // DWMSBT_NONE
741+
break;
742+
case VisualEffect::Blur:
743+
case VisualEffect::Acrylic:
744+
backdrop_type = 3; // DWMSBT_TRANSIENTWINDOW (Acrylic)
745+
break;
746+
case VisualEffect::Mica:
747+
backdrop_type = 2; // DWMSBT_MAINWINDOW (Mica)
748+
break;
749+
}
750+
751+
DwmSetWindowAttribute(pimpl_->hwnd_, 38, &backdrop_type, sizeof(backdrop_type));
752+
}
753+
754+
VisualEffect Window::GetVisualEffect() const {
755+
return pimpl_->visual_effect_;
756+
}
757+
724758
void Window::SetVisibleOnAllWorkspaces(bool is_visible_on_all_workspaces) {
725759
// Windows doesn't have the same concept of workspaces as macOS
726760
// This would require integration with virtual desktop APIs

src/window.h

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,38 @@ enum class TitleBarStyle {
4242
Hidden
4343
};
4444

45+
/**
46+
* @brief Visual effect styles for window background.
47+
*
48+
* Defines blur or material effects applied to the window background.
49+
* These effects typically provide a translucent or "frosted glass" appearance.
50+
*/
51+
enum class VisualEffect {
52+
/** No visual effect. Standard solid background. */
53+
None,
54+
55+
/**
56+
* Standard background blur.
57+
* - Windows: Standard blur (Blur behind)
58+
* - macOS: Default vibrancy effect
59+
*/
60+
Blur,
61+
62+
/**
63+
* Enhanced translucent blur effect.
64+
* - Windows: Acrylic effect
65+
* - macOS: Thick vibrancy
66+
*/
67+
Acrylic,
68+
69+
/**
70+
* Material effect that samples the desktop wallpaper.
71+
* - Windows: Mica effect (Windows 11+)
72+
* - macOS: WindowBackground vibrancy
73+
*/
74+
Mica
75+
};
76+
4577
/**
4678
* @class Window
4779
* @brief Cross-platform window abstraction class.
@@ -577,6 +609,22 @@ class Window : public NativeObjectProvider, public std::enable_shared_from_this<
577609
*/
578610
float GetOpacity() const;
579611

612+
/**
613+
* @brief Sets the visual effect (blur/vibrancy) for the window background.
614+
*
615+
* Allows creating translucent windows with various platform-specific effects.
616+
*
617+
* @param effect The visual effect to apply
618+
*/
619+
void SetVisualEffect(VisualEffect effect);
620+
621+
/**
622+
* @brief Gets the current visual effect applied to the window.
623+
*
624+
* @return VisualEffect The current visual effect
625+
*/
626+
VisualEffect GetVisualEffect() const;
627+
580628
/**
581629
* @brief Sets whether the window appears on all virtual desktops/workspaces.
582630
*

0 commit comments

Comments
 (0)