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 cast::CastFloat;
9
10use super::{Feature, FrameStyle, MarginStyle, SizableText, Text, TextClass};
11use crate::autoimpl;
12use crate::dir::Directional;
13use crate::geom::Rect;
14use crate::layout::{AlignPair, AxisInfo, FrameRules, Margins, SizeRules};
15use crate::text::format::FormattableText;
16use std::ops::Deref;
17
18#[allow(unused)]
19use crate::{event::ConfigCx, layout::Stretch, theme::DrawCx};
20
21/// Size and scale interface
22///
23/// This interface is provided to widgets in [`crate::Layout::size_rules`].
24/// It may also be accessed through [`crate::event::EventCx::size_cx`],
25/// [`DrawCx::size_cx`].
26///
27/// Most methods get or calculate the size of some feature. These same features
28/// may be drawn through [`DrawCx`].
29pub struct SizeCx<'a>(&'a dyn ThemeSize);
30
31impl<'a> SizeCx<'a> {
32    /// Construct from a [`ThemeSize`]
33    #[cfg_attr(not(feature = "internal_doc"), doc(hidden))]
34    #[cfg_attr(docsrs, doc(cfg(internal_doc)))]
35    pub fn new(h: &'a dyn ThemeSize) -> Self {
36        SizeCx(h)
37    }
38
39    /// Reborrow with a new lifetime
40    ///
41    /// Rust allows references like `&T` or `&mut T` to be "reborrowed" through
42    /// coercion: essentially, the pointer is copied under a new, shorter, lifetime.
43    /// Until rfcs#1403 lands, reborrows on user types require a method call.
44    ///
45    /// Calling this method is zero-cost.
46    #[inline(always)]
47    pub fn re<'b>(&'b self) -> SizeCx<'b>
48    where
49        'a: 'b,
50    {
51        SizeCx(self.0)
52    }
53
54    /// Get the scale factor
55    ///
56    /// "Traditional" PC screens have a scale factor of 1; high-DPI screens
57    /// may have a factor of 2 or higher. This may be fractional and may be
58    /// adjusted to suit the device type (e.g. a phone or desktop monitor) as
59    /// well as the user's preference.
60    ///
61    /// One could use this value to calculate physical size, but be warned that
62    /// the result may be quite inaccurate on anything other than a desktop
63    /// monitor: `25.4 mm = 1 inch = (96 * scale_factor) pixels`
64    ///
65    /// To calculate screen pixel sizes from virtual pixel sizes:
66    /// ```
67    /// use kas_core::cast::*;
68    /// # let scale_factor = 1.5f32;
69    /// let size: i32 = (100.0 * scale_factor).cast_ceil();
70    /// ```
71    ///
72    /// This value may change during a program's execution (e.g. when a window
73    /// is moved to a different monitor); in this case all widgets will be
74    /// resized via [`crate::Layout::size_rules`].
75    pub fn scale_factor(&self) -> f32 {
76        self.0.scale_factor()
77    }
78
79    /// The Em size of the standard font in pixels
80    ///
81    /// The Em is a unit of typography (variously defined as the point-size of
82    /// the font, the height of the font or the width of an upper-case `M`).
83    /// The method [`Self::line_height`] returns a related but distinct value.
84    ///
85    /// This method returns the size of 1 Em in physical pixels, derived from
86    /// the font size in use by the theme and the screen's scale factor.
87    pub fn dpem(&self) -> f32 {
88        self.0.dpem()
89    }
90
91    /// The minimum size of a scrollable area
92    pub fn min_scroll_size(&self, axis: impl Directional) -> i32 {
93        self.0.min_scroll_size(axis.is_vertical())
94    }
95
96    /// The length of the grip (draggable handle) on a scroll bar or slider
97    ///
98    /// This is the length in line with the control. The size on the opposite
99    /// axis is assumed to be equal to the feature size as reported by
100    /// [`Self::feature`].
101    pub fn grip_len(&self) -> i32 {
102        self.0.grip_len()
103    }
104
105    /// The width of a vertical scroll bar
106    ///
107    /// This value is also available through [`Self::feature`].
108    pub fn scroll_bar_width(&self) -> i32 {
109        self.0.scroll_bar_width()
110    }
111
112    /// Get margin size
113    pub fn margins(&self, style: MarginStyle) -> Margins {
114        self.0.margins(style)
115    }
116
117    /// Get margins for [`MarginStyle::Inner`]
118    pub fn inner_margins(&self) -> Margins {
119        self.0.margins(MarginStyle::Inner)
120    }
121
122    /// Get margins for [`MarginStyle::Tiny`]
123    pub fn tiny_margins(&self) -> Margins {
124        self.0.margins(MarginStyle::Tiny)
125    }
126
127    /// Get margins for [`MarginStyle::Small`]
128    pub fn small_margins(&self) -> Margins {
129        self.0.margins(MarginStyle::Small)
130    }
131
132    /// Get margins for [`MarginStyle::Large`]
133    pub fn large_margins(&self) -> Margins {
134        self.0.margins(MarginStyle::Large)
135    }
136
137    /// Get margins for [`MarginStyle::Text`]
138    pub fn text_margins(&self) -> Margins {
139        self.0.margins(MarginStyle::Text)
140    }
141
142    /// Size rules for a feature
143    pub fn feature(&self, feature: Feature, axis: impl Directional) -> SizeRules {
144        self.0.feature(feature, axis.is_vertical())
145    }
146
147    /// Size of a frame around another element
148    pub fn frame(&self, style: FrameStyle, axis: impl Directional) -> FrameRules {
149        self.0.frame(style, axis.is_vertical())
150    }
151
152    /// The height of a line of text using the corresponding font
153    ///
154    /// This method looks up the font face corresponding to the given `class`,
155    /// scales this according to [`Self::dpem`], then measures the line height.
156    /// The result is typically 100% - 150% of the value returned by
157    /// [`Self::dpem`], depending on the font face.
158    ///
159    /// Prefer to use [`Self::text_line_height`] where possible.
160    pub fn line_height(&self, class: TextClass) -> i32 {
161        self.0.line_height(class)
162    }
163
164    /// Get the line-height of a configured text object
165    pub fn text_line_height<T: FormattableText>(&self, text: &Text<T>) -> i32 {
166        text.line_height().expect("not configured").cast_ceil()
167    }
168
169    /// Get [`SizeRules`] for a text element
170    ///
171    /// The [`TextClass`] is used to select a font and controls whether line
172    /// wrapping is enabled.
173    ///
174    /// Horizontal size without wrapping is simply the size the text.
175    /// Horizontal size with wrapping is bounded to some width dependant on the
176    /// theme, and may have non-zero [`Stretch`] depending on the size.
177    ///
178    /// Vertical size is the size of the text with or without wrapping, but with
179    /// the minimum at least the height of one line of text.
180    ///
181    /// Widgets with editable text contents or internal scrolling enabled may
182    /// wish to adjust the result.
183    ///
184    /// Note: this method partially prepares the `text` object. It is not
185    /// required to call this method but it is required to call
186    /// [`ConfigCx::text_configure`] before text display for correct results.
187    pub fn text_rules<T: FormattableText>(&self, text: &mut Text<T>, axis: AxisInfo) -> SizeRules {
188        let class = text.class();
189        self.0.text_rules(text, class, axis)
190    }
191}
192
193/// Theme sizing implementation
194#[autoimpl(for<S: trait + ?Sized, R: Deref<Target = S>> R)]
195pub trait ThemeSize {
196    /// Get the scale factor
197    fn scale_factor(&self) -> f32;
198
199    /// Get the Em size of the standard font in pixels
200    fn dpem(&self) -> f32;
201
202    /// The minimum size of a scrollable area
203    fn min_scroll_size(&self, axis_is_vertical: bool) -> i32;
204
205    /// The length of the grip (draggable handle) on a scroll bar or slider
206    fn grip_len(&self) -> i32;
207
208    /// The width of a vertical scroll bar
209    fn scroll_bar_width(&self) -> i32;
210
211    /// Get margin size
212    fn margins(&self, style: MarginStyle) -> Margins;
213
214    /// Size rules for a feature
215    fn feature(&self, feature: Feature, axis_is_vertical: bool) -> SizeRules;
216
217    /// Align a feature's rect
218    ///
219    /// In case the input `rect` is larger than desired on either axis, it is
220    /// reduced in size and offset within the original `rect` as is preferred.
221    fn align_feature(&self, feature: Feature, rect: Rect, align: AlignPair) -> Rect;
222
223    /// Size of a frame around another element
224    fn frame(&self, style: FrameStyle, axis_is_vertical: bool) -> FrameRules;
225
226    /// The height of a line of text by class
227    ///
228    /// Prefer to use [`SizeCx::text_line_height`] where possible.
229    fn line_height(&self, class: TextClass) -> i32;
230
231    /// Configure a text object, setting font properties
232    fn text_configure(&self, text: &mut dyn SizableText, class: TextClass);
233
234    /// Get [`SizeRules`] for a text element
235    fn text_rules(&self, text: &mut dyn SizableText, class: TextClass, axis: AxisInfo)
236        -> SizeRules;
237}