aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIvan Molodetskikh <yalterz@gmail.com>2025-08-12 22:34:13 +0300
committerIvan Molodetskikh <yalterz@gmail.com>2025-08-14 15:58:59 +0300
commite3101ced70b908d4fc9bceef47e878d2de2b4c5d (patch)
tree60962874a6544f43a2531e064d104c150cde96b1
parentea438b21e933d45672e80ca04db42eb54050fcca (diff)
downloadniri-e3101ced70b908d4fc9bceef47e878d2de2b4c5d.tar.gz
niri-e3101ced70b908d4fc9bceef47e878d2de2b4c5d.tar.bz2
niri-e3101ced70b908d4fc9bceef47e878d2de2b4c5d.zip
layout: Offset Y animations for non-animated resizes
-rw-r--r--src/layout/scrolling.rs35
-rw-r--r--src/layout/tests/animations.rs56
-rw-r--r--src/layout/tile.rs11
3 files changed, 71 insertions, 31 deletions
diff --git a/src/layout/scrolling.rs b/src/layout/scrolling.rs
index 94f2aa97..1d43ac56 100644
--- a/src/layout/scrolling.rs
+++ b/src/layout/scrolling.rs
@@ -4056,12 +4056,35 @@ impl<W: LayoutElement> Column<W> {
// windows in the column, so they should all be animated. How should this interact with
// animated vs. non-animated resizes? For example, an animated +20 resize followed by two
// non-animated -10 resizes.
- if !is_tabbed && tile.resize_animation().is_some() && offset != 0. {
- for tile in &mut self.tiles[tile_idx + 1..] {
- tile.animate_move_y_from_with_config(
- offset,
- self.options.animations.window_resize.anim,
- );
+ if !is_tabbed && offset != 0. {
+ if tile.resize_animation().is_some() {
+ // If there's a resize animation (that may have just started in
+ // tile.update_window()), then the apparent size change is smooth with no sudden
+ // jumps. This corresponds to adding an Y animation to tiles below.
+ for tile in &mut self.tiles[tile_idx + 1..] {
+ tile.animate_move_y_from_with_config(
+ offset,
+ self.options.animations.window_resize.anim,
+ );
+ }
+ } else {
+ // There's no resize animation, but the offset is nonzero. This could happen for
+ // example:
+ // - if the window resized on its own, which we don't animate
+ // - if the window resized by less than 10 px (the resize threshold)
+ //
+ // The latter case could also cancel an ongoing resize animation.
+ //
+ // Now, stationary tiles below shouldn't react to this offset change in any way,
+ // i.e. their apparent Y position should jump together with the resize. However,
+ // tiles below that are already animating an Y movement should offset their
+ // animations to avoid the jump.
+ //
+ // Notably, this is necessary to fix the animation jump when resizing height back
+ // and forth in quick succession (in a way that cancels the resize animation).
+ for tile in &mut self.tiles[tile_idx + 1..] {
+ tile.offset_move_y_anim_current(offset);
+ }
}
}
}
diff --git a/src/layout/tests/animations.rs b/src/layout/tests/animations.rs
index 817304df..3db2bfe8 100644
--- a/src/layout/tests/animations.rs
+++ b/src/layout/tests/animations.rs
@@ -269,14 +269,20 @@ fn height_resize_and_cancel() {
];
check_ops_on_layout(&mut layout, &ops);
- // Since the resize animation is cancelled, the height goes to the new value immediately, and
- // the Y position should also go to 100 immediately, cancelling the movement.
- //
- // FIXME: right now, the Y position jumps to 5 and will continue animating to 100 from there
- // because cancelling the resize anim doesn't cancel the induced Y movement.
+ // Since the resize animation is cancelled, the height goes to the new value immediately. The Y
+ // position doesn't jump, instead the animation is offset to preserve the current position.
assert_snapshot!(format_tiles(&layout), @r"
100 × 100 at x: 0 y: 0
- 200 × 200 at x: 0 y: 5
+ 200 × 200 at x: 0 y:105
+ ");
+
+ // Advance to the end of the move animation.
+ Op::AdvanceAnimations { msec_delta: 950 }.apply(&mut layout);
+
+ // Final state.
+ assert_snapshot!(format_tiles(&layout), @r"
+ 100 × 100 at x: 0 y: 0
+ 200 × 200 at x: 0 y:100
");
}
@@ -506,23 +512,30 @@ fn height_resize_and_cancel_during_another_y_anim() {
];
check_ops_on_layout(&mut layout, &ops);
- // Since the resize anim was cancelled, second window's Y should jump a little to go back to
- // its original trajectory.
- //
- // FIXME: right now, the Y position jumps and will continue to animate from there because
- // cancelling the resize anim doesn't cancel the induced Y movement.
+ // Since the resize anim was cancelled, second window's Y anim is adjusted to preserve the
+ // current position while targeting the new final position.
assert_snapshot!(format_tiles(&layout), @r"
100 × 100 at x: 0 y: 0
- 200 × 200 at x: 45 y:-43
+ 200 × 200 at x: 45 y: 58
");
// Advance the time to complete the consume movement.
Op::AdvanceAnimations { msec_delta: 450 }.apply(&mut layout);
- // Final state. Y should be at 100 since the resize-induced Y anim was cancelled.
+ // Since we don't cancel the resize-induced part of the anim (in fact the move Y anim isn't
+ // split into parts, so there's no way to tell), it keeps going still.
assert_snapshot!(format_tiles(&layout), @r"
100 × 100 at x: 0 y: 0
- 200 × 200 at x: 0 y: 25
+ 200 × 200 at x: 0 y: 78
+ ");
+
+ // Advance the time to complete the resize-induced anim.
+ Op::AdvanceAnimations { msec_delta: 550 }.apply(&mut layout);
+
+ // Final state.
+ assert_snapshot!(format_tiles(&layout), @r"
+ 100 × 100 at x: 0 y: 0
+ 200 × 200 at x: 0 y:100
");
}
@@ -723,14 +736,10 @@ fn height_resize_before_another_y_anim_then_cancel() {
];
check_ops_on_layout(&mut layout, &ops);
- // This should cause the second window's trajectory to readjust to the new final position at
- // 100px. Ideally without big jumps because even though the first window's actual size changed
- // from 200 px to 100 px, the cancelled resize only shifted it from 104 px to 100 px.
- //
- // FIXME: currently causes a 100 px jump due to the change in the actual window size.
+ // The second window's trajectory readjusts to the new final position at 100 px, without jumps.
assert_snapshot!(format_tiles(&layout), @r"
100 × 100 at x: 0 y: 0
- 200 × 200 at x: 98 y:-96
+ 200 × 200 at x: 98 y: 4
");
// Advance the time to complete the consume movement.
@@ -791,13 +800,10 @@ fn clientside_height_change_during_another_y_anim() {
];
check_ops_on_layout(&mut layout, &ops);
- // This should cause the second window's trajectory to readjust to the new final position at
- // 200px. Ideally without big jumps.
- //
- // FIXME: currently causes a 100 px jump due to the change in the window size.
+ // The second window's trajectory readjusts to the new final position at 200 px, without jumps.
assert_snapshot!(format_tiles(&layout), @r"
100 × 200 at x: 0 y: 0
- 200 × 200 at x: 80 y:120
+ 200 × 200 at x: 80 y: 20
");
// Advance the time to complete the consume movement.
diff --git a/src/layout/tile.rs b/src/layout/tile.rs
index dea2a773..a4fe8a57 100644
--- a/src/layout/tile.rs
+++ b/src/layout/tile.rs
@@ -485,6 +485,17 @@ impl<W: LayoutElement> Tile<W> {
});
}
+ pub fn offset_move_y_anim_current(&mut self, offset: f64) {
+ if let Some(move_) = self.move_y_animation.as_mut() {
+ // If the anim is almost done, there's little point trying to offset it; we can let
+ // things jump. If it turns out like a bad idea, we could restart the anim intead.
+ let value = move_.anim.value();
+ if value > 0.001 {
+ move_.from += offset / value;
+ }
+ }
+ }
+
pub fn stop_move_animations(&mut self) {
self.move_x_animation = None;
self.move_y_animation = None;