diff options
| author | Ivan Molodetskikh <yalterz@gmail.com> | 2024-10-11 19:52:03 +0300 |
|---|---|---|
| committer | Ivan Molodetskikh <yalterz@gmail.com> | 2024-10-12 09:58:03 +0300 |
| commit | d5cbc35811dc758753b4f74b05a8c439fd850d83 (patch) | |
| tree | 76c1523953a23c5a623b6d4dcf237941a93cfab0 | |
| parent | a038c5aaabd1cf4224268518f7c8840ae7b30078 (diff) | |
| download | niri-d5cbc35811dc758753b4f74b05a8c439fd850d83.tar.gz niri-d5cbc35811dc758753b4f74b05a8c439fd850d83.tar.bz2 niri-d5cbc35811dc758753b4f74b05a8c439fd850d83.zip | |
Implement ConsumeOrExpelWindow{Left,Right} by id
| -rw-r--r-- | niri-config/src/lib.rs | 18 | ||||
| -rw-r--r-- | niri-ipc/src/lib.rs | 28 | ||||
| -rw-r--r-- | src/input/mod.rs | 26 | ||||
| -rw-r--r-- | src/layout/mod.rs | 82 | ||||
| -rw-r--r-- | src/layout/monitor.rs | 8 | ||||
| -rw-r--r-- | src/layout/workspace.rs | 147 |
6 files changed, 228 insertions, 81 deletions
diff --git a/niri-config/src/lib.rs b/niri-config/src/lib.rs index 17ed2440..dd81e665 100644 --- a/niri-config/src/lib.rs +++ b/niri-config/src/lib.rs @@ -1122,7 +1122,11 @@ pub enum Action { MoveWindowDownOrToWorkspaceDown, MoveWindowUpOrToWorkspaceUp, ConsumeOrExpelWindowLeft, + #[knuffel(skip)] + ConsumeOrExpelWindowLeftById(u64), ConsumeOrExpelWindowRight, + #[knuffel(skip)] + ConsumeOrExpelWindowRightById(u64), ConsumeWindowIntoColumn, ExpelWindowFromColumn, CenterColumn, @@ -1229,8 +1233,18 @@ impl From<niri_ipc::Action> for Action { Self::MoveWindowDownOrToWorkspaceDown } niri_ipc::Action::MoveWindowUpOrToWorkspaceUp {} => Self::MoveWindowUpOrToWorkspaceUp, - niri_ipc::Action::ConsumeOrExpelWindowLeft {} => Self::ConsumeOrExpelWindowLeft, - niri_ipc::Action::ConsumeOrExpelWindowRight {} => Self::ConsumeOrExpelWindowRight, + niri_ipc::Action::ConsumeOrExpelWindowLeft { id: None } => { + Self::ConsumeOrExpelWindowLeft + } + niri_ipc::Action::ConsumeOrExpelWindowLeft { id: Some(id) } => { + Self::ConsumeOrExpelWindowLeftById(id) + } + niri_ipc::Action::ConsumeOrExpelWindowRight { id: None } => { + Self::ConsumeOrExpelWindowRight + } + niri_ipc::Action::ConsumeOrExpelWindowRight { id: Some(id) } => { + Self::ConsumeOrExpelWindowRightById(id) + } niri_ipc::Action::ConsumeWindowIntoColumn {} => Self::ConsumeWindowIntoColumn, niri_ipc::Action::ExpelWindowFromColumn {} => Self::ExpelWindowFromColumn, niri_ipc::Action::CenterColumn {} => Self::CenterColumn, diff --git a/niri-ipc/src/lib.rs b/niri-ipc/src/lib.rs index b7cfa192..69759626 100644 --- a/niri-ipc/src/lib.rs +++ b/niri-ipc/src/lib.rs @@ -242,10 +242,30 @@ pub enum Action { MoveWindowDownOrToWorkspaceDown {}, /// Move the focused window up in a column or to the workspace above. MoveWindowUpOrToWorkspaceUp {}, - /// Consume or expel the focused window left. - ConsumeOrExpelWindowLeft {}, - /// Consume or expel the focused window right. - ConsumeOrExpelWindowRight {}, + /// Consume or expel a window left. + #[cfg_attr( + feature = "clap", + clap(about = "Consume or expel the focused window left") + )] + ConsumeOrExpelWindowLeft { + /// Id of the window to consume or expel. + /// + /// If `None`, uses the focused window. + #[cfg_attr(feature = "clap", arg(long))] + id: Option<u64>, + }, + /// Consume or expel a window right. + #[cfg_attr( + feature = "clap", + clap(about = "Consume or expel the focused window right") + )] + ConsumeOrExpelWindowRight { + /// Id of the window to consume or expel. + /// + /// If `None`, uses the focused window. + #[cfg_attr(feature = "clap", arg(long))] + id: Option<u64>, + }, /// Consume the window to the right into the focused column. ConsumeWindowIntoColumn {}, /// Expel the focused window from the column. diff --git a/src/input/mod.rs b/src/input/mod.rs index 763620f5..a89682b9 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -707,17 +707,39 @@ impl State { self.niri.queue_redraw_all(); } Action::ConsumeOrExpelWindowLeft => { - self.niri.layout.consume_or_expel_window_left(); + self.niri.layout.consume_or_expel_window_left(None); self.maybe_warp_cursor_to_focus(); // FIXME: granular self.niri.queue_redraw_all(); } + Action::ConsumeOrExpelWindowLeftById(id) => { + let window = self.niri.layout.windows().find(|(_, m)| m.id().get() == id); + let window = window.map(|(_, m)| m.window.clone()); + if let Some(window) = window { + self.niri.layout.consume_or_expel_window_left(Some(&window)); + self.maybe_warp_cursor_to_focus(); + // FIXME: granular + self.niri.queue_redraw_all(); + } + } Action::ConsumeOrExpelWindowRight => { - self.niri.layout.consume_or_expel_window_right(); + self.niri.layout.consume_or_expel_window_right(None); self.maybe_warp_cursor_to_focus(); // FIXME: granular self.niri.queue_redraw_all(); } + Action::ConsumeOrExpelWindowRightById(id) => { + let window = self.niri.layout.windows().find(|(_, m)| m.id().get() == id); + let window = window.map(|(_, m)| m.window.clone()); + if let Some(window) = window { + self.niri + .layout + .consume_or_expel_window_right(Some(&window)); + self.maybe_warp_cursor_to_focus(); + // FIXME: granular + self.niri.queue_redraw_all(); + } + } Action::FocusColumnLeft => { self.niri.layout.focus_left(); self.maybe_warp_cursor_to_focus(); diff --git a/src/layout/mod.rs b/src/layout/mod.rs index 7fe0d230..0a1063fb 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -1340,18 +1340,38 @@ impl<W: LayoutElement> Layout<W> { monitor.move_up_or_to_workspace_up(); } - pub fn consume_or_expel_window_left(&mut self) { - let Some(monitor) = self.active_monitor() else { + pub fn consume_or_expel_window_left(&mut self, window: Option<&W::Id>) { + let workspace = if let Some(window) = window { + Some( + self.workspaces_mut() + .find(|ws| ws.has_window(window)) + .unwrap(), + ) + } else { + self.active_workspace_mut() + }; + + let Some(workspace) = workspace else { return; }; - monitor.consume_or_expel_window_left(); + workspace.consume_or_expel_window_left(window); } - pub fn consume_or_expel_window_right(&mut self) { - let Some(monitor) = self.active_monitor() else { + pub fn consume_or_expel_window_right(&mut self, window: Option<&W::Id>) { + let workspace = if let Some(window) = window { + Some( + self.workspaces_mut() + .find(|ws| ws.has_window(window)) + .unwrap(), + ) + } else { + self.active_workspace_mut() + }; + + let Some(workspace) = workspace else { return; }; - monitor.consume_or_expel_window_right(); + workspace.consume_or_expel_window_right(window); } pub fn focus_left(&mut self) { @@ -3011,8 +3031,14 @@ mod tests { MoveWindowUp, MoveWindowDownOrToWorkspaceDown, MoveWindowUpOrToWorkspaceUp, - ConsumeOrExpelWindowLeft, - ConsumeOrExpelWindowRight, + ConsumeOrExpelWindowLeft { + #[proptest(strategy = "proptest::option::of(1..=5usize)")] + id: Option<usize>, + }, + ConsumeOrExpelWindowRight { + #[proptest(strategy = "proptest::option::of(1..=5usize)")] + id: Option<usize>, + }, ConsumeWindowIntoColumn, ExpelWindowFromColumn, CenterColumn, @@ -3420,8 +3446,14 @@ mod tests { Op::MoveWindowUp => layout.move_up(), Op::MoveWindowDownOrToWorkspaceDown => layout.move_down_or_to_workspace_down(), Op::MoveWindowUpOrToWorkspaceUp => layout.move_up_or_to_workspace_up(), - Op::ConsumeOrExpelWindowLeft => layout.consume_or_expel_window_left(), - Op::ConsumeOrExpelWindowRight => layout.consume_or_expel_window_right(), + Op::ConsumeOrExpelWindowLeft { id } => { + let id = id.filter(|id| layout.has_window(id)); + layout.consume_or_expel_window_left(id.as_ref()); + } + Op::ConsumeOrExpelWindowRight { id } => { + let id = id.filter(|id| layout.has_window(id)); + layout.consume_or_expel_window_right(id.as_ref()); + } Op::ConsumeWindowIntoColumn => layout.consume_into_column(), Op::ExpelWindowFromColumn => layout.expel_from_column(), Op::CenterColumn => layout.center_column(), @@ -3700,8 +3732,8 @@ mod tests { Op::MoveWindowDownOrToWorkspaceDown, Op::MoveWindowUp, Op::MoveWindowUpOrToWorkspaceUp, - Op::ConsumeOrExpelWindowLeft, - Op::ConsumeOrExpelWindowRight, + Op::ConsumeOrExpelWindowLeft { id: None }, + Op::ConsumeOrExpelWindowRight { id: None }, Op::MoveWorkspaceToOutput(1), ]; @@ -3897,8 +3929,8 @@ mod tests { Op::MoveWindowDownOrToWorkspaceDown, Op::MoveWindowUp, Op::MoveWindowUpOrToWorkspaceUp, - Op::ConsumeOrExpelWindowLeft, - Op::ConsumeOrExpelWindowRight, + Op::ConsumeOrExpelWindowLeft { id: None }, + Op::ConsumeOrExpelWindowRight { id: None }, ]; for third in every_op { @@ -4233,7 +4265,7 @@ mod tests { bbox: Rectangle::from_loc_and_size((0, 0), (100, 200)), min_max_size: (Size::from((0, 0)), Size::from((i32::MAX, i32::MAX))), }, - Op::ConsumeOrExpelWindowLeft, + Op::ConsumeOrExpelWindowLeft { id: None }, Op::SetFullscreenWindow { window: 2, is_fullscreen: false, @@ -4301,7 +4333,7 @@ mod tests { bbox: Rectangle::from_loc_and_size((0, 0), (100, 200)), min_max_size: Default::default(), }, - Op::ConsumeOrExpelWindowRight, + Op::ConsumeOrExpelWindowRight { id: None }, ]; check_ops(&ops); @@ -4358,7 +4390,7 @@ mod tests { bbox: Rectangle::from_loc_and_size((0, 0), (100, 200)), min_max_size: Default::default(), }, - Op::ConsumeOrExpelWindowLeft, + Op::ConsumeOrExpelWindowLeft { id: None }, Op::FullscreenWindow(0), ]; @@ -4459,7 +4491,7 @@ mod tests { bbox: Rectangle::from_loc_and_size((0, 0), (1280, 200)), min_max_size: Default::default(), }, - Op::ConsumeOrExpelWindowLeft, + Op::ConsumeOrExpelWindowLeft { id: None }, Op::SwitchPresetWindowHeight { id: None }, Op::SwitchPresetWindowHeight { id: None }, ]; @@ -4554,13 +4586,13 @@ mod tests { bbox: Rectangle::from_loc_and_size((0, 0), (100, 200)), min_max_size: Default::default(), }, - Op::ConsumeOrExpelWindowLeft, + Op::ConsumeOrExpelWindowLeft { id: None }, Op::AddWindow { id: 2, bbox: Rectangle::from_loc_and_size((0, 0), (100, 200)), min_max_size: Default::default(), }, - Op::ConsumeOrExpelWindowLeft, + Op::ConsumeOrExpelWindowLeft { id: None }, Op::SetWindowHeight { id: None, change: SizeChange::SetFixed(100), @@ -4589,13 +4621,13 @@ mod tests { bbox: Rectangle::from_loc_and_size((0, 0), (100, 200)), min_max_size: Default::default(), }, - Op::ConsumeOrExpelWindowLeft, + Op::ConsumeOrExpelWindowLeft { id: None }, Op::AddWindow { id: 2, bbox: Rectangle::from_loc_and_size((0, 0), (100, 200)), min_max_size: Default::default(), }, - Op::ConsumeOrExpelWindowLeft, + Op::ConsumeOrExpelWindowLeft { id: None }, Op::SetWindowHeight { id: None, change: SizeChange::SetFixed(100), @@ -4628,13 +4660,13 @@ mod tests { bbox: Rectangle::from_loc_and_size((0, 0), (100, 200)), min_max_size: Default::default(), }, - Op::ConsumeOrExpelWindowLeft, + Op::ConsumeOrExpelWindowLeft { id: None }, Op::AddWindow { id: 2, bbox: Rectangle::from_loc_and_size((0, 0), (100, 200)), min_max_size: Default::default(), }, - Op::ConsumeOrExpelWindowLeft, + Op::ConsumeOrExpelWindowLeft { id: None }, Op::SetWindowHeight { id: None, change: SizeChange::SetFixed(100), @@ -4671,7 +4703,7 @@ mod tests { bbox: Rectangle::from_loc_and_size((0, 0), (100, 200)), min_max_size: Default::default(), }, - Op::ConsumeOrExpelWindowLeft, + Op::ConsumeOrExpelWindowLeft { id: None }, ]; let options = Options { diff --git a/src/layout/monitor.rs b/src/layout/monitor.rs index 9e2c6e27..e1cfb512 100644 --- a/src/layout/monitor.rs +++ b/src/layout/monitor.rs @@ -325,14 +325,6 @@ impl<W: LayoutElement> Monitor<W> { } } - pub fn consume_or_expel_window_left(&mut self) { - self.active_workspace().consume_or_expel_window_left(); - } - - pub fn consume_or_expel_window_right(&mut self) { - self.active_workspace().consume_or_expel_window_right(); - } - pub fn focus_left(&mut self) { self.active_workspace().focus_left(); } diff --git a/src/layout/workspace.rs b/src/layout/workspace.rs index 55d18a06..d115f07c 100644 --- a/src/layout/workspace.rs +++ b/src/layout/workspace.rs @@ -1798,28 +1798,59 @@ impl<W: LayoutElement> Workspace<W> { self.columns[self.active_column_idx].move_up(); } - pub fn consume_or_expel_window_left(&mut self) { + pub fn consume_or_expel_window_left(&mut self, window: Option<&W::Id>) { if self.columns.is_empty() { return; } - let source_col_idx = self.active_column_idx; + let (source_col_idx, source_tile_idx) = if let Some(window) = window { + self.columns + .iter_mut() + .enumerate() + .find_map(|(col_idx, col)| { + col.tiles + .iter() + .position(|tile| tile.window().id() == window) + .map(|tile_idx| (col_idx, tile_idx)) + }) + .unwrap() + } else { + let source_col_idx = self.active_column_idx; + let source_tile_idx = self.columns[self.active_column_idx].active_tile_idx; + (source_col_idx, source_tile_idx) + }; + let source_column = &self.columns[source_col_idx]; - let prev_off = source_column.tile_offset(source_column.active_tile_idx); + let prev_off = source_column.tile_offset(source_tile_idx); + + let source_tile_was_active = self.active_column_idx == source_col_idx + && source_column.active_tile_idx == source_tile_idx; if source_column.tiles.len() == 1 { - if self.active_column_idx == 0 { + if source_col_idx == 0 { return; } - 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 = if self.active_column_idx <= source_col_idx { + // Tiles to the right animate from the following column. + self.column_x(source_col_idx) - self.column_x(target_column_idx) + } else { + // Tiles to the left animate to preserve their right edge position. + f64::max( + 0., + self.data[target_column_idx].width - self.data[source_col_idx].width, + ) + }; + let mut offset = Point::from((offset, 0.)); + + if source_tile_was_active { + // 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); + } + offset.x += self.columns[source_col_idx].render_offset().x; let tile = self.remove_tile_by_idx( source_col_idx, @@ -1827,7 +1858,7 @@ impl<W: LayoutElement> Workspace<W> { Transaction::new(), Some(self.options.animations.window_movement.0), ); - self.add_tile_to_column(target_column_idx, None, tile, true); + self.add_tile_to_column(target_column_idx, None, tile, source_tile_was_active); let target_column = &mut self.columns[target_column_idx]; offset.x -= target_column.render_offset().x; @@ -1842,63 +1873,91 @@ impl<W: LayoutElement> Workspace<W> { 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, - Transaction::new(), - None, - ); + let tile = + self.remove_tile_by_idx(source_col_idx, source_tile_idx, Transaction::new(), None); + + // We're inserting into the source column position. + let target_column_idx = source_col_idx; self.add_tile( - Some(self.active_column_idx), + Some(target_column_idx), tile, - true, + source_tile_was_active, width, is_full_width, Some(self.options.animations.window_movement.0), ); - // We added to the left, don't activate even further left on removal. - self.activate_prev_column_on_removal = None; + if source_tile_was_active { + // We added to the left, don't activate even further left on removal. + self.activate_prev_column_on_removal = None; + } - let new_col = &mut self.columns[self.active_column_idx]; + if target_column_idx < self.active_column_idx { + // Tiles to the left animate from the following column. + offset.x += self.column_x(target_column_idx + 1) - self.column_x(target_column_idx); + } + + let new_col = &mut self.columns[target_column_idx]; offset += prev_off - new_col.tile_offset(0); new_col.tiles[0].animate_move_from(offset); } } - pub fn consume_or_expel_window_right(&mut self) { + pub fn consume_or_expel_window_right(&mut self, window: Option<&W::Id>) { if self.columns.is_empty() { return; } - let source_col_idx = self.active_column_idx; - let offset = self.column_x(source_col_idx) - self.column_x(source_col_idx + 1); - let mut offset = Point::from((offset, 0.)); + let (source_col_idx, source_tile_idx) = if let Some(window) = window { + self.columns + .iter_mut() + .enumerate() + .find_map(|(col_idx, col)| { + col.tiles + .iter() + .position(|tile| tile.window().id() == window) + .map(|tile_idx| (col_idx, tile_idx)) + }) + .unwrap() + } else { + let source_col_idx = self.active_column_idx; + let source_tile_idx = self.columns[self.active_column_idx].active_tile_idx; + (source_col_idx, source_tile_idx) + }; + + let cur_x = self.column_x(source_col_idx); let source_column = &self.columns[source_col_idx]; - offset.x += source_column.render_offset().x; - let prev_off = source_column.tile_offset(source_column.active_tile_idx); + let mut offset = Point::from((source_column.render_offset().x, 0.)); + let prev_off = source_column.tile_offset(source_tile_idx); + + let source_tile_was_active = self.active_column_idx == source_col_idx + && source_column.active_tile_idx == source_tile_idx; if source_column.tiles.len() == 1 { - if self.active_column_idx + 1 == self.columns.len() { + if source_col_idx + 1 == self.columns.len() { return; } // Move into adjacent column. let target_column_idx = source_col_idx; + offset.x += cur_x - self.column_x(source_col_idx + 1); 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; + if source_tile_was_active { + // Make sure the target column gets activated. + self.activate_prev_column_on_removal = None; + } + let tile = self.remove_tile_by_idx( source_col_idx, 0, Transaction::new(), Some(self.options.animations.window_movement.0), ); - self.add_tile_to_column(target_column_idx, None, tile, true); + self.add_tile_to_column(target_column_idx, None, tile, source_tile_was_active); let target_column = &mut self.columns[target_column_idx]; offset += prev_off - target_column.tile_offset(target_column.tiles.len() - 1); @@ -1910,23 +1969,31 @@ impl<W: LayoutElement> Workspace<W> { let width = source_column.width; let is_full_width = source_column.is_full_width; - let tile = self.remove_tile_by_idx( - source_col_idx, - source_column.active_tile_idx, - Transaction::new(), - None, - ); + let prev_width = self.data[source_col_idx].width; + + let tile = + self.remove_tile_by_idx(source_col_idx, source_tile_idx, Transaction::new(), None); + + let target_column_idx = source_col_idx + 1; self.add_tile( - Some(self.active_column_idx + 1), + Some(target_column_idx), tile, - true, + source_tile_was_active, width, is_full_width, Some(self.options.animations.window_movement.0), ); - let new_col = &mut self.columns[self.active_column_idx]; + offset.x += if self.active_column_idx <= target_column_idx { + // Tiles to the right animate to the following column. + cur_x - self.column_x(target_column_idx) + } else { + // Tiles to the left animate for a change in width. + -f64::max(0., prev_width - self.data[target_column_idx].width) + }; + + let new_col = &mut self.columns[target_column_idx]; offset += prev_off - new_col.tile_offset(0); new_col.tiles[0].animate_move_from(offset); } |
