From 57084c4ddf38eb18b4700c6a2373f94a308e16ae Mon Sep 17 00:00:00 2001 From: LGUG2Z Date: Thu, 29 Oct 2026 08:48:28 -0700 Subject: [PATCH] fix(scrolling): put the left edge of a column where it is dragged to, however far A column that is resized by its left edge is made wider and the strip is panned by what it was made wider by, so that its left edge is where it was moved to and its right edge where it was. But a column that is made wider than the room that it has to the right is brought into view as it is made wider, which moves the strip already, so that for what was beyond the room the strip moved twice. A recording of a drag of the last column of a strip, 1342 pixels, has the strip 1008 pixels too far to the left at the end of it, and every step with the strip moved by twice what the drag was less the room there was. Say where the right edge of the column is before and after it is made wider, and pan by the difference, which is what is left to put back whether the strip moved or not. --- komorebi-layouts/src/scrolling.rs | 83 ++++++++++++++++++++++++++++--- 1 file changed, 77 insertions(+), 6 deletions(-) diff --git a/komorebi-layouts/src/scrolling.rs b/komorebi-layouts/src/scrolling.rs index 45fb1ae0..cdc34a0f 100644 --- a/komorebi-layouts/src/scrolling.rs +++ b/komorebi-layouts/src/scrolling.rs @@ -213,16 +213,28 @@ pub fn resize( return; }; + // a column grows to the right, from its left edge, unless it is its left edge that is + // moved: that one is to be where it is moved to, and its right edge where it was + let left_edge = matches!(direction, OperationDirection::Left) && space.active_id() == Some(id); + let right_edge = |space: &ScrollingSpace| { + space + .layout() + .into_iter() + .find(|placement| &placement.id == id) + .map(|placement| placement.rect.x + placement.rect.width) + }; + let before = left_edge.then(|| right_edge(space)).flatten(); + let resized = ((width + change).round() as i32).max(MINIMUM_COLUMN_WIDTH); space.set_width(id, ColumnWidth::Fixed(resized)); - // a column grows to the right, from its left edge, unless it is its left edge that is - // moved: that one is to be where it is moved to, and its right edge where it was - if matches!(direction, OperationDirection::Left) - && space.active_id() == Some(id) - && let Some(width_now) = space.column_width(id) + // a column that is made wider than the room it has is brought into view, which moves the + // strip already: what is left to put back is where its right edge has got to, and not + // what the column grew by + if let Some(before) = before + && let Some(after) = right_edge(space) { - space.pan_by(width - width_now); + space.pan_by(f64::from(before - after)); } } @@ -484,4 +496,63 @@ mod tests { assert_eq!(grown[1].0, before[1].0); assert_eq!(grown[1].1, before[1].1 + 100); } + + #[test] + fn the_left_edge_of_a_column_is_where_it_is_moved_to_however_far_it_is_moved() { + let work_area = Rect { + left: 0, + top: 0, + right: 1200, + bottom: 800, + }; + let ids = vec!["a".to_string(), "b".to_string()]; + let mut space = None; + + sync( + &mut space, + scrolling_options(options(3, None)), + viewport_of(&work_area), + &ids, + ids.get(1), + ); + + let space = space.as_mut().unwrap(); + let edges = |space: &ScrollingSpace| { + place(space, &work_area, 0) + .iter() + .map(|placement| { + ( + placement.rect.left, + placement.rect.left + placement.rect.right, + ) + }) + .collect::>() + }; + + // the last column ends at 800 of a viewport of 1200: there is room for 400 more, and + // past it the column is brought into view, which moves the strip as well + let before = edges(space); + assert_eq!(before[1], (400, 800)); + + for growth in [300, 400, 500, 600, 900] { + let mut grown = space.clone(); + resize( + &mut grown, + &ids[1], + OperationDirection::Left, + Sizing::Increase, + growth * 2, + ); + + let after = edges(&grown); + assert_eq!( + after[1], + (before[1].0 - growth, before[1].1), + "grown by {growth}" + ); + + // and the column before it follows its left edge + assert_eq!(after[0].1, after[1].0, "grown by {growth}"); + } + } }