1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5 //! Borders, padding, and margins.
6
7 #![deny(unsafe_code)]
8
9 use app_units::Au;
10 use euclid::{SideOffsets2D, Size2D};
11 use fragment::Fragment;
12 use std::cmp::{max, min};
13 use std::fmt;
14 use style::logical_geometry::{LogicalMargin, WritingMode};
15 use style::properties::ComputedValues;
16 use style::values::computed::{BorderCornerRadius, LengthOrPercentageOrAuto};
17 use style::values::computed::{LengthOrPercentage, LengthOrPercentageOrNone};
18
19 /// A collapsible margin. See CSS 2.1 § 8.3.1.
20 #[derive(Clone, Copy, Debug)]
21 pub struct AdjoiningMargins {
22 /// The value of the greatest positive margin.
23 pub most_positive: Au,
24
25 /// The actual value (not the absolute value) of the negative margin with the largest absolute
26 /// value. Since this is not the absolute value, this is always zero or negative.
27 pub most_negative: Au,
28 }
29
30 impl AdjoiningMargins {
new() -> AdjoiningMargins31 pub fn new() -> AdjoiningMargins {
32 AdjoiningMargins {
33 most_positive: Au(0),
34 most_negative: Au(0),
35 }
36 }
37
from_margin(margin_value: Au) -> AdjoiningMargins38 pub fn from_margin(margin_value: Au) -> AdjoiningMargins {
39 if margin_value >= Au(0) {
40 AdjoiningMargins {
41 most_positive: margin_value,
42 most_negative: Au(0),
43 }
44 } else {
45 AdjoiningMargins {
46 most_positive: Au(0),
47 most_negative: margin_value,
48 }
49 }
50 }
51
union(&mut self, other: AdjoiningMargins)52 pub fn union(&mut self, other: AdjoiningMargins) {
53 self.most_positive = max(self.most_positive, other.most_positive);
54 self.most_negative = min(self.most_negative, other.most_negative)
55 }
56
collapse(&self) -> Au57 pub fn collapse(&self) -> Au {
58 self.most_positive + self.most_negative
59 }
60 }
61
62 /// Represents the block-start and block-end margins of a flow with collapsible margins. See CSS 2.1 § 8.3.1.
63 #[derive(Clone, Copy, Debug)]
64 pub enum CollapsibleMargins {
65 /// Margins may not collapse with this flow.
66 None(Au, Au),
67
68 /// Both the block-start and block-end margins (specified here in that order) may collapse, but the
69 /// margins do not collapse through this flow.
70 Collapse(AdjoiningMargins, AdjoiningMargins),
71
72 /// Margins collapse *through* this flow. This means, essentially, that the flow doesn’t
73 /// have any border, padding, or out-of-flow (floating or positioned) content
74 CollapseThrough(AdjoiningMargins),
75 }
76
77 impl CollapsibleMargins {
new() -> CollapsibleMargins78 pub fn new() -> CollapsibleMargins {
79 CollapsibleMargins::None(Au(0), Au(0))
80 }
81
82 /// Returns the amount of margin that should be applied in a noncollapsible context. This is
83 /// currently used to apply block-start margin for hypothetical boxes, since we do not collapse
84 /// margins of hypothetical boxes.
block_start_margin_for_noncollapsible_context(&self) -> Au85 pub fn block_start_margin_for_noncollapsible_context(&self) -> Au {
86 match *self {
87 CollapsibleMargins::None(block_start, _) => block_start,
88 CollapsibleMargins::Collapse(ref block_start, _) |
89 CollapsibleMargins::CollapseThrough(ref block_start) => block_start.collapse(),
90 }
91 }
92
block_end_margin_for_noncollapsible_context(&self) -> Au93 pub fn block_end_margin_for_noncollapsible_context(&self) -> Au {
94 match *self {
95 CollapsibleMargins::None(_, block_end) => block_end,
96 CollapsibleMargins::Collapse(_, ref block_end) |
97 CollapsibleMargins::CollapseThrough(ref block_end) => block_end.collapse(),
98 }
99 }
100 }
101
102 enum FinalMarginState {
103 MarginsCollapseThrough,
104 BottomMarginCollapses,
105 }
106
107 pub struct MarginCollapseInfo {
108 pub state: MarginCollapseState,
109 pub block_start_margin: AdjoiningMargins,
110 pub margin_in: AdjoiningMargins,
111 }
112
113 impl MarginCollapseInfo {
initialize_block_start_margin( fragment: &Fragment, can_collapse_block_start_margin_with_kids: bool, ) -> MarginCollapseInfo114 pub fn initialize_block_start_margin(
115 fragment: &Fragment,
116 can_collapse_block_start_margin_with_kids: bool,
117 ) -> MarginCollapseInfo {
118 MarginCollapseInfo {
119 state: if can_collapse_block_start_margin_with_kids {
120 MarginCollapseState::AccumulatingCollapsibleTopMargin
121 } else {
122 MarginCollapseState::AccumulatingMarginIn
123 },
124 block_start_margin: AdjoiningMargins::from_margin(fragment.margin.block_start),
125 margin_in: AdjoiningMargins::new(),
126 }
127 }
128
finish_and_compute_collapsible_margins(mut self, fragment: &Fragment, containing_block_size: Option<Au>, can_collapse_block_end_margin_with_kids: bool, mut may_collapse_through: bool) -> (CollapsibleMargins, Au)129 pub fn finish_and_compute_collapsible_margins(mut self,
130 fragment: &Fragment,
131 containing_block_size: Option<Au>,
132 can_collapse_block_end_margin_with_kids: bool,
133 mut may_collapse_through: bool)
134 -> (CollapsibleMargins, Au) {
135 let state = match self.state {
136 MarginCollapseState::AccumulatingCollapsibleTopMargin => {
137 may_collapse_through = may_collapse_through &&
138 match fragment.style().content_block_size() {
139 LengthOrPercentageOrAuto::Auto => true,
140 LengthOrPercentageOrAuto::Length(l) => l.px() == 0.,
141 LengthOrPercentageOrAuto::Percentage(v) => {
142 v.0 == 0. || containing_block_size.is_none()
143 }
144 LengthOrPercentageOrAuto::Calc(_) => false,
145 };
146
147 if may_collapse_through {
148 match fragment.style().min_block_size() {
149 LengthOrPercentage::Length(l) if l.px() == 0. => {
150 FinalMarginState::MarginsCollapseThrough
151 },
152 LengthOrPercentage::Percentage(v) if v.0 == 0. => {
153 FinalMarginState::MarginsCollapseThrough
154 },
155 _ => {
156 // If the fragment has non-zero min-block-size, margins may not
157 // collapse through it.
158 FinalMarginState::BottomMarginCollapses
159 }
160 }
161 } else {
162 // If the fragment has an explicitly specified block-size, margins may not
163 // collapse through it.
164 FinalMarginState::BottomMarginCollapses
165 }
166 }
167 MarginCollapseState::AccumulatingMarginIn => FinalMarginState::BottomMarginCollapses,
168 };
169
170 // Different logic is needed here depending on whether this flow can collapse its block-end
171 // margin with its children.
172 let block_end_margin = fragment.margin.block_end;
173 if !can_collapse_block_end_margin_with_kids {
174 match state {
175 FinalMarginState::MarginsCollapseThrough => {
176 let advance = self.block_start_margin.collapse();
177 self.margin_in.union(AdjoiningMargins::from_margin(block_end_margin));
178 (CollapsibleMargins::Collapse(self.block_start_margin, self.margin_in),
179 advance)
180 }
181 FinalMarginState::BottomMarginCollapses => {
182 let advance = self.margin_in.collapse();
183 self.margin_in.union(AdjoiningMargins::from_margin(block_end_margin));
184 (CollapsibleMargins::Collapse(self.block_start_margin, self.margin_in),
185 advance)
186 }
187 }
188 } else {
189 match state {
190 FinalMarginState::MarginsCollapseThrough => {
191 self.block_start_margin.union(AdjoiningMargins::from_margin(block_end_margin));
192 (CollapsibleMargins::CollapseThrough(self.block_start_margin), Au(0))
193 }
194 FinalMarginState::BottomMarginCollapses => {
195 self.margin_in.union(AdjoiningMargins::from_margin(block_end_margin));
196 (CollapsibleMargins::Collapse(self.block_start_margin, self.margin_in), Au(0))
197 }
198 }
199 }
200 }
201
current_float_ceiling(&mut self) -> Au202 pub fn current_float_ceiling(&mut self) -> Au {
203 match self.state {
204 MarginCollapseState::AccumulatingCollapsibleTopMargin => {
205 // We do not include the top margin in the float ceiling, because the float flow
206 // needs to be positioned relative to our *border box*, not our margin box. See
207 // `tests/ref/float_under_top_margin_a.html`.
208 Au(0)
209 }
210 MarginCollapseState::AccumulatingMarginIn => self.margin_in.collapse(),
211 }
212 }
213
214 /// Adds the child's potentially collapsible block-start margin to the current margin state and
215 /// advances the Y offset by the appropriate amount to handle that margin. Returns the amount
216 /// that should be added to the Y offset during block layout.
advance_block_start_margin(&mut self, child_collapsible_margins: &CollapsibleMargins, can_collapse_block_start_margin: bool) -> Au217 pub fn advance_block_start_margin(&mut self,
218 child_collapsible_margins: &CollapsibleMargins,
219 can_collapse_block_start_margin: bool)
220 -> Au {
221 if !can_collapse_block_start_margin {
222 self.state = MarginCollapseState::AccumulatingMarginIn
223 }
224
225 match (self.state, *child_collapsible_margins) {
226 (MarginCollapseState::AccumulatingCollapsibleTopMargin,
227 CollapsibleMargins::None(block_start, _)) => {
228 self.state = MarginCollapseState::AccumulatingMarginIn;
229 block_start
230 }
231 (MarginCollapseState::AccumulatingCollapsibleTopMargin,
232 CollapsibleMargins::Collapse(block_start, _)) => {
233 self.block_start_margin.union(block_start);
234 self.state = MarginCollapseState::AccumulatingMarginIn;
235 Au(0)
236 }
237 (MarginCollapseState::AccumulatingMarginIn,
238 CollapsibleMargins::None(block_start, _)) => {
239 let previous_margin_value = self.margin_in.collapse();
240 self.margin_in = AdjoiningMargins::new();
241 previous_margin_value + block_start
242 }
243 (MarginCollapseState::AccumulatingMarginIn,
244 CollapsibleMargins::Collapse(block_start, _)) => {
245 self.margin_in.union(block_start);
246 let margin_value = self.margin_in.collapse();
247 self.margin_in = AdjoiningMargins::new();
248 margin_value
249 }
250 (_, CollapsibleMargins::CollapseThrough(_)) => {
251 // For now, we ignore this; this will be handled by `advance_block_end_margin`
252 // below.
253 Au(0)
254 }
255 }
256 }
257
258 /// Adds the child's potentially collapsible block-end margin to the current margin state and
259 /// advances the Y offset by the appropriate amount to handle that margin. Returns the amount
260 /// that should be added to the Y offset during block layout.
advance_block_end_margin(&mut self, child_collapsible_margins: &CollapsibleMargins) -> Au261 pub fn advance_block_end_margin(&mut self, child_collapsible_margins: &CollapsibleMargins)
262 -> Au {
263 match (self.state, *child_collapsible_margins) {
264 (MarginCollapseState::AccumulatingCollapsibleTopMargin, CollapsibleMargins::None(..)) |
265 (MarginCollapseState::AccumulatingCollapsibleTopMargin,
266 CollapsibleMargins::Collapse(..)) => {
267 // Can't happen because the state will have been replaced with
268 // `MarginCollapseState::AccumulatingMarginIn` above.
269 panic!("should not be accumulating collapsible block_start margins anymore!")
270 }
271 (MarginCollapseState::AccumulatingCollapsibleTopMargin,
272 CollapsibleMargins::CollapseThrough(margin)) => {
273 self.block_start_margin.union(margin);
274 Au(0)
275 }
276 (MarginCollapseState::AccumulatingMarginIn,
277 CollapsibleMargins::None(_, block_end)) => {
278 assert_eq!(self.margin_in.most_positive, Au(0));
279 assert_eq!(self.margin_in.most_negative, Au(0));
280 block_end
281 }
282 (MarginCollapseState::AccumulatingMarginIn,
283 CollapsibleMargins::Collapse(_, block_end)) |
284 (MarginCollapseState::AccumulatingMarginIn,
285 CollapsibleMargins::CollapseThrough(block_end)) => {
286 self.margin_in.union(block_end);
287 Au(0)
288 }
289 }
290 }
291 }
292
293 #[derive(Clone, Copy, Debug)]
294 pub enum MarginCollapseState {
295 /// We are accumulating margin on the logical top of this flow.
296 AccumulatingCollapsibleTopMargin,
297 /// We are accumulating margin between two blocks.
298 AccumulatingMarginIn,
299 }
300
301 /// Intrinsic inline-sizes, which consist of minimum and preferred.
302 #[derive(Clone, Copy, Serialize)]
303 pub struct IntrinsicISizes {
304 /// The *minimum inline-size* of the content.
305 pub minimum_inline_size: Au,
306 /// The *preferred inline-size* of the content.
307 pub preferred_inline_size: Au,
308 }
309
310 impl fmt::Debug for IntrinsicISizes {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result311 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
312 write!(f, "min={:?}, pref={:?}", self.minimum_inline_size, self.preferred_inline_size)
313 }
314 }
315
316 impl IntrinsicISizes {
new() -> IntrinsicISizes317 pub fn new() -> IntrinsicISizes {
318 IntrinsicISizes {
319 minimum_inline_size: Au(0),
320 preferred_inline_size: Au(0),
321 }
322 }
323 }
324
325 /// The temporary result of the computation of intrinsic inline-sizes.
326 #[derive(Debug)]
327 pub struct IntrinsicISizesContribution {
328 /// Intrinsic sizes for the content only (not counting borders, padding, or margins).
329 pub content_intrinsic_sizes: IntrinsicISizes,
330 /// The inline size of borders and padding, as well as margins if appropriate.
331 pub surrounding_size: Au,
332 }
333
334 impl IntrinsicISizesContribution {
335 /// Creates and initializes an inline size computation with all sizes set to zero.
new() -> IntrinsicISizesContribution336 pub fn new() -> IntrinsicISizesContribution {
337 IntrinsicISizesContribution {
338 content_intrinsic_sizes: IntrinsicISizes::new(),
339 surrounding_size: Au(0),
340 }
341 }
342
343 /// Adds the content intrinsic sizes and the surrounding size together to yield the final
344 /// intrinsic size computation.
finish(self) -> IntrinsicISizes345 pub fn finish(self) -> IntrinsicISizes {
346 IntrinsicISizes {
347 minimum_inline_size: self.content_intrinsic_sizes.minimum_inline_size +
348 self.surrounding_size,
349 preferred_inline_size: self.content_intrinsic_sizes.preferred_inline_size +
350 self.surrounding_size,
351 }
352 }
353
354 /// Updates the computation so that the minimum is the maximum of the current minimum and the
355 /// given minimum and the preferred is the sum of the current preferred and the given
356 /// preferred. This is used when laying out fragments in the inline direction.
357 ///
358 /// FIXME(pcwalton): This is incorrect when the inline fragment contains forced line breaks
359 /// (e.g. `<br>` or `white-space: pre`).
union_inline(&mut self, sizes: &IntrinsicISizes)360 pub fn union_inline(&mut self, sizes: &IntrinsicISizes) {
361 self.content_intrinsic_sizes.minimum_inline_size =
362 max(self.content_intrinsic_sizes.minimum_inline_size, sizes.minimum_inline_size);
363 self.content_intrinsic_sizes.preferred_inline_size =
364 self.content_intrinsic_sizes.preferred_inline_size + sizes.preferred_inline_size
365 }
366
367 /// Updates the computation so that the minimum is the sum of the current minimum and the
368 /// given minimum and the preferred is the sum of the current preferred and the given
369 /// preferred. This is used when laying out fragments in the inline direction when
370 /// `white-space` is `pre` or `nowrap`.
union_nonbreaking_inline(&mut self, sizes: &IntrinsicISizes)371 pub fn union_nonbreaking_inline(&mut self, sizes: &IntrinsicISizes) {
372 self.content_intrinsic_sizes.minimum_inline_size =
373 self.content_intrinsic_sizes.minimum_inline_size + sizes.minimum_inline_size;
374 self.content_intrinsic_sizes.preferred_inline_size =
375 self.content_intrinsic_sizes.preferred_inline_size + sizes.preferred_inline_size
376 }
377
378 /// Updates the computation so that the minimum is the maximum of the current minimum and the
379 /// given minimum and the preferred is the maximum of the current preferred and the given
380 /// preferred. This can be useful when laying out fragments in the block direction (but note
381 /// that it does not take floats into account, so `BlockFlow` does not use it).
382 ///
383 /// This is used when contributing the intrinsic sizes for individual fragments.
union_block(&mut self, sizes: &IntrinsicISizes)384 pub fn union_block(&mut self, sizes: &IntrinsicISizes) {
385 self.content_intrinsic_sizes.minimum_inline_size =
386 max(self.content_intrinsic_sizes.minimum_inline_size, sizes.minimum_inline_size);
387 self.content_intrinsic_sizes.preferred_inline_size =
388 max(self.content_intrinsic_sizes.preferred_inline_size, sizes.preferred_inline_size)
389 }
390 }
391
392 /// Useful helper data type when computing values for blocks and positioned elements.
393 #[derive(Clone, Copy, Debug, PartialEq)]
394 pub enum MaybeAuto {
395 Auto,
396 Specified(Au),
397 }
398
399 impl MaybeAuto {
400 #[inline]
from_style(length: LengthOrPercentageOrAuto, containing_length: Au) -> MaybeAuto401 pub fn from_style(length: LengthOrPercentageOrAuto, containing_length: Au)
402 -> MaybeAuto {
403 match length {
404 LengthOrPercentageOrAuto::Auto => MaybeAuto::Auto,
405 LengthOrPercentageOrAuto::Percentage(percent) => {
406 MaybeAuto::Specified(containing_length.scale_by(percent.0))
407 }
408 LengthOrPercentageOrAuto::Calc(calc) => {
409 MaybeAuto::from_option(calc.to_used_value(Some(containing_length)))
410 }
411 LengthOrPercentageOrAuto::Length(length) => MaybeAuto::Specified(Au::from(length))
412 }
413 }
414
415 #[inline]
from_option(au: Option<Au>) -> MaybeAuto416 pub fn from_option(au: Option<Au>) -> MaybeAuto {
417 match au {
418 Some(l) => MaybeAuto::Specified(l),
419 _ => MaybeAuto::Auto,
420 }
421 }
422
423 #[inline]
to_option(&self) -> Option<Au>424 pub fn to_option(&self) -> Option<Au> {
425 match *self {
426 MaybeAuto::Specified(value) => Some(value),
427 MaybeAuto::Auto => None,
428 }
429 }
430
431 #[inline]
specified_or_default(&self, default: Au) -> Au432 pub fn specified_or_default(&self, default: Au) -> Au {
433 match *self {
434 MaybeAuto::Auto => default,
435 MaybeAuto::Specified(value) => value,
436 }
437 }
438
439 #[inline]
specified_or_zero(&self) -> Au440 pub fn specified_or_zero(&self) -> Au {
441 self.specified_or_default(Au::new(0))
442 }
443
444 #[inline]
map<F>(&self, mapper: F) -> MaybeAuto where F: FnOnce(Au) -> Au445 pub fn map<F>(&self, mapper: F) -> MaybeAuto where F: FnOnce(Au) -> Au {
446 match *self {
447 MaybeAuto::Auto => MaybeAuto::Auto,
448 MaybeAuto::Specified(value) => MaybeAuto::Specified(mapper(value)),
449 }
450 }
451 }
452
453 /// Receive an optional container size and return used value for width or height.
454 ///
455 /// `style_length`: content size as given in the CSS.
style_length(style_length: LengthOrPercentageOrAuto, container_size: Option<Au>) -> MaybeAuto456 pub fn style_length(style_length: LengthOrPercentageOrAuto,
457 container_size: Option<Au>) -> MaybeAuto {
458 match container_size {
459 Some(length) => MaybeAuto::from_style(style_length, length),
460 None => if let LengthOrPercentageOrAuto::Length(length) = style_length {
461 MaybeAuto::Specified(Au::from(length))
462 } else {
463 MaybeAuto::Auto
464 }
465 }
466 }
467
468 /// Computes a border radius size against the containing size.
469 ///
470 /// Note that percentages in `border-radius` are resolved against the relevant
471 /// box dimension instead of only against the width per [1]:
472 ///
473 /// > Percentages: Refer to corresponding dimension of the border box.
474 ///
475 /// [1]: https://drafts.csswg.org/css-backgrounds-3/#border-radius
specified_border_radius( radius: BorderCornerRadius, containing_size: Size2D<Au>) -> Size2D<Au>476 pub fn specified_border_radius(
477 radius: BorderCornerRadius,
478 containing_size: Size2D<Au>)
479 -> Size2D<Au>
480 {
481 let w = radius.0.width().to_used_value(containing_size.width);
482 let h = radius.0.height().to_used_value(containing_size.height);
483 Size2D::new(w, h)
484 }
485
486 #[inline]
padding_from_style(style: &ComputedValues, containing_block_inline_size: Au, writing_mode: WritingMode) -> LogicalMargin<Au>487 pub fn padding_from_style(style: &ComputedValues,
488 containing_block_inline_size: Au,
489 writing_mode: WritingMode)
490 -> LogicalMargin<Au> {
491 let padding_style = style.get_padding();
492 LogicalMargin::from_physical(writing_mode, SideOffsets2D::new(
493 padding_style.padding_top.to_used_value(containing_block_inline_size),
494 padding_style.padding_right.to_used_value(containing_block_inline_size),
495 padding_style.padding_bottom.to_used_value(containing_block_inline_size),
496 padding_style.padding_left.to_used_value(containing_block_inline_size)))
497 }
498
499 /// Returns the explicitly-specified margin lengths from the given style. Percentage and auto
500 /// margins are returned as zero.
501 ///
502 /// This is used when calculating intrinsic inline sizes.
503 #[inline]
specified_margin_from_style(style: &ComputedValues, writing_mode: WritingMode) -> LogicalMargin<Au>504 pub fn specified_margin_from_style(style: &ComputedValues,
505 writing_mode: WritingMode) -> LogicalMargin<Au> {
506 let margin_style = style.get_margin();
507 LogicalMargin::from_physical(writing_mode, SideOffsets2D::new(
508 MaybeAuto::from_style(margin_style.margin_top, Au(0)).specified_or_zero(),
509 MaybeAuto::from_style(margin_style.margin_right, Au(0)).specified_or_zero(),
510 MaybeAuto::from_style(margin_style.margin_bottom, Au(0)).specified_or_zero(),
511 MaybeAuto::from_style(margin_style.margin_left, Au(0)).specified_or_zero()))
512 }
513
514 /// A min-size and max-size constraint. The constructor has a optional `border`
515 /// parameter, and when it is present the constraint will be subtracted. This is
516 /// used to adjust the constraint for `box-sizing: border-box`, and when you do so
517 /// make sure the size you want to clamp is intended to be used for content box.
518 #[derive(Clone, Copy, Debug, Serialize)]
519 pub struct SizeConstraint {
520 min_size: Au,
521 max_size: Option<Au>,
522 }
523
524 impl SizeConstraint {
525 /// Create a `SizeConstraint` for an axis.
new(container_size: Option<Au>, min_size: LengthOrPercentage, max_size: LengthOrPercentageOrNone, border: Option<Au>) -> SizeConstraint526 pub fn new(container_size: Option<Au>,
527 min_size: LengthOrPercentage,
528 max_size: LengthOrPercentageOrNone,
529 border: Option<Au>) -> SizeConstraint {
530 let mut min_size = match container_size {
531 Some(container_size) => min_size.to_used_value(container_size),
532 None => if let LengthOrPercentage::Length(length) = min_size {
533 Au::from(length)
534 } else {
535 Au(0)
536 }
537 };
538
539 let mut max_size = match container_size {
540 Some(container_size) => max_size.to_used_value(container_size),
541 None => if let LengthOrPercentageOrNone::Length(length) = max_size {
542 Some(Au::from(length))
543 } else {
544 None
545 }
546 };
547 // Make sure max size is not smaller than min size.
548 max_size = max_size.map(|x| max(x, min_size));
549
550 if let Some(border) = border {
551 min_size = max(min_size - border, Au(0));
552 max_size = max_size.map(|x| max(x - border, Au(0)));
553 }
554
555 SizeConstraint {
556 min_size: min_size,
557 max_size: max_size
558 }
559 }
560
561 /// Clamp the given size by the given min size and max size constraint.
clamp(&self, other: Au) -> Au562 pub fn clamp(&self, other: Au) -> Au {
563 if other < self.min_size {
564 self.min_size
565 } else {
566 match self.max_size {
567 Some(max_size) if max_size < other => max_size,
568 _ => other
569 }
570 }
571 }
572 }
573