feat(cli): new layout-ratios command

The implementation adds a new layout-ratio CLI command to komorebi that
allows users to dynamically set column and row ratios for layouts at
runtime via komorebic layout-ratio --columns 0.3 0.4 --rows 0.5.

A public validate_ratios function was added to komorebi-layouts that
clamps ratio values between 0.1 and 0.9, truncates when the cumulative
sum would reach or exceed 1.0, and limits to a maximum of 5 ratios. This
function is shared between config file deserialization and the new CLI
command, ensuring consistent validation behavior.

The SocketMessage enum in komorebi/src/core/mod.rs has a new
LayoutRatios variant. The handler in process_command.rs uses the shared
validate_ratios function to process the ratios before applying them to
the focused workspace's layout options.

When the CLI command is called without any arguments, it prints a
helpful message instead of returning an error.
This commit is contained in:
Csaba
2026-02-10 23:25:29 +01:00
committed by LGUG2Z
parent 9977cca500
commit c165172b5a
6 changed files with 118 additions and 24 deletions
+31 -24
View File
@@ -23,6 +23,36 @@ pub const DEFAULT_RATIO: f32 = 0.5;
/// Default secondary ratio value for UltrawideVerticalStack layout
pub const DEFAULT_SECONDARY_RATIO: f32 = 0.25;
/// Validates and converts a Vec of ratios into a fixed-size array.
/// - Clamps values to MIN_RATIO..MAX_RATIO range
/// - Truncates when cumulative sum reaches or exceeds 1.0
/// - Limits to MAX_RATIOS values
#[must_use]
pub fn validate_ratios(ratios: &[f32]) -> [Option<f32>; MAX_RATIOS] {
let mut arr = [None; MAX_RATIOS];
let mut cumulative_sum = 0.0_f32;
for (i, &val) in ratios.iter().take(MAX_RATIOS).enumerate() {
let clamped_val = val.clamp(MIN_RATIO, MAX_RATIO);
// Only add this ratio if cumulative sum stays below 1.0
if cumulative_sum + clamped_val < 1.0 {
arr[i] = Some(clamped_val);
cumulative_sum += clamped_val;
} else {
// Stop adding ratios - cumulative sum would reach or exceed 1.0
tracing::debug!(
"Truncating ratios at index {} - cumulative sum {} + {} would reach/exceed 1.0",
i,
cumulative_sum,
clamped_val
);
break;
}
}
arr
}
#[derive(
Clone, Copy, Debug, Serialize, Deserialize, Eq, PartialEq, Display, EnumString, ValueEnum,
)]
@@ -137,30 +167,7 @@ where
D: serde::Deserializer<'de>,
{
let opt: Option<Vec<f32>> = Option::deserialize(deserializer)?;
Ok(opt.map(|vec| {
let mut arr = [None; MAX_RATIOS];
let mut cumulative_sum = 0.0_f32;
for (i, &val) in vec.iter().take(MAX_RATIOS).enumerate() {
let clamped_val = val.clamp(MIN_RATIO, MAX_RATIO);
// Only add this ratio if cumulative sum stays below 1.0
if cumulative_sum + clamped_val < 1.0 {
arr[i] = Some(clamped_val);
cumulative_sum += clamped_val;
} else {
// Stop adding ratios - cumulative sum would reach or exceed 1.0
tracing::debug!(
"Truncating ratios at index {} - cumulative sum {} + {} would reach/exceed 1.0",
i,
cumulative_sum,
clamped_val
);
break;
}
}
arr
}))
Ok(opt.map(|vec| validate_ratios(&vec)))
}
/// Helper to serialize [Option<f32>; MAX_RATIOS] as a compact array (without trailing nulls)