kasuari-kotlin
0.1.6indexedCassowary constraint-solving implementation for UI layout, low-level solver API with weighted strengths, editable variables for interactive updates, and dual error styles (exceptions or Result-based).
Cassowary constraint-solving implementation for UI layout, low-level solver API with weighted strengths, editable variables for interactive updates, and dual error styles (exceptions or Result-based).
A Kotlin Multiplatform Native implementation of the Cassowary constraint solving algorithm (Badros et. al 2001). This is a port of the Rust kasuari library by the Ratatui team.
Kasuari is the Indonesian name for the Cassowary bird.
Cassowary is designed for solving constraints to lay out user interfaces. Constraints typically take the form "this button must line up with this text box", or "this box should try to be 3 times the size of this other box". Its most popular incarnation by far is in Apple's AutoLayout system for macOS and iOS user interfaces.
This library is a low-level interface to the solving algorithm. It does not have any intrinsic knowledge of common user interface conventions like rectangular regions or even two dimensions. These abstractions belong in a higher-level library.
Kasuari-Kotlin is published to Maven Central as
io.github.kotlinmania:kasuari-kotlin.
dependencies {
implementation("io.github.kotlinmania:kasuari-kotlin:0.1.6")
}
For a Kotlin Multiplatform consumer, add it to the relevant source set:
kotlin {
sourceSets {
val commonMain by getting {
dependencies {
implementation("io.github.kotlinmania:kasuari-kotlin:0.1.6")
}
}
}
}
import kasuari.*
solver = Solver.new()
left = Variable.new()
width = Variable.new()
right = Variable.new()
solver.addConstraint(
right with WeightedRelation.EQ(Strength.REQUIRED) to (left + width)
)
solver.addConstraint(
left with WeightedRelation.EQ(Strength.REQUIRED) to
)
solver.addConstraint(
width with WeightedRelation.EQ(Strength.STRONG) to
)
println()
println()
println()
For interactive applications, use edit variables to dynamically change values:
val solver = Solver.new()
val x = Variable.new()
// Add a constraint that x >= 0
solver.addConstraint(x with WeightedRelation.GE(Strength.REQUIRED) to 0.0)
// Register x as an edit variable
solver.addEditVariable(x, Strength.STRONG)
// Suggest values for x
solver.suggestValue(x, 50.0)
println(solver.getValue(x)) // 50.0
solver.suggestValue(x, -10.0)
println(solver.getValue(x)) // 0.0 (constrained to >= 0)
The library provides two styles of error handling:
try {
solver.addConstraint(constraint)
} catch (e: AddConstraintError.DuplicateConstraint) {
println("Constraint already exists")
} catch (e: AddConstraintError.UnsatisfiableConstraint) {
println("Constraint conflicts with existing constraints")
}
when (val result = solver.tryAddConstraint(constraint)) {
is Result.Ok -> println("Constraint added")
is Result.Err -> when (result.error) {
is AddConstraintError.DuplicateConstraint -> println("Already exists")
is AddConstraintError.UnsatisfiableConstraint -> println("Conflicts")
is AddConstraintError.InternalSolver -> println("Internal error")
}
}
Constraints have strengths that determine priority when conflicts arise:
Strength.REQUIRED - Must be satisfied (solver fails if impossible)Strength.STRONG - High priority, but can be violatedStrength.MEDIUM - Medium priorityStrength.WEAK - Low priority, used for defaults/preferences// Required: x must equal 100
val required = x with WeightedRelation.EQ(Strength.REQUIRED) to 100.0
// Strong: x should be at least 0
val strong = x with WeightedRelation.GE(Strength.STRONG) to 0.0
// Weak: x prefers to be 50
val weak = x with WeightedRelation.EQ(Strength.WEAK) to 50.0
Licensed under
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the MIT license, shall be licensed as above, without any additional terms or conditions.
This Kotlin Multiplatform port was created by Sydney Renee of The Solace Project for KotlinMania.
Special thanks to the original authors and contributors:
macosArm64, iosArm64, iosSimulatorArm64, iosX64,
tvosArm64, tvosSimulatorArm64, watchosArm32, watchosArm64,
watchosDeviceArm64, watchosSimulatorArm64linuxX64, linuxArm64mingwX64compileSdk 34, minSdk 24) plus
androidNativeArm32, androidNativeArm64, androidNativeX86,
androidNativeX64jvmToolchain(21))js (browser + Node.js), wasmJs (browser + Node.js), wasmWasi
(Node.js)Surfaced from shared tags and platforms — no rankings paid for.