diff --git a/crates/gpui/src/taffy.rs b/crates/gpui/src/taffy.rs index 99a50b87c8aa9f40a7694f1c2084b10f6d0a9315..88019746566a51e4c85227c9bc0b5b631a7f3a26 100644 --- a/crates/gpui/src/taffy.rs +++ b/crates/gpui/src/taffy.rs @@ -1,6 +1,6 @@ use crate::{ AbsoluteLength, App, Bounds, DefiniteLength, Edges, Length, Pixels, Point, Size, Style, Window, - point, size, + point, size, util::round_device_pixels_midpoint_down, }; use collections::{FxHashMap, FxHashSet}; use stacksafe::{StackSafe, stacksafe}; @@ -369,16 +369,8 @@ impl ToTaffy for Style { impl ToTaffy for AbsoluteLength { fn to_taffy(&self, rem_size: Pixels, scale_factor: f32) -> f32 { - match self { - AbsoluteLength::Pixels(pixels) => { - let pixels: f32 = pixels.into(); - pixels * scale_factor - } - AbsoluteLength::Rems(rems) => { - let pixels: f32 = (*rems * rem_size).into(); - pixels * scale_factor - } - } + let logical_pixels = self.to_pixels(rem_size).0; + round_device_pixels_midpoint_down((logical_pixels * scale_factor).max(0.0)) } } @@ -407,16 +399,7 @@ impl ToTaffy for Length { impl ToTaffy for DefiniteLength { fn to_taffy(&self, rem_size: Pixels, scale_factor: f32) -> taffy::style::LengthPercentage { match self { - DefiniteLength::Absolute(length) => match length { - AbsoluteLength::Pixels(pixels) => { - let pixels: f32 = pixels.into(); - taffy::style::LengthPercentage::length(pixels * scale_factor) - } - AbsoluteLength::Rems(rems) => { - let pixels: f32 = (*rems * rem_size).into(); - taffy::style::LengthPercentage::length(pixels * scale_factor) - } - }, + DefiniteLength::Absolute(length) => length.to_taffy(rem_size, scale_factor), DefiniteLength::Fraction(fraction) => { taffy::style::LengthPercentage::percent(*fraction) } @@ -427,16 +410,7 @@ impl ToTaffy for DefiniteLength { impl ToTaffy for DefiniteLength { fn to_taffy(&self, rem_size: Pixels, scale_factor: f32) -> taffy::style::LengthPercentageAuto { match self { - DefiniteLength::Absolute(length) => match length { - AbsoluteLength::Pixels(pixels) => { - let pixels: f32 = pixels.into(); - taffy::style::LengthPercentageAuto::length(pixels * scale_factor) - } - AbsoluteLength::Rems(rems) => { - let pixels: f32 = (*rems * rem_size).into(); - taffy::style::LengthPercentageAuto::length(pixels * scale_factor) - } - }, + DefiniteLength::Absolute(length) => length.to_taffy(rem_size, scale_factor), DefiniteLength::Fraction(fraction) => { taffy::style::LengthPercentageAuto::percent(*fraction) } @@ -447,15 +421,7 @@ impl ToTaffy for DefiniteLength { impl ToTaffy for DefiniteLength { fn to_taffy(&self, rem_size: Pixels, scale_factor: f32) -> taffy::style::Dimension { match self { - DefiniteLength::Absolute(length) => match length { - AbsoluteLength::Pixels(pixels) => { - let pixels: f32 = pixels.into(); - taffy::style::Dimension::length(pixels * scale_factor) - } - AbsoluteLength::Rems(rems) => { - taffy::style::Dimension::length((*rems * rem_size * scale_factor).into()) - } - }, + DefiniteLength::Absolute(length) => length.to_taffy(rem_size, scale_factor), DefiniteLength::Fraction(fraction) => taffy::style::Dimension::percent(*fraction), } } @@ -463,16 +429,19 @@ impl ToTaffy for DefiniteLength { impl ToTaffy for AbsoluteLength { fn to_taffy(&self, rem_size: Pixels, scale_factor: f32) -> taffy::style::LengthPercentage { - match self { - AbsoluteLength::Pixels(pixels) => { - let pixels: f32 = pixels.into(); - taffy::style::LengthPercentage::length(pixels * scale_factor) - } - AbsoluteLength::Rems(rems) => { - let pixels: f32 = (*rems * rem_size).into(); - taffy::style::LengthPercentage::length(pixels * scale_factor) - } - } + taffy::style::LengthPercentage::length(self.to_taffy(rem_size, scale_factor)) + } +} + +impl ToTaffy for AbsoluteLength { + fn to_taffy(&self, rem_size: Pixels, scale_factor: f32) -> taffy::style::LengthPercentageAuto { + taffy::style::LengthPercentageAuto::length(self.to_taffy(rem_size, scale_factor)) + } +} + +impl ToTaffy for AbsoluteLength { + fn to_taffy(&self, rem_size: Pixels, scale_factor: f32) -> taffy::style::Dimension { + taffy::style::Dimension::length(self.to_taffy(rem_size, scale_factor)) } } diff --git a/crates/gpui/src/util.rs b/crates/gpui/src/util.rs index 8a7411e0ac29e86beff8f6a803c5f6a31518048e..bd8c03167e80ef98fceaa35d06d2e2f202424d34 100644 --- a/crates/gpui/src/util.rs +++ b/crates/gpui/src/util.rs @@ -125,12 +125,32 @@ pub(crate) fn atomic_incr_if_not_zero(counter: &AtomicUsize) -> usize { } } +/// Rounds a device-pixel value to the nearest integer, with .5 ties rounded down. +#[inline] +pub(crate) fn round_device_pixels_midpoint_down(value: f32) -> f32 { + let floor = value.floor(); + if value - floor > 0.5 { + floor + 1.0 + } else { + floor + } +} + #[cfg(test)] mod tests { use crate::TestAppContext; use super::*; + #[test] + fn test_round_device_pixels_midpoint_down() { + assert_eq!(round_device_pixels_midpoint_down(0.5), 0.0); + assert_eq!(round_device_pixels_midpoint_down(1.5), 1.0); + assert_eq!(round_device_pixels_midpoint_down(2.5), 2.0); + assert_eq!(round_device_pixels_midpoint_down(1.5001), 2.0); + assert_eq!(round_device_pixels_midpoint_down(-1.5), -2.0); + } + #[gpui::test] async fn test_with_timeout(cx: &mut TestAppContext) { Task::ready(()) diff --git a/crates/gpui/src/window.rs b/crates/gpui/src/window.rs index eaa9594e7e23b059a0911dace009f425034de91f..2a36da065c19862a5cbfc82e33ff7c40a49644ea 100644 --- a/crates/gpui/src/window.rs +++ b/crates/gpui/src/window.rs @@ -57,7 +57,7 @@ use uuid::Uuid; mod prompts; -use crate::util::atomic_incr_if_not_zero; +use crate::util::{atomic_incr_if_not_zero, round_device_pixels_midpoint_down}; pub use prompts::*; /// Default window size used when no explicit size is provided. @@ -2118,6 +2118,12 @@ impl Window { px((value.0 * scale_factor).round() / scale_factor) } + /// Returns the logical length corresponding to one physical device pixel. + #[inline] + pub fn one_device_pixel(&self) -> Pixels { + px(1.0 / self.scale_factor()) + } + #[inline] fn round_point_to_device_pixels(&self, position: Point) -> Point { point( @@ -2145,11 +2151,16 @@ impl Window { #[inline] fn round_bounds_to_device_pixels(&self, bounds: Bounds) -> Bounds { let scale_factor = self.scale_factor(); - let left = (bounds.left().0 * scale_factor).round(); - let top = (bounds.top().0 * scale_factor).round(); - let right = (bounds.right().0 * scale_factor).round(); - let bottom = (bounds.bottom().0 * scale_factor).round(); - self.bounds_from_device_edges(left, top, right, bottom) + Bounds { + origin: point( + ScaledPixels((bounds.origin.x.0 * scale_factor).round()), + ScaledPixels((bounds.origin.y.0 * scale_factor).round()), + ), + size: size( + self.round_length_to_device_pixels(bounds.size.width), + self.round_length_to_device_pixels(bounds.size.height), + ), + } } #[inline] @@ -2174,18 +2185,18 @@ impl Window { #[inline] fn round_edges_to_device_pixels(&self, edges: Edges) -> Edges { - let scale_factor = self.scale_factor(); Edges { - top: ScaledPixels((edges.top.0 * scale_factor).round()), - right: ScaledPixels((edges.right.0 * scale_factor).round()), - bottom: ScaledPixels((edges.bottom.0 * scale_factor).round()), - left: ScaledPixels((edges.left.0 * scale_factor).round()), + top: self.round_nonzero_length_to_device_pixels(edges.top), + right: self.round_nonzero_length_to_device_pixels(edges.right), + bottom: self.round_nonzero_length_to_device_pixels(edges.bottom), + left: self.round_nonzero_length_to_device_pixels(edges.left), } } #[inline] fn round_length_to_device_pixels(&self, value: Pixels) -> ScaledPixels { - ScaledPixels((value.0 * self.scale_factor()).round()) + let scaled = (value.0 * self.scale_factor()).max(0.0); + ScaledPixels(round_device_pixels_midpoint_down(scaled)) } #[inline]