diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/input/mod.rs | 16 | ||||
| -rw-r--r-- | src/layout/floating.rs | 45 | ||||
| -rw-r--r-- | src/layout/mod.rs | 47 | ||||
| -rw-r--r-- | src/layout/scrolling.rs | 9 | ||||
| -rw-r--r-- | src/layout/workspace.rs | 51 |
5 files changed, 152 insertions, 16 deletions
diff --git a/src/input/mod.rs b/src/input/mod.rs index d874ee34..83336fb3 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -1361,6 +1361,22 @@ impl State { // FIXME: granular self.niri.queue_redraw_all(); } + Action::MoveFloatingWindowById { id, x, y } => { + let window = if let Some(id) = id { + let window = self.niri.layout.windows().find(|(_, m)| m.id().get() == id); + let window = window.map(|(_, m)| m.window.clone()); + if window.is_none() { + return; + } + window + } else { + None + }; + + self.niri.layout.move_floating_window(window.as_ref(), x, y); + // FIXME: granular + self.niri.queue_redraw_all(); + } } } diff --git a/src/layout/floating.rs b/src/layout/floating.rs index 0552b01d..d59a33e7 100644 --- a/src/layout/floating.rs +++ b/src/layout/floating.rs @@ -3,7 +3,7 @@ use std::iter::zip; use std::rc::Rc; use niri_config::PresetSize; -use niri_ipc::SizeChange; +use niri_ipc::{PositionChange, SizeChange}; use smithay::backend::renderer::gles::GlesRenderer; use smithay::utils::{Logical, Point, Rectangle, Scale, Serial, Size}; @@ -839,16 +839,19 @@ impl<W: LayoutElement> FloatingSpace<W> { } } + fn move_to(&mut self, idx: usize, new_pos: Point<f64, Logical>) { + self.move_and_animate(idx, new_pos); + self.interactive_resize_end(None); + } + fn move_by(&mut self, amount: Point<f64, Logical>) { let Some(active_id) = &self.active_window_id else { return; }; - let active_idx = self.idx_of(active_id).unwrap(); - - let new_pos = self.data[active_idx].logical_pos + amount; - self.move_and_animate(active_idx, new_pos); + let idx = self.idx_of(active_id).unwrap(); - self.interactive_resize_end(None); + let new_pos = self.data[idx].logical_pos + amount; + self.move_to(idx, new_pos) } pub fn move_left(&mut self) { @@ -867,17 +870,32 @@ impl<W: LayoutElement> FloatingSpace<W> { self.move_by(Point::from((0., DIRECTIONAL_MOVE_PX))); } + pub fn move_window(&mut self, id: Option<&W::Id>, x: PositionChange, y: PositionChange) { + let Some(id) = id.or(self.active_window_id.as_ref()) else { + return; + }; + let idx = self.idx_of(id).unwrap(); + + let mut new_pos = self.data[idx].logical_pos; + match x { + PositionChange::SetFixed(x) => new_pos.x = x + self.working_area.loc.x, + PositionChange::AdjustFixed(x) => new_pos.x += x, + } + match y { + PositionChange::SetFixed(y) => new_pos.y = y + self.working_area.loc.y, + PositionChange::AdjustFixed(y) => new_pos.y += y, + } + self.move_to(idx, new_pos); + } + pub fn center_window(&mut self) { let Some(active_id) = &self.active_window_id else { return; }; - let active_idx = self.idx_of(active_id).unwrap(); - - let new_pos = - center_preferring_top_left_in_area(self.working_area, self.data[active_idx].size); - self.move_and_animate(active_idx, new_pos); + let idx = self.idx_of(active_id).unwrap(); - self.interactive_resize_end(None); + let new_pos = center_preferring_top_left_in_area(self.working_area, self.data[idx].size); + self.move_to(idx, new_pos); } pub fn descendants_added(&mut self, id: &W::Id) -> bool { @@ -1082,7 +1100,7 @@ impl<W: LayoutElement> FloatingSpace<W> { rect.loc } - fn scale_by_working_area(&self, pos: Point<f64, SizeFrac>) -> Point<f64, Logical> { + pub fn scale_by_working_area(&self, pos: Point<f64, SizeFrac>) -> Point<f64, Logical> { Data::scale_by_working_area(self.working_area, pos) } @@ -1163,7 +1181,6 @@ impl<W: LayoutElement> FloatingSpace<W> { self.view_size } - #[cfg(test)] pub fn working_area(&self) -> Rectangle<f64, Logical> { self.working_area } diff --git a/src/layout/mod.rs b/src/layout/mod.rs index c11eb6ab..ed92fad3 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -40,7 +40,7 @@ use niri_config::{ CenterFocusedColumn, Config, CornerRadius, FloatOrInt, PresetSize, Struts, Workspace as WorkspaceConfig, }; -use niri_ipc::SizeChange; +use niri_ipc::{PositionChange, SizeChange}; use scrolling::{Column, ColumnWidth, InsertHint, InsertPosition}; use smithay::backend::renderer::element::surface::WaylandSurfaceRenderElement; use smithay::backend::renderer::element::Id; @@ -2750,6 +2750,30 @@ impl<W: LayoutElement> Layout<W> { workspace.switch_focus_floating_tiling(); } + pub fn move_floating_window( + &mut self, + id: Option<&W::Id>, + x: PositionChange, + y: PositionChange, + ) { + if let Some(InteractiveMoveState::Moving(move_)) = &mut self.interactive_move { + if id.is_none() || id == Some(move_.tile.window().id()) { + return; + } + } + + let workspace = if let Some(id) = id { + Some(self.workspaces_mut().find(|ws| ws.has_window(id)).unwrap()) + } else { + self.active_workspace_mut() + }; + + let Some(workspace) = workspace else { + return; + }; + workspace.move_floating_window(id, x, y); + } + pub fn focus_output(&mut self, output: &Output) { if let MonitorSet::Normal { monitors, @@ -4202,6 +4226,15 @@ mod tests { ] } + fn arbitrary_position_change() -> impl Strategy<Value = PositionChange> { + prop_oneof![ + (-1000f64..1000f64).prop_map(PositionChange::SetFixed), + (-1000f64..1000f64).prop_map(PositionChange::AdjustFixed), + any::<f64>().prop_map(PositionChange::SetFixed), + any::<f64>().prop_map(PositionChange::AdjustFixed), + ] + } + fn arbitrary_min_max() -> impl Strategy<Value = (i32, i32)> { prop_oneof![ Just((0, 0)), @@ -4410,6 +4443,14 @@ mod tests { FocusFloating, FocusTiling, SwitchFocusFloatingTiling, + MoveFloatingWindow { + #[proptest(strategy = "proptest::option::of(1..=5usize)")] + id: Option<usize>, + #[proptest(strategy = "arbitrary_position_change()")] + x: PositionChange, + #[proptest(strategy = "arbitrary_position_change()")] + y: PositionChange, + }, SetParent { #[proptest(strategy = "1..=5usize")] id: usize, @@ -4934,6 +4975,10 @@ mod tests { Op::SwitchFocusFloatingTiling => { layout.switch_focus_floating_tiling(); } + Op::MoveFloatingWindow { id, x, y } => { + let id = id.filter(|id| layout.has_window(id)); + layout.move_floating_window(id.as_ref(), x, y); + } Op::SetParent { id, mut new_parent_id, diff --git a/src/layout/scrolling.rs b/src/layout/scrolling.rs index c0de770a..1a8c8f5f 100644 --- a/src/layout/scrolling.rs +++ b/src/layout/scrolling.rs @@ -373,6 +373,15 @@ impl<W: LayoutElement> ScrollingSpace<W> { Some(col.tiles[col.active_tile_idx].window()) } + pub fn active_tile_mut(&mut self) -> Option<&mut Tile<W>> { + if self.columns.is_empty() { + return None; + } + + let col = &mut self.columns[self.active_column_idx]; + Some(&mut col.tiles[col.active_tile_idx]) + } + pub fn is_active_fullscreen(&self) -> bool { if self.columns.is_empty() { return false; diff --git a/src/layout/workspace.rs b/src/layout/workspace.rs index 06900afc..de190275 100644 --- a/src/layout/workspace.rs +++ b/src/layout/workspace.rs @@ -3,7 +3,7 @@ use std::rc::Rc; use std::time::Duration; use niri_config::{OutputName, PresetSize, Workspace as WorkspaceConfig}; -use niri_ipc::SizeChange; +use niri_ipc::{PositionChange, SizeChange}; use smithay::backend::renderer::gles::GlesRenderer; use smithay::desktop::{layer_map_for_output, Window}; use smithay::output::Output; @@ -1185,6 +1185,55 @@ impl<W: LayoutElement> Workspace<W> { }; } + pub fn move_floating_window( + &mut self, + id: Option<&W::Id>, + x: PositionChange, + y: PositionChange, + ) { + if id.map_or(self.floating_is_active.get(), |id| { + self.floating.has_window(id) + }) { + self.floating.move_window(id, x, y); + } else { + // If the target tile isn't floating, set its stored floating position. + let tile = if let Some(id) = id { + self.scrolling + .tiles_mut() + .find(|tile| tile.window().id() == id) + .unwrap() + } else if let Some(tile) = self.scrolling.active_tile_mut() { + tile + } else { + return; + }; + + let working_area_loc = self.floating.working_area().loc; + // If there's no stored floating position, we can only set both components at once, not + // adjust. + let Some(pos) = tile.floating_pos.or_else(|| { + (matches!(x, PositionChange::SetFixed(_)) + && matches!(y, PositionChange::SetFixed(_))) + .then_some(Point::default()) + }) else { + return; + }; + + let mut pos = self.floating.scale_by_working_area(pos); + match x { + PositionChange::SetFixed(x) => pos.x = x + working_area_loc.x, + PositionChange::AdjustFixed(x) => pos.x += x, + } + match y { + PositionChange::SetFixed(y) => pos.y = y + working_area_loc.y, + PositionChange::AdjustFixed(y) => pos.y += y, + } + + let pos = self.floating.logical_to_size_frac(pos); + tile.floating_pos = Some(pos); + } + } + pub fn has_windows(&self) -> bool { self.windows().next().is_some() } |
