Format Rust sources at 100 columns

This commit is contained in:
Samuel Onoja committed 2026-10-22 01:28:03 +01:00
1 parent 4661f595f8
commit 610478f8c7
336 files changed
+23497 -7892

No files matched your search

+2 -1
View File
@@ -88,7 +88,8 @@ impl Adaptive {
}
pub fn deadline(&self) -> Option<Duration> {
self.pending.map(|(_, since)| since.saturating_add(HYSTERESIS))
self.pending
.map(|(_, since)| since.saturating_add(HYSTERESIS))
}
}
+38 -16
View File
@@ -180,7 +180,9 @@ impl Hub {
pins.iter()
.any(|value| !value.is_finite() || !(0. ..=f64::from(f32::MAX)).contains(value))
}) {
return Err("hub radius pins must be finite, nonnegative pixel values representable as f32");
return Err(
"hub radius pins must be finite, nonnegative pixel values representable as f32",
);
}
self.pins = pins;
let mut target = self.motion.target();
@@ -221,7 +223,10 @@ impl Hub {
}
pub fn retarget(&mut self, mode: Mode, available: [f64; 2]) -> Result<(), &'static str> {
if available.iter().any(|value| !value.is_finite() || *value <= 0.) {
if available
.iter()
.any(|value| !value.is_finite() || *value <= 0.)
{
return Err("hub requires a finite positive available area");
}
let [width, height] = self.profile.size(mode, self.height);
@@ -302,12 +307,14 @@ impl Hub {
pub fn sample(&self) -> Sample {
let fade = |elapsed: Duration| (elapsed.as_secs_f32() / FADE.as_secs_f32()).clamp(0., 1.);
let opacity =
self.initial_opacity + (1. - self.initial_opacity) * fade(self.elapsed.saturating_sub(CONTENT_DELAY));
let opacity = self.initial_opacity
+ (1. - self.initial_opacity) * fade(self.elapsed.saturating_sub(CONTENT_DELAY));
let mut geometry = self.motion.current();
geometry.bounds.width = geometry.bounds.width.clamp(0., self.available[0]);
geometry.bounds.height = geometry.bounds.height.clamp(0., self.available[1]);
geometry.radii = geometry.radii.map(|radius| radius.clamp(0., f64::from(f32::MAX)));
geometry.radii = geometry
.radii
.map(|radius| radius.clamp(0., f64::from(f32::MAX)));
Sample {
geometry,
previous: self.previous,
@@ -352,7 +359,8 @@ mod tests {
#[test]
fn measured_height_preserves_fade_and_motion_and_caps_long_content() {
let mut hub = Hub::new(Profile::default(), 34.).unwrap();
hub.retarget(Mode::Expanded(Content::Controls), [800., 600.]).unwrap();
hub.retarget(Mode::Expanded(Content::Controls), [800., 600.])
.unwrap();
hub.advance(Duration::from_millis(180), false);
let before = hub.sample();
hub.fit_content_height(148.).unwrap();
@@ -363,7 +371,10 @@ mod tests {
assert_eq!(hub.sample().geometry.bounds.height, 148.);
assert_eq!(hub.sample().opacity, 1.);
hub.fit_content_height(900.).unwrap();
assert_eq!(hub.target_size(), [340., Profile::default().controls_height]);
assert_eq!(
hub.target_size(),
[340., Profile::default().controls_height]
);
hub.retarget(Mode::Idle, [800., 600.]).unwrap();
hub.fit_content_height(100.).unwrap();
assert_eq!(hub.target_size(), [150., 34.]);
@@ -372,7 +383,8 @@ mod tests {
#[test]
fn a_content_swap_fades_without_restarting_the_geometry() {
let mut hub = Hub::new(Profile::default(), 34.).unwrap();
hub.retarget(Mode::Expanded(Content::Controls), [800., 600.]).unwrap();
hub.retarget(Mode::Expanded(Content::Controls), [800., 600.])
.unwrap();
hub.advance(Duration::from_secs(2), false);
hub.advance(Duration::from_secs(1), false);
let geometry = hub.sample().geometry;
@@ -394,7 +406,8 @@ mod tests {
fn content_swap_uses_a_fixed_delay_instead_of_waiting_for_spring_settlement() {
let mut hub = Hub::new(Profile::default(), 34.).unwrap();
assert!(!hub.active());
hub.retarget(Mode::Expanded(Content::Calendar), [800., 600.]).unwrap();
hub.retarget(Mode::Expanded(Content::Calendar), [800., 600.])
.unwrap();
assert_eq!(hub.target_size(), [340., 230.]);
hub.advance(Duration::from_millis(120), false);
assert_eq!(hub.sample().opacity, 0.);
@@ -403,7 +416,10 @@ mod tests {
assert!((hub.sample().opacity - 0.5).abs() < 0.001);
hub.advance(Duration::from_millis(60), false);
assert_eq!(hub.sample().opacity, 1.);
assert!(hub.active(), "content finishes while the shape is still settling");
assert!(
hub.active(),
"content finishes while the shape is still settling"
);
for _ in 0..50 {
hub.advance(Duration::from_millis(16), false);
}
@@ -415,9 +431,11 @@ mod tests {
#[test]
fn an_interrupted_content_delay_never_reveals_the_cancelled_panel() {
let mut hub = Hub::new(Profile::default(), 34.).unwrap();
hub.retarget(Mode::Expanded(Content::Media), [800., 600.]).unwrap();
hub.retarget(Mode::Expanded(Content::Media), [800., 600.])
.unwrap();
hub.advance(Duration::from_millis(80), false);
hub.retarget(Mode::Expanded(Content::Calendar), [800., 600.]).unwrap();
hub.retarget(Mode::Expanded(Content::Calendar), [800., 600.])
.unwrap();
for _ in 0..130 {
hub.advance(Duration::from_millis(8), false);
let sample = hub.sample();
@@ -431,7 +449,8 @@ mod tests {
#[test]
fn spring_overshoots_then_settles_within_its_reserved_surface() {
let mut hub = Hub::new(Profile::default(), 34.).unwrap();
hub.retarget(Mode::Expanded(Content::Calendar), [800., 600.]).unwrap();
hub.retarget(Mode::Expanded(Content::Calendar), [800., 600.])
.unwrap();
let allocation = hub.allocation_size();
let mut peak = 340_f64;
for _ in 0..200 {
@@ -450,7 +469,8 @@ mod tests {
#[test]
fn overshoot_is_clipped_to_small_outputs_and_reduced_motion_removes_all_fades() {
let mut hub = Hub::new(Profile::default(), 34.).unwrap();
hub.retarget(Mode::Expanded(Content::Calendar), [240., 180.]).unwrap();
hub.retarget(Mode::Expanded(Content::Calendar), [240., 180.])
.unwrap();
for _ in 0..60 {
hub.advance(Duration::from_millis(8), false);
let bounds = hub.sample().geometry.bounds;
@@ -467,7 +487,8 @@ mod tests {
#[test]
fn interrupted_expansion_keeps_the_presented_shape() {
let mut hub = Hub::new(Profile::default(), 34.).unwrap();
hub.retarget(Mode::Expanded(Content::Media), [800., 600.]).unwrap();
hub.retarget(Mode::Expanded(Content::Media), [800., 600.])
.unwrap();
hub.advance(Duration::from_millis(40), false);
let before = hub.sample().geometry;
hub.retarget(Mode::Idle, [800., 600.]).unwrap();
@@ -482,7 +503,8 @@ mod tests {
#[test]
fn narrow_outputs_and_reduced_motion_keep_the_hub_bounded() {
let mut hub = Hub::new(Profile::default(), 30.).unwrap();
hub.retarget(Mode::Expanded(Content::Calendar), [240., 180.]).unwrap();
hub.retarget(Mode::Expanded(Content::Calendar), [240., 180.])
.unwrap();
hub.advance(Duration::ZERO, true);
assert_eq!(hub.sample().geometry.bounds, Rect::new(0., 0., 240., 180.));
assert_eq!(hub.sample().opacity, 1.);
+23 -7
View File
@@ -31,7 +31,12 @@ impl Geometry {
fn values(self) -> [f64; COUNT] {
let mut values = [0.; COUNT];
values[..4].copy_from_slice(&[self.bounds.x, self.bounds.y, self.bounds.width, self.bounds.height]);
values[..4].copy_from_slice(&[
self.bounds.x,
self.bounds.y,
self.bounds.width,
self.bounds.height,
]);
values[4..8].copy_from_slice(&self.radii);
values[8..12].copy_from_slice(&self.margin);
values[12..16].copy_from_slice(&self.thickness);
@@ -53,7 +58,9 @@ impl Geometry {
fn validate(self) -> Result<Self, &'static str> {
let values = self.values();
if values.iter().any(|value| !value.is_finite()) || values[2..].iter().any(|value| *value < 0.) {
if values.iter().any(|value| !value.is_finite())
|| values[2..].iter().any(|value| *value < 0.)
{
Err("panel animation geometry must be finite with nonnegative extents")
} else {
Ok(self)
@@ -82,7 +89,10 @@ impl Motion {
)
}
pub(crate) fn with_spring(geometry: Geometry, spring: SpringConfig) -> Result<Self, &'static str> {
pub(crate) fn with_spring(
geometry: Geometry,
spring: SpringConfig,
) -> Result<Self, &'static str> {
Ok(Self {
values: geometry.validate()?.values().map(AnimatedValue::new),
spring,
@@ -106,14 +116,16 @@ impl Motion {
// Extrema alternate; later peaks decay, so the first two suffice.
for phase in [first, first + std::f64::consts::PI] {
let t = phase / frequency;
let position =
value.target + (-decay * t).exp() * (displacement * phase.cos() + sine * phase.sin());
let position = value.target
+ (-decay * t).exp() * (displacement * phase.cos() + sine * phase.sin());
maximum = maximum.max(position);
}
} else {
// Total spring energy bounds displacement for any nonnegative damping.
maximum = maximum.max(
value.target + (displacement * displacement + value.velocity * value.velocity / omega2).sqrt(),
value.target
+ (displacement * displacement + value.velocity * value.velocity / omega2)
.sqrt(),
);
}
maximum
@@ -229,7 +241,11 @@ mod tests {
let before = motion.current();
let velocity = motion.values.map(|value| value.velocity);
motion
.retarget(if turn % 2 == 0 { expanded() } else { collapsed() })
.retarget(if turn % 2 == 0 {
expanded()
} else {
collapsed()
})
.unwrap();
assert_eq!(motion.current(), before);
assert_eq!(motion.values.map(|value| value.velocity), velocity);
+70 -16
View File
@@ -4,10 +4,14 @@ use std::f64::consts::{FRAC_PI_2, PI};
pub type Contour = Vec<[f64; 2]>;
fn arc(points: &mut Contour, center: [f64; 2], radius: f64, start: f64, tolerance: f64) {
let segments = ((FRAC_PI_2 / (8. * tolerance / radius.max(tolerance)).sqrt()).ceil() as usize).clamp(1, 128);
let segments = ((FRAC_PI_2 / (8. * tolerance / radius.max(tolerance)).sqrt()).ceil() as usize)
.clamp(1, 128);
for i in 0..=segments {
let angle = start + FRAC_PI_2 * i as f64 / segments as f64;
points.push([center[0] + radius * angle.cos(), center[1] + radius * angle.sin()]);
points.push([
center[0] + radius * angle.cos(),
center[1] + radius * angle.sin(),
]);
}
}
@@ -66,20 +70,32 @@ pub fn contours(scene: &Scene, tolerance: f64) -> Result<Vec<Contour>, &'static
.collect();
if scene.fusion > 0. {
for zone in 1..=3 {
let shapes: Vec<_> = scene.paint.iter().filter(|p| p.blend_zone == zone).copied().collect();
let shapes: Vec<_> = scene
.paint
.iter()
.filter(|p| p.blend_zone == zone)
.copied()
.collect();
if shapes.is_empty() {
continue;
}
// Fused rows share their vertical baseline. Each component is
// vertically convex; solve its upper and lower boundary, rather
// than sampling every pixel of a full-output bitmap.
let top = shapes.iter().map(|p| p.bounds.y).fold(f64::INFINITY, f64::min);
let top = shapes
.iter()
.map(|p| p.bounds.y)
.fold(f64::INFINITY, f64::min);
let bottom = shapes
.iter()
.map(|p| p.bounds.y + p.bounds.height)
.fold(f64::NEG_INFINITY, f64::max);
let mid = (top + bottom) * 0.5;
let left = shapes.iter().map(|p| p.bounds.x).fold(f64::INFINITY, f64::min) - scene.fusion;
let left = shapes
.iter()
.map(|p| p.bounds.x)
.fold(f64::INFINITY, f64::min)
- scene.fusion;
let right = shapes
.iter()
.map(|p| p.bounds.x + p.bounds.width)
@@ -135,7 +151,11 @@ pub fn contours(scene: &Scene, tolerance: f64) -> Result<Vec<Contour>, &'static
/// Open border paths for screen-attached notches. Split shared straight edges
/// before cancelling them, so the body and its fillets have one continuous rim.
pub fn notch_border(scene: &Scene, tolerance: f64, edge: crate::Edge) -> Result<Vec<Contour>, &'static str> {
pub fn notch_border(
scene: &Scene,
tolerance: f64,
edge: crate::Edge,
) -> Result<Vec<Contour>, &'static str> {
let contours = contours(scene, tolerance)?;
let vertices: Vec<_> = contours.iter().flatten().copied().collect();
let screen_edge = vertices.iter().map(|p| p[1]).fold(
@@ -144,13 +164,23 @@ pub fn notch_border(scene: &Scene, tolerance: f64, edge: crate::Edge) -> Result<
} else {
f64::NEG_INFINITY
},
if edge == crate::Edge::Top { f64::min } else { f64::max },
if edge == crate::Edge::Top {
f64::min
} else {
f64::max
},
);
let close = |a: [f64; 2], b: [f64; 2]| (a[0] - b[0]).abs() < 1e-6 && (a[1] - b[1]).abs() < 1e-6;
let mut segments: Vec<([f64; 2], [f64; 2])> = Vec::new();
for contour in &contours {
for (&a, &b) in contour.iter().zip(contour.iter().cycle().skip(1)).take(contour.len()) {
if close(a, b) || ((a[1] - screen_edge).abs() < 1e-6 && (b[1] - screen_edge).abs() < 1e-6) {
for (&a, &b) in contour
.iter()
.zip(contour.iter().cycle().skip(1))
.take(contour.len())
{
if close(a, b)
|| ((a[1] - screen_edge).abs() < 1e-6 && (b[1] - screen_edge).abs() < 1e-6)
{
continue;
}
let delta = [b[0] - a[0], b[1] - a[1]];
@@ -181,7 +211,10 @@ pub fn notch_border(scene: &Scene, tolerance: f64, edge: crate::Edge) -> Result<
let mut paths = Vec::new();
while let Some((a, b)) = segments.pop() {
let mut path = vec![a, b];
while let Some(index) = segments.iter().position(|&(a, _)| close(a, *path.last().unwrap())) {
while let Some(index) = segments
.iter()
.position(|&(a, _)| close(a, *path.last().unwrap()))
{
path.push(segments.swap_remove(index).1);
}
while let Some(index) = segments.iter().position(|&(_, b)| close(b, path[0])) {
@@ -202,7 +235,11 @@ mod tests {
fn winding(contours: &[Contour], point: [f64; 2]) -> i32 {
let mut winding = 0;
for points in contours {
for (a, b) in points.iter().zip(points.iter().cycle().skip(1)).take(points.len()) {
for (a, b) in points
.iter()
.zip(points.iter().cycle().skip(1))
.take(points.len())
{
let cross = (b[0] - a[0]) * (point[1] - a[1]) - (point[0] - a[0]) * (b[1] - a[1]);
if a[1] <= point[1] && b[1] > point[1] && cross > 0. {
winding += 1;
@@ -241,14 +278,22 @@ mod tests {
let screen_y = if edge == crate::Edge::Top { 0. } else { 44. };
assert!((path.first().unwrap()[1] - screen_y).abs() < 1e-6);
assert!((path.last().unwrap()[1] - screen_y).abs() < 1e-6);
let body = scene.paint.iter().find(|p| p.operation == 0).unwrap().bounds;
let body = scene
.paint
.iter()
.find(|p| p.operation == 0)
.unwrap()
.bounds;
assert!(path.iter().any(|p| p[0] < body.x));
assert!(path.iter().any(|p| p[0] > body.x + body.width));
for segment in path.windows(2) {
let [a, b] = [segment[0], segment[1]];
assert!((a[1] - screen_y).abs() > 1e-6 || (b[1] - screen_y).abs() > 1e-6);
let mid = [(a[0] + b[0]) / 2., (a[1] + b[1]) / 2.];
assert!(scene.distance(mid).abs() < 0.12 / scale, "off outline: {mid:?}");
assert!(
scene.distance(mid).abs() < 0.12 / scale,
"off outline: {mid:?}"
);
// A shared body/fillet seam has fill on both sides, unlike a visible rim.
let delta = [b[0] - a[0], b[1] - a[1]];
let length = delta[0].hypot(delta[1]);
@@ -266,7 +311,9 @@ mod tests {
fn attached_morph_rim_follows_animated_body_at_fractional_scales() {
for scale in [1., 1.25, 1.5, 2.] {
for edge in [crate::Edge::Top, crate::Edge::Bottom] {
for (width, height, radius) in [(150., 34., 17.), (218.3, 41.7, 20.85), (340., 230., 26.)] {
for (width, height, radius) in
[(150., 34., 17.), (218.3, 41.7, 20.85), (340., 230., 26.)]
{
let scene = derive(
Parameters {
style: Style::Morph,
@@ -283,7 +330,11 @@ mod tests {
.unwrap();
let body = scene.paint[0];
assert_eq!(scene.paint.len(), 3);
assert_eq!(scene.input.len(), 1, "decorative shoulders must not take pointer input");
assert_eq!(
scene.input.len(),
1,
"decorative shoulders must not take pointer input"
);
let screen_y = if edge == crate::Edge::Top {
body.bounds.y
} else {
@@ -299,7 +350,10 @@ mod tests {
assert!((path.first().unwrap()[1] - screen_y).abs() < 1e-6);
assert!((path.last().unwrap()[1] - screen_y).abs() < 1e-6);
assert!(path.iter().any(|p| p[0] < body.bounds.x));
assert!(path.iter().any(|p| p[0] > body.bounds.x + body.bounds.width));
assert!(
path.iter()
.any(|p| p[0] > body.bounds.x + body.bounds.width)
);
for segment in path.windows(2) {
let [a, b] = [segment[0], segment[1]];
assert!(
+48 -12
View File
@@ -220,9 +220,12 @@ impl<'de> Deserialize<'de> for Item {
return Err(serde::de::Error::unknown_field(key, OPTIONS));
}
}
if fields.contains_key("style") && fields.get("kind").and_then(serde_json::Value::as_str) != Some("workspaces")
if fields.contains_key("style")
&& fields.get("kind").and_then(serde_json::Value::as_str) != Some("workspaces")
{
return Err(serde::de::Error::custom("style is only supported by Workspaces items"));
return Err(serde::de::Error::custom(
"style is only supported by Workspaces items",
));
}
if fields.contains_key("percentage")
&& fields.get("kind").and_then(serde_json::Value::as_str) != Some("battery")
@@ -447,7 +450,10 @@ pub fn validate(panels: &[Panel]) -> Result<(), String> {
let mut panel_ids = HashSet::new();
for panel in panels {
if !PanelStyle::AVAILABLE.contains(&panel.style) {
return Err(format!("panel style {} is not available yet", panel.style.key()));
return Err(format!(
"panel style {} is not available yet",
panel.style.key()
));
}
panel.geometry.validate()?;
panel.morph.validate()?;
@@ -497,7 +503,10 @@ pub fn validate(panels: &[Panel]) -> Result<(), String> {
return Err("panel item priority must be 0..100 and gaps 0..64".into());
}
if item.kind == ItemKind::Overflow {
return Err("overflow is supplied by the panel; it cannot be configured as an item".into());
return Err(
"overflow is supplied by the panel; it cannot be configured as an item"
.into(),
);
}
}
}
@@ -538,7 +547,11 @@ impl Panel {
}
pub fn zone_spacing(&self, zone: &Zone) -> f32 {
if self.style.connected() { 0. } else { zone.spacing }
if self.style.connected() {
0.
} else {
zone.spacing
}
}
pub fn shape_parameters(&self) -> crate::scene::Parameters {
@@ -606,7 +619,8 @@ impl Panel {
}
pub fn resolved_radius(&self, shell_radius: f32) -> CornerRadii {
self.corner_radius.unwrap_or_else(|| CornerRadii::uniform(shell_radius))
self.corner_radius
.unwrap_or_else(|| CornerRadii::uniform(shell_radius))
}
/// The overflow trigger is generated, never persisted as a configured item.
@@ -629,7 +643,9 @@ impl Panel {
.end
.groups
.first()
.map_or(crate::default_island_padding(), |group| group.island_padding),
.map_or(crate::default_island_padding(), |group| {
group.island_padding
}),
}
}
@@ -685,7 +701,12 @@ impl Panel {
let mut controls = controls;
controls.extend([clock, display]);
(
vec![Group::new("navigation", vec![overview, workspaces], [2, 3], padding)],
vec![Group::new(
"navigation",
vec![overview, workspaces],
[2, 3],
padding,
)],
Group::new("title", vec![title], [0, 0], padding),
vec![Group::new("status", controls, [2, 3], padding)],
)
@@ -703,7 +724,11 @@ impl Panel {
corner_radius: Option::None,
start: Zone {
groups: start,
spacing: if preset == PanelStyle::Floating { 8.0 } else { 3.0 },
spacing: if preset == PanelStyle::Floating {
8.0
} else {
3.0
},
},
center: Zone {
groups: vec![center],
@@ -821,7 +846,9 @@ mod tests {
battery: true,
external_display: true
}),
all.iter().map(|item| item.id.0.as_str()).collect::<Vec<_>>()
all.iter()
.map(|item| item.id.0.as_str())
.collect::<Vec<_>>()
);
assert_eq!(items(&panel).len(), 13);
}
@@ -842,7 +869,10 @@ mod tests {
{
group.island_padding = 13.;
}
assert_eq!(panel.center.groups[0].items[0].kind, ItemKind::FocusedWindow);
assert_eq!(
panel.center.groups[0].items[0].kind,
ItemKind::FocusedWindow
);
assert!(!panel.center.groups[0].items[0].visible);
let all = items(&panel);
assert_eq!(
@@ -854,7 +884,13 @@ mod tests {
assert_eq!(group.island_padding, 13.0);
}
}
assert_eq!(all.iter().find(|item| item.id.0 == "clock").unwrap().gap_before, None);
assert_eq!(
all.iter()
.find(|item| item.id.0 == "clock")
.unwrap()
.gap_before,
None
);
}
#[test]
+21 -4
View File
@@ -25,8 +25,16 @@ impl GeometryIntent {
Morph => 34.,
_ => 36.,
}),
edge_margin: if flush { 0 } else { self.edge_margin.unwrap_or(8) },
side_margins: if flush { 0 } else { self.side_margins.unwrap_or(8) },
edge_margin: if flush {
0
} else {
self.edge_margin.unwrap_or(8)
},
side_margins: if flush {
0
} else {
self.side_margins.unwrap_or(8)
},
window_clearance: self.window_clearance.unwrap_or(0),
inner_padding: self.inner_padding.unwrap_or(12.),
}
@@ -74,7 +82,10 @@ impl PanelGeometry {
.into_iter()
.any(|value| !(0..=4096).contains(&value))
{
return Err("panel height must be 24..128, inner padding 0..64, and margins/clearance 0..4096".into());
return Err(
"panel height must be 24..128, inner padding 0..64, and margins/clearance 0..4096"
.into(),
);
}
Ok(())
}
@@ -101,7 +112,13 @@ mod tests {
assert_eq!(pins.resolve(Flat).side_margins, 0);
assert_eq!(pins.resolve(Floating).side_margins, 18);
assert_eq!(pins.resolve(Notch).edge_margin, 0);
assert!(serde_json::to_value(intent).unwrap().as_object().unwrap().is_empty());
assert!(
serde_json::to_value(intent)
.unwrap()
.as_object()
.unwrap()
.is_empty()
);
assert!(
GeometryIntent {
edge_margin: Some(-1),
+117 -31
View File
@@ -10,7 +10,10 @@ pub struct Measurement {
#[derive(Clone, Debug, PartialEq)]
pub enum Placement {
Visible { representation: Representation, width: f32 },
Visible {
representation: Representation,
width: f32,
},
Overflow,
}
@@ -103,7 +106,12 @@ impl Resolution {
}
}
pub fn resolve(panel: &Panel, measurements: &[Measurement], available: f32, overflow_width: f32) -> Resolution {
pub fn resolve(
panel: &Panel,
measurements: &[Measurement],
available: f32,
overflow_width: f32,
) -> Resolution {
resolve_with_gap(panel, measurements, available, overflow_width, 8.)
}
@@ -127,7 +135,10 @@ pub fn resolve_with_gap(
.flat_map(|zone| &zone.groups)
.flat_map(|group| &group.items)
.collect();
let measurements: BTreeMap<_, _> = measurements.iter().map(|measured| (&measured.id, measured)).collect();
let measurements: BTreeMap<_, _> = measurements
.iter()
.map(|measured| (&measured.id, measured))
.collect();
// Allocation policy follows composition, never the renderer's sample order.
// Lower priorities yield first; definition order breaks equal-priority ties.
let mut candidates: Vec<_> = ordered.iter().enumerate().collect();
@@ -143,9 +154,13 @@ pub fn resolve_with_gap(
result.items.insert(item.id.clone(), Placement::Overflow);
result.overflow.push(item.id.clone());
} else {
result
.items
.insert(item.id.clone(), Placement::Visible { representation, width });
result.items.insert(
item.id.clone(),
Placement::Visible {
representation,
width,
},
);
}
}
result.update_widths(panel, overflow_width);
@@ -164,13 +179,20 @@ pub fn resolve_with_gap(
result.minimum_width = minimum.required_width();
// Flexible content yields space without changing the stored item preference.
for (_, item) in &candidates {
let Some(measured) = measurements.get(&item.id).filter(|measured| measured.minimum.is_some()) else {
let Some(measured) = measurements
.get(&item.id)
.filter(|measured| measured.minimum.is_some())
else {
continue;
};
if fits(&result) {
break;
}
let Some(Placement::Visible { representation, width }) = result.items.get(&measured.id).cloned() else {
let Some(Placement::Visible {
representation,
width,
}) = result.items.get(&measured.id).cloned()
else {
continue;
};
let minimum = measured.minimum.unwrap().min(width);
@@ -191,12 +213,18 @@ pub fn resolve_with_gap(
continue;
};
for &(representation, width) in measured.alternatives.iter().skip(1) {
if let Some(Placement::Visible { width: previous, .. }) = result.items.get(&item.id)
if let Some(Placement::Visible {
width: previous, ..
}) = result.items.get(&item.id)
&& width < *previous
{
result
.items
.insert(item.id.clone(), Placement::Visible { representation, width });
result.items.insert(
item.id.clone(),
Placement::Visible {
representation,
width,
},
);
result.update_widths(panel, overflow_width);
if fits(&result) {
break;
@@ -214,7 +242,8 @@ pub fn resolve_with_gap(
if item.overflow == OverflowPolicy::Never && !force {
continue;
}
if matches!(result.items.get(&item.id), Some(Placement::Visible { width, .. }) if *width > 0.0) {
if matches!(result.items.get(&item.id), Some(Placement::Visible { width, .. }) if *width > 0.0)
{
result.items.insert(item.id.clone(), Placement::Overflow);
result.overflow.push(item.id.clone());
result.forced_overflow |= force && item.overflow == OverflowPolicy::Never;
@@ -235,7 +264,11 @@ pub fn resolve_with_gap(
let Some(measured) = measurements.get(&item.id) else {
continue;
};
let Some(&(representation, width)) = measured.alternatives.iter().min_by(|a, b| a.1.total_cmp(&b.1)) else {
let Some(&(representation, width)) = measured
.alternatives
.iter()
.min_by(|a, b| a.1.total_cmp(&b.1))
else {
continue;
};
result.items.insert(
@@ -255,7 +288,8 @@ pub fn resolve_with_gap(
}
}
result.forced_overflow = ordered.iter().any(|item| {
item.overflow == OverflowPolicy::Never && matches!(result.items.get(&item.id), Some(Placement::Overflow))
item.overflow == OverflowPolicy::Never
&& matches!(result.items.get(&item.id), Some(Placement::Overflow))
});
// A reduction on the shorter side can leave unused room after the other
// side shrinks. Restore preferred representations before expanding titles.
@@ -263,7 +297,9 @@ pub fn resolve_with_gap(
let Some(measured) = measurements.get(&item.id) else {
continue;
};
let Some(previous @ Placement::Visible { representation, .. }) = result.items.get(&item.id).cloned() else {
let Some(previous @ Placement::Visible { representation, .. }) =
result.items.get(&item.id).cloned()
else {
continue;
};
for &(candidate, width) in measured
@@ -288,7 +324,10 @@ pub fn resolve_with_gap(
result.update_widths(panel, overflow_width);
// Restore the most protected flexible items first, reversing the yield order.
for (_, item) in candidates.iter().rev() {
let Some(measured) = measurements.get(&item.id).filter(|measured| measured.minimum.is_some()) else {
let Some(measured) = measurements
.get(&item.id)
.filter(|measured| measured.minimum.is_some())
else {
continue;
};
let Some(Placement::Visible {
@@ -309,9 +348,16 @@ pub fn resolve_with_gap(
let mut high = maximum;
for _ in 0..20 {
let width = (low + high) * 0.5;
*result.items.get_mut(&measured.id).unwrap() = Placement::Visible { representation, width };
*result.items.get_mut(&measured.id).unwrap() = Placement::Visible {
representation,
width,
};
result.update_widths(panel, overflow_width);
if fits(&result) { low = width } else { high = width }
if fits(&result) {
low = width
} else {
high = width
}
}
*result.items.get_mut(&measured.id).unwrap() = Placement::Visible {
representation,
@@ -333,7 +379,11 @@ mod tests {
fn asymmetric_overflow_restores_items_that_fit_after_the_larger_side_yields() {
use super::super::{Group, Item, ItemKind};
for mirrored in [false, true] {
for policy in [OverflowPolicy::Auto, OverflowPolicy::Never, OverflowPolicy::Always] {
for policy in [
OverflowPolicy::Auto,
OverflowPolicy::Never,
OverflowPolicy::Always,
] {
let group = |id: &str, priority, overflow| {
let mut item = Item::new(id, ItemKind::Clock);
item.priority = priority;
@@ -360,7 +410,10 @@ mod tests {
let resolution = resolve(&panel, &measurements, 300., 32.);
assert!(resolution.fits(300.));
assert_eq!(
matches!(resolution.items[&ItemId("small".into())], Placement::Visible { .. }),
matches!(
resolution.items[&ItemId("small".into())],
Placement::Visible { .. }
),
policy != OverflowPolicy::Always
);
assert!(resolution.overflow.contains(&ItemId("large".into())));
@@ -381,10 +434,18 @@ mod tests {
let mut group = Group::new("content", vec![first, hidden, last], [0, 3], 5.);
panel.style = crate::PanelStyle::Floating;
group.island_padding = 4.;
let empty = Group::new("empty", vec![Item::new("missing", ItemKind::Media)], [0, 99], 99.);
let empty = Group::new(
"empty",
vec![Item::new("missing", ItemKind::Media)],
[0, 99],
99.,
);
let trailing = Group::new(
"trailing",
vec![Item::new("trailing", ItemKind::Battery { percentage: false })],
vec![Item::new(
"trailing",
ItemKind::Battery { percentage: false },
)],
[0, 2],
5.,
);
@@ -630,9 +691,15 @@ mod tests {
let expected = resolve_with_gap(&panel, &measured, width, 32., 16.);
for _ in 0..measured.len() {
measured.rotate_left(1);
assert_eq!(resolve_with_gap(&panel, &measured, width, 32., 16.), expected);
assert_eq!(
resolve_with_gap(&panel, &measured, width, 32., 16.),
expected
);
measured.reverse();
assert_eq!(resolve_with_gap(&panel, &measured, width, 32., 16.), expected);
assert_eq!(
resolve_with_gap(&panel, &measured, width, 32., 16.),
expected
);
measured.reverse();
}
}
@@ -700,7 +767,10 @@ mod tests {
let margin = full.side_margin(requested, output_width, 12.);
let available = output_width as f32 - 2. * (margin as f32 + 12.);
let result = resolve(&panel, &measurements, available, 32.);
assert!(result.overflow.is_empty(), "output {output_width}, margin {margin}");
assert!(
result.overflow.is_empty(),
"output {output_width}, margin {margin}"
);
assert!(result.fits(available));
assert!(margin <= requested);
}
@@ -714,7 +784,8 @@ mod tests {
}
#[test]
fn margin_limit_includes_group_padding_and_authored_overflow_without_dependence_on_current_surface_width() {
fn margin_limit_includes_group_padding_and_authored_overflow_without_dependence_on_current_surface_width()
{
let mut panel = Panel::default();
panel.end.groups[0].items[0].overflow = OverflowPolicy::Always;
let measurements = measurements(&panel);
@@ -722,7 +793,12 @@ mod tests {
let narrow = resolve(&panel, &measurements, 100., 32.);
assert_eq!(full.minimum_width, narrow.minimum_width);
let margin = full.side_margin(4096, 2400, 12.);
let result = resolve(&panel, &measurements, 2400. - 2. * (margin as f32 + 12.), 32.);
let result = resolve(
&panel,
&measurements,
2400. - 2. * (margin as f32 + 12.),
32.,
);
assert_eq!(result.overflow, full.overflow);
let original = full.minimum_width;
panel.end.groups[0].padding[1] += 10;
@@ -794,7 +870,10 @@ mod tests {
assert!(result.overflow.is_empty());
assert!(result.items.values().all(|placement| matches!(placement, Placement::Visible { representation: Representation::Wide, width } if *width == 50.)));
}
assert_eq!(panel, initial, "resolution must not rewrite configuration intent");
assert_eq!(
panel, initial,
"resolution must not rewrite configuration intent"
);
}
}
#[test]
@@ -804,8 +883,15 @@ mod tests {
let initial = resolve(&panel, &measured, 2400., 32.);
panel.style = crate::PanelStyle::Flat;
let plain = resolve(&panel, &measured, 2400., 32.);
for (i, zone) in [&panel.start, &panel.center, &panel.end].into_iter().enumerate() {
let padding: f32 = zone.groups.iter().map(|group| 2. * group.island_padding).sum();
for (i, zone) in [&panel.start, &panel.center, &panel.end]
.into_iter()
.enumerate()
{
let padding: f32 = zone
.groups
.iter()
.map(|group| 2. * group.island_padding)
.sum();
assert_eq!(plain.zone_widths[i], initial.zone_widths[i] - padding);
}
panel.style = crate::PanelStyle::Flat;
+10 -3
View File
@@ -79,7 +79,8 @@ impl std::str::FromStr for CornerRadii {
let number = if part == "0" {
part
} else {
part.strip_suffix("px").ok_or("use pixel values such as 4px")?
part.strip_suffix("px")
.ok_or("use pixel values such as 4px")?
};
number.parse::<f32>().map_err(|_| "invalid corner radius")
})
@@ -152,7 +153,10 @@ mod tests {
let radii = CornerRadii([4., 8., 12., 16.]);
assert_eq!(radii.at_top_edge(true).0, [0., 0., 12., 16.]);
assert_eq!(radii.at_top_edge(false), radii);
assert_eq!(radii.at_edge(super::super::Edge::Bottom, true).0, [4., 8., 0., 0.]);
assert_eq!(
radii.at_edge(super::super::Edge::Bottom, true).0,
[4., 8., 0., 0.]
);
assert_eq!(radii.at_edge(super::super::Edge::Bottom, false), radii);
assert_eq!(radii.0, [4., 8., 12., 16.]);
}
@@ -171,6 +175,9 @@ mod tests {
] {
assert!(source.parse::<CornerRadii>().is_err(), "{source}");
}
assert_eq!(CornerRadii([4., 40., 0., 18.]).clamped(100., 28.).0, [4., 14., 0., 14.]);
assert_eq!(
CornerRadii([4., 40., 0., 18.]).clamped(100., 28.).0,
[4., 14., 0., 14.]
);
}
}
+81 -21
View File
@@ -17,7 +17,12 @@ pub struct Primitive {
impl Primitive {
pub fn packet(self) -> [f32; 10] {
let Rect { x, y, width, height } = self.bounds;
let Rect {
x,
y,
width,
height,
} = self.bounds;
let [tl, tr, br, bl] = self.radii.0;
[
x as f32,
@@ -34,7 +39,9 @@ impl Primitive {
}
pub fn from_packet(values: [f32; 10]) -> Result<Self, &'static str> {
if !values.into_iter().all(|v| v.is_finite() && v.abs() <= 32768.)
if !values
.into_iter()
.all(|v| v.is_finite() && v.abs() <= 32768.)
|| values[2] <= 0.
|| values[3] <= 0.
|| values[4..8].iter().any(|v| *v < 0.)
@@ -46,8 +53,14 @@ impl Primitive {
return Err("invalid panel shape primitive");
}
Ok(Self {
bounds: Rect::new(values[0].into(), values[1].into(), values[2].into(), values[3].into()),
radii: CornerRadii([values[4], values[5], values[6], values[7]]).normalized(values[2], values[3]),
bounds: Rect::new(
values[0].into(),
values[1].into(),
values[2].into(),
values[3].into(),
),
radii: CornerRadii([values[4], values[5], values[6], values[7]])
.normalized(values[2], values[3]),
operation: values[8] as u8,
blend_zone: values[9] as u8,
})
@@ -63,7 +76,8 @@ impl Primitive {
4 => [b.x + b.width, b.y],
_ => [b.x, b.y],
};
return rect.max(b.width.min(b.height) - (point[0] - center[0]).hypot(point[1] - center[1]));
return rect
.max(b.width.min(b.height) - (point[0] - center[0]).hypot(point[1] - center[1]));
}
let b = self.bounds;
let (x, y) = (point[0] - b.x, point[1] - b.y);
@@ -150,12 +164,21 @@ impl Scene {
let bottom = ((b.y + b.height) * scale).ceil() as i32;
for y in top..bottom {
let mut first = left;
while first < right && !shape.contains([(f64::from(first) + 0.5) / scale, (f64::from(y) + 0.5) / scale])
while first < right
&& !shape.contains([
(f64::from(first) + 0.5) / scale,
(f64::from(y) + 0.5) / scale,
])
{
first += 1;
}
let mut last = right;
while last > first && !shape.contains([(f64::from(last) - 0.5) / scale, (f64::from(y) + 0.5) / scale]) {
while last > first
&& !shape.contains([
(f64::from(last) - 0.5) / scale,
(f64::from(y) + 0.5) / scale,
])
{
last -= 1;
}
if first == last {
@@ -328,8 +351,18 @@ pub fn derive(
Style::CornerWrap => {
let h = parameters.height.min(bounds.height);
let w = parameters.wrap_width.min(bounds.width);
let horizontal = Rect::new(bounds.x, bounds.y, parameters.wrap_size[0].min(bounds.width), h);
let vertical = Rect::new(bounds.x, bounds.y, w, parameters.wrap_size[1].min(bounds.height));
let horizontal = Rect::new(
bounds.x,
bounds.y,
parameters.wrap_size[0].min(bounds.width),
h,
);
let vertical = Rect::new(
bounds.x,
bounds.y,
w,
parameters.wrap_size[1].min(bounds.height),
);
add(
&mut scene,
grid,
@@ -338,7 +371,14 @@ pub fn derive(
true,
0,
)?;
add(&mut scene, grid, vertical, CornerRadii([0., 0., 14., 0.]), true, 0)?;
add(
&mut scene,
grid,
vertical,
CornerRadii([0., 0., 14., 0.]),
true,
0,
)?;
// Capture the union once: overlapping translucent captures darken the elbow.
scene.damage = vec![
horizontal,
@@ -388,8 +428,12 @@ pub fn derive(
// One material capture owns the body and both shoulders. Separate
// captures overlap after logical rounding at fractional scales,
// painting translucent joins twice as the spring settles.
scene.damage =
vec![grid.rect(Rect::new(bounds.x - r, bounds.y, bounds.width + 2. * r, bounds.height))?];
scene.damage = vec![grid.rect(Rect::new(
bounds.x - r,
bounds.y,
bounds.width + 2. * r,
bounds.height,
))?];
}
}
}
@@ -406,7 +450,10 @@ pub fn derive(
continue;
}
if style.connected() {
let x = occupied.iter().map(|g| g.bounds.x).fold(f64::INFINITY, f64::min);
let x = occupied
.iter()
.map(|g| g.bounds.x)
.fold(f64::INFINITY, f64::min);
let end = occupied
.iter()
.map(|g| g.bounds.x + g.bounds.width)
@@ -440,9 +487,12 @@ pub fn derive(
add(&mut scene, grid, body, radii, true, 0)?;
for group in occupied.iter().take(occupied.len() - 1) {
let seam = grid.snap(group.bounds.x + group.bounds.width);
scene
.dividers
.push([seam, bounds.y + 8., seam, bounds.y + bounds.height - 8.]);
scene.dividers.push([
seam,
bounds.y + 8.,
seam,
bounds.y + bounds.height - 8.,
]);
}
if style == Style::Notch && zone == ZonePosition::Center {
let r = parameters
@@ -469,7 +519,8 @@ pub fn derive(
}
} else {
for group in occupied {
let body = Rect::new(group.bounds.x, bounds.y, group.bounds.width, bounds.height);
let body =
Rect::new(group.bounds.x, bounds.y, group.bounds.width, bounds.height);
let radii = parameters
.radius
.map_or(CornerRadii::uniform((bounds.height / 2.) as f32), |r| {
@@ -500,7 +551,8 @@ pub fn derive(
if let Some(first) = shapes.next() {
let mut region = first.bounds;
for shape in shapes {
let right = (region.x + region.width).max(shape.bounds.x + shape.bounds.width);
let right =
(region.x + region.width).max(shape.bounds.x + shape.bounds.width);
region.x = region.x.min(shape.bounds.x);
region.width = right - region.x;
}
@@ -510,7 +562,9 @@ pub fn derive(
}
}
}
if parameters.edge == Edge::Bottom && matches!(parameters.style, Style::Frame | Style::CornerWrap) {
if parameters.edge == Edge::Bottom
&& matches!(parameters.style, Style::Frame | Style::CornerWrap)
{
let mirror = |mut r: Rect| {
r.y = 2. * bounds.y + bounds.height - r.y - r.height;
r
@@ -529,7 +583,8 @@ pub fn derive(
}
for input in &mut scene.input {
let [tl, tr, br, bl] = input.radii().0;
*input = RoundedSurface::new(mirror(input.bounds()), CornerRadii([bl, br, tr, tl]), grid)?;
*input =
RoundedSurface::new(mirror(input.bounds()), CornerRadii([bl, br, tr, tl]), grid)?;
}
for damage in &mut scene.damage {
*damage = mirror(*damage);
@@ -629,7 +684,12 @@ mod tests {
assert!(!scene.contains([38., 38.])); // the decorative elbow is not input
for (i, a) in scene.damage.iter().enumerate() {
for b in &scene.damage[i + 1..] {
assert!(a.x + a.width <= b.x || b.x + b.width <= a.x || a.y + a.height <= b.y || b.y + b.height <= a.y);
assert!(
a.x + a.width <= b.x
|| b.x + b.width <= a.x
|| a.y + a.height <= b.y
|| b.y + b.height <= a.y
);
}
}
for x in (0..800).step_by(3) {
+98 -17
View File
@@ -77,7 +77,12 @@ impl Style {
pub const fn detached(self) -> bool {
matches!(
self,
Self::Floating | Self::Capsules | Self::Fused | Self::Morph | Self::Adaptive | Self::WindowHug
Self::Floating
| Self::Capsules
| Self::Fused
| Self::Morph
| Self::Adaptive
| Self::WindowHug
)
}
}
@@ -129,7 +134,10 @@ pub fn effective_radii(
index: usize,
count: usize,
) -> Result<CornerRadii, &'static str> {
if !size.into_iter().all(|value| value.is_finite() && value >= 0.) {
if !size
.into_iter()
.all(|value| value.is_finite() && value >= 0.)
{
return Err("panel dimensions must be finite and nonnegative");
}
if index >= count {
@@ -137,9 +145,12 @@ pub fn effective_radii(
}
let [width, height] = size;
let base = AnchoredRadii(match style {
Style::Floating | Style::Capsules | Style::Fused | Style::Morph | Style::Adaptive | Style::WindowHug => {
[height / 2.; 4]
}
Style::Floating
| Style::Capsules
| Style::Fused
| Style::Morph
| Style::Adaptive
| Style::WindowHug => [height / 2.; 4],
Style::Notch => match zone {
ZonePosition::Start => [0., 0., 14., 0.],
ZonePosition::Center => [0., 0., 20., 20.],
@@ -189,7 +200,12 @@ impl PixelGrid {
if ![left, top, right, bottom].into_iter().all(f64::is_finite) {
return Err("surface bounds exceed the physical pixel grid");
}
Ok(Rect::new(left, top, (right - left).max(0.), (bottom - top).max(0.)))
Ok(Rect::new(
left,
top,
(right - left).max(0.),
(bottom - top).max(0.),
))
}
}
@@ -266,7 +282,17 @@ mod tests {
assert_eq!(radius(1, 2).0, [0., 18., 18., 0.]);
assert_eq!(radius(0, 1).0, [18.; 4]);
assert_eq!(radius(0, 2).0, [18., 0., 0., 18.]);
assert!(effective_radii(Style::Capsules, ZonePosition::Start, Edge::Top, [100., 36.], 0, 0).is_err());
assert!(
effective_radii(
Style::Capsules,
ZonePosition::Start,
Edge::Top,
[100., 36.],
0,
0
)
.is_err()
);
}
#[test]
@@ -288,14 +314,25 @@ mod tests {
#[test]
fn asymmetric_corners_preserve_notch_shape_and_scale_proportionally() {
let notch = effective_radii(Style::Notch, ZonePosition::Center, Edge::Top, [100., 28.], 0, 1).unwrap();
let notch = effective_radii(
Style::Notch,
ZonePosition::Center,
Edge::Top,
[100., 28.],
0,
1,
)
.unwrap();
assert_eq!(notch.0, [0., 0., 20., 20.]);
assert_eq!(notch.normalized(30., 28.).0, [0., 0., 15., 15.]);
assert_eq!(
CornerRadii([10., 20., 40., 30.]).normalized(35., 25.).0,
[25. / 6., 25. / 3., 50. / 3., 12.5]
);
assert_eq!(CornerRadii::uniform(f32::MAX).normalized(40., 28.).0, [14.; 4]);
assert_eq!(
CornerRadii::uniform(f32::MAX).normalized(40., 28.).0,
[14.; 4]
);
assert_eq!(notch.normalized(0., 28.).0, [0.; 4]);
}
@@ -317,9 +354,25 @@ mod tests {
#[test]
fn floating_gaps_and_round_corners_do_not_capture_input() {
let grid = PixelGrid::new(1.25).unwrap();
let left = RoundedSurface::new(Rect::new(8., 8., 100., 36.), CornerRadii::uniform(18.), grid).unwrap();
let right = RoundedSurface::new(Rect::new(116., 8., 100., 36.), CornerRadii::uniform(18.), grid).unwrap();
for point in [[0., 0.], [8., 8.], [112., 26.], [216., 26.], [f64::NAN, 26.]] {
let left = RoundedSurface::new(
Rect::new(8., 8., 100., 36.),
CornerRadii::uniform(18.),
grid,
)
.unwrap();
let right = RoundedSurface::new(
Rect::new(116., 8., 100., 36.),
CornerRadii::uniform(18.),
grid,
)
.unwrap();
for point in [
[0., 0.],
[8., 8.],
[112., 26.],
[216., 26.],
[f64::NAN, 26.],
] {
assert!(!left.contains(point) && !right.contains(point), "{point:?}");
}
assert!(left.contains([30., 26.]));
@@ -330,7 +383,12 @@ mod tests {
fn asymmetric_hit_testing_matches_a_vertically_mirrored_surface() {
let grid = PixelGrid::new(1.).unwrap();
let bounds = Rect::new(0., 0., 100., 28.);
let top = RoundedSurface::new(bounds, AnchoredRadii([0., 0., 20., 20.]).screen_space(Edge::Top), grid).unwrap();
let top = RoundedSurface::new(
bounds,
AnchoredRadii([0., 0., 20., 20.]).screen_space(Edge::Top),
grid,
)
.unwrap();
let bottom = RoundedSurface::new(
bounds,
AnchoredRadii([0., 0., 20., 20.]).screen_space(Edge::Bottom),
@@ -352,8 +410,18 @@ mod tests {
#[test]
fn a_connected_seam_has_exactly_one_input_owner() {
let grid = PixelGrid::new(1.5).unwrap();
let left = RoundedSurface::new(Rect::new(0., 0., 30., 36.), CornerRadii([18., 0., 0., 18.]), grid).unwrap();
let right = RoundedSurface::new(Rect::new(30., 0., 40., 36.), CornerRadii([0., 18., 18., 0.]), grid).unwrap();
let left = RoundedSurface::new(
Rect::new(0., 0., 30., 36.),
CornerRadii([18., 0., 0., 18.]),
grid,
)
.unwrap();
let right = RoundedSurface::new(
Rect::new(30., 0., 40., 36.),
CornerRadii([0., 18., 18., 0.]),
grid,
)
.unwrap();
for y in [0., 18., 35.9] {
assert!(!left.contains([30., y]));
assert!(right.contains([30., y]));
@@ -373,7 +441,20 @@ mod tests {
] {
assert!(grid.rect(bounds).is_err());
}
assert!(RoundedSurface::new(Rect::new(0., 0., 10., 28.), CornerRadii::uniform(-1.), grid).is_err());
assert!(effective_radii(Style::Floating, ZonePosition::Start, Edge::Top, [100., f32::NAN], 0, 1).is_err());
assert!(
RoundedSurface::new(Rect::new(0., 0., 10., 28.), CornerRadii::uniform(-1.), grid)
.is_err()
);
assert!(
effective_radii(
Style::Floating,
ZonePosition::Start,
Edge::Top,
[100., f32::NAN],
0,
1
)
.is_err()
);
}
}