diff options
| author | Ivan Molodetskikh <yalterz@gmail.com> | 2024-03-19 18:22:25 +0400 |
|---|---|---|
| committer | Ivan Molodetskikh <yalterz@gmail.com> | 2024-03-19 18:29:13 +0400 |
| commit | db49deb7fd2fbe805ceec060aa4dec65009ad7a7 (patch) | |
| tree | b8f85d4aee4d366643626e97b7f37fa49d93b75b | |
| parent | c61361de3ca4484387f39b067eadc612908560eb (diff) | |
| download | niri-db49deb7fd2fbe805ceec060aa4dec65009ad7a7.tar.gz niri-db49deb7fd2fbe805ceec060aa4dec65009ad7a7.tar.bz2 niri-db49deb7fd2fbe805ceec060aa4dec65009ad7a7.zip | |
Implement draw-border-with-background window rule
| -rw-r--r-- | niri-config/src/lib.rs | 3 | ||||
| -rw-r--r-- | niri-visual-tests/src/test_window.rs | 6 | ||||
| -rw-r--r-- | resources/default-config.kdl | 10 | ||||
| -rw-r--r-- | src/handlers/compositor.rs | 2 | ||||
| -rw-r--r-- | src/layout/mod.rs | 8 | ||||
| -rw-r--r-- | src/layout/tile.rs | 26 | ||||
| -rw-r--r-- | src/window/mapped.rs | 4 | ||||
| -rw-r--r-- | src/window/mod.rs | 27 |
8 files changed, 79 insertions, 7 deletions
diff --git a/niri-config/src/lib.rs b/niri-config/src/lib.rs index 7a1233da..937df0ad 100644 --- a/niri-config/src/lib.rs +++ b/niri-config/src/lib.rs @@ -693,6 +693,9 @@ pub struct WindowRule { pub max_width: Option<u16>, #[knuffel(child, unwrap(argument))] pub max_height: Option<u16>, + + #[knuffel(child, unwrap(argument))] + pub draw_border_with_background: Option<bool>, } #[derive(knuffel::Decode, Debug, Default, Clone)] diff --git a/niri-visual-tests/src/test_window.rs b/niri-visual-tests/src/test_window.rs index df7f33c5..a0098f73 100644 --- a/niri-visual-tests/src/test_window.rs +++ b/niri-visual-tests/src/test_window.rs @@ -4,6 +4,7 @@ use std::rc::Rc; use niri::layout::{LayoutElement, LayoutElementRenderElement}; use niri::render_helpers::renderer::NiriRenderer; +use niri::window::ResolvedWindowRules; use smithay::backend::renderer::element::solid::{SolidColorBuffer, SolidColorRenderElement}; use smithay::backend::renderer::element::{Id, Kind}; use smithay::output::Output; @@ -217,4 +218,9 @@ impl LayoutElement for TestWindow { } fn refresh(&self) {} + + fn rules(&self) -> &ResolvedWindowRules { + static EMPTY: ResolvedWindowRules = ResolvedWindowRules::empty(); + &EMPTY + } } diff --git a/resources/default-config.kdl b/resources/default-config.kdl index 014135fa..1b6cddf2 100644 --- a/resources/default-config.kdl +++ b/resources/default-config.kdl @@ -123,6 +123,9 @@ layout { // If you don't like that, you should uncomment `prefer-no-csd` below. // Niri will draw focus ring and border *around* windows that agree to omit their // client-side decorations. + // + // Alternatively, you can override it with a window rule called + // `draw-border-with-background`. // You can change how the focus ring looks. focus-ring { @@ -393,6 +396,13 @@ animations { // if it is equal to min-height. Either set this equal to min-height, // or change the window height manually for this to apply. max-height 300 + + // Override whether the border and the focus ring draw with a background. + // Set this to `true` to draw them as solid colored rectangles even for + // windows which agreed to omit their client-side decorations. + // Set this to `false` to draw them as borders around the window even for + // windows which use client-side decorations. + draw-border-with-background false } // Here's a useful example. Work around WezTerm's initial configure bug diff --git a/src/handlers/compositor.rs b/src/handlers/compositor.rs index 188ba831..19489f2c 100644 --- a/src/handlers/compositor.rs +++ b/src/handlers/compositor.rs @@ -123,7 +123,7 @@ impl CompositorHandler for State { (rules, width, is_full_width, output) } else { error!("window map must happen after initial configure"); - (ResolvedWindowRules::default(), None, false, None) + (ResolvedWindowRules::empty(), None, false, None) }; let parent = window diff --git a/src/layout/mod.rs b/src/layout/mod.rs index c2c4bf4e..7162c3e3 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -49,6 +49,7 @@ use self::workspace::{compute_working_area, Column, ColumnWidth, OutputId, Works use crate::niri_render_elements; use crate::render_helpers::renderer::NiriRenderer; use crate::utils::output_size; +use crate::window::ResolvedWindowRules; pub mod focus_ring; pub mod monitor; @@ -121,6 +122,8 @@ pub trait LayoutElement { /// This *will* switch immediately after a [`LayoutElement::request_fullscreen()`] call. fn is_pending_fullscreen(&self) -> bool; + fn rules(&self) -> &ResolvedWindowRules; + /// Runs periodic clean-up tasks. fn refresh(&self); } @@ -1905,6 +1908,11 @@ mod tests { } fn refresh(&self) {} + + fn rules(&self) -> &ResolvedWindowRules { + static EMPTY: ResolvedWindowRules = ResolvedWindowRules::empty(); + &EMPTY + } } fn arbitrary_bbox() -> impl Strategy<Value = Rectangle<i32, Logical>> { diff --git a/src/layout/tile.rs b/src/layout/tile.rs index 589b2fc0..0609583e 100644 --- a/src/layout/tile.rs +++ b/src/layout/tile.rs @@ -85,11 +85,22 @@ impl<W: LayoutElement> Tile<W> { } pub fn advance_animations(&mut self, current_time: Duration, is_active: bool) { + let draw_border_with_background = self + .window + .rules() + .draw_border_with_background + .unwrap_or_else(|| !self.window.has_ssd()); self.border - .update(self.window.size(), self.window.has_ssd()); + .update(self.window.size(), !draw_border_with_background); self.border.set_active(is_active); - self.focus_ring.update(self.tile_size(), self.has_ssd()); + let draw_focus_ring_with_background = if self.effective_border_width().is_some() { + false + } else { + draw_border_with_background + }; + self.focus_ring + .update(self.tile_size(), !draw_focus_ring_with_background); self.focus_ring.set_active(is_active); match &mut self.open_animation { @@ -295,8 +306,15 @@ impl<W: LayoutElement> Tile<W> { size } - pub fn has_ssd(&self) -> bool { - self.effective_border_width().is_some() || self.window.has_ssd() + pub fn draw_border_with_background(&self) -> bool { + if self.effective_border_width().is_some() { + return false; + } + + self.window + .rules() + .draw_border_with_background + .unwrap_or_else(|| !self.window.has_ssd()) } fn render_inner<R: NiriRenderer>( diff --git a/src/window/mapped.rs b/src/window/mapped.rs index 6afd995e..b5caf8e2 100644 --- a/src/window/mapped.rs +++ b/src/window/mapped.rs @@ -184,4 +184,8 @@ impl LayoutElement for Mapped { fn refresh(&self) { self.window.refresh(); } + + fn rules(&self) -> &ResolvedWindowRules { + &self.rules + } } diff --git a/src/window/mod.rs b/src/window/mod.rs index 25b3531f..bb6f2151 100644 --- a/src/window/mod.rs +++ b/src/window/mod.rs @@ -13,7 +13,7 @@ pub mod unmapped; pub use unmapped::{InitialConfigureState, Unmapped}; /// Rules fully resolved for a window. -#[derive(Debug, Default, PartialEq)] +#[derive(Debug, PartialEq)] pub struct ResolvedWindowRules { /// Default width for this window. /// @@ -39,13 +39,32 @@ pub struct ResolvedWindowRules { pub max_width: Option<u16>, /// Extra bound on the maximum window height. pub max_height: Option<u16>, + + /// Whether or not to draw the border with a solid background. + /// + /// `None` means using the SSD heuristic. + pub draw_border_with_background: Option<bool>, } impl ResolvedWindowRules { + pub const fn empty() -> Self { + Self { + default_width: None, + open_on_output: None, + open_maximized: None, + open_fullscreen: None, + min_width: None, + min_height: None, + max_width: None, + max_height: None, + draw_border_with_background: None, + } + } + pub fn compute(rules: &[WindowRule], toplevel: &ToplevelSurface) -> Self { let _span = tracy_client::span!("ResolvedWindowRules::compute"); - let mut resolved = ResolvedWindowRules::default(); + let mut resolved = ResolvedWindowRules::empty(); with_states(toplevel.wl_surface(), |states| { let role = states @@ -100,6 +119,10 @@ impl ResolvedWindowRules { if let Some(x) = rule.max_height { resolved.max_height = Some(x); } + + if let Some(x) = rule.draw_border_with_background { + resolved.draw_border_with_background = Some(x); + } } resolved.open_on_output = open_on_output.map(|x| x.to_owned()); |
