Clarify constructor failures and borrowed API contracts

This commit is contained in:
Samuel Onoja committed 2026-10-22 01:28:03 +01:00
1 parent 23e280a6b4
commit a7fcd69897
55 files changed
+332 -102

No files matched your search

+4
View File
@@ -18,6 +18,7 @@ pub enum Presentation {
}
impl Presentation {
#[must_use]
pub fn style(self) -> Option<PanelStyle> {
match self {
Self::Floating => Some(PanelStyle::Floating),
@@ -48,6 +49,7 @@ pub struct Adaptive {
}
impl Adaptive {
#[must_use]
pub fn new(state: WindowState) -> Self {
Self {
current: state.into(),
@@ -55,6 +57,7 @@ impl Adaptive {
}
}
#[must_use]
pub fn current(&self) -> Presentation {
self.current
}
@@ -87,6 +90,7 @@ impl Adaptive {
}
}
#[must_use]
pub fn deadline(&self) -> Option<Duration> {
self.pending
.map(|(_, since)| since.saturating_add(HYSTERESIS))
+1
View File
@@ -9,6 +9,7 @@ pub mod shape;
pub use shape::Style as PanelStyle;
#[must_use]
pub const fn default_island_padding() -> f32 {
4.0
}
+17 -10
View File
@@ -75,6 +75,7 @@ impl Profile {
}
}
#[must_use]
pub fn size(self, mode: Mode, collapsed_height: f64) -> [f64; 2] {
match mode {
Mode::Idle => [self.idle_width, collapsed_height],
@@ -114,6 +115,7 @@ pub enum Mode {
}
impl Mode {
#[must_use]
pub fn expanded(self) -> bool {
matches!(self, Self::Expanded(_))
}
@@ -145,7 +147,7 @@ pub struct Hub {
}
impl Hub {
pub fn new(profile: Profile, collapsed_height: f64) -> Result<Self, &'static str> {
pub fn try_new(profile: Profile, collapsed_height: f64) -> Result<Self, &'static str> {
profile.validate()?;
if !collapsed_height.is_finite() || !(24. ..=128.).contains(&collapsed_height) {
return Err("hub height must be 24..128");
@@ -196,6 +198,7 @@ impl Hub {
self.motion.retarget(target)
}
#[must_use]
pub fn mode(&self) -> Mode {
self.mode
}
@@ -301,10 +304,12 @@ impl Hub {
self.active()
}
#[must_use]
pub fn active(&self) -> bool {
self.motion.active() || self.sample().previous_opacity > 0. || self.sample().opacity < 1.
}
#[must_use]
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
@@ -324,11 +329,13 @@ impl Hub {
}
}
#[must_use]
pub fn allocation_size(&self) -> [f64; 2] {
let maximum = self.motion.maximum_size();
std::array::from_fn(|i| maximum[i].min(self.available[i]))
}
#[must_use]
pub fn target_size(&self) -> [f64; 2] {
let target = self.motion.target().bounds;
[target.width, target.height]
@@ -341,7 +348,7 @@ mod tests {
#[test]
fn workspace_width_changes_preserve_content_and_motion() {
let mut hub = Hub::new(Profile::default(), 34.).unwrap();
let mut hub = Hub::try_new(Profile::default(), 34.).unwrap();
hub.retarget_idle(270., [800., 600.]).unwrap();
hub.advance(Duration::from_millis(60), false);
let before = hub.sample();
@@ -358,7 +365,7 @@ mod tests {
#[test]
fn measured_height_preserves_fade_and_motion_and_caps_long_content() {
let mut hub = Hub::new(Profile::default(), 34.).unwrap();
let mut hub = Hub::try_new(Profile::default(), 34.).unwrap();
hub.retarget(Mode::Expanded(Content::Controls), [800., 600.])
.unwrap();
hub.advance(Duration::from_millis(180), false);
@@ -382,7 +389,7 @@ mod tests {
#[test]
fn a_content_swap_fades_without_restarting_the_geometry() {
let mut hub = Hub::new(Profile::default(), 34.).unwrap();
let mut hub = Hub::try_new(Profile::default(), 34.).unwrap();
hub.retarget(Mode::Expanded(Content::Controls), [800., 600.])
.unwrap();
hub.advance(Duration::from_secs(2), false);
@@ -404,7 +411,7 @@ mod tests {
#[test]
fn content_swap_uses_a_fixed_delay_instead_of_waiting_for_spring_settlement() {
let mut hub = Hub::new(Profile::default(), 34.).unwrap();
let mut hub = Hub::try_new(Profile::default(), 34.).unwrap();
assert!(!hub.active());
hub.retarget(Mode::Expanded(Content::Calendar), [800., 600.])
.unwrap();
@@ -430,7 +437,7 @@ mod tests {
#[test]
fn an_interrupted_content_delay_never_reveals_the_cancelled_panel() {
let mut hub = Hub::new(Profile::default(), 34.).unwrap();
let mut hub = Hub::try_new(Profile::default(), 34.).unwrap();
hub.retarget(Mode::Expanded(Content::Media), [800., 600.])
.unwrap();
hub.advance(Duration::from_millis(80), false);
@@ -448,7 +455,7 @@ mod tests {
#[test]
fn spring_overshoots_then_settles_within_its_reserved_surface() {
let mut hub = Hub::new(Profile::default(), 34.).unwrap();
let mut hub = Hub::try_new(Profile::default(), 34.).unwrap();
hub.retarget(Mode::Expanded(Content::Calendar), [800., 600.])
.unwrap();
let allocation = hub.allocation_size();
@@ -468,7 +475,7 @@ 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();
let mut hub = Hub::try_new(Profile::default(), 34.).unwrap();
hub.retarget(Mode::Expanded(Content::Calendar), [240., 180.])
.unwrap();
for _ in 0..60 {
@@ -486,7 +493,7 @@ mod tests {
#[test]
fn interrupted_expansion_keeps_the_presented_shape() {
let mut hub = Hub::new(Profile::default(), 34.).unwrap();
let mut hub = Hub::try_new(Profile::default(), 34.).unwrap();
hub.retarget(Mode::Expanded(Content::Media), [800., 600.])
.unwrap();
hub.advance(Duration::from_millis(40), false);
@@ -502,7 +509,7 @@ mod tests {
#[test]
fn narrow_outputs_and_reduced_motion_keep_the_hub_bounded() {
let mut hub = Hub::new(Profile::default(), 30.).unwrap();
let mut hub = Hub::try_new(Profile::default(), 30.).unwrap();
hub.retarget(Mode::Expanded(Content::Calendar), [240., 180.])
.unwrap();
hub.advance(Duration::ZERO, true);
+8 -5
View File
@@ -18,6 +18,7 @@ pub struct Geometry {
const COUNT: usize = 18;
impl Geometry {
#[must_use]
pub fn rounded(bounds: Rect, radius: f64) -> Self {
Self {
bounds,
@@ -75,7 +76,7 @@ pub struct Motion {
}
impl Motion {
pub fn new(geometry: Geometry) -> Result<Self, &'static str> {
pub fn try_new(geometry: Geometry) -> Result<Self, &'static str> {
Self::with_spring(
geometry,
SpringConfig {
@@ -132,10 +133,12 @@ impl Motion {
})
}
#[must_use]
pub fn current(&self) -> Geometry {
Geometry::from_values(self.values.map(|value| value.current))
}
#[must_use]
pub fn target(&self) -> Geometry {
Geometry::from_values(self.values.map(|value| value.target))
}
@@ -180,7 +183,7 @@ mod tests {
#[test]
fn interruption_keeps_the_current_geometry_and_velocity() {
let mut motion = Motion::new(collapsed()).unwrap();
let mut motion = Motion::try_new(collapsed()).unwrap();
motion.retarget(expanded()).unwrap();
motion.advance(Duration::from_millis(16), false);
let before = motion.current();
@@ -197,7 +200,7 @@ mod tests {
#[test]
fn dimensions_share_one_trajectory_and_stop_requesting_frames() {
let mut motion = Motion::new(collapsed()).unwrap();
let mut motion = Motion::try_new(collapsed()).unwrap();
motion.retarget(expanded()).unwrap();
for _ in 0..12 {
motion.advance(Duration::from_millis(16), false);
@@ -218,7 +221,7 @@ mod tests {
#[test]
fn reduced_motion_snaps_an_inflight_animation() {
let mut motion = Motion::new(collapsed()).unwrap();
let mut motion = Motion::try_new(collapsed()).unwrap();
motion.retarget(expanded()).unwrap();
motion.advance(Duration::from_millis(16), false);
assert!(!motion.advance(Duration::ZERO, true));
@@ -263,7 +266,7 @@ mod tests {
#[test]
fn invalid_retarget_leaves_motion_untouched() {
let mut motion = Motion::new(collapsed()).unwrap();
let mut motion = Motion::try_new(collapsed()).unwrap();
let mut invalid = expanded();
invalid.bounds.height = f64::NAN;
assert!(motion.retarget(invalid).is_err());
+3 -3
View File
@@ -269,7 +269,7 @@ mod tests {
zone: ZonePosition::Center,
bounds: Rect::new(70.3, 0., 160.4, 44.),
}],
PixelGrid::new(scale).unwrap(),
PixelGrid::try_new(scale).unwrap(),
)
.unwrap();
let paths = notch_border(&scene, 0.1 / scale, edge).unwrap();
@@ -325,7 +325,7 @@ mod tests {
},
Rect::new(30.3, 0., width, height),
&[],
PixelGrid::new(scale).unwrap(),
PixelGrid::try_new(scale).unwrap(),
)
.unwrap();
let body = scene.paint[0];
@@ -420,7 +420,7 @@ mod tests {
},
Rect::new(0., 0., 96., 64.),
&groups,
PixelGrid::new(1.25).unwrap(),
PixelGrid::try_new(1.25).unwrap(),
)
.unwrap();
let contours = contours(&scene, 0.1).unwrap();
@@ -33,6 +33,7 @@ pub enum Edge {
}
impl Edge {
#[must_use]
pub fn key(self) -> &'static str {
match self {
Self::Top => "top",
@@ -86,6 +87,7 @@ impl WorkspaceStyle {
Self::AppIcons,
];
#[must_use]
pub fn key(self) -> &'static str {
match self {
Self::Numbers => "numbers",
@@ -96,6 +98,7 @@ impl WorkspaceStyle {
}
}
#[must_use]
pub fn label(self) -> &'static str {
match self {
Self::Numbers => "Numbers",
@@ -135,6 +138,7 @@ pub enum ItemKind {
impl ItemKind {
/// Smallest useful allocation before a flexible control moves into overflow.
#[must_use]
pub fn minimum_width(self) -> Option<f32> {
match self {
Self::FocusedWindow => Some(48.),
@@ -144,6 +148,7 @@ impl ItemKind {
}
}
#[must_use]
pub fn label(self) -> &'static str {
match self {
Self::Overview => "Overview",
@@ -163,6 +168,7 @@ impl ItemKind {
Self::Overflow => "Overflow",
}
}
#[must_use]
pub fn representations(self) -> &'static [Representation] {
use Representation::*;
match self {
@@ -265,6 +271,7 @@ impl<'de> Deserialize<'de> for Item {
impl Item {
/// Start at the configured preference and retain supported smaller forms.
#[must_use]
pub fn representations(&self) -> Vec<Representation> {
let supported = self.kind.representations();
let preferred = self.representation.unwrap_or(supported[0]);
@@ -279,6 +286,7 @@ impl Item {
alternatives
}
}
#[must_use]
pub fn new(id: &str, kind: ItemKind) -> Self {
Self {
id: ItemId(id.into()),
@@ -305,6 +313,7 @@ impl Item {
}
}
#[must_use]
pub fn available(&self, availability: Availability) -> bool {
if !self.visible {
return false;
@@ -351,6 +360,7 @@ pub enum GroupSurface {
}
impl GroupSurface {
#[must_use]
pub fn key(self) -> &'static str {
match self {
Self::None => "none",
@@ -519,6 +529,7 @@ impl Default for Panel {
}
impl Panel {
#[must_use]
pub fn preset(&self) -> Option<PanelStyle> {
Some(self.style)
}
@@ -527,6 +538,7 @@ impl Panel {
self.style = style;
}
#[must_use]
pub fn surface(&self) -> PanelSurface {
if matches!(self.style, PanelStyle::Flat | PanelStyle::Frame) {
PanelSurface::Solid
@@ -535,6 +547,7 @@ impl Panel {
}
}
#[must_use]
pub fn group_surface(&self) -> GroupSurface {
if self.surface() == PanelSurface::Solid {
GroupSurface::None
@@ -543,6 +556,7 @@ impl Panel {
}
}
#[must_use]
pub fn zone_spacing(&self, zone: &Zone) -> f32 {
if self.style.connected() {
0.
@@ -551,6 +565,7 @@ impl Panel {
}
}
#[must_use]
pub fn shape_parameters(&self) -> crate::scene::Parameters {
crate::scene::Parameters {
style: self.style,
@@ -567,6 +582,7 @@ impl Panel {
}
}
#[must_use]
pub fn resolved_geometry(&self) -> PanelGeometry {
let mut geometry = self.geometry.resolve(self.style);
if self.style == PanelStyle::Morph && self.morph.attached {
@@ -575,6 +591,7 @@ impl Panel {
geometry
}
#[must_use]
pub fn exclusive_zone(&self, hidden: bool) -> i32 {
if hidden {
0
@@ -584,6 +601,7 @@ impl Panel {
}
}
#[must_use]
pub fn group_count(&self) -> usize {
[&self.start, &self.center, &self.end]
.into_iter()
@@ -592,6 +610,7 @@ impl Panel {
}
/// Appearance changes do not replace item ownership or measured allocation.
#[must_use]
pub fn same_composition(&self, other: &Self) -> bool {
let same_zone = |left: &Zone, right: &Zone| {
left.spacing == right.spacing
@@ -615,12 +634,14 @@ impl Panel {
&& same_zone(&self.end, &other.end)
}
#[must_use]
pub fn resolved_radius(&self, shell_radius: f32) -> CornerRadii {
self.corner_radius
.unwrap_or_else(|| CornerRadii::uniform(shell_radius))
}
/// The overflow trigger is generated, never persisted as a configured item.
#[must_use]
pub fn overflow_group(&self) -> Group {
Group {
id: GroupId("_overflow".into()),
@@ -646,6 +667,7 @@ impl Panel {
}
}
#[must_use]
pub fn item(&self, id: &ItemId) -> Option<&Item> {
[&self.start, &self.center, &self.end]
.into_iter()
@@ -655,6 +677,7 @@ impl Panel {
}
/// Build a stable initial composition; presets are an editor convenience.
#[must_use]
pub fn from_preset(preset: PanelStyle) -> Self {
use ItemKind::*;
@@ -16,6 +16,7 @@ pub struct GeometryIntent {
}
impl GeometryIntent {
#[must_use]
pub fn resolve(self, style: crate::PanelStyle) -> PanelGeometry {
use crate::PanelStyle::*;
let flush = matches!(style, Notch | Flat | Frame | CornerWrap);
+4
View File
@@ -92,12 +92,14 @@ impl Resolution {
}
}
#[must_use]
pub fn fits(&self, available: f32) -> bool {
self.required_width() <= available
}
/// Clamp a requested inset independently for each output. Content may still
/// overflow on an output too small to fit it even without an inset.
#[must_use]
pub fn side_margin(&self, requested: i32, output_width: i32, padding: f32) -> i32 {
let maximum = ((output_width as f32 - 2. * padding - self.minimum_width) / 2.)
.floor()
@@ -106,6 +108,7 @@ impl Resolution {
}
}
#[must_use]
pub fn resolve(
panel: &Panel,
measurements: &[Measurement],
@@ -116,6 +119,7 @@ pub fn resolve(
}
/// Reserve separation between zones while keeping the center on the panel midpoint.
#[must_use]
pub fn resolve_with_gap(
panel: &Panel,
measurements: &[Measurement],
+5
View File
@@ -5,6 +5,7 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer};
pub struct CornerRadii(pub [f32; 4]);
impl CornerRadii {
#[must_use]
pub fn uniform(radius: f32) -> Self {
Self([radius; 4])
}
@@ -18,6 +19,7 @@ impl CornerRadii {
}
/// A panel touching the top edge must cover its top corners.
#[must_use]
pub fn at_top_edge(mut self, touching: bool) -> Self {
if touching {
self.0[0] = 0.;
@@ -26,6 +28,7 @@ impl CornerRadii {
self
}
#[must_use]
pub fn at_edge(mut self, edge: super::Edge, touching: bool) -> Self {
if touching {
match edge {
@@ -44,6 +47,7 @@ impl CornerRadii {
}
/// Keep every corner within half the shorter surface dimension.
#[must_use]
pub fn clamped(self, width: f32, height: f32) -> Self {
let limit = width.min(height).max(0.) / 2.;
Self(self.0.map(|value| value.min(limit)))
@@ -52,6 +56,7 @@ impl CornerRadii {
/// Scale all corners together when their sums exceed an adjacent edge.
/// Unlike the symmetric round-box clamp, this permits a large free-side
/// corner when the corner on the anchored side is square.
#[must_use]
pub fn normalized(self, width: f32, height: f32) -> Self {
let [tl, tr, br, bl] = self.0;
let mut factor = 1.0_f64;
+17 -8
View File
@@ -16,6 +16,7 @@ pub struct Primitive {
}
impl Primitive {
#[must_use]
pub fn packet(self) -> [f32; 10] {
let Rect {
x,
@@ -66,6 +67,7 @@ impl Primitive {
})
}
#[must_use]
pub fn distance(self, point: [f64; 2]) -> f64 {
let rect = rectangle_distance(point, self.bounds);
if self.operation >= 2 {
@@ -103,6 +105,7 @@ fn rectangle_distance(point: [f64; 2], b: Rect) -> f64 {
x.max(0.).hypot(y.max(0.)) + x.max(y).min(0.)
}
#[must_use]
pub fn smooth_min(a: f64, b: f64, strength: f64) -> f64 {
if strength <= 0. {
return a.min(b);
@@ -141,16 +144,19 @@ impl Scene {
zones.into_iter().fold(f64::INFINITY, f64::min).max(-cutout)
}
#[must_use]
pub fn contains(&self, point: [f64; 2]) -> bool {
self.input.iter().any(|shape| shape.contains(point))
}
#[must_use]
pub fn packets(&self) -> Vec<[f32; 10]> {
self.paint.iter().map(|p| p.packet()).collect()
}
/// Horizontal runs at physical-pixel centers. Corners and animated gaps
/// stay click-through; adjacent equal runs are coalesced into one rectangle.
#[must_use]
pub fn input_rectangles(&self, scale: f64) -> Vec<[i32; 4]> {
if !scale.is_finite() || scale <= 0. {
return Vec::new();
@@ -246,7 +252,7 @@ fn add(
input: bool,
zone: u8,
) -> Result<(), &'static str> {
let rounded = RoundedSurface::new(rect, radii, grid)?;
let rounded = RoundedSurface::try_new(rect, radii, grid)?;
if rounded.bounds().width == 0. || rounded.bounds().height == 0. {
return Ok(());
}
@@ -320,7 +326,7 @@ pub fn derive(
operation: 1,
blend_zone: 0,
});
scene.input.push(RoundedSurface::new(
scene.input.push(RoundedSurface::try_new(
Rect::new(bounds.x, bounds.y, bounds.width, top),
CornerRadii::uniform(0.),
grid,
@@ -583,8 +589,11 @@ 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::try_new(
mirror(input.bounds()),
CornerRadii([bl, br, tr, tl]),
grid,
)?;
}
for damage in &mut scene.damage {
*damage = mirror(*damage);
@@ -624,7 +633,7 @@ mod tests {
},
Rect::new(0., 0., 800., 36.),
&groups(),
PixelGrid::new(1.25).unwrap(),
PixelGrid::try_new(1.25).unwrap(),
)
.unwrap()
}
@@ -677,7 +686,7 @@ mod tests {
},
Rect::new(0., 0., 800., 600.),
&[],
PixelGrid::new(1.25).unwrap(),
PixelGrid::try_new(1.25).unwrap(),
)
.unwrap();
assert!(scene.contains([18., 200.]));
@@ -714,7 +723,7 @@ mod tests {
},
Rect::new(0., 0., 1600., 1000.),
&[],
PixelGrid::new(1.5).unwrap(),
PixelGrid::try_new(1.5).unwrap(),
)
.unwrap();
assert!(scene.distance([800., 20.]) < 0. && scene.distance([800., 500.]) > 0.);
@@ -746,7 +755,7 @@ mod tests {
fn attached_styles_mirror_the_whole_outline_at_the_bottom() {
for style in [Style::Notch, Style::Frame, Style::CornerWrap] {
let bounds = Rect::new(0., 0., 800., if style == Style::Notch { 36. } else { 600. });
let grid = PixelGrid::new(1.).unwrap();
let grid = PixelGrid::try_new(1.).unwrap();
let top = derive(
Parameters {
style,
+28 -15
View File
@@ -43,6 +43,7 @@ impl Style {
Self::WindowHug,
];
#[must_use]
pub const fn key(self) -> &'static str {
match self {
Self::Floating => "floating",
@@ -57,6 +58,7 @@ impl Style {
Self::WindowHug => "window-hug",
}
}
#[must_use]
pub const fn label(self) -> &'static str {
match self {
Self::Floating => "Floating",
@@ -71,9 +73,11 @@ impl Style {
Self::WindowHug => "Window hug",
}
}
#[must_use]
pub const fn connected(self) -> bool {
matches!(self, Self::Capsules | Self::Notch)
}
#[must_use]
pub const fn detached(self) -> bool {
matches!(
self,
@@ -100,6 +104,7 @@ pub enum ZonePosition {
pub struct AnchoredRadii(pub [f32; 4]);
impl AnchoredRadii {
#[must_use]
pub fn screen_space(self, edge: Edge) -> CornerRadii {
let [flat_left, flat_right, free_right, free_left] = self.0;
CornerRadii(match edge {
@@ -173,13 +178,14 @@ pub struct PixelGrid {
}
impl PixelGrid {
pub fn new(scale: f64) -> Result<Self, &'static str> {
pub fn try_new(scale: f64) -> Result<Self, &'static str> {
if !scale.is_finite() || scale <= 0. {
return Err("output scale must be finite and positive");
}
Ok(Self { scale })
}
#[must_use]
pub fn snap(self, position: f64) -> f64 {
(position * self.scale).round() / self.scale
}
@@ -218,7 +224,11 @@ pub struct RoundedSurface {
}
impl RoundedSurface {
pub fn new(bounds: Rect, radii: CornerRadii, grid: PixelGrid) -> Result<Self, &'static str> {
pub fn try_new(
bounds: Rect,
radii: CornerRadii,
grid: PixelGrid,
) -> Result<Self, &'static str> {
if radii.validate().is_err() {
return Err("surface radii must be finite and nonnegative");
}
@@ -230,16 +240,19 @@ impl RoundedSurface {
Ok(Self { bounds, radii })
}
#[must_use]
pub fn bounds(self) -> Rect {
self.bounds
}
#[must_use]
pub fn radii(self) -> CornerRadii {
self.radii
}
/// Right and bottom boundaries are excluded so a shared seam belongs to
/// exactly one surface, rather than both neighbors.
#[must_use]
pub fn contains(self, point: [f64; 2]) -> bool {
let [x, y] = [point[0] - self.bounds.x, point[1] - self.bounds.y];
let (width, height) = (self.bounds.width, self.bounds.height);
@@ -339,7 +352,7 @@ mod tests {
#[test]
fn shared_boundaries_snap_without_seams_at_fractional_scales() {
for scale in [1., 1.25, 1.5, 2.] {
let grid = PixelGrid::new(scale).unwrap();
let grid = PixelGrid::try_new(scale).unwrap();
for origin in [-1800.3, -0.4, 0., 23.3] {
let left = grid.rect(Rect::new(origin, 0., 33.3, 36.)).unwrap();
let right = grid.rect(Rect::new(origin + 33.3, 0., 42.7, 36.)).unwrap();
@@ -353,14 +366,14 @@ mod tests {
#[test]
fn floating_gaps_and_round_corners_do_not_capture_input() {
let grid = PixelGrid::new(1.25).unwrap();
let left = RoundedSurface::new(
let grid = PixelGrid::try_new(1.25).unwrap();
let left = RoundedSurface::try_new(
Rect::new(8., 8., 100., 36.),
CornerRadii::uniform(18.),
grid,
)
.unwrap();
let right = RoundedSurface::new(
let right = RoundedSurface::try_new(
Rect::new(116., 8., 100., 36.),
CornerRadii::uniform(18.),
grid,
@@ -381,15 +394,15 @@ mod tests {
#[test]
fn asymmetric_hit_testing_matches_a_vertically_mirrored_surface() {
let grid = PixelGrid::new(1.).unwrap();
let grid = PixelGrid::try_new(1.).unwrap();
let bounds = Rect::new(0., 0., 100., 28.);
let top = RoundedSurface::new(
let top = RoundedSurface::try_new(
bounds,
AnchoredRadii([0., 0., 20., 20.]).screen_space(Edge::Top),
grid,
)
.unwrap();
let bottom = RoundedSurface::new(
let bottom = RoundedSurface::try_new(
bounds,
AnchoredRadii([0., 0., 20., 20.]).screen_space(Edge::Bottom),
grid,
@@ -409,14 +422,14 @@ mod tests {
#[test]
fn a_connected_seam_has_exactly_one_input_owner() {
let grid = PixelGrid::new(1.5).unwrap();
let left = RoundedSurface::new(
let grid = PixelGrid::try_new(1.5).unwrap();
let left = RoundedSurface::try_new(
Rect::new(0., 0., 30., 36.),
CornerRadii([18., 0., 0., 18.]),
grid,
)
.unwrap();
let right = RoundedSurface::new(
let right = RoundedSurface::try_new(
Rect::new(30., 0., 40., 36.),
CornerRadii([0., 18., 18., 0.]),
grid,
@@ -431,9 +444,9 @@ mod tests {
#[test]
fn invalid_runtime_geometry_is_rejected() {
for scale in [0., -1., f64::NAN, f64::INFINITY] {
assert!(PixelGrid::new(scale).is_err());
assert!(PixelGrid::try_new(scale).is_err());
}
let grid = PixelGrid::new(1.).unwrap();
let grid = PixelGrid::try_new(1.).unwrap();
for bounds in [
Rect::new(0., 0., -1., 28.),
Rect::new(f64::NAN, 0., 10., 28.),
@@ -442,7 +455,7 @@ mod tests {
assert!(grid.rect(bounds).is_err());
}
assert!(
RoundedSurface::new(Rect::new(0., 0., 10., 28.), CornerRadii::uniform(-1.), grid)
RoundedSurface::try_new(Rect::new(0., 0., 10., 28.), CornerRadii::uniform(-1.), grid)
.is_err()
);
assert!(