use std::collections::HashMap;
use fret_core::AppWindowId;
use crate::PlatformCapabilities;
use crate::window_style::{
ActivationPolicy, TaskbarVisibility, WindowBackgroundMaterialRequest, WindowDecorationsRequest,
WindowHitTestRequestV1, WindowStyleRequest, WindowZLevel, canonicalize_hit_test_regions_v1,
hit_test_regions_signature_v1,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RunnerWindowCompositedAlphaSourceV1 {
Unavailable,
ExplicitTrue,
ExplicitFalse,
ImpliedByMaterialCreateTime,
DefaultOpaque,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RunnerWindowAppearanceV1 {
Opaque,
CompositedNoBackdrop,
CompositedBackdrop,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RunnerWindowHitTestSourceV1 {
Default,
HitTestFacet,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RunnerWindowHitTestClampReasonV1 {
None,
MissingPassthroughAllCapability,
MissingPassthroughRegionsCapability,
}
#[derive(Debug, Clone, PartialEq)]
pub struct RunnerWindowStyleEffectiveSnapshotV1 {
pub decorations: WindowDecorationsRequest,
pub resizable: bool,
pub surface_composited_alpha: bool,
pub surface_composited_alpha_source: RunnerWindowCompositedAlphaSourceV1,
pub visual_transparent: bool,
pub appearance: RunnerWindowAppearanceV1,
pub background_material: WindowBackgroundMaterialRequest,
pub hit_test: WindowHitTestRequestV1,
pub hit_test_requested: Option<WindowHitTestRequestV1>,
pub hit_test_regions_signature: Option<String>,
pub hit_test_regions_fingerprint64: Option<u64>,
pub hit_test_regions_requested_fingerprint64: Option<u64>,
pub hit_test_source: RunnerWindowHitTestSourceV1,
pub hit_test_clamp_reason: RunnerWindowHitTestClampReasonV1,
pub taskbar: TaskbarVisibility,
pub activation: ActivationPolicy,
pub z_level: WindowZLevel,
}
impl Default for RunnerWindowStyleEffectiveSnapshotV1 {
fn default() -> Self {
Self {
decorations: WindowDecorationsRequest::System,
resizable: true,
surface_composited_alpha: false,
surface_composited_alpha_source: RunnerWindowCompositedAlphaSourceV1::DefaultOpaque,
visual_transparent: false,
appearance: RunnerWindowAppearanceV1::Opaque,
background_material: WindowBackgroundMaterialRequest::None,
hit_test: WindowHitTestRequestV1::Normal,
hit_test_requested: None,
hit_test_regions_signature: None,
hit_test_regions_fingerprint64: None,
hit_test_regions_requested_fingerprint64: None,
hit_test_source: RunnerWindowHitTestSourceV1::Default,
hit_test_clamp_reason: RunnerWindowHitTestClampReasonV1::None,
taskbar: TaskbarVisibility::Show,
activation: ActivationPolicy::Activates,
z_level: WindowZLevel::Normal,
}
}
}
#[derive(Debug, Default)]
pub struct RunnerWindowStyleDiagnosticsStore {
effective: HashMap<AppWindowId, RunnerWindowStyleEffectiveSnapshotV1>,
transparent_explicit: HashMap<AppWindowId, Option<bool>>,
transparent_implied_by_material_create_time: HashMap<AppWindowId, bool>,
}
impl RunnerWindowStyleDiagnosticsStore {
fn update_hit_test_region_signatures(snapshot: &mut RunnerWindowStyleEffectiveSnapshotV1) {
snapshot.hit_test_regions_signature = None;
snapshot.hit_test_regions_fingerprint64 = None;
snapshot.hit_test_regions_requested_fingerprint64 = None;
if let WindowHitTestRequestV1::PassthroughRegions { regions } = &snapshot.hit_test {
let (sig, fp) = hit_test_regions_signature_v1(regions);
snapshot.hit_test_regions_signature = Some(sig);
snapshot.hit_test_regions_fingerprint64 = Some(fp.fingerprint64);
}
if let Some(WindowHitTestRequestV1::PassthroughRegions { regions }) =
snapshot.hit_test_requested.as_ref()
{
let canonical = canonicalize_hit_test_regions_v1(regions.clone());
let (_sig, fp) = hit_test_regions_signature_v1(&canonical);
snapshot.hit_test_regions_requested_fingerprint64 = Some(fp.fingerprint64);
}
}
fn derive_appearance(
surface_composited_alpha: bool,
background_material: WindowBackgroundMaterialRequest,
) -> RunnerWindowAppearanceV1 {
if !surface_composited_alpha {
return RunnerWindowAppearanceV1::Opaque;
}
if background_material != WindowBackgroundMaterialRequest::None {
return RunnerWindowAppearanceV1::CompositedBackdrop;
}
RunnerWindowAppearanceV1::CompositedNoBackdrop
}
pub fn clamp_hit_test_request(
requested: WindowHitTestRequestV1,
caps: &PlatformCapabilities,
) -> (WindowHitTestRequestV1, RunnerWindowHitTestClampReasonV1) {
match requested {
WindowHitTestRequestV1::Normal => (
WindowHitTestRequestV1::Normal,
RunnerWindowHitTestClampReasonV1::None,
),
WindowHitTestRequestV1::PassthroughAll if caps.ui.window_hit_test_passthrough_all => (
WindowHitTestRequestV1::PassthroughAll,
RunnerWindowHitTestClampReasonV1::None,
),
WindowHitTestRequestV1::PassthroughAll => (
WindowHitTestRequestV1::Normal,
RunnerWindowHitTestClampReasonV1::MissingPassthroughAllCapability,
),
WindowHitTestRequestV1::PassthroughRegions { regions }
if caps.ui.window_hit_test_passthrough_regions =>
{
(
WindowHitTestRequestV1::PassthroughRegions {
regions: canonicalize_hit_test_regions_v1(regions),
},
RunnerWindowHitTestClampReasonV1::None,
)
}
WindowHitTestRequestV1::PassthroughRegions { .. }
if caps.ui.window_hit_test_passthrough_all =>
{
(
WindowHitTestRequestV1::PassthroughAll,
RunnerWindowHitTestClampReasonV1::MissingPassthroughRegionsCapability,
)
}
WindowHitTestRequestV1::PassthroughRegions { .. } => (
WindowHitTestRequestV1::Normal,
RunnerWindowHitTestClampReasonV1::MissingPassthroughRegionsCapability,
),
}
}
fn requested_hit_test_from_request(
requested: &WindowStyleRequest,
) -> Option<(WindowHitTestRequestV1, RunnerWindowHitTestSourceV1)> {
if let Some(hit_test) = requested.hit_test.clone() {
return Some((hit_test, RunnerWindowHitTestSourceV1::HitTestFacet));
}
None
}
pub fn effective_snapshot(
&self,
window: AppWindowId,
) -> Option<RunnerWindowStyleEffectiveSnapshotV1> {
self.effective.get(&window).cloned()
}
pub fn record_window_open(
&mut self,
window: AppWindowId,
requested: WindowStyleRequest,
caps: &PlatformCapabilities,
) {
let mut next = RunnerWindowStyleEffectiveSnapshotV1::default();
self.transparent_explicit
.insert(window, requested.transparent);
self.transparent_implied_by_material_create_time
.insert(window, false);
if caps.ui.window_decorations
&& let Some(decorations) = requested.decorations
{
next.decorations = decorations;
}
if caps.ui.window_resizable
&& let Some(resizable) = requested.resizable
{
next.resizable = resizable;
}
if let Some(material) = requested.background_material {
let clamped = clamp_background_material_request(material, caps);
next.background_material = clamped;
}
if !caps.ui.window_transparent {
next.surface_composited_alpha = false;
next.surface_composited_alpha_source = RunnerWindowCompositedAlphaSourceV1::Unavailable;
} else if let Some(transparent) = requested.transparent {
next.surface_composited_alpha = transparent;
next.surface_composited_alpha_source = if transparent {
RunnerWindowCompositedAlphaSourceV1::ExplicitTrue
} else {
RunnerWindowCompositedAlphaSourceV1::ExplicitFalse
};
} else if next.background_material != WindowBackgroundMaterialRequest::None {
next.surface_composited_alpha = true;
next.surface_composited_alpha_source =
RunnerWindowCompositedAlphaSourceV1::ImpliedByMaterialCreateTime;
self.transparent_implied_by_material_create_time
.insert(window, true);
} else {
next.surface_composited_alpha = false;
next.surface_composited_alpha_source =
RunnerWindowCompositedAlphaSourceV1::DefaultOpaque;
}
if !next.surface_composited_alpha {
next.background_material = WindowBackgroundMaterialRequest::None;
}
next.visual_transparent = next.background_material != WindowBackgroundMaterialRequest::None
|| matches!(requested.transparent, Some(true));
next.appearance =
Self::derive_appearance(next.surface_composited_alpha, next.background_material);
if let Some((hit_test, source)) = Self::requested_hit_test_from_request(&requested) {
next.hit_test_requested = Some(hit_test.clone());
let (effective, clamp_reason) = Self::clamp_hit_test_request(hit_test, caps);
next.hit_test = effective;
next.hit_test_source = source;
next.hit_test_clamp_reason = clamp_reason;
Self::update_hit_test_region_signatures(&mut next);
}
if let Some(taskbar) = requested.taskbar {
next.taskbar = if taskbar == TaskbarVisibility::Hide && !caps.ui.window_skip_taskbar {
TaskbarVisibility::Show
} else {
taskbar
};
}
if let Some(activation) = requested.activation {
next.activation = if activation == ActivationPolicy::NonActivating
&& !caps.ui.window_non_activating
{
ActivationPolicy::Activates
} else {
activation
};
}
if let Some(z_level) = requested.z_level {
next.z_level = if z_level == WindowZLevel::AlwaysOnTop
&& matches!(caps.ui.window_z_level, crate::WindowZLevelQuality::None)
{
WindowZLevel::Normal
} else {
z_level
};
}
self.effective.insert(window, next);
}
pub fn record_window_close(&mut self, window: AppWindowId) {
self.effective.remove(&window);
self.transparent_explicit.remove(&window);
self.transparent_implied_by_material_create_time
.remove(&window);
}
pub fn apply_style_patch(
&mut self,
window: AppWindowId,
patch: WindowStyleRequest,
caps: &PlatformCapabilities,
) {
let Some(current) = self.effective.get_mut(&window) else {
return;
};
if let Some(material) = patch.background_material {
let next_material = clamp_background_material_request(material, caps);
current.background_material = if next_material != WindowBackgroundMaterialRequest::None
&& !current.surface_composited_alpha
{
WindowBackgroundMaterialRequest::None
} else {
next_material
};
let explicit = self.transparent_explicit.get(&window).copied().flatten();
current.visual_transparent = current.background_material
!= WindowBackgroundMaterialRequest::None
|| matches!(explicit, Some(true));
current.appearance = Self::derive_appearance(
current.surface_composited_alpha,
current.background_material,
);
if !caps.ui.window_transparent {
current.surface_composited_alpha = false;
current.surface_composited_alpha_source =
RunnerWindowCompositedAlphaSourceV1::Unavailable;
} else if let Some(explicit) = explicit {
current.surface_composited_alpha = explicit;
current.surface_composited_alpha_source = if explicit {
RunnerWindowCompositedAlphaSourceV1::ExplicitTrue
} else {
RunnerWindowCompositedAlphaSourceV1::ExplicitFalse
};
} else if self
.transparent_implied_by_material_create_time
.get(&window)
.copied()
.unwrap_or(false)
{
current.surface_composited_alpha = true;
current.surface_composited_alpha_source =
RunnerWindowCompositedAlphaSourceV1::ImpliedByMaterialCreateTime;
} else {
current.surface_composited_alpha = false;
current.surface_composited_alpha_source =
RunnerWindowCompositedAlphaSourceV1::DefaultOpaque;
}
current.appearance = Self::derive_appearance(
current.surface_composited_alpha,
current.background_material,
);
}
if let Some(hit_test) = patch.hit_test {
current.hit_test_requested = Some(hit_test.clone());
let (effective, clamp_reason) = Self::clamp_hit_test_request(hit_test, caps);
current.hit_test = effective;
current.hit_test_source = RunnerWindowHitTestSourceV1::HitTestFacet;
current.hit_test_clamp_reason = clamp_reason;
Self::update_hit_test_region_signatures(current);
}
if let Some(taskbar) = patch.taskbar {
if taskbar == TaskbarVisibility::Hide && !caps.ui.window_skip_taskbar {
} else {
current.taskbar = taskbar;
}
}
if let Some(activation) = patch.activation {
if activation == ActivationPolicy::NonActivating && !caps.ui.window_non_activating {
} else {
current.activation = activation;
}
}
if let Some(z_level) = patch.z_level {
if z_level == WindowZLevel::AlwaysOnTop
&& matches!(caps.ui.window_z_level, crate::WindowZLevelQuality::None)
{
} else {
current.z_level = z_level;
}
}
}
}
pub fn clamp_background_material_request(
requested: WindowBackgroundMaterialRequest,
caps: &PlatformCapabilities,
) -> WindowBackgroundMaterialRequest {
use WindowBackgroundMaterialRequest::*;
match requested {
None => None,
SystemDefault if caps.ui.window_background_material_system_default => SystemDefault,
Mica if caps.ui.window_background_material_mica => Mica,
Acrylic if caps.ui.window_background_material_acrylic => Acrylic,
Vibrancy if caps.ui.window_background_material_vibrancy => Vibrancy,
_ => None,
}
}
#[cfg(test)]
mod tests {
use super::*;
use slotmap::KeyData;
fn window(id: u64) -> AppWindowId {
AppWindowId::from(KeyData::from_ffi(id))
}
#[test]
fn implied_transparency_stays_sticky_when_material_cleared() {
let caps = PlatformCapabilities::default();
let mut store = RunnerWindowStyleDiagnosticsStore::default();
let w = window(1);
store.record_window_open(
w,
WindowStyleRequest {
background_material: Some(WindowBackgroundMaterialRequest::Mica),
..Default::default()
},
&caps,
);
let before = store.effective_snapshot(w).unwrap();
assert!(before.surface_composited_alpha);
assert_eq!(
before.background_material,
WindowBackgroundMaterialRequest::Mica
);
store.apply_style_patch(
w,
WindowStyleRequest {
background_material: Some(WindowBackgroundMaterialRequest::None),
..Default::default()
},
&caps,
);
let after = store.effective_snapshot(w).unwrap();
assert!(after.surface_composited_alpha);
assert_eq!(
after.background_material,
WindowBackgroundMaterialRequest::None
);
}
#[test]
fn material_request_degrades_when_window_not_composited() {
let caps = PlatformCapabilities::default();
let mut store = RunnerWindowStyleDiagnosticsStore::default();
let w = window(2);
store.record_window_open(w, WindowStyleRequest::default(), &caps);
let before = store.effective_snapshot(w).unwrap();
assert!(!before.surface_composited_alpha);
assert_eq!(
before.background_material,
WindowBackgroundMaterialRequest::None
);
store.apply_style_patch(
w,
WindowStyleRequest {
background_material: Some(WindowBackgroundMaterialRequest::Mica),
..Default::default()
},
&caps,
);
let after = store.effective_snapshot(w).unwrap();
assert!(!after.surface_composited_alpha);
assert_eq!(
after.background_material,
WindowBackgroundMaterialRequest::None
);
}
#[test]
fn implied_material_transparency_clears_visually_when_material_cleared() {
let caps = PlatformCapabilities::default();
let mut store = RunnerWindowStyleDiagnosticsStore::default();
let w = window(3);
store.record_window_open(
w,
WindowStyleRequest {
background_material: Some(WindowBackgroundMaterialRequest::Mica),
..Default::default()
},
&caps,
);
let before = store.effective_snapshot(w).unwrap();
assert!(before.surface_composited_alpha);
assert!(before.visual_transparent);
assert_eq!(
before.appearance,
RunnerWindowAppearanceV1::CompositedBackdrop
);
store.apply_style_patch(
w,
WindowStyleRequest {
background_material: Some(WindowBackgroundMaterialRequest::None),
..Default::default()
},
&caps,
);
let after = store.effective_snapshot(w).unwrap();
assert!(after.surface_composited_alpha);
assert!(!after.visual_transparent);
assert_eq!(
after.background_material,
WindowBackgroundMaterialRequest::None
);
assert_eq!(
after.appearance,
RunnerWindowAppearanceV1::CompositedNoBackdrop
);
}
#[test]
fn explicit_transparent_window_defaults_to_visual_transparency_without_material() {
let caps = PlatformCapabilities::default();
let mut store = RunnerWindowStyleDiagnosticsStore::default();
let w = window(4);
store.record_window_open(
w,
WindowStyleRequest {
transparent: Some(true),
..Default::default()
},
&caps,
);
let have = store.effective_snapshot(w).unwrap();
assert!(have.surface_composited_alpha);
assert!(have.visual_transparent);
assert_eq!(
have.background_material,
WindowBackgroundMaterialRequest::None
);
assert_eq!(
have.appearance,
RunnerWindowAppearanceV1::CompositedNoBackdrop
);
}
#[test]
fn hit_test_passthrough_all_degrades_when_unsupported() {
let mut caps = PlatformCapabilities::default();
caps.ui.window_hit_test_passthrough_all = false;
let mut store = RunnerWindowStyleDiagnosticsStore::default();
let w = window(6);
store.record_window_open(
w,
WindowStyleRequest {
hit_test: Some(WindowHitTestRequestV1::PassthroughAll),
..Default::default()
},
&caps,
);
let have = store.effective_snapshot(w).unwrap();
assert_eq!(have.hit_test, WindowHitTestRequestV1::Normal);
assert_eq!(
have.hit_test_requested,
Some(WindowHitTestRequestV1::PassthroughAll)
);
assert_eq!(
have.hit_test_source,
RunnerWindowHitTestSourceV1::HitTestFacet
);
assert_eq!(
have.hit_test_clamp_reason,
RunnerWindowHitTestClampReasonV1::MissingPassthroughAllCapability
);
}
}