1 /*
2 * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "core/components/box/render_box_base.h"
17
18 #include <algorithm>
19
20 #include "base/geometry/offset.h"
21 #include "base/log/dump_log.h"
22 #include "base/utils/string_expression.h"
23 #include "core/animation/property_animatable_helper.h"
24 #include "core/components/box/box_base_component.h"
25 #include "core/components/common/properties/radius.h"
26 #include "core/components/flex/render_flex.h"
27 #include "core/components/text_field/render_text_field.h"
28
29 namespace OHOS::Ace {
30 namespace {
31
32 const double CIRCLE_LAYOUT_IN_BOX_SCALE = sin(M_PI_4);
33 constexpr double BOX_DIAMETER_TO_RADIUS = 2.0;
34 constexpr int32_t COMPATIBLE_VERSION = 5;
35 constexpr double TWO_SIDES = 2.0;
36
37 } // namespace
38
GetBorderSize() const39 Size RenderBoxBase::GetBorderSize() const
40 {
41 return Size(0.0, 0.0);
42 }
43
GetBorderOffset() const44 Offset RenderBoxBase::GetBorderOffset() const
45 {
46 return Offset(0.0, 0.0);
47 }
48
GetBorderRadius() const49 Radius RenderBoxBase::GetBorderRadius() const
50 {
51 return Radius();
52 }
53
IsSizeValid(const Dimension & value,double maxLimit)54 bool RenderBoxBase::IsSizeValid(const Dimension& value, double maxLimit)
55 {
56 if (NearZero(value.Value())) {
57 return false;
58 }
59 if ((value.Unit() == DimensionUnit::PERCENT) && (NearEqual(maxLimit, Size::INFINITE_SIZE))) {
60 // When maxLimit is INFINITE, percent value is invalid, except PERCENT_FLAG_USE_VIEW_PORT is set.
61 return percentFlag_ == PERCENT_FLAG_USE_VIEW_PORT;
62 }
63 return true;
64 }
65
OnAnimationCallback()66 void RenderBoxBase::OnAnimationCallback()
67 {
68 MarkNeedLayout();
69 }
70
CalculateHeightPercent(double percent) const71 double RenderBoxBase::CalculateHeightPercent(double percent) const
72 {
73 return ConvertVerticalDimensionToPx(Dimension(percent, DimensionUnit::PERCENT));
74 }
75
ConvertMarginToPx(CalcDimension dimension,bool vertical,bool additional) const76 double RenderBoxBase::ConvertMarginToPx(CalcDimension dimension, bool vertical, bool additional) const
77 {
78 if (dimension.Unit() == DimensionUnit::CALC) {
79 std::string value = dimension.CalcValue();
80 auto node = AceType::Claim(const_cast<RenderBoxBase*>(this));
81 return StringExpression::CalculateExp(value, [vertical, node](const Dimension& dim) -> double {
82 return node->NormalizePercentToPx(dim, vertical, false);
83 });
84 } else if (dimension.Unit() == DimensionUnit::PERCENT) {
85 double parentLimit = 0.0;
86 if (vertical) {
87 parentLimit = GetLayoutParam().GetMaxSize().Height();
88 if (NearEqual(parentLimit, Size::INFINITE_SIZE) && !NearEqual(selfMaxHeight_, Size::INFINITE_SIZE)) {
89 parentLimit = selfMaxHeight_;
90 }
91 } else {
92 parentLimit = GetLayoutParam().GetMaxSize().Width();
93 if (NearEqual(parentLimit, Size::INFINITE_SIZE) && !NearEqual(selfMaxWidth_, Size::INFINITE_SIZE)) {
94 parentLimit = selfMaxWidth_;
95 }
96 }
97 if (NearEqual(parentLimit, Size::INFINITE_SIZE)) {
98 if (additional || percentFlag_ != PERCENT_FLAG_USE_VIEW_PORT) {
99 return 0.0; // Additional(from theme) set to 0.0 when INFINITE_SIZE.
100 }
101 parentLimit = vertical ? viewPort_.Height() : viewPort_.Width();
102 }
103 return parentLimit * dimension.Value();
104 } else if (dimension.Unit() == DimensionUnit::PX) {
105 return dimension.Value();
106 } else {
107 auto context = context_.Upgrade();
108 if (!context) {
109 return dimension.Value();
110 }
111 return context->NormalizeToPx(dimension);
112 }
113 }
114
ConvertDimensionToPx(CalcDimension dimension,bool vertical,bool defaultZero) const115 double RenderBoxBase::ConvertDimensionToPx(CalcDimension dimension, bool vertical, bool defaultZero) const
116 {
117 if (dimension.Unit() == DimensionUnit::CALC) {
118 std::string value = dimension.CalcValue();
119 auto node = AceType::Claim(const_cast<RenderBoxBase*>(this));
120 return StringExpression::CalculateExp(value, [vertical, node](const Dimension& dim) -> double {
121 return node->NormalizePercentToPx(dim, vertical, false);
122 });
123 } else if (dimension.Unit() == DimensionUnit::PERCENT) {
124 double parentLimit = GetLayoutParam().GetMaxSize().Width();
125 if (vertical) {
126 parentLimit = GetLayoutParam().GetMaxSize().Height();
127 }
128 if (NearEqual(parentLimit, Size::INFINITE_SIZE)) {
129 if (percentFlag_ != PERCENT_FLAG_USE_VIEW_PORT) {
130 return defaultZero ? 0.0 : Size::INFINITE_SIZE;
131 } else {
132 parentLimit = vertical ? viewPort_.Height() : viewPort_.Width();
133 }
134 }
135 return parentLimit * dimension.Value();
136 } else if (dimension.Unit() == DimensionUnit::PX) {
137 return dimension.Value();
138 } else {
139 auto context = context_.Upgrade();
140 if (!context) {
141 return dimension.Value();
142 }
143 return context->NormalizeToPx(dimension);
144 }
145 }
146
ConvertHorizontalDimensionToPx(CalcDimension dimension,bool defaultZero) const147 double RenderBoxBase::ConvertHorizontalDimensionToPx(CalcDimension dimension, bool defaultZero) const
148 {
149 return ConvertDimensionToPx(dimension, false, defaultZero);
150 }
151
ConvertVerticalDimensionToPx(CalcDimension dimension,bool defaultZero) const152 double RenderBoxBase::ConvertVerticalDimensionToPx(CalcDimension dimension, bool defaultZero) const
153 {
154 return ConvertDimensionToPx(dimension, true, defaultZero);
155 }
156
CalculateWidth()157 void RenderBoxBase::CalculateWidth()
158 {
159 useFlexWidth_ = true;
160 selfDefineWidth_ = false;
161 selfMaxWidth_ = ConvertHorizontalDimensionToPx(width_, false);
162 selfMinWidth_ = 0.0;
163 if (GreatOrEqual(selfMaxWidth_, 0.0) && !NearEqual(selfMaxWidth_, Size::INFINITE_SIZE)) {
164 selfMinWidth_ = 0.0;
165 selfDefineWidth_ = true;
166 useFlexWidth_ = false;
167 } else if (constraints_.IsWidthValid()) {
168 selfMaxWidth_ = constraints_.GetMaxSize().Width();
169 selfMinWidth_ = constraints_.GetMinSize().Width();
170 useFlexWidth_ = false;
171 } else if (flex_ != BoxFlex::FLEX_X && flex_ != BoxFlex::FLEX_XY) {
172 selfMaxWidth_ = Size::INFINITE_SIZE;
173 useFlexWidth_ = false;
174 } else {
175 // No width, no constrain, no flex, use default min and max, reset selfMaxWidth_ here.
176 selfMaxWidth_ = Size::INFINITE_SIZE;
177 }
178 if (!GetLayoutParam().HasUsedConstraints() && constraints_.IsWidthValid()) {
179 selfMaxWidth_ = std::clamp(selfMaxWidth_, constraints_.GetMinSize().Width(), constraints_.GetMaxSize().Width());
180 selfMinWidth_ = std::clamp(selfMinWidth_, constraints_.GetMinSize().Width(), constraints_.GetMaxSize().Width());
181 }
182 }
183
CalculateHeight()184 void RenderBoxBase::CalculateHeight()
185 {
186 useFlexHeight_ = true;
187 selfDefineHeight_ = false;
188 selfMaxHeight_ = ConvertVerticalDimensionToPx(height_, false);
189 selfMinHeight_ = 0.0;
190 if (GreatOrEqual(selfMaxHeight_, 0.0) && !NearEqual(selfMaxHeight_, Size::INFINITE_SIZE)) {
191 selfMinHeight_ = 0.0;
192 selfDefineHeight_ = true;
193 useFlexHeight_ = false;
194 } else if (constraints_.IsHeightValid()) {
195 selfMaxHeight_ = constraints_.GetMaxSize().Height();
196 selfMinHeight_ = constraints_.GetMinSize().Height();
197 useFlexHeight_ = false;
198 } else if (flex_ != BoxFlex::FLEX_Y && flex_ != BoxFlex::FLEX_XY) {
199 selfMaxHeight_ = Size::INFINITE_SIZE;
200 useFlexHeight_ = false;
201 } else {
202 // No height, no constrain, no flex, use default min and max, reset selfMaxHeight_ here.
203 selfMaxHeight_ = Size::INFINITE_SIZE;
204 }
205 if (!GetLayoutParam().HasUsedConstraints() && constraints_.IsHeightValid()) {
206 selfMaxHeight_ =
207 std::clamp(selfMaxHeight_, constraints_.GetMinSize().Height(), constraints_.GetMaxSize().Height());
208 selfMinHeight_ =
209 std::clamp(selfMinHeight_, constraints_.GetMinSize().Height(), constraints_.GetMaxSize().Height());
210 }
211 }
212
ConvertEdgeToPx(const Edge & edge,bool additional)213 EdgePx RenderBoxBase::ConvertEdgeToPx(const Edge& edge, bool additional)
214 {
215 EdgePx edgePx;
216 edgePx.SetLeft(Dimension(ConvertMarginToPx(edge.Left(), false, additional)));
217 edgePx.SetRight(Dimension(ConvertMarginToPx(edge.Right(), false, additional)));
218 edgePx.SetTop(Dimension(ConvertMarginToPx(edge.Top(), true, additional)));
219 edgePx.SetBottom(Dimension(ConvertMarginToPx(edge.Bottom(), true, additional)));
220 return edgePx;
221 }
222
CalculateAutoMargin()223 void RenderBoxBase::CalculateAutoMargin()
224 {
225 double freeSpace = 0.0;
226 FlexDirection flexDir = FlexDirection::ROW;
227 if (marginOrigin_.Left().Unit() == DimensionUnit::AUTO || marginOrigin_.Right().Unit() == DimensionUnit::AUTO
228 || marginOrigin_.Top().Unit() == DimensionUnit::AUTO
229 || marginOrigin_.Bottom().Unit() == DimensionUnit::AUTO) {
230 RefPtr<RenderFlex> flexFather;
231 auto parent = GetParent().Upgrade();
232 while (parent) {
233 flexFather = AceType::DynamicCast<RenderFlex>(parent);
234 if (flexFather) {
235 flexDir = flexFather->GetDirection();
236 break;
237 }
238 parent = parent->GetParent().Upgrade();
239 }
240 LayoutParam param = GetLayoutParam();
241 if (flexDir == FlexDirection::ROW) {
242 freeSpace = param.GetMaxSize().Height() - height_.Value();
243 if (marginOrigin_.Top().Unit() == DimensionUnit::AUTO
244 && marginOrigin_.Bottom().Unit() == DimensionUnit::AUTO) {
245 marginOrigin_.SetTop(Dimension(freeSpace / TWO_SIDES, DimensionUnit::PX));
246 marginOrigin_.SetBottom(Dimension(freeSpace / TWO_SIDES, DimensionUnit::PX));
247 } else if (marginOrigin_.Top().Unit() == DimensionUnit::AUTO) {
248 marginOrigin_.SetBottom(Dimension(ConvertMarginToPx(marginOrigin_.Bottom(), true, false)));
249 marginOrigin_.SetTop(Dimension((freeSpace - marginOrigin_.Bottom().Value()), DimensionUnit::PX));
250 } else if (marginOrigin_.Bottom().Unit() == DimensionUnit::AUTO) {
251 marginOrigin_.SetTop(Dimension(ConvertMarginToPx(marginOrigin_.Top(), true, false)));
252 marginOrigin_.SetBottom(Dimension((freeSpace - marginOrigin_.Top().Value()), DimensionUnit::PX));
253 }
254 } else if (flexDir == FlexDirection::COLUMN) {
255 freeSpace = param.GetMaxSize().Width() - width_.Value();
256 if (marginOrigin_.Left().Unit() == DimensionUnit::AUTO
257 && marginOrigin_.Right().Unit() == DimensionUnit::AUTO) {
258 marginOrigin_.SetLeft(Dimension(freeSpace / TWO_SIDES, DimensionUnit::PX));
259 marginOrigin_.SetRight(Dimension(freeSpace / TWO_SIDES, DimensionUnit::PX));
260 } else if (marginOrigin_.Left().Unit() == DimensionUnit::AUTO) {
261 marginOrigin_.SetRight(Dimension(ConvertMarginToPx(marginOrigin_.Right(), false, false)));
262 marginOrigin_.SetLeft(Dimension((freeSpace - marginOrigin_.Right().Value()), DimensionUnit::PX));
263 } else if (marginOrigin_.Right().Unit() == DimensionUnit::AUTO) {
264 marginOrigin_.SetLeft(Dimension(ConvertMarginToPx(marginOrigin_.Left(), false, false)));
265 marginOrigin_.SetRight(Dimension((freeSpace - marginOrigin_.Left().Value()), DimensionUnit::PX));
266 }
267 }
268 }
269 }
270
ConvertMarginPaddingToPx()271 void RenderBoxBase::ConvertMarginPaddingToPx()
272 {
273 padding_ = ConvertEdgeToPx(paddingOrigin_, false) + ConvertEdgeToPx(additionalPadding_, true);
274 CalculateAutoMargin();
275 margin_ = ConvertEdgeToPx(marginOrigin_, false);
276 }
277
ConvertConstraintsToPx()278 void RenderBoxBase::ConvertConstraintsToPx()
279 {
280 // constraints is set from two ways, one is from BoxComponent::SetConstraints, the other is from DomNode.
281 // BoxComponent::SetConstraints is higher priority than DOMNode.
282 if (GetLayoutParam().HasUsedConstraints() || constraints_.IsWidthValid() || constraints_.IsHeightValid()) {
283 return;
284 }
285 double minWidth = ConvertHorizontalDimensionToPx(minWidth_, true);
286 double minHeight = ConvertVerticalDimensionToPx(minHeight_, true);
287 double maxWidth = ConvertHorizontalDimensionToPx(maxWidth_, true);
288 double maxHeight = ConvertVerticalDimensionToPx(maxHeight_, true);
289 if (LessOrEqual(minWidth, 0.0) && LessOrEqual(minHeight, 0.0) && LessOrEqual(maxWidth, 0.0) &&
290 LessOrEqual(maxHeight, 0.0)) {
291 return;
292 }
293 if (GreatNotEqual(minWidth, 0.0) && NearZero(maxWidth)) {
294 maxWidth = Size::INFINITE_SIZE;
295 }
296 if (GreatNotEqual(minHeight, 0.0) && NearZero(maxHeight)) {
297 maxHeight = Size::INFINITE_SIZE;
298 }
299 if (LessNotEqual(maxWidth, minWidth)) {
300 maxWidth = minWidth;
301 }
302 if (LessNotEqual(maxHeight, minHeight)) {
303 maxHeight = minHeight;
304 }
305 if (GreatNotEqual(minWidth, 0.0) || GreatNotEqual(minHeight, 0.0)) {
306 deliverMinToChild_ = true;
307 }
308 Size minSize = Size(minWidth, minHeight);
309 Size maxSize = Size(maxWidth, maxHeight);
310 constraints_ = LayoutParam(maxSize, minSize);
311 }
312
CalculateGridLayoutSize()313 void RenderBoxBase::CalculateGridLayoutSize()
314 {
315 if (!gridColumnInfo_) {
316 return;
317 }
318
319 auto offset = gridColumnInfo_->GetOffset();
320 if (offset != UNDEFINED_DIMENSION) {
321 auto context = context_.Upgrade();
322 positionParam_.type =
323 (context && context->GetIsDeclarative()) ? PositionType::SEMI_RELATIVE : PositionType::ABSOLUTE;
324 std::pair<AnimatableDimension, bool>& edge =
325 (GetTextDirection() == TextDirection::RTL) ? positionParam_.right : positionParam_.left;
326 edge.first = offset;
327 edge.second = true;
328 }
329
330 double defaultWidth = gridColumnInfo_->GetWidth();
331 if (NearEqual(defaultWidth, 0.0)) {
332 return;
333 }
334 double maxWidth = gridColumnInfo_->GetMaxWidth();
335 if (!NearEqual(defaultWidth, maxWidth)) {
336 constraints_.SetMinWidth(defaultWidth);
337 constraints_.SetMaxWidth(maxWidth);
338 } else {
339 width_ = AnimatableDimension(gridColumnInfo_->GetWidth(), DimensionUnit::PX);
340 LayoutParam gridLayoutParam = GetLayoutParam();
341 gridLayoutParam.SetMaxSize(Size(width_.Value(), gridLayoutParam.GetMaxSize().Height()));
342 gridLayoutParam.SetMinSize(Size(width_.Value(), gridLayoutParam.GetMinSize().Height()));
343 SetLayoutParam(gridLayoutParam);
344 }
345 }
346
CalculateSelfLayoutParam()347 void RenderBoxBase::CalculateSelfLayoutParam()
348 {
349 // first. Calculate width and height with the parameter that user set in box component
350 ConvertConstraintsToPx();
351 CalculateWidth();
352 CalculateHeight();
353
354 if (gridContainerInfo_ && gridContainerInfo_->GetColumnType() == GridColumnType::NONE) {
355 marginOrigin_ = Edge(gridContainerInfo_->GetMarginLeft(), marginOrigin_.Top(),
356 gridContainerInfo_->GetMarginRight(), marginOrigin_.Bottom());
357 }
358 ConvertMarginPaddingToPx();
359 if (GreatNotEqual(aspectRatio_.Value(), 0.0)) {
360 AdjustSizeByAspectRatio();
361 }
362
363 Size selfMax = Size(selfMaxWidth_, selfMaxHeight_);
364 Size selfMin = Size(selfMinWidth_, selfMinHeight_);
365
366 // second. constrain parameter with LayoutParam
367 const LayoutParam& layoutSetByParent = GetLayoutParam();
368 Size constrainMax = selfMax;
369 Size constrainMin = selfMin;
370
371 if (minPlatformVersion_ != COMPATIBLE_VERSION || width_.Unit() != DimensionUnit::PERCENT) {
372 constrainMax.SetWidth(constrainMax.Width() + margin_.GetLayoutSize().Width());
373 constrainMin.SetWidth(constrainMin.Width() + margin_.GetLayoutSize().Width());
374 }
375 if (minPlatformVersion_ != COMPATIBLE_VERSION || height_.Unit() != DimensionUnit::PERCENT) {
376 constrainMax.SetHeight(constrainMax.Height() + margin_.GetLayoutSize().Height());
377 constrainMin.SetHeight(constrainMin.Height() + margin_.GetLayoutSize().Height());
378 }
379
380 selfMax = layoutSetByParent.Constrain(constrainMax);
381 selfMin = layoutSetByParent.Constrain(constrainMin);
382 auto context = context_.Upgrade();
383 // allow overflow parent when set height or width, except when set flexgrow or flexshrink
384 if (context->GetIsDeclarative()) {
385 if (selfDefineWidth_ && layoutSetByParent.GetMinSize().Width() != layoutSetByParent.GetMaxSize().Width()) {
386 selfMax.SetWidth(constrainMax.Width());
387 }
388 if (selfDefineHeight_ && layoutSetByParent.GetMinSize().Height() != layoutSetByParent.GetMaxSize().Height()) {
389 selfMax.SetHeight(constrainMax.Height());
390 }
391 }
392
393 selfLayoutParam_.SetMaxSize(selfMax);
394 selfLayoutParam_.SetMinSize(selfMin);
395
396 if (gridContainerInfo_) {
397 double width = selfMax.Width();
398 gridContainerInfo_->BuildColumnWidth(width);
399 }
400
401 ConvertPaddingForLayoutInBox();
402 }
403
AdjustSizeByAspectRatio()404 void RenderBoxBase::AdjustSizeByAspectRatio()
405 {
406 const LayoutParam& layoutSetByParent = GetLayoutParam();
407 LayoutParam selfLayout = layoutSetByParent;
408 if (!layoutSetByParent.HasUsedConstraints() && constraints_.IsWidthValid() && constraints_.IsHeightValid()) {
409 selfLayout = layoutSetByParent.Enforce(constraints_);
410 }
411 auto maxWidth = selfLayout.GetMaxSize().Width();
412 auto minWidth = selfLayout.GetMinSize().Width();
413 auto maxHeight = selfLayout.GetMaxSize().Height();
414 auto minHeight = selfLayout.GetMinSize().Height();
415 // Adjust by aspect ratio, firstly pick height based on width. It means that when width, height and aspectRatio are
416 // all set, the height is not used.
417 if (selfDefineWidth_) {
418 selfMaxHeight_ = selfMaxWidth_ / aspectRatio_.Value();
419 } else if (selfDefineHeight_) {
420 selfMaxWidth_ = selfMaxHeight_ * aspectRatio_.Value();
421 } else if (NearEqual(selfMaxWidth_, Size::INFINITE_SIZE)) {
422 selfMaxWidth_ = selfMaxHeight_ * aspectRatio_.Value();
423 } else {
424 selfMaxHeight_ = selfMaxWidth_ / aspectRatio_.Value();
425 }
426 if (selfMaxWidth_ > maxWidth) {
427 selfMaxWidth_ = maxWidth;
428 selfMaxHeight_ = selfMaxWidth_ / aspectRatio_.Value();
429 }
430 if (selfMaxHeight_ > maxHeight) {
431 selfMaxHeight_ = maxHeight;
432 selfMaxWidth_ = selfMaxHeight_ * aspectRatio_.Value();
433 }
434 if (selfMaxWidth_ < minWidth) {
435 selfMaxWidth_ = minWidth;
436 selfMaxHeight_ = selfMaxWidth_ / aspectRatio_.Value();
437 }
438 if (selfMaxHeight_ < minHeight) {
439 selfMaxHeight_ = minHeight;
440 selfMaxWidth_ = selfMaxHeight_ * aspectRatio_.Value();
441 }
442 if (!NearEqual(selfMaxWidth_, Size::INFINITE_SIZE) && !NearEqual(selfMaxHeight_, Size::INFINITE_SIZE)) {
443 selfDefineWidth_ = true;
444 selfDefineHeight_ = true;
445 }
446 }
447
SetChildLayoutParam()448 void RenderBoxBase::SetChildLayoutParam()
449 {
450 Size deflate;
451 if (boxSizing_ == BoxSizing::BORDER_BOX) {
452 deflate += padding_.GetLayoutSize();
453 deflate += GetBorderSize();
454 }
455 deflate += margin_.GetLayoutSize();
456
457 if (deliverMinToChild_) {
458 double minWidth = std::max(selfLayoutParam_.GetMinSize().Width() - deflate.Width(), 0.0);
459 double minHeight = std::max(selfLayoutParam_.GetMinSize().Height() - deflate.Height(), 0.0);
460 childLayoutParam_.SetMinSize(Size(minWidth, minHeight));
461 } else {
462 childLayoutParam_.SetMinSize(Size(0.0, 0.0));
463 }
464
465 double maxWidth = std::max(selfLayoutParam_.GetMaxSize().Width() - deflate.Width(), 0.0);
466 double maxHeight = std::max(selfLayoutParam_.GetMaxSize().Height() - deflate.Height(), 0.0);
467 childLayoutParam_.SetMaxSize(Size(maxWidth, maxHeight));
468
469 // First time layout all children
470 for (const auto& item : GetChildren()) {
471 item->Layout(childLayoutParam_);
472 }
473 }
474
ConvertPaddingForLayoutInBox()475 void RenderBoxBase::ConvertPaddingForLayoutInBox()
476 {
477 if (!layoutInBox_) {
478 return;
479 }
480
481 Size layoutParmMax = selfLayoutParam_.GetMaxSize();
482 Size borderSize = GetBorderSize();
483 double diameter = std::min(layoutParmMax.Width() - margin_.GetLayoutSize().Width() - borderSize.Width(),
484 layoutParmMax.Height() - margin_.GetLayoutSize().Height() - borderSize.Height());
485
486 double circlePadding = diameter * (1.0 - CIRCLE_LAYOUT_IN_BOX_SCALE) / BOX_DIAMETER_TO_RADIUS;
487
488 padding_.SetLeft(Dimension(std::max(padding_.LeftPx(), circlePadding)));
489 padding_.SetTop(Dimension(std::max(padding_.TopPx(), circlePadding)));
490 padding_.SetRight(Dimension(std::max(padding_.RightPx(), circlePadding)));
491 padding_.SetBottom(Dimension(std::max(padding_.BottomPx(), circlePadding)));
492 }
493
CalculateSelfLayoutSize()494 void RenderBoxBase::CalculateSelfLayoutSize()
495 {
496 Size borderSize = GetBorderSize();
497
498 const LayoutParam& layoutSetByParent = GetLayoutParam();
499 Size selfMax = selfLayoutParam_.GetMaxSize() - margin_.GetLayoutSize();
500 if (!GetChildren().empty()) {
501 childSize_ = GetChildren().front()->GetLayoutSize();
502 }
503 // calculate width
504 double width = 0.0;
505 double childWidth = childSize_.Width() + padding_.GetLayoutSize().Width() + borderSize.Width();
506 if (selfDefineWidth_) {
507 if (boxSizing_ == BoxSizing::BORDER_BOX) {
508 width = selfMax.Width();
509 } else {
510 width = selfMax.Width() + padding_.GetLayoutSize().Width() + borderSize.Width();
511 }
512 } else if (useFlexWidth_) {
513 if (layoutSetByParent.GetMaxSize().IsWidthInfinite() && viewPort_.Width() < childWidth) {
514 width = childWidth;
515 } else {
516 double flexWidth = layoutSetByParent.GetMaxSize().IsWidthInfinite() && !viewPort_.IsWidthInfinite()
517 ? viewPort_.Width()
518 : layoutSetByParent.GetMaxSize().Width();
519 width = flexWidth - margin_.GetLayoutSize().Width();
520 }
521 } else {
522 width = childWidth;
523 if (gridColumnInfo_) {
524 auto columnWidth = gridColumnInfo_->GetWidth();
525 if (NearEqual(columnWidth, gridColumnInfo_->GetMaxWidth()) && !NearEqual(columnWidth, 0.0)) {
526 width = columnWidth;
527 }
528 }
529 }
530 // calculate height
531 double height = 0.0;
532 double childHeight = childSize_.Height() + padding_.GetLayoutSize().Height() + borderSize.Height();
533 if (selfDefineHeight_) {
534 if (boxSizing_ == BoxSizing::BORDER_BOX) {
535 height = selfMax.Height();
536 } else {
537 height = selfMax.Height() + padding_.GetLayoutSize().Height() + borderSize.Height();
538 }
539 } else if (useFlexHeight_) {
540 if (layoutSetByParent.GetMaxSize().IsHeightInfinite() && viewPort_.Height() < childHeight) {
541 height = childHeight;
542 } else {
543 double flexHeight = layoutSetByParent.GetMaxSize().IsHeightInfinite() && !viewPort_.IsHeightInfinite()
544 ? viewPort_.Height()
545 : layoutSetByParent.GetMaxSize().Height();
546 height = flexHeight - margin_.GetLayoutSize().Height();
547 }
548 } else {
549 height = childSize_.Height() + padding_.GetLayoutSize().Height() + borderSize.Height();
550 }
551
552 const static int32_t PLATFORM_VERSION_SIX = 6;
553 auto context = GetContext().Upgrade();
554 if (context && context->GetMinPlatformVersion() >= PLATFORM_VERSION_SIX) {
555 double minWidth = padding_.GetLayoutSize().Width() + borderSize.Width();
556 double minHeight = padding_.GetLayoutSize().Height() + borderSize.Height();
557 width = width > minWidth ? width : minWidth;
558 height = height > minHeight ? height : minHeight;
559 }
560 // allow force layoutsize for parend
561 if (layoutSetByParent.GetMaxSize().Width() == layoutSetByParent.GetMinSize().Width()) {
562 width = layoutSetByParent.GetMinSize().Width();
563 }
564 if (layoutSetByParent.GetMaxSize().Height() == layoutSetByParent.GetMinSize().Height()) {
565 height = layoutSetByParent.GetMinSize().Height();
566 }
567 paintSize_ = Size(width, height);
568
569 if (context && context->GetIsDeclarative()) {
570 // box layout size = paint size + margin size
571 if (LessNotEqual(margin_.LeftPx(), 0.0)) {
572 positionParam_.left = std::make_pair(margin_.Left(), true);
573 margin_.SetLeft(Dimension(0.0, margin_.Left().Unit()));
574 }
575 if (LessNotEqual(margin_.TopPx(), 0.0)) {
576 positionParam_.top = std::make_pair(margin_.Top(), true);
577 margin_.SetTop(Dimension(0.0, margin_.Top().Unit()));
578 }
579 selfLayoutSize_ = paintSize_ + margin_.GetLayoutSize();
580 } else {
581 selfLayoutSize_ = GetLayoutParam().Constrain(paintSize_ + margin_.GetLayoutSize());
582 }
583
584 paintSize_ = selfLayoutSize_ - margin_.GetLayoutSize();
585 touchArea_.SetOffset(margin_.GetOffset());
586 touchArea_.SetSize(paintSize_);
587 SetLayoutSize(selfLayoutSize_);
588 isChildOverflow_ = childSize_.Width() > GetLayoutSize().Width() || childSize_.Height() > GetLayoutSize().Height();
589 }
590
CalculateChildPosition()591 void RenderBoxBase::CalculateChildPosition()
592 {
593 Offset borderOffset = GetBorderOffset();
594 Size parentSize = selfLayoutSize_ - margin_.GetLayoutSize() - padding_.GetLayoutSize();
595 parentSize -= GetBorderSize();
596
597 if (!GetChildren().empty()) {
598 const auto& child = GetChildren().front();
599 childPosition_ = margin_.GetOffset() + borderOffset + padding_.GetOffset() +
600 Alignment::GetAlignPosition(parentSize, child->GetLayoutSize(), align_);
601 child->SetPosition(childPosition_);
602 }
603 }
604
PerformLayout()605 void RenderBoxBase::PerformLayout()
606 {
607 // update scale for margin, padding
608 auto context = context_.Upgrade();
609 if (!context) {
610 LOGE("[BOX][Dep:%{public}d][LAYOUT]Call Context Upgrade failed. PerformLayout failed.", this->GetDepth());
611 return;
612 }
613 if (isUseAlign_) {
614 context->AddAlignDeclarationNode(AceType::Claim(this));
615 }
616
617 CalculateGridLayoutSize();
618 // first. calculate self layout param
619 CalculateSelfLayoutParam();
620 // second. set layout param of child to calculate layout size
621 SetChildLayoutParam();
622 // third. using layout size of child, calculate layout size of box
623 CalculateSelfLayoutSize();
624 // forth. calculate position of child
625 CalculateChildPosition();
626
627 if (isUseAlign_) {
628 CalculateAlignDeclaration();
629 }
630
631 if (layoutCallback_) {
632 layoutCallback_();
633 }
634 }
635
PerformLayoutInLiteMode()636 void RenderBoxBase::PerformLayoutInLiteMode()
637 {
638 // Lite must has width and height
639 CalculateWidth();
640 CalculateHeight();
641 ConvertMarginPaddingToPx();
642 if (NearEqual(selfMaxWidth_, Size::INFINITE_SIZE) && !selfDefineWidth_) {
643 selfMaxWidth_ = GetLayoutParam().GetMaxSize().Width();
644 }
645 if (NearEqual(selfMaxHeight_, Size::INFINITE_SIZE) && !selfDefineHeight_) {
646 selfMaxHeight_ = GetLayoutParam().GetMaxSize().Height();
647 }
648 Size selfMax = Size(selfMaxWidth_, selfMaxHeight_) + margin_.GetLayoutSize();
649 Size selfMin = Size(selfMinWidth_, selfMinHeight_) + margin_.GetLayoutSize();
650 // Do not constrain param in lite mode
651 selfLayoutParam_.SetMaxSize(selfMax);
652 selfLayoutParam_.SetMinSize(selfMin);
653 SetChildLayoutParam();
654 double width = 0.0;
655 double height = 0.0;
656 Size borderSize = GetBorderSize();
657 childSize_ = GetChildren().front()->GetLayoutSize();
658 if (selfDefineWidth_) {
659 width = selfMax.Width() - margin_.GetLayoutSize().Width();
660 } else {
661 width = childSize_.Width() + padding_.GetLayoutSize().Width() + borderSize.Width();
662 }
663 if (selfDefineHeight_) {
664 height = selfMax.Height() - margin_.GetLayoutSize().Height();
665 } else {
666 height = childSize_.Height() + padding_.GetLayoutSize().Height() + borderSize.Height();
667 }
668 double targetHeight = padding_.GetLayoutSize().Height() + borderSize.Height();
669 targetHeight = targetHeight > height ? targetHeight : height;
670 double targetWidth = padding_.GetLayoutSize().Width() + borderSize.Width();
671 targetHeight = targetWidth > width ? targetWidth : width;
672 // Determine self size, not use constrain.
673 paintSize_ = Size(targetWidth, targetHeight);
674 selfLayoutSize_ = paintSize_ + margin_.GetLayoutSize();
675 touchArea_.SetOffset(margin_.GetOffset());
676 touchArea_.SetSize(paintSize_);
677 SetLayoutSize(selfLayoutSize_);
678 CalculateChildPosition();
679 }
680
Update(const RefPtr<Component> & component)681 void RenderBoxBase::Update(const RefPtr<Component>& component)
682 {
683 const RefPtr<BoxBaseComponent> box = AceType::DynamicCast<BoxBaseComponent>(component);
684 if (box) {
685 scrollPage_ = box->GetScrollPage();
686
687 paddingOrigin_ = box->GetPadding();
688 marginOrigin_ = box->GetMargin();
689 additionalPadding_ = box->GetAdditionalPadding();
690 flex_ = box->GetFlex();
691 boxSizing_ = box->GetBoxSizing();
692 constraints_ = box->GetConstraints();
693 align_ = box->GetAlignment();
694 overflow_ = box->GetOverflow();
695 clipPath_ = box->GetClipPath();
696 deliverMinToChild_ = box->GetDeliverMinToChild();
697 width_ = box->GetWidthDimension();
698 height_ = box->GetHeightDimension();
699 auto context = context_.Upgrade();
700 if (context && scrollPage_) {
701 height_ = AnimatableDimension(context->GetStageRect().Height(), DimensionUnit::PX);
702 }
703 percentFlag_ = box->GetPercentFlag();
704 layoutInBox_ = box->GetLayoutInBoxFlag();
705 aspectRatio_ = box->GetAspectRatio();
706 minWidth_ = box->GetMinWidth();
707 minHeight_ = box->GetMinHeight();
708 maxWidth_ = box->GetMaxWidth();
709 maxHeight_ = box->GetMaxHeight();
710 useLiteStyle_ = box->UseLiteStyle();
711 mask_ = box->GetMask();
712 auto gridLayoutInfo = box->GetGridLayoutInfo();
713 auto gridColumnInfo = AceType::DynamicCast<GridColumnInfo>(gridLayoutInfo);
714 if (gridColumnInfo) {
715 gridColumnInfo_ = gridColumnInfo;
716 } else {
717 auto gridContainerInfo = AceType::DynamicCast<GridContainerInfo>(gridLayoutInfo);
718 if (gridContainerInfo) {
719 gridContainerInfo_ = gridContainerInfo;
720 }
721 }
722 isUseAlign_ = box->IsUseAlign();
723 if (isUseAlign_) {
724 alignPtr_ = box->GetAlignDeclarationPtr();
725 alignSide_ = box->GetUseAlignSide();
726 alignItemOffset_ = box->GetUseAlignOffset();
727 }
728 boxClipFlag_ = box->GetBoxClipFlag();
729 pixelMap_ = box->GetPixelMap();
730
731 MarkNeedLayout();
732 }
733 }
734
GetPaintPosition() const735 Offset RenderBoxBase::GetPaintPosition() const
736 {
737 return margin_.GetOffset();
738 }
739
GetPaintSize() const740 const Size& RenderBoxBase::GetPaintSize() const
741 {
742 return paintSize_;
743 }
744
SetPaintSize(const Size & paintSize)745 void RenderBoxBase::SetPaintSize(const Size& paintSize)
746 {
747 paintSize_ = paintSize;
748 }
749
Dump()750 void RenderBoxBase::Dump()
751 {
752 double dipScale = 1.0;
753 auto context = context_.Upgrade();
754 if (context) {
755 dipScale = context->GetDipScale();
756 }
757 Size borderSize = GetBorderSize();
758 Radius radius = GetBorderRadius();
759 DumpLog::GetInstance().AddDesc("BoxFlex: " + std::to_string(static_cast<int32_t>(flex_)) +
760 ", BGcolor: " + std::to_string(GetColor().GetValue()) + ", BoxSizing: " +
761 std::to_string(static_cast<int32_t>(boxSizing_)) + ", Align: " + align_.ToString());
762 DumpLog::GetInstance().AddDesc(std::string("WH: ")
763 .append(Size(width_.Value(), height_.Value()).ToString())
764 .append(", Margin: ")
765 .append(margin_.GetLayoutSizeInPx(dipScale).ToString())
766 .append(", Padding: ")
767 .append(padding_.GetLayoutSizeInPx(dipScale).ToString())
768 .append(", Border: ")
769 .append(borderSize.ToString())
770 .append(", Radius: ")
771 .append(radius.GetX().ToString()));
772 DumpLog::GetInstance().AddDesc(std::string("Constraints: ")
773 .append(constraints_.ToString())
774 .append(", ChildLayout: ")
775 .append(childLayoutParam_.ToString()));
776 DumpLog::GetInstance().AddDesc(std::string("PaintSize: ")
777 .append(paintSize_.ToString())
778 .append(", TouchArea: ")
779 .append(touchArea_.ToString()));
780 DumpLog::GetInstance().AddDesc(std::string("SelfSize: ")
781 .append(selfLayoutSize_.ToString())
782 .append(", ChildSize: ")
783 .append(childSize_.ToString())
784 .append(", ChildPos: ")
785 .append(childPosition_.ToString()));
786 DumpLog::GetInstance().AddDesc(std::string("alignOffset: ")
787 .append(alignOffset_.ToString()));
788 if (gridColumnInfo_) {
789 DumpLog::GetInstance().AddDesc(std::string("GridColumnInfo"));
790 }
791 if (gridContainerInfo_) {
792 DumpLog::GetInstance().AddDesc(std::string("GridContainerInfo"));
793 }
794 }
795
ClearRenderObject()796 void RenderBoxBase::ClearRenderObject()
797 {
798 RenderNode::ClearRenderObject();
799 width_ = Dimension(-1.0);
800 height_ = Dimension(-1.0);
801 flex_ = BoxFlex::FLEX_NO;
802
803 constraints_ = LayoutParam(Size(), Size());
804 padding_ = EdgePx();
805 margin_ = EdgePx();
806 align_ = Alignment();
807 paintSize_ = Size();
808 touchArea_ = Rect();
809
810 deliverMinToChild_ = true;
811 scrollPage_ = false;
812 percentFlag_ = 0;
813 layoutInBox_ = false;
814
815 paddingOrigin_ = Edge();
816 marginOrigin_ = Edge();
817 additionalPadding_ = Edge();
818
819 useFlexWidth_ = false;
820 useFlexHeight_ = false;
821 selfDefineWidth_ = false;
822 selfDefineHeight_ = false;
823 selfMaxWidth_ = Size::INFINITE_SIZE;
824 selfMinWidth_ = 0.0;
825 selfMaxHeight_ = Size::INFINITE_SIZE;
826 selfMinHeight_ = 0.0;
827
828 aspectRatio_ = AnimatableDimension();
829 minWidth_ = Dimension();
830 minHeight_ = Dimension();
831 maxWidth_ = Dimension();
832 maxHeight_ = Dimension();
833
834 selfLayoutParam_ = LayoutParam();
835 selfLayoutSize_ = Size();
836 childLayoutParam_ = LayoutParam();
837 childSize_ = Size();
838 childPosition_ = Offset();
839
840 layoutCallback_ = nullptr;
841 gridColumnInfo_ = nullptr;
842 gridContainerInfo_ = nullptr;
843
844 isUseAlign_ = false;
845 alignPtr_ = nullptr;
846 alignSide_ = AlignDeclaration::Edge::AUTO;
847 alignItemOffset_ = Dimension();
848 alignOffset_.Reset();
849 }
850
GetFloatPropertySetterMap()851 FloatPropertyAnimatable::SetterMap RenderBoxBase::GetFloatPropertySetterMap()
852 {
853 FloatPropertyAnimatable::SetterMap map;
854 auto weak = AceType::WeakClaim(this);
855 map[PropertyAnimatableType::PROPERTY_WIDTH] = [weak](float value) {
856 auto box = weak.Upgrade();
857 if (!box) {
858 LOGE("Set width failed. box is null.");
859 return;
860 }
861 box->SetWidth(value);
862 };
863 const RefPtr<RenderTextField> renderTextField = AceType::DynamicCast<RenderTextField>(GetFirstChild());
864 if (renderTextField) {
865 WeakPtr<RenderTextField> textWeak = renderTextField;
866 map[PropertyAnimatableType::PROPERTY_HEIGHT] = [textWeak](float value) {
867 auto renderTextField = textWeak.Upgrade();
868 if (!renderTextField) {
869 LOGE("Set height failed. text is null.");
870 return;
871 }
872 return renderTextField->SetHeight(value);
873 };
874 } else {
875 map[PropertyAnimatableType::PROPERTY_HEIGHT] = [weak](float value) {
876 auto box = weak.Upgrade();
877 if (!box) {
878 LOGE("Set height failed. box is null.");
879 return;
880 }
881 box->SetHeight(value);
882 };
883 }
884 return map;
885 };
886
GetFloatPropertyGetterMap()887 FloatPropertyAnimatable::GetterMap RenderBoxBase::GetFloatPropertyGetterMap()
888 {
889 FloatPropertyAnimatable::GetterMap map;
890 auto weak = AceType::WeakClaim(this);
891 map[PropertyAnimatableType::PROPERTY_WIDTH] = [weak]() -> float {
892 auto box = weak.Upgrade();
893 if (!box) {
894 LOGE("Get width failed. box is null.");
895 return 0.0;
896 }
897 return box->GetWidth();
898 };
899 const RefPtr<RenderTextField> renderTextField = AceType::DynamicCast<RenderTextField>(GetFirstChild());
900 if (renderTextField) {
901 WeakPtr<RenderTextField> textWeak = renderTextField;
902 map[PropertyAnimatableType::PROPERTY_HEIGHT] = [textWeak]() -> float {
903 auto renderTextField = textWeak.Upgrade();
904 if (!renderTextField) {
905 LOGE("Get height failed. text is null.");
906 return 0.0;
907 }
908 return renderTextField->GetHeight();
909 };
910 } else {
911 map[PropertyAnimatableType::PROPERTY_HEIGHT] = [weak]() -> float {
912 auto box = weak.Upgrade();
913 if (!box) {
914 LOGE("Get height failed. box is null.");
915 return 0.0;
916 }
917 return box->GetHeight();
918 };
919 }
920 return map;
921 }
922
CalculateAlignDeclaration()923 void RenderBoxBase::CalculateAlignDeclaration()
924 {
925 alignOffset_.Reset();
926 if (!GetAlignDeclarationOffset(alignPtr_, alignOffset_)) {
927 alignOffset_.Reset();
928 return;
929 }
930
931 double itemAlignOffset = 0.0;
932 auto context = GetContext().Upgrade();
933 if (context) {
934 itemAlignOffset = context->NormalizeToPx(alignItemOffset_);
935 }
936
937 switch (alignSide_) {
938 case AlignDeclaration::Edge::TOP:
939 alignOffset_ = alignOffset_ + Offset(0, itemAlignOffset);
940 break;
941 case AlignDeclaration::Edge::CENTER:
942 alignOffset_ = alignOffset_ - Offset(0, GetLayoutSize().Height() / 2 - itemAlignOffset);
943 break;
944 case AlignDeclaration::Edge::BOTTOM:
945 alignOffset_ = alignOffset_ - Offset(0, GetLayoutSize().Height() - itemAlignOffset);
946 break;
947 case AlignDeclaration::Edge::BASELINE:
948 alignOffset_ = alignOffset_ - Offset(0, GetBaselineDistance(TextBaseline::ALPHABETIC) - itemAlignOffset);
949 break;
950 case AlignDeclaration::Edge::START:
951 alignOffset_ = alignOffset_ + Offset(itemAlignOffset, 0);
952 break;
953 case AlignDeclaration::Edge::MIDDLE:
954 alignOffset_ = alignOffset_ - Offset(GetLayoutSize().Width() / 2 - itemAlignOffset, 0);
955 break;
956 case AlignDeclaration::Edge::END:
957 alignOffset_ = alignOffset_ - Offset(GetLayoutSize().Width() - itemAlignOffset, 0);
958 break;
959 default:
960 alignOffset_.Reset();
961 break;
962 }
963 }
964
965 } // namespace OHOS::Ace
966