1 | // Copyright 2014 The Flutter Authors. All rights reserved. |
2 | // Use of this source code is governed by a BSD-style license that can be |
3 | // found in the LICENSE file. |
4 | |
5 | import 'dart:ui' as ui show ViewPadding, lerpDouble; |
6 | |
7 | import 'package:flutter/foundation.dart'; |
8 | |
9 | import 'basic_types.dart'; |
10 | |
11 | /// Base class for [EdgeInsets] that allows for text-direction aware |
12 | /// resolution. |
13 | /// |
14 | /// A property or argument of this type accepts classes created either with [ |
15 | /// EdgeInsets.fromLTRB] and its variants, or [ |
16 | /// EdgeInsetsDirectional.fromSTEB] and its variants. |
17 | /// |
18 | /// To convert an [EdgeInsetsGeometry] object of indeterminate type into a |
19 | /// [EdgeInsets] object, call the [resolve] method. |
20 | /// |
21 | /// See also: |
22 | /// |
23 | /// * [Padding], a widget that describes margins using [EdgeInsetsGeometry]. |
24 | @immutable |
25 | abstract class EdgeInsetsGeometry { |
26 | /// Abstract const constructor. This constructor enables subclasses to provide |
27 | /// const constructors so that they can be used in const expressions. |
28 | const EdgeInsetsGeometry(); |
29 | |
30 | double get _bottom; |
31 | double get _end; |
32 | double get _left; |
33 | double get _right; |
34 | double get _start; |
35 | double get _top; |
36 | |
37 | /// An [EdgeInsetsGeometry] with infinite offsets in each direction. |
38 | /// |
39 | /// Can be used as an infinite upper bound for [clamp]. |
40 | static const EdgeInsetsGeometry infinity = _MixedEdgeInsets.fromLRSETB( |
41 | double.infinity, |
42 | double.infinity, |
43 | double.infinity, |
44 | double.infinity, |
45 | double.infinity, |
46 | double.infinity, |
47 | ); |
48 | |
49 | /// Whether every dimension is non-negative. |
50 | bool get isNonNegative { |
51 | return _left >= 0.0 |
52 | && _right >= 0.0 |
53 | && _start >= 0.0 |
54 | && _end >= 0.0 |
55 | && _top >= 0.0 |
56 | && _bottom >= 0.0; |
57 | } |
58 | |
59 | /// The total offset in the horizontal direction. |
60 | double get horizontal => _left + _right + _start + _end; |
61 | |
62 | /// The total offset in the vertical direction. |
63 | double get vertical => _top + _bottom; |
64 | |
65 | /// The total offset in the given direction. |
66 | double along(Axis axis) { |
67 | switch (axis) { |
68 | case Axis.horizontal: |
69 | return horizontal; |
70 | case Axis.vertical: |
71 | return vertical; |
72 | } |
73 | } |
74 | |
75 | /// The size that this [EdgeInsets] would occupy with an empty interior. |
76 | Size get collapsedSize => Size(horizontal, vertical); |
77 | |
78 | /// An [EdgeInsetsGeometry] with top and bottom, left and right, and start and end flipped. |
79 | EdgeInsetsGeometry get flipped => _MixedEdgeInsets.fromLRSETB(_right, _left, _end, _start, _bottom, _top); |
80 | |
81 | /// Returns a new size that is bigger than the given size by the amount of |
82 | /// inset in the horizontal and vertical directions. |
83 | /// |
84 | /// See also: |
85 | /// |
86 | /// * [EdgeInsets.inflateRect], to inflate a [Rect] rather than a [Size] (for |
87 | /// [EdgeInsetsDirectional], requires first calling [resolve] to establish |
88 | /// how the start and end map to the left or right). |
89 | /// * [deflateSize], to deflate a [Size] rather than inflating it. |
90 | Size inflateSize(Size size) { |
91 | return Size(size.width + horizontal, size.height + vertical); |
92 | } |
93 | |
94 | /// Returns a new size that is smaller than the given size by the amount of |
95 | /// inset in the horizontal and vertical directions. |
96 | /// |
97 | /// If the argument is smaller than [collapsedSize], then the resulting size |
98 | /// will have negative dimensions. |
99 | /// |
100 | /// See also: |
101 | /// |
102 | /// * [EdgeInsets.deflateRect], to deflate a [Rect] rather than a [Size]. (for |
103 | /// [EdgeInsetsDirectional], requires first calling [resolve] to establish |
104 | /// how the start and end map to the left or right). |
105 | /// * [inflateSize], to inflate a [Size] rather than deflating it. |
106 | Size deflateSize(Size size) { |
107 | return Size(size.width - horizontal, size.height - vertical); |
108 | } |
109 | |
110 | /// Returns the difference between two [EdgeInsetsGeometry] objects. |
111 | /// |
112 | /// If you know you are applying this to two [EdgeInsets] or two |
113 | /// [EdgeInsetsDirectional] objects, consider using the binary infix `-` |
114 | /// operator instead, which always returns an object of the same type as the |
115 | /// operands, and is typed accordingly. |
116 | /// |
117 | /// If [subtract] is applied to two objects of the same type ([EdgeInsets] or |
118 | /// [EdgeInsetsDirectional]), an object of that type will be returned (though |
119 | /// this is not reflected in the type system). Otherwise, an object |
120 | /// representing a combination of both is returned. That object can be turned |
121 | /// into a concrete [EdgeInsets] using [resolve]. |
122 | /// |
123 | /// This method returns the same result as [add] applied to the result of |
124 | /// negating the argument (using the prefix unary `-` operator or multiplying |
125 | /// the argument by -1.0 using the `*` operator). |
126 | EdgeInsetsGeometry subtract(EdgeInsetsGeometry other) { |
127 | return _MixedEdgeInsets.fromLRSETB( |
128 | _left - other._left, |
129 | _right - other._right, |
130 | _start - other._start, |
131 | _end - other._end, |
132 | _top - other._top, |
133 | _bottom - other._bottom, |
134 | ); |
135 | } |
136 | |
137 | /// Returns the sum of two [EdgeInsetsGeometry] objects. |
138 | /// |
139 | /// If you know you are adding two [EdgeInsets] or two [EdgeInsetsDirectional] |
140 | /// objects, consider using the `+` operator instead, which always returns an |
141 | /// object of the same type as the operands, and is typed accordingly. |
142 | /// |
143 | /// If [add] is applied to two objects of the same type ([EdgeInsets] or |
144 | /// [EdgeInsetsDirectional]), an object of that type will be returned (though |
145 | /// this is not reflected in the type system). Otherwise, an object |
146 | /// representing a combination of both is returned. That object can be turned |
147 | /// into a concrete [EdgeInsets] using [resolve]. |
148 | EdgeInsetsGeometry add(EdgeInsetsGeometry other) { |
149 | return _MixedEdgeInsets.fromLRSETB( |
150 | _left + other._left, |
151 | _right + other._right, |
152 | _start + other._start, |
153 | _end + other._end, |
154 | _top + other._top, |
155 | _bottom + other._bottom, |
156 | ); |
157 | } |
158 | |
159 | /// Returns a new [EdgeInsetsGeometry] object with all values greater than |
160 | /// or equal to `min`, and less than or equal to `max`. |
161 | EdgeInsetsGeometry clamp(EdgeInsetsGeometry min, EdgeInsetsGeometry max) { |
162 | return _MixedEdgeInsets.fromLRSETB( |
163 | clampDouble(_left, min._left, max._left), |
164 | clampDouble(_right, min._right, max._right), |
165 | clampDouble(_start, min._start, max._start), |
166 | clampDouble(_end, min._end, max._end), |
167 | clampDouble(_top, min._top, max._top), |
168 | clampDouble(_bottom, min._bottom, max._bottom), |
169 | ); |
170 | } |
171 | |
172 | /// Returns the [EdgeInsetsGeometry] object with each dimension negated. |
173 | /// |
174 | /// This is the same as multiplying the object by -1.0. |
175 | /// |
176 | /// This operator returns an object of the same type as the operand. |
177 | EdgeInsetsGeometry operator -(); |
178 | |
179 | /// Scales the [EdgeInsetsGeometry] object in each dimension by the given factor. |
180 | /// |
181 | /// This operator returns an object of the same type as the operand. |
182 | EdgeInsetsGeometry operator *(double other); |
183 | |
184 | /// Divides the [EdgeInsetsGeometry] object in each dimension by the given factor. |
185 | /// |
186 | /// This operator returns an object of the same type as the operand. |
187 | EdgeInsetsGeometry operator /(double other); |
188 | |
189 | /// Integer divides the [EdgeInsetsGeometry] object in each dimension by the given factor. |
190 | /// |
191 | /// This operator returns an object of the same type as the operand. |
192 | /// |
193 | /// This operator may have unexpected results when applied to a mixture of |
194 | /// [EdgeInsets] and [EdgeInsetsDirectional] objects. |
195 | EdgeInsetsGeometry operator ~/(double other); |
196 | |
197 | /// Computes the remainder in each dimension by the given factor. |
198 | /// |
199 | /// This operator returns an object of the same type as the operand. |
200 | /// |
201 | /// This operator may have unexpected results when applied to a mixture of |
202 | /// [EdgeInsets] and [EdgeInsetsDirectional] objects. |
203 | EdgeInsetsGeometry operator %(double other); |
204 | |
205 | /// Linearly interpolate between two [EdgeInsetsGeometry] objects. |
206 | /// |
207 | /// If either is null, this function interpolates from [EdgeInsets.zero], and |
208 | /// the result is an object of the same type as the non-null argument. |
209 | /// |
210 | /// If [lerp] is applied to two objects of the same type ([EdgeInsets] or |
211 | /// [EdgeInsetsDirectional]), an object of that type will be returned (though |
212 | /// this is not reflected in the type system). Otherwise, an object |
213 | /// representing a combination of both is returned. That object can be turned |
214 | /// into a concrete [EdgeInsets] using [resolve]. |
215 | /// |
216 | /// {@macro dart.ui.shadow.lerp} |
217 | static EdgeInsetsGeometry? lerp(EdgeInsetsGeometry? a, EdgeInsetsGeometry? b, double t) { |
218 | if (identical(a, b)) { |
219 | return a; |
220 | } |
221 | if (a == null) { |
222 | return b! * t; |
223 | } |
224 | if (b == null) { |
225 | return a * (1.0 - t); |
226 | } |
227 | if (a is EdgeInsets && b is EdgeInsets) { |
228 | return EdgeInsets.lerp(a, b, t); |
229 | } |
230 | if (a is EdgeInsetsDirectional && b is EdgeInsetsDirectional) { |
231 | return EdgeInsetsDirectional.lerp(a, b, t); |
232 | } |
233 | return _MixedEdgeInsets.fromLRSETB( |
234 | ui.lerpDouble(a._left, b._left, t)!, |
235 | ui.lerpDouble(a._right, b._right, t)!, |
236 | ui.lerpDouble(a._start, b._start, t)!, |
237 | ui.lerpDouble(a._end, b._end, t)!, |
238 | ui.lerpDouble(a._top, b._top, t)!, |
239 | ui.lerpDouble(a._bottom, b._bottom, t)!, |
240 | ); |
241 | } |
242 | |
243 | /// Convert this instance into an [EdgeInsets], which uses literal coordinates |
244 | /// (i.e. the `left` coordinate being explicitly a distance from the left, and |
245 | /// the `right` coordinate being explicitly a distance from the right). |
246 | /// |
247 | /// See also: |
248 | /// |
249 | /// * [EdgeInsets], for which this is a no-op (returns itself). |
250 | /// * [EdgeInsetsDirectional], which flips the horizontal direction |
251 | /// based on the `direction` argument. |
252 | EdgeInsets resolve(TextDirection? direction); |
253 | |
254 | @override |
255 | String toString() { |
256 | if (_start == 0.0 && _end == 0.0) { |
257 | if (_left == 0.0 && _right == 0.0 && _top == 0.0 && _bottom == 0.0) { |
258 | return 'EdgeInsets.zero' ; |
259 | } |
260 | if (_left == _right && _right == _top && _top == _bottom) { |
261 | return 'EdgeInsets.all( ${_left.toStringAsFixed(1)})' ; |
262 | } |
263 | return 'EdgeInsets( ${_left.toStringAsFixed(1)}, ' |
264 | ' ${_top.toStringAsFixed(1)}, ' |
265 | ' ${_right.toStringAsFixed(1)}, ' |
266 | ' ${_bottom.toStringAsFixed(1)})' ; |
267 | } |
268 | if (_left == 0.0 && _right == 0.0) { |
269 | return 'EdgeInsetsDirectional( ${_start.toStringAsFixed(1)}, ' |
270 | ' ${_top.toStringAsFixed(1)}, ' |
271 | ' ${_end.toStringAsFixed(1)}, ' |
272 | ' ${_bottom.toStringAsFixed(1)})' ; |
273 | } |
274 | return 'EdgeInsets( ${_left.toStringAsFixed(1)}, ' |
275 | ' ${_top.toStringAsFixed(1)}, ' |
276 | ' ${_right.toStringAsFixed(1)}, ' |
277 | ' ${_bottom.toStringAsFixed(1)})' |
278 | ' + ' |
279 | 'EdgeInsetsDirectional( ${_start.toStringAsFixed(1)}, ' |
280 | '0.0, ' |
281 | ' ${_end.toStringAsFixed(1)}, ' |
282 | '0.0)' ; |
283 | } |
284 | |
285 | @override |
286 | bool operator ==(Object other) { |
287 | return other is EdgeInsetsGeometry |
288 | && other._left == _left |
289 | && other._right == _right |
290 | && other._start == _start |
291 | && other._end == _end |
292 | && other._top == _top |
293 | && other._bottom == _bottom; |
294 | } |
295 | |
296 | @override |
297 | int get hashCode => Object.hash(_left, _right, _start, _end, _top, _bottom); |
298 | } |
299 | |
300 | /// An immutable set of offsets in each of the four cardinal directions. |
301 | /// |
302 | /// Typically used for an offset from each of the four sides of a box. For |
303 | /// example, the padding inside a box can be represented using this class. |
304 | /// |
305 | /// The [EdgeInsets] class specifies offsets in terms of visual edges, left, |
306 | /// top, right, and bottom. These values are not affected by the |
307 | /// [TextDirection]. To support both left-to-right and right-to-left layouts, |
308 | /// consider using [EdgeInsetsDirectional], which is expressed in terms of |
309 | /// _start_, top, _end_, and bottom, where start and end are resolved in terms |
310 | /// of a [TextDirection] (typically obtained from the ambient [Directionality]). |
311 | /// |
312 | /// {@tool snippet} |
313 | /// |
314 | /// Here are some examples of how to create [EdgeInsets] instances: |
315 | /// |
316 | /// Typical eight-pixel margin on all sides: |
317 | /// |
318 | /// ```dart |
319 | /// const EdgeInsets.all(8.0) |
320 | /// ``` |
321 | /// {@end-tool} |
322 | /// {@tool snippet} |
323 | /// |
324 | /// Eight pixel margin above and below, no horizontal margins: |
325 | /// |
326 | /// ```dart |
327 | /// const EdgeInsets.symmetric(vertical: 8.0) |
328 | /// ``` |
329 | /// {@end-tool} |
330 | /// {@tool snippet} |
331 | /// |
332 | /// Left margin indent of 40 pixels: |
333 | /// |
334 | /// ```dart |
335 | /// const EdgeInsets.only(left: 40.0) |
336 | /// ``` |
337 | /// {@end-tool} |
338 | /// |
339 | /// See also: |
340 | /// |
341 | /// * [Padding], a widget that accepts [EdgeInsets] to describe its margins. |
342 | /// * [EdgeInsetsDirectional], which (for properties and arguments that accept |
343 | /// the type [EdgeInsetsGeometry]) allows the horizontal insets to be |
344 | /// specified in a [TextDirection]-aware manner. |
345 | class EdgeInsets extends EdgeInsetsGeometry { |
346 | /// Creates insets from offsets from the left, top, right, and bottom. |
347 | const EdgeInsets.fromLTRB(this.left, this.top, this.right, this.bottom); |
348 | |
349 | /// Creates insets where all the offsets are `value`. |
350 | /// |
351 | /// {@tool snippet} |
352 | /// |
353 | /// Typical eight-pixel margin on all sides: |
354 | /// |
355 | /// ```dart |
356 | /// const EdgeInsets.all(8.0) |
357 | /// ``` |
358 | /// {@end-tool} |
359 | const EdgeInsets.all(double value) |
360 | : left = value, |
361 | top = value, |
362 | right = value, |
363 | bottom = value; |
364 | |
365 | /// Creates insets with only the given values non-zero. |
366 | /// |
367 | /// {@tool snippet} |
368 | /// |
369 | /// Left margin indent of 40 pixels: |
370 | /// |
371 | /// ```dart |
372 | /// const EdgeInsets.only(left: 40.0) |
373 | /// ``` |
374 | /// {@end-tool} |
375 | const EdgeInsets.only({ |
376 | this.left = 0.0, |
377 | this.top = 0.0, |
378 | this.right = 0.0, |
379 | this.bottom = 0.0, |
380 | }); |
381 | |
382 | /// Creates insets with symmetrical vertical and horizontal offsets. |
383 | /// |
384 | /// {@tool snippet} |
385 | /// |
386 | /// Eight pixel margin above and below, no horizontal margins: |
387 | /// |
388 | /// ```dart |
389 | /// const EdgeInsets.symmetric(vertical: 8.0) |
390 | /// ``` |
391 | /// {@end-tool} |
392 | const EdgeInsets.symmetric({ |
393 | double vertical = 0.0, |
394 | double horizontal = 0.0, |
395 | }) : left = horizontal, |
396 | top = vertical, |
397 | right = horizontal, |
398 | bottom = vertical; |
399 | |
400 | /// Creates insets that match the given view padding. |
401 | /// |
402 | /// If you need the current system padding or view insets in the context of a |
403 | /// widget, consider using [MediaQuery.paddingOf] to obtain these values rather than |
404 | /// using the value from a [FlutterView] directly, so that you get notified of |
405 | /// changes. |
406 | EdgeInsets.fromViewPadding(ui.ViewPadding padding, double devicePixelRatio) |
407 | : left = padding.left / devicePixelRatio, |
408 | top = padding.top / devicePixelRatio, |
409 | right = padding.right / devicePixelRatio, |
410 | bottom = padding.bottom / devicePixelRatio; |
411 | |
412 | /// Deprecated. Will be removed in a future version of Flutter. |
413 | /// |
414 | /// Use [EdgeInsets.fromViewPadding] instead. |
415 | @Deprecated( |
416 | 'Use EdgeInsets.fromViewPadding instead. ' |
417 | 'This feature was deprecated after v3.8.0-14.0.pre.' , |
418 | ) |
419 | factory EdgeInsets.fromWindowPadding(ui.ViewPadding padding, double devicePixelRatio) = EdgeInsets.fromViewPadding; |
420 | |
421 | /// An [EdgeInsets] with zero offsets in each direction. |
422 | static const EdgeInsets zero = EdgeInsets.only(); |
423 | |
424 | /// The offset from the left. |
425 | final double left; |
426 | |
427 | @override |
428 | double get _left => left; |
429 | |
430 | /// The offset from the top. |
431 | final double top; |
432 | |
433 | @override |
434 | double get _top => top; |
435 | |
436 | /// The offset from the right. |
437 | final double right; |
438 | |
439 | @override |
440 | double get _right => right; |
441 | |
442 | /// The offset from the bottom. |
443 | final double bottom; |
444 | |
445 | @override |
446 | double get _bottom => bottom; |
447 | |
448 | @override |
449 | double get _start => 0.0; |
450 | |
451 | @override |
452 | double get _end => 0.0; |
453 | |
454 | /// An Offset describing the vector from the top left of a rectangle to the |
455 | /// top left of that rectangle inset by this object. |
456 | Offset get topLeft => Offset(left, top); |
457 | |
458 | /// An Offset describing the vector from the top right of a rectangle to the |
459 | /// top right of that rectangle inset by this object. |
460 | Offset get topRight => Offset(-right, top); |
461 | |
462 | /// An Offset describing the vector from the bottom left of a rectangle to the |
463 | /// bottom left of that rectangle inset by this object. |
464 | Offset get bottomLeft => Offset(left, -bottom); |
465 | |
466 | /// An Offset describing the vector from the bottom right of a rectangle to the |
467 | /// bottom right of that rectangle inset by this object. |
468 | Offset get bottomRight => Offset(-right, -bottom); |
469 | |
470 | /// An [EdgeInsets] with top and bottom as well as left and right flipped. |
471 | @override |
472 | EdgeInsets get flipped => EdgeInsets.fromLTRB(right, bottom, left, top); |
473 | |
474 | /// Returns a new rect that is bigger than the given rect in each direction by |
475 | /// the amount of inset in each direction. Specifically, the left edge of the |
476 | /// rect is moved left by [left], the top edge of the rect is moved up by |
477 | /// [top], the right edge of the rect is moved right by [right], and the |
478 | /// bottom edge of the rect is moved down by [bottom]. |
479 | /// |
480 | /// See also: |
481 | /// |
482 | /// * [inflateSize], to inflate a [Size] rather than a [Rect]. |
483 | /// * [deflateRect], to deflate a [Rect] rather than inflating it. |
484 | Rect inflateRect(Rect rect) { |
485 | return Rect.fromLTRB(rect.left - left, rect.top - top, rect.right + right, rect.bottom + bottom); |
486 | } |
487 | |
488 | /// Returns a new rect that is smaller than the given rect in each direction by |
489 | /// the amount of inset in each direction. Specifically, the left edge of the |
490 | /// rect is moved right by [left], the top edge of the rect is moved down by |
491 | /// [top], the right edge of the rect is moved left by [right], and the |
492 | /// bottom edge of the rect is moved up by [bottom]. |
493 | /// |
494 | /// If the argument's [Rect.size] is smaller than [collapsedSize], then the |
495 | /// resulting rectangle will have negative dimensions. |
496 | /// |
497 | /// See also: |
498 | /// |
499 | /// * [deflateSize], to deflate a [Size] rather than a [Rect]. |
500 | /// * [inflateRect], to inflate a [Rect] rather than deflating it. |
501 | Rect deflateRect(Rect rect) { |
502 | return Rect.fromLTRB(rect.left + left, rect.top + top, rect.right - right, rect.bottom - bottom); |
503 | } |
504 | |
505 | @override |
506 | EdgeInsetsGeometry subtract(EdgeInsetsGeometry other) { |
507 | if (other is EdgeInsets) { |
508 | return this - other; |
509 | } |
510 | return super.subtract(other); |
511 | } |
512 | |
513 | @override |
514 | EdgeInsetsGeometry add(EdgeInsetsGeometry other) { |
515 | if (other is EdgeInsets) { |
516 | return this + other; |
517 | } |
518 | return super.add(other); |
519 | } |
520 | |
521 | @override |
522 | EdgeInsetsGeometry clamp(EdgeInsetsGeometry min, EdgeInsetsGeometry max) { |
523 | return EdgeInsets.fromLTRB( |
524 | clampDouble(_left, min._left, max._left), |
525 | clampDouble(_top, min._top, max._top), |
526 | clampDouble(_right, min._right, max._right), |
527 | clampDouble(_bottom, min._bottom, max._bottom), |
528 | ); |
529 | } |
530 | |
531 | /// Returns the difference between two [EdgeInsets]. |
532 | EdgeInsets operator -(EdgeInsets other) { |
533 | return EdgeInsets.fromLTRB( |
534 | left - other.left, |
535 | top - other.top, |
536 | right - other.right, |
537 | bottom - other.bottom, |
538 | ); |
539 | } |
540 | |
541 | /// Returns the sum of two [EdgeInsets]. |
542 | EdgeInsets operator +(EdgeInsets other) { |
543 | return EdgeInsets.fromLTRB( |
544 | left + other.left, |
545 | top + other.top, |
546 | right + other.right, |
547 | bottom + other.bottom, |
548 | ); |
549 | } |
550 | |
551 | /// Returns the [EdgeInsets] object with each dimension negated. |
552 | /// |
553 | /// This is the same as multiplying the object by -1.0. |
554 | @override |
555 | EdgeInsets operator -() { |
556 | return EdgeInsets.fromLTRB( |
557 | -left, |
558 | -top, |
559 | -right, |
560 | -bottom, |
561 | ); |
562 | } |
563 | |
564 | /// Scales the [EdgeInsets] in each dimension by the given factor. |
565 | @override |
566 | EdgeInsets operator *(double other) { |
567 | return EdgeInsets.fromLTRB( |
568 | left * other, |
569 | top * other, |
570 | right * other, |
571 | bottom * other, |
572 | ); |
573 | } |
574 | |
575 | /// Divides the [EdgeInsets] in each dimension by the given factor. |
576 | @override |
577 | EdgeInsets operator /(double other) { |
578 | return EdgeInsets.fromLTRB( |
579 | left / other, |
580 | top / other, |
581 | right / other, |
582 | bottom / other, |
583 | ); |
584 | } |
585 | |
586 | /// Integer divides the [EdgeInsets] in each dimension by the given factor. |
587 | @override |
588 | EdgeInsets operator ~/(double other) { |
589 | return EdgeInsets.fromLTRB( |
590 | (left ~/ other).toDouble(), |
591 | (top ~/ other).toDouble(), |
592 | (right ~/ other).toDouble(), |
593 | (bottom ~/ other).toDouble(), |
594 | ); |
595 | } |
596 | |
597 | /// Computes the remainder in each dimension by the given factor. |
598 | @override |
599 | EdgeInsets operator %(double other) { |
600 | return EdgeInsets.fromLTRB( |
601 | left % other, |
602 | top % other, |
603 | right % other, |
604 | bottom % other, |
605 | ); |
606 | } |
607 | |
608 | /// Linearly interpolate between two [EdgeInsets]. |
609 | /// |
610 | /// If either is null, this function interpolates from [EdgeInsets.zero]. |
611 | /// |
612 | /// {@macro dart.ui.shadow.lerp} |
613 | static EdgeInsets? lerp(EdgeInsets? a, EdgeInsets? b, double t) { |
614 | if (identical(a, b)) { |
615 | return a; |
616 | } |
617 | if (a == null) { |
618 | return b! * t; |
619 | } |
620 | if (b == null) { |
621 | return a * (1.0 - t); |
622 | } |
623 | return EdgeInsets.fromLTRB( |
624 | ui.lerpDouble(a.left, b.left, t)!, |
625 | ui.lerpDouble(a.top, b.top, t)!, |
626 | ui.lerpDouble(a.right, b.right, t)!, |
627 | ui.lerpDouble(a.bottom, b.bottom, t)!, |
628 | ); |
629 | } |
630 | |
631 | @override |
632 | EdgeInsets resolve(TextDirection? direction) => this; |
633 | |
634 | /// Creates a copy of this EdgeInsets but with the given fields replaced |
635 | /// with the new values. |
636 | EdgeInsets copyWith({ |
637 | double? left, |
638 | double? top, |
639 | double? right, |
640 | double? bottom, |
641 | }) { |
642 | return EdgeInsets.only( |
643 | left: left ?? this.left, |
644 | top: top ?? this.top, |
645 | right: right ?? this.right, |
646 | bottom: bottom ?? this.bottom, |
647 | ); |
648 | } |
649 | } |
650 | |
651 | /// An immutable set of offsets in each of the four cardinal directions, but |
652 | /// whose horizontal components are dependent on the writing direction. |
653 | /// |
654 | /// This can be used to indicate padding from the left in [TextDirection.ltr] |
655 | /// text and padding from the right in [TextDirection.rtl] text without having |
656 | /// to be aware of the current text direction. |
657 | /// |
658 | /// See also: |
659 | /// |
660 | /// * [EdgeInsets], a variant that uses physical labels (left and right instead |
661 | /// of start and end). |
662 | class EdgeInsetsDirectional extends EdgeInsetsGeometry { |
663 | /// Creates insets from offsets from the start, top, end, and bottom. |
664 | const EdgeInsetsDirectional.fromSTEB(this.start, this.top, this.end, this.bottom); |
665 | |
666 | /// Creates insets with only the given values non-zero. |
667 | /// |
668 | /// {@tool snippet} |
669 | /// |
670 | /// A margin indent of 40 pixels on the leading side: |
671 | /// |
672 | /// ```dart |
673 | /// const EdgeInsetsDirectional.only(start: 40.0) |
674 | /// ``` |
675 | /// {@end-tool} |
676 | const EdgeInsetsDirectional.only({ |
677 | this.start = 0.0, |
678 | this.top = 0.0, |
679 | this.end = 0.0, |
680 | this.bottom = 0.0, |
681 | }); |
682 | |
683 | /// Creates insets with symmetric vertical and horizontal offsets. |
684 | /// |
685 | /// This is equivalent to [EdgeInsets.symmetric], since the inset is the same |
686 | /// with either [TextDirection]. This constructor is just a convenience for |
687 | /// type compatibility. |
688 | /// |
689 | /// {@tool snippet} |
690 | /// Eight pixel margin above and below, no horizontal margins: |
691 | /// |
692 | /// ```dart |
693 | /// const EdgeInsetsDirectional.symmetric(vertical: 8.0) |
694 | /// ``` |
695 | /// {@end-tool} |
696 | const EdgeInsetsDirectional.symmetric({ |
697 | double horizontal = 0.0, |
698 | double vertical = 0.0, |
699 | }) : start = horizontal, |
700 | end = horizontal, |
701 | top = vertical, |
702 | bottom = vertical; |
703 | |
704 | /// Creates insets where all the offsets are `value`. |
705 | /// |
706 | /// {@tool snippet} |
707 | /// |
708 | /// Typical eight-pixel margin on all sides: |
709 | /// |
710 | /// ```dart |
711 | /// const EdgeInsetsDirectional.all(8.0) |
712 | /// ``` |
713 | /// {@end-tool} |
714 | const EdgeInsetsDirectional.all(double value) |
715 | : start = value, |
716 | top = value, |
717 | end = value, |
718 | bottom = value; |
719 | |
720 | /// An [EdgeInsetsDirectional] with zero offsets in each direction. |
721 | /// |
722 | /// Consider using [EdgeInsets.zero] instead, since that object has the same |
723 | /// effect, but will be cheaper to [resolve]. |
724 | static const EdgeInsetsDirectional zero = EdgeInsetsDirectional.only(); |
725 | |
726 | /// The offset from the start side, the side from which the user will start |
727 | /// reading text. |
728 | /// |
729 | /// This value is normalized into an [EdgeInsets.left] or [EdgeInsets.right] |
730 | /// value by the [resolve] method. |
731 | final double start; |
732 | |
733 | @override |
734 | double get _start => start; |
735 | |
736 | /// The offset from the top. |
737 | /// |
738 | /// This value is passed through to [EdgeInsets.top] unmodified by the |
739 | /// [resolve] method. |
740 | final double top; |
741 | |
742 | @override |
743 | double get _top => top; |
744 | |
745 | /// The offset from the end side, the side on which the user ends reading |
746 | /// text. |
747 | /// |
748 | /// This value is normalized into an [EdgeInsets.left] or [EdgeInsets.right] |
749 | /// value by the [resolve] method. |
750 | final double end; |
751 | |
752 | @override |
753 | double get _end => end; |
754 | |
755 | /// The offset from the bottom. |
756 | /// |
757 | /// This value is passed through to [EdgeInsets.bottom] unmodified by the |
758 | /// [resolve] method. |
759 | final double bottom; |
760 | |
761 | @override |
762 | double get _bottom => bottom; |
763 | |
764 | @override |
765 | double get _left => 0.0; |
766 | |
767 | @override |
768 | double get _right => 0.0; |
769 | |
770 | @override |
771 | bool get isNonNegative => start >= 0.0 && top >= 0.0 && end >= 0.0 && bottom >= 0.0; |
772 | |
773 | /// An [EdgeInsetsDirectional] with [top] and [bottom] as well as [start] and [end] flipped. |
774 | @override |
775 | EdgeInsetsDirectional get flipped => EdgeInsetsDirectional.fromSTEB(end, bottom, start, top); |
776 | |
777 | @override |
778 | EdgeInsetsGeometry subtract(EdgeInsetsGeometry other) { |
779 | if (other is EdgeInsetsDirectional) { |
780 | return this - other; |
781 | } |
782 | return super.subtract(other); |
783 | } |
784 | |
785 | @override |
786 | EdgeInsetsGeometry add(EdgeInsetsGeometry other) { |
787 | if (other is EdgeInsetsDirectional) { |
788 | return this + other; |
789 | } |
790 | return super.add(other); |
791 | } |
792 | |
793 | /// Returns the difference between two [EdgeInsetsDirectional] objects. |
794 | EdgeInsetsDirectional operator -(EdgeInsetsDirectional other) { |
795 | return EdgeInsetsDirectional.fromSTEB( |
796 | start - other.start, |
797 | top - other.top, |
798 | end - other.end, |
799 | bottom - other.bottom, |
800 | ); |
801 | } |
802 | |
803 | /// Returns the sum of two [EdgeInsetsDirectional] objects. |
804 | EdgeInsetsDirectional operator +(EdgeInsetsDirectional other) { |
805 | return EdgeInsetsDirectional.fromSTEB( |
806 | start + other.start, |
807 | top + other.top, |
808 | end + other.end, |
809 | bottom + other.bottom, |
810 | ); |
811 | } |
812 | |
813 | /// Returns the [EdgeInsetsDirectional] object with each dimension negated. |
814 | /// |
815 | /// This is the same as multiplying the object by -1.0. |
816 | @override |
817 | EdgeInsetsDirectional operator -() { |
818 | return EdgeInsetsDirectional.fromSTEB( |
819 | -start, |
820 | -top, |
821 | -end, |
822 | -bottom, |
823 | ); |
824 | } |
825 | |
826 | /// Scales the [EdgeInsetsDirectional] object in each dimension by the given factor. |
827 | @override |
828 | EdgeInsetsDirectional operator *(double other) { |
829 | return EdgeInsetsDirectional.fromSTEB( |
830 | start * other, |
831 | top * other, |
832 | end * other, |
833 | bottom * other, |
834 | ); |
835 | } |
836 | |
837 | /// Divides the [EdgeInsetsDirectional] object in each dimension by the given factor. |
838 | @override |
839 | EdgeInsetsDirectional operator /(double other) { |
840 | return EdgeInsetsDirectional.fromSTEB( |
841 | start / other, |
842 | top / other, |
843 | end / other, |
844 | bottom / other, |
845 | ); |
846 | } |
847 | |
848 | /// Integer divides the [EdgeInsetsDirectional] object in each dimension by the given factor. |
849 | @override |
850 | EdgeInsetsDirectional operator ~/(double other) { |
851 | return EdgeInsetsDirectional.fromSTEB( |
852 | (start ~/ other).toDouble(), |
853 | (top ~/ other).toDouble(), |
854 | (end ~/ other).toDouble(), |
855 | (bottom ~/ other).toDouble(), |
856 | ); |
857 | } |
858 | |
859 | /// Computes the remainder in each dimension by the given factor. |
860 | @override |
861 | EdgeInsetsDirectional operator %(double other) { |
862 | return EdgeInsetsDirectional.fromSTEB( |
863 | start % other, |
864 | top % other, |
865 | end % other, |
866 | bottom % other, |
867 | ); |
868 | } |
869 | |
870 | /// Linearly interpolate between two [EdgeInsetsDirectional]. |
871 | /// |
872 | /// If either is null, this function interpolates from [EdgeInsetsDirectional.zero]. |
873 | /// |
874 | /// To interpolate between two [EdgeInsetsGeometry] objects of arbitrary type |
875 | /// (either [EdgeInsets] or [EdgeInsetsDirectional]), consider the |
876 | /// [EdgeInsetsGeometry.lerp] static method. |
877 | /// |
878 | /// {@macro dart.ui.shadow.lerp} |
879 | static EdgeInsetsDirectional? lerp(EdgeInsetsDirectional? a, EdgeInsetsDirectional? b, double t) { |
880 | if (identical(a, b)) { |
881 | return a; |
882 | } |
883 | if (a == null) { |
884 | return b! * t; |
885 | } |
886 | if (b == null) { |
887 | return a * (1.0 - t); |
888 | } |
889 | return EdgeInsetsDirectional.fromSTEB( |
890 | ui.lerpDouble(a.start, b.start, t)!, |
891 | ui.lerpDouble(a.top, b.top, t)!, |
892 | ui.lerpDouble(a.end, b.end, t)!, |
893 | ui.lerpDouble(a.bottom, b.bottom, t)!, |
894 | ); |
895 | } |
896 | |
897 | @override |
898 | EdgeInsets resolve(TextDirection? direction) { |
899 | assert(direction != null); |
900 | switch (direction!) { |
901 | case TextDirection.rtl: |
902 | return EdgeInsets.fromLTRB(end, top, start, bottom); |
903 | case TextDirection.ltr: |
904 | return EdgeInsets.fromLTRB(start, top, end, bottom); |
905 | } |
906 | } |
907 | |
908 | /// Creates a copy of this EdgeInsetsDirectional but with the given |
909 | /// fields replaced with the new values. |
910 | EdgeInsetsDirectional copyWith({ |
911 | double? start, |
912 | double? top, |
913 | double? end, |
914 | double? bottom, |
915 | }) { |
916 | return EdgeInsetsDirectional.only( |
917 | start: start ?? this.start, |
918 | top: top ?? this.top, |
919 | end: end ?? this.end, |
920 | bottom: bottom ?? this.bottom, |
921 | ); |
922 | } |
923 | } |
924 | |
925 | class _MixedEdgeInsets extends EdgeInsetsGeometry { |
926 | const _MixedEdgeInsets.fromLRSETB(this._left, this._right, this._start, this._end, this._top, this._bottom); |
927 | |
928 | @override |
929 | final double _left; |
930 | |
931 | @override |
932 | final double _right; |
933 | |
934 | @override |
935 | final double _start; |
936 | |
937 | @override |
938 | final double _end; |
939 | |
940 | @override |
941 | final double _top; |
942 | |
943 | @override |
944 | final double _bottom; |
945 | |
946 | @override |
947 | bool get isNonNegative { |
948 | return _left >= 0.0 |
949 | && _right >= 0.0 |
950 | && _start >= 0.0 |
951 | && _end >= 0.0 |
952 | && _top >= 0.0 |
953 | && _bottom >= 0.0; |
954 | } |
955 | |
956 | @override |
957 | _MixedEdgeInsets operator -() { |
958 | return _MixedEdgeInsets.fromLRSETB( |
959 | -_left, |
960 | -_right, |
961 | -_start, |
962 | -_end, |
963 | -_top, |
964 | -_bottom, |
965 | ); |
966 | } |
967 | |
968 | @override |
969 | _MixedEdgeInsets operator *(double other) { |
970 | return _MixedEdgeInsets.fromLRSETB( |
971 | _left * other, |
972 | _right * other, |
973 | _start * other, |
974 | _end * other, |
975 | _top * other, |
976 | _bottom * other, |
977 | ); |
978 | } |
979 | |
980 | @override |
981 | _MixedEdgeInsets operator /(double other) { |
982 | return _MixedEdgeInsets.fromLRSETB( |
983 | _left / other, |
984 | _right / other, |
985 | _start / other, |
986 | _end / other, |
987 | _top / other, |
988 | _bottom / other, |
989 | ); |
990 | } |
991 | |
992 | @override |
993 | _MixedEdgeInsets operator ~/(double other) { |
994 | return _MixedEdgeInsets.fromLRSETB( |
995 | (_left ~/ other).toDouble(), |
996 | (_right ~/ other).toDouble(), |
997 | (_start ~/ other).toDouble(), |
998 | (_end ~/ other).toDouble(), |
999 | (_top ~/ other).toDouble(), |
1000 | (_bottom ~/ other).toDouble(), |
1001 | ); |
1002 | } |
1003 | |
1004 | @override |
1005 | _MixedEdgeInsets operator %(double other) { |
1006 | return _MixedEdgeInsets.fromLRSETB( |
1007 | _left % other, |
1008 | _right % other, |
1009 | _start % other, |
1010 | _end % other, |
1011 | _top % other, |
1012 | _bottom % other, |
1013 | ); |
1014 | } |
1015 | |
1016 | @override |
1017 | EdgeInsets resolve(TextDirection? direction) { |
1018 | assert(direction != null); |
1019 | switch (direction!) { |
1020 | case TextDirection.rtl: |
1021 | return EdgeInsets.fromLTRB(_end + _left, _top, _start + _right, _bottom); |
1022 | case TextDirection.ltr: |
1023 | return EdgeInsets.fromLTRB(_start + _left, _top, _end + _right, _bottom); |
1024 | } |
1025 | } |
1026 | } |
1027 | |