aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/handlers/compositor.rs18
-rw-r--r--src/layout/mod.rs138
-rw-r--r--src/layout/monitor.rs25
-rw-r--r--src/layout/workspace.rs37
4 files changed, 216 insertions, 2 deletions
diff --git a/src/handlers/compositor.rs b/src/handlers/compositor.rs
index 517c8942..79ad5d12 100644
--- a/src/handlers/compositor.rs
+++ b/src/handlers/compositor.rs
@@ -104,8 +104,22 @@ impl CompositorHandler for State {
let window = entry.remove();
window.on_commit();
- if let Some(output) = self.niri.layout.add_window(window, None, false).cloned()
- {
+ let parent = window
+ .toplevel()
+ .parent()
+ .and_then(|parent| self.niri.layout.find_window_and_output(&parent))
+ .map(|(win, _)| win.clone());
+
+ let win = window.clone();
+
+ // Open dialogs immediately to the right of their parent window.
+ let output = if let Some(p) = parent {
+ self.niri.layout.add_window_right_of(&p, win, None, false)
+ } else {
+ self.niri.layout.add_window(win, None, false)
+ };
+
+ if let Some(output) = output.cloned() {
self.niri.queue_redraw(output);
}
return;
diff --git a/src/layout/mod.rs b/src/layout/mod.rs
index acd26348..dff5c0d0 100644
--- a/src/layout/mod.rs
+++ b/src/layout/mod.rs
@@ -564,6 +564,58 @@ impl<W: LayoutElement> Layout<W> {
}
}
+ /// Adds a new window to the layout immediately to the right of another window.
+ ///
+ /// If that another window was active, activates the new window.
+ ///
+ /// Returns an output that the window was added to, if there were any outputs.
+ pub fn add_window_right_of(
+ &mut self,
+ right_of: &W,
+ window: W,
+ width: Option<ColumnWidth>,
+ is_full_width: bool,
+ ) -> Option<&Output> {
+ let width = width
+ .or(self.options.default_width)
+ .unwrap_or_else(|| ColumnWidth::Fixed(window.size().w));
+
+ match &mut self.monitor_set {
+ MonitorSet::Normal {
+ monitors,
+ active_monitor_idx,
+ ..
+ } => {
+ let mon_idx = monitors
+ .iter()
+ .position(|mon| mon.workspaces.iter().any(|ws| ws.has_window(right_of)))
+ .unwrap();
+ let mon = &mut monitors[mon_idx];
+
+ let mut activate = false;
+ if mon_idx == *active_monitor_idx {
+ let active_ws = &mon.workspaces[mon.active_workspace_idx];
+ if !active_ws.columns.is_empty() {
+ let active_col = &active_ws.columns[active_ws.active_column_idx];
+ let active_tile = &active_col.tiles[active_col.active_tile_idx];
+ activate = active_tile.window() == right_of;
+ }
+ }
+
+ mon.add_window_right_of(right_of, window, activate, width, is_full_width);
+ Some(&mon.output)
+ }
+ MonitorSet::NoOutputs { workspaces } => {
+ let ws = workspaces
+ .iter_mut()
+ .find(|ws| ws.has_window(right_of))
+ .unwrap();
+ ws.add_window_right_of(right_of, window, true, width, is_full_width);
+ None
+ }
+ }
+ }
+
pub fn remove_window(&mut self, window: &W) {
match &mut self.monitor_set {
MonitorSet::Normal { monitors, .. } => {
@@ -1777,6 +1829,16 @@ mod tests {
#[proptest(strategy = "arbitrary_min_max_size()")]
min_max_size: (Size<i32, Logical>, Size<i32, Logical>),
},
+ AddWindowRightOf {
+ #[proptest(strategy = "1..=5usize")]
+ id: usize,
+ #[proptest(strategy = "1..=5usize")]
+ right_of_id: usize,
+ #[proptest(strategy = "arbitrary_bbox()")]
+ bbox: Rectangle<i32, Logical>,
+ #[proptest(strategy = "arbitrary_min_max_size()")]
+ min_max_size: (Size<i32, Logical>, Size<i32, Logical>),
+ },
CloseWindow(#[proptest(strategy = "1..=5usize")] usize),
FullscreenWindow(#[proptest(strategy = "1..=5usize")] usize),
FocusColumnLeft,
@@ -1897,6 +1959,58 @@ mod tests {
let win = TestWindow::new(id, bbox, min_max_size.0, min_max_size.1);
layout.add_window(win, None, false);
}
+ Op::AddWindowRightOf {
+ id,
+ right_of_id,
+ bbox,
+ min_max_size,
+ } => {
+ let mut found_right_of = false;
+
+ match &mut layout.monitor_set {
+ MonitorSet::Normal { monitors, .. } => {
+ for mon in monitors {
+ for ws in &mut mon.workspaces {
+ for win in ws.windows() {
+ if win.0.id == id {
+ return;
+ }
+
+ if win.0.id == right_of_id {
+ found_right_of = true;
+ }
+ }
+ }
+ }
+ }
+ MonitorSet::NoOutputs { workspaces, .. } => {
+ for ws in workspaces {
+ for win in ws.windows() {
+ if win.0.id == id {
+ return;
+ }
+
+ if win.0.id == right_of_id {
+ found_right_of = true;
+ }
+ }
+ }
+ }
+ }
+
+ if !found_right_of {
+ return;
+ }
+
+ let right_of_win = TestWindow::new(
+ right_of_id,
+ Rectangle::default(),
+ Size::default(),
+ Size::default(),
+ );
+ let win = TestWindow::new(id, bbox, min_max_size.0, min_max_size.1);
+ layout.add_window_right_of(&right_of_win, win, None, false);
+ }
Op::CloseWindow(id) => {
let dummy =
TestWindow::new(id, Rectangle::default(), Size::default(), Size::default());
@@ -2052,6 +2166,18 @@ mod tests {
bbox: Rectangle::from_loc_and_size((0, 0), (100, 200)),
min_max_size: Default::default(),
},
+ Op::AddWindowRightOf {
+ id: 3,
+ right_of_id: 0,
+ bbox: Rectangle::from_loc_and_size((0, 0), (100, 200)),
+ min_max_size: Default::default(),
+ },
+ Op::AddWindowRightOf {
+ id: 4,
+ right_of_id: 1,
+ bbox: Rectangle::from_loc_and_size((0, 0), (100, 200)),
+ min_max_size: Default::default(),
+ },
Op::CloseWindow(0),
Op::CloseWindow(1),
Op::CloseWindow(2),
@@ -2185,6 +2311,18 @@ mod tests {
bbox: Rectangle::from_loc_and_size((0, 0), (100, 200)),
min_max_size: Default::default(),
},
+ Op::AddWindowRightOf {
+ id: 6,
+ right_of_id: 0,
+ bbox: Rectangle::from_loc_and_size((0, 0), (100, 200)),
+ min_max_size: Default::default(),
+ },
+ Op::AddWindowRightOf {
+ id: 7,
+ right_of_id: 1,
+ bbox: Rectangle::from_loc_and_size((0, 0), (100, 200)),
+ min_max_size: Default::default(),
+ },
Op::CloseWindow(0),
Op::CloseWindow(1),
Op::CloseWindow(2),
diff --git a/src/layout/monitor.rs b/src/layout/monitor.rs
index 0e5802cd..f5da804a 100644
--- a/src/layout/monitor.rs
+++ b/src/layout/monitor.rs
@@ -126,6 +126,31 @@ impl<W: LayoutElement> Monitor<W> {
}
}
+ pub fn add_window_right_of(
+ &mut self,
+ right_of: &W,
+ window: W,
+ activate: bool,
+ width: ColumnWidth,
+ is_full_width: bool,
+ ) {
+ let workspace_idx = self
+ .workspaces
+ .iter_mut()
+ .position(|ws| ws.has_window(right_of))
+ .unwrap();
+ let workspace = &mut self.workspaces[workspace_idx];
+
+ workspace.add_window_right_of(right_of, window, activate, width, is_full_width);
+
+ // After adding a new window, workspace becomes this output's own.
+ workspace.original_output = OutputId::new(&self.output);
+
+ if activate {
+ self.activate_workspace(workspace_idx);
+ }
+ }
+
pub fn add_column(&mut self, workspace_idx: usize, column: Column<W>, activate: bool) {
let workspace = &mut self.workspaces[workspace_idx];
diff --git a/src/layout/workspace.rs b/src/layout/workspace.rs
index 6e2a7c00..47be5877 100644
--- a/src/layout/workspace.rs
+++ b/src/layout/workspace.rs
@@ -555,6 +555,43 @@ impl<W: LayoutElement> Workspace<W> {
}
}
+ pub fn add_window_right_of(
+ &mut self,
+ right_of: &W,
+ window: W,
+ activate: bool,
+ width: ColumnWidth,
+ is_full_width: bool,
+ ) {
+ self.enter_output_for_window(&window);
+
+ let idx = self
+ .columns
+ .iter()
+ .position(|col| col.contains(right_of))
+ .unwrap()
+ + 1;
+
+ let column = Column::new(
+ window,
+ self.view_size,
+ self.working_area,
+ self.options.clone(),
+ width,
+ is_full_width,
+ );
+ self.columns.insert(idx, column);
+
+ if self.active_column_idx >= idx {
+ self.active_column_idx += 1;
+ }
+
+ if activate {
+ self.activate_column(idx);
+ self.activate_prev_column_on_removal = true;
+ }
+ }
+
pub fn add_column(&mut self, mut column: Column<W>, activate: bool) {
for tile in &column.tiles {
self.enter_output_for_window(tile.window());