aboutsummaryrefslogtreecommitdiff
path: root/niri-config/src
diff options
context:
space:
mode:
authorIvan Molodetskikh <yalterz@gmail.com>2024-06-17 09:16:28 +0300
committerIvan Molodetskikh <yalterz@gmail.com>2024-06-18 14:01:28 +0300
commit1dae45c58d7eabeda21ef490d712915890bf6cff (patch)
tree62c473ab1662a1161ed522517ea57b7bd8db340c /niri-config/src
parent997119c44338ad96a40b4a1d6e958f77062a37ef (diff)
downloadniri-1dae45c58d7eabeda21ef490d712915890bf6cff.tar.gz
niri-1dae45c58d7eabeda21ef490d712915890bf6cff.tar.bz2
niri-1dae45c58d7eabeda21ef490d712915890bf6cff.zip
Refactor layout to fractional-logical
Lets borders, gaps, and everything else stay pixel-perfect even with fractional scale. Allows setting fractional border widths, gaps, struts. See the new wiki .md for more details.
Diffstat (limited to 'niri-config/src')
-rw-r--r--niri-config/src/lib.rs114
1 files changed, 92 insertions, 22 deletions
diff --git a/niri-config/src/lib.rs b/niri-config/src/lib.rs
index 4fbb96e1..a9285fad 100644
--- a/niri-config/src/lib.rs
+++ b/niri-config/src/lib.rs
@@ -331,6 +331,10 @@ pub struct Position {
pub y: i32,
}
+// MIN and MAX generics are only used during parsing to check the value.
+#[derive(Debug, Default, Clone, Copy, PartialEq)]
+pub struct FloatOrInt<const MIN: i32, const MAX: i32>(pub f64);
+
#[derive(knuffel::Decode, Debug, Clone, PartialEq)]
pub struct Layout {
#[knuffel(child, default)]
@@ -344,7 +348,7 @@ pub struct Layout {
#[knuffel(child, unwrap(argument), default)]
pub center_focused_column: CenterFocusedColumn,
#[knuffel(child, unwrap(argument), default = Self::default().gaps)]
- pub gaps: u16,
+ pub gaps: FloatOrInt<0, 65535>,
#[knuffel(child, default)]
pub struts: Struts,
}
@@ -357,7 +361,7 @@ impl Default for Layout {
preset_column_widths: Default::default(),
default_column_width: Default::default(),
center_focused_column: Default::default(),
- gaps: 16,
+ gaps: FloatOrInt(16.),
struts: Default::default(),
}
}
@@ -374,7 +378,7 @@ pub struct FocusRing {
#[knuffel(child)]
pub off: bool,
#[knuffel(child, unwrap(argument), default = Self::default().width)]
- pub width: u16,
+ pub width: FloatOrInt<0, 65535>,
#[knuffel(child, default = Self::default().active_color)]
pub active_color: Color,
#[knuffel(child, default = Self::default().inactive_color)]
@@ -389,7 +393,7 @@ impl Default for FocusRing {
fn default() -> Self {
Self {
off: false,
- width: 4,
+ width: FloatOrInt(4.),
active_color: Color::new(127, 200, 255, 255),
inactive_color: Color::new(80, 80, 80, 255),
active_gradient: None,
@@ -422,7 +426,7 @@ pub struct Border {
#[knuffel(child)]
pub off: bool,
#[knuffel(child, unwrap(argument), default = Self::default().width)]
- pub width: u16,
+ pub width: FloatOrInt<0, 65535>,
#[knuffel(child, default = Self::default().active_color)]
pub active_color: Color,
#[knuffel(child, default = Self::default().inactive_color)]
@@ -437,7 +441,7 @@ impl Default for Border {
fn default() -> Self {
Self {
off: true,
- width: 4,
+ width: FloatOrInt(4.),
active_color: Color::new(255, 200, 127, 255),
inactive_color: Color::new(80, 80, 80, 255),
active_gradient: None,
@@ -519,16 +523,16 @@ pub enum PresetWidth {
#[derive(Debug, Clone, PartialEq)]
pub struct DefaultColumnWidth(pub Option<PresetWidth>);
-#[derive(knuffel::Decode, Debug, Default, Clone, Copy, PartialEq, Eq)]
+#[derive(knuffel::Decode, Debug, Default, Clone, Copy, PartialEq)]
pub struct Struts {
#[knuffel(child, unwrap(argument), default)]
- pub left: u16,
+ pub left: FloatOrInt<0, 65535>,
#[knuffel(child, unwrap(argument), default)]
- pub right: u16,
+ pub right: FloatOrInt<0, 65535>,
#[knuffel(child, unwrap(argument), default)]
- pub top: u16,
+ pub top: FloatOrInt<0, 65535>,
#[knuffel(child, unwrap(argument), default)]
- pub bottom: u16,
+ pub bottom: FloatOrInt<0, 65535>,
}
#[derive(knuffel::Decode, Debug, Default, Clone, Copy, PartialEq, Eq)]
@@ -863,7 +867,7 @@ pub struct BorderRule {
#[knuffel(child)]
pub on: bool,
#[knuffel(child, unwrap(argument))]
- pub width: Option<u16>,
+ pub width: Option<FloatOrInt<0, 65535>>,
#[knuffel(child)]
pub active_color: Option<Color>,
#[knuffel(child)]
@@ -1144,6 +1148,72 @@ impl<S: knuffel::traits::ErrorSpan> knuffel::DecodeScalar<S> for WorkspaceRefere
}
}
+impl<S: knuffel::traits::ErrorSpan, const MIN: i32, const MAX: i32> knuffel::DecodeScalar<S>
+ for FloatOrInt<MIN, MAX>
+{
+ fn type_check(
+ type_name: &Option<knuffel::span::Spanned<knuffel::ast::TypeName, S>>,
+ ctx: &mut knuffel::decode::Context<S>,
+ ) {
+ if let Some(type_name) = &type_name {
+ ctx.emit_error(DecodeError::unexpected(
+ type_name,
+ "type name",
+ "no type name expected for this node",
+ ));
+ }
+ }
+
+ fn raw_decode(
+ val: &knuffel::span::Spanned<knuffel::ast::Literal, S>,
+ ctx: &mut knuffel::decode::Context<S>,
+ ) -> Result<Self, DecodeError<S>> {
+ match &**val {
+ knuffel::ast::Literal::Int(ref value) => match value.try_into() {
+ Ok(v) => {
+ if (MIN..=MAX).contains(&v) {
+ Ok(FloatOrInt(f64::from(v)))
+ } else {
+ ctx.emit_error(DecodeError::conversion(
+ val,
+ format!("value must be between {MIN} and {MAX}"),
+ ));
+ Ok(FloatOrInt::default())
+ }
+ }
+ Err(e) => {
+ ctx.emit_error(DecodeError::conversion(val, e));
+ Ok(FloatOrInt::default())
+ }
+ },
+ knuffel::ast::Literal::Decimal(ref value) => match value.try_into() {
+ Ok(v) => {
+ if (f64::from(MIN)..=f64::from(MAX)).contains(&v) {
+ Ok(FloatOrInt(v))
+ } else {
+ ctx.emit_error(DecodeError::conversion(
+ val,
+ format!("value must be between {MIN} and {MAX}"),
+ ));
+ Ok(FloatOrInt::default())
+ }
+ }
+ Err(e) => {
+ ctx.emit_error(DecodeError::conversion(val, e));
+ Ok(FloatOrInt::default())
+ }
+ },
+ _ => {
+ ctx.emit_error(DecodeError::unsupported(
+ val,
+ "Unsupported value, only numbers are recognized",
+ ));
+ Ok(FloatOrInt::default())
+ }
+ }
+ }
+}
+
#[derive(knuffel::Decode, Debug, Default, PartialEq)]
pub struct DebugConfig {
#[knuffel(child, unwrap(argument))]
@@ -2483,7 +2553,7 @@ mod tests {
border {
on
- width 8
+ width 8.5
}
}
@@ -2579,7 +2649,7 @@ mod tests {
layout: Layout {
focus_ring: FocusRing {
off: false,
- width: 5,
+ width: FloatOrInt(5.),
active_color: Color {
r: 0,
g: 100,
@@ -2602,7 +2672,7 @@ mod tests {
},
border: Border {
off: false,
- width: 3,
+ width: FloatOrInt(3.),
active_color: Color {
r: 255,
g: 200,
@@ -2627,12 +2697,12 @@ mod tests {
default_column_width: Some(DefaultColumnWidth(Some(PresetWidth::Proportion(
0.25,
)))),
- gaps: 8,
+ gaps: FloatOrInt(8.),
struts: Struts {
- left: 1,
- right: 2,
- top: 3,
- bottom: 0,
+ left: FloatOrInt(1.),
+ right: FloatOrInt(2.),
+ top: FloatOrInt(3.),
+ bottom: FloatOrInt(0.),
},
center_focused_column: CenterFocusedColumn::OnOverflow,
},
@@ -2716,12 +2786,12 @@ mod tests {
open_fullscreen: Some(false),
focus_ring: BorderRule {
off: true,
- width: Some(3),
+ width: Some(FloatOrInt(3.)),
..Default::default()
},
border: BorderRule {
on: true,
- width: Some(8),
+ width: Some(FloatOrInt(8.5)),
..Default::default()
},
..Default::default()