use std::cell::RefCell;
use windows::{
UI::Color,
core::{IInspectable_Vtbl, Interface, Ref, Result, imp::WeakRefCount, implement},
};
use winio_primitive::ColorTheme;
use winui3::{
ChildClass, Compose, CreateInstanceFn,
Microsoft::UI::{
Composition::{
ICompositionSupportsSystemBackdrop,
SystemBackdrops::{DesktopAcrylicController, SystemBackdropConfiguration},
},
Xaml::{
self as MUX,
Media::{
ISystemBackdropFactory, ISystemBackdropOverrides, ISystemBackdropOverrides_Impl,
SystemBackdrop,
},
},
},
};
use crate::color_theme;
const fn color(dark: bool) -> Color {
if dark {
Color {
A: 0xFF,
R: 0x54,
G: 0x54,
B: 0x54,
}
} else {
Color {
A: 0xFF,
R: 0xD3,
G: 0xD3,
B: 0xD3,
}
}
}
fn update_color(controller: &DesktopAcrylicController) -> Result<()> {
let color = color(color_theme() == ColorTheme::Dark);
controller.SetTintColor(color)?;
controller.SetFallbackColor(color)?;
Ok(())
}
struct CustomDesktopAcrylicBackdropControllerEntry {
controller: DesktopAcrylicController,
target: ICompositionSupportsSystemBackdrop,
}
impl CustomDesktopAcrylicBackdropControllerEntry {
pub fn new(
target: ICompositionSupportsSystemBackdrop,
controller: DesktopAcrylicController,
configuration: &SystemBackdropConfiguration,
) -> Result<Self> {
controller.AddSystemBackdropTarget(&target)?;
controller.SetSystemBackdropConfiguration(configuration)?;
Ok(Self { target, controller })
}
}
impl Drop for CustomDesktopAcrylicBackdropControllerEntry {
fn drop(&mut self) {
self.controller
.RemoveSystemBackdropTarget(&self.target)
.ok();
self.controller.Close().ok();
}
}
#[implement(ISystemBackdropOverrides, Agile = false)]
pub struct CustomDesktopAcrylicBackdrop {
controllers: RefCell<Vec<CustomDesktopAcrylicBackdropControllerEntry>>,
}
impl CustomDesktopAcrylicBackdrop {
pub fn compose() -> Result<SystemBackdrop> {
Compose::compose(Self {
controllers: RefCell::new(vec![]),
})
}
}
impl ISystemBackdropOverrides_Impl for CustomDesktopAcrylicBackdrop_Impl {
fn OnTargetConnected(
&self,
target: Ref<ICompositionSupportsSystemBackdrop>,
root: Ref<MUX::XamlRoot>,
) -> Result<()> {
let base = unsafe { Compose::<CustomDesktopAcrylicBackdrop>::base(self) };
let target = target.ok()?;
let root = root.ok()?;
unsafe {
let base = base.cast::<ISystemBackdropOverrides>()?;
(base.vtable().OnTargetConnected)(base.as_raw(), target.as_raw(), root.as_raw())
.ok()?;
}
let configuration = base
.cast::<SystemBackdrop>()?
.GetDefaultSystemBackdropConfiguration(target, root)?;
let controller = DesktopAcrylicController::new()?;
controller.SetLuminosityOpacity(0.65)?;
update_color(&controller)?;
self.controllers
.borrow_mut()
.push(CustomDesktopAcrylicBackdropControllerEntry::new(
target.clone(),
controller,
&configuration,
)?);
Ok(())
}
fn OnTargetDisconnected(&self, target: Ref<ICompositionSupportsSystemBackdrop>) -> Result<()> {
let base = unsafe { Compose::<CustomDesktopAcrylicBackdrop>::base(self) };
let target = target.ok()?;
unsafe {
let base = base.cast::<ISystemBackdropOverrides>()?;
(base.vtable().OnTargetDisconnected)(base.as_raw(), target.as_raw()).ok()?;
}
let mut controllers = self.controllers.borrow_mut();
if let Some(pos) = controllers.iter().position(|entry| entry.target == *target) {
controllers.remove(pos);
}
Ok(())
}
fn OnDefaultSystemBackdropConfigurationChanged(
&self,
target: Ref<ICompositionSupportsSystemBackdrop>,
root: Ref<MUX::XamlRoot>,
) -> Result<()> {
let base = unsafe { Compose::<CustomDesktopAcrylicBackdrop>::base(self) };
let target = target.ok()?;
let root = root.ok()?;
unsafe {
let base = base.cast::<ISystemBackdropOverrides>()?;
(base.vtable().OnDefaultSystemBackdropConfigurationChanged)(
base.as_raw(),
target.as_raw(),
root.as_raw(),
)
.ok()?;
}
for entry in self.controllers.borrow().iter() {
if entry.target == *target {
update_color(&entry.controller)?;
break;
}
}
Ok(())
}
}
impl ChildClass for CustomDesktopAcrylicBackdrop {
type BaseType = SystemBackdrop;
type FactoryInterface = ISystemBackdropFactory;
fn create_interface_fn(
vtable: &<Self::FactoryInterface as Interface>::Vtable,
) -> CreateInstanceFn {
vtable.CreateInstance
}
fn identity_vtable(vtable: &mut Self::Outer) -> &mut &'static IInspectable_Vtbl {
&mut vtable.identity
}
fn ref_count(vtable: &Self::Outer) -> &WeakRefCount {
&vtable.count
}
fn into_outer(self) -> Self::Outer {
Self::into_outer(self)
}
}