From 8e4ba05a8d8e25521a1b7c0613b4d19f6ed6697f Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Tue, 21 Jul 2026 16:19:10 +0000 Subject: [PATCH 1/2] Update easy-cast --- Cargo.toml | 2 +- crates/kas-core/Cargo.toml | 2 +- crates/kas-core/src/draw/color.rs | 6 +- crates/kas-core/src/event/components.rs | 8 +- crates/kas-core/src/event/cx/press.rs | 4 +- crates/kas-core/src/event/cx/press/mouse.rs | 8 +- crates/kas-core/src/event/cx/press/touch.rs | 12 +- crates/kas-core/src/geom.rs | 200 +++++++++++++------ crates/kas-core/src/geom/vector.rs | 83 ++++---- crates/kas-core/src/layout/size_types.rs | 34 ++-- crates/kas-core/src/text/forme.rs | 8 +- crates/kas-core/src/text/raster.rs | 10 +- crates/kas-core/src/theme/dimensions.rs | 56 +++--- crates/kas-core/src/theme/flat_theme.rs | 6 +- crates/kas-core/src/theme/size.rs | 2 +- crates/kas-core/src/theme/traits.rs | 2 +- crates/kas-image/src/lib.rs | 6 +- crates/kas-image/src/sprite.rs | 3 +- crates/kas-soft/src/atlas.rs | 26 +-- crates/kas-soft/src/basic.rs | 10 +- crates/kas-wgpu/src/shaded_theme.rs | 6 +- crates/kas-widgets/src/adapt/adapt_widget.rs | 6 +- crates/kas-widgets/src/check_box.rs | 5 +- crates/kas-widgets/src/edit/edit_field.rs | 9 +- crates/kas-widgets/src/edit/editor.rs | 26 +-- crates/kas-widgets/src/scroll_label.rs | 13 +- crates/kas-widgets/src/slider.rs | 5 +- 27 files changed, 325 insertions(+), 233 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index cbbd299a9..f4e1aa247 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -175,4 +175,4 @@ resolver = "2" [patch.crates-io.kas-text] git = "https://github.com/kas-gui/kas-text.git" -rev = "efe15a6040edde83b64cb668c64da63b164abfd7" +rev = "9554604bbaa40af58114126496a2c20e1553c468" diff --git a/crates/kas-core/Cargo.toml b/crates/kas-core/Cargo.toml index 0ee94b6b4..272a4803b 100644 --- a/crates/kas-core/Cargo.toml +++ b/crates/kas-core/Cargo.toml @@ -115,7 +115,7 @@ ab_glyph = { version = "0.2.10", optional = true } swash = { version = "0.2.4", features = ["scale"] } linearize = { version = "0.1.5", features = ["derive"] } kas-text = "0.9.0" -easy-cast = "0.6.1" +easy-cast = "0.7.0" pulldown-cmark = { version = "0.13.0", optional = true } [dependencies.kas-macros] diff --git a/crates/kas-core/src/draw/color.rs b/crates/kas-core/src/draw/color.rs index ab6984809..075fae6db 100644 --- a/crates/kas-core/src/draw/color.rs +++ b/crates/kas-core/src/draw/color.rs @@ -9,7 +9,7 @@ use std::ops::Add; -use crate::cast::{Conv, ConvFloat}; +use crate::cast::{Conv, ConvTo, Nearest}; use thiserror::Error; /// 4-part colour data, linear, sRGB colour space @@ -561,7 +561,7 @@ fn from_linear(x: f32) -> u8 { } else { x.powf(1.0 / 2.4) * 1.055 - 0.055 }; - u8::conv_nearest(x * 255.0) + u8::conv_to(Nearest, x * 255.0) } impl From for Rgba { @@ -592,7 +592,7 @@ impl From for Rgba8Srgb { from_linear(c.r), from_linear(c.g), from_linear(c.b), - u8::conv_nearest(c.a * 255.0), + u8::conv_to(Nearest, c.a * 255.0), ]) } } diff --git a/crates/kas-core/src/event/components.rs b/crates/kas-core/src/event/components.rs index 74b2d6623..9821da316 100644 --- a/crates/kas-core/src/event/components.rs +++ b/crates/kas-core/src/event/components.rs @@ -6,7 +6,7 @@ //! Event handling components use super::*; -use crate::cast::traits::*; +use crate::cast::{Nearest, Trunc, traits::*}; use crate::geom::{Coord, Offset, Rect, Size, Vec2}; use crate::text::CursorRange; use crate::{ActionMoved, Id}; @@ -87,7 +87,7 @@ impl Kinetic { pub fn start(&mut self, start: KineticStart) -> Offset { self.vel += start.vel; let d = self.rest + start.rest; - let delta = Offset::conv_trunc(d); + let delta = Offset::conv_to(Trunc, d); self.rest = d - Vec2::conv(delta); self.t_step = Instant::now(); delta @@ -120,7 +120,7 @@ impl Kinetic { } let d = self.vel * dur + self.rest; - let delta = Offset::conv_trunc(d); + let delta = Offset::conv_to(Trunc, d); self.rest = d - Vec2::conv(delta); Some(delta) @@ -308,7 +308,7 @@ impl ScrollComponent { fn scroll_by_delta(&mut self, cx: &mut EventCx, d: Vec2) { let delta = d + self.kinetic.rest; - let offset = delta.cast_nearest(); + let offset = delta.cast_to(Nearest); self.kinetic.rest = delta - Vec2::conv(offset); let delta = self.scroll_self_by_delta(cx, offset); cx.set_scroll(if delta != Offset::ZERO { diff --git a/crates/kas-core/src/event/cx/press.rs b/crates/kas-core/src/event/cx/press.rs index b20403727..7d109dee9 100644 --- a/crates/kas-core/src/event/cx/press.rs +++ b/crates/kas-core/src/event/cx/press.rs @@ -13,9 +13,9 @@ pub(crate) mod velocity; use super::{EventCx, IsUsed}; #[allow(unused)] use crate::Events; // for doc-links use crate::Id; +use crate::cast::{Approx, Cast, CastTo, Conv}; use crate::event::{CursorIcon, MouseButton, Unused, Used}; use crate::geom::{Coord, DVec2, Offset, Vec2}; -use cast::{Cast, CastApprox, Conv}; pub(crate) use mouse::Mouse; pub(crate) use touch::Touch; use winit::event::FingerId; @@ -191,7 +191,7 @@ impl PressStart { /// Get the current press coordinate #[inline] pub fn coord(&self) -> Coord { - self.position.cast_approx() + self.position.cast_to(Approx) } /// Grab pan/move/press-end events for widget `id` diff --git a/crates/kas-core/src/event/cx/press/mouse.rs b/crates/kas-core/src/event/cx/press/mouse.rs index 61f5804f5..728c0ad0a 100644 --- a/crates/kas-core/src/event/cx/press/mouse.rs +++ b/crates/kas-core/src/event/cx/press/mouse.rs @@ -13,7 +13,7 @@ use crate::geom::{Affine, Coord, DVec2, Vec2}; use crate::window::WindowErased; use crate::window::WindowWidget; use crate::{ActionRedraw, Id, Node, Tile, TileExt}; -use cast::{CastApprox, CastFloat}; +use cast::{CastApprox, CastTo, Nearest}; use std::time::{Duration, Instant}; use winit::cursor::CursorIcon; use winit::event::{ElementState, MouseButton, MouseScrollDelta}; @@ -330,7 +330,7 @@ impl<'a> EventCx<'a> { let press = Press { source: PressSource::mouse(Some(grab.button), grab.repetitions), id: self.mouse.over.clone(), - coord: self.mouse.last_position.cast_nearest(), + coord: self.mouse.last_position.cast_to(Nearest), }; let event = Event::PressEnd { press, success }; to_send = Some((grab.start_id.clone(), event)); @@ -357,7 +357,7 @@ impl<'a> EventCx<'a> { self.action_redraw(redraw); if self.action_moved.is_some() { - let over = node.try_probe(self.mouse.last_position.cast_nearest()); + let over = node.try_probe(self.mouse.last_position.cast_to(Nearest)); self.set_over(node, over); } } @@ -369,7 +369,7 @@ impl<'a> EventCx<'a> { data: &A, position: DVec2, ) { - let coord = position.cast_nearest(); + let coord = position.cast_to(Nearest); let id = win.try_probe(coord); self.tooltip_motion(win, &id); self.handle_pointer_moved_(id, win.as_node(data), coord, position); diff --git a/crates/kas-core/src/event/cx/press/touch.rs b/crates/kas-core/src/event/cx/press/touch.rs index 91d773a2b..0216ad990 100644 --- a/crates/kas-core/src/event/cx/press/touch.rs +++ b/crates/kas-core/src/event/cx/press/touch.rs @@ -10,7 +10,7 @@ use crate::config::EventWindowConfig; use crate::event::{Event, EventCx, EventState, FocusSource, NavAdvance, PressStart}; use crate::geom::{Affine, DVec2, Vec2}; use crate::{ActionRedraw, Id, Node}; -use cast::{Cast, CastApprox, CastFloat, Conv}; +use cast::{Cast, CastApprox, CastTo, Conv, Nearest}; use smallvec::SmallVec; use winit::event::FingerId; @@ -259,7 +259,7 @@ impl<'a> EventCx<'a> { let press = Press { source: PressSource::touch(grab.finger_id), id: grab.over, - coord: grab.last_position.cast_nearest(), + coord: grab.last_position.cast_to(Nearest), }; let event = Event::PressEnd { press, @@ -273,7 +273,7 @@ impl<'a> EventCx<'a> { if self.action_moved.is_some() { for grab in self.touch.touch_grab.iter_mut() { - grab.over = node.try_probe(grab.last_position.cast_nearest()); + grab.over = node.try_probe(grab.last_position.cast_to(Nearest)); } } } @@ -310,7 +310,7 @@ impl<'a> EventCx<'a> { finger_id: FingerId, position: DVec2, ) { - let coord = position.cast_nearest(); + let coord = position.cast_to(Nearest); let over = node.try_probe(coord); self.close_non_ancestors_of(over.as_ref()); @@ -337,7 +337,7 @@ impl<'a> EventCx<'a> { finger_id: FingerId, position: DVec2, ) { - let coord = position.cast_nearest(); + let coord = position.cast_to(Nearest); let over = node.try_probe(coord); let mut pan_grab = None; @@ -402,7 +402,7 @@ impl<'a> EventCx<'a> { let source = PressSource::touch(finger_id); let id = grab.over.clone(); let success = id.is_some(); - let coord = position.cast_nearest(); + let coord = position.cast_to(Nearest); let press = Press { source, id, coord }; let event = Event::PressEnd { press, success }; diff --git a/crates/kas-core/src/geom.rs b/crates/kas-core/src/geom.rs index da1057de2..f47158018 100644 --- a/crates/kas-core/src/geom.rs +++ b/crates/kas-core/src/geom.rs @@ -13,12 +13,13 @@ //! //! Conversions types mostly use [`Cast`] and [`Conv`]. [`From`] may be used to //! simply pack/unpack components. To convert from floating-point types to -//! integer types, use [`CastApprox`] or [`CastFloat`] to specify the rounding +//! integer types, use [`CastApprox`] or [`CastTo`] to specify the rounding //! mode. use crate::cast::*; use crate::dir::Directional; use std::cmp::{Ordering, PartialOrd}; +use std::convert::Infallible; mod transform; mod vector; @@ -259,10 +260,12 @@ impl std::ops::SubAssign for Coord { } } -impl Conv for kas_text::Vec2 { +impl ConvTo for kas_text::Vec2 { + type Error = R::MaximumError; + #[inline] - fn try_conv(pos: Coord) -> Result { - Ok(Vec2::try_conv(pos)?.into()) + fn try_conv_to(mode: R, pos: Coord) -> Result { + pos.try_cast_to(mode) } } @@ -326,17 +329,19 @@ impl Size { } } -impl Conv<(i32, i32)> for Size { - fn try_conv(v: (i32, i32)) -> Result { +impl ConvExact<(i32, i32)> for Size { + type Error = RangeError; + + fn try_conv_exact(v: (i32, i32)) -> Result { if v.0 >= 0 && v.1 >= 0 { Ok(Size(v.0, v.1)) } else { - Err(Error::Range) + Err(RangeError) } } #[inline] - fn conv(v: (i32, i32)) -> Self { + fn conv_exact(v: (i32, i32)) -> Self { Self::new(v.0, v.1) } } @@ -405,9 +410,11 @@ impl std::ops::Div for Size { } /// Convert an [`Offset`] into a [`Coord`] -impl Conv for Coord { +impl ConvExact for Coord { + type Error = Infallible; + #[inline] - fn try_conv(v: Offset) -> Result { + fn try_conv_exact(v: Offset) -> Result { Ok(Self(v.0, v.1)) } } @@ -415,46 +422,79 @@ impl Conv for Coord { /// Convert an [`Offset`] into a [`Size`] /// /// In debug mode this asserts that the result is non-negative. -impl Conv for Size { +impl ConvExact for Size { + type Error = RangeError; + #[inline] - fn try_conv(v: Offset) -> Result { - debug_assert!(v.0 >= 0 && v.1 >= 0, "Size::conv({v:?}): negative value"); - Ok(Self(v.0, v.1)) + fn try_conv_exact(v: Offset) -> Result { + if v.0 >= 0 && v.1 >= 0 { + Ok(Self(v.0, v.1)) + } else { + Err(RangeError) + } + } + + #[inline] + fn conv_exact(v: Offset) -> Self { + debug_assert!( + v.0 >= 0 && v.1 >= 0, + "ConvExact::conv_exact({v:?}): negative value" + ); + Self(v.0, v.1) } } // used for marigns -impl Conv for (u16, u16) { +impl ConvExact for (u16, u16) { + type Error = RangeError; + #[inline] - fn try_conv(size: Size) -> Result { + fn try_conv_exact(size: Size) -> Result { Ok((size.0.try_cast()?, size.1.try_cast()?)) } + + #[inline] + fn conv_exact(size: Size) -> Self { + (size.0.cast(), size.1.cast()) + } } -impl Conv<(u16, u16)> for Size { +impl ConvExact<(u16, u16)> for Size { + type Error = Infallible; + #[inline] - fn try_conv(v: (u16, u16)) -> Result { - Ok(Self(i32::try_conv(v.0)?, i32::try_conv(v.1)?)) + fn try_conv_exact(v: (u16, u16)) -> Result { + Ok(Self(v.0.try_cast()?, v.1.try_cast()?)) } } -impl Conv<(u32, u32)> for Size { +impl ConvExact<(u32, u32)> for Size { + type Error = RangeError; + #[inline] - fn try_conv(v: (u32, u32)) -> Result { - Ok(Self(i32::try_conv(v.0)?, i32::try_conv(v.1)?)) + fn try_conv_exact(v: (u32, u32)) -> Result { + Ok(Self(v.0.try_cast()?, v.1.try_cast()?)) } } -impl Conv for (u32, u32) { +impl ConvExact for (u32, u32) { + type Error = RangeError; + #[inline] - fn try_conv(size: Size) -> Result { - Ok((u32::try_conv(size.0)?, u32::try_conv(size.1)?)) + fn try_conv_exact(size: Size) -> Result { + Ok((size.0.try_cast()?, size.1.try_cast()?)) } } -impl Conv for kas_text::Vec2 { +impl ConvTo for kas_text::Vec2 +where + f32: ConvTo, +{ + type Error = >::Error; + #[inline] - fn try_conv(size: Size) -> Result { - Ok(Vec2::try_conv(size)?.into()) + fn try_conv_to(mode: R, size: Size) -> Result { + let v: Vec2 = size.try_cast_to(mode)?; + Ok(v.into()) } } @@ -555,24 +595,34 @@ impl std::ops::Div for Offset { } } -impl Conv for Offset { +impl ConvExact for Offset { + type Error = Infallible; + #[inline] - fn try_conv(v: Coord) -> Result { + fn try_conv_exact(v: Coord) -> Result { Ok(Self(v.0, v.1)) } } -impl Conv for Offset { +impl ConvExact for Offset { + type Error = Infallible; + #[inline] - fn try_conv(v: Size) -> Result { + fn try_conv_exact(v: Size) -> Result { Ok(Self(v.0, v.1)) } } -impl Conv for kas_text::Vec2 { +impl ConvTo for kas_text::Vec2 +where + f32: ConvTo, +{ + type Error = >::Error; + #[inline] - fn try_conv(v: Offset) -> Result { - Ok(Vec2::try_conv(v)?.into()) + fn try_conv_to(mode: R, v: Offset) -> Result { + let v: Vec2 = v.try_cast_to(mode)?; + Ok(v.into()) } } @@ -689,16 +739,34 @@ impl std::ops::SubAssign for Rect { #[cfg(feature = "accesskit")] mod accesskit_impls { use super::{Coord, Offset, Rect}; - use crate::cast::{Cast, CastApprox, Conv, ConvApprox, Result}; + use cast::{ConvExact, ConvTo, ConvertInto, Rounding}; + + impl ConvTo for Coord + where + i32: ConvTo, + { + type Error = >::Error; + + fn try_conv_to(mode: R, rect: accesskit::Rect) -> Result { + Ok(Coord(p.x.try_cast_to(mode)?, p.y.try_cast_to(mode)?)) + } + } + + impl ConvTo for Offset + where + i32: ConvTo, + { + type Error = >::Error; - impl ConvApprox for Coord { - fn try_conv_approx(p: accesskit::Point) -> Result { - Ok(Coord(p.x.try_cast_approx()?, p.y.try_cast_approx()?)) + fn try_conv_to(mode: R, rect: accesskit::Rect) -> Result { + Ok(Offset(p.x.try_cast_to(mode)?, p.y.try_cast_to(mode)?)) } } - impl Conv for accesskit::Rect { - fn try_conv(rect: Rect) -> Result { + impl ConvExact for accesskit::Rect { + type Error = Infallible; + + fn try_convert(rect: Rect) -> Result { let p = rect.pos; let p2 = rect.pos2(); Ok(accesskit::Rect { @@ -710,41 +778,51 @@ mod accesskit_impls { } } - impl ConvApprox for Rect { - fn try_conv_approx(rect: accesskit::Rect) -> Result { - let pos = Coord(rect.x0.try_cast_approx()?, rect.y0.try_cast_approx()?); - let p2 = Coord(rect.x1.try_cast_approx()?, rect.y1.try_cast_approx()?); + impl ConvTo for Rect + where + i32: ConvTo, + { + type Error = >::Error; + + fn try_conv_to(rect: accesskit::Rect) -> Result { + let mode = R::default(); + let pos = Coord(rect.x0.try_convert(mode)?, rect.y0.try_convert(mode)?); + let p2 = Coord(rect.x1.try_convert(mode)?, rect.y1.try_convert(mode)?); let size = (p2 - pos).cast(); Ok(Rect { pos, size }) } } - - impl ConvApprox for Offset { - fn try_conv_approx(point: accesskit::Point) -> Result { - Ok(Offset( - point.x.try_cast_approx()?, - point.y.try_cast_approx()?, - )) - } - } } mod winit_impls { use super::{Coord, Size}; - use crate::cast::{Cast, CastApprox, Conv, ConvApprox, Result}; + use cast::{Cast, CastTo, ConvTo, Rounding}; use winit::dpi::{PhysicalPosition, PhysicalSize}; - impl> ConvApprox> for Coord { + impl ConvTo, R> for Coord + where + i32: ConvTo, + { + type Error = >::Error; + #[inline] - fn try_conv_approx(pos: PhysicalPosition) -> Result { - Ok(Coord(pos.x.try_cast_approx()?, pos.y.try_cast_approx()?)) + fn try_conv_to(mode: R, pos: PhysicalPosition) -> Result { + Ok(Coord(pos.x.try_cast_to(mode)?, pos.y.try_cast_to(mode)?)) } } - impl> Conv> for Size { + impl ConvTo, R> for Size + where + i32: ConvTo, + { + type Error = >::Error; + #[inline] - fn try_conv(size: PhysicalSize) -> Result { - Ok(Size(size.width.cast(), size.height.cast())) + fn try_conv_to(mode: R, size: PhysicalSize) -> Result { + Ok(Size( + size.width.try_cast_to(mode)?, + size.height.try_cast_to(mode)?, + )) } } diff --git a/crates/kas-core/src/geom/vector.rs b/crates/kas-core/src/geom/vector.rs index b450420b2..e4e4fa933 100644 --- a/crates/kas-core/src/geom/vector.rs +++ b/crates/kas-core/src/geom/vector.rs @@ -7,7 +7,7 @@ //! //! For drawing operations, all dimensions use the `f32` type. -use crate::cast::*; +use crate::cast::{CastTo, Conv, ConvTo, Rounding, impl_via_from}; use crate::dir::Directional; use crate::geom::{Coord, Offset, Rect, Size}; use std::cmp::{Ordering, PartialOrd}; @@ -152,11 +152,17 @@ impl Sub for Quad { } } -impl Conv for Quad { +impl ConvTo for Quad +where + f32: ConvTo, +{ + type Error = >::Error; + #[inline] - fn try_conv(rect: Rect) -> Result { - let a = Vec2::try_conv(rect.pos)?; - let b = a + Vec2::try_conv(rect.size)?; + fn try_conv_to(mode: R, rect: Rect) -> Result { + let a = rect.pos.try_cast_to(mode)?; + let size: Vec2 = rect.size.try_cast_to(mode)?; + let b = a + size; Ok(Quad { a, b }) } } @@ -641,9 +647,14 @@ impl From for DVec2 { } } impl_via_from!(Vec2: kas_text::Vec2, DVec2); -impl ConvApprox for Vec2 { - fn try_conv_approx(size: DVec2) -> Result { - Ok(Vec2(size.0.try_cast_approx()?, size.1.try_cast_approx()?)) +impl ConvTo for Vec2 +where + f32: ConvTo, +{ + type Error = >::Error; + + fn try_conv_to(mode: R, size: DVec2) -> Result { + Ok(Vec2(size.0.try_cast_to(mode)?, size.1.try_cast_to(mode)?)) } } @@ -651,51 +662,39 @@ impl_vec2!(Vec2, f32); impl_vec2!(DVec2, f64); macro_rules! impl_conv_vec2 { - ($S:ty, $T:ty) => { - impl Conv<$S> for $T { - #[inline] - fn try_conv(arg: $S) -> Result { - Ok(Self(arg.0.try_cast()?, arg.1.try_cast()?)) - } - } + ($S:ty, $T:ty, $t:ty) => { + impl ConvTo<$S, R> for $T + where + $t: ConvTo, + { + type Error = <$t as ConvTo>::Error; - impl ConvApprox<$T> for $S { #[inline] - fn try_conv_approx(arg: $T) -> Result { - Ok(Self(arg.0.try_cast_approx()?, arg.1.try_cast_approx()?)) + fn try_conv_to(mode: R, arg: $S) -> Result { + Ok(Self(arg.0.try_cast_to(mode)?, arg.1.try_cast_to(mode)?)) } } - impl ConvFloat<$T> for $S { - #[inline] - fn try_conv_trunc(x: $T) -> Result { - Ok(Self(i32::try_conv_trunc(x.0)?, i32::try_conv_trunc(x.1)?)) - } - #[inline] - fn try_conv_nearest(x: $T) -> Result { - Ok(Self( - i32::try_conv_nearest(x.0)?, - i32::try_conv_nearest(x.1)?, - )) - } - #[inline] - fn try_conv_floor(x: $T) -> Result { - Ok(Self(i32::try_conv_floor(x.0)?, i32::try_conv_floor(x.1)?)) - } + impl ConvTo<$T, R> for $S + where + i32: ConvTo<$t, R>, + { + type Error = >::Error; + #[inline] - fn try_conv_ceil(x: $T) -> Result { - Ok(Self(i32::try_conv_ceil(x.0)?, i32::try_conv_ceil(x.1)?)) + fn try_conv_to(mode: R, arg: $T) -> Result { + Ok(Self(arg.0.try_cast_to(mode)?, arg.1.try_cast_to(mode)?)) } } }; } -impl_conv_vec2!(Coord, Vec2); -impl_conv_vec2!(Size, Vec2); -impl_conv_vec2!(Offset, Vec2); -impl_conv_vec2!(Coord, DVec2); -impl_conv_vec2!(Size, DVec2); -impl_conv_vec2!(Offset, DVec2); +impl_conv_vec2!(Coord, Vec2, f32); +impl_conv_vec2!(Size, Vec2, f32); +impl_conv_vec2!(Offset, Vec2, f32); +impl_conv_vec2!(Coord, DVec2, f64); +impl_conv_vec2!(Size, DVec2, f64); +impl_conv_vec2!(Offset, DVec2, f64); /// 3D vector /// diff --git a/crates/kas-core/src/layout/size_types.rs b/crates/kas-core/src/layout/size_types.rs index 890714416..5af8490e7 100644 --- a/crates/kas-core/src/layout/size_types.rs +++ b/crates/kas-core/src/layout/size_types.rs @@ -6,9 +6,9 @@ //! Types used by size rules use super::SizeRules; -use crate::cast::*; use crate::dir::Directional; use crate::geom::{Size, Vec2}; +use cast::{Cast, CastTo, Ceil, Conv, ConvTo, Nearest, Rounding}; // for doc use #[allow(unused)] use crate::theme::SizeCx; @@ -26,8 +26,8 @@ impl LogicalSize { /// /// Values are multiplied by the window's scale factor and cast to nearest. pub fn to_physical(self, scale_factor: f32) -> Size { - let w = i32::conv_nearest(self.0 * scale_factor); - let h = i32::conv_nearest(self.1 * scale_factor); + let w = i32::conv_to(Nearest, self.0 * scale_factor); + let h = i32::conv_to(Nearest, self.1 * scale_factor); Size(w, h) } @@ -47,7 +47,7 @@ impl LogicalSize { /// Take component and scale pub fn extract_scaled(self, dir: impl Directional, scale_factor: f32) -> i32 { - (self.extract(dir) * scale_factor).cast_nearest() + (self.extract(dir) * scale_factor).cast_to(Nearest) } } @@ -58,17 +58,27 @@ impl From<(f32, f32)> for LogicalSize { } } -impl Conv<(i32, i32)> for LogicalSize { +impl ConvTo<(i32, i32), R> for LogicalSize +where + f32: ConvTo, +{ + type Error = >::Error; + #[inline] - fn try_conv((w, h): (i32, i32)) -> Result { - Ok(LogicalSize(w.try_cast()?, h.try_cast()?)) + fn try_conv_to(mode: R, (w, h): (i32, i32)) -> Result { + Ok(LogicalSize(w.try_cast_to(mode)?, h.try_cast_to(mode)?)) } } -impl Conv<(u32, u32)> for LogicalSize { +impl ConvTo<(u32, u32), R> for LogicalSize +where + f32: ConvTo, +{ + type Error = >::Error; + #[inline] - fn try_conv((w, h): (u32, u32)) -> Result { - Ok(LogicalSize(w.try_cast()?, h.try_cast()?)) + fn try_conv_to(mode: R, (w, h): (u32, u32)) -> Result { + Ok(LogicalSize(w.try_cast_to(mode)?, h.try_cast_to(mode)?)) } } @@ -237,8 +247,8 @@ impl LogicalBuilder { let min = self.size.extract(axis) * self.scale_factor; let ideal = min * self.ideal_factor; let margin = self.margin * self.scale_factor; - SizeRules::new(min.cast_ceil(), ideal.cast_ceil(), self.stretch) - .with_margin(margin.cast_nearest()) + SizeRules::new(min.cast_to(Ceil), ideal.cast_to(Ceil), self.stretch) + .with_margin(margin.cast_to(Nearest)) } } diff --git a/crates/kas-core/src/text/forme.rs b/crates/kas-core/src/text/forme.rs index cb2502aa2..6a598ae53 100644 --- a/crates/kas-core/src/text/forme.rs +++ b/crates/kas-core/src/text/forme.rs @@ -6,7 +6,7 @@ //! Theme-applied Text element use crate::Layout; -use crate::cast::{Cast, CastFloat}; +use crate::cast::{Cast, CastTo, Ceil}; use crate::geom::{Rect, Vec2}; use crate::layout::{AlignHints, AxisInfo, SizeRules, Stretch}; use crate::text::fonts::FontSelector; @@ -62,13 +62,13 @@ impl Layout for ConfiguredForme { let (min, ideal) = cx.wrapped_line_len(self.class(), self.font_size()); let bound: i32 = self .measure_width(ideal.cast()) - .map(|b| b.cast_ceil()) + .map(|b| b.cast_to(Ceil)) .unwrap_or_default(); SizeRules::new(bound.min(min), bound.min(ideal), Stretch::Filler) } else { let bound: i32 = self .measure_width(f32::INFINITY) - .map(|b| b.cast_ceil()) + .map(|b| b.cast_to(Ceil)) .unwrap_or_default(); SizeRules::new(bound, bound, Stretch::Filler) } @@ -80,7 +80,7 @@ impl Layout for ConfiguredForme { .unwrap_or(f32::INFINITY); let bound: i32 = self .measure_height(wrap_width, None) - .map(|b| b.cast_ceil()) + .map(|b| b.cast_to(Ceil)) .unwrap_or_default(); SizeRules::new(bound, bound, Stretch::Filler) }; diff --git a/crates/kas-core/src/text/raster.rs b/crates/kas-core/src/text/raster.rs index a09313c12..3254f24be 100644 --- a/crates/kas-core/src/text/raster.rs +++ b/crates/kas-core/src/text/raster.rs @@ -13,7 +13,7 @@ use crate::config::SubpixelMode; use crate::text::format::{Color, Colors, Decoration, DecorationType, LineStyle}; use crate::theme::ColorsLinear; -use kas::cast::traits::*; +use kas::cast::{Trunc, traits::*}; use kas::config::RasterConfig; use kas::draw::{AllocError, Allocation, PassId, color::Rgba}; use kas::geom::{Quad, Vec2}; @@ -129,8 +129,8 @@ impl SpriteDescriptor { let steps = Self::sub_pixel_x_steps(config, dpem); let mult = f32::conv(steps); - let dpem = u32::conv_trunc(dpem * SCALE_STEPS + 0.5); - let x_off = u8::conv_trunc(glyph.position.0.fract() * mult) % steps; + let dpem = u32::conv_to(Trunc, dpem * SCALE_STEPS + 0.5); + let x_off = u8::conv_to(Trunc, glyph.position.0.fract() * mult) % steps; // y-offset serves little purpose since we don't support vertical text // and kas-text already rounds the v-caret to the nearest pixel. let y_off = 0; @@ -296,9 +296,9 @@ impl State { }; let bounds = outline.px_bounds(); - let offset: (i32, i32) = (bounds.min.x.cast_trunc(), bounds.min.y.cast_trunc()); + let offset: (i32, i32) = (bounds.min.x.cast_to(Trunc), bounds.min.y.cast_to(Trunc)); let size = bounds.max - bounds.min; - let size = (u32::conv_trunc(size.x), u32::conv_trunc(size.y)); + let size = (u32::conv_to(Trunc, size.x), u32::conv_trunc(size.y)); if size.0 == 0 || size.1 == 0 { // Ignore this common error self.glyphs.insert(desc, Sprite::default()); diff --git a/crates/kas-core/src/theme/dimensions.rs b/crates/kas-core/src/theme/dimensions.rs index 891245d96..5596646e8 100644 --- a/crates/kas-core/src/theme/dimensions.rs +++ b/crates/kas-core/src/theme/dimensions.rs @@ -13,7 +13,7 @@ use std::rc::Rc; use super::anim::AnimState; use super::{Feature, FrameStyle, MarginStyle, MarkStyle, TextClass, ThemeSize}; -use crate::cast::traits::*; +use crate::cast::{Ceil, Nearest, traits::*}; use crate::config::{Config, WindowConfig}; use crate::dir::Directional; use crate::geom::{Rect, Size, Vec2}; @@ -122,8 +122,8 @@ impl Dimensions { let scale = config.scale_factor(); let font = config.font(); - let text_m0 = (params.m_text.0 * scale).cast_nearest(); - let text_m1 = (params.m_text.1 * scale).cast_nearest(); + let text_m0 = (params.m_text.0 * scale).cast_to(Nearest); + let text_m1 = (params.m_text.1 * scale).cast_to(Nearest); let shadow_size = params.shadow_size * scale; let shadow_offset = shadow_size * params.shadow_rel_offset; @@ -136,24 +136,24 @@ impl Dimensions { mark_line, diagonal_mark_line: mark_line / f32::sqrt(2.0), min_line_len_em: 8.0, - m_inner: (params.m_inner * scale).cast_nearest(), - m_tiny: (params.m_tiny * scale).cast_nearest(), - m_small: (params.m_small * scale).cast_nearest(), - m_large: (params.m_large * scale).cast_nearest(), - m_huge: (params.m_huge * scale).cast_nearest(), + m_inner: (params.m_inner * scale).cast_to(Nearest), + m_tiny: (params.m_tiny * scale).cast_to(Nearest), + m_small: (params.m_small * scale).cast_to(Nearest), + m_large: (params.m_large * scale).cast_to(Nearest), + m_huge: (params.m_huge * scale).cast_to(Nearest), m_text: (text_m0, text_m1), - frame: (params.frame * scale).cast_nearest(), - frame_window: (params.frame_window * scale).cast_nearest(), - frame_popup: (params.frame_popup * scale).cast_nearest(), - menu_frame: (params.menu_frame * scale).cast_nearest(), - button_frame: (params.button_frame * scale).cast_nearest(), - button_inner: (params.button_inner * scale).cast_nearest(), - check_box: i32::conv_nearest(params.check_box * scale), - mark: i32::conv_nearest(params.mark * scale), - grip_len: i32::conv_nearest(params.grip_len * scale), - scroll_bar: Size::conv_nearest(params.scroll_bar_size * scale), - slider: Size::conv_nearest(params.slider_size * scale), - progress_bar: Size::conv_nearest(params.progress_bar * scale), + frame: (params.frame * scale).cast_to(Nearest), + frame_window: (params.frame_window * scale).cast_to(Nearest), + frame_popup: (params.frame_popup * scale).cast_to(Nearest), + menu_frame: (params.menu_frame * scale).cast_to(Nearest), + button_frame: (params.button_frame * scale).cast_to(Nearest), + button_inner: (params.button_inner * scale).cast_to(Nearest), + check_box: i32::conv_to(Nearest, params.check_box * scale), + mark: i32::conv_to(Nearest, params.mark * scale), + grip_len: i32::conv_to(Nearest, params.grip_len * scale), + scroll_bar: Size::conv_to(Nearest, params.scroll_bar_size * scale), + slider: Size::conv_to(Nearest, params.slider_size * scale), + progress_bar: Size::conv_to(Nearest, params.progress_bar * scale), shadow_a: shadow_offset - shadow_size, shadow_b: shadow_offset + shadow_size, } @@ -211,12 +211,12 @@ impl ThemeSize for Window { } fn wrapped_line_len(&self, _: TextClass, dpem: f32) -> (i32, i32) { - let min: i32 = (self.dims.min_line_len_em * dpem).cast_nearest(); + let min: i32 = (self.dims.min_line_len_em * dpem).cast_to(Nearest); (min, 2 * min) } fn min_element_size(&self) -> i32 { - (self.dims.scale * 8.0).cast_nearest() + (self.dims.scale * 8.0).cast_to(Nearest) } fn min_scroll_size(&self, axis_is_vertical: bool, class: TextClass) -> i32 { @@ -225,7 +225,7 @@ impl ThemeSize for Window { } else { self.dims.min_line_len_em }; - (self.dims.dpem[class] * factor).cast_ceil() + (self.dims.dpem[class] * factor).cast_to(Ceil) } fn grip_len(&self) -> i32 { @@ -245,10 +245,10 @@ impl ThemeSize for Window { MarginStyle::Large => Margins::splat(self.dims.m_large), MarginStyle::Huge => Margins::splat(self.dims.m_huge), MarginStyle::Text => Margins::hv_splat(self.dims.m_text), - MarginStyle::Px(px) => Margins::splat(u16::conv_nearest(px * self.dims.scale)), + MarginStyle::Px(px) => Margins::splat(u16::conv_to(Nearest, px * self.dims.scale)), MarginStyle::Em(em) => { let dpem = self.dims.dpem[TextClass::Standard]; - Margins::splat(u16::conv_nearest(em * dpem)) + Margins::splat(u16::conv_to(Nearest, em * dpem)) } } } @@ -266,11 +266,11 @@ impl ThemeSize for Window { Feature::Mark(style) => { let w = match style { MarkStyle::Chevron(dir) => match dir.is_vertical() == axis_is_vertical { - true => self.dims.mark / 2 + i32::conv_ceil(self.dims.mark_line), - false => self.dims.mark + i32::conv_ceil(self.dims.mark_line), + true => self.dims.mark / 2 + i32::conv_to(Ceil, self.dims.mark_line), + false => self.dims.mark + i32::conv_to(Ceil, self.dims.mark_line), }, MarkStyle::X | MarkStyle::Plus | MarkStyle::Minus => { - self.dims.mark + i32::conv_ceil(self.dims.mark_line) + self.dims.mark + i32::conv_to(Ceil, self.dims.mark_line) } }; return SizeRules::fixed(w).with_margin(self.dims.m_tiny); diff --git a/crates/kas-core/src/theme/flat_theme.rs b/crates/kas-core/src/theme/flat_theme.rs index 62ab408ab..26b6e2d30 100644 --- a/crates/kas-core/src/theme/flat_theme.rs +++ b/crates/kas-core/src/theme/flat_theme.rs @@ -11,7 +11,7 @@ use std::time::Instant; use super::SimpleTheme; use crate::Id; -use crate::cast::traits::*; +use crate::cast::{Ceil, Floor, traits::*}; use crate::config::{Config, WindowConfig}; use crate::dir::{Direction, Directional}; use crate::draw::{color::Rgba, *}; @@ -284,8 +284,8 @@ where shadow = Quad::conv(inner_rect); shadow.a += self.w.dims.shadow_a * SHADOW_POPUP; shadow.b += self.w.dims.shadow_b * SHADOW_POPUP; - let a = Coord::conv_floor(shadow.a); - let b = Coord::conv_ceil(shadow.b); + let a = Coord::conv_to(Floor, shadow.a); + let b = Coord::conv_to(Ceil, shadow.b); outer_rect = Rect::new(a, (b - a).cast()); } let mut draw = self.draw.new_pass(outer_rect, offset, class); diff --git a/crates/kas-core/src/theme/size.rs b/crates/kas-core/src/theme/size.rs index ea9d204e4..b8e0fd1e6 100644 --- a/crates/kas-core/src/theme/size.rs +++ b/crates/kas-core/src/theme/size.rs @@ -65,7 +65,7 @@ impl<'a> SizeCx<'a> { /// ``` /// use kas_core::cast::*; /// # let scale_factor = 1.5f32; - /// let size: i32 = (100.0 * scale_factor).cast_ceil(); + /// let size: i32 = (100.0 * scale_factor).cast_to(Ceil); /// ``` /// /// This value may change during a program's execution (e.g. when a window diff --git a/crates/kas-core/src/theme/traits.rs b/crates/kas-core/src/theme/traits.rs index 9176b8527..a818d3608 100644 --- a/crates/kas-core/src/theme/traits.rs +++ b/crates/kas-core/src/theme/traits.rs @@ -48,7 +48,7 @@ pub trait Theme { /// `9/8 = 1.125` works well with many 1440p screens. It is recommended to /// round dimensions to the nearest integer, and cache the result: /// ```notest - /// self.margin = i32::conv_nearest(MARGIN * factor); + /// self.margin = i32::conv_to(Nearest, MARGIN * factor); /// ``` /// /// A reference to the draw backend is provided allowing configuration. diff --git a/crates/kas-image/src/lib.rs b/crates/kas-image/src/lib.rs index 8021a7371..1395a4b6f 100644 --- a/crates/kas-image/src/lib.rs +++ b/crates/kas-image/src/lib.rs @@ -26,7 +26,7 @@ pub use canvas::{Canvas, CanvasProgram}; pub use sprite::Sprite; #[cfg(feature = "svg")] pub use svg::Svg; -use kas::cast::{Conv, ConvFloat}; +use kas::cast::{Conv, ConvTo, Nearest}; use kas::geom::{Rect, Vec2}; use kas::impl_scope; use kas::layout::{AlignPair, AxisInfo, LogicalSize, SizeRules, Stretch}; @@ -99,9 +99,9 @@ impl Scaling { // Use smaller ratio, if any is finite if rw < rh { - size.1 = i32::conv_nearest(rw * logical_size.1); + size.1 = i32::conv_to(Nearest, rw * logical_size.1); } else if rh < rw { - size.0 = i32::conv_nearest(rh * logical_size.0); + size.0 = i32::conv_to(Nearest, rh * logical_size.0); } } diff --git a/crates/kas-image/src/sprite.rs b/crates/kas-image/src/sprite.rs index a81da9c6c..588dfd458 100644 --- a/crates/kas-image/src/sprite.rs +++ b/crates/kas-image/src/sprite.rs @@ -6,6 +6,7 @@ //! 2D pixmap widget use super::Scaling; +use kas::cast::Nearest; use kas::draw::ImageHandle; use kas::layout::LogicalSize; use kas::prelude::*; @@ -138,7 +139,7 @@ mod Sprite { impl Layout for Self { fn size_rules(&mut self, cx: &mut SizeCx, axis: AxisInfo) -> SizeRules { if self.scaling.size == LogicalSize::default() { - let scale: i32 = (cx.scale_factor() * 0.9).cast_nearest(); + let scale: i32 = (cx.scale_factor() * 0.9).cast_to(Nearest); debug_assert!(scale >= 1); SizeRules::fixed(self.image_size.extract(axis) * scale) .with_margins(cx.margins(self.scaling.margins).extract(axis)) diff --git a/crates/kas-soft/src/atlas.rs b/crates/kas-soft/src/atlas.rs index 44071e126..552dd4d8b 100644 --- a/crates/kas-soft/src/atlas.rs +++ b/crates/kas-soft/src/atlas.rs @@ -10,7 +10,7 @@ use kas::config::SubpixelMode; use std::collections::HashMap; use kas::autoimpl; -use kas::cast::traits::*; +use kas::cast::{Nearest, traits::*}; use kas::draw::{ AllocError, Allocation, Allocator, ImageFormat, ImageId, PassId, UploadError, color, }; @@ -318,8 +318,8 @@ impl Format for InstanceRgba { offset: Offset, ) { let (clip_p, clip_q) = (clip_rect.pos, clip_rect.pos2()); - let p = (Coord::conv_nearest(self.a) - offset).clamp(clip_p, clip_q); - let q = (Coord::conv_nearest(self.b) - offset).clamp(clip_p, clip_q); + let p = (Coord::conv_to(Nearest, self.a) - offset).clamp(clip_p, clip_q); + let q = (Coord::conv_to(Nearest, self.b) - offset).clamp(clip_p, clip_q); let xdi = 1.0 / (self.b.0 - self.a.0); let txd = self.tb.0 - self.ta.0; @@ -328,11 +328,11 @@ impl Format for InstanceRgba { for y in p.1..q.1 { let ly = (f32::conv(y + offset.1) - self.a.1) * ydi; - let ty: usize = (self.ta.1 + tyd * ly).cast_nearest(); + let ty: usize = (self.ta.1 + tyd * ly).cast_to(Nearest); for x in p.0..q.0 { let lx = (f32::conv(x + offset.0) - self.a.0) * xdi; - let tx: usize = (self.ta.0 + txd * lx).cast_nearest(); + let tx: usize = (self.ta.0 + txd * lx).cast_to(Nearest); let tc = tex[ty * tex_size.0 + tx]; let a = tc >> 24; @@ -368,8 +368,8 @@ impl Format for InstanceMask { offset: Offset, ) { let (clip_p, clip_q) = (clip_rect.pos, clip_rect.pos2()); - let p = (Coord::conv_nearest(self.a) - offset).clamp(clip_p, clip_q); - let q = (Coord::conv_nearest(self.b) - offset).clamp(clip_p, clip_q); + let p = (Coord::conv_to(Nearest, self.a) - offset).clamp(clip_p, clip_q); + let q = (Coord::conv_to(Nearest, self.b) - offset).clamp(clip_p, clip_q); let xdi = 1.0 / (self.b.0 - self.a.0); let txd = self.tb.0 - self.ta.0; @@ -378,11 +378,11 @@ impl Format for InstanceMask { for y in p.1..q.1 { let ly = (f32::conv(y + offset.1) - self.a.1) * ydi; - let ty: usize = (self.ta.1 + tyd * ly).cast_nearest(); + let ty: usize = (self.ta.1 + tyd * ly).cast_to(Nearest); for x in p.0..q.0 { let lx = (f32::conv(x + offset.0) - self.a.0) * xdi; - let tx: usize = (self.ta.0 + txd * lx).cast_nearest(); + let tx: usize = (self.ta.0 + txd * lx).cast_to(Nearest); let a = tex[ty * tex_size.0 + tx] as u32; let ba = 255 - a; @@ -501,8 +501,8 @@ impl Format for InstanceRgbaMask { offset: Offset, ) { let (clip_p, clip_q) = (clip_rect.pos, clip_rect.pos2()); - let p = (Coord::conv_nearest(self.a) - offset).clamp(clip_p, clip_q); - let q = (Coord::conv_nearest(self.b) - offset).clamp(clip_p, clip_q); + let p = (Coord::conv_to(Nearest, self.a) - offset).clamp(clip_p, clip_q); + let q = (Coord::conv_to(Nearest, self.b) - offset).clamp(clip_p, clip_q); let xdi = 1.0 / (self.b.0 - self.a.0); let txd = self.tb.0 - self.ta.0; @@ -511,11 +511,11 @@ impl Format for InstanceRgbaMask { for y in p.1..q.1 { let ly = (f32::conv(y + offset.1) - self.a.1) * ydi; - let ty: usize = (self.ta.1 + tyd * ly).cast_nearest(); + let ty: usize = (self.ta.1 + tyd * ly).cast_to(Nearest); for x in p.0..q.0 { let lx = (f32::conv(x + offset.0) - self.a.0) * xdi; - let tx: usize = (self.ta.0 + txd * lx).cast_nearest(); + let tx: usize = (self.ta.0 + txd * lx).cast_to(Nearest); let m = tex[ty * tex_size.0 + tx]; let mr = m >> 16 & 0xFF; diff --git a/crates/kas-soft/src/basic.rs b/crates/kas-soft/src/basic.rs index e12e919e6..5381201af 100644 --- a/crates/kas-soft/src/basic.rs +++ b/crates/kas-soft/src/basic.rs @@ -6,7 +6,7 @@ //! Basic shapes use super::color_to_u32; -use kas::cast::traits::*; +use kas::cast::{Nearest, traits::*}; use kas::draw::PassId; use kas::draw::color; use kas::geom::{Coord, Quad, Vec2}; @@ -97,8 +97,8 @@ impl Draw { let (clip_p, clip_q) = (clip_rect.pos, clip_rect.pos2()); for (rect, col) in pass.rects.drain(..) { - let p = (Coord::conv_nearest(rect.a) - offset).clamp(clip_p, clip_q); - let q = (Coord::conv_nearest(rect.b) - offset).clamp(clip_p, clip_q); + let p = (Coord::conv_to(Nearest, rect.a) - offset).clamp(clip_p, clip_q); + let q = (Coord::conv_to(Nearest, rect.b) - offset).clamp(clip_p, clip_q); let (x0, x1): (usize, usize) = (p.0.cast(), q.0.cast()); let c = color_to_u32(col); @@ -124,8 +124,8 @@ impl Draw { } a -= Vec2::splat(r); b += Vec2::splat(r); - let a = (Coord::conv_nearest(a) - offset).clamp(clip_p, clip_q); - let b = (Coord::conv_nearest(b) - offset).clamp(clip_p, clip_q); + let a = (Coord::conv_to(Nearest, a) - offset).clamp(clip_p, clip_q); + let b = (Coord::conv_to(Nearest, b) - offset).clamp(clip_p, clip_q); for y in a.1..b.1 { let d1 = dx * (f32::conv(y + offset.1) - p1.1); diff --git a/crates/kas-wgpu/src/shaded_theme.rs b/crates/kas-wgpu/src/shaded_theme.rs index aee767acf..63eae24cd 100644 --- a/crates/kas-wgpu/src/shaded_theme.rs +++ b/crates/kas-wgpu/src/shaded_theme.rs @@ -11,7 +11,7 @@ use std::time::Instant; use crate::{DrawShaded, DrawShadedImpl}; use kas::Id; -use kas::cast::traits::*; +use kas::cast::{Ceil, Floor, traits::*}; use kas::config::{Config, WindowConfig}; use kas::dir::{Direction, Directional}; use kas::draw::{color::Rgba, *}; @@ -202,8 +202,8 @@ where shadow = Quad::conv(inner_rect); shadow.a += self.w.dims.shadow_a; shadow.b += self.w.dims.shadow_b; - let a = Coord::conv_floor(shadow.a); - let b = Coord::conv_ceil(shadow.b); + let a = Coord::conv_to(Floor, shadow.a); + let b = Coord::conv_to(Ceil, shadow.b); outer_rect = Rect::new(a, (b - a).cast()); } let mut draw = self.draw.new_pass(outer_rect, offset, class); diff --git a/crates/kas-widgets/src/adapt/adapt_widget.rs b/crates/kas-widgets/src/adapt/adapt_widget.rs index 805c930cb..2de3edf76 100644 --- a/crates/kas-widgets/src/adapt/adapt_widget.rs +++ b/crates/kas-widgets/src/adapt/adapt_widget.rs @@ -8,7 +8,7 @@ use super::*; #[allow(unused)] use kas::Events; use kas::Widget; -use kas::cast::{Cast, CastFloat}; +use kas::cast::{Cast, CastTo, Ceil}; use kas::dir::Directional; use kas::geom::Vec2; use kas::layout::{AlignHints, AxisInfo, SizeRules}; @@ -168,7 +168,7 @@ pub trait AdaptWidget: Widget + Sized { let size = Vec2(w.cast(), h.cast()); Reserve::new(self, move |cx: &mut SizeCx, axis: AxisInfo| { let size = size.extract(axis) * cx.scale_factor(); - SizeRules::fixed(size.cast_ceil()) + SizeRules::fixed(size.cast_to(Ceil)) }) } @@ -182,7 +182,7 @@ pub trait AdaptWidget: Widget + Sized { let size = Vec2(w, h); Reserve::new(self, move |cx: &mut SizeCx, axis: AxisInfo| { let size = size.extract(axis) * cx.dpem(TextClass::Standard); - SizeRules::fixed(size.cast_ceil()) + SizeRules::fixed(size.cast_to(Ceil)) }) } diff --git a/crates/kas-widgets/src/check_box.rs b/crates/kas-widgets/src/check_box.rs index a2404ae59..36933ec10 100644 --- a/crates/kas-widgets/src/check_box.rs +++ b/crates/kas-widgets/src/check_box.rs @@ -6,6 +6,7 @@ //! Toggle widgets use super::AccessLabel; +use kas::cast::{Ceil, Floor}; use kas::prelude::*; use kas::theme::Feature; use std::fmt::Debug; @@ -178,11 +179,11 @@ pub(crate) fn shrink_to_text(rect: &mut Rect, direction: Direction, label: &Acce match direction { Direction::Right => { let offset = label.rect().pos.0 - rect.pos.0; - let text_right: i32 = ((bb.1).0).cast_ceil(); + let text_right: i32 = ((bb.1).0).cast_to(Ceil); rect.size.0 = offset + text_right; } Direction::Left => { - let text_left: i32 = ((bb.0).0).cast_floor(); + let text_left: i32 = ((bb.0).0).cast_to(Floor); rect.pos.0 += text_left; rect.size.0 -= text_left } diff --git a/crates/kas-widgets/src/edit/edit_field.rs b/crates/kas-widgets/src/edit/edit_field.rs index 44ee3213b..213a1e76e 100644 --- a/crates/kas-widgets/src/edit/edit_field.rs +++ b/crates/kas-widgets/src/edit/edit_field.rs @@ -8,6 +8,7 @@ use super::editor::{Component, EventAction}; use super::*; use crate::edit::highlight::{Highlighter, Plain}; +use kas::cast::Ceil; use kas::event::CursorIcon; use kas::messages::{ReplaceSelectedText, SetValueText}; use kas::prelude::*; @@ -85,15 +86,15 @@ mod EditBoxCore { let (min, mut ideal): (i32, i32); if axis.is_horizontal() { let dpem = cx.dpem(TextClass::Editor); - min = (self.width.0 * dpem).cast_ceil(); - ideal = (self.width.1 * dpem).cast_ceil(); + min = (self.width.0 * dpem).cast_to(Ceil); + ideal = (self.width.1 * dpem).cast_to(Ceil); } else if let Some(width) = axis.other() { // Use the height of the first line as a reference let height = self .editor .measure_height(width.cast(), std::num::NonZero::new(1)); - min = (self.lines.0 * height).cast_ceil(); - ideal = (self.lines.1 * height).cast_ceil(); + min = (self.lines.0 * height).cast_to(Ceil); + ideal = (self.lines.1 * height).cast_to(Ceil); } else { unreachable!() }; diff --git a/crates/kas-widgets/src/edit/editor.rs b/crates/kas-widgets/src/edit/editor.rs index 67bc04f21..c5290e474 100644 --- a/crates/kas-widgets/src/edit/editor.rs +++ b/crates/kas-widgets/src/edit/editor.rs @@ -16,7 +16,7 @@ use super::highlight::{self, Highlighter, SchemeColors}; use super::*; -use kas::cast::Cast; +use kas::cast::{Cast, Ceil, Floor, Nearest}; use kas::event::components::{TextInput, TextInputAction}; use kas::event::{ ConfigCx, ElementState, FocusSource, Ime, ImePurpose, ImeSurroundingText, Scroll, @@ -607,12 +607,12 @@ impl Part { if common.wrap { let (min, ideal) = cx.wrapped_line_len(TextClass::Editor, common.dpem); if self.status >= Status::Shaped { - bound = self.forme.measure_width(ideal.cast()).cast_ceil(); + bound = self.forme.measure_width(ideal.cast()).cast_to(Ceil); } SizeRules::new(bound.min(min), bound.min(ideal), Stretch::Filler) } else { if self.status >= Status::Shaped { - bound = self.forme.measure_width(f32::INFINITY).cast_ceil(); + bound = self.forme.measure_width(f32::INFINITY).cast_to(Ceil); } SizeRules::new(bound, bound, Stretch::Filler) } @@ -624,7 +624,7 @@ impl Part { .unwrap_or(f32::INFINITY); let mut bound = 0i32; if self.status >= Status::Shaped { - bound = self.forme.measure_height(wrap_width, None).cast_ceil(); + bound = self.forme.measure_height(wrap_width, None).cast_to(Ceil); } SizeRules::new(bound, bound, Stretch::Filler) }; @@ -714,7 +714,7 @@ impl Part { } let (tl, br) = self.forme.bounding_box(); - (Vec2::from(br) - Vec2::from(tl)).cast_ceil() + (Vec2::from(br) - Vec2::from(tl)).cast_to(Ceil) } /// Implementation of [`Viewport::draw_with_offset`] @@ -979,12 +979,12 @@ impl Part { let right = c1.pos.0.max(c2.pos.0); let top = (c1.pos.1 - c1.ascent).min(c2.pos.1 - c2.ascent); let bottom = (c1.pos.1 - c1.descent).max(c2.pos.1 - c2.ascent); - let p1 = Vec2(left, top).cast_floor(); - let p2 = Vec2(right, bottom).cast_ceil(); + let p1 = Vec2(left, top).cast_to(Floor); + let p2 = Vec2(right, bottom).cast_to(Ceil); Rect::from_coords(p1, p2) } else if let Some(c) = m1.or(m2) { - let p1 = Vec2(c.pos.0, c.pos.1 - c.ascent).cast_floor(); - let p2 = Vec2(c.pos.0, c.pos.1 - c.descent).cast_ceil(); + let p1 = Vec2(c.pos.0, c.pos.1 - c.ascent).cast_to(Floor); + let p2 = Vec2(c.pos.0, c.pos.1 - c.descent).cast_to(Ceil); Rect::from_coords(p1, p2) } else { return; @@ -1776,7 +1776,7 @@ impl Common { h_dist *= -1.0; } v.1 += h_dist; - let pos = c_part.rect.pos + Offset::conv_nearest(v); + let pos = c_part.rect.pos + Offset::conv_to(Nearest, v); let index = self.text_index_nearest(parts, pos).byte(); Action::Move(TextIndex::new(c_p, index), Some(v.0)) } @@ -1975,9 +1975,9 @@ impl Common { if part.is_ready() && let Some(marker) = part.forme.text_glyph_pos(cursor.byte()).next_back() { - let y0 = (marker.pos.1 - marker.ascent).cast_floor(); - let pos = part.rect.pos + Offset(marker.pos.0.cast_nearest(), y0); - let size = Size(0, i32::conv_ceil(marker.pos.1 - marker.descent) - y0); + let y0 = (marker.pos.1 - marker.ascent).cast_to(Floor); + let pos = part.rect.pos + Offset(marker.pos.0.cast_to(Nearest), y0); + let size = Size(0, i32::conv_to(Ceil, marker.pos.1 - marker.descent) - y0); cx.set_scroll(Scroll::Rect(Rect { pos, size })); } } diff --git a/crates/kas-widgets/src/scroll_label.rs b/crates/kas-widgets/src/scroll_label.rs index f0480cb8b..382d085fc 100644 --- a/crates/kas-widgets/src/scroll_label.rs +++ b/crates/kas-widgets/src/scroll_label.rs @@ -6,6 +6,7 @@ //! Scrollable and selectable label use super::{ScrollBar, ScrollBarMsg}; +use kas::cast::{Ceil, Floor, Nearest}; use kas::event::components::{ScrollComponent, TextInput, TextInputAction}; use kas::event::{CursorIcon, FocusSource, Scroll}; use kas::prelude::*; @@ -49,7 +50,7 @@ mod ScrollTextCore { let height = self .text .measure_height(width.cast(), std::num::NonZero::new(3)); - rules.reduce_min_to(height.cast_ceil()); + rules.reduce_min_to(height.cast_to(Ceil)); } rules } @@ -59,7 +60,7 @@ mod ScrollTextCore { #[inline] fn content_size(&self) -> Size { if let Ok((tl, br)) = self.text.bounding_box() { - (br - tl).cast_ceil() + (br - tl).cast_to(Ceil) } else { Size::ZERO } @@ -215,9 +216,9 @@ mod ScrollTextCore { .ok() .and_then(|mut m| m.next_back()) { - let y0 = (marker.pos.1 - marker.ascent).cast_floor(); - let pos = Coord(marker.pos.0.cast_nearest(), y0); - let size = Size(0, i32::conv_ceil(marker.pos.1 - marker.descent) - y0); + let y0 = (marker.pos.1 - marker.ascent).cast_to(Floor); + let pos = Coord(marker.pos.0.cast_to(Nearest), y0); + let size = Size(0, i32::conv_to(Ceil, marker.pos.1 - marker.descent) - y0); cx.set_scroll(Scroll::Rect(Rect { pos, size })); } } @@ -373,7 +374,7 @@ mod ScrollText { let _ = self.vert_bar.size_rules(cx, axis); if axis.is_vertical() { let dpem = cx.dpem(self.text.text.class()); - rules.reduce_min_to((dpem * 4.0).cast_ceil()); + rules.reduce_min_to((dpem * 4.0).cast_to(Ceil)); } rules.with_stretch(Stretch::Low) } diff --git a/crates/kas-widgets/src/slider.rs b/crates/kas-widgets/src/slider.rs index a0f3f19f4..10997d341 100644 --- a/crates/kas-widgets/src/slider.rs +++ b/crates/kas-widgets/src/slider.rs @@ -6,6 +6,7 @@ //! `Slider` control use super::{GripMsg, GripPart}; +use kas::cast::Floor; use kas::event::FocusSource; use kas::messages::{DecrementStep, IncrementStep, SetValueF64}; use kas::prelude::*; @@ -246,8 +247,8 @@ mod Slider { frac = 1.0 - frac; } match self.direction.is_vertical() { - false => Offset((max_offset.0 as f64 * frac).cast_floor(), 0), - true => Offset(0, (max_offset.1 as f64 * frac).cast_floor()), + false => Offset((max_offset.0 as f64 * frac).cast_to(Floor), 0), + true => Offset(0, (max_offset.1 as f64 * frac).cast_to(Floor)), } } From 08cf52b5e100f333856d1704848d68c0a28db96f Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Wed, 5 Aug 2026 09:25:08 +0000 Subject: [PATCH 2/2] Clippy --- crates/kas-core/Cargo.toml | 1 + crates/kas-soft/src/atlas.rs | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/crates/kas-core/Cargo.toml b/crates/kas-core/Cargo.toml index 272a4803b..c4370b223 100644 --- a/crates/kas-core/Cargo.toml +++ b/crates/kas-core/Cargo.toml @@ -144,6 +144,7 @@ collapsible_if = "allow" collapsible_else_if = "allow" bool_comparison = "allow" option_map_unit_fn = "allow" +needless_late_init = "allow" [lints.rust] unexpected_cfgs = { level = "warn", check-cfg = ['cfg(internal_doc)'] } diff --git a/crates/kas-soft/src/atlas.rs b/crates/kas-soft/src/atlas.rs index 552dd4d8b..3d7263f14 100644 --- a/crates/kas-soft/src/atlas.rs +++ b/crates/kas-soft/src/atlas.rs @@ -297,9 +297,10 @@ impl Format for InstanceRgba { fn copy_texture_slice(src: &[u8], dst: &mut [Self::C]) { assert!(src.len() == 4 * dst.len()); - for (out, chunk) in dst.iter_mut().zip(src.chunks_exact(4)) { + let (chunks, _) = src.as_chunks::<4>(); + for (out, chunk) in dst.iter_mut().zip(chunks) { // We convert color from input RGBA (LE) to ARGB (BE) with pre-multiplied alpha - let c = u32::from_le_bytes(chunk.try_into().unwrap()); + let c = u32::from_le_bytes(*chunk); let a = c >> 24 & 0xFF; let b = a * (c >> 16 & 0xFF) / 255; let g = a * (c >> 8 & 0xFF) / 255;