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