From 0fd23acd2bff2d06fb2feb76e8b275ddfba94d8a Mon Sep 17 00:00:00 2001 From: "Pedro M. de Echanove Pasquin" Date: Fri, 2 Oct 2026 07:55:23 +0200 Subject: [PATCH] =?UTF-8?q?A=20`Length::dp`=20font=20size=20keeps=20its=20?= =?UTF-8?q?logical=20size=20on=20a=20scaled=20output=20instead=20of=20grow?= =?UTF-8?q?ing=20with=20the=20scale=20On=20a=2014.1"=202560x1440=20laptop,?= =?UTF-8?q?=20where=20forge=20picks=20a=202.25=20output=20scale=20and=20lt?= =?UTF-8?q?k=20draws=20at=20buffer=20scale=203,=20the=20title=20of=20every?= =?UTF-8?q?=20settings=20screen=20but=20the=20first=20came=20out=20about?= =?UTF-8?q?=20three=20times=20its=20design=20size,=20while=20on=20a=201920?= =?UTF-8?q?x1080=20laptop=20at=20scale=201=20the=20same=20title=20was=20sm?= =?UTF-8?q?aller=20than=20the=2028=20px=20heading=20of=20the=20home=20scre?= =?UTF-8?q?en.=20That=20title=20is=20a=20`list=5Fitem`=20with=20`.font=5Fs?= =?UTF-8?q?ize(=20Length::widget(=2020.0=20)=20)`,=20which=20in=20`WidgetS?= =?UTF-8?q?caling::Physical`=20is=20`Length::dp(=2020.0=20)`.=20`Canvas::r?= =?UTF-8?q?esolve=5Ffont`=20resolved=20a=20`Dp`=20length=20with=20the=20ca?= =?UTF-8?q?nvas'=20density,=20which=20the=20runtime=20sets=20to=20the=20ou?= =?UTF-8?q?tput=20scale,=20and=20returned=20it=20as=20a=20font=20size.=20F?= =?UTF-8?q?ont=20sizes=20are=20logical:=20the=20raster=20path=20multiplies?= =?UTF-8?q?=20them=20by=20`dpi=5Fscale`.=20A=20`Dp`=20font=20size=20was=20?= =?UTF-8?q?therefore=20scaled=20twice,=2020=20=C3=97=203=20=3D=2060=20logi?= =?UTF-8?q?cal=20px=20and=20180=20physical,=20against=2020=20at=20scale=20?= =?UTF-8?q?1.=20Geometry=20did=20not=20have=20the=20problem,=20because=20`?= =?UTF-8?q?resolve=5Fgeom`=20returns=20physical=20pixels=20that=20nothing?= =?UTF-8?q?=20scales=20again,=20and=20neither=20did=20a=20stock=20widget?= =?UTF-8?q?=20without=20an=20override,=20because=20`font=5Fpx`=20already?= =?UTF-8?q?=20divided=20by=20`dpi=5Fscale`=20in=20Physical=20mode.=20The?= =?UTF-8?q?=20same=20`dp`=20value=20thus=20gave=20one=20size=20through=20`?= =?UTF-8?q?font=5Fpx`=20and=20another=20through=20an=20explicit=20override?= =?UTF-8?q?.=20`resolve=5Ffont`=20now=20resolves=20`Dp`=20with=20`density?= =?UTF-8?q?=20/=20dpi=5Fscale`,=20so=20that=20`=C3=97=20dpi=5Fscale`=20at?= =?UTF-8?q?=20raster=20yields=20`n=20=C3=97=20density`=20physical=20pixels?= =?UTF-8?q?,=20the=20same=20as=20`resolve=5Fgeom`=20gives=20the=20same=20l?= =?UTF-8?q?ength.=20Pixel,=20viewport=20and=20fluid=20lengths=20are=20unto?= =?UTF-8?q?uched,=20since=20they=20do=20not=20depend=20on=20the=20density.?= =?UTF-8?q?=20The=20Physical=20branch=20of=20`font=5Fpx`=20now=20delegates?= =?UTF-8?q?=20to=20`resolve=5Ffont(=20Length::dp(=20design=5Fpx=20)=20)`,?= =?UTF-8?q?=20which=20is=20the=20formula=20it=20carried=20on=20its=20own.?= =?UTF-8?q?=20Every=20caller=20of=20`resolve=5Ffont`=20is=20affected:=20th?= =?UTF-8?q?e=20`font=5Fsize`=20overrides=20of=20`button`,=20`text=5Fedit`?= =?UTF-8?q?=20and=20`list=5Fitem`,=20and=20the=20`size`=20of=20`text`=20an?= =?UTF-8?q?d=20`rich=5Ftext`=20when=20given=20a=20`Dp`=20or,=20in=20Physic?= =?UTF-8?q?al=20mode,=20a=20`Length::widget`.=20At=20scale=201=20nothing?= =?UTF-8?q?=20changes.=20The=20new=20test=20`dp=5Ffont=5Fmatches=5Fdp=5Fge?= =?UTF-8?q?ometry=5Fon=5Fhidpi`=20checks=20at=20scale=203=20that=20a=2020?= =?UTF-8?q?=20dp=20font=20resolves=20to=2020=20logical=20px,=20equals=20`f?= =?UTF-8?q?ont=5Fpx(=2020=20)`=20for=20`Length::widget`,=20and=20times=20t?= =?UTF-8?q?he=20scale=20equals=20the=20same=20length=20resolved=20as=20geo?= =?UTF-8?q?metry.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/render/mod.rs | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/src/render/mod.rs b/src/render/mod.rs index a25b167..665721a 100644 --- a/src/render/mod.rs +++ b/src/render/mod.rs @@ -336,10 +336,12 @@ impl Canvas /// [`Self::viewport_logical`], with this canvas' [`Self::density`], /// times the global accessibility [`crate::text_scale`]. Counterpart /// of [`Self::resolve_geom`] for font sizes, which are handed to the - /// raster path pre-`dpi_scale`. + /// raster path pre-`dpi_scale`, so `Dp` uses `density / dpi_scale`. pub fn resolve_font( &self, l: Length ) -> f32 { - l.resolve_with_density( self.viewport_logical(), Length::EM_BASE_DEFAULT, self.density() ) + let scale = self.dpi_scale(); + let scale = if scale > 0.0 { scale } else { 1.0 }; + l.resolve_with_density( self.viewport_logical(), Length::EM_BASE_DEFAULT, self.density() / scale ) * crate::types::text_scale() } @@ -375,9 +377,7 @@ impl Canvas } WidgetScaling::Physical => { - let scale = self.dpi_scale(); - let scale = if scale > 0.0 { scale } else { 1.0 }; - design_px * self.density() / scale * crate::types::text_scale() + self.resolve_font( Length::dp( design_px ) ) } } } @@ -1148,6 +1148,24 @@ mod viewport_tests set_density( 1.0 ); set_widget_scaling( WidgetScaling::Fluid ); } + + #[ test ] + fn dp_font_matches_dp_geometry_on_hidpi() + { + use crate::types::{ set_widget_scaling, set_density, WidgetScaling }; + let _g = crate::TEST_GLOBALS_LOCK.lock().unwrap_or_else( |e| e.into_inner() ); + + set_widget_scaling( WidgetScaling::Physical ); + set_density( 3.0 ); + let mut c = Canvas::new( 2560, 1440 ); + c.set_dpi_scale( 3.0 ); + assert_eq!( c.resolve_font( Length::dp( 20.0 ) ), 20.0 ); + assert_eq!( c.resolve_font( Length::widget( 20.0 ) ), c.font_px( 20.0 ) ); + assert_eq!( c.resolve_font( Length::dp( 20.0 ) ) * 3.0, c.resolve_geom( Length::dp( 20.0 ) ) ); + + set_density( 1.0 ); + set_widget_scaling( WidgetScaling::Fluid ); + } } #[ cfg( test ) ]