diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ca629c..161c45c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 execute change callbacks for the same dynamic at the same time. - The initial `inner_size` of a `Window` is now used if it is non-zero and `WindowAttributes::inner_size` is `None`. +- Container's layout and drawing functions now properly round up/down the + measurements to ensure accurate rendering. Fixes [#158][158]. + +[158]: https://github.com/khonsulabs/cushy/issues/158 ### Added @@ -46,6 +50,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `Fraction` now has `LinearInterpolation` and `PercentBetween` implementations. - `Window::zoom` allows setting a `Dynamic` that scales all DPI-scaled operations by an additional scaling factor. +- `Edges` and `ContainerShadow` now implement `figures::Round`. ## v0.3.0 (2024-05-12) diff --git a/Cargo.lock b/Cargo.lock index 8c0def7..1200142 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1290,7 +1290,7 @@ checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" [[package]] name = "kludgine" version = "0.9.0" -source = "git+https://github.com/khonsulabs/kludgine#eca1ac5d3a9dead20c9c94fb849a4e2bf7719654" +source = "git+https://github.com/khonsulabs/kludgine#3a33c18c2875146371c5f483dcb80e2b4ca5a7da" dependencies = [ "ahash", "alot", @@ -3126,9 +3126,9 @@ checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" [[package]] name = "version_check" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "walkdir" diff --git a/src/styles.rs b/src/styles.rs index 19a9b30..31dff60 100644 --- a/src/styles.rs +++ b/src/styles.rs @@ -12,7 +12,7 @@ use std::sync::Arc; use ahash::AHashMap; use figures::units::{Lp, Px, UPx}; -use figures::{Fraction, IntoSigned, IntoUnsigned, Rect, ScreenScale, Size, Zero}; +use figures::{Fraction, IntoSigned, IntoUnsigned, Rect, Round, ScreenScale, Size, Zero}; use intentional::Cast; pub use kludgine::cosmic_text::{FamilyOwned, Style, Weight}; pub use kludgine::shapes::CornerRadii; @@ -1245,6 +1245,23 @@ where } } +impl Round for Edges +where + T: Round, +{ + fn round(self) -> Self { + self.map(Round::round) + } + + fn ceil(self) -> Self { + self.map(Round::ceil) + } + + fn floor(self) -> Self { + self.map(Round::floor) + } +} + impl Edges { /// Updates `top` and returns self. #[must_use] diff --git a/src/widgets/container.rs b/src/widgets/container.rs index f8cc5c4..09f4f01 100644 --- a/src/widgets/container.rs +++ b/src/widgets/container.rs @@ -224,13 +224,17 @@ impl Widget for Container { let shadow = self .shadow .get_tracking_invalidate(context) - .into_px(context.gfx.scale()); + .into_px(context.gfx.scale()) + .ceil(); - let child_shadow_offset = shadow.offset.min(Point::ZERO).abs(); + let child_shadow_offset = shadow.offset.min(Point::ZERO).abs().ceil(); let child_size = context.gfx.region().size - shadow.spread * 2 - shadow.offset.abs(); let child_area = Rect::new(child_shadow_offset + shadow.spread, child_size); - let corner_radii = context.get(&CornerRadius).into_px(context.gfx.scale()); + let corner_radii = context + .get(&CornerRadius) + .into_px(context.gfx.scale()) + .ceil(); // check if the shadow would be obscured before we try to draw it. if child_area.origin != Point::ZERO || child_size != context.gfx.region().size { @@ -255,15 +259,18 @@ impl Widget for Container { ) -> Size { let child = self.child.mounted(context); - let corner_radii = context.get(&CornerRadius).into_upx(context.gfx.scale()); + let corner_radii = context + .get(&CornerRadius) + .into_upx(context.gfx.scale()) + .ceil(); let max_space = available_space.map(ConstraintLimit::max); let min_dimension = max_space.width.min(max_space.height); - let max_corner_radii = min_dimension / 2; + let max_corner_radii = (min_dimension / 2).floor(); let corner_radii = corner_radii.map(|r| r.min(max_corner_radii)); - let mut padding = self.padding(context).into_upx(context.gfx.scale()); + let mut padding = self.padding(context).into_upx(context.gfx.scale()).ceil(); padding.left = padding .left .max(corner_radii.top_left / std::f32::consts::PI) @@ -285,7 +292,8 @@ impl Widget for Container { let shadow = self .shadow .get_tracking_invalidate(context) - .into_px(context.gfx.scale()); + .into_px(context.gfx.scale()) + .ceil(); let shadow_spread = shadow.spread.into_unsigned(); let child_shadow_offset_amount = shadow.offset.abs().into_unsigned(); @@ -736,6 +744,38 @@ impl ContainerShadow { } } +impl Round for ContainerShadow +where + Unit: Round, +{ + fn round(self) -> Self { + Self { + color: self.color, + offset: self.offset.round(), + blur_radius: self.blur_radius.round(), + spread: self.spread.round(), + } + } + + fn ceil(self) -> Self { + Self { + color: self.color, + offset: self.offset.ceil(), + blur_radius: self.blur_radius.ceil(), + spread: self.spread.ceil(), + } + } + + fn floor(self) -> Self { + Self { + color: self.color, + offset: self.offset.floor(), + blur_radius: self.blur_radius.floor(), + spread: self.spread.floor(), + } + } +} + impl ScreenScale for ContainerShadow where Unit: ScreenScale,