How to apply a transparent blur background (rust, only tested in macos) #5710
Unanswered
alisomay
asked this question in
Show and tell
Replies: 1 comment 2 replies
-
Didn't work for me on Windows 10 :/ Looks like it shouldn't work: https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/nf-dwmapi-dwmenableblurbehindwindow This is hacky but works, Windows 10: //Helper function to dynamically load function pointer as some functions
// may not be available on all Windows platforms supported by winit.
//
// `library` and `function` must be zero-terminated.
pub fn get_function_impl(library: &str, function: &str) -> Option<*const c_void> {
assert_eq!(library.chars().last(), Some('\0'));
assert_eq!(function.chars().last(), Some('\0'));
// Library names we will use are ASCII so we can use the A version to avoid string conversion.
let module = unsafe { LoadLibraryA(library.as_ptr() as winapi::um::winnt::LPCSTR) };
if module.is_null() {
return None;
}
Option::from(unsafe { GetProcAddress(module, function.as_ptr() as winapi::um::winnt::LPCSTR) } as *const c_void)//.map(|function_ptr| function_ptr as _)
}
macro_rules! get_function {
($lib:expr, $func:ident) => {
crate::get_function_impl(
concat!($lib, '\0'),
concat!(stringify!($func), '\0'),
)
.map(|f| unsafe { std::mem::transmute::<*const _, $func>(f) })
};
}
// A poly-fill for `lazy_cell`
// Replace with std::sync::LazyLock when https://github.com/rust-lang/rust/issues/109736 is stabilized.
// This isn't used on every platform, which can come up as dead code warnings.
use std::any::Any;
use std::ops::Deref;
use std::sync::OnceLock;
pub(crate) struct Lazy<T> {
cell: OnceLock<T>,
init: fn() -> T,
}
impl<T> Lazy<T> {
pub const fn new(f: fn() -> T) -> Self {
Self { cell: OnceLock::new(), init: f }
}
}
impl<T> Deref for Lazy<T> {
type Target = T;
#[inline]
fn deref(&self) -> &'_ T {
self.cell.get_or_init(self.init)
}
}
fn set_blur(hwnd: HWND, is_blur: bool) -> bool {
// Uses Windows undocumented API SetWindowCompositionAttribute,
// as seen in win32-darkmode example linked at top of file.
type SetWindowCompositionAttribute =
unsafe extern "system" fn(HWND, *mut WINDOWCOMPOSITIONATTRIBDATA) -> BOOL;
#[allow(clippy::upper_case_acronyms)]
type WINDOWCOMPOSITIONATTRIB = u32;
const WCA_ACCENT_POLICY: WINDOWCOMPOSITIONATTRIB = 19;
const ACCENT_ENABLE_BLURBEHIND : WINDOWCOMPOSITIONATTRIB = 3;
// https://vhanla.codigobit.info/2015/07/enable-windows-10-aero-glass-aka-blur.html
#[allow(non_snake_case)]
#[allow(clippy::upper_case_acronyms)]
#[repr(C)]
#[derive(Default)]
struct AccentPolicy {
AccentState: u32,
AccentFlags: u32,
GradientColor: u32,
AnimationId: u32
}
#[allow(non_snake_case)]
#[allow(clippy::upper_case_acronyms)]
#[repr(C)]
struct WINDOWCOMPOSITIONATTRIBDATA {
Attrib: WINDOWCOMPOSITIONATTRIB,
pvData: *mut c_void,
cbData: usize,
}
static SET_WINDOW_COMPOSITION_ATTRIBUTE: Lazy<Option<SetWindowCompositionAttribute>> =
Lazy::new(|| get_function!("user32.dll", SetWindowCompositionAttribute));
let mut data = AccentPolicy {
AccentState: ACCENT_ENABLE_BLURBEHIND,
..Default::default()
};
if let Some(set_window_composition_attribute) = *SET_WINDOW_COMPOSITION_ATTRIBUTE {
unsafe {
let mut data = WINDOWCOMPOSITIONATTRIBDATA {
Attrib: WCA_ACCENT_POLICY,
pvData: &mut data as *mut _ as _,
cbData: std::mem::size_of_val(&data) as _,
};
let status = set_window_composition_attribute(hwnd, &mut data);
dbg!(status);
status != false.into()
}
} else {
false
}
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut ui = WelcomeWindow::new()?;
ui.window()
.with_winit_window(|winit_window: &winit::window::Window| {
let handle = winit_window.window_handle().unwrap().as_raw();
dbg!(handle);
if let RawWindowHandle::Win32(mut win_handle) = handle {
unsafe {
set_blur(win_handle.hwnd.get() as HWND, true);
}
}
}); glued together from https://vhanla.codigobit.info/2015/07/enable-windows-10-aero-glass-aka-blur.html and https://github.com/rust-windowing/winit/blob/master/src/platform_impl/windows/dark_mode.rs#L82 |
Beta Was this translation helpful? Give feedback.
2 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
-
I've spend a day to figure this out due to my unfamiliarity with desktop native ui programming currently but I hope it helps someone.
This is for the
winit
backend.At the time of this show and tell the
slint
version is1.7
.Dependencies for the whole concept you may pick what you'd need with common sense:
Here is how I've achieved a blurred background in my window.
There is another way to get a handle to the window to work with platform specific stuff but this happens after (I assume) slint has the knowledge or started operating on the window to a certain extent. It might be useful to some also. This would be similar for other platforms until you get the native window handle.
You might need to bring some traits into scope but compiler would remind you about that anyway ✨
WindowAttributes
will give you a lot of options for configuration.Also the native window handle will give you all the options for native apis.
There is one thing to beware in macos though, since slint (I suppose) uses the root
NSView
akacontentView
to render, if you play with view hierarchy try not to mask or block slint rendering or mess event handling.It might be like that for other platforms also. I'm a newbie in this particular field.
After going through these I also felt like it would be nice to have an interface to prepare a native window with a view hierarchy and possibly other settings and pass it over to slint to continue ui development with it afterwards. Currently it seems that it requires writing a custom backend which may add a lot of maintenence overhead but since I don't know deep knowledge about this field my idea might not be reasonable.
Anyway, I'm leaving this to help people who'd like to achieve a similar thing, and if there is a better way to achieve this I'd be grateful if you comment and guide me.
Hope it is useful 🙏
Result:
Beta Was this translation helpful? Give feedback.
All reactions