fix: preserve default outer gaps

This commit is contained in:
Samuel Onoja committed 2026-09-25 02:43:53 +01:00
1 parent ed529ae900
commit 94a83c83bb
7 files changed
+119 -14

No files matched your search

+73 -3
View File
@@ -7,7 +7,7 @@ use std::path::PathBuf;
use ferese_animation::SpringConfig;
use ferese_core::LayoutMode;
use ferese_layout::{ColumnWidth, ViewportFocusStrategy};
use ferese_layout::{ColumnWidth, GapConfig, ViewportFocusStrategy};
use serde::Deserialize;
#[derive(Debug, Default, Deserialize)]
@@ -86,9 +86,26 @@ impl Default for SpringSettings {
}
}
#[derive(Debug, Default, Deserialize)]
#[derive(Debug, Deserialize)]
struct LayoutConfig {
mode: Option<LayoutModeValue>,
#[serde(default = "default_inner_gap")]
inner_gap: f64,
#[serde(default = "default_outer_gap")]
outer_gap: f64,
#[serde(default)]
smart_gaps: bool,
}
impl Default for LayoutConfig {
fn default() -> Self {
Self {
mode: None,
inner_gap: default_inner_gap(),
outer_gap: default_outer_gap(),
smart_gaps: false,
}
}
}
#[derive(Clone, Copy, Debug, Deserialize)]
@@ -138,6 +155,10 @@ pub enum ConfigError {
field: &'static str,
value: f64,
},
InvalidLayoutValue {
field: &'static str,
value: f64,
},
}
impl fmt::Display for ConfigError {
@@ -156,6 +177,9 @@ impl fmt::Display for ConfigError {
Self::InvalidAnimationValue { field, value } => {
write!(formatter, "invalid animations.{field} value {value}")
}
Self::InvalidLayoutValue { field, value } => {
write!(formatter, "invalid layout.{field} value {value}")
}
}
}
}
@@ -165,7 +189,9 @@ impl Error for ConfigError {
match self {
Self::Read { source, .. } => Some(source),
Self::Parse { source, .. } => Some(source),
Self::InvalidColumnWidth { .. } | Self::InvalidAnimationValue { .. } => None,
Self::InvalidColumnWidth { .. }
| Self::InvalidAnimationValue { .. }
| Self::InvalidLayoutValue { .. } => None,
}
}
}
@@ -191,6 +217,17 @@ impl Config {
}
}
pub fn gap_config(&self) -> Result<GapConfig, ConfigError> {
let inner = nonnegative_layout_value(self.layout.inner_gap, "inner_gap")?;
let outer = nonnegative_layout_value(self.layout.outer_gap, "outer_gap")?;
Ok(GapConfig {
inner,
outer,
smart: self.layout.smart_gaps,
})
}
pub fn default_column_width(&self) -> Result<ColumnWidth, ConfigError> {
parse_column_width(
self.scrolling
@@ -286,10 +323,26 @@ fn positive_animation_value(value: f64, field: &'static str) -> Result<f64, Conf
}
}
fn nonnegative_layout_value(value: f64, field: &'static str) -> Result<f64, ConfigError> {
if value.is_finite() && value >= 0.0 {
Ok(value)
} else {
Err(ConfigError::InvalidLayoutValue { field, value })
}
}
const fn enabled_by_default() -> bool {
true
}
const fn default_inner_gap() -> f64 {
10.0
}
const fn default_outer_gap() -> f64 {
10.0
}
const fn default_animation_speed() -> f64 {
1.0
}
@@ -367,6 +420,7 @@ mod tests {
config.default_column_width().unwrap(),
ColumnWidth::Proportion(0.5)
);
assert_eq!(config.gap_config().unwrap(), GapConfig::default());
}
#[test]
@@ -449,6 +503,22 @@ mod tests {
assert!(damping.spring_config().is_err());
}
#[test]
fn parses_and_validates_layout_gaps() {
let configured = parse("[layout]\ninner_gap = 6.0\nouter_gap = 14.0\nsmart_gaps = true");
let invalid = parse("[layout]\nouter_gap = -1.0");
assert_eq!(
configured.gap_config().unwrap(),
GapConfig {
inner: 6.0,
outer: 14.0,
smart: true,
}
);
assert!(invalid.gap_config().is_err());
}
#[test]
fn derives_critical_viewport_damping() {
let config = parse("");
+1
View File
@@ -28,6 +28,7 @@ fn main() -> Result<(), Box<dyn Error>> {
let display = Display::new()?;
let runtime = RuntimeConfig {
layout_mode: config.layout_mode(),
gap_config: config.gap_config()?,
default_column_width: config.default_column_width()?,
scrolling_focus_strategy: config.scrolling_focus_strategy(),
column_width_presets: config.width_presets()?,
+7 -4
View File
@@ -90,6 +90,7 @@ pub struct Ferese {
scrolling_world_x: HashMap<WindowId, (WorkspaceId, AnimatedValue)>,
pub focused_window: Option<WindowId>,
column_width_presets: Vec<ColumnWidth>,
gap_config: GapConfig,
animations_enabled: bool,
animation_speed: f64,
spring_config: SpringConfig,
@@ -137,6 +138,7 @@ pub struct Ferese {
pub struct RuntimeConfig {
pub layout_mode: LayoutMode,
pub gap_config: GapConfig,
pub default_column_width: ColumnWidth,
pub scrolling_focus_strategy: ViewportFocusStrategy,
pub column_width_presets: Vec<ColumnWidth>,
@@ -363,6 +365,7 @@ impl Ferese {
scrolling_world_x: HashMap::new(),
focused_window: None,
column_width_presets: config.column_width_presets,
gap_config: config.gap_config,
animations_enabled: config.animations_enabled,
animation_speed: config.animation_speed,
spring_config: config.spring_config,
@@ -699,7 +702,7 @@ impl Ferese {
};
let layout = match workspace.layout.geometry_with_constraints(
bounds,
GapConfig::default(),
self.gap_config,
&constraints,
focused,
) {
@@ -1162,7 +1165,7 @@ impl Ferese {
match self
.workspaces
.center_window(window, bounds, GapConfig::default(), &constraints)
.center_window(window, bounds, self.gap_config, &constraints)
{
Ok(true) => self.relayout(),
Ok(false) => {}
@@ -1401,7 +1404,7 @@ impl Ferese {
.workspaces
.workspace_mut(workspace)?
.layout
.geometry_with_constraints(bounds, GapConfig::default(), &constraints, focused)
.geometry_with_constraints(bounds, self.gap_config, &constraints, focused)
.ok()?
.geometry
.get(&window)
@@ -1416,7 +1419,7 @@ impl Ferese {
self.workspaces
.active_mut()
.layout
.geometry_with_constraints(bounds, GapConfig::default(), &constraints, focused)
.geometry_with_constraints(bounds, self.gap_config, &constraints, focused)
}
fn window_constraints(&self) -> HashMap<WindowId, SizeConstraints> {