1use std::{
8 fmt::Display,
9 ops::{Div, Mul},
10};
11
12use crate::{Px, RuntimeSession};
13
14pub fn set_scale_factor(scale_factor: f64) {
27 debug_assert!(
28 scale_factor.is_finite() && scale_factor > 0.0,
29 "set_scale_factor called with a non-positive or non-finite value \
30 ({scale_factor}); keeping the previous scale factor. Expected a \
31 positive finite value."
32 );
33 if scale_factor.is_finite() && scale_factor > 0.0 {
34 RuntimeSession::current()
35 .expect("density changes require an active UI session")
36 .set_density(scale_factor);
37 }
38}
39
40pub fn scale_factor() -> f64 {
42 RuntimeSession::current().map_or(1.0, |session| session.density())
43}
44
45#[derive(Debug, Default, Clone, Copy, PartialEq, PartialOrd)]
56pub struct Dp(pub f64);
57
58impl Display for Dp {
59 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
60 write!(f, "{:.2}dp", self.0)
61 }
62}
63
64impl Dp {
65 pub const ZERO: Dp = Dp(0.0);
67
68 pub const fn new(value: f64) -> Self {
70 Dp(value)
71 }
72
73 pub fn to_pixels_f64(&self) -> f64 {
75 let scale_factor = scale_factor();
76 self.0 * scale_factor
77 }
78
79 pub fn from_pixels_f64(value: f64) -> Self {
81 let scale_factor = scale_factor();
82 Dp(value / scale_factor)
83 }
84
85 pub fn to_pixels_u32(&self) -> u32 {
87 let scale_factor = scale_factor();
88 (self.0 * scale_factor) as u32
89 }
90
91 pub fn from_pixels_u32(value: u32) -> Self {
93 let scale_factor = scale_factor();
94 Dp((value as f64) / scale_factor)
95 }
96
97 pub fn to_pixels_f32(&self) -> f32 {
99 let scale_factor = scale_factor();
100 (self.0 * scale_factor) as f32
101 }
102
103 pub fn from_pixels_f32(value: f32) -> Self {
105 let scale_factor = scale_factor();
106 Dp((value as f64) / scale_factor)
107 }
108
109 pub fn to_px(&self) -> Px {
111 Px::from_f32(self.to_pixels_f32())
112 }
113}
114
115impl From<f64> for Dp {
116 fn from(value: f64) -> Self {
118 Dp(value)
119 }
120}
121
122impl From<Px> for Dp {
123 fn from(px: Px) -> Self {
125 px.to_dp()
126 }
127}
128
129impl Mul<f32> for Dp {
130 type Output = Dp;
131
132 fn mul(self, rhs: f32) -> Self::Output {
133 Dp(self.0 * (rhs as f64))
134 }
135}
136
137impl Div<f32> for Dp {
138 type Output = Dp;
139
140 fn div(self, rhs: f32) -> Self::Output {
141 Dp(self.0 / (rhs as f64))
142 }
143}
144
145impl Mul<f64> for Dp {
146 type Output = Dp;
147
148 fn mul(self, rhs: f64) -> Self::Output {
149 Dp(self.0 * rhs)
150 }
151}
152
153impl Div<f64> for Dp {
154 type Output = Dp;
155
156 fn div(self, rhs: f64) -> Self::Output {
157 Dp(self.0 / rhs)
158 }
159}
160
161#[cfg(test)]
162mod set_scale_factor_tests {
163 use std::panic::catch_unwind;
164
165 use super::*;
166
167 #[test]
168 fn density_rejects_invalid_values_without_replacing_the_last_valid_scale() {
169 let session = crate::RuntimeSession::new();
170 let _scope = session.enter();
171 set_scale_factor(2.5);
172 assert_eq!(scale_factor(), 2.5);
173 assert_eq!(Dp(10.0).to_pixels_f64(), 25.0);
174 for invalid in [f64::NAN, 0.0, -1.0, f64::INFINITY, f64::NEG_INFINITY] {
175 let rejected = catch_unwind(|| set_scale_factor(invalid));
176 assert_eq!(rejected.is_err(), cfg!(debug_assertions));
177 assert_eq!(scale_factor(), 2.5);
178 assert_eq!(Dp(10.0).to_pixels_f64(), 25.0);
179 assert_eq!(Dp(10.0).to_pixels_f32(), 25.0);
180 assert_eq!(Dp(10.0).to_px(), Px(25));
181 }
182 }
183}