diff --git a/Cargo.lock b/Cargo.lock index 3ed8f262..7a2f80bc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -354,12 +354,6 @@ dependencies = [ "thiserror 2.0.12", ] -[[package]] -name = "cassowary" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" - [[package]] name = "cast" version = "0.3.0" @@ -903,6 +897,16 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +[[package]] +name = "env_logger" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3" +dependencies = [ + "log", + "regex", +] + [[package]] name = "equivalent" version = "1.0.2" @@ -1651,6 +1655,19 @@ dependencies = [ "simple_asn1", ] +[[package]] +name = "kasuari" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abf4d16f344910446711b7e223a5351073ab8c93dc5c84c5be2c0965346316b1" +dependencies = [ + "hashbrown", + "quickcheck", + "quickcheck_macros", + "rstest", + "thiserror 2.0.12", +] + [[package]] name = "lab" version = "0.11.0" @@ -2325,6 +2342,28 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "quickcheck" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "588f6378e4dd99458b60ec275b4477add41ce4fa9f64dcba6f15adccb19b50d6" +dependencies = [ + "env_logger", + "log", + "rand 0.8.5", +] + +[[package]] +name = "quickcheck_macros" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b22a693222d716a9587786f37ac3f6b4faedb5b80c23914e7303ff5a1d8016e9" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "quote" version = "1.0.39" @@ -2436,6 +2475,9 @@ name = "rand_core" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.15", +] [[package]] name = "rand_core" @@ -2548,12 +2590,12 @@ version = "0.1.0-alpha.3" dependencies = [ "anstyle", "bitflags 2.9.0", - "cassowary", "compact_str", "document-features", "hashbrown", "indoc", "itertools 0.13.0", + "kasuari", "lru", "palette", "pretty_assertions", diff --git a/ratatui-core/Cargo.toml b/ratatui-core/Cargo.toml index 9b92bfc8..78495c63 100644 --- a/ratatui-core/Cargo.toml +++ b/ratatui-core/Cargo.toml @@ -45,12 +45,12 @@ serde = ["dep:serde", "bitflags/serde", "compact_str/serde"] [dependencies] anstyle = { version = "1", optional = true } bitflags = "2.3" -cassowary = "0.3" compact_str = "0.8.0" document-features = { workspace = true, optional = true } hashbrown = "0.15.2" indoc.workspace = true itertools.workspace = true +kasuari = "0.4.0" lru = "0.12.0" palette = { version = "0.7.6", optional = true } serde = { workspace = true, optional = true } diff --git a/ratatui-core/src/layout/layout.rs b/ratatui-core/src/layout/layout.rs index 9e7588fc..f45758b8 100644 --- a/ratatui-core/src/layout/layout.rs +++ b/ratatui-core/src/layout/layout.rs @@ -3,11 +3,10 @@ use core::cell::RefCell; use core::iter; use core::num::NonZeroUsize; -use cassowary::strength::REQUIRED; -use cassowary::WeightedRelation::{EQ, GE, LE}; -use cassowary::{AddConstraintError, Expression, Solver, Variable}; use hashbrown::HashMap; use itertools::Itertools; +use kasuari::WeightedRelation::{EQ, GE, LE}; +use kasuari::{AddConstraintError, Expression, Solver, Strength, Variable}; use lru::LruCache; use self::strengths::{ @@ -769,8 +768,8 @@ fn configure_area( area_start: f64, area_end: f64, ) -> Result<(), AddConstraintError> { - solver.add_constraint(area.start | EQ(REQUIRED) | area_start)?; - solver.add_constraint(area.end | EQ(REQUIRED) | area_end)?; + solver.add_constraint(area.start | EQ(Strength::REQUIRED) | area_start)?; + solver.add_constraint(area.end | EQ(Strength::REQUIRED) | area_end)?; Ok(()) } @@ -781,8 +780,8 @@ fn configure_variable_in_area_constraints( ) -> Result<(), AddConstraintError> { // all variables are in the range [area.start, area.end] for &variable in variables { - solver.add_constraint(variable | GE(REQUIRED) | area.start)?; - solver.add_constraint(variable | LE(REQUIRED) | area.end)?; + solver.add_constraint(variable | GE(Strength::REQUIRED) | area.start)?; + solver.add_constraint(variable | LE(Strength::REQUIRED) | area.end)?; } Ok(()) @@ -802,7 +801,7 @@ fn configure_variable_constraints( // └v0 └v1 └v2 └v3 └v4 └v5 └v6 └v7 for (&left, &right) in variables.iter().skip(1).tuples() { - solver.add_constraint(left | LE(REQUIRED) | right)?; + solver.add_constraint(left | LE(Strength::REQUIRED) | right)?; } Ok(()) } @@ -1048,24 +1047,24 @@ impl Element { self.end - self.start } - fn has_max_size(&self, size: u16, strength: f64) -> cassowary::Constraint { + fn has_max_size(&self, size: u16, strength: Strength) -> kasuari::Constraint { self.size() | LE(strength) | (f64::from(size) * FLOAT_PRECISION_MULTIPLIER) } - fn has_min_size(&self, size: i16, strength: f64) -> cassowary::Constraint { + fn has_min_size(&self, size: i16, strength: Strength) -> kasuari::Constraint { self.size() | GE(strength) | (f64::from(size) * FLOAT_PRECISION_MULTIPLIER) } - fn has_int_size(&self, size: u16, strength: f64) -> cassowary::Constraint { + fn has_int_size(&self, size: u16, strength: Strength) -> kasuari::Constraint { self.size() | EQ(strength) | (f64::from(size) * FLOAT_PRECISION_MULTIPLIER) } - fn has_size>(&self, size: E, strength: f64) -> cassowary::Constraint { + fn has_size>(&self, size: E, strength: Strength) -> kasuari::Constraint { self.size() | EQ(strength) | size.into() } - fn is_empty(&self) -> cassowary::Constraint { - self.size() | EQ(REQUIRED - 1.0) | 0.0 + fn is_empty(&self) -> kasuari::Constraint { + self.size() | EQ(Strength::REQUIRED - Strength::WEAK) | 0.0 } } @@ -1084,91 +1083,91 @@ impl From<&Element> for Expression { } mod strengths { - use cassowary::strength::{MEDIUM, REQUIRED, STRONG, WEAK}; + use kasuari::Strength; /// The strength to apply to Spacers to ensure that their sizes are equal. /// /// ┌ ┐┌───┐┌ ┐┌───┐┌ ┐ /// ==x │ │ ==x │ │ ==x /// └ ┘└───┘└ ┘└───┘└ ┘ - pub const SPACER_SIZE_EQ: f64 = REQUIRED / 10.0; + pub const SPACER_SIZE_EQ: Strength = Strength::REQUIRED.div_f64(10.0); /// The strength to apply to Min inequality constraints. /// /// ┌────────┐ /// │Min(>=x)│ /// └────────┘ - pub const MIN_SIZE_GE: f64 = STRONG * 100.0; + pub const MIN_SIZE_GE: Strength = Strength::STRONG.mul_f64(100.0); /// The strength to apply to Max inequality constraints. /// /// ┌────────┐ /// │Max(<=x)│ /// └────────┘ - pub const MAX_SIZE_LE: f64 = STRONG * 100.0; + pub const MAX_SIZE_LE: Strength = Strength::STRONG.mul_f64(100.0); /// The strength to apply to Length constraints. /// /// ┌───────────┐ /// │Length(==x)│ /// └───────────┘ - pub const LENGTH_SIZE_EQ: f64 = STRONG * 10.0; + pub const LENGTH_SIZE_EQ: Strength = Strength::STRONG.mul_f64(10.0); /// The strength to apply to Percentage constraints. /// /// ┌───────────────┐ /// │Percentage(==x)│ /// └───────────────┘ - pub const PERCENTAGE_SIZE_EQ: f64 = STRONG; + pub const PERCENTAGE_SIZE_EQ: Strength = Strength::STRONG; /// The strength to apply to Ratio constraints. /// /// ┌────────────┐ /// │Ratio(==x,y)│ /// └────────────┘ - pub const RATIO_SIZE_EQ: f64 = STRONG / 10.0; + pub const RATIO_SIZE_EQ: Strength = Strength::STRONG.div_f64(10.0); /// The strength to apply to Min equality constraints. /// /// ┌────────┐ /// │Min(==x)│ /// └────────┘ - pub const MIN_SIZE_EQ: f64 = MEDIUM * 10.0; + pub const MIN_SIZE_EQ: Strength = Strength::MEDIUM.mul_f64(10.0); /// The strength to apply to Max equality constraints. /// /// ┌────────┐ /// │Max(==x)│ /// └────────┘ - pub const MAX_SIZE_EQ: f64 = MEDIUM * 10.0; + pub const MAX_SIZE_EQ: Strength = Strength::MEDIUM.mul_f64(10.0); /// The strength to apply to Fill growing constraints. /// /// ┌─────────────────────┐ /// │<= Fill(x) =>│ /// └─────────────────────┘ - pub const FILL_GROW: f64 = MEDIUM; + pub const FILL_GROW: Strength = Strength::MEDIUM; /// The strength to apply to growing constraints. /// /// ┌────────────┐ /// │<= Min(x) =>│ /// └────────────┘ - pub const GROW: f64 = MEDIUM / 10.0; + pub const GROW: Strength = Strength::MEDIUM.div_f64(10.0); /// The strength to apply to Spacer growing constraints. /// /// ┌ ┐ /// <= x => /// └ ┘ - pub const SPACE_GROW: f64 = WEAK * 10.0; + pub const SPACE_GROW: Strength = Strength::WEAK.mul_f64(10.0); /// The strength to apply to growing the size of all segments equally. /// /// ┌───────┐ /// │<= x =>│ /// └───────┘ - pub const ALL_SEGMENT_GROW: f64 = WEAK; + pub const ALL_SEGMENT_GROW: Strength = Strength::WEAK; } #[cfg(test)] @@ -2675,41 +2674,4 @@ mod tests { assert_eq!(result, expected); } } - - #[test] - fn test_solver() { - use super::*; - - let mut solver = Solver::new(); - let x = Variable::new(); - let y = Variable::new(); - - solver.add_constraint((x + y) | EQ(4.0) | 5.0).unwrap(); - solver.add_constraint(x | EQ(1.0) | 2.0).unwrap(); - for _ in 0..5 { - solver.add_constraint(y | EQ(1.0) | 2.0).unwrap(); - } - - let changes: HashMap = solver.fetch_changes().iter().copied().collect(); - let x = changes.get(&x).unwrap_or(&0.0).round() as u16; - let y = changes.get(&y).unwrap_or(&0.0).round() as u16; - assert_eq!(x, 3); - assert_eq!(y, 2); - - let mut solver = Solver::new(); - let x = Variable::new(); - let y = Variable::new(); - - solver.add_constraint((x + y) | EQ(4.0) | 5.0).unwrap(); - solver.add_constraint(y | EQ(1.0) | 2.0).unwrap(); - for _ in 0..5 { - solver.add_constraint(x | EQ(1.0) | 2.0).unwrap(); - } - - let changes: HashMap = solver.fetch_changes().iter().copied().collect(); - let x = changes.get(&x).unwrap_or(&0.0).round() as u16; - let y = changes.get(&y).unwrap_or(&0.0).round() as u16; - assert_eq!(x, 2); - assert_eq!(y, 3); - } }