Refine shell status controls and glass rendering; defer cursor redraw safely
This commit is contained in:
1 parent
e85fc1e032
commit
b3fa82b53b
53 files changed
+3329
-259
No files matched your search
+165
-45
@@ -16,7 +16,10 @@ use smithay::{
|
||||
};
|
||||
|
||||
use crate::{
|
||||
Ferese, config::MaterialStyle, private_client::ClientCapabilities, state::ClientState,
|
||||
Ferese,
|
||||
config::{GlassQuality, GlassSettings, MaterialStyle},
|
||||
private_client::ClientCapabilities,
|
||||
state::ClientState,
|
||||
};
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
@@ -38,47 +41,72 @@ pub(crate) struct ResolvedMaterial {
|
||||
pub saturation: f32,
|
||||
pub brightness: f32,
|
||||
pub noise: f32,
|
||||
pub shadow: [f64; 3],
|
||||
}
|
||||
|
||||
pub(crate) fn resolve_material(role: SemanticRole, style: MaterialStyle) -> ResolvedMaterial {
|
||||
let (base_opacity, blur, saturation, brightness, noise) = match role {
|
||||
SemanticRole::Panel => (0.78, 24.0, 1.08, 1.02, 0.012),
|
||||
SemanticRole::PanelElevated
|
||||
| SemanticRole::Popover
|
||||
| SemanticRole::Menu
|
||||
| SemanticRole::Notification => (0.84, 28.0, 1.10, 1.03, 0.014),
|
||||
SemanticRole::Hud | SemanticRole::Modal => (0.88, 30.0, 1.06, 1.04, 0.010),
|
||||
pub(crate) fn resolve_material(
|
||||
role: SemanticRole,
|
||||
style: MaterialStyle,
|
||||
settings: GlassSettings,
|
||||
) -> ResolvedMaterial {
|
||||
// Restrained shell shadows in logical pixels: the full-width bar needs
|
||||
// less elevation than popovers, while dialogs retain a little more depth.
|
||||
let (opacity, blur, saturation, shadow) = match role {
|
||||
SemanticRole::Panel => (0.55, 24.0, 1.35, [1.0, 5.0, 0.04]),
|
||||
SemanticRole::PanelElevated => (0.55, 24.0, 1.35, [2.0, 8.0, 0.07]),
|
||||
SemanticRole::Popover => (0.55, 22.0, 1.35, [2.0, 8.0, 0.07]),
|
||||
SemanticRole::Menu => (0.60, 20.0, 1.30, [2.0, 8.0, 0.07]),
|
||||
SemanticRole::Hud => (0.65, 18.0, 1.25, [1.0, 4.0, 0.04]),
|
||||
SemanticRole::Notification => (0.58, 24.0, 1.35, [3.0, 10.0, 0.09]),
|
||||
SemanticRole::Modal => (0.62, 28.0, 1.40, [3.0, 10.0, 0.09]),
|
||||
};
|
||||
|
||||
match style {
|
||||
MaterialStyle::Glass => ResolvedMaterial {
|
||||
style,
|
||||
// Glass already contains an opaque copy of the backdrop. Its tint
|
||||
// therefore needs less coverage than the no-capture translucent
|
||||
// profile or the blur becomes visually indistinguishable from a
|
||||
// flat alpha surface.
|
||||
opacity: base_opacity * 0.72,
|
||||
blur,
|
||||
saturation,
|
||||
brightness,
|
||||
noise,
|
||||
// Preserve the separately defined translucent family when selected
|
||||
// directly. Glass degradation keeps the glass role's contrast floor.
|
||||
let opacity = if style == MaterialStyle::Translucent {
|
||||
match role {
|
||||
SemanticRole::Panel => 0.78,
|
||||
SemanticRole::Hud | SemanticRole::Modal => 0.88,
|
||||
_ => 0.84,
|
||||
}
|
||||
} else {
|
||||
opacity
|
||||
};
|
||||
let style = if style == MaterialStyle::Glass {
|
||||
match settings.quality {
|
||||
GlassQuality::Solid => MaterialStyle::Solid,
|
||||
GlassQuality::Translucent => MaterialStyle::Translucent,
|
||||
_ => style,
|
||||
}
|
||||
} else {
|
||||
style
|
||||
};
|
||||
let glass = style == MaterialStyle::Glass;
|
||||
ResolvedMaterial {
|
||||
style,
|
||||
opacity: if style == MaterialStyle::Solid {
|
||||
1.0
|
||||
} else {
|
||||
opacity
|
||||
},
|
||||
MaterialStyle::Translucent => ResolvedMaterial {
|
||||
style,
|
||||
opacity: base_opacity,
|
||||
blur: 0.0,
|
||||
saturation: 1.0,
|
||||
brightness: 1.0,
|
||||
noise: 0.0,
|
||||
blur: if !glass {
|
||||
0.0
|
||||
} else if settings.quality >= GlassQuality::ReducedBlur {
|
||||
(blur * settings.blur_scale).min(12.0)
|
||||
} else {
|
||||
blur * settings.blur_scale
|
||||
},
|
||||
MaterialStyle::Solid => ResolvedMaterial {
|
||||
style,
|
||||
opacity: 1.0,
|
||||
blur: 0.0,
|
||||
saturation: 1.0,
|
||||
brightness: 1.0,
|
||||
noise: 0.0,
|
||||
saturation: if glass && settings.quality < GlassQuality::NoSaturation {
|
||||
saturation
|
||||
} else {
|
||||
1.0
|
||||
},
|
||||
brightness: 1.0,
|
||||
noise: if glass && settings.grain && settings.quality < GlassQuality::NoGrain {
|
||||
0.05
|
||||
} else {
|
||||
0.0
|
||||
},
|
||||
shadow,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -297,13 +325,25 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn styles_preserve_semantics_but_disable_unsupported_effects() {
|
||||
let glass = resolve_material(SemanticRole::Popover, MaterialStyle::Glass);
|
||||
let translucent = resolve_material(SemanticRole::Popover, MaterialStyle::Translucent);
|
||||
let solid = resolve_material(SemanticRole::Popover, MaterialStyle::Solid);
|
||||
let glass = resolve_material(
|
||||
SemanticRole::Popover,
|
||||
MaterialStyle::Glass,
|
||||
GlassSettings::default(),
|
||||
);
|
||||
let translucent = resolve_material(
|
||||
SemanticRole::Popover,
|
||||
MaterialStyle::Translucent,
|
||||
GlassSettings::default(),
|
||||
);
|
||||
let solid = resolve_material(
|
||||
SemanticRole::Popover,
|
||||
MaterialStyle::Solid,
|
||||
GlassSettings::default(),
|
||||
);
|
||||
|
||||
assert!(glass.blur > 0.0);
|
||||
assert!(glass.noise > 0.0);
|
||||
assert!(glass.opacity < translucent.opacity);
|
||||
assert!(glass.opacity <= translucent.opacity);
|
||||
assert_eq!(translucent.blur, 0.0);
|
||||
assert_eq!(translucent.noise, 0.0);
|
||||
assert_eq!(solid.opacity, 1.0);
|
||||
@@ -311,11 +351,91 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn elevated_roles_resolve_more_strongly_than_the_panel() {
|
||||
let panel = resolve_material(SemanticRole::Panel, MaterialStyle::Glass);
|
||||
let popover = resolve_material(SemanticRole::Popover, MaterialStyle::Glass);
|
||||
fn glass_presets_use_role_specific_tints_and_restrained_shadows() {
|
||||
for (role, blur, saturation, opacity, shadow) in [
|
||||
(SemanticRole::Panel, 24.0, 1.35, 0.55, [1.0, 5.0, 0.04]),
|
||||
(SemanticRole::Popover, 22.0, 1.35, 0.55, [2.0, 8.0, 0.07]),
|
||||
(SemanticRole::Menu, 20.0, 1.30, 0.60, [2.0, 8.0, 0.07]),
|
||||
(SemanticRole::Hud, 18.0, 1.25, 0.65, [1.0, 4.0, 0.04]),
|
||||
(
|
||||
SemanticRole::Notification,
|
||||
24.0,
|
||||
1.35,
|
||||
0.58,
|
||||
[3.0, 10.0, 0.09],
|
||||
),
|
||||
(SemanticRole::Modal, 28.0, 1.40, 0.62, [3.0, 10.0, 0.09]),
|
||||
] {
|
||||
let material = resolve_material(role, MaterialStyle::Glass, GlassSettings::default());
|
||||
assert_eq!(
|
||||
(
|
||||
material.blur,
|
||||
material.saturation,
|
||||
material.opacity,
|
||||
material.shadow
|
||||
),
|
||||
(blur, saturation, opacity, shadow)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
assert!(popover.opacity > panel.opacity);
|
||||
assert!(popover.blur > panel.blur);
|
||||
#[test]
|
||||
fn degradation_is_ordered_and_preserves_contrast_and_elevation() {
|
||||
let reference = resolve_material(
|
||||
SemanticRole::Modal,
|
||||
MaterialStyle::Glass,
|
||||
GlassSettings::default(),
|
||||
);
|
||||
for quality in [
|
||||
GlassQuality::Full,
|
||||
GlassQuality::NoGrain,
|
||||
GlassQuality::NoSaturation,
|
||||
GlassQuality::ReducedBlur,
|
||||
GlassQuality::Translucent,
|
||||
GlassQuality::Solid,
|
||||
] {
|
||||
let material = resolve_material(
|
||||
SemanticRole::Modal,
|
||||
MaterialStyle::Glass,
|
||||
GlassSettings {
|
||||
quality,
|
||||
..GlassSettings::default()
|
||||
},
|
||||
);
|
||||
assert!(material.opacity >= reference.opacity);
|
||||
assert_eq!(material.shadow, reference.shadow);
|
||||
if quality >= GlassQuality::NoGrain {
|
||||
assert_eq!(material.noise, 0.0);
|
||||
}
|
||||
if quality >= GlassQuality::NoSaturation {
|
||||
assert_eq!(material.saturation, 1.0);
|
||||
}
|
||||
if quality >= GlassQuality::ReducedBlur {
|
||||
assert!(material.blur <= 12.0);
|
||||
}
|
||||
if quality >= GlassQuality::Translucent {
|
||||
assert_eq!(material.blur, 0.0);
|
||||
}
|
||||
if quality == GlassQuality::Solid {
|
||||
assert_eq!(material.opacity, 1.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn popover_uses_the_same_tint_with_a_smaller_blur_than_panel() {
|
||||
let panel = resolve_material(
|
||||
SemanticRole::Panel,
|
||||
MaterialStyle::Glass,
|
||||
GlassSettings::default(),
|
||||
);
|
||||
let popover = resolve_material(
|
||||
SemanticRole::Popover,
|
||||
MaterialStyle::Glass,
|
||||
GlassSettings::default(),
|
||||
);
|
||||
|
||||
assert_eq!(popover.opacity, panel.opacity);
|
||||
assert!(popover.blur < panel.blur);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user