Line data Source code
1 : // Copyright 2014 The Flutter Authors.
2 : // Copyright 2021 Suragch.
3 : // All rights reserved.
4 : // Use of this source code is governed by a BSD-style license that can be
5 : // found in the LICENSE file.
6 :
7 : import 'package:flutter/foundation.dart';
8 : import 'package:flutter/rendering.dart';
9 : import 'package:flutter/widgets.dart';
10 :
11 : /// A widget that sizes its child to the child's maximum intrinsic height.
12 : ///
13 : /// This class is useful, for example, when unlimited height is available and
14 : /// you would like a child that would otherwise attempt to expand infinitely to
15 : /// instead size itself to a more reasonable height.
16 : ///
17 : /// The constraints that this widget passes to its child will adhere to the
18 : /// parent's constraints, so if the constraints are not large enough to satisfy
19 : /// the child's maximum intrinsic height, then the child will get less height
20 : /// than it otherwise would. Likewise, if the minimum height constraint is
21 : /// larger than the child's maximum intrinsic height, the child will be given
22 : /// more width than it otherwise would.
23 : ///
24 : /// If [stepHeight] is non-null, the child's height will be snapped to a multiple
25 : /// of the [stepHeight]. Similarly, if [stepWidth] is non-null, the child's
26 : /// width will be snapped to a multiple of the [stepWidth].
27 : ///
28 : /// This class is relatively expensive, because it adds a speculative layout
29 : /// pass before the final layout phase. Avoid using it where possible. In the
30 : /// worst case, this widget can result in a layout that is O(N²) in the depth of
31 : /// the tree.
32 : ///
33 : /// See also:
34 : ///
35 : /// * [Align], a widget that aligns its child within itself. This can be used
36 : /// to loosen the constraints passed to the [MongolRenderIntrinsicHeight],
37 : /// allowing the [MongolRenderIntrinsicHeight]'s child to be smaller than that of
38 : /// its parent.
39 : /// * [Column], which when used with [CrossAxisAlignment.stretch] can be used
40 : /// to loosen just the height constraints that are passed to the
41 : /// [MongolRenderIntrinsicHeight], allowing the [MongolRenderIntrinsicHeight]'s child's
42 : /// height to be smaller than that of its parent.
43 : class MongolIntrinsicHeight extends SingleChildRenderObjectWidget {
44 : /// Creates a widget that sizes its child to the child's intrinsic height.
45 : ///
46 : /// This class is relatively expensive. Avoid using it where possible.
47 1 : const MongolIntrinsicHeight(
48 : {Key? key, this.stepHeight, this.stepWidth, Widget? child})
49 1 : : assert(stepHeight == null || stepHeight >= 0.0),
50 0 : assert(stepWidth == null || stepWidth >= 0.0),
51 1 : super(key: key, child: child);
52 :
53 : /// If non-null, force the child's height to be a multiple of this value.
54 : ///
55 : /// If null or 0.0 the child's height will be the same as its maximum
56 : /// intrinsic height.
57 : ///
58 : /// This value must not be negative.
59 : ///
60 : /// See also:
61 : ///
62 : /// * [RenderBox.getMaxIntrinsicHeight], which defines a widget's max
63 : /// intrinsic height in general.
64 : final double? stepHeight;
65 :
66 : /// If non-null, force the child's width to be a multiple of this value.
67 : ///
68 : /// If null or 0.0 the child's width will not be constrained.
69 : ///
70 : /// This value must not be negative.
71 : final double? stepWidth;
72 :
73 4 : double? get _stepHeight => stepHeight == 0.0 ? null : stepHeight;
74 4 : double? get _stepWidth => stepWidth == 0.0 ? null : stepWidth;
75 :
76 1 : @override
77 : MongolRenderIntrinsicHeight createRenderObject(BuildContext context) {
78 1 : return MongolRenderIntrinsicHeight(
79 2 : stepHeight: _stepHeight, stepWidth: _stepWidth);
80 : }
81 :
82 1 : @override
83 : void updateRenderObject(
84 : BuildContext context, MongolRenderIntrinsicHeight renderObject) {
85 : renderObject
86 2 : ..stepHeight = _stepHeight
87 2 : ..stepWidth = _stepWidth;
88 : }
89 : }
90 :
91 : /// Sizes its child to the child's maximum intrinsic height.
92 : ///
93 : /// This class is useful, for example, when unlimited height is available and
94 : /// you would like a child that would otherwise attempt to expand infinitely to
95 : /// instead size itself to a more reasonable height.
96 : ///
97 : /// The constraints that this widget passes to its child will adhere to the
98 : /// parent's constraints, so if the constraints are not large enough to satisfy
99 : /// the child's maximum intrinsic height, then the child will get less height
100 : /// than it otherwise would. Likewise, if the minimum height constraint is
101 : /// larger than the child's maximum intrinsic height, the child will be given
102 : /// more width than it otherwise would.
103 : ///
104 : /// If [stepHeight] is non-null, the child's height will be snapped to a multiple
105 : /// of the [stepHeight]. Similarly, if [stepWidth] is non-null, the child's
106 : /// width will be snapped to a multiple of the [stepWidth].
107 : ///
108 : /// This class is relatively expensive, because it adds a speculative layout
109 : /// pass before the final layout phase. Avoid using it where possible. In the
110 : /// worst case, this widget can result in a layout that is O(N²) in the depth of
111 : /// the tree.
112 : ///
113 : /// See also:
114 : ///
115 : /// * [Align], a widget that aligns its child within itself. This can be used
116 : /// to loosen the constraints passed to the [MongolRenderIntrinsicHeight],
117 : /// allowing the [MongolRenderIntrinsicHeight]'s child to be smaller than that of
118 : /// its parent.
119 : /// * [Column], which when used with [CrossAxisAlignment.stretch] can be used
120 : /// to loosen just the height constraints that are passed to the
121 : /// [MongolRenderIntrinsicHeight], allowing the [MongolRenderIntrinsicHeight]'s child's
122 : /// height to be smaller than that of its parent.
123 : class MongolRenderIntrinsicHeight extends RenderProxyBox {
124 : /// Creates a render object that sizes itself to its child's intrinsic height.
125 : ///
126 : /// If [stepHeight] is non-null it must be > 0.0. Similarly If [stepWidth] is
127 : /// non-null it must be > 0.0.
128 1 : MongolRenderIntrinsicHeight({
129 : double? stepHeight,
130 : double? stepWidth,
131 : RenderBox? child,
132 1 : }) : assert(stepHeight == null || stepHeight > 0.0),
133 0 : assert(stepWidth == null || stepWidth > 0.0),
134 : _stepHeight = stepHeight,
135 : _stepWidth = stepWidth,
136 1 : super(child);
137 :
138 : /// If non-null, force the child's height to be a multiple of this value.
139 : ///
140 : /// This value must be null or > 0.0.
141 0 : double? get stepHeight => _stepHeight;
142 : double? _stepHeight;
143 1 : set stepHeight(double? value) {
144 1 : assert(value == null || value > 0.0);
145 2 : if (value == _stepHeight) return;
146 0 : _stepHeight = value;
147 0 : markNeedsLayout();
148 : }
149 :
150 : /// If non-null, force the child's width to be a multiple of this value.
151 : ///
152 : /// This value must be null or > 0.0.
153 0 : double? get stepWidth => _stepWidth;
154 : double? _stepWidth;
155 1 : set stepWidth(double? value) {
156 0 : assert(value == null || value > 0.0);
157 2 : if (value == _stepWidth) return;
158 0 : _stepWidth = value;
159 0 : markNeedsLayout();
160 : }
161 :
162 1 : static double _applyStep(double input, double? step) {
163 1 : assert(input.isFinite);
164 : if (step == null) return input;
165 3 : return (input / step).ceil() * step;
166 : }
167 :
168 1 : @override
169 : double computeMinIntrinsicHeight(double width) {
170 1 : return computeMaxIntrinsicHeight(width);
171 : }
172 :
173 1 : @override
174 : double computeMaxIntrinsicHeight(double width) {
175 1 : if (child == null) return 0.0;
176 2 : final double height = child!.getMaxIntrinsicHeight(width);
177 2 : return _applyStep(height, _stepHeight);
178 : }
179 :
180 1 : @override
181 : double computeMinIntrinsicWidth(double height) {
182 1 : if (child == null) return 0.0;
183 2 : if (!height.isFinite) height = computeMaxIntrinsicHeight(double.infinity);
184 1 : assert(height.isFinite);
185 2 : final double width = child!.getMinIntrinsicWidth(height);
186 2 : return _applyStep(width, _stepWidth);
187 : }
188 :
189 1 : @override
190 : double computeMaxIntrinsicWidth(double height) {
191 1 : if (child == null) return 0.0;
192 2 : if (!height.isFinite) height = computeMaxIntrinsicHeight(double.infinity);
193 1 : assert(height.isFinite);
194 2 : final double width = child!.getMaxIntrinsicWidth(height);
195 2 : return _applyStep(width, _stepWidth);
196 : }
197 :
198 1 : Size _computeSize(
199 : {required ChildLayouter layoutChild,
200 : required BoxConstraints constraints}) {
201 1 : if (child != null) {
202 1 : if (!constraints.hasTightHeight) {
203 : final double height =
204 3 : child!.getMaxIntrinsicHeight(constraints.maxWidth);
205 1 : assert(height.isFinite);
206 : constraints =
207 3 : constraints.tighten(height: _applyStep(height, _stepHeight));
208 : }
209 1 : if (_stepWidth != null) {
210 0 : final double width = child!.getMaxIntrinsicWidth(constraints.maxHeight);
211 0 : assert(width.isFinite);
212 0 : constraints = constraints.tighten(width: _applyStep(width, _stepWidth));
213 : }
214 2 : return layoutChild(child!, constraints);
215 : } else {
216 0 : return constraints.smallest;
217 : }
218 : }
219 :
220 1 : @override
221 : Size computeDryLayout(BoxConstraints constraints) {
222 1 : return _computeSize(
223 : layoutChild: ChildLayoutHelper.dryLayoutChild,
224 : constraints: constraints,
225 : );
226 : }
227 :
228 1 : @override
229 : void performLayout() {
230 2 : size = _computeSize(
231 : layoutChild: ChildLayoutHelper.layoutChild,
232 1 : constraints: constraints,
233 : );
234 : }
235 :
236 0 : @override
237 : void debugFillProperties(DiagnosticPropertiesBuilder properties) {
238 0 : super.debugFillProperties(properties);
239 0 : properties.add(DoubleProperty('stepHeight', stepHeight));
240 0 : properties.add(DoubleProperty('stepWidth', stepWidth));
241 : }
242 : }
|