Struct kas_core::theme::SizeMgr

source ·
pub struct SizeMgr<'a>(_);
Expand description

Size and scale interface

This interface is provided to widgets in crate::Layout::size_rules. It may also be accessed through crate::event::EventMgr::size_mgr, DrawMgr::size_mgr.

Most methods get or calculate the size of some feature. These same features may be drawn through DrawMgr.

Implementations§

Reborrow with a new lifetime

Rust allows references like &T or &mut T to be “reborrowed” through coercion: essentially, the pointer is copied under a new, shorter, lifetime. Until rfcs#1403 lands, reborrows on user types require a method call.

Calling this method is zero-cost.

Examples found in repository?
src/layout/sizer.rs (line 83)
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
pub fn solve_size_rules<W: Widget + ?Sized>(
    widget: &mut W,
    size_mgr: SizeMgr,
    x_size: Option<i32>,
    y_size: Option<i32>,
    h_align: Option<Align>,
    v_align: Option<Align>,
) {
    widget.size_rules(size_mgr.re(), AxisInfo::new(false, y_size, h_align));
    widget.size_rules(size_mgr.re(), AxisInfo::new(true, x_size, v_align));
}

/// Size solver
///
/// This struct is used to solve widget layout, read size constraints and
/// cache the results until the next solver run.
///
/// [`SolveCache::find_constraints`] constructs an instance of this struct,
/// solving for size constraints.
///
/// [`SolveCache::apply_rect`] accepts a [`Rect`], updates constraints as
/// necessary and sets widget positions within this `rect`.
pub struct SolveCache {
    // Technically we don't need to store min and ideal here, but it simplifies
    // the API for very little real cost.
    min: Size,
    ideal: Size,
    margins: Margins,
    refresh_rules: bool,
    last_width: i32,
}

impl SolveCache {
    /// Get the minimum size
    ///
    /// If `inner_margin` is true, margins are included in the result.
    pub fn min(&self, inner_margin: bool) -> Size {
        if inner_margin {
            self.margins.pad(self.min)
        } else {
            self.min
        }
    }

    /// Get the ideal size
    ///
    /// If `inner_margin` is true, margins are included in the result.
    pub fn ideal(&self, inner_margin: bool) -> Size {
        if inner_margin {
            self.margins.pad(self.ideal)
        } else {
            self.ideal
        }
    }

    /// Get the margins
    pub fn margins(&self) -> Margins {
        self.margins
    }

    /// Calculate required size of widget
    ///
    /// Assumes no explicit alignment.
    pub fn find_constraints(widget: &mut dyn Widget, size_mgr: SizeMgr) -> Self {
        let start = std::time::Instant::now();

        let w = widget.size_rules(size_mgr.re(), AxisInfo::new(false, None, None));
        let h = widget.size_rules(
            size_mgr.re(),
            AxisInfo::new(true, Some(w.ideal_size()), None),
        );

        let min = Size(w.min_size(), h.min_size());
        let ideal = Size(w.ideal_size(), h.ideal_size());
        let margins = Margins::hv(w.margins(), h.margins());

        log::trace!(
            target: "kas_perf::layout", "find_constraints: {}μs",
            start.elapsed().as_micros(),
        );
        log::debug!("find_constraints: min={min:?}, ideal={ideal:?}, margins={margins:?}");
        let refresh_rules = false;
        let last_width = ideal.0;
        SolveCache {
            min,
            ideal,
            margins,
            refresh_rules,
            last_width,
        }
    }
More examples
Hide additional examples
src/layout/visitor.rs (line 226)
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
    fn size_rules_(&mut self, mgr: SizeMgr, axis: AxisInfo) -> SizeRules {
        match &mut self.layout {
            LayoutType::None => SizeRules::EMPTY,
            LayoutType::Component(component) => component.size_rules(mgr, axis),
            LayoutType::BoxComponent(component) => component.size_rules(mgr, axis),
            LayoutType::Single(child) => child.size_rules(mgr, axis),
            LayoutType::AlignSingle(child, hints) => {
                child.size_rules(mgr, axis.with_align_hints(*hints))
            }
            LayoutType::Align(layout, hints) => {
                layout.size_rules_(mgr, axis.with_align_hints(*hints))
            }
            LayoutType::Pack(layout, stor, hints) => {
                let rules = layout.size_rules_(mgr, stor.apply_align(axis, *hints));
                stor.size.set_component(axis, rules.ideal_size());
                rules
            }
            LayoutType::Margins(child, dirs, margins) => {
                let mut child_rules = child.size_rules_(mgr.re(), axis);
                if dirs.intersects(Directions::from(axis)) {
                    let mut rule_margins = child_rules.margins();
                    let margins = mgr.margins(*margins).extract(axis);
                    if dirs.intersects(Directions::LEFT | Directions::UP) {
                        rule_margins.0 = margins.0;
                    }
                    if dirs.intersects(Directions::RIGHT | Directions::DOWN) {
                        rule_margins.1 = margins.1;
                    }
                    child_rules.set_margins(rule_margins);
                }
                child_rules
            }
            LayoutType::Frame(child, storage, style) => {
                let child_rules = child.size_rules_(mgr.re(), storage.child_axis(axis));
                storage.size_rules(mgr, axis, child_rules, *style)
            }
            LayoutType::Button(child, storage, _) => {
                let child_rules = child.size_rules_(mgr.re(), storage.child_axis_centered(axis));
                storage.size_rules(mgr, axis, child_rules, FrameStyle::Button)
            }
        }
    }

    /// Apply a given `rect` to self
    #[inline]
    pub fn set_rect(mut self, mgr: &mut ConfigMgr, rect: Rect) {
        self.set_rect_(mgr, rect);
    }
    fn set_rect_(&mut self, mgr: &mut ConfigMgr, rect: Rect) {
        match &mut self.layout {
            LayoutType::None => (),
            LayoutType::Component(component) => component.set_rect(mgr, rect),
            LayoutType::BoxComponent(layout) => layout.set_rect(mgr, rect),
            LayoutType::Single(child) => child.set_rect(mgr, rect),
            LayoutType::Align(layout, _) => layout.set_rect_(mgr, rect),
            LayoutType::AlignSingle(child, _) => child.set_rect(mgr, rect),
            LayoutType::Pack(layout, stor, _) => layout.set_rect_(mgr, stor.aligned_rect(rect)),
            LayoutType::Margins(child, _, _) => child.set_rect_(mgr, rect),
            LayoutType::Frame(child, storage, _) | LayoutType::Button(child, storage, _) => {
                storage.rect = rect;
                let child_rect = Rect {
                    pos: rect.pos + storage.offset,
                    size: rect.size - storage.size,
                };
                child.set_rect_(mgr, child_rect);
            }
        }
    }

    /// Find a widget by coordinate
    ///
    /// Does not return the widget's own identifier. See example usage in
    /// [`Visitor::find_id`].
    #[inline]
    pub fn find_id(mut self, coord: Coord) -> Option<WidgetId> {
        self.find_id_(coord)
    }
    fn find_id_(&mut self, coord: Coord) -> Option<WidgetId> {
        match &mut self.layout {
            LayoutType::None => None,
            LayoutType::Component(component) => component.find_id(coord),
            LayoutType::BoxComponent(layout) => layout.find_id(coord),
            LayoutType::Single(child) | LayoutType::AlignSingle(child, _) => child.find_id(coord),
            LayoutType::Align(layout, _) => layout.find_id_(coord),
            LayoutType::Pack(layout, _, _) => layout.find_id_(coord),
            LayoutType::Margins(layout, _, _) => layout.find_id_(coord),
            LayoutType::Frame(child, _, _) => child.find_id_(coord),
            // Buttons steal clicks, hence Button never returns ID of content
            LayoutType::Button(_, _, _) => None,
        }
    }

    /// Draw a widget's children
    #[inline]
    pub fn draw(mut self, draw: DrawMgr) {
        self.draw_(draw);
    }
    fn draw_(&mut self, mut draw: DrawMgr) {
        match &mut self.layout {
            LayoutType::None => (),
            LayoutType::Component(component) => component.draw(draw),
            LayoutType::BoxComponent(layout) => layout.draw(draw),
            LayoutType::Single(child) | LayoutType::AlignSingle(child, _) => draw.recurse(*child),
            LayoutType::Align(layout, _) => layout.draw_(draw),
            LayoutType::Pack(layout, _, _) => layout.draw_(draw),
            LayoutType::Margins(layout, _, _) => layout.draw_(draw),
            LayoutType::Frame(child, storage, style) => {
                draw.frame(storage.rect, *style, Background::Default);
                child.draw_(draw);
            }
            LayoutType::Button(child, storage, color) => {
                let bg = match color {
                    Some(rgb) => Background::Rgb(*rgb),
                    None => Background::Default,
                };
                draw.frame(storage.rect, FrameStyle::Button, bg);
                child.draw_(draw);
            }
        }
    }
}

/// Implement row/column layout for children
struct List<'a, S, D, I> {
    data: &'a mut S,
    direction: D,
    children: I,
}

impl<'a, S: RowStorage, D: Directional, I> Layout for List<'a, S, D, I>
where
    I: ExactSizeIterator<Item = Visitor<'a>>,
{
    fn size_rules(&mut self, mgr: SizeMgr, axis: AxisInfo) -> SizeRules {
        let dim = (self.direction, self.children.len());
        let mut solver = RowSolver::new(axis, dim, self.data);
        for (n, child) in (&mut self.children).enumerate() {
            solver.for_child(self.data, n, |axis| child.size_rules(mgr.re(), axis));
        }
        solver.finish(self.data)
    }

    fn set_rect(&mut self, mgr: &mut ConfigMgr, rect: Rect) {
        let dim = (self.direction, self.children.len());
        let mut setter = RowSetter::<D, Vec<i32>, _>::new(rect, dim, self.data);

        for (n, child) in (&mut self.children).enumerate() {
            child.set_rect(mgr, setter.child_rect(self.data, n));
        }
    }

    fn find_id(&mut self, coord: Coord) -> Option<WidgetId> {
        // TODO(opt): more efficient search strategy?
        self.children.find_map(|child| child.find_id(coord))
    }

    fn draw(&mut self, mut draw: DrawMgr) {
        for child in &mut self.children {
            child.draw(draw.re_clone());
        }
    }
}

/// Float layout
struct Float<'a, I>
where
    I: DoubleEndedIterator<Item = Visitor<'a>>,
{
    children: I,
}

impl<'a, I> Layout for Float<'a, I>
where
    I: DoubleEndedIterator<Item = Visitor<'a>>,
{
    fn size_rules(&mut self, mgr: SizeMgr, axis: AxisInfo) -> SizeRules {
        let mut rules = SizeRules::EMPTY;
        for child in &mut self.children {
            rules = rules.max(child.size_rules(mgr.re(), axis));
        }
        rules
    }

    fn set_rect(&mut self, mgr: &mut ConfigMgr, rect: Rect) {
        for child in &mut self.children {
            child.set_rect(mgr, rect);
        }
    }

    fn find_id(&mut self, coord: Coord) -> Option<WidgetId> {
        self.children.find_map(|child| child.find_id(coord))
    }

    fn draw(&mut self, mut draw: DrawMgr) {
        let mut iter = (&mut self.children).rev();
        if let Some(first) = iter.next() {
            first.draw(draw.re_clone());
        }
        for child in iter {
            draw.with_pass(|draw| child.draw(draw));
        }
    }
}

/// A row/column over a slice
struct Slice<'a, W: Widget, D: Directional> {
    data: &'a mut DynRowStorage,
    direction: D,
    children: &'a mut [W],
}

impl<'a, W: Widget, D: Directional> Layout for Slice<'a, W, D> {
    fn size_rules(&mut self, mgr: SizeMgr, axis: AxisInfo) -> SizeRules {
        let dim = (self.direction, self.children.len());
        let mut solver = RowSolver::new(axis, dim, self.data);
        for (n, child) in self.children.iter_mut().enumerate() {
            solver.for_child(self.data, n, |axis| child.size_rules(mgr.re(), axis));
        }
        solver.finish(self.data)
    }

    fn set_rect(&mut self, mgr: &mut ConfigMgr, rect: Rect) {
        let dim = (self.direction, self.children.len());
        let mut setter = RowSetter::<D, Vec<i32>, _>::new(rect, dim, self.data);

        for (n, child) in self.children.iter_mut().enumerate() {
            child.set_rect(mgr, setter.child_rect(self.data, n));
        }
    }

    fn find_id(&mut self, coord: Coord) -> Option<WidgetId> {
        let solver = RowPositionSolver::new(self.direction);
        solver
            .find_child_mut(self.children, coord)
            .and_then(|child| child.find_id(coord))
    }

    fn draw(&mut self, mut draw: DrawMgr) {
        let solver = RowPositionSolver::new(self.direction);
        solver.for_children(self.children, draw.get_clip_rect(), |w| draw.recurse(w));
    }
}

/// Implement grid layout for children
struct Grid<'a, S, I> {
    data: &'a mut S,
    dim: GridDimensions,
    children: I,
}

impl<'a, S: GridStorage, I> Layout for Grid<'a, S, I>
where
    I: Iterator<Item = (GridChildInfo, Visitor<'a>)>,
{
    fn size_rules(&mut self, mgr: SizeMgr, axis: AxisInfo) -> SizeRules {
        let mut solver = GridSolver::<Vec<_>, Vec<_>, _>::new(axis, self.dim, self.data);
        for (info, child) in &mut self.children {
            solver.for_child(self.data, info, |axis| child.size_rules(mgr.re(), axis));
        }
        solver.finish(self.data)
    }

Get the scale factor

“Traditional” PC screens have a scale factor of 1; high-DPI screens may have a factor of 2 or higher. This may be fractional and may be adjusted to suit the device type (e.g. a phone or desktop monitor) as well as the user’s preference.

One could use this value to calculate physical size, but be warned that the result may be quite inaccurate on anything other than a desktop monitor: 25.4 mm = 1 inch = (96 * scale_factor) pixels

To calculate screen pixel sizes from virtual pixel sizes:

use kas_core::cast::*;
let size: i32 = (100.0 * scale_factor).cast_ceil();

This value may change during a program’s execution (e.g. when a window is moved to a different monitor); in this case all widgets will be resized via crate::Layout::size_rules.

Examples found in repository?
src/layout/size_types.rs (line 255)
253
254
255
256
257
258
259
260
261
262
263
264
265
266
    pub fn size_rules(&mut self, mgr: SizeMgr, axis: AxisInfo) -> SizeRules {
        let margins = mgr.margins(self.margins).extract(axis);
        let scale_factor = mgr.scale_factor();
        let min = self
            .size
            .to_physical(scale_factor * self.min_factor)
            .extract(axis);
        let ideal = self
            .size
            .to_physical(scale_factor * self.ideal_factor)
            .extract(axis);
        self.align.set_component(axis, axis.align_or_center());
        SizeRules::new(min, ideal, margins, self.stretch)
    }
More examples
Hide additional examples
src/event/manager/mgr_shell.rs (line 255)
253
254
255
256
257
258
259
260
261
262
263
264
265
266
    pub fn update_widgets(&mut self, widget: &mut dyn Widget, id: UpdateId, payload: u64) {
        if id == self.state.config.config.id() {
            let (sf, dpem) = self.size_mgr(|size| (size.scale_factor(), size.dpem()));
            self.state.config.update(sf, dpem);
        }

        let start = Instant::now();
        let count = self.send_all(widget, Event::Update { id, payload });
        log::debug!(
            target: "kas_core::event::manager",
            "update_widgets: sent Event::Update ({id:?}) to {count} widgets in {}μs",
            start.elapsed().as_micros()
        );
    }

The Em size of the standard font in pixels

The Em is a unit of typography (variously defined as the point-size of the font, the height of the font or the width of an upper-case M). The method Self::line_height returns a related but distinct value.

This method returns the size of 1 Em in physical pixels, derived from the font size in use by the theme and the screen’s scale factor.

Examples found in repository?
src/event/manager/mgr_shell.rs (line 255)
253
254
255
256
257
258
259
260
261
262
263
264
265
266
    pub fn update_widgets(&mut self, widget: &mut dyn Widget, id: UpdateId, payload: u64) {
        if id == self.state.config.config.id() {
            let (sf, dpem) = self.size_mgr(|size| (size.scale_factor(), size.dpem()));
            self.state.config.update(sf, dpem);
        }

        let start = Instant::now();
        let count = self.send_all(widget, Event::Update { id, payload });
        log::debug!(
            target: "kas_core::event::manager",
            "update_widgets: sent Event::Update ({id:?}) to {count} widgets in {}μs",
            start.elapsed().as_micros()
        );
    }

The minimum size of a scrollable area

The length of a dragable handle for a scroll bar or slider

This is the length in line with the control. The size on the opposite axis is assumed to be equal to the feature size as reported by Self::feature.

The width of a vertical scroll bar

This value is also available through Self::feature.

Get margin size

Examples found in repository?
src/layout/size_types.rs (line 254)
253
254
255
256
257
258
259
260
261
262
263
264
265
266
    pub fn size_rules(&mut self, mgr: SizeMgr, axis: AxisInfo) -> SizeRules {
        let margins = mgr.margins(self.margins).extract(axis);
        let scale_factor = mgr.scale_factor();
        let min = self
            .size
            .to_physical(scale_factor * self.min_factor)
            .extract(axis);
        let ideal = self
            .size
            .to_physical(scale_factor * self.ideal_factor)
            .extract(axis);
        self.align.set_component(axis, axis.align_or_center());
        SizeRules::new(min, ideal, margins, self.stretch)
    }
More examples
Hide additional examples
src/layout/visitor.rs (line 229)
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
    fn size_rules_(&mut self, mgr: SizeMgr, axis: AxisInfo) -> SizeRules {
        match &mut self.layout {
            LayoutType::None => SizeRules::EMPTY,
            LayoutType::Component(component) => component.size_rules(mgr, axis),
            LayoutType::BoxComponent(component) => component.size_rules(mgr, axis),
            LayoutType::Single(child) => child.size_rules(mgr, axis),
            LayoutType::AlignSingle(child, hints) => {
                child.size_rules(mgr, axis.with_align_hints(*hints))
            }
            LayoutType::Align(layout, hints) => {
                layout.size_rules_(mgr, axis.with_align_hints(*hints))
            }
            LayoutType::Pack(layout, stor, hints) => {
                let rules = layout.size_rules_(mgr, stor.apply_align(axis, *hints));
                stor.size.set_component(axis, rules.ideal_size());
                rules
            }
            LayoutType::Margins(child, dirs, margins) => {
                let mut child_rules = child.size_rules_(mgr.re(), axis);
                if dirs.intersects(Directions::from(axis)) {
                    let mut rule_margins = child_rules.margins();
                    let margins = mgr.margins(*margins).extract(axis);
                    if dirs.intersects(Directions::LEFT | Directions::UP) {
                        rule_margins.0 = margins.0;
                    }
                    if dirs.intersects(Directions::RIGHT | Directions::DOWN) {
                        rule_margins.1 = margins.1;
                    }
                    child_rules.set_margins(rule_margins);
                }
                child_rules
            }
            LayoutType::Frame(child, storage, style) => {
                let child_rules = child.size_rules_(mgr.re(), storage.child_axis(axis));
                storage.size_rules(mgr, axis, child_rules, *style)
            }
            LayoutType::Button(child, storage, _) => {
                let child_rules = child.size_rules_(mgr.re(), storage.child_axis_centered(axis));
                storage.size_rules(mgr, axis, child_rules, FrameStyle::Button)
            }
        }
    }

Get margins for MarginStyle::Inner

Get margins for MarginStyle::Tiny

Get margins for MarginStyle::Small

Get margins for MarginStyle::Large

Get margins for MarginStyle::Text

Size rules for a feature

Size of a frame around another element

Examples found in repository?
src/layout/visitor.rs (line 543)
536
537
538
539
540
541
542
543
544
545
546
547
548
    pub fn size_rules(
        &mut self,
        mgr: SizeMgr,
        axis: AxisInfo,
        child_rules: SizeRules,
        style: FrameStyle,
    ) -> SizeRules {
        let frame_rules = mgr.frame(style, axis);
        let (rules, offset, size) = frame_rules.surround(child_rules);
        self.offset.set_component(axis, offset);
        self.size.set_component(axis, size);
        rules
    }

The height of a line of text using the corresponding font

This method looks up the font face corresponding to the given class, scales this according to Self::dpem, then measures the line height. The result is typically 100% - 150% of the value returned by Self::dpem, depending on the font face.

Get SizeRules for a text element

The TextClass is used to select a font and controls whether line wrapping is enabled.

Alignment is set from AxisInfo::align_or_default. If other alignment is desired, modify axis before calling this method.

Horizontal size without wrapping is simply the size the text. Horizontal size with wrapping is bounded to some width dependant on the theme, and may have non-zero Stretch depending on the size.

Vertical size is the size of the text with or without wrapping, but with the minimum at least the height of one line of text.

Widgets with editable text contents or internal scrolling enabled may wish to adjust the result.

Note: this method partially prepares the text object. It is not required to call this method but it is required to call ConfigMgr::text_set_size before text display for correct results.

Examples found in repository?
src/label.rs (line 51)
49
50
51
52
        fn size_rules(&mut self, size_mgr: SizeMgr, mut axis: AxisInfo) -> SizeRules {
            axis.set_default_align_hv(Align::Default, Align::Center);
            size_mgr.text_rules(&mut self.label, Self::CLASS, axis)
        }

Auto Trait Implementations§

Blanket Implementations§

Gets the TypeId of self. Read more
Immutably borrows from an owned value. Read more
Mutably borrows from an owned value. Read more
Cast from Self to T Read more
Try converting from Self to T Read more
Try approximate conversion from Self to T Read more
Cast approximately from Self to T Read more
Cast to integer, truncating Read more
Cast to the nearest integer Read more
Cast the floor to an integer Read more
Cast the ceiling to an integer Read more
Try converting to integer with truncation Read more
Try converting to the nearest integer Read more
Try converting the floor to an integer Read more
Try convert the ceiling to an integer Read more

Returns the argument unchanged.

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

The type returned in the event of a conversion error.
Performs the conversion.
The type returned in the event of a conversion error.
Performs the conversion.