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
SetAutoMargin(FlexDirection flexDir,double freeSpace,bool isFirst)223 Edge RenderBoxBase::SetAutoMargin(FlexDirection flexDir, double freeSpace, bool isFirst)
224 {
225 Edge margin;
226 if (isFirst) {
227 margin = marginOrigin_;
228 } else {
229 margin = marginBackup_;
230 }
231
232 if (flexDir == FlexDirection::COLUMN) {
233 if (margin.Left().Unit() == DimensionUnit::AUTO && margin.Right().Unit() == DimensionUnit::AUTO) {
234 marginOrigin_.SetLeft(Dimension(freeSpace / TWO_SIDES, DimensionUnit::PX));
235 marginOrigin_.SetRight(Dimension(freeSpace / TWO_SIDES, DimensionUnit::PX));
236 } else if (margin.Left().Unit() == DimensionUnit::AUTO) {
237 marginOrigin_.SetRight(Dimension(ConvertMarginToPx(margin.Right(), false, false)));
238 marginOrigin_.SetLeft(Dimension((freeSpace - marginOrigin_.Right().Value()), DimensionUnit::PX));
239 } else if (margin.Right().Unit() == DimensionUnit::AUTO) {
240 marginOrigin_.SetLeft(Dimension(ConvertMarginToPx(margin.Left(), false, false)));
241 marginOrigin_.SetRight(Dimension((freeSpace - marginOrigin_.Left().Value()), DimensionUnit::PX));
242 }
243 } else {
244 if (margin.Top().Unit() == DimensionUnit::AUTO && margin.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 (margin.Top().Unit() == DimensionUnit::AUTO) {
248 marginOrigin_.SetBottom(Dimension(ConvertMarginToPx(margin.Bottom(), true, false)));
249 marginOrigin_.SetTop(Dimension((freeSpace - marginOrigin_.Bottom().Value()), DimensionUnit::PX));
250 } else if (margin.Bottom().Unit() == DimensionUnit::AUTO) {
251 marginOrigin_.SetTop(Dimension(ConvertMarginToPx(margin.Top(), true, false)));
252 marginOrigin_.SetBottom(Dimension((freeSpace - marginOrigin_.Top().Value()), DimensionUnit::PX));
253 }
254 }
255 return marginOrigin_;
256 }
257
CalculateAutoMargin()258 void RenderBoxBase::CalculateAutoMargin()
259 {
260 double freeSpace = 0.0, width = 0.0, height = 0.0;
261 FlexDirection flexDir = FlexDirection::ROW;
262 if (marginOrigin_.Left().Unit() == DimensionUnit::AUTO || marginOrigin_.Right().Unit() == DimensionUnit::AUTO ||
263 marginOrigin_.Top().Unit() == DimensionUnit::AUTO || marginOrigin_.Bottom().Unit() == DimensionUnit::AUTO ||
264 marginBackup_.Left().Unit() == DimensionUnit::AUTO || marginBackup_.Right().Unit() == DimensionUnit::AUTO ||
265 marginBackup_.Top().Unit() == DimensionUnit::AUTO || marginBackup_.Bottom().Unit() == DimensionUnit::AUTO) {
266 auto parent = GetParent().Upgrade();
267 while (parent) {
268 RefPtr<RenderFlex> flexFather = AceType::DynamicCast<RenderFlex>(parent);
269 if (flexFather) {
270 flexDir = flexFather->GetDirection();
271 break;
272 }
273 parent = parent->GetParent().Upgrade();
274 }
275 LayoutParam param = GetLayoutParam();
276 if ((flexDir == FlexDirection::COLUMN ||
277 (flexDir == FlexDirection::ROW && displayType_ == DisplayType::BLOCK)) &&
278 width_.Value() == -1.0) {
279 if (childWidth_ != 0.0) {
280 width = childWidth_;
281 freeSpace = param.GetMaxSize().Width() - width;
282 SetAutoMargin(FlexDirection::COLUMN, freeSpace, false);
283 } else {
284 marginBackup_ = marginOrigin_;
285 marginOrigin_.SetLeft(Dimension(0.0, DimensionUnit::PX));
286 marginOrigin_.SetRight(Dimension(0.0, DimensionUnit::PX));
287 needReCalc_ = true;
288 }
289 } else if ((flexDir == FlexDirection::COLUMN ||
290 (flexDir == FlexDirection::ROW && displayType_ == DisplayType::BLOCK)) &&
291 width_.Value() != -1.0) {
292 width = width_.Value();
293 freeSpace = param.GetMaxSize().Width() - width;
294 SetAutoMargin(FlexDirection::COLUMN, freeSpace, true);
295 } else if (flexDir == FlexDirection::ROW && height_.Value() == -1.0) {
296 if (childHeight_ != 0.0) {
297 height = childHeight_;
298 freeSpace = param.GetMaxSize().Height() - height;
299 SetAutoMargin(flexDir, freeSpace, false);
300 } else {
301 marginBackup_ = marginOrigin_;
302 marginOrigin_.SetTop(Dimension(0.0, DimensionUnit::PX));
303 marginOrigin_.SetBottom(Dimension(0.0, DimensionUnit::PX));
304 needReCalc_ = true;
305 }
306 } else if (flexDir == FlexDirection::ROW && height_.Value() != -1.0) {
307 height = height_.Value();
308 freeSpace = param.GetMaxSize().Height() - height;
309 SetAutoMargin(flexDir, freeSpace, true);
310 }
311 }
312 }
313
ConvertMarginPaddingToPx()314 void RenderBoxBase::ConvertMarginPaddingToPx()
315 {
316 padding_ = ConvertEdgeToPx(paddingOrigin_, false) + ConvertEdgeToPx(additionalPadding_, true);
317 CalculateAutoMargin();
318 margin_ = ConvertEdgeToPx(marginOrigin_, false);
319 }
320
ConvertConstraintsToPx()321 void RenderBoxBase::ConvertConstraintsToPx()
322 {
323 // constraints is set from two ways, one is from BoxComponent::SetConstraints, the other is from DomNode.
324 // BoxComponent::SetConstraints is higher priority than DOMNode.
325 if (GetLayoutParam().HasUsedConstraints() || constraints_.IsWidthValid() || constraints_.IsHeightValid()) {
326 return;
327 }
328 double minWidth = ConvertHorizontalDimensionToPx(minWidth_, true);
329 double minHeight = ConvertVerticalDimensionToPx(minHeight_, true);
330 double maxWidth = ConvertHorizontalDimensionToPx(maxWidth_, true);
331 double maxHeight = ConvertVerticalDimensionToPx(maxHeight_, true);
332 if (LessOrEqual(minWidth, 0.0) && LessOrEqual(minHeight, 0.0) && LessOrEqual(maxWidth, 0.0) &&
333 LessOrEqual(maxHeight, 0.0)) {
334 return;
335 }
336 if (GreatNotEqual(minWidth, 0.0) && NearZero(maxWidth)) {
337 maxWidth = Size::INFINITE_SIZE;
338 }
339 if (GreatNotEqual(minHeight, 0.0) && NearZero(maxHeight)) {
340 maxHeight = Size::INFINITE_SIZE;
341 }
342 if (LessNotEqual(maxWidth, minWidth)) {
343 maxWidth = minWidth;
344 }
345 if (LessNotEqual(maxHeight, minHeight)) {
346 maxHeight = minHeight;
347 }
348 if (GreatNotEqual(minWidth, 0.0) || GreatNotEqual(minHeight, 0.0)) {
349 deliverMinToChild_ = true;
350 }
351 Size minSize = Size(minWidth, minHeight);
352 Size maxSize = Size(maxWidth, maxHeight);
353 constraints_ = LayoutParam(maxSize, minSize);
354 }
355
CalculateGridLayoutSize()356 void RenderBoxBase::CalculateGridLayoutSize()
357 {
358 if (!gridColumnInfo_) {
359 return;
360 }
361
362 auto offset = gridColumnInfo_->GetOffset();
363 if (offset != UNDEFINED_DIMENSION) {
364 if (IsHeadRenderNode()) {
365 auto context = context_.Upgrade();
366 positionParam_.type =
367 (context && context->GetIsDeclarative()) ? PositionType::PTSEMI_RELATIVE : PositionType::PTABSOLUTE;
368 std::pair<AnimatableDimension, bool>& edge =
369 (GetTextDirection() == TextDirection::RTL) ? positionParam_.right : positionParam_.left;
370 edge.first = offset;
371 edge.second = true;
372 } else {
373 auto headRenderNode = GetHeadRenderNode();
374 if (headRenderNode) {
375 auto context = headRenderNode->GetContext().Upgrade();
376 headRenderNode->SetPositionType(
377 (context && context->GetIsDeclarative()) ? PositionType::PTSEMI_RELATIVE :
378 PositionType::PTABSOLUTE);
379 headRenderNode->GetTextDirection() == TextDirection::RTL ? headRenderNode->SetRight(offset)
380 : headRenderNode->SetLeft(offset);
381 }
382 }
383 // the above two cases will only work on rosen, so using below to support on preview.
384 #ifndef ENABLE_ROSEN_BACKEND
385 auto context = context_.Upgrade();
386 positionParam_.type =
387 (context && context->GetIsDeclarative()) ? PositionType::PTSEMI_RELATIVE : PositionType::PTABSOLUTE;
388 std::pair<AnimatableDimension, bool>& edge =
389 (GetTextDirection() == TextDirection::RTL) ? positionParam_.right : positionParam_.left;
390 edge.first = offset;
391 edge.second = true;
392 #endif
393 }
394
395 double defaultWidth = gridColumnInfo_->GetWidth();
396 if (NearEqual(defaultWidth, 0.0)) {
397 return;
398 }
399 double maxWidth = gridColumnInfo_->GetMaxWidth();
400 if (!NearEqual(defaultWidth, maxWidth)) {
401 constraints_.SetMinWidth(defaultWidth);
402 constraints_.SetMaxWidth(maxWidth);
403 } else {
404 width_ = AnimatableDimension(gridColumnInfo_->GetWidth(), DimensionUnit::PX);
405 LayoutParam gridLayoutParam = GetLayoutParam();
406 gridLayoutParam.SetMaxSize(Size(width_.Value(), gridLayoutParam.GetMaxSize().Height()));
407 gridLayoutParam.SetMinSize(Size(width_.Value(), gridLayoutParam.GetMinSize().Height()));
408 SetLayoutParam(gridLayoutParam);
409 }
410 }
411
CalculateSelfLayoutParam()412 void RenderBoxBase::CalculateSelfLayoutParam()
413 {
414 // first. Calculate width and height with the parameter that user set in box component
415 ConvertConstraintsToPx();
416 CalculateWidth();
417 CalculateHeight();
418
419 if (gridContainerInfo_ && gridContainerInfo_->GetColumnType() == GridColumnType::NONE) {
420 marginOrigin_ = Edge(gridContainerInfo_->GetMarginLeft(), marginOrigin_.Top(),
421 gridContainerInfo_->GetMarginRight(), marginOrigin_.Bottom());
422 }
423 ConvertMarginPaddingToPx();
424 if (GreatNotEqual(aspectRatio_.Value(), 0.0)) {
425 AdjustSizeByAspectRatio();
426 }
427
428 Size selfMax = Size(selfMaxWidth_, selfMaxHeight_);
429 Size selfMin = Size(selfMinWidth_, selfMinHeight_);
430
431 // second. constrain parameter with LayoutParam
432 const LayoutParam& layoutSetByParent = GetLayoutParam();
433 Size constrainMax = selfMax;
434 Size constrainMin = selfMin;
435
436 if (minPlatformVersion_ != COMPATIBLE_VERSION || width_.Unit() != DimensionUnit::PERCENT) {
437 constrainMax.SetWidth(constrainMax.Width() + margin_.GetLayoutSize().Width());
438 constrainMin.SetWidth(constrainMin.Width() + margin_.GetLayoutSize().Width());
439 }
440 if (minPlatformVersion_ != COMPATIBLE_VERSION || height_.Unit() != DimensionUnit::PERCENT) {
441 constrainMax.SetHeight(constrainMax.Height() + margin_.GetLayoutSize().Height());
442 constrainMin.SetHeight(constrainMin.Height() + margin_.GetLayoutSize().Height());
443 }
444
445 selfMax = layoutSetByParent.Constrain(constrainMax);
446 selfMin = layoutSetByParent.Constrain(constrainMin);
447 auto context = context_.Upgrade();
448 // allow overflow parent when set height or width, except when set flexgrow or flexshrink
449 if (context->GetIsDeclarative()) {
450 if (selfDefineWidth_ && layoutSetByParent.GetMinSize().Width() != layoutSetByParent.GetMaxSize().Width()) {
451 selfMax.SetWidth(constrainMax.Width());
452 }
453 if (selfDefineHeight_ && layoutSetByParent.GetMinSize().Height() != layoutSetByParent.GetMaxSize().Height()) {
454 selfMax.SetHeight(constrainMax.Height());
455 }
456 }
457
458 selfLayoutParam_.SetMaxSize(selfMax);
459 selfLayoutParam_.SetMinSize(selfMin);
460
461 if (gridContainerInfo_) {
462 double width = selfMax.Width();
463 gridContainerInfo_->BuildColumnWidth(width);
464 }
465
466 ConvertPaddingForLayoutInBox();
467 }
468
AdjustSizeByAspectRatio()469 void RenderBoxBase::AdjustSizeByAspectRatio()
470 {
471 const LayoutParam& layoutSetByParent = GetLayoutParam();
472 LayoutParam selfLayout = layoutSetByParent;
473 if (!layoutSetByParent.HasUsedConstraints() && constraints_.IsWidthValid() && constraints_.IsHeightValid()) {
474 selfLayout = layoutSetByParent.Enforce(constraints_);
475 }
476 auto maxWidth = selfLayout.GetMaxSize().Width();
477 auto minWidth = selfLayout.GetMinSize().Width();
478 auto maxHeight = selfLayout.GetMaxSize().Height();
479 auto minHeight = selfLayout.GetMinSize().Height();
480 // Adjust by aspect ratio, firstly pick height based on width. It means that when width, height and aspectRatio are
481 // all set, the height is not used.
482 if (selfDefineWidth_) {
483 selfMaxHeight_ = selfMaxWidth_ / aspectRatio_.Value();
484 } else if (selfDefineHeight_) {
485 selfMaxWidth_ = selfMaxHeight_ * aspectRatio_.Value();
486 } else if (NearEqual(selfMaxWidth_, Size::INFINITE_SIZE)) {
487 selfMaxWidth_ = selfMaxHeight_ * aspectRatio_.Value();
488 } else {
489 selfMaxHeight_ = selfMaxWidth_ / aspectRatio_.Value();
490 }
491 if (selfMaxWidth_ > maxWidth) {
492 selfMaxWidth_ = maxWidth;
493 selfMaxHeight_ = selfMaxWidth_ / aspectRatio_.Value();
494 }
495 if (selfMaxHeight_ > maxHeight) {
496 selfMaxHeight_ = maxHeight;
497 selfMaxWidth_ = selfMaxHeight_ * aspectRatio_.Value();
498 }
499 if (selfMaxWidth_ < minWidth) {
500 selfMaxWidth_ = minWidth;
501 selfMaxHeight_ = selfMaxWidth_ / aspectRatio_.Value();
502 }
503 if (selfMaxHeight_ < minHeight) {
504 selfMaxHeight_ = minHeight;
505 selfMaxWidth_ = selfMaxHeight_ * aspectRatio_.Value();
506 }
507 if (!NearEqual(selfMaxWidth_, Size::INFINITE_SIZE) && !NearEqual(selfMaxHeight_, Size::INFINITE_SIZE)) {
508 selfDefineWidth_ = true;
509 selfDefineHeight_ = true;
510 }
511 }
512
SetChildLayoutParam()513 void RenderBoxBase::SetChildLayoutParam()
514 {
515 Size deflate;
516 if (boxSizing_ == BoxSizing::BORDER_BOX) {
517 deflate += padding_.GetLayoutSize();
518 deflate += GetBorderSize();
519 }
520 deflate += margin_.GetLayoutSize();
521
522 if (deliverMinToChild_) {
523 double minWidth = std::max(selfLayoutParam_.GetMinSize().Width() - deflate.Width(), 0.0);
524 double minHeight = std::max(selfLayoutParam_.GetMinSize().Height() - deflate.Height(), 0.0);
525 childLayoutParam_.SetMinSize(Size(minWidth, minHeight));
526 } else {
527 childLayoutParam_.SetMinSize(Size(0.0, 0.0));
528 }
529
530 double maxWidth = std::max(selfLayoutParam_.GetMaxSize().Width() - deflate.Width(), 0.0);
531 double maxHeight = std::max(selfLayoutParam_.GetMaxSize().Height() - deflate.Height(), 0.0);
532 childLayoutParam_.SetMaxSize(Size(maxWidth, maxHeight));
533
534 // First time layout all children
535 for (const auto& item : GetChildren()) {
536 item->Layout(childLayoutParam_);
537 }
538 }
539
ConvertPaddingForLayoutInBox()540 void RenderBoxBase::ConvertPaddingForLayoutInBox()
541 {
542 if (!layoutInBox_) {
543 return;
544 }
545
546 Size layoutParmMax = selfLayoutParam_.GetMaxSize();
547 Size borderSize = GetBorderSize();
548 double diameter = std::min(layoutParmMax.Width() - margin_.GetLayoutSize().Width() - borderSize.Width(),
549 layoutParmMax.Height() - margin_.GetLayoutSize().Height() - borderSize.Height());
550
551 double circlePadding = diameter * (1.0 - CIRCLE_LAYOUT_IN_BOX_SCALE) / BOX_DIAMETER_TO_RADIUS;
552
553 padding_.SetLeft(Dimension(std::max(padding_.LeftPx(), circlePadding)));
554 padding_.SetTop(Dimension(std::max(padding_.TopPx(), circlePadding)));
555 padding_.SetRight(Dimension(std::max(padding_.RightPx(), circlePadding)));
556 padding_.SetBottom(Dimension(std::max(padding_.BottomPx(), circlePadding)));
557 }
558
CalculateSelfLayoutSize()559 void RenderBoxBase::CalculateSelfLayoutSize()
560 {
561 Size borderSize = GetBorderSize();
562
563 const LayoutParam& layoutSetByParent = GetLayoutParam();
564 Size selfMax = selfLayoutParam_.GetMaxSize() - margin_.GetLayoutSize();
565 if (!GetChildren().empty()) {
566 childSize_ = GetChildren().front()->GetLayoutSize();
567 childWidth_ = childSize_.Width();
568 childHeight_ = childSize_.Height();
569 }
570 // calculate width
571 double width = 0.0;
572 double childWidth = childSize_.Width() + padding_.GetLayoutSize().Width() + borderSize.Width();
573 if (selfDefineWidth_) {
574 if (boxSizing_ == BoxSizing::BORDER_BOX) {
575 width = selfMax.Width();
576 } else {
577 width = selfMax.Width() + padding_.GetLayoutSize().Width() + borderSize.Width();
578 }
579 } else if (useFlexWidth_) {
580 if (layoutSetByParent.GetMaxSize().IsWidthInfinite() && viewPort_.Width() < childWidth) {
581 width = childWidth;
582 } else {
583 double flexWidth = layoutSetByParent.GetMaxSize().IsWidthInfinite() && !viewPort_.IsWidthInfinite()
584 ? viewPort_.Width()
585 : layoutSetByParent.GetMaxSize().Width();
586 width = flexWidth - margin_.GetLayoutSize().Width();
587 }
588 } else {
589 width = childWidth;
590 if (gridColumnInfo_) {
591 auto columnWidth = gridColumnInfo_->GetWidth();
592 if (NearEqual(columnWidth, gridColumnInfo_->GetMaxWidth()) && !NearEqual(columnWidth, 0.0)) {
593 width = columnWidth;
594 }
595 }
596 }
597 // calculate height
598 double height = 0.0;
599 double childHeight = childSize_.Height() + padding_.GetLayoutSize().Height() + borderSize.Height();
600 if (selfDefineHeight_) {
601 if (boxSizing_ == BoxSizing::BORDER_BOX) {
602 height = selfMax.Height();
603 } else {
604 height = selfMax.Height() + padding_.GetLayoutSize().Height() + borderSize.Height();
605 }
606 } else if (useFlexHeight_) {
607 if (layoutSetByParent.GetMaxSize().IsHeightInfinite() && viewPort_.Height() < childHeight) {
608 height = childHeight;
609 } else {
610 double flexHeight = layoutSetByParent.GetMaxSize().IsHeightInfinite() && !viewPort_.IsHeightInfinite()
611 ? viewPort_.Height()
612 : layoutSetByParent.GetMaxSize().Height();
613 height = flexHeight - margin_.GetLayoutSize().Height();
614 }
615 } else {
616 height = childSize_.Height() + padding_.GetLayoutSize().Height() + borderSize.Height();
617 }
618
619 const static int32_t PLATFORM_VERSION_SIX = 6;
620 auto context = GetContext().Upgrade();
621 if (context && context->GetMinPlatformVersion() >= PLATFORM_VERSION_SIX) {
622 double minWidth = padding_.GetLayoutSize().Width() + borderSize.Width();
623 double minHeight = padding_.GetLayoutSize().Height() + borderSize.Height();
624 width = width > minWidth ? width : minWidth;
625 height = height > minHeight ? height : minHeight;
626 }
627 // allow force layoutsize for parent
628
629 if (layoutSetByParent.GetMaxSize().Width() == layoutSetByParent.GetMinSize().Width()) {
630 width = layoutSetByParent.GetMinSize().Width() - margin_.GetLayoutSize().Width();
631 }
632 if (layoutSetByParent.GetMaxSize().Height() == layoutSetByParent.GetMinSize().Height()) {
633 height = layoutSetByParent.GetMinSize().Height() - margin_.GetLayoutSize().Height();
634 }
635 paintSize_ = Size(width, height);
636 if (context && context->GetIsDeclarative()) {
637 // box layout size = paint size + margin size
638 if (LessNotEqual(margin_.LeftPx(), 0.0)) {
639 positionParam_.left = std::make_pair(margin_.Left(), true);
640 margin_.SetLeft(Dimension(0.0, margin_.Left().Unit()));
641 }
642 if (LessNotEqual(margin_.TopPx(), 0.0)) {
643 positionParam_.top = std::make_pair(margin_.Top(), true);
644 margin_.SetTop(Dimension(0.0, margin_.Top().Unit()));
645 }
646 selfLayoutSize_ = paintSize_ + margin_.GetLayoutSize();
647 } else {
648 selfLayoutSize_ = GetLayoutParam().Constrain(paintSize_ + margin_.GetLayoutSize());
649 }
650
651 paintSize_ = selfLayoutSize_ - margin_.GetLayoutSize();
652 touchArea_.SetOffset(margin_.GetOffset());
653 touchArea_.SetSize(paintSize_);
654 SetLayoutSize(selfLayoutSize_);
655 isChildOverflow_ = childSize_.Width() > GetLayoutSize().Width() || childSize_.Height() > GetLayoutSize().Height();
656 }
657
CalculateChildPosition()658 void RenderBoxBase::CalculateChildPosition()
659 {
660 Offset borderOffset = GetBorderOffset();
661 Size parentSize = selfLayoutSize_ - margin_.GetLayoutSize() - padding_.GetLayoutSize();
662 parentSize -= GetBorderSize();
663
664 if (!GetChildren().empty()) {
665 const auto& child = GetChildren().front();
666 childPosition_ = margin_.GetOffset() + borderOffset + padding_.GetOffset() +
667 Alignment::GetAlignPosition(parentSize, child->GetLayoutSize(), align_);
668 child->SetPosition(childPosition_);
669 }
670 }
671
PerformLayout()672 void RenderBoxBase::PerformLayout()
673 {
674 // update scale for margin, padding
675 auto context = context_.Upgrade();
676 if (!context) {
677 LOGE("[BOX][Dep:%{public}d][LAYOUT]Call Context Upgrade failed. PerformLayout failed.", this->GetDepth());
678 return;
679 }
680 if (isUseAlign_) {
681 context->AddAlignDeclarationNode(AceType::Claim(this));
682 }
683
684 CalculateGridLayoutSize();
685 // first. calculate self layout param
686 CalculateSelfLayoutParam();
687 // second. set layout param of child to calculate layout size
688 SetChildLayoutParam();
689 // third. using layout size of child, calculate layout size of box
690 CalculateSelfLayoutSize();
691
692 if (needReCalc_) {
693 needReCalc_ = false;
694 CalculateSelfLayoutParam();
695 SetChildLayoutParam();
696 CalculateSelfLayoutSize();
697 }
698 // forth. calculate position of child
699 CalculateChildPosition();
700
701 if (isUseAlign_) {
702 CalculateAlignDeclaration();
703 }
704
705 if (layoutCallback_) {
706 layoutCallback_();
707 }
708 }
709
Update(const RefPtr<Component> & component)710 void RenderBoxBase::Update(const RefPtr<Component>& component)
711 {
712 const RefPtr<BoxBaseComponent> box = AceType::DynamicCast<BoxBaseComponent>(component);
713 if (box) {
714 scrollPage_ = box->GetScrollPage();
715
716 displayType_ = box->GetDisplayType();
717 paddingOrigin_ = box->GetPadding();
718 marginOrigin_ = box->GetMargin();
719 additionalPadding_ = box->GetAdditionalPadding();
720 flex_ = box->GetFlex();
721 boxSizing_ = box->GetBoxSizing();
722 constraints_ = box->GetConstraints();
723 align_ = box->GetAlignment();
724 overflow_ = box->GetOverflow();
725 clipPath_ = box->GetClipPath();
726 deliverMinToChild_ = box->GetDeliverMinToChild();
727 width_ = box->GetWidthDimension();
728 height_ = box->GetHeightDimension();
729 auto context = context_.Upgrade();
730 if (context && scrollPage_) {
731 height_ = AnimatableDimension(context->GetStageRect().Height(), DimensionUnit::PX);
732 }
733 percentFlag_ = box->GetPercentFlag();
734 layoutInBox_ = box->GetLayoutInBoxFlag();
735 aspectRatio_ = box->GetAspectRatio();
736 minWidth_ = box->GetMinWidth();
737 minHeight_ = box->GetMinHeight();
738 maxWidth_ = box->GetMaxWidth();
739 maxHeight_ = box->GetMaxHeight();
740 if (aspectRatio_.IsValid()) {
741 if (GreatNotEqual(minWidth_.Value(), 0.0) && NearZero(minHeight_.Value())) {
742 minHeight_ = minWidth_ / aspectRatio_.Value();
743 }
744 if (GreatNotEqual(minHeight_.Value(), 0.0) && NearZero(minWidth_.Value())) {
745 minWidth_ = minHeight_ * aspectRatio_.Value();
746 }
747 }
748 useLiteStyle_ = box->UseLiteStyle();
749 mask_ = box->GetMask();
750 auto gridLayoutInfo = box->GetGridLayoutInfo();
751 auto gridColumnInfo = AceType::DynamicCast<GridColumnInfo>(gridLayoutInfo);
752 if (gridColumnInfo) {
753 gridColumnInfo_ = gridColumnInfo;
754 } else {
755 auto gridContainerInfo = AceType::DynamicCast<GridContainerInfo>(gridLayoutInfo);
756 if (gridContainerInfo) {
757 gridContainerInfo_ = gridContainerInfo;
758 }
759 }
760 isUseAlign_ = box->IsUseAlign();
761 if (isUseAlign_) {
762 alignPtr_ = box->GetAlignDeclarationPtr();
763 alignSide_ = box->GetUseAlignSide();
764 alignItemOffset_ = box->GetUseAlignOffset();
765 }
766 boxClipFlag_ = box->GetBoxClipFlag();
767 pixelMap_ = box->GetPixelMap();
768
769 MarkNeedLayout();
770 }
771 }
772
GetPaintPosition() const773 Offset RenderBoxBase::GetPaintPosition() const
774 {
775 return margin_.GetOffset();
776 }
777
GetPaintSize() const778 const Size& RenderBoxBase::GetPaintSize() const
779 {
780 return paintSize_;
781 }
782
SetPaintSize(const Size & paintSize)783 void RenderBoxBase::SetPaintSize(const Size& paintSize)
784 {
785 paintSize_ = paintSize;
786 }
787
Dump()788 void RenderBoxBase::Dump()
789 {
790 double dipScale = 1.0;
791 auto context = context_.Upgrade();
792 if (context) {
793 dipScale = context->GetDipScale();
794 }
795 Size borderSize = GetBorderSize();
796 Radius radius = GetBorderRadius();
797 DumpLog::GetInstance().AddDesc(std::string("WH: ")
798 .append(Size(width_.Value(), height_.Value()).ToString())
799 .append(", Margin: ")
800 .append(margin_.GetLayoutSizeInPx(dipScale).ToString())
801 .append(", Padding: ")
802 .append(padding_.GetLayoutSizeInPx(dipScale).ToString())
803 .append(", Border: ")
804 .append(borderSize.ToString())
805 .append(", Radius: ")
806 .append(radius.GetX().ToString())
807 .append(", Constraints: ")
808 .append(constraints_.ToString())
809 .append(", BGcolor: ")
810 .append(std::to_string(GetColor().GetValue())));
811 DumpLog::GetInstance().AddDesc(std::string("SelfSize: ")
812 .append(selfLayoutSize_.ToString())
813 .append(", ChildSize: ")
814 .append(childSize_.ToString())
815 .append(", ChildPos: ")
816 .append(childPosition_.ToString()));
817 if (gridColumnInfo_) {
818 DumpLog::GetInstance().AddDesc(std::string("GridColumnInfo"));
819 }
820 if (gridContainerInfo_) {
821 DumpLog::GetInstance().AddDesc(std::string("GridContainerInfo"));
822 }
823 }
824
ClearRenderObject()825 void RenderBoxBase::ClearRenderObject()
826 {
827 RenderNode::ClearRenderObject();
828 width_ = Dimension(-1.0);
829 height_ = Dimension(-1.0);
830 flex_ = BoxFlex::FLEX_NO;
831
832 constraints_ = LayoutParam(Size(), Size());
833 padding_ = EdgePx();
834 margin_ = EdgePx();
835 align_ = Alignment();
836 paintSize_ = Size();
837 touchArea_ = Rect();
838
839 deliverMinToChild_ = true;
840 scrollPage_ = false;
841 percentFlag_ = 0;
842 layoutInBox_ = false;
843
844 displayType_ = DisplayType::NO_SETTING;
845 paddingOrigin_ = Edge();
846 marginOrigin_ = Edge();
847 additionalPadding_ = Edge();
848
849 useFlexWidth_ = false;
850 useFlexHeight_ = false;
851 selfDefineWidth_ = false;
852 selfDefineHeight_ = false;
853 selfMaxWidth_ = Size::INFINITE_SIZE;
854 selfMinWidth_ = 0.0;
855 selfMaxHeight_ = Size::INFINITE_SIZE;
856 selfMinHeight_ = 0.0;
857
858 aspectRatio_ = AnimatableDimension();
859 minWidth_ = Dimension();
860 minHeight_ = Dimension();
861 maxWidth_ = Dimension();
862 maxHeight_ = Dimension();
863
864 selfLayoutParam_ = LayoutParam();
865 selfLayoutSize_ = Size();
866 childLayoutParam_ = LayoutParam();
867 childSize_ = Size();
868 childPosition_ = Offset();
869
870 layoutCallback_ = nullptr;
871 gridColumnInfo_ = nullptr;
872 gridContainerInfo_ = nullptr;
873
874 isUseAlign_ = false;
875 alignPtr_ = nullptr;
876 alignSide_ = AlignDeclaration::Edge::AUTO;
877 alignItemOffset_ = Dimension();
878 alignOffset_.Reset();
879 }
880
GetFloatPropertySetterMap()881 FloatPropertyAnimatable::SetterMap RenderBoxBase::GetFloatPropertySetterMap()
882 {
883 FloatPropertyAnimatable::SetterMap map;
884 auto weak = AceType::WeakClaim(this);
885 map[PropertyAnimatableType::PROPERTY_WIDTH] = [weak](float value) {
886 auto box = weak.Upgrade();
887 if (!box) {
888 LOGE("Set width failed. box is null.");
889 return;
890 }
891 box->SetWidth(value);
892 };
893 const RefPtr<RenderTextField> renderTextField = AceType::DynamicCast<RenderTextField>(GetFirstChild());
894 if (renderTextField) {
895 WeakPtr<RenderTextField> textWeak = renderTextField;
896 map[PropertyAnimatableType::PROPERTY_HEIGHT] = [textWeak](float value) {
897 auto renderTextField = textWeak.Upgrade();
898 if (!renderTextField) {
899 LOGE("Set height failed. text is null.");
900 return;
901 }
902 return renderTextField->SetHeight(value);
903 };
904 } else {
905 map[PropertyAnimatableType::PROPERTY_HEIGHT] = [weak](float value) {
906 auto box = weak.Upgrade();
907 if (!box) {
908 LOGE("Set height failed. box is null.");
909 return;
910 }
911 box->SetHeight(value);
912 };
913 }
914 return map;
915 };
916
GetFloatPropertyGetterMap()917 FloatPropertyAnimatable::GetterMap RenderBoxBase::GetFloatPropertyGetterMap()
918 {
919 FloatPropertyAnimatable::GetterMap map;
920 auto weak = AceType::WeakClaim(this);
921 map[PropertyAnimatableType::PROPERTY_WIDTH] = [weak]() -> float {
922 auto box = weak.Upgrade();
923 if (!box) {
924 LOGE("Get width failed. box is null.");
925 return 0.0;
926 }
927 return box->GetWidth();
928 };
929 const RefPtr<RenderTextField> renderTextField = AceType::DynamicCast<RenderTextField>(GetFirstChild());
930 if (renderTextField) {
931 WeakPtr<RenderTextField> textWeak = renderTextField;
932 map[PropertyAnimatableType::PROPERTY_HEIGHT] = [textWeak]() -> float {
933 auto renderTextField = textWeak.Upgrade();
934 if (!renderTextField) {
935 LOGE("Get height failed. text is null.");
936 return 0.0;
937 }
938 return renderTextField->GetHeight();
939 };
940 } else {
941 map[PropertyAnimatableType::PROPERTY_HEIGHT] = [weak]() -> float {
942 auto box = weak.Upgrade();
943 if (!box) {
944 LOGE("Get height failed. box is null.");
945 return 0.0;
946 }
947 return box->GetHeight();
948 };
949 }
950 return map;
951 }
952
CalculateAlignDeclaration()953 void RenderBoxBase::CalculateAlignDeclaration()
954 {
955 alignOffset_.Reset();
956 if (!GetAlignDeclarationOffset(alignPtr_, alignOffset_)) {
957 alignOffset_.Reset();
958 return;
959 }
960
961 double itemAlignOffset = 0.0;
962 auto context = GetContext().Upgrade();
963 if (context) {
964 itemAlignOffset = context->NormalizeToPx(alignItemOffset_);
965 }
966
967 switch (alignSide_) {
968 case AlignDeclaration::Edge::TOP:
969 alignOffset_ = alignOffset_ + Offset(0, itemAlignOffset);
970 break;
971 case AlignDeclaration::Edge::CENTER:
972 alignOffset_ = alignOffset_ - Offset(0, GetLayoutSize().Height() / 2 - itemAlignOffset);
973 break;
974 case AlignDeclaration::Edge::BOTTOM:
975 alignOffset_ = alignOffset_ - Offset(0, GetLayoutSize().Height() - itemAlignOffset);
976 break;
977 case AlignDeclaration::Edge::BASELINE:
978 alignOffset_ = alignOffset_ - Offset(0, GetBaselineDistance(TextBaseline::ALPHABETIC) - itemAlignOffset);
979 break;
980 case AlignDeclaration::Edge::START:
981 alignOffset_ = alignOffset_ + Offset(itemAlignOffset, 0);
982 break;
983 case AlignDeclaration::Edge::MIDDLE:
984 alignOffset_ = alignOffset_ - Offset(GetLayoutSize().Width() / 2 - itemAlignOffset, 0);
985 break;
986 case AlignDeclaration::Edge::END:
987 alignOffset_ = alignOffset_ - Offset(GetLayoutSize().Width() - itemAlignOffset, 0);
988 break;
989 default:
990 alignOffset_.Reset();
991 break;
992 }
993 }
994
995 } // namespace OHOS::Ace
996