diff options
| author | Ivan Molodetskikh <yalterz@gmail.com> | 2024-05-14 15:00:11 +0400 |
|---|---|---|
| committer | Ivan Molodetskikh <yalterz@gmail.com> | 2024-05-14 15:35:43 +0400 |
| commit | 5f402210510aff0051abd080d664581c26e77c2b (patch) | |
| tree | f75918a6a30295b4f571846d09cc12ee75397f12 /src | |
| parent | b14405904a8309fdcebc938f59584832ffe27fd6 (diff) | |
| download | niri-5f402210510aff0051abd080d664581c26e77c2b.tar.gz niri-5f402210510aff0051abd080d664581c26e77c2b.tar.bz2 niri-5f402210510aff0051abd080d664581c26e77c2b.zip | |
Refactor column and tile offsets, fix a few issues
Diffstat (limited to 'src')
| -rw-r--r-- | src/handlers/xdg_shell.rs | 4 | ||||
| -rw-r--r-- | src/layout/mod.rs | 31 | ||||
| -rw-r--r-- | src/layout/tile.rs | 15 | ||||
| -rw-r--r-- | src/layout/workspace.rs | 776 |
4 files changed, 459 insertions, 367 deletions
diff --git a/src/handlers/xdg_shell.rs b/src/handlers/xdg_shell.rs index 1b8194aa..44f07839 100644 --- a/src/handlers/xdg_shell.rs +++ b/src/handlers/xdg_shell.rs @@ -759,7 +759,7 @@ impl State { // height. let mut target = Rectangle::from_loc_and_size((0, 0), (window_geo.size.w, output_geo.size.h)); - target.loc.y -= self.niri.layout.window_y(window).unwrap(); + target.loc -= self.niri.layout.window_loc(window).unwrap(); target.loc -= get_popup_toplevel_coords(popup); self.position_popup_within_rect(popup, target); @@ -953,8 +953,6 @@ pub fn add_mapped_toplevel_pre_commit_hook(toplevel: &ToplevelSurface) -> HookId state.backend.with_primary_renderer(|renderer| { mapped.store_animation_snapshot(renderer); }); - - state.niri.layout.prepare_for_resize_animation(&window); } // The toplevel remains mapped; clear any stored unmap snapshot. diff --git a/src/layout/mod.rs b/src/layout/mod.rs index 1442dfd0..88b9b3ec 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -745,14 +745,14 @@ impl<W: LayoutElement> Layout<W> { None } - pub fn window_y(&self, window: &W::Id) -> Option<i32> { + pub fn window_loc(&self, window: &W::Id) -> Option<Point<i32, Logical>> { match &self.monitor_set { MonitorSet::Normal { monitors, .. } => { for mon in monitors { for ws in &mon.workspaces { for col in &ws.columns { if let Some(idx) = col.position(window) { - return Some(col.window_y(idx)); + return Some(col.window_loc(idx)); } } } @@ -762,7 +762,7 @@ impl<W: LayoutElement> Layout<W> { for ws in workspaces { for col in &ws.columns { if let Some(idx) = col.position(window) { - return Some(col.window_y(idx)); + return Some(col.window_loc(idx)); } } } @@ -1995,31 +1995,6 @@ impl<W: LayoutElement> Layout<W> { } } - pub fn prepare_for_resize_animation(&mut self, window: &W::Id) { - let _span = tracy_client::span!("Layout::prepare_for_resize_animation"); - - match &mut self.monitor_set { - MonitorSet::Normal { monitors, .. } => { - for mon in monitors { - for ws in &mut mon.workspaces { - if ws.has_window(window) { - ws.prepare_for_resize_animation(window); - return; - } - } - } - } - MonitorSet::NoOutputs { workspaces, .. } => { - for ws in workspaces { - if ws.has_window(window) { - ws.prepare_for_resize_animation(window); - return; - } - } - } - } - } - pub fn refresh(&mut self) { let _span = tracy_client::span!("Layout::refresh"); diff --git a/src/layout/tile.rs b/src/layout/tile.rs index 7f9c6e75..d53eb2b1 100644 --- a/src/layout/tile.rs +++ b/src/layout/tile.rs @@ -1,4 +1,3 @@ -use std::cell::RefCell; use std::cmp::max; use std::rc::Rc; use std::time::Duration; @@ -67,7 +66,7 @@ pub struct Tile<W: LayoutElement> { move_y_animation: Option<MoveAnimation>, /// Snapshot of the last render for use in the close animation. - unmap_snapshot: RefCell<Option<TileRenderSnapshot>>, + unmap_snapshot: Option<TileRenderSnapshot>, /// Extra damage for clipped surface corner radius changes. rounded_corner_damage: RoundedCornerDamage, @@ -122,7 +121,7 @@ impl<W: LayoutElement> Tile<W> { resize_animation: None, move_x_animation: None, move_y_animation: None, - unmap_snapshot: RefCell::new(None), + unmap_snapshot: None, rounded_corner_damage: Default::default(), options, } @@ -237,9 +236,7 @@ impl<W: LayoutElement> Tile<W> { || self.move_y_animation.is_some() } - pub fn update(&mut self, is_active: bool, mut view_rect: Rectangle<i32, Logical>) { - view_rect.loc -= self.render_offset(); - + pub fn update(&mut self, is_active: bool, view_rect: Rectangle<i32, Logical>) { let rules = self.window.rules(); let draw_border_with_background = rules @@ -840,11 +837,11 @@ impl<W: LayoutElement> Tile<W> { renderer: &mut GlesRenderer, scale: Scale<f64>, ) { - if self.unmap_snapshot.get_mut().is_some() { + if self.unmap_snapshot.is_some() { return; } - *self.unmap_snapshot.get_mut() = Some(self.render_snapshot(renderer, scale)); + self.unmap_snapshot = Some(self.render_snapshot(renderer, scale)); } fn render_snapshot( @@ -881,7 +878,7 @@ impl<W: LayoutElement> Tile<W> { } } - pub fn take_unmap_snapshot(&self) -> Option<TileRenderSnapshot> { + pub fn take_unmap_snapshot(&mut self) -> Option<TileRenderSnapshot> { self.unmap_snapshot.take() } } diff --git a/src/layout/workspace.rs b/src/layout/workspace.rs index fdcd7cb0..5aa78f2b 100644 --- a/src/layout/workspace.rs +++ b/src/layout/workspace.rs @@ -54,6 +54,9 @@ pub struct Workspace<W: LayoutElement> { /// Columns of windows on this workspace. pub columns: Vec<Column<W>>, + /// Extra per-column data. + data: Vec<ColumnData>, + /// Index of the currently active column, if any. pub active_column_idx: usize, @@ -116,6 +119,13 @@ niri_render_elements! { } } +/// Extra per-column data. +#[derive(Debug, Clone, Copy, PartialEq)] +struct ColumnData { + /// Cached actual column width. + width: i32, +} + #[derive(Debug)] enum ViewOffsetAdjustment { Animation(Animation), @@ -181,12 +191,10 @@ pub struct Column<W: LayoutElement> { /// Must be non-empty. pub tiles: Vec<Tile<W>>, - /// Heights of the windows. + /// Extra per-tile data. /// /// Must have the same number of elements as `tiles`. - /// - /// These heights are window heights, not tile heights, so they exclude tile decorations. - heights: Vec<WindowHeight>, + data: Vec<TileData>, /// Index of the currently active tile. pub active_tile_idx: usize, @@ -206,9 +214,6 @@ pub struct Column<W: LayoutElement> { /// Animation of the render offset during window swapping. move_animation: Option<Animation>, - /// Width right before a resize animation on one of the windows of the column. - pub width_before_resize: Option<i32>, - /// Latest known view size for this column's workspace. view_size: Size<i32, Logical>, @@ -219,6 +224,21 @@ pub struct Column<W: LayoutElement> { options: Rc<Options>, } +/// Extra per-tile data. +#[derive(Debug, Clone, Copy, PartialEq)] +struct TileData { + /// Requested height of the window. + /// + /// This is window height, not tile height, so it excludes tile decorations. + height: WindowHeight, + + /// Cached actual size of the tile. + size: Size<i32, Logical>, + + /// Cached whether the tile is being interactively resized by its left edge. + interactively_resizing_by_left_edge: bool, +} + impl OutputId { pub fn new(output: &Output) -> Self { Self(output.name()) @@ -238,6 +258,18 @@ impl ViewOffsetAdjustment { } } +impl ColumnData { + pub fn new<W: LayoutElement>(column: &Column<W>) -> Self { + let mut rv = Self { width: 0 }; + rv.update(column); + rv + } + + pub fn update<W: LayoutElement>(&mut self, column: &Column<W>) { + self.width = column.width(); + } +} + impl ColumnWidth { fn resolve(self, options: &Options, view_width: i32) -> i32 { match self { @@ -259,6 +291,26 @@ impl From<PresetWidth> for ColumnWidth { } } +impl TileData { + pub fn new<W: LayoutElement>(tile: &Tile<W>, height: WindowHeight) -> Self { + let mut rv = Self { + height, + size: Size::default(), + interactively_resizing_by_left_edge: false, + }; + rv.update(tile); + rv + } + + pub fn update<W: LayoutElement>(&mut self, tile: &Tile<W>) { + self.size = tile.tile_size(); + self.interactively_resizing_by_left_edge = tile + .window() + .interactive_resize_data() + .map_or(false, |data| data.edges.contains(ResizeEdge::LEFT)); + } +} + impl<W: LayoutElement> Workspace<W> { pub fn new(output: Output, options: Rc<Options>) -> Self { let working_area = compute_working_area(&output, options.struts); @@ -268,6 +320,7 @@ impl<W: LayoutElement> Workspace<W> { working_area, output: Some(output), columns: vec![], + data: vec![], active_column_idx: 0, interactive_resize: None, view_offset: 0, @@ -287,6 +340,7 @@ impl<W: LayoutElement> Workspace<W> { view_size: Size::from((1280, 720)), working_area: Rectangle::from_loc_and_size((0, 0), (1280, 720)), columns: vec![], + data: vec![], active_column_idx: 0, interactive_resize: None, view_offset: 0, @@ -339,12 +393,15 @@ impl<W: LayoutElement> Workspace<W> { } pub fn update_render_elements(&mut self, is_active: bool) { - let mut view_rect = Rectangle::from_loc_and_size((self.view_pos(), 0), self.view_size); - - for (col_idx, col) in self.columns.iter_mut().enumerate() { - let is_active = is_active && col_idx == self.active_column_idx; + let view_pos = Point::from((self.view_pos(), 0)); + let view_size = self.view_size(); + let active_idx = self.active_column_idx; + for (col_idx, (col, col_x)) in self.columns_mut().enumerate() { + let is_active = is_active && col_idx == active_idx; + let col_off = Point::from((col_x, 0)); + let col_pos = view_pos - col_off - col.render_offset(); + let view_rect = Rectangle::from_loc_and_size(col_pos, view_size); col.update_render_elements(is_active, view_rect); - view_rect.loc.x -= col.width() + self.options.gaps; } } @@ -730,17 +787,6 @@ impl<W: LayoutElement> Workspace<W> { self.windows_mut().find(|win| win.is_wl_surface(wl_surface)) } - /// Computes the X position of the windows in the given column, in logical coordinates. - pub fn column_x(&self, column_idx: usize) -> i32 { - let mut x = 0; - - for column in self.columns.iter().take(column_idx) { - x += column.width() + self.options.gaps; - } - - x - } - pub fn add_window_at( &mut self, col_idx: usize, @@ -776,6 +822,7 @@ impl<W: LayoutElement> Workspace<W> { true, ); let width = column.width(); + self.data.insert(col_idx, ColumnData::new(&column)); self.columns.insert(col_idx, column); if activate { @@ -874,6 +921,7 @@ impl<W: LayoutElement> Workspace<W> { is_full_width, true, ); + self.data.insert(idx, ColumnData::new(&column)); self.columns.insert(idx, column); // Activate the new window if right_of was active. @@ -913,6 +961,7 @@ impl<W: LayoutElement> Workspace<W> { column.set_view_size(self.view_size, self.working_area); let width = column.width(); + self.data.insert(idx, ColumnData::new(&column)); self.columns.insert(idx, column); if activate { @@ -958,15 +1007,18 @@ impl<W: LayoutElement> Workspace<W> { let offset = self.column_x(column_idx + 1) - self.column_x(column_idx); let column = &mut self.columns[column_idx]; + let prev_width = self.data[column_idx].width; // Animate movement of other tiles. - let offset_y = column.tile_y(window_idx + 1) - column.tile_y(window_idx); + // FIXME: tiles can move by X too, in a centered or resizing layout with one window smaller + // than the others. + let offset_y = column.tile_offset(window_idx + 1).y - column.tile_offset(window_idx).y; for tile in &mut column.tiles[window_idx + 1..] { tile.animate_move_y_from(offset_y); } let tile = column.tiles.remove(window_idx); - column.heights.remove(window_idx); + column.data.remove(window_idx); if let Some(output) = &self.output { tile.window().output_leave(output); @@ -979,7 +1031,30 @@ impl<W: LayoutElement> Workspace<W> { } } - if column.tiles.is_empty() { + let became_empty = column.tiles.is_empty(); + let offset = if became_empty { + offset + } else { + column.active_tile_idx = min(column.active_tile_idx, column.tiles.len() - 1); + column.update_tile_sizes(true); + self.data[column_idx].update(column); + + prev_width - column.width() + }; + + // Animate movement of the other columns. + let movement_config = anim_config.unwrap_or(self.options.animations.window_movement.0); + if self.active_column_idx <= column_idx { + for col in &mut self.columns[column_idx + 1..] { + col.animate_move_from_with_config(offset, movement_config); + } + } else { + for col in &mut self.columns[..=column_idx] { + col.animate_move_from_with_config(-offset, movement_config); + } + } + + if became_empty { if column_idx + 1 == self.active_column_idx { // The previous column, that we were going to activate upon removal of the active // column, has just been itself removed. @@ -990,19 +1065,8 @@ impl<W: LayoutElement> Workspace<W> { self.view_offset_before_fullscreen = None; } - // Animate movement of the other columns. - let movement_config = anim_config.unwrap_or(self.options.animations.window_movement.0); - if self.active_column_idx <= column_idx { - for col in &mut self.columns[column_idx + 1..] { - col.animate_move_from_with_config(offset, movement_config); - } - } else { - for col in &mut self.columns[..column_idx] { - col.animate_move_from_with_config(-offset, movement_config); - } - } - self.columns.remove(column_idx); + self.data.remove(column_idx); if self.columns.is_empty() { return tile; } @@ -1050,9 +1114,6 @@ impl<W: LayoutElement> Workspace<W> { return tile; } - column.active_tile_idx = min(column.active_tile_idx, column.tiles.len() - 1); - column.update_tile_sizes(true); - tile } @@ -1070,6 +1131,7 @@ impl<W: LayoutElement> Workspace<W> { } let column = self.columns.remove(column_idx); + self.data.remove(column_idx); if let Some(output) = &self.output { for tile in &column.tiles { @@ -1149,22 +1211,29 @@ impl<W: LayoutElement> Workspace<W> { .enumerate() .find(|(_, col)| col.contains(window)) .unwrap(); - let tile_idx = column + let (tile_idx, tile) = column .tiles - .iter() - .position(|tile| tile.window().id() == window) + .iter_mut() + .enumerate() + .find(|(_, tile)| tile.window().id() == window) .unwrap(); - let offset = column - .width_before_resize - .take() - .map_or(0, |prev| prev - column.width()); + let was_fullscreen = tile.is_fullscreen(); + let resize = tile.window_mut().interactive_resize_data(); - let was_fullscreen = column.tiles[tile_idx].is_fullscreen(); + // Do this before calling update_window() so it can get up-to-date info. + if let Some(serial) = serial { + tile.window_mut().update_interactive_resize(serial); + } + + let prev_width = self.data[col_idx].width; column.update_window(window); + self.data[col_idx].update(column); column.update_tile_sizes(false); + let offset = prev_width - self.data[col_idx].width; + // Move other columns in tandem with resizing. let started_resize_anim = column.tiles[tile_idx].resize_animation().is_some() && offset != 0; @@ -1190,12 +1259,6 @@ impl<W: LayoutElement> Workspace<W> { let tile = &mut col.tiles[tile_idx]; let window = tile.window_mut(); - let resize = window.interactive_resize_data(); - - if let Some(serial) = serial { - window.update_interactive_resize(serial); - } - // If this was the last resize commit, this function will now return None. This way we can // animate the window into view after the last resize commit. let resize_still_ongoing = window.interactive_resize_data().is_some(); @@ -1270,28 +1333,15 @@ impl<W: LayoutElement> Workspace<W> { .map(|o| Scale::from(o.current_scale().fractional_scale())) .unwrap_or(Scale::from(1.)); - let mut view_rect_ws = Rectangle::from_loc_and_size((self.view_pos(), 0), self.view_size); - for col in &mut self.columns { - let col_width = col.width(); - - let mut view_rect = view_rect_ws; - view_rect.loc -= col.render_offset(); - if !col.is_fullscreen { - view_rect.loc.y -= self.working_area.loc.y + self.options.gaps; - } - - for tile in &mut col.tiles { - if tile.window().id() == window { - view_rect.loc -= Column::render_offset_for_tile(tile, col_width, &self.options); - tile.update(false, view_rect); - tile.store_unmap_snapshot_if_empty(renderer, output_scale); - return; - } - - view_rect.loc.y -= tile.tile_size().h + self.options.gaps; + let view_size = self.view_size(); + for (tile, tile_pos) in self.tiles_with_render_positions_mut() { + if tile.window().id() == window { + let view_pos = Point::from((-tile_pos.x, -tile_pos.y)); + let view_rect = Rectangle::from_loc_and_size(view_pos, view_size); + tile.update(false, view_rect); + tile.store_unmap_snapshot_if_empty(renderer, output_scale); + return; } - - view_rect_ws.loc.x -= col_width + self.options.gaps; } } @@ -1311,11 +1361,6 @@ impl<W: LayoutElement> Workspace<W> { renderer: &mut GlesRenderer, window: &W::Id, ) { - let (tile, mut tile_pos) = self - .tiles_in_render_order() - .find(|(tile, _)| tile.window().id() == window) - .unwrap(); - // FIXME: workspaces should probably cache their last used scale so they can be correctly // rendered even with no outputs connected. let output_scale = self @@ -1324,36 +1369,53 @@ impl<W: LayoutElement> Workspace<W> { .map(|o| Scale::from(o.current_scale().fractional_scale())) .unwrap_or(Scale::from(1.)); + let (tile, mut tile_pos) = self + .tiles_with_render_positions_mut() + .find(|(tile, _)| tile.window().id() == window) + .unwrap(); + let Some(snapshot) = tile.take_unmap_snapshot() else { return; }; - let col_idx = self + let tile_size = tile.tile_size(); + + let (col_idx, tile_idx) = self .columns .iter() - .position(|col| col.contains(window)) + .enumerate() + .find_map(|(col_idx, col)| { + col.tiles + .iter() + .position(|tile| tile.window().id() == window) + .map(move |tile_idx| (col_idx, tile_idx)) + }) .unwrap(); let col = &self.columns[col_idx]; let removing_last = col.tiles.len() == 1; - let offset = self.column_x(col_idx + 1) - self.column_x(col_idx); tile_pos.x += self.view_pos(); - if col_idx < self.active_column_idx && removing_last { + if col_idx < self.active_column_idx { + let offset = if removing_last { + self.column_x(col_idx + 1) - self.column_x(col_idx) + } else { + self.data[col_idx].width + - col + .data + .iter() + .enumerate() + .filter_map(|(idx, data)| (idx != tile_idx).then_some(data.size.w)) + .max() + .unwrap() + }; tile_pos.x -= offset; } let anim = Animation::new(0., 1., 0., self.options.animations.window_close.anim); - let res = ClosingWindow::new( - renderer, - snapshot, - output_scale, - tile.tile_size(), - tile_pos, - anim, - ); + let res = ClosingWindow::new(renderer, snapshot, output_scale, tile_size, tile_pos, anim); match res { Ok(closing) => { self.closing_windows.push(closing); @@ -1364,27 +1426,22 @@ impl<W: LayoutElement> Workspace<W> { } } - pub fn prepare_for_resize_animation(&mut self, window: &W::Id) { - let column = self - .columns - .iter_mut() - .find(|col| col.contains(window)) - .unwrap(); - - column.width_before_resize = Some(column.width()); - } - #[cfg(test)] pub fn verify_invariants(&self) { assert!(self.view_size.w > 0); assert!(self.view_size.h > 0); + assert_eq!(self.columns.len(), self.data.len()); if !self.columns.is_empty() { assert!(self.active_column_idx < self.columns.len()); - for column in &self.columns { + for (column, data) in zip(&self.columns, &self.data) { assert!(Rc::ptr_eq(&self.options, &column.options)); column.verify_invariants(); + + let mut data2 = *data; + data2.update(column); + assert_eq!(data, &data2, "column data must be up to date"); } let col = &self.columns[self.active_column_idx]; @@ -1461,8 +1518,10 @@ impl<W: LayoutElement> Workspace<W> { let next_col_x = self.column_x(self.active_column_idx + 1); let mut column = self.columns.remove(self.active_column_idx); + let data = self.data.remove(self.active_column_idx); cancel_resize_if_this_column(&mut self.interactive_resize, &mut column); self.columns.insert(new_idx, column); + self.data.insert(new_idx, data); // Preserve the camera position when moving to the left. let view_offset_delta = -self.column_x(self.active_column_idx) + current_col_x; @@ -1540,21 +1599,22 @@ impl<W: LayoutElement> Workspace<W> { let source_col_idx = self.active_column_idx; let source_column = &self.columns[source_col_idx]; - let prev_y = source_column.tile_y(source_column.active_tile_idx); + let prev_off = source_column.tile_offset(source_column.active_tile_idx); if source_column.tiles.len() == 1 { if self.active_column_idx == 0 { return; } - let offset_x = self.column_x(source_col_idx) - self.column_x(source_col_idx - 1); + let offset = self.column_x(source_col_idx) - self.column_x(source_col_idx - 1); + let mut offset = Point::from((offset, 0)); // Move into adjacent column. let target_column_idx = source_col_idx - 1; // Make sure the previous (target) column is activated so the animation looks right. - self.activate_prev_column_on_removal = Some(self.static_view_offset() + offset_x); - let offset_x = offset_x + self.columns[source_col_idx].render_offset().x; + self.activate_prev_column_on_removal = Some(self.static_view_offset() + offset.x); + offset.x += self.columns[source_col_idx].render_offset().x; let tile = self.remove_tile_by_idx( source_col_idx, 0, @@ -1566,14 +1626,16 @@ impl<W: LayoutElement> Workspace<W> { let prev_next_x = self.column_x(next_col_idx); let target_column = &mut self.columns[target_column_idx]; - let offset_x = offset_x - target_column.render_offset().x; - let offset_y = prev_y - target_column.tile_y(target_column.tiles.len()); + offset.x -= target_column.render_offset().x; target_column.add_tile(tile, true); + self.data[target_column_idx].update(target_column); target_column.focus_last(); + offset += prev_off - target_column.tile_offset(target_column.tiles.len() - 1); + let new_tile = target_column.tiles.last_mut().unwrap(); - new_tile.animate_move_from(Point::from((offset_x, offset_y))); + new_tile.animate_move_from(offset); // Consuming a window into a column could've increased its width if the new window had a // larger min width. Move the next columns to account for this. @@ -1586,7 +1648,7 @@ impl<W: LayoutElement> Workspace<W> { let width = source_column.width; let is_full_width = source_column.is_full_width; - let offset_x = source_column.render_offset().x; + let mut offset = Point::from((source_column.render_offset().x, 0)); let tile = self.remove_tile_by_idx(source_col_idx, source_column.active_tile_idx, None); @@ -1603,8 +1665,8 @@ impl<W: LayoutElement> Workspace<W> { self.activate_prev_column_on_removal = None; let new_col = &mut self.columns[self.active_column_idx]; - let offset_y = prev_y - new_col.tile_y(0); - new_col.tiles[0].animate_move_from(Point::from((offset_x, offset_y))); + offset += prev_off - new_col.tile_offset(0); + new_col.tiles[0].animate_move_from(offset); } } @@ -1614,11 +1676,12 @@ impl<W: LayoutElement> Workspace<W> { } let source_col_idx = self.active_column_idx; - let offset_x = self.column_x(source_col_idx) - self.column_x(source_col_idx + 1); + let offset = self.column_x(source_col_idx) - self.column_x(source_col_idx + 1); + let mut offset = Point::from((offset, 0)); let source_column = &self.columns[source_col_idx]; - let offset_x = offset_x + source_column.render_offset().x; - let prev_y = source_column.tile_y(source_column.active_tile_idx); + offset.x += source_column.render_offset().x; + let prev_off = source_column.tile_offset(source_column.active_tile_idx); if source_column.tiles.len() == 1 { if self.active_column_idx + 1 == self.columns.len() { @@ -1628,7 +1691,7 @@ impl<W: LayoutElement> Workspace<W> { // Move into adjacent column. let target_column_idx = source_col_idx; - let offset_x = offset_x - self.columns[source_col_idx + 1].render_offset().x; + offset.x -= self.columns[source_col_idx + 1].render_offset().x; // Make sure the target column gets activated. self.activate_prev_column_on_removal = None; @@ -1642,13 +1705,14 @@ impl<W: LayoutElement> Workspace<W> { let prev_next_x = self.column_x(target_column_idx + 1); let target_column = &mut self.columns[target_column_idx]; - let offset_y = prev_y - target_column.tile_y(target_column.tiles.len()); - target_column.add_tile(tile, true); + self.data[target_column_idx].update(target_column); target_column.focus_last(); + offset += prev_off - target_column.tile_offset(target_column.tiles.len() - 1); + let new_tile = target_column.tiles.last_mut().unwrap(); - new_tile.animate_move_from(Point::from((offset_x, offset_y))); + new_tile.animate_move_from(offset); // Consuming a window into a column could've increased its width if the new window had a // larger min width. Move the next columns to account for this. @@ -1672,8 +1736,8 @@ impl<W: LayoutElement> Workspace<W> { ); let new_col = &mut self.columns[self.active_column_idx]; - let offset_y = prev_y - new_col.tile_y(0); - new_col.tiles[0].animate_move_from(Point::from((offset_x, offset_y))); + offset += prev_off - new_col.tile_offset(0); + new_col.tiles[0].animate_move_from(offset); } } @@ -1688,10 +1752,11 @@ impl<W: LayoutElement> Workspace<W> { let source_column_idx = self.active_column_idx + 1; - let offset_x = self.column_x(source_column_idx) + let offset = self.column_x(source_column_idx) + self.columns[source_column_idx].render_offset().x - self.column_x(self.active_column_idx); - let prev_y = self.columns[source_column_idx].tile_y(0); + let mut offset = Point::from((offset, 0)); + let prev_off = self.columns[source_column_idx].tile_offset(0); let tile = self.remove_tile_by_idx(source_column_idx, 0, None); self.enter_output_for_window(tile.window()); @@ -1700,18 +1765,20 @@ impl<W: LayoutElement> Workspace<W> { let target_column = &mut self.columns[self.active_column_idx]; let was_fullscreen = target_column.tiles[target_column.active_tile_idx].is_fullscreen(); - let offset_y = prev_y - target_column.tile_y(target_column.tiles.len()); target_column.add_tile(tile, true); + self.data[self.active_column_idx].update(target_column); + + offset += prev_off - target_column.tile_offset(target_column.tiles.len() - 1); if !was_fullscreen { self.view_offset_before_fullscreen = None; } - let offset_x = offset_x - target_column.render_offset().x; + offset.x -= target_column.render_offset().x; let new_tile = target_column.tiles.last_mut().unwrap(); - new_tile.animate_move_from(Point::from((offset_x, offset_y))); + new_tile.animate_move_from(offset); // Consuming a window into a column could've increased its width if the new window had a // larger min width. Move the next columns to account for this. @@ -1726,17 +1793,17 @@ impl<W: LayoutElement> Workspace<W> { return; } - let offset_x = + let offset = self.column_x(self.active_column_idx) - self.column_x(self.active_column_idx + 1); + let mut offset = Point::from((offset, 0)); let source_column = &self.columns[self.active_column_idx]; if source_column.tiles.len() == 1 { return; } - let offset_x = offset_x + source_column.render_offset().x; - - let prev_y = source_column.tile_y(source_column.active_tile_idx); + offset.x += source_column.render_offset().x; + let prev_off = source_column.tile_offset(source_column.active_tile_idx); let width = source_column.width; let is_full_width = source_column.is_full_width; @@ -1752,8 +1819,8 @@ impl<W: LayoutElement> Workspace<W> { ); let new_col = &mut self.columns[self.active_column_idx]; - let offset_y = prev_y - new_col.tile_y(0); - new_col.tiles[0].animate_move_from(Point::from((offset_x, offset_y))); + offset += prev_off - new_col.tile_offset(0); + new_col.tiles[0].animate_move_from(offset); } pub fn center_column(&mut self) { @@ -1786,89 +1853,113 @@ impl<W: LayoutElement> Workspace<W> { } } - fn tiles_in_render_order(&self) -> impl Iterator<Item = (&'_ Tile<W>, Point<i32, Logical>)> { - let view_pos = self.view_pos(); - - // Start with the active window since it's drawn on top. - let col = &self.columns[self.active_column_idx]; - let col_width = col.width(); - let tile = &col.tiles[col.active_tile_idx]; - let tile_pos = Point::from((-self.view_offset, col.tile_y(col.active_tile_idx))) - + col.render_offset() - + Column::render_offset_for_tile(tile, col_width, &self.options) - + tile.render_offset(); - let first = iter::once((tile, tile_pos)); - - // Next, the rest of the tiles in the active column, since it should be drawn on top as a - // whole during animations. - let next = - zip(&col.tiles, col.tile_ys()) - .enumerate() - .filter_map(move |(tile_idx, (tile, y))| { - if tile_idx == col.active_tile_idx { - // Active tile comes first. - return None; - } + // HACK: pass a self.data iterator in manually as a workaround for the lack of method partial + // borrowing. Note that this method's return value does not borrow the entire &Self! + fn column_xs(&self, data: impl Iterator<Item = ColumnData>) -> impl Iterator<Item = i32> { + let gaps = self.options.gaps; + let mut x = 0; - let tile_pos = Point::from((-self.view_offset, y)) - + col.render_offset() - + Column::render_offset_for_tile(tile, col_width, &self.options) - + tile.render_offset(); - Some((tile, tile_pos)) - }); + // Chain with a dummy value to be able to get one past all columns' X. + let dummy = ColumnData { width: 0 }; + let data = data.chain(iter::once(dummy)); - let mut x = -view_pos; - let rest = self - .columns - .iter() + data.map(move |data| { + let rv = x; + x += data.width + gaps; + rv + }) + } + + fn column_x(&self, column_idx: usize) -> i32 { + self.column_xs(self.data.iter().copied()) + .nth(column_idx) + .unwrap() + } + + fn column_xs_in_render_order( + &self, + data: impl Iterator<Item = ColumnData>, + ) -> impl Iterator<Item = i32> { + let active_idx = self.active_column_idx; + let active_pos = self.column_x(active_idx); + let offsets = self + .column_xs(data) .enumerate() - // Keep track of column X position. - .map(move |(col_idx, col)| { - let col_width = col.width(); - let rv = (col_idx, col, col_width, x); - x += col_width + self.options.gaps; |
