Skip to content

Commit

Permalink
Implement window shadows
Browse files Browse the repository at this point in the history
  • Loading branch information
YaLTeR committed Jan 17, 2025
1 parent b4add62 commit bd559a2
Show file tree
Hide file tree
Showing 12 changed files with 947 additions and 9 deletions.
150 changes: 150 additions & 0 deletions niri-config/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ extern crate tracing;

use std::collections::HashSet;
use std::ffi::OsStr;
use std::ops::Mul;
use std::path::{Path, PathBuf};
use std::str::FromStr;
use std::time::Duration;
Expand Down Expand Up @@ -436,6 +437,8 @@ pub struct Layout {
#[knuffel(child, default)]
pub border: Border,
#[knuffel(child, default)]
pub shadow: Shadow,
#[knuffel(child, default)]
pub insert_hint: InsertHint,
#[knuffel(child, unwrap(children), default)]
pub preset_column_widths: Vec<PresetSize>,
Expand All @@ -460,6 +463,7 @@ impl Default for Layout {
Self {
focus_ring: Default::default(),
border: Default::default(),
shadow: Default::default(),
insert_hint: Default::default(),
preset_column_widths: Default::default(),
default_column_width: Default::default(),
Expand Down Expand Up @@ -608,6 +612,49 @@ impl From<FocusRing> for Border {
}
}

#[derive(knuffel::Decode, Debug, Clone, Copy, PartialEq)]
pub struct Shadow {
#[knuffel(child)]
pub on: bool,
#[knuffel(child, default = Self::default().offset)]
pub offset: ShadowOffset,
#[knuffel(child, unwrap(argument), default = Self::default().softness)]
pub softness: FloatOrInt<0, 1024>,
#[knuffel(child, unwrap(argument), default = Self::default().spread)]
pub spread: FloatOrInt<0, 1024>,
#[knuffel(child, unwrap(argument), default = Self::default().draw_behind_window)]
pub draw_behind_window: bool,
#[knuffel(child, default = Self::default().color)]
pub color: Color,
#[knuffel(child)]
pub inactive_color: Option<Color>,
}

impl Default for Shadow {
fn default() -> Self {
Self {
on: false,
offset: ShadowOffset {
x: FloatOrInt(0.),
y: FloatOrInt(5.),
},
softness: FloatOrInt(30.),
spread: FloatOrInt(5.),
draw_behind_window: false,
color: Color::from_rgba8_unpremul(0, 0, 0, 0x70),
inactive_color: None,
}
}
}

#[derive(knuffel::Decode, Debug, Clone, Copy, PartialEq)]
pub struct ShadowOffset {
#[knuffel(property, default)]
pub x: FloatOrInt<-65535, 65535>,
#[knuffel(property, default)]
pub y: FloatOrInt<-65535, 65535>,
}

#[derive(knuffel::Decode, Debug, Clone, Copy, PartialEq)]
pub struct InsertHint {
#[knuffel(child)]
Expand Down Expand Up @@ -679,6 +726,15 @@ impl Color {
}
}

impl Mul<f32> for Color {
type Output = Self;

fn mul(mut self, rhs: f32) -> Self::Output {
self.a *= rhs;
self
}
}

#[derive(knuffel::Decode, Debug, PartialEq)]
pub struct Cursor {
#[knuffel(child, unwrap(argument), default = String::from("default"))]
Expand Down Expand Up @@ -1007,6 +1063,8 @@ pub struct WindowRule {
pub focus_ring: BorderRule,
#[knuffel(child, default)]
pub border: BorderRule,
#[knuffel(child, default)]
pub shadow: ShadowRule,
#[knuffel(child, unwrap(argument))]
pub draw_border_with_background: Option<bool>,
#[knuffel(child, unwrap(argument))]
Expand Down Expand Up @@ -1084,6 +1142,26 @@ pub struct BorderRule {
pub inactive_gradient: Option<Gradient>,
}

#[derive(knuffel::Decode, Debug, Default, Clone, Copy, PartialEq)]
pub struct ShadowRule {
#[knuffel(child)]
pub off: bool,
#[knuffel(child)]
pub on: bool,
#[knuffel(child)]
pub offset: Option<ShadowOffset>,
#[knuffel(child, unwrap(argument))]
pub softness: Option<FloatOrInt<0, 1024>>,
#[knuffel(child, unwrap(argument))]
pub spread: Option<FloatOrInt<0, 1024>>,
#[knuffel(child, unwrap(argument))]
pub draw_behind_window: Option<bool>,
#[knuffel(child)]
pub color: Option<Color>,
#[knuffel(child)]
pub inactive_color: Option<Color>,
}

#[derive(knuffel::Decode, Debug, Clone, Copy, PartialEq)]
pub struct FloatingPosition {
#[knuffel(property)]
Expand Down Expand Up @@ -1803,6 +1881,67 @@ impl BorderRule {
}
}

impl ShadowRule {
pub fn merge_with(&mut self, other: &Self) {
if other.off {
self.off = true;
self.on = false;
}

if other.on {
self.off = false;
self.on = true;
}

if let Some(x) = other.offset {
self.offset = Some(x);
}
if let Some(x) = other.softness {
self.softness = Some(x);
}
if let Some(x) = other.spread {
self.spread = Some(x);
}
if let Some(x) = other.draw_behind_window {
self.draw_behind_window = Some(x);
}
if let Some(x) = other.color {
self.color = Some(x);
}
if let Some(x) = other.inactive_color {
self.inactive_color = Some(x);
}
}

pub fn resolve_against(&self, mut config: Shadow) -> Shadow {
config.on |= self.on;
if self.off {
config.on = false;
}

if let Some(x) = self.offset {
config.offset = x;
}
if let Some(x) = self.softness {
config.softness = x;
}
if let Some(x) = self.spread {
config.spread = x;
}
if let Some(x) = self.draw_behind_window {
config.draw_behind_window = x;
}
if let Some(x) = self.color {
config.color = x;
}
if let Some(x) = self.inactive_color {
config.inactive_color = Some(x);
}

config
}
}

impl CornerRadius {
pub fn fit_to(self, width: f32, height: f32) -> Self {
// Like in CSS: https://drafts.csswg.org/css-backgrounds/#corner-overlap
Expand Down Expand Up @@ -3221,6 +3360,10 @@ mod tests {
inactive-color "rgba(255, 200, 100, 0.0)"
}
shadow {
offset x=10 y=-20
}
preset-column-widths {
proportion 0.25
proportion 0.5
Expand Down Expand Up @@ -3460,6 +3603,13 @@ mod tests {
active_gradient: None,
inactive_gradient: None,
},
shadow: Shadow {
offset: ShadowOffset {
x: FloatOrInt(10.),
y: FloatOrInt(-20.),
},
..Default::default()
},
insert_hint: InsertHint {
off: false,
color: Color::from_rgba8_unpremul(255, 200, 127, 255),
Expand Down
37 changes: 37 additions & 0 deletions resources/default-config.kdl
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,43 @@ layout {
// inactive-gradient from="#505050" to="#808080" angle=45 relative-to="workspace-view"
}

// You can enable drop shadows for windows.
shadow {
// Uncomment the next line to enable shadows.
// on

// By default, the shadow draws only around its window, and not behind it.
// Uncomment this setting to make the shadow draw behind its window.
//
// Note that niri has no way of knowing about the CSD window corner
// radius. It has to assume that windows have square corners, leading to
// shadow artifacts inside the CSD rounded corners. This setting fixes
// those artifacts.
//
// However, instead you may want to set prefer-no-csd and/or
// geometry-corner-radius. Then, niri will know the corner radius and
// draw the shadow correctly, without having to draw it behind the
// window. These will also remove client-side shadows if the window
// draws any.
//
// draw-behind-window true

// You can change how shadows look. The values below are in logical
// pixels and match the CSS box-shadow properties.

// Softness controls the shadow blur radius.
softness 30

// Spread expands the shadow.
spread 5

// Offset moves the shadow relative to the window.
offset x=0 y=5

// You can also change the shadow color and opacity.
color "#0007"
}

// Struts shrink the area occupied by windows, similarly to layer-shell panels.
// You can think of them as a kind of outer gaps. They are set in logical pixels.
// Left and right struts will cause the next window to the side to always be visible.
Expand Down
19 changes: 19 additions & 0 deletions src/layout/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ pub mod insert_hint_element;
pub mod monitor;
pub mod opening_window;
pub mod scrolling;
pub mod shadow;
pub mod tile;
pub mod workspace;

Expand Down Expand Up @@ -304,6 +305,7 @@ pub struct Options {
pub struts: Struts,
pub focus_ring: niri_config::FocusRing,
pub border: niri_config::Border,
pub shadow: niri_config::Shadow,
pub insert_hint: niri_config::InsertHint,
pub center_focused_column: CenterFocusedColumn,
pub always_center_single_column: bool,
Expand All @@ -327,6 +329,7 @@ impl Default for Options {
struts: Default::default(),
focus_ring: Default::default(),
border: Default::default(),
shadow: Default::default(),
insert_hint: Default::default(),
center_focused_column: Default::default(),
always_center_single_column: false,
Expand Down Expand Up @@ -509,6 +512,7 @@ impl Options {
struts: layout.struts,
focus_ring: layout.focus_ring,
border: layout.border,
shadow: layout.shadow,
insert_hint: layout.insert_hint,
center_focused_column: layout.center_focused_column,
always_center_single_column: layout.always_center_single_column,
Expand Down Expand Up @@ -7072,12 +7076,26 @@ mod tests {
}
}

prop_compose! {
fn arbitrary_shadow()(
on in any::<bool>(),
width in arbitrary_spacing(),
) -> niri_config::Shadow {
niri_config::Shadow {
on,
softness: FloatOrInt(width),
..Default::default()
}
}
}

prop_compose! {
fn arbitrary_options()(
gaps in arbitrary_spacing(),
struts in arbitrary_struts(),
focus_ring in arbitrary_focus_ring(),
border in arbitrary_border(),
shadow in arbitrary_shadow(),
center_focused_column in arbitrary_center_focused_column(),
always_center_single_column in any::<bool>(),
empty_workspace_above_first in any::<bool>(),
Expand All @@ -7090,6 +7108,7 @@ mod tests {
empty_workspace_above_first,
focus_ring,
border,
shadow,
..Default::default()
}
}
Expand Down
Loading

0 comments on commit bd559a2

Please sign in to comment.