kas_core/theme/size.rs
1// Licensed under the Apache License, Version 2.0 (the "License");
2// you may not use this file except in compliance with the License.
3// You may obtain a copy of the License in the LICENSE-APACHE file or at:
4// https://www.apache.org/licenses/LICENSE-2.0
5
6//! "Handle" types used by themes
7
8use super::{Feature, FrameStyle, MarginStyle, TextClass};
9use crate::autoimpl;
10use crate::dir::Directional;
11use crate::event::EventState;
12use crate::geom::Rect;
13use crate::layout::{AlignPair, FrameRules, LogicalBuilder, Margins, SizeRules};
14use crate::text::fonts::FontSelector;
15use std::ops::{Deref, DerefMut};
16
17#[allow(unused)]
18use crate::{event::ConfigCx, layout::Stretch, theme::DrawCx};
19
20/// Size and scaling interface
21///
22/// This context provides scaled sizing information from the theme.
23///
24/// Most methods get or calculate the size of some feature. These same features
25/// may be drawn through [`DrawCx`].
26pub struct SizeCx<'a> {
27 ev: &'a mut EventState,
28 // ThemeSize is implemented by super::dimensions::Window
29 w: &'a dyn ThemeSize,
30}
31
32impl<'a> Deref for SizeCx<'a> {
33 type Target = EventState;
34 fn deref(&self) -> &EventState {
35 self.ev
36 }
37}
38impl<'a> DerefMut for SizeCx<'a> {
39 fn deref_mut(&mut self) -> &mut EventState {
40 self.ev
41 }
42}
43
44impl<'a> SizeCx<'a> {
45 /// Construct from [`EventState`] and a [`ThemeSize`]
46 #[cfg_attr(not(feature = "internal_doc"), doc(hidden))]
47 #[cfg_attr(docsrs, doc(cfg(internal_doc)))]
48 #[inline]
49 pub fn new(ev: &'a mut EventState, w: &'a dyn ThemeSize) -> Self {
50 SizeCx { ev, w }
51 }
52
53 /// Get the scale factor
54 ///
55 /// "Traditional" PC screens have a scale factor of 1; high-DPI screens
56 /// may have a factor of 2 or higher. This may be fractional and may be
57 /// adjusted to suit the device type (e.g. a phone or desktop monitor) as
58 /// well as the user's preference.
59 ///
60 /// One could use this value to calculate physical size, but be warned that
61 /// the result may be quite inaccurate on anything other than a desktop
62 /// monitor: `25.4 mm = 1 inch = (96 * scale_factor) pixels`
63 ///
64 /// To calculate screen pixel sizes from virtual pixel sizes:
65 /// ```
66 /// use kas_core::cast::*;
67 /// # let scale_factor = 1.5f32;
68 /// let size: i32 = (100.0 * scale_factor).cast_ceil();
69 /// ```
70 ///
71 /// This value may change during a program's execution (e.g. when a window
72 /// is moved to a different monitor); in this case all widgets will be
73 /// resized via [`crate::Layout::size_rules`].
74 #[inline]
75 pub fn scale_factor(&self) -> f32 {
76 self.w.scale_factor()
77 }
78
79 /// Build [`SizeRules`] from the input size in logical pixels
80 #[inline]
81 pub fn logical(&self, width: f32, height: f32) -> LogicalBuilder {
82 LogicalBuilder::new((width, height), self.scale_factor())
83 }
84
85 /// Get the configured font selector for `class`
86 #[inline]
87 pub fn font(&self, class: TextClass) -> FontSelector {
88 self.w.font(class)
89 }
90
91 /// Get the default font size for `class`
92 ///
93 /// Units are physical pixels per font Em.
94 ///
95 /// The Em is a unit of typography (variously defined as the point-size of
96 /// the font, the height of the font or the width of an upper-case `M`).
97 ///
98 /// This method returns the size of 1 Em in physical pixels, derived from
99 /// the font size in use by the theme and the screen's scale factor.
100 #[inline]
101 pub fn dpem(&self, class: TextClass) -> f32 {
102 self.w.dpem(class)
103 }
104
105 /// Get the `(min, ideal)` line length for text which wraps
106 #[inline]
107 pub fn wrapped_line_len(&self, class: TextClass, dpem: f32) -> (i32, i32) {
108 self.w.wrapped_line_len(class, dpem)
109 }
110
111 /// The smallest reasonable size for a visible (non-frame) component
112 ///
113 /// This is used as a suggestion by some heuristics.
114 #[inline]
115 pub fn min_element_size(&self) -> i32 {
116 self.w.min_element_size()
117 }
118
119 /// The minimum size of a scrollable area
120 #[inline]
121 pub fn min_scroll_size(&self, axis: impl Directional, class: Option<TextClass>) -> i32 {
122 let class = class.unwrap_or(TextClass::Standard);
123 self.w.min_scroll_size(axis.is_vertical(), class)
124 }
125
126 /// The length of the grip (draggable handle) on a scroll bar or slider
127 ///
128 /// This is the length in line with the control. The size on the opposite
129 /// axis is assumed to be equal to the feature size as reported by
130 /// [`Self::feature`].
131 #[inline]
132 pub fn grip_len(&self) -> i32 {
133 self.w.grip_len()
134 }
135
136 /// The width of a vertical scroll bar
137 ///
138 /// This value is also available through [`Self::feature`].
139 #[inline]
140 pub fn scroll_bar_width(&self) -> i32 {
141 self.w.scroll_bar_width()
142 }
143
144 /// Get margin size
145 #[inline]
146 pub fn margins(&self, style: MarginStyle) -> Margins {
147 self.w.margins(style)
148 }
149
150 /// Get margins for [`MarginStyle::Inner`]
151 #[inline]
152 pub fn inner_margins(&self) -> Margins {
153 self.w.margins(MarginStyle::Inner)
154 }
155
156 /// Get margins for [`MarginStyle::Tiny`]
157 #[inline]
158 pub fn tiny_margins(&self) -> Margins {
159 self.w.margins(MarginStyle::Tiny)
160 }
161
162 /// Get margins for [`MarginStyle::Small`]
163 #[inline]
164 pub fn small_margins(&self) -> Margins {
165 self.w.margins(MarginStyle::Small)
166 }
167
168 /// Get margins for [`MarginStyle::Large`]
169 #[inline]
170 pub fn large_margins(&self) -> Margins {
171 self.w.margins(MarginStyle::Large)
172 }
173
174 /// Get margins for [`MarginStyle::Text`]
175 #[inline]
176 pub fn text_margins(&self) -> Margins {
177 self.w.margins(MarginStyle::Text)
178 }
179
180 /// Size rules for a feature
181 #[inline]
182 pub fn feature(&self, feature: Feature, axis: impl Directional) -> SizeRules {
183 self.w.feature(feature, axis.is_vertical())
184 }
185
186 /// Size of a frame around another element
187 #[inline]
188 pub fn frame(&self, style: FrameStyle, axis: impl Directional) -> FrameRules {
189 self.w.frame(style, axis.is_vertical())
190 }
191
192 /// Align a feature's rect
193 ///
194 /// In case the input `rect` is larger than desired on either axis, it is
195 /// reduced in size and offset within the original `rect` as is preferred.
196 #[inline]
197 pub fn align_feature(&self, feature: Feature, rect: Rect, align: AlignPair) -> Rect {
198 self.w.align_feature(feature, rect, align)
199 }
200}
201
202/// Theme sizing implementation
203#[autoimpl(for<S: trait + ?Sized, R: Deref<Target = S>> R)]
204pub trait ThemeSize {
205 /// Get the scale factor
206 fn scale_factor(&self) -> f32;
207
208 /// Get the configured font selector for `class`
209 fn font(&self, class: TextClass) -> FontSelector;
210
211 /// Get the default font size for `class`
212 ///
213 /// Units are physical pixels per Em.
214 fn dpem(&self, class: TextClass) -> f32;
215
216 /// Get the `(min, ideal)` line length for text which wraps
217 fn wrapped_line_len(&self, class: TextClass, dpem: f32) -> (i32, i32);
218
219 /// The smallest reasonable size for a visible (non-frame) component
220 ///
221 /// This is used as a suggestion by some heuristics.
222 fn min_element_size(&self) -> i32;
223
224 /// The minimum size of a scrollable area
225 fn min_scroll_size(&self, axis_is_vertical: bool, class: TextClass) -> i32;
226
227 /// The length of the grip (draggable handle) on a scroll bar or slider
228 fn grip_len(&self) -> i32;
229
230 /// The width of a vertical scroll bar
231 fn scroll_bar_width(&self) -> i32;
232
233 /// Get margin size
234 fn margins(&self, style: MarginStyle) -> Margins;
235
236 /// Size rules for a feature
237 fn feature(&self, feature: Feature, axis_is_vertical: bool) -> SizeRules;
238
239 /// Align a feature's rect
240 ///
241 /// In case the input `rect` is larger than desired on either axis, it is
242 /// reduced in size and offset within the original `rect` as is preferred.
243 fn align_feature(&self, feature: Feature, rect: Rect, align: AlignPair) -> Rect;
244
245 /// Size of a frame around another element
246 fn frame(&self, style: FrameStyle, axis_is_vertical: bool) -> FrameRules;
247}