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
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
//! Defines the [`Shape`] enum and its variants, used for describing the
//! geometric form of UI components.
//!
//! This module provides a flexible way to define very basic components' shape,
//! including [`crate::surface::surface`] and
//! [`crate::fluid_glass::fluid_glass`].
use tessera_ui::{PxSize, dp::Dp};
/// Capsule shapes use a constant `g2_k_value` to maintain circular ends.
pub const CAPSULE_G2_K_VALUE: f32 = 2.0;
/// Corner definition: capsule or manual radius with per-corner G2.
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum RoundedCorner {
/// Capsule radius derived from `min(width, height) / 2.0`, with
/// `CAPSULE_G2_K_VALUE`.
Capsule,
/// Manual radius (in `Dp`) with per-corner G2.
Manual {
/// Corner radius in device-independent pixels.
radius: Dp,
/// Corner G2 value (2.0 yields circular curvature).
g2_k_value: f32,
},
}
impl RoundedCorner {
/// A corner with zero radius.
pub const ZERO: Self = RoundedCorner::Manual {
radius: Dp(0.0),
g2_k_value: 3.0,
};
/// Helper to create a manual corner.
pub const fn manual(radius: Dp, g2_k_value: f32) -> Self {
Self::Manual { radius, g2_k_value }
}
/// Resolves into `(radius_px, g2)` using the provided size.
pub fn resolve(self, size: PxSize) -> (f32, f32) {
match self {
RoundedCorner::Capsule => (
size.width.to_f32().min(size.height.to_f32()) / 2.0,
CAPSULE_G2_K_VALUE,
),
RoundedCorner::Manual { radius, g2_k_value } => (radius.to_pixels_f32(), g2_k_value),
}
}
}
/// Resolved representation of a shape for rendering.
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum ResolvedShape {
/// Rounded rect with resolved radii and G2 values.
Rounded {
/// Pixel radii for each corner.
corner_radii: [f32; 4],
/// G2 parameters per corner.
corner_g2: [f32; 4],
},
/// Ellipse occupies the full bounds.
Ellipse,
}
/// Shape definitions for UI components.
///
/// `Shape` is used by multiple components (`surface`, `fluid_glass`, sliders,
/// progress, buttons) to define visual outline, hit-testing, and pipeline
/// behavior.
///
/// # Variants
/// * [`Shape::RoundedRectangle`] – Per-corner capsule or manual radius +
/// per-corner G2
/// * [`Shape::Ellipse`] – Ellipse filling the component bounds
///
/// # Example
///
/// ```
/// use tessera_components::shape_def::{RoundedCorner, Shape};
/// use tessera_ui::dp::Dp;
///
/// // Explicit rounded rectangle
/// let rr = Shape::RoundedRectangle {
/// top_left: RoundedCorner::manual(Dp(8.0), 3.0),
/// top_right: RoundedCorner::manual(Dp(8.0), 3.0),
/// bottom_right: RoundedCorner::manual(Dp(8.0), 3.0),
/// bottom_left: RoundedCorner::manual(Dp(8.0), 3.0),
/// };
///
/// // Ellipse
/// let ellipse = Shape::Ellipse;
///
/// // Mixed capsule/fixed corners (left side capsule, right side explicit)
/// let mixed = Shape::RoundedRectangle {
/// top_left: RoundedCorner::Capsule, // auto radius = min(width, height) / 2
/// top_right: RoundedCorner::manual(Dp(8.0), 3.0),
/// bottom_right: RoundedCorner::manual(Dp(8.0), 3.0),
/// bottom_left: RoundedCorner::Capsule, // also capsule
/// };
/// ```
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Shape {
/// Rounded rectangle with per-corner capsule or manual radius + G2.
RoundedRectangle {
/// Top-left corner definition.
top_left: RoundedCorner,
/// Top-right corner definition.
top_right: RoundedCorner,
/// Bottom-right corner definition.
bottom_right: RoundedCorner,
/// Bottom-left corner definition.
bottom_left: RoundedCorner,
},
/// Ellipse fitting the component bounds.
Ellipse,
}
impl Default for Shape {
/// Returns the default shape, which is a rectangle with zero corner radius.
///
/// # Example
///
/// ```
/// use tessera_components::shape_def::{RoundedCorner, Shape};
/// use tessera_ui::dp::Dp;
/// let default_shape = Shape::default();
/// assert_eq!(
/// default_shape,
/// Shape::RoundedRectangle {
/// top_left: RoundedCorner::manual(Dp(0.0), 3.0),
/// top_right: RoundedCorner::manual(Dp(0.0), 3.0),
/// bottom_right: RoundedCorner::manual(Dp(0.0), 3.0),
/// bottom_left: RoundedCorner::manual(Dp(0.0), 3.0),
/// }
/// );
/// ```
fn default() -> Self {
Shape::RoundedRectangle {
top_left: RoundedCorner::manual(Dp(0.0), 3.0),
top_right: RoundedCorner::manual(Dp(0.0), 3.0),
bottom_right: RoundedCorner::manual(Dp(0.0), 3.0),
bottom_left: RoundedCorner::manual(Dp(0.0), 3.0),
}
}
}
impl Shape {
/// A pure rectangle shape with no rounded corners.
pub const RECTANGLE: Self = Shape::RoundedRectangle {
top_left: RoundedCorner::manual(Dp(0.0), 3.0),
top_right: RoundedCorner::manual(Dp(0.0), 3.0),
bottom_right: RoundedCorner::manual(Dp(0.0), 3.0),
bottom_left: RoundedCorner::manual(Dp(0.0), 3.0),
};
/// A helper to create a uniform rounded rectangle shape with manual
/// corners.
pub const fn rounded_rectangle(radius: Dp) -> Self {
Shape::RoundedRectangle {
top_left: RoundedCorner::manual(radius, 3.0),
top_right: RoundedCorner::manual(radius, 3.0),
bottom_right: RoundedCorner::manual(radius, 3.0),
bottom_left: RoundedCorner::manual(radius, 3.0),
}
}
/// A helper to create a uniform capsule on all corners.
pub const fn capsule() -> Self {
Shape::RoundedRectangle {
top_left: RoundedCorner::Capsule,
top_right: RoundedCorner::Capsule,
bottom_right: RoundedCorner::Capsule,
bottom_left: RoundedCorner::Capsule,
}
}
/// Resolves a shape into pixel radii and per-corner G2 parameters for a
/// given size.
pub fn resolve_for_size(self, size: PxSize) -> ResolvedShape {
match self {
Shape::RoundedRectangle {
top_left,
top_right,
bottom_right,
bottom_left,
} => {
let (tl_r, tl_g2) = top_left.resolve(size);
let (tr_r, tr_g2) = top_right.resolve(size);
let (br_r, br_g2) = bottom_right.resolve(size);
let (bl_r, bl_g2) = bottom_left.resolve(size);
ResolvedShape::Rounded {
corner_radii: [tl_r, tr_r, br_r, bl_r],
corner_g2: [tl_g2, tr_g2, br_g2, bl_g2],
}
}
Shape::Ellipse => ResolvedShape::Ellipse,
}
}
}