window management library
git clone https://git.lucas.co/cce-window-manager.git
feat: animate flag on SetCamera — eased overview enter/exit
Expose transitions now declare animate: true and the mechanism eases
pan and zoom toward the target instead of snapping; keyed zoom and
View anchor jumps stay instant (animate: false). The overview mode
flip itself always applies immediately so a re-toggle mid-flight
exits/enters rather than re-entering.
Co-Authored-By: Claude Fable 5 <[email protected]>
src/actions.rs | 19 ++++++++++---------
src/api.rs | 5 ++++-
2 files changed, 14 insertions(+), 10 deletions(-)
diff --git a/src/actions.rs b/src/actions.rs
index 202712c..83f2cde 100644
--- a/src/actions.rs
+++ b/src/actions.rs
@@ -83,7 +83,7 @@ fn zoom(ctx: &ActionCtx, action: Action) -> Vec<Command> {
new_zoom,
);
vec![
- Command::SetCamera { camera: cam, overview: Some(camera::is_overview(cam.zoom)) },
+ Command::SetCamera { camera: cam, overview: Some(camera::is_overview(cam.zoom)), animate: false },
Command::Relayout,
]
}
@@ -115,7 +115,7 @@ fn pan_step(ctx: &ActionCtx, action: Action) -> Vec<Command> {
fn view(ctx: &ActionCtx, action: Action) -> Vec<Command> {
let (tx, ty) = anchor_point(action);
let cam = camera::center_on(tx, ty, ctx.viewport_w, ctx.viewport_h, ctx.camera.zoom);
- vec![Command::SetCamera { camera: cam, overview: None }, Command::Relayout]
+ vec![Command::SetCamera { camera: cam, overview: None, animate: false }, Command::Relayout]
}
/// Send the focused window to one of the four fixed anchors (centered on
@@ -146,6 +146,7 @@ fn expose(ctx: &ActionCtx) -> Vec<Command> {
Command::SetCamera {
camera: Camera { pan_x: 0.0, pan_y: 0.0, zoom: 1.0 },
overview: Some(false),
+ animate: true,
},
Command::RefreshCamera,
];
@@ -157,7 +158,7 @@ fn expose(ctx: &ActionCtx) -> Vec<Command> {
return vec![
Command::Focus(id),
Command::StopPanAnimation,
- Command::SetCamera { camera: cam, overview: Some(false) },
+ Command::SetCamera { camera: cam, overview: Some(false), animate: true },
Command::RefreshCamera,
];
}
@@ -167,7 +168,7 @@ fn expose(ctx: &ActionCtx) -> Vec<Command> {
let cam = camera::center_on(vx, vy, ow, oh, 1.0);
vec![
Command::StopPanAnimation,
- Command::SetCamera { camera: cam, overview: Some(false) },
+ Command::SetCamera { camera: cam, overview: Some(false), animate: true },
Command::RefreshCamera,
]
} else {
@@ -187,7 +188,7 @@ fn expose(ctx: &ActionCtx) -> Vec<Command> {
// Overview by fiat even when the fit lands at zoom 1 (a desktop
// smaller than the screen): the next Expose must exit, not re-enter.
vec![
- Command::SetCamera { camera: cam, overview: Some(true) },
+ Command::SetCamera { camera: cam, overview: Some(true), animate: true },
Command::RefreshCamera,
]
}
@@ -596,14 +597,14 @@ mod tests {
fn zoom_in_sets_overview_and_relayouts() {
let cmds = dispatch(&ctx(), Action::ZoomIn);
assert_eq!(cmds.len(), 2);
- let Command::SetCamera { camera, overview } = cmds[0] else { panic!() };
+ let Command::SetCamera { camera, overview, .. } = cmds[0] else { panic!() };
assert!((camera.zoom - 1.1).abs() < 1e-9);
assert_eq!(overview, Some(true));
assert_eq!(cmds[1], Command::Relayout);
// Reset from zoomed goes back to normal.
let mut c = ctx();
c.camera.zoom = 2.0;
- let Command::SetCamera { camera, overview } = dispatch(&c, Action::ZoomReset)[0] else { panic!() };
+ let Command::SetCamera { camera, overview, .. } = dispatch(&c, Action::ZoomReset)[0] else { panic!() };
assert_eq!(camera.zoom, 1.0);
assert_eq!(overview, Some(false));
}
@@ -638,7 +639,7 @@ mod tests {
ineligible.expose_eligible = false;
c.windows.push(ineligible);
let cmds = dispatch(&c, Action::Expose);
- let Command::SetCamera { camera, overview } = cmds[0] else { panic!() };
+ let Command::SetCamera { camera, overview, .. } = cmds[0] else { panic!() };
assert_eq!(overview, Some(true));
// Only window 1 counts: 400x300 fits without zooming out.
assert_eq!(camera.zoom, 1.0);
@@ -658,7 +659,7 @@ mod tests {
let cmds = dispatch(&c, Action::Expose);
assert_eq!(cmds[0], Command::Focus(wid(3)));
assert_eq!(cmds[1], Command::StopPanAnimation);
- let Command::SetCamera { camera, overview } = cmds[2] else { panic!() };
+ let Command::SetCamera { camera, overview, .. } = cmds[2] else { panic!() };
assert_eq!(overview, Some(false));
assert_eq!(camera.zoom, 1.0);
// Centered on the window's center (1200, 2150).
diff --git a/src/api.rs b/src/api.rs
index b5de195..cbab97f 100644
--- a/src/api.rs
+++ b/src/api.rs
@@ -366,7 +366,10 @@ pub enum Command {
/// Spawn a command line (the mechanism forks `sh -c`).
Spawn(String),
/// Write the camera. `overview: None` leaves the mode untouched.
- SetCamera { camera: Camera, overview: Option<bool> },
+ /// `animate: true` eases pan and zoom toward the target (the overview
+ /// enter/exit transition); `false` snaps and cancels any easing in
+ /// flight. The mode flip itself always applies immediately.
+ SetCamera { camera: Camera, overview: Option<bool>, animate: bool },
/// Set pan-animation targets (a `None` axis is left alone) and start
/// easing toward them.
PanTo { x: Option<f64>, y: Option<f64> },