Struct kas_core::layout::SizeRules

source ·
pub struct SizeRules { /* private fields */ }
Expand description

Widget sizing information

This is the return value of crate::Layout::size_rules and is used to describe size and margin requirements for widgets. This type only concerns size requirements along a single axis.

All units are in pixels. Sizes usually come directly from SizeMgr or from a fixed quantity multiplied by SizeMgr::scale_factor.

Sizes

The widget size model is simple: a rectangular box, plus a margin on each side. The SizeRules type represents expectations along a single axis:

  • The minimum acceptable size (almost always met)
  • The ideal size (often the same size; this distinction is most useful for scrollable regions which are ideally large enough not to require scrolling, but can be much smaller)
  • A Stretch priority, used to prioritize allocation of excess space

Note that Stretch::None does not prevent stretching, but simply states that it is undesired (lowest priority). Actually preventing stretching requires alignment.

Margins

Required margin sizes are handled separately for each side of a widget. Since SizeRules concerns only one axis, it stores only two margin sizes: “pre” (left/top) and “post” (right/bottom). These are stored as u16 values on the assumption that no margin need exceed 65536.

When widgets are placed next to each other, their margins may be combined; e.g. if a widget with margin of 6px is followed by another with margin 2px, the required margin between the two is the maximum, 6px.

Only the layout engine and parent widgets need consider margins (beyond their specification). For these cases, one needs to be aware that due to margin-merging behaviour, one cannot simply “add” two SizeRules. Instead, when placing one widget next to another, use SizeRules::append or SizeRules::appended; when placing a widget within a frame, use FrameRules::surround. When calculating the size of a sequence of widgets, one may use the Sum implementation (this assumes that the sequence is in left-to-right or top-to-bottom order).

Alignment

SizeRules concerns calculations of size requirements, which the layout engine uses to assign each widget a Rect; it is up to the widget itself to either fill this rect or align itself within the given space. See crate::Layout::set_rect for more information.

For widgets with a stretch priority of Stretch::None, it is still possible for layout code to assign a size larger than the preference. It is up to the widget to align itself within this space: see crate::Layout::set_rect and crate::layout::AlignHints.

Implementations§

Empty (zero size) widget

Warning: appending another size to EMPTY does include margins even though EMPTY itself has zero size. However, EMPTY itself has zero-size margins, so this only affects appending an EMPTY with a non-empty SizeRules.

Empty space with the given stretch priority

See warning on SizeRules::EMPTY.

Examples found in repository?
src/layout/size_rules.rs (line 96)
96
    pub const EMPTY: Self = SizeRules::empty(Stretch::None);

A fixed size with given (pre, post) margins

Examples found in repository?
src/layout/size_rules.rs (line 126)
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
    pub fn fixed_splat(size: i32, margin: u16) -> Self {
        Self::fixed(size, (margin, margin))
    }

    /// A fixed size, scaled from virtual pixels
    ///
    /// This is a shortcut to [`SizeRules::fixed`] using virtual-pixel sizes
    /// and a scale factor. It also assumes both margins are equal.
    #[inline]
    pub fn fixed_scaled(size: f32, margins: f32, scale_factor: f32) -> Self {
        debug_assert!(size >= 0.0 && margins >= 0.0);
        let size = (scale_factor * size).cast_nearest();
        let m = (scale_factor * margins).cast_nearest();
        SizeRules::fixed(size, (m, m))
    }
More examples
Hide additional examples
src/layout/size_types.rs (line 38)
37
38
39
    pub fn to_rules(self, dir: impl Directional, scale_factor: f32) -> SizeRules {
        SizeRules::fixed(self.extract_scaled(dir, scale_factor), (0, 0))
    }

A fixed size with given (symmetric) margin

A fixed size, scaled from virtual pixels

This is a shortcut to SizeRules::fixed using virtual-pixel sizes and a scale factor. It also assumes both margins are equal.

Construct rules from given data

Examples found in repository?
src/layout/size_rules.rs (line 152)
151
152
153
    pub fn extract_fixed<D: Directional>(dir: D, size: Size, margin: Margins) -> Self {
        SizeRules::extract(dir, size, margin, Stretch::None)
    }

Construct fixed-size rules from given data

Construct with custom rules

Region size should meet the given min-imum size and has a given ideal size, plus a given stretch priority.

Expected: ideal >= min (if not, ideal is clamped to min).

Examples found in repository?
src/layout/size_rules.rs (line 146)
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
    pub fn extract<D: Directional>(dir: D, size: Size, margins: Margins, stretch: Stretch) -> Self {
        let size = size.extract(dir);
        let m = margins.extract(dir);
        SizeRules::new(size, size, m, stretch)
    }

    /// Construct fixed-size rules from given data
    #[inline]
    pub fn extract_fixed<D: Directional>(dir: D, size: Size, margin: Margins) -> Self {
        SizeRules::extract(dir, size, margin, Stretch::None)
    }

    /// Construct with custom rules
    ///
    /// Region size should meet the given `min`-imum size and has a given
    /// `ideal` size, plus a given `stretch` priority.
    ///
    /// Expected: `ideal >= min` (if not, ideal is clamped to min).
    #[inline]
    pub fn new(min: i32, ideal: i32, margins: (u16, u16), stretch: Stretch) -> Self {
        debug_assert!(0 <= min && 0 <= ideal);
        SizeRules {
            a: min,
            b: ideal.max(min),
            m: margins,
            stretch,
        }
    }

    /// Set stretch factor, inline
    #[inline]
    pub fn with_stretch(self, stretch: Stretch) -> Self {
        Self::new(self.a, self.b, self.m, stretch)
    }
More examples
Hide additional examples
src/layout/size_types.rs (line 52)
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
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
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
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
    pub fn to_rules_with_factor(
        self,
        dir: impl Directional,
        scale_factor: f32,
        ideal_factor: f32,
    ) -> SizeRules {
        let min = self.extract_scaled(dir, scale_factor);
        let ideal = self.extract_scaled(dir, scale_factor * ideal_factor);
        SizeRules::new(min, ideal, (0, 0), Stretch::None)
    }

    /// Take horizontal/vertical axis component
    pub fn extract(self, dir: impl Directional) -> f32 {
        match dir.is_vertical() {
            false => self.0,
            true => self.1,
        }
    }

    /// Take component and scale
    pub fn extract_scaled(self, dir: impl Directional, scale_factor: f32) -> i32 {
        (self.extract(dir) * scale_factor).cast_nearest()
    }
}

impl From<(f32, f32)> for LogicalSize {
    #[inline]
    fn from((w, h): (f32, f32)) -> Self {
        LogicalSize(w, h)
    }
}

impl Conv<(i32, i32)> for LogicalSize {
    #[inline]
    fn try_conv((w, h): (i32, i32)) -> Result<Self> {
        Ok(LogicalSize(w.try_cast()?, h.try_cast()?))
    }
}

impl Conv<(u32, u32)> for LogicalSize {
    #[inline]
    fn try_conv((w, h): (u32, u32)) -> Result<Self> {
        Ok(LogicalSize(w.try_cast()?, h.try_cast()?))
    }
}

impl Conv<Size> for LogicalSize {
    #[inline]
    fn try_conv(size: Size) -> Result<Self> {
        Ok(LogicalSize(size.0.try_cast()?, size.1.try_cast()?))
    }
}

impl From<Vec2> for LogicalSize {
    #[inline]
    fn from(Vec2(w, h): Vec2) -> Self {
        LogicalSize(w, h)
    }
}

impl From<LogicalSize> for Vec2 {
    #[inline]
    fn from(LogicalSize(w, h): LogicalSize) -> Self {
        Vec2(w, h)
    }
}

/// Margin sizes
///
/// Used by the layout system for margins around child widgets. Margins may be
/// drawn in and handle events like any other widget area.
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
pub struct Margins {
    /// Size of horizontal margins
    pub horiz: (u16, u16),
    /// Size of vertical margins
    pub vert: (u16, u16),
}

impl Margins {
    /// Zero-sized margins
    pub const ZERO: Margins = Margins::splat(0);

    /// Margins with equal size on each edge.
    #[inline]
    pub const fn splat(size: u16) -> Self {
        Margins::hv_splat((size, size))
    }

    /// Margins via horizontal and vertical sizes
    #[inline]
    pub const fn hv(horiz: (u16, u16), vert: (u16, u16)) -> Self {
        Margins { horiz, vert }
    }

    /// Margins via horizontal and vertical sizes
    #[inline]
    pub const fn hv_splat((h, v): (u16, u16)) -> Self {
        Margins {
            horiz: (h, h),
            vert: (v, v),
        }
    }

    /// Sum of horizontal margins
    #[inline]
    pub fn sum_horiz(&self) -> i32 {
        i32::from(self.horiz.0) + i32::from(self.horiz.1)
    }

    /// Sum of vertical margins
    #[inline]
    pub fn sum_vert(&self) -> i32 {
        i32::from(self.vert.0) + i32::from(self.vert.1)
    }

    /// Pad a size with margins
    pub fn pad(self, size: Size) -> Size {
        Size::new(size.0 + self.sum_horiz(), size.1 + self.sum_vert())
    }

    /// Extract one component, based on a direction
    ///
    /// This merely extracts the horizontal or vertical component.
    /// It never negates it, even if the axis is reversed.
    #[inline]
    pub fn extract<D: Directional>(self, dir: D) -> (u16, u16) {
        match dir.is_vertical() {
            false => self.horiz,
            true => self.vert,
        }
    }
}

impl From<Size> for Margins {
    fn from(size: Size) -> Self {
        Margins::hv_splat(size.cast())
    }
}

/// Priority for stretching widgets beyond ideal size
///
/// When more space is available than required to meet widgets' "ideal size",
/// that extra space is allocated based on the `Stretch` priority: the widget(s)
/// with the highest priority level represented are allocated extra space (the
/// excess is evenly divided between these widgets).
///
/// Note that `Stretch` only affects how much space is *made available*, not
/// how that space is used. By default, widgets expand to fill all space made
/// available to them; any other behaviour requires alignment.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
pub enum Stretch {
    /// Prefer no stretching
    ///
    /// This is the default value and indicates that stretching is undesirable,
    /// but does not prevent it.
    None,
    /// Fill unwanted space
    ///
    /// The main use of this is to force layout with a `Filler` widget.
    Filler,
    /// Extra space is considered of low utility (but higher than `Filler`)
    Low,
    /// Extra space is considered of high utility
    High,
    /// Greedily consume as much space as possible
    Maximize,
}

impl Default for Stretch {
    fn default() -> Self {
        Stretch::None
    }
}

impl_scope! {
    /// Control over scaling
    #[impl_default]
    #[derive(Clone, Debug, PartialEq)]
    pub struct PixmapScaling {
        /// Margins
        pub margins: MarginStyle,
        /// Display size
        ///
        /// This may be set by the providing type or by the user.
        pub size: LogicalSize,
        /// Minimum size relative to [`Self::size`]
        ///
        /// Default: `1.0`
        pub min_factor: f32 = 1.0,
        /// Ideal size relative to [`Self::size`]
        ///
        /// Default: `1.0`
        pub ideal_factor: f32 = 1.0,
        /// If true, aspect ratio is fixed relative to [`Self.size`]
        ///
        /// Default: `true`
        pub fix_aspect: bool = true,
        /// Widget stretchiness
        ///
        /// If is `None`, max size is limited to ideal size.
        pub stretch: Stretch,
        /// Alignment (set by `Self::size_rules`)
        align: AlignPair,
    }
}

impl PixmapScaling {
    /// Generates `size_rules` based on size
    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)
    }

    /// Constrains and aligns within `rect`
    ///
    /// The resulting size is then aligned using the `align` hints, defaulting to centered.
    pub fn align_rect(&mut self, rect: Rect, scale_factor: f32) -> Rect {
        let mut size = rect.size;

        if self.stretch == Stretch::None {
            let ideal = self.size.to_physical(scale_factor * self.ideal_factor);
            size = size.min(ideal);
        }

        if self.fix_aspect {
            let logical_size = Vec2::from(self.size);
            let Vec2(rw, rh) = Vec2::conv(size) / logical_size;

            // Use smaller ratio, if any is finite
            if rw < rh {
                size.1 = i32::conv_nearest(rw * logical_size.1);
            } else if rh < rw {
                size.0 = i32::conv_nearest(rh * logical_size.0);
            }
        }

        self.align.aligned_rect(size, rect)
    }
}

/// Frame size rules
///
/// This is a special variant of [`SizeRules`] for frames. It is assumed that
/// frames are not stretchy (i.e. that min-size equals ideal-size); additionally
/// frame rules have a content offset and a minimum internal margin size.
#[derive(Clone, Copy, Debug)]
pub struct FrameRules {
    // (pre, post) pairs
    size: i32,
    inner: (u16, u16),
    outer: (u16, u16),
}

impl FrameRules {
    pub const ZERO: Self = FrameRules::new_sym(0, 0, 0);

    /// Construct new `FrameRules`
    ///
    /// All parameters use pairs `(first, second)` where `first` is the top/left
    /// component. Parameters `inner` and `outer` are inner and outer margin
    /// sizes respectively while `size` is the frame size.
    ///
    /// If `size > 0` then internal margins are the maximum of `inner` and
    /// content margin; generated rules have size
    /// `content_size + size + inner_margin` and outer margin `outer`.
    ///
    /// If `size ≤ 0` then the generated rules are simply content rules but
    /// with margins the maximum of `inner` and content margins; `outer` and
    /// `size` are ignored (other than to enable this mode).
    #[inline]
    pub const fn new(size: i32, inner: (u16, u16), outer: (u16, u16)) -> Self {
        FrameRules { size, inner, outer }
    }

    /// Construct (symmetric on axis)
    #[inline]
    pub const fn new_sym(size: i32, inner: u16, outer: u16) -> Self {
        Self::new(size, (inner, inner), (outer, outer))
    }

    /// Generate rules for content surrounded by this frame
    ///
    /// Returns the tuple `(rules, offset, size)`:
    ///
    /// -   the generated `rules`
    /// -   the content `offset` within the allocated rect
    /// -   the size consumed by the frame and inner margins (thus the content's
    ///     size will be that allocated for this object minus this `size` value)
    pub fn surround(self, content: SizeRules) -> (SizeRules, i32, i32) {
        if self.size > 0 {
            let (m0, m1) = content.margins();
            let m0 = m0.max(self.inner.0);
            let m1 = m1.max(self.inner.1);

            let offset = self.size + i32::conv(m0);
            let size = offset + self.size + i32::conv(m1);

            let rules = SizeRules::new(
                content.min_size() + size,
                content.ideal_size() + size,
                self.outer,
                content.stretch(),
            );
            (rules, offset, size)
        } else {
            let mut rules = content;
            rules.include_margins(self.inner);
            (rules, 0, 0)
        }
    }

Set stretch factor, inline

Get the minimum size

Examples found in repository?
src/layout/size_types.rs (line 353)
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
    pub fn surround(self, content: SizeRules) -> (SizeRules, i32, i32) {
        if self.size > 0 {
            let (m0, m1) = content.margins();
            let m0 = m0.max(self.inner.0);
            let m1 = m1.max(self.inner.1);

            let offset = self.size + i32::conv(m0);
            let size = offset + self.size + i32::conv(m1);

            let rules = SizeRules::new(
                content.min_size() + size,
                content.ideal_size() + size,
                self.outer,
                content.stretch(),
            );
            (rules, offset, size)
        } else {
            let mut rules = content;
            rules.include_margins(self.inner);
            (rules, 0, 0)
        }
    }
More examples
Hide additional examples
src/layout/row_solver.rs (line 226)
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
    fn maximal_rect_of(&mut self, storage: &mut Self::Storage, index: Self::ChildInfo) -> Rect {
        let pre_rules = SizeRules::min_sum(&storage.rules()[0..index]);
        let m = storage.rules()[index].margins();
        let len = storage.widths().len();
        let post_rules = SizeRules::min_sum(&storage.rules()[(index + 1)..len]);

        let size1 = pre_rules.min_size() + i32::from(pre_rules.margins().1.max(m.0));
        let size2 = size1 + post_rules.min_size() + i32::from(post_rules.margins().0.max(m.1));

        let mut rect = self.rect;
        if self.direction.is_horizontal() {
            rect.pos.0 = self.rect.pos.0 + size1;
            rect.size.0 = (self.rect.size.0 - size2).max(0);
        } else {
            rect.pos.1 = self.rect.pos.1 + size1;
            rect.size.1 = (self.rect.size.1 - size2).max(0);
        }
        rect
    }
src/layout/sizer.rs (line 147)
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
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
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
    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,
        }
    }

    /// Force updating of size rules
    ///
    /// This should be called whenever widget size rules have been changed. It
    /// forces [`SolveCache::apply_rect`] to recompute these rules when next
    /// called.
    pub fn invalidate_rule_cache(&mut self) {
        self.refresh_rules = true;
    }

    /// Apply layout solution to a widget
    ///
    /// The widget's layout is solved for the given `rect` and assigned.
    /// If `inner_margin` is true, margins are internal to this `rect`; if not,
    /// the caller is responsible for handling margins.
    ///
    /// If [`SolveCache::invalidate_rule_cache`] was called since rules were
    /// last calculated then this method will recalculate all rules; otherwise
    /// it will only do so if necessary (when dimensions do not match those
    /// last used).
    pub fn apply_rect(
        &mut self,
        widget: &mut dyn Widget,
        mgr: &mut ConfigMgr,
        mut rect: Rect,
        inner_margin: bool,
        print_heirarchy: bool,
    ) {
        let start = std::time::Instant::now();

        let mut width = rect.size.0;
        if inner_margin {
            width -= self.margins.sum_horiz();
        }

        // We call size_rules not because we want the result, but to allow
        // internal layout solving.
        if self.refresh_rules || width != self.last_width {
            if self.refresh_rules {
                let w = widget.size_rules(mgr.size_mgr(), AxisInfo::new(false, None, None));
                self.min.0 = w.min_size();
                self.ideal.0 = w.ideal_size();
                self.margins.horiz = w.margins();
                width = rect.size.0 - self.margins.sum_horiz();
            }

            let h = widget.size_rules(mgr.size_mgr(), AxisInfo::new(true, Some(width), None));
            self.min.1 = h.min_size();
            self.ideal.1 = h.ideal_size();
            self.margins.vert = h.margins();
            self.last_width = width;
        }

        if inner_margin {
            rect.pos += Size::conv((self.margins.horiz.0, self.margins.vert.0));
            rect.size.0 = width;
            rect.size.1 -= self.margins.sum_vert();
        }
        widget.set_rect(mgr, rect);

        log::trace!(target: "kas_perf::layout", "apply_rect: {}μs", start.elapsed().as_micros());
        if print_heirarchy {
            log::trace!(
                target: "kas_core::layout::hierarchy",
                "apply_rect: rect={rect:?}:{}",
                WidgetHeirarchy(widget, 0),
            );
        }

        self.refresh_rules = false;
    }

Get the ideal size

Examples found in repository?
src/layout/size_types.rs (line 354)
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
    pub fn surround(self, content: SizeRules) -> (SizeRules, i32, i32) {
        if self.size > 0 {
            let (m0, m1) = content.margins();
            let m0 = m0.max(self.inner.0);
            let m1 = m1.max(self.inner.1);

            let offset = self.size + i32::conv(m0);
            let size = offset + self.size + i32::conv(m1);

            let rules = SizeRules::new(
                content.min_size() + size,
                content.ideal_size() + size,
                self.outer,
                content.stretch(),
            );
            (rules, offset, size)
        } else {
            let mut rules = content;
            rules.include_margins(self.inner);
            (rules, 0, 0)
        }
    }
More examples
Hide additional examples
src/layout/sizer.rs (line 144)
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
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
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
    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,
        }
    }

    /// Force updating of size rules
    ///
    /// This should be called whenever widget size rules have been changed. It
    /// forces [`SolveCache::apply_rect`] to recompute these rules when next
    /// called.
    pub fn invalidate_rule_cache(&mut self) {
        self.refresh_rules = true;
    }

    /// Apply layout solution to a widget
    ///
    /// The widget's layout is solved for the given `rect` and assigned.
    /// If `inner_margin` is true, margins are internal to this `rect`; if not,
    /// the caller is responsible for handling margins.
    ///
    /// If [`SolveCache::invalidate_rule_cache`] was called since rules were
    /// last calculated then this method will recalculate all rules; otherwise
    /// it will only do so if necessary (when dimensions do not match those
    /// last used).
    pub fn apply_rect(
        &mut self,
        widget: &mut dyn Widget,
        mgr: &mut ConfigMgr,
        mut rect: Rect,
        inner_margin: bool,
        print_heirarchy: bool,
    ) {
        let start = std::time::Instant::now();

        let mut width = rect.size.0;
        if inner_margin {
            width -= self.margins.sum_horiz();
        }

        // We call size_rules not because we want the result, but to allow
        // internal layout solving.
        if self.refresh_rules || width != self.last_width {
            if self.refresh_rules {
                let w = widget.size_rules(mgr.size_mgr(), AxisInfo::new(false, None, None));
                self.min.0 = w.min_size();
                self.ideal.0 = w.ideal_size();
                self.margins.horiz = w.margins();
                width = rect.size.0 - self.margins.sum_horiz();
            }

            let h = widget.size_rules(mgr.size_mgr(), AxisInfo::new(true, Some(width), None));
            self.min.1 = h.min_size();
            self.ideal.1 = h.ideal_size();
            self.margins.vert = h.margins();
            self.last_width = width;
        }

        if inner_margin {
            rect.pos += Size::conv((self.margins.horiz.0, self.margins.vert.0));
            rect.size.0 = width;
            rect.size.1 -= self.margins.sum_vert();
        }
        widget.set_rect(mgr, rect);

        log::trace!(target: "kas_perf::layout", "apply_rect: {}μs", start.elapsed().as_micros());
        if print_heirarchy {
            log::trace!(
                target: "kas_core::layout::hierarchy",
                "apply_rect: rect={rect:?}:{}",
                WidgetHeirarchy(widget, 0),
            );
        }

        self.refresh_rules = false;
    }
src/layout/visitor.rs (line 222)
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 the (pre, post) margin sizes

Examples found in repository?
src/layout/size_types.rs (line 345)
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
    pub fn surround(self, content: SizeRules) -> (SizeRules, i32, i32) {
        if self.size > 0 {
            let (m0, m1) = content.margins();
            let m0 = m0.max(self.inner.0);
            let m1 = m1.max(self.inner.1);

            let offset = self.size + i32::conv(m0);
            let size = offset + self.size + i32::conv(m1);

            let rules = SizeRules::new(
                content.min_size() + size,
                content.ideal_size() + size,
                self.outer,
                content.stretch(),
            );
            (rules, offset, size)
        } else {
            let mut rules = content;
            rules.include_margins(self.inner);
            (rules, 0, 0)
        }
    }
More examples
Hide additional examples
src/layout/row_solver.rs (line 222)
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
    fn maximal_rect_of(&mut self, storage: &mut Self::Storage, index: Self::ChildInfo) -> Rect {
        let pre_rules = SizeRules::min_sum(&storage.rules()[0..index]);
        let m = storage.rules()[index].margins();
        let len = storage.widths().len();
        let post_rules = SizeRules::min_sum(&storage.rules()[(index + 1)..len]);

        let size1 = pre_rules.min_size() + i32::from(pre_rules.margins().1.max(m.0));
        let size2 = size1 + post_rules.min_size() + i32::from(post_rules.margins().0.max(m.1));

        let mut rect = self.rect;
        if self.direction.is_horizontal() {
            rect.pos.0 = self.rect.pos.0 + size1;
            rect.size.0 = (self.rect.size.0 - size2).max(0);
        } else {
            rect.pos.1 = self.rect.pos.1 + size1;
            rect.size.1 = (self.rect.size.1 - size2).max(0);
        }
        rect
    }
src/layout/sizer.rs (line 149)
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
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
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
    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,
        }
    }

    /// Force updating of size rules
    ///
    /// This should be called whenever widget size rules have been changed. It
    /// forces [`SolveCache::apply_rect`] to recompute these rules when next
    /// called.
    pub fn invalidate_rule_cache(&mut self) {
        self.refresh_rules = true;
    }

    /// Apply layout solution to a widget
    ///
    /// The widget's layout is solved for the given `rect` and assigned.
    /// If `inner_margin` is true, margins are internal to this `rect`; if not,
    /// the caller is responsible for handling margins.
    ///
    /// If [`SolveCache::invalidate_rule_cache`] was called since rules were
    /// last calculated then this method will recalculate all rules; otherwise
    /// it will only do so if necessary (when dimensions do not match those
    /// last used).
    pub fn apply_rect(
        &mut self,
        widget: &mut dyn Widget,
        mgr: &mut ConfigMgr,
        mut rect: Rect,
        inner_margin: bool,
        print_heirarchy: bool,
    ) {
        let start = std::time::Instant::now();

        let mut width = rect.size.0;
        if inner_margin {
            width -= self.margins.sum_horiz();
        }

        // We call size_rules not because we want the result, but to allow
        // internal layout solving.
        if self.refresh_rules || width != self.last_width {
            if self.refresh_rules {
                let w = widget.size_rules(mgr.size_mgr(), AxisInfo::new(false, None, None));
                self.min.0 = w.min_size();
                self.ideal.0 = w.ideal_size();
                self.margins.horiz = w.margins();
                width = rect.size.0 - self.margins.sum_horiz();
            }

            let h = widget.size_rules(mgr.size_mgr(), AxisInfo::new(true, Some(width), None));
            self.min.1 = h.min_size();
            self.ideal.1 = h.ideal_size();
            self.margins.vert = h.margins();
            self.last_width = width;
        }

        if inner_margin {
            rect.pos += Size::conv((self.margins.horiz.0, self.margins.vert.0));
            rect.size.0 = width;
            rect.size.1 -= self.margins.sum_vert();
        }
        widget.set_rect(mgr, rect);

        log::trace!(target: "kas_perf::layout", "apply_rect: {}μs", start.elapsed().as_micros());
        if print_heirarchy {
            log::trace!(
                target: "kas_core::layout::hierarchy",
                "apply_rect: rect={rect:?}:{}",
                WidgetHeirarchy(widget, 0),
            );
        }

        self.refresh_rules = false;
    }
src/layout/visitor.rs (line 228)
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 the (pre, post) margin sizes, cast to i32

Examples found in repository?
src/layout/row_solver.rs (line 181)
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
    pub fn update_offsets(&mut self, storage: &mut S) {
        let offsets = self.offsets.as_mut();
        let len = offsets.len();
        if len == 0 {
            return;
        }

        let pos = if self.direction.is_horizontal() {
            self.rect.pos.0
        } else {
            self.rect.pos.1
        };

        if self.direction.is_reversed() {
            offsets[len - 1] = pos;
            for i in (0..(len - 1)).rev() {
                let i1 = i + 1;
                let m1 = storage.rules()[i1].margins_i32().1;
                let m0 = storage.rules()[i].margins_i32().0;
                offsets[i] = offsets[i1] + storage.widths()[i1] + m1.max(m0);
            }
        } else {
            offsets[0] = pos;
            for i in 1..len {
                let i1 = i - 1;
                let m1 = storage.rules()[i1].margins_i32().1;
                let m0 = storage.rules()[i].margins_i32().0;
                offsets[i] = offsets[i1] + storage.widths()[i1] + m1.max(m0);
            }
        }
    }
More examples
Hide additional examples
src/layout/grid_solver.rs (line 257)
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
    pub fn new(rect: Rect, dim: GridDimensions, storage: &mut S) -> Self {
        let (cols, rows) = (dim.cols.cast(), dim.rows.cast());
        let mut w_offsets = CT::default();
        w_offsets.set_len(cols);
        let mut h_offsets = RT::default();
        h_offsets.set_len(rows);

        storage.set_dims(cols, rows);

        if cols > 0 {
            let (widths, rules) = storage.widths_and_rules();
            let target = rect.size.0;
            SizeRules::solve_seq(widths, rules, target);

            w_offsets.as_mut()[0] = 0;
            for i in 1..w_offsets.as_mut().len() {
                let i1 = i - 1;
                let m1 = storage.width_rules()[i1].margins_i32().1;
                let m0 = storage.width_rules()[i].margins_i32().0;
                w_offsets.as_mut()[i] = w_offsets.as_mut()[i1] + storage.widths()[i1] + m1.max(m0);
            }
        }

        if rows > 0 {
            let (heights, rules) = storage.heights_and_rules();
            let target = rect.size.1;
            SizeRules::solve_seq(heights, rules, target);

            h_offsets.as_mut()[0] = 0;
            for i in 1..h_offsets.as_mut().len() {
                let i1 = i - 1;
                let m1 = storage.height_rules()[i1].margins_i32().1;
                let m0 = storage.height_rules()[i].margins_i32().0;
                h_offsets.as_mut()[i] = h_offsets.as_mut()[i1] + storage.heights()[i1] + m1.max(m0);
            }
        }

        GridSetter {
            w_offsets,
            h_offsets,
            pos: rect.pos,
            _s: Default::default(),
        }
    }

Get the stretch priority

Examples found in repository?
src/layout/size_types.rs (line 356)
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
    pub fn surround(self, content: SizeRules) -> (SizeRules, i32, i32) {
        if self.size > 0 {
            let (m0, m1) = content.margins();
            let m0 = m0.max(self.inner.0);
            let m1 = m1.max(self.inner.1);

            let offset = self.size + i32::conv(m0);
            let size = offset + self.size + i32::conv(m1);

            let rules = SizeRules::new(
                content.min_size() + size,
                content.ideal_size() + size,
                self.outer,
                content.stretch(),
            );
            (rules, offset, size)
        } else {
            let mut rules = content;
            rules.include_margins(self.inner);
            (rules, 0, 0)
        }
    }
More examples
Hide additional examples
src/layout/grid_solver.rs (line 187)
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
        fn calculate(widths: &mut [SizeRules], spans: &mut [(SizeRules, u32, u32)]) -> SizeRules {
            // spans: &mut [(rules, begin, end)]

            // To avoid losing Stretch, we distribute this first
            const BASE_WEIGHT: u32 = 100;
            const SPAN_WEIGHT: u32 = 10;
            let mut scores: Vec<u32> = widths
                .iter()
                .map(|w| w.stretch() as u32 * BASE_WEIGHT)
                .collect();
            for span in spans.iter() {
                let w = span.0.stretch() as u32 * SPAN_WEIGHT;
                for score in &mut scores[(usize::conv(span.1))..(usize::conv(span.2))] {
                    *score += w;
                }
            }
            for span in spans.iter() {
                let range = (usize::conv(span.1))..(usize::conv(span.2));
                span.0
                    .distribute_stretch_over_by(&mut widths[range.clone()], &scores[range]);
            }

            // Sort spans to apply smallest first
            spans.sort_by_key(|span| span.2.saturating_sub(span.1));

            // We are left with non-overlapping spans.
            // For each span, we ensure cell widths are sufficiently large.
            for span in spans {
                let rules = span.0;
                let begin = usize::conv(span.1);
                let end = usize::conv(span.2);
                rules.distribute_span_over(&mut widths[begin..end]);
            }

            SizeRules::sum(widths)
        }

Set the stretch priority

Set margins

Examples found in repository?
src/layout/visitor.rs (line 236)
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)
            }
        }
    }

Set margins to max of own margins and given margins

Examples found in repository?
src/layout/size_types.rs (line 361)
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
    pub fn surround(self, content: SizeRules) -> (SizeRules, i32, i32) {
        if self.size > 0 {
            let (m0, m1) = content.margins();
            let m0 = m0.max(self.inner.0);
            let m1 = m1.max(self.inner.1);

            let offset = self.size + i32::conv(m0);
            let size = offset + self.size + i32::conv(m1);

            let rules = SizeRules::new(
                content.min_size() + size,
                content.ideal_size() + size,
                self.outer,
                content.stretch(),
            );
            (rules, offset, size)
        } else {
            let mut rules = content;
            rules.include_margins(self.inner);
            (rules, 0, 0)
        }
    }

Use the maximum size of self and rhs.

Examples found in repository?
src/layout/size_rules.rs (line 241)
240
241
242
    pub fn max_with(&mut self, rhs: Self) {
        *self = self.max(rhs);
    }
More examples
Hide additional examples
src/layout/visitor.rs (line 386)
383
384
385
386
387
388
389
    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
    }
src/layout/row_solver.rs (line 88)
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
    fn for_child<CR: FnOnce(AxisInfo) -> SizeRules>(
        &mut self,
        storage: &mut Self::Storage,
        index: Self::ChildInfo,
        child_rules: CR,
    ) {
        if self.axis.has_fixed && self.axis_is_vertical {
            self.axis.other_axis = storage.widths()[index];
        }
        let child_rules = child_rules(self.axis);

        if !self.axis_is_vertical {
            storage.rules()[index] = child_rules;
            if let Some(rules) = self.rules {
                if self.axis_is_reversed {
                    self.rules = Some(child_rules.appended(rules));
                } else {
                    self.rules = Some(rules.appended(child_rules));
                }
            } else {
                self.rules = Some(child_rules);
            }
        } else {
            self.rules = Some(
                self.rules
                    .map(|rules| rules.max(child_rules))
                    .unwrap_or(child_rules),
            );
        }
    }

Set self = self.max(rhs);

Examples found in repository?
src/layout/grid_solver.rs (line 160)
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
166
167
168
169
170
171
172
173
174
175
176
    fn for_child<CR: FnOnce(AxisInfo) -> SizeRules>(
        &mut self,
        storage: &mut Self::Storage,
        info: Self::ChildInfo,
        child_rules: CR,
    ) {
        if self.axis.has_fixed {
            if self.axis.is_horizontal() {
                self.axis.other_axis = ((info.row + 1)..info.row_end)
                    .fold(storage.heights()[usize::conv(info.row)], |h, i| {
                        h + storage.heights()[usize::conv(i)]
                    });
            } else {
                self.axis.other_axis = ((info.col + 1)..info.col_end)
                    .fold(storage.widths()[usize::conv(info.col)], |w, i| {
                        w + storage.widths()[usize::conv(i)]
                    });
            }
        }
        let child_rules = child_rules(self.axis);
        if self.axis.is_horizontal() {
            if info.col_end > info.col + 1 {
                let span = &mut self.col_spans.as_mut()[self.next_col_span];
                span.0.max_with(child_rules);
                span.1 = info.col;
                span.2 = info.col_end;
                self.next_col_span += 1;
            } else {
                storage.width_rules()[usize::conv(info.col)].max_with(child_rules);
            }
        } else if info.row_end > info.row + 1 {
            let span = &mut self.row_spans.as_mut()[self.next_row_span];
            span.0.max_with(child_rules);
            span.1 = info.row;
            span.2 = info.row_end;
            self.next_row_span += 1;
        } else {
            storage.height_rules()[usize::conv(info.row)].max_with(child_rules);
        };
    }

Multiply the (min, ideal) size, including internal margins

E.g. given margin = margins.0 + margins.1 and factors (2, 5), the minimum size is set to min * 2 + margin and the ideal to ideal * 5 + 4 * margin.

Panics if either factor is 0.

Append the rules for rhs to self

This implies that rhs rules concern an element to the right of or below self. Note that order matters since margins may be combined.

Note also that appending SizeRules::EMPTY does include interior margins (those between EMPTY and the other rules) within the result.

Return the rules for self appended by rhs

This implies that rhs rules concern an element to the right of or below self. Note that order matters since margins may be combined.

Note also that appending SizeRules::EMPTY does include interior margins (those between EMPTY and the other rules) within the result.

Examples found in repository?
src/layout/size_rules.rs (line 673)
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
    fn sum<I: Iterator<Item = Self>>(mut iter: I) -> Self {
        if let Some(first) = iter.next() {
            iter.fold(first, |x, y| x.appended(y))
        } else {
            SizeRules::EMPTY
        }
    }
}

/// Return the sum over a sequence of rules, assuming these are ordered
///
/// Uses [`SizeRules::appended`] on all rules in sequence.
impl<'a> Sum<&'a Self> for SizeRules {
    fn sum<I: Iterator<Item = &'a Self>>(mut iter: I) -> Self {
        if let Some(first) = iter.next() {
            iter.fold(*first, |x, y| x.appended(*y))
        } else {
            SizeRules::EMPTY
        }
    }
More examples
Hide additional examples
src/layout/row_solver.rs (line 78)
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
    fn for_child<CR: FnOnce(AxisInfo) -> SizeRules>(
        &mut self,
        storage: &mut Self::Storage,
        index: Self::ChildInfo,
        child_rules: CR,
    ) {
        if self.axis.has_fixed && self.axis_is_vertical {
            self.axis.other_axis = storage.widths()[index];
        }
        let child_rules = child_rules(self.axis);

        if !self.axis_is_vertical {
            storage.rules()[index] = child_rules;
            if let Some(rules) = self.rules {
                if self.axis_is_reversed {
                    self.rules = Some(child_rules.appended(rules));
                } else {
                    self.rules = Some(rules.appended(child_rules));
                }
            } else {
                self.rules = Some(child_rules);
            }
        } else {
            self.rules = Some(
                self.rules
                    .map(|rules| rules.max(child_rules))
                    .unwrap_or(child_rules),
            );
        }
    }

Return the result of appending all given ranges

Examples found in repository?
src/layout/size_rules.rs (line 359)
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
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
    pub fn solve_seq(out: &mut [i32], rules: &[Self], target: i32) {
        let total = SizeRules::sum(rules);
        Self::solve_seq_total(out, rules, total, target);
    }

    /// Solve a sequence of rules
    ///
    /// This is the same as [`SizeRules::solve_seq`] except that the rules' sum
    /// is passed explicitly.
    #[cfg_attr(not(feature = "internal_doc"), doc(hidden))]
    #[cfg_attr(doc_cfg, doc(cfg(internal_doc)))]
    #[allow(
        clippy::comparison_chain,
        clippy::needless_range_loop,
        clippy::needless_return
    )]
    #[inline]
    pub fn solve_seq_total(out: &mut [i32], rules: &[Self], total: Self, target: i32) {
        type Targets = SmallVec<[i32; 16]>;
        #[allow(non_snake_case)]
        let N = out.len();
        assert_eq!(rules.len(), N);
        if N == 0 {
            return;
        }
        #[cfg(debug_assertions)]
        {
            assert!(out.iter().all(|w| *w >= 0));
            let mut sum = SizeRules::sum(rules);
            sum.m = total.m; // external margins are unimportant here
            assert_eq!(sum, total);
        }

        if target > total.a {
            // All minimum sizes can be met.
            out[0] = out[0].max(rules[0].a);
            let mut margin_sum = 0;
            let mut sum = out[0];
            let mut dist_under_b = (rules[0].b - out[0]).max(0);
            let mut dist_over_b = (out[0] - rules[0].b).max(0);
            for i in 1..N {
                out[i] = out[i].max(rules[i].a);
                margin_sum += i32::from((rules[i - 1].m.1).max(rules[i].m.0));
                sum += out[i];
                dist_under_b += (rules[i].b - out[i]).max(0);
                dist_over_b += (out[i] - rules[i].b).max(0);
            }
            let target = target - margin_sum;

            if sum == target {
                return;
            } else if sum < target {
                fn increase_targets<F: Fn(usize) -> i32>(
                    out: &mut [i32],
                    targets: &mut Targets,
                    base: F,
                    mut avail: i32,
                ) {
                    if targets.is_empty() {
                        return;
                    }

                    // Calculate ceiling above which sizes will not be increased
                    let mut any_removed = true;
                    while any_removed {
                        any_removed = false;
                        let count = i32::conv(targets.len());
                        let ceil = (avail + count - 1) / count; // round up
                        let mut t = 0;
                        while t < targets.len() {
                            let i = usize::conv(targets[t]);
                            if out[i] >= base(i) + ceil {
                                avail -= out[i] - base(i);
                                targets.remove(t);
                                any_removed = true;
                                continue;
                            }
                            t += 1;
                        }
                        if targets.is_empty() {
                            return;
                        }
                    }

                    // Since no more are removed by a ceiling, all remaining
                    // targets will be (approx) equal. Arbitrarily distribute
                    // rounding errors to the first ones.
                    let count = i32::conv(targets.len());
                    let per_elt = avail / count;
                    let extra = usize::conv(avail - per_elt * count);
                    assert!(extra < targets.len());
                    for t in 0..extra {
                        let i = usize::conv(targets[t]);
                        out[i] = base(i) + per_elt + 1;
                    }
                    for t in extra..targets.len() {
                        let i = usize::conv(targets[t]);
                        out[i] = base(i) + per_elt;
                    }
                }

                if target - sum >= dist_under_b {
                    // We can increase all sizes to their ideal. Since this may
                    // not be enough, we also count the number with highest
                    // stretch factor and how far these are over their ideal.
                    // If highest stretch is None, do not expand beyond ideal.
                    sum = 0;
                    let highest_stretch = total.stretch;
                    let mut targets = Targets::new();
                    let mut over = 0;
                    for i in 0..N {
                        out[i] = out[i].max(rules[i].b);
                        sum += out[i];
                        if rules[i].stretch == highest_stretch {
                            over += out[i] - rules[i].b;
                            targets.push(i.cast());
                        }
                    }

                    let avail = target - sum + over;
                    increase_targets(out, &mut targets, |i| rules[i].b, avail);
                    debug_assert!(target >= (0..N).fold(0, |x, i| x + out[i]));
                } else {
                    // We cannot increase sizes as far as their ideal: instead
                    // increase over minimum size and under ideal
                    let mut targets = Targets::new();
                    let mut over = 0;
                    for i in 0..N {
                        if out[i] < rules[i].b {
                            over += out[i] - rules[i].a;
                            targets.push(i.cast());
                        }
                    }

                    let avail = target - sum + over;
                    increase_targets(out, &mut targets, |i| rules[i].a, avail);
                    debug_assert_eq!(target, (0..N).fold(0, |x, i| x + out[i]));
                }
            } else {
                // sum > target: we need to decrease some sizes
                fn reduce_targets<F: Fn(usize) -> i32>(
                    out: &mut [i32],
                    targets: &mut Targets,
                    base: F,
                    mut avail: i32,
                ) {
                    // We can ignore everything below the floor
                    let mut any_removed = true;
                    while any_removed {
                        any_removed = false;
                        let floor = avail / i32::conv(targets.len());
                        let mut t = 0;
                        while t < targets.len() {
                            let i = usize::conv(targets[t]);
                            if out[i] <= base(i) + floor {
                                avail -= out[i] - base(i);
                                targets.remove(t);
                                any_removed = true;
                                continue;
                            }
                            t += 1;
                        }
                    }

                    // All targets remaining must be reduced to floor, bar rounding errors
                    let floor = avail / i32::conv(targets.len());
                    let extra = usize::conv(avail) - usize::conv(floor) * targets.len();
                    assert!(extra < targets.len());
                    for t in 0..extra {
                        let i = usize::conv(targets[t]);
                        out[i] = base(i) + floor + 1;
                    }
                    for t in extra..targets.len() {
                        let i = usize::conv(targets[t]);
                        out[i] = base(i) + floor;
                    }
                }

                if dist_over_b > sum - target {
                    // we do not go below ideal, and will keep at least one above
                    // calculate distance over for each stretch priority
                    const MAX_STRETCH: usize = Stretch::Maximize as usize + 1;
                    let mut dists = [0; MAX_STRETCH];
                    for i in 0..N {
                        dists[rules[i].stretch as usize] += (out[i] - rules[i].b).max(0);
                    }
                    let mut accum = 0;
                    let mut highest_affected = 0;
                    for i in 0..MAX_STRETCH {
                        highest_affected = i;
                        dists[i] += accum;
                        accum = dists[i];
                        if accum >= sum - target {
                            break;
                        }
                    }

                    let mut avail = 0;
                    let mut targets = Targets::new();
                    for i in 0..N {
                        let stretch = rules[i].stretch as usize;
                        if out[i] > rules[i].b {
                            if stretch < highest_affected {
                                sum -= out[i] - rules[i].b;
                                out[i] = rules[i].b;
                            } else if stretch == highest_affected {
                                avail += out[i] - rules[i].b;
                                targets.push(i.cast());
                            }
                        }
                    }
                    if sum > target {
                        avail = avail + target - sum;
                        reduce_targets(out, &mut targets, |i| rules[i].b, avail);
                    }
                    debug_assert_eq!(target, (0..N).fold(0, |x, i| x + out[i]));
                } else {
                    // No size can exceed the ideal
                    // First, ensure nothing exceeds the ideal:
                    let mut targets = Targets::new();
                    sum = 0;
                    for i in 0..N {
                        out[i] = out[i].min(rules[i].b);
                        sum += out[i];
                        if out[i] > rules[i].a {
                            targets.push(i.cast());
                        }
                    }
                    if sum > target {
                        let avail = target + margin_sum - total.a;
                        reduce_targets(out, &mut targets, |i| rules[i].a, avail);
                    }
                    debug_assert_eq!(target, (0..N).fold(0, |x, i| x + out[i]));
                }
            }
        } else {
            // Below minimum size: ignore target and use minimum sizes.
            for n in 0..N {
                out[n] = rules[n].a;
            }
        }
    }
More examples
Hide additional examples
src/layout/grid_solver.rs (line 213)
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
        fn calculate(widths: &mut [SizeRules], spans: &mut [(SizeRules, u32, u32)]) -> SizeRules {
            // spans: &mut [(rules, begin, end)]

            // To avoid losing Stretch, we distribute this first
            const BASE_WEIGHT: u32 = 100;
            const SPAN_WEIGHT: u32 = 10;
            let mut scores: Vec<u32> = widths
                .iter()
                .map(|w| w.stretch() as u32 * BASE_WEIGHT)
                .collect();
            for span in spans.iter() {
                let w = span.0.stretch() as u32 * SPAN_WEIGHT;
                for score in &mut scores[(usize::conv(span.1))..(usize::conv(span.2))] {
                    *score += w;
                }
            }
            for span in spans.iter() {
                let range = (usize::conv(span.1))..(usize::conv(span.2));
                span.0
                    .distribute_stretch_over_by(&mut widths[range.clone()], &scores[range]);
            }

            // Sort spans to apply smallest first
            spans.sort_by_key(|span| span.2.saturating_sub(span.1));

            // We are left with non-overlapping spans.
            // For each span, we ensure cell widths are sufficiently large.
            for span in spans {
                let rules = span.0;
                let begin = usize::conv(span.1);
                let end = usize::conv(span.2);
                rules.distribute_span_over(&mut widths[begin..end]);
            }

            SizeRules::sum(widths)
        }

Return the result of appending all given ranges (min only)

This is a specialised version of sum: only the minimum is calculated

Examples found in repository?
src/layout/row_solver.rs (line 221)
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
    fn maximal_rect_of(&mut self, storage: &mut Self::Storage, index: Self::ChildInfo) -> Rect {
        let pre_rules = SizeRules::min_sum(&storage.rules()[0..index]);
        let m = storage.rules()[index].margins();
        let len = storage.widths().len();
        let post_rules = SizeRules::min_sum(&storage.rules()[(index + 1)..len]);

        let size1 = pre_rules.min_size() + i32::from(pre_rules.margins().1.max(m.0));
        let size2 = size1 + post_rules.min_size() + i32::from(post_rules.margins().0.max(m.1));

        let mut rect = self.rect;
        if self.direction.is_horizontal() {
            rect.pos.0 = self.rect.pos.0 + size1;
            rect.size.0 = (self.rect.size.0 - size2).max(0);
        } else {
            rect.pos.1 = self.rect.pos.1 + size1;
            rect.size.1 = (self.rect.size.1 - size2).max(0);
        }
        rect
    }

Set self to self - x + y, clamped to 0 or greater

This is a specialised operation to join two spans, subtracing the common overlap (x), thus margins are self.m.0 and y.m.1.

Reduce the minimum size

If min is greater than the current minimum size, this has no effect.

Adjust a sequence of rules to ensure that the total is at least self

This is used by grids to ensure that cell spans are sufficiently large.

Examples found in repository?
src/layout/grid_solver.rs (line 210)
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
        fn calculate(widths: &mut [SizeRules], spans: &mut [(SizeRules, u32, u32)]) -> SizeRules {
            // spans: &mut [(rules, begin, end)]

            // To avoid losing Stretch, we distribute this first
            const BASE_WEIGHT: u32 = 100;
            const SPAN_WEIGHT: u32 = 10;
            let mut scores: Vec<u32> = widths
                .iter()
                .map(|w| w.stretch() as u32 * BASE_WEIGHT)
                .collect();
            for span in spans.iter() {
                let w = span.0.stretch() as u32 * SPAN_WEIGHT;
                for score in &mut scores[(usize::conv(span.1))..(usize::conv(span.2))] {
                    *score += w;
                }
            }
            for span in spans.iter() {
                let range = (usize::conv(span.1))..(usize::conv(span.2));
                span.0
                    .distribute_stretch_over_by(&mut widths[range.clone()], &scores[range]);
            }

            // Sort spans to apply smallest first
            spans.sort_by_key(|span| span.2.saturating_sub(span.1));

            // We are left with non-overlapping spans.
            // For each span, we ensure cell widths are sufficiently large.
            for span in spans {
                let rules = span.0;
                let begin = usize::conv(span.1);
                let end = usize::conv(span.2);
                rules.distribute_span_over(&mut widths[begin..end]);
            }

            SizeRules::sum(widths)
        }

Trait Implementations§

Returns a copy of the value. Read more
Performs copy-assignment from source. Read more
Formats the value using the given formatter. Read more
Returns the “default value” for a type. Read more
This method tests for self and other values to be equal, and is used by ==.
This method tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.

Return the sum over a sequence of rules, assuming these are ordered

Uses SizeRules::appended on all rules in sequence.

Method which takes an iterator and generates Self from the elements by “summing up” the items.

Return the sum over a sequence of rules, assuming these are ordered

Uses SizeRules::appended on all rules in sequence.

Method which takes an iterator and generates Self from the elements by “summing up” the items.

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
Compare self to key and return true if they are equal.

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 resulting type after obtaining ownership.
Creates owned data from borrowed data, usually by cloning. Read more
Uses borrowed data to replace owned data, usually by cloning. Read more
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.