From 09cf8402c3e25fbe4ab790cb997ae4033f9d30f3 Mon Sep 17 00:00:00 2001 From: Ivan Molodetskikh Date: Thu, 25 Sep 2025 18:15:46 +0300 Subject: Add per-output layout config --- src/layout/monitor.rs | 44 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 40 insertions(+), 4 deletions(-) (limited to 'src/layout/monitor.rs') diff --git a/src/layout/monitor.rs b/src/layout/monitor.rs index 54bcf9af..bb4b2ec8 100644 --- a/src/layout/monitor.rs +++ b/src/layout/monitor.rs @@ -3,7 +3,7 @@ use std::iter::zip; use std::rc::Rc; use std::time::Duration; -use niri_config::CornerRadius; +use niri_config::{CornerRadius, LayoutPart}; use smithay::backend::renderer::element::utils::{ CropRenderElement, Relocate, RelocateRenderElement, RescaleRenderElement, }; @@ -81,8 +81,12 @@ pub struct Monitor { overview_progress: Option, /// Clock for driving animations. pub(super) clock: Clock, + /// Configurable properties of the layout as received from the parent layout. + pub(super) base_options: Rc, /// Configurable properties of the layout. pub(super) options: Rc, + /// Layout config overrides for this monitor. + layout_config: Option, } #[derive(Debug)] @@ -280,8 +284,12 @@ impl Monitor { mut workspaces: Vec>, ws_id_to_activate: Option, clock: Clock, - options: Rc, + base_options: Rc, + layout_config: Option, ) -> Self { + let options = + Rc::new(Options::clone(&base_options).with_merged_layout(layout_config.as_ref())); + let scale = output.current_scale(); let view_size = output_size(&output); let working_area = compute_working_area(&output); @@ -293,6 +301,7 @@ impl Monitor { assert!(ws.has_windows_or_name()); ws.set_output(Some(output.clone())); + ws.update_config(options.clone()); if ws_id_to_activate.is_some_and(|id| ws.id() == id) { active_workspace_idx = idx; @@ -324,7 +333,9 @@ impl Monitor { overview_progress: None, workspace_switch: None, clock, + base_options, options, + layout_config, } } @@ -666,6 +677,7 @@ impl Monitor { pub fn insert_workspace(&mut self, mut ws: Workspace, mut idx: usize, activate: bool) { ws.set_output(Some(self.output.clone())); + ws.update_config(self.options.clone()); // Don't insert past the last empty workspace. if idx == self.workspaces.len() { @@ -699,6 +711,7 @@ impl Monitor { for ws in &mut workspaces { ws.set_output(Some(self.output.clone())); + ws.update_config(self.options.clone()); } let empty_was_focused = self.active_workspace_idx == self.workspaces.len() - 1; @@ -1151,7 +1164,10 @@ impl Monitor { } } - pub fn update_config(&mut self, options: Rc) { + pub fn update_config(&mut self, base_options: Rc) { + let options = + Rc::new(Options::clone(&base_options).with_merged_layout(self.layout_config.as_ref())); + if self.options.layout.empty_workspace_above_first != options.layout.empty_workspace_above_first && self.workspaces.len() > 1 @@ -1171,9 +1187,21 @@ impl Monitor { self.insert_hint_element .update_config(options.layout.insert_hint); + self.base_options = base_options; self.options = options; } + pub fn update_layout_config(&mut self, layout_config: Option) -> bool { + if self.layout_config == layout_config { + return false; + } + + self.layout_config = layout_config; + self.update_config(self.base_options.clone()); + + true + } + pub fn update_shaders(&mut self) { for ws in &mut self.workspaces { ws.update_shaders(); @@ -2017,10 +2045,18 @@ impl Monitor { self.working_area } + pub fn layout_config(&self) -> Option<&niri_config::LayoutPart> { + self.layout_config.as_ref() + } + #[cfg(test)] pub(super) fn verify_invariants(&self) { use approx::assert_abs_diff_eq; + let options = + Options::clone(&self.base_options).with_merged_layout(self.layout_config.as_ref()); + assert_eq!(&*self.options, &options); + assert!( !self.workspaces.is_empty(), "monitor must have at least one workspace" @@ -2107,7 +2143,7 @@ impl Monitor { assert_eq!( workspace.base_options, self.options, - "workspace options must be synchronized with layout" + "workspace options must be synchronized with monitor" ); } -- cgit