Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions niri-config/src/appearance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,8 @@ pub struct FocusRing {
pub active_gradient: Option<Gradient>,
pub inactive_gradient: Option<Gradient>,
pub urgent_gradient: Option<Gradient>,
pub fade_duration_ms: u64,
pub gradient_spin_speed: f64,
}

impl Default for FocusRing {
Expand All @@ -251,6 +253,8 @@ impl Default for FocusRing {
active_gradient: None,
inactive_gradient: None,
urgent_gradient: None,
fade_duration_ms: 0,
gradient_spin_speed: 0.,
}
}
}
Expand Down Expand Up @@ -293,6 +297,8 @@ impl From<Border> for FocusRing {
active_gradient: value.active_gradient,
inactive_gradient: value.inactive_gradient,
urgent_gradient: value.urgent_gradient,
fade_duration_ms: 0,
gradient_spin_speed: 0.,
}
}
}
Expand Down Expand Up @@ -332,9 +338,19 @@ impl MergeWith<BorderRule> for Border {

impl MergeWith<BorderRule> for FocusRing {
fn merge_with(&mut self, part: &BorderRule) {
let saved_fade = self.fade_duration_ms;
let saved_spin = self.gradient_spin_speed;
let mut x = Border::from(*self);
x.merge_with(part);
*self = FocusRing::from(x);
self.fade_duration_ms = match part.fade_duration_ms {
Some(v) => v.0 as u64,
None => saved_fade,
};
self.gradient_spin_speed = match part.gradient_spin_speed {
Some(v) => v.0 as f64,
None => saved_spin,
};
}
}

Expand Down Expand Up @@ -642,6 +658,10 @@ pub struct BorderRule {
pub inactive_gradient: Option<Gradient>,
#[knuffel(child)]
pub urgent_gradient: Option<Gradient>,
#[knuffel(child, unwrap(argument))]
pub fade_duration_ms: Option<FloatOrInt<0, 65535>>,
#[knuffel(child, unwrap(argument))]
pub gradient_spin_speed: Option<FloatOrInt<0, 3600>>,
}

#[derive(knuffel::Decode, Debug, Default, Clone, Copy, PartialEq)]
Expand Down
3 changes: 2 additions & 1 deletion src/layout/focus_ring.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ impl FocusRing {
radius: CornerRadius,
scale: f64,
alpha: f32,
gradient_angle_offset: f32,
) {
let width = self.config.width;
self.full_size = win_size + Size::from((width, width)).upscale(2.);
Expand Down Expand Up @@ -187,7 +188,7 @@ impl FocusRing {
gradient.in_,
gradient.from,
gradient.to,
((gradient.angle as f32) - 90.).to_radians(),
((gradient.angle as f32) - 90. + gradient_angle_offset).to_radians(),
Rectangle::new(full_rect.loc - loc, full_rect.size),
rounded_corner_border_width,
radius,
Expand Down
6 changes: 5 additions & 1 deletion src/layout/insert_hint_element.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ impl InsertHintElement {
active_gradient: config.gradient,
inactive_gradient: config.gradient,
urgent_gradient: config.gradient,
fade_duration_ms: 0,
gradient_spin_speed: 0.,
}),
}
}
Expand All @@ -37,6 +39,8 @@ impl InsertHintElement {
active_gradient: config.gradient,
inactive_gradient: config.gradient,
urgent_gradient: config.gradient,
fade_duration_ms: 0,
gradient_spin_speed: 0.,
});
}

Expand All @@ -52,7 +56,7 @@ impl InsertHintElement {
scale: f64,
) {
self.inner
.update_render_elements(size, true, false, false, view_rect, radius, scale, 1.);
.update_render_elements(size, true, false, false, view_rect, radius, scale, 1., 0.);
}

pub fn render(
Expand Down
66 changes: 64 additions & 2 deletions src/layout/tile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,12 @@ pub struct Tile<W: LayoutElement> {
/// The animation of the tile's opacity.
pub(super) alpha_animation: Option<AlphaAnimation>,

/// The animation of the focus ring fading in/out.
focus_ring_anim: Option<Animation>,

/// Whether the tile was focused on the last render update.
was_focus_active: bool,

/// Offset during the initial interactive move rubberband.
pub(super) interactive_move_offset: Point<f64, Logical>,

Expand Down Expand Up @@ -202,6 +208,8 @@ impl<W: LayoutElement> Tile<W> {
move_x_animation: None,
move_y_animation: None,
alpha_animation: None,
focus_ring_anim: None,
was_focus_active: false,
interactive_move_offset: Point::from((0., 0.)),
unmap_snapshot: None,
rounded_corner_damage: Default::default(),
Expand Down Expand Up @@ -450,6 +458,11 @@ impl<W: LayoutElement> Tile<W> {
.alpha_animation
.as_ref()
.is_some_and(|alpha| !alpha.anim.is_done())
|| self
.focus_ring_anim
.as_ref()
.is_some_and(|anim| !anim.is_done())
|| (self.was_focus_active && self.options.layout.focus_ring.gradient_spin_speed > 0.)
}

pub fn update_render_elements(&mut self, is_active: bool, view_rect: Rectangle<f64, Logical>) {
Expand Down Expand Up @@ -487,6 +500,7 @@ impl<W: LayoutElement> Tile<W> {
radius,
self.scale,
1. - expanded_progress as f32,
0.,
);

let radius = if self.visual_border_width().is_some() {
Expand All @@ -511,15 +525,63 @@ impl<W: LayoutElement> Tile<W> {
false
};
let radius = radius.expanded_by(self.focus_ring.width() as f32);

let fade_ms = self.options.layout.focus_ring.fade_duration_ms;

// Animate focus ring fade in/out on focus change.
if is_active != self.was_focus_active {
if fade_ms > 0 {
let (from, to) = if is_active { (0., 1.) } else { (1., 0.) };
self.focus_ring_anim = Some(Animation::ease(
self.clock.clone(),
from,
to,
0.,
fade_ms,
crate::animation::Curve::EaseOutCubic,
));
} else {
self.focus_ring_anim = None;
}
self.was_focus_active = is_active;
}

let focus_ring_alpha = if fade_ms > 0 {
match &self.focus_ring_anim {
Some(anim) if !anim.is_done() => {
anim.clamped_value() as f32
}
_ => {
self.focus_ring_anim = None;
if is_active { 1.0 } else { 0.0 }
}
}
} else {
if is_active { 1.0 } else { 0.0 }
};

// During fade-out, keep rendering with active colors.
let ring_is_active = is_active || focus_ring_alpha > 0.0;

// Rotate gradient while focus ring is visible.
let spin_speed = self.options.layout.focus_ring.gradient_spin_speed as f32;
let gradient_angle_offset = if ring_is_active && spin_speed > 0. {
let secs = self.clock.now().as_secs_f32();
(secs * spin_speed) % 360.
} else {
0.
};

self.focus_ring.update_render_elements(
animated_tile_size,
is_active,
ring_is_active,
!draw_focus_ring_with_background,
self.window.is_urgent(),
view_rect,
radius,
self.scale,
1. - expanded_progress as f32,
focus_ring_alpha * (1. - expanded_progress as f32),
gradient_angle_offset,
);

self.fullscreen_backdrop.resize(animated_tile_size);
Expand Down
2 changes: 2 additions & 0 deletions src/ui/mru.rs
Original file line number Diff line number Diff line change
Expand Up @@ -531,6 +531,7 @@ impl Thumbnail {
radius,
scale,
0.5,
0.,
);
let bg_elems = background
.render(renderer, loc)
Expand All @@ -552,6 +553,7 @@ impl Thumbnail {
radius.expanded_by(config.width as f32),
scale,
1.,
0.,
);

let border_elems = border
Expand Down