mirror of
https://github.com/niri-wm/niri.git
synced 2026-06-22 02:01:55 +07:00
Add move-floating-window action
This commit is contained in:
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+31
-14
@@ -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 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);
|
||||
|
||||
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 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);
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
+46
-1
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
+50
-1
@@ -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()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user