Respect motion settings and improve accessible theme transitions
This commit is contained in:
1 parent
53bad8c672
commit
7a9fd5c864
13 files changed
+390
-67
No files matched your search
@@ -42,6 +42,7 @@ impl Default for StatusConfig {
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub(crate) struct ShellTheme {
|
||||
pub(crate) appearance: ferese_config::theme::Appearance,
|
||||
pub(crate) high_contrast: bool,
|
||||
pub(crate) bar_background: [u8; 4],
|
||||
pub(crate) bar_text_primary: [u8; 4],
|
||||
pub(crate) bar_text_muted: [u8; 4],
|
||||
@@ -70,6 +71,7 @@ impl Default for ShellTheme {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
appearance: Default::default(),
|
||||
high_contrast: false,
|
||||
bar_background: [28, 32, 46, 255],
|
||||
bar_text_primary: [240, 243, 250, 255],
|
||||
bar_text_muted: [170, 180, 199, 255],
|
||||
@@ -102,6 +104,7 @@ impl ShellTheme {
|
||||
let surface = color(self.surface_base);
|
||||
ferese_theme::Palette {
|
||||
appearance: self.appearance,
|
||||
high_contrast: self.high_contrast,
|
||||
background: surface,
|
||||
sidebar: surface,
|
||||
card: surface,
|
||||
@@ -339,11 +342,13 @@ pub(crate) fn load() -> ShellConfig {
|
||||
pub(crate) fn parse_source(source: &str) -> Result<ShellConfig, ferese_config::Error> {
|
||||
let document = ferese_config::Document::parse(source)?;
|
||||
let snapshot = ferese_theme::service::current();
|
||||
parse_document(
|
||||
let mut config = parse_document(
|
||||
&document.with_theme(&snapshot.presented),
|
||||
snapshot.presented.appearance,
|
||||
snapshot.mode,
|
||||
)
|
||||
)?;
|
||||
config.apply_theme(&snapshot.presented);
|
||||
Ok(config)
|
||||
}
|
||||
|
||||
fn parse_document(
|
||||
@@ -394,6 +399,7 @@ impl ShellConfig {
|
||||
let config: ThemeConfig = serde_json::from_value(serde_json::to_value(&theme.tokens).unwrap()).unwrap();
|
||||
self.theme = shell_theme(&config);
|
||||
self.theme.appearance = theme.appearance;
|
||||
self.theme.high_contrast = theme.accessibility.increase_contrast;
|
||||
self.theme.material_radius = theme.tokens.geometry.shell_radius as f32;
|
||||
self.theme.bar_radius = self.theme.material_radius;
|
||||
self.font_family = Some(theme.tokens.typography.font_family.clone());
|
||||
@@ -422,6 +428,7 @@ fn shell_theme(theme: &ThemeConfig) -> ShellTheme {
|
||||
|
||||
ShellTheme {
|
||||
appearance: defaults.appearance,
|
||||
high_contrast: defaults.high_contrast,
|
||||
material_radius: defaults.material_radius,
|
||||
bar_background: {
|
||||
let mut background = parse_color(&theme.surface.bar.background).unwrap_or(defaults.bar_background);
|
||||
|
||||
@@ -874,10 +874,13 @@ impl FereseShell {
|
||||
}
|
||||
motion::configure(config.animations, config.theme.material_radius);
|
||||
|
||||
if self.config.animations != config.animations
|
||||
&& let Some(menu) = &mut self.menu
|
||||
{
|
||||
menu.motion.update_settings(config.animations);
|
||||
if self.config.animations != config.animations {
|
||||
if let Some(menu) = &mut self.menu {
|
||||
menu.motion.update_settings(config.animations);
|
||||
}
|
||||
if let Some(modal) = &mut self.system_modal {
|
||||
modal.motion.update_settings(config.animations);
|
||||
}
|
||||
}
|
||||
self.status_service
|
||||
.update_settings(config.status.settings_command.clone());
|
||||
|
||||
@@ -76,7 +76,9 @@ impl PopupMotion {
|
||||
let now = std::time::Instant::now();
|
||||
self.start = self.progress_at(now);
|
||||
self.settings = settings;
|
||||
self.started = Some(now);
|
||||
if self.started.is_some() {
|
||||
self.started = Some(now);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn new(settings: Settings) -> Self {
|
||||
@@ -95,13 +97,13 @@ impl PopupMotion {
|
||||
}
|
||||
|
||||
pub(crate) fn progress_at(&self, now: std::time::Instant) -> f32 {
|
||||
let Some(started) = self.started else {
|
||||
return self.start;
|
||||
};
|
||||
let duration = self.settings.duration(if self.target == 1.0 { 200.0 } else { 140.0 });
|
||||
if duration.is_zero() {
|
||||
return self.target;
|
||||
}
|
||||
let Some(started) = self.started else {
|
||||
return self.start;
|
||||
};
|
||||
let t = (now.saturating_duration_since(started).as_secs_f32() / duration.as_secs_f32()).min(1.0);
|
||||
if t == 1.0 {
|
||||
return self.target;
|
||||
@@ -422,6 +424,11 @@ impl<M> Widget<M, Theme, cosmic::Renderer> for Motion<'_, M> {
|
||||
) {
|
||||
let v = self.translation();
|
||||
let transform = Transformation::translate(v.x, v.y);
|
||||
// Layout/region collection still run before the surface starts opening.
|
||||
// No child may paint an opaque control before the presentation begins.
|
||||
if self.progress <= 0.0 {
|
||||
return;
|
||||
}
|
||||
renderer.with_transformation(transform, |renderer| {
|
||||
self.content
|
||||
.as_widget()
|
||||
@@ -556,12 +563,26 @@ mod tests {
|
||||
},
|
||||
] {
|
||||
let mut motion = PopupMotion::new(settings);
|
||||
assert_eq!(motion.progress_at(now), 0.0);
|
||||
motion.begin(now);
|
||||
assert_eq!(motion.progress_at(now), 1.0);
|
||||
motion.retarget(0.0, now);
|
||||
assert_eq!(motion.progress_at(now), 0.0);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn updating_motion_policy_preserves_surface_readiness() {
|
||||
let mut motion = PopupMotion::new(Settings::default());
|
||||
motion.update_settings(Settings {
|
||||
reduced_motion: true,
|
||||
..Settings::default()
|
||||
});
|
||||
assert_eq!(motion.progress(), 0.0);
|
||||
motion.begin(std::time::Instant::now());
|
||||
assert_eq!(motion.progress(), 1.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hover_fades_settle_and_idle_does_not_schedule_frames() {
|
||||
let now = std::time::Instant::now();
|
||||
|
||||
@@ -242,13 +242,15 @@ impl FereseShell {
|
||||
let Some(entry) = modal.surfaces.iter_mut().find(|entry| entry.id == id) else {
|
||||
return;
|
||||
};
|
||||
modal.motion.begin(Instant::now());
|
||||
if entry.primary && entry.effects.is_none() {
|
||||
match EffectsBinding::attach_role(surface, None, 0.0) {
|
||||
Ok(effects) => entry.effects = Some(effects),
|
||||
Err(error) => eprintln!("ferese-shell: system modal material unavailable: {error}"),
|
||||
}
|
||||
}
|
||||
if entry.primary {
|
||||
modal.motion.begin(Instant::now());
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn update_power_materials(&self) {
|
||||
@@ -422,13 +424,8 @@ impl FereseShell {
|
||||
let content: Element<'_, cosmic::Action<Message>> = if surface.primary {
|
||||
let theme = self.config.theme;
|
||||
let material = surface.effects.is_some();
|
||||
let alpha = if material { 1.0 } else { progress };
|
||||
let mut palette = theme.palette();
|
||||
palette.text = palette.text.scale_alpha(alpha);
|
||||
palette.muted = palette.muted.scale_alpha(alpha);
|
||||
palette.sidebar = palette.sidebar.scale_alpha(alpha);
|
||||
palette.card = palette.card.scale_alpha(alpha);
|
||||
palette.accent = palette.accent.scale_alpha(alpha);
|
||||
// Opacity belongs to the presented surface, never to semantic colors.
|
||||
let palette = theme.palette();
|
||||
let rows = match &modal.content {
|
||||
Content::Power(action) => {
|
||||
let mut rows = column![
|
||||
@@ -526,7 +523,7 @@ impl FereseShell {
|
||||
.padding(24)
|
||||
.class(theme::Container::custom(move |_| container::Style {
|
||||
background: (!material)
|
||||
.then_some(Background::Color(color_with_opacity(theme.surface_popover, alpha))),
|
||||
.then_some(Background::Color(color_with_opacity(theme.surface_popover, 1.0))),
|
||||
text_color: Some(palette.text),
|
||||
icon_color: Some(palette.text),
|
||||
border: Border {
|
||||
@@ -550,6 +547,7 @@ impl FereseShell {
|
||||
} else {
|
||||
content
|
||||
};
|
||||
let scrim_alpha = if surface.effects.is_some() { 1.0 } else { progress };
|
||||
let centered = container(content)
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill)
|
||||
@@ -557,7 +555,7 @@ impl FereseShell {
|
||||
.align_x(cosmic::iced::Alignment::Center)
|
||||
.align_y(cosmic::iced::Alignment::Center)
|
||||
.class(theme::Container::custom(move |_| container::Style {
|
||||
background: Some(Background::Color(Color::from_rgba(0.0, 0.0, 0.0, 0.34 * progress))),
|
||||
background: Some(Background::Color(Color::from_rgba(0.0, 0.0, 0.0, 0.34 * scrim_alpha))),
|
||||
..Default::default()
|
||||
}));
|
||||
centered.into()
|
||||
|
||||
Reference in new issue
Block a user