1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
//! CSS-like units for layout dimensions
//!
//! Provides semantic unit types that make layout code clearer and prevent
//! confusion between raw pixels and scaled units.
//!
//! # Examples
//!
//! ```rust,ignore
//! use blinc_layout::units::{px, sp, pct, Length};
//!
//! // Raw pixels
//! div().padding(px(16.0))
//!
//! // Spacing units (4px grid - for consistent spacing)
//! div().padding(sp(4.0)) // 4 * 4 = 16px
//!
//! // Percentage
//! div().w(pct(50.0)) // 50% of parent
//! ```
use taffy::{LengthPercentage, LengthPercentageAuto};
/// A length value with its unit
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Length {
/// Raw pixels (no scaling)
Px(f32),
/// Spacing units (multiplied by 4.0 for a 4px grid system)
Sp(f32),
/// Percentage of parent dimension
Pct(f32),
/// Auto sizing
Auto,
}
impl Length {
/// Convert to raw pixels
pub fn to_px(self) -> f32 {
match self {
Length::Px(v) => v,
Length::Sp(v) => v * 4.0,
Length::Pct(_) => 0.0, // Percentage needs context
Length::Auto => 0.0,
}
}
/// Check if this is a percentage
pub fn is_percentage(&self) -> bool {
matches!(self, Length::Pct(_))
}
/// Check if this is auto
pub fn is_auto(&self) -> bool {
matches!(self, Length::Auto)
}
}
impl Default for Length {
fn default() -> Self {
Length::Px(0.0)
}
}
// Conversion to Taffy types
impl From<Length> for LengthPercentage {
fn from(len: Length) -> Self {
match len {
Length::Px(v) => LengthPercentage::Length(v),
Length::Sp(v) => LengthPercentage::Length(v * 4.0),
Length::Pct(v) => LengthPercentage::Percent(v / 100.0),
Length::Auto => LengthPercentage::Length(0.0),
}
}
}
impl From<Length> for LengthPercentageAuto {
fn from(len: Length) -> Self {
match len {
Length::Px(v) => LengthPercentageAuto::Length(v),
Length::Sp(v) => LengthPercentageAuto::Length(v * 4.0),
Length::Pct(v) => LengthPercentageAuto::Percent(v / 100.0),
Length::Auto => LengthPercentageAuto::Auto,
}
}
}
// Convenience constructors
/// Create a pixel length value
#[inline]
pub const fn px(value: f32) -> Length {
Length::Px(value)
}
/// Create a spacing unit length value (4px grid)
///
/// Common usage:
/// - `sp(1)` = 4px
/// - `sp(2)` = 8px
/// - `sp(4)` = 16px
/// - `sp(8)` = 32px
#[inline]
pub const fn sp(units: f32) -> Length {
Length::Sp(units)
}
/// Create a percentage length value
#[inline]
pub const fn pct(value: f32) -> Length {
Length::Pct(value)
}
/// Tuple conversion for ergonomic unit specification
/// Allows: `(16.0, Px)` or `(4.0, Sp)` syntax
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Unit {
/// Raw pixels
Px,
/// Spacing units (4px grid)
Sp,
/// Percentage
Pct,
}
impl From<(f32, Unit)> for Length {
fn from((value, unit): (f32, Unit)) -> Self {
match unit {
Unit::Px => Length::Px(value),
Unit::Sp => Length::Sp(value),
Unit::Pct => Length::Pct(value),
}
}
}
// Allow raw f32 to be used as pixels for backwards compatibility
impl From<f32> for Length {
fn from(value: f32) -> Self {
Length::Px(value)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_px_conversion() {
assert_eq!(px(16.0).to_px(), 16.0);
}
#[test]
fn test_sp_conversion() {
assert_eq!(sp(4.0).to_px(), 16.0); // 4 * 4 = 16
assert_eq!(sp(1.0).to_px(), 4.0);
}
#[test]
fn test_tuple_syntax() {
let len: Length = (16.0, Unit::Px).into();
assert_eq!(len.to_px(), 16.0);
let len: Length = (4.0, Unit::Sp).into();
assert_eq!(len.to_px(), 16.0);
}
#[test]
fn test_taffy_conversion() {
let lp: LengthPercentage = px(16.0).into();
assert!(matches!(lp, LengthPercentage::Length(v) if (v - 16.0).abs() < 0.001));
let lp: LengthPercentage = sp(4.0).into();
assert!(matches!(lp, LengthPercentage::Length(v) if (v - 16.0).abs() < 0.001));
let lp: LengthPercentage = pct(50.0).into();
assert!(matches!(lp, LengthPercentage::Percent(v) if (v - 0.5).abs() < 0.001));
}
}