1 /*
2 * Copyright (c) 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_ng/layout/layout_property.h"
17
18 #include <optional>
19
20 #include "base/geometry/ng/size_t.h"
21 #include "base/utils/utils.h"
22 #include "core/components_ng/base/frame_node.h"
23 #include "core/components_ng/base/ui_node.h"
24 #include "core/components_ng/property/calc_length.h"
25 #include "core/components_ng/property/layout_constraint.h"
26 #include "core/components_ng/property/measure_utils.h"
27 #include "core/components_v2/inspector/inspector_constants.h"
28 #include "core/pipeline_ng/pipeline_context.h"
29
30 namespace OHOS::Ace::NG {
31 namespace {
VisibleTypeToString(VisibleType type)32 std::string VisibleTypeToString(VisibleType type)
33 {
34 static const LinearEnumMapNode<VisibleType, std::string> visibilityMap[] = {
35 { VisibleType::VISIBLE, "Visibility.Visible" },
36 { VisibleType::INVISIBLE, "Visibility.Hidden" },
37 { VisibleType::GONE, "Visibility.None" },
38 };
39 auto idx = BinarySearchFindIndex(visibilityMap, ArraySize(visibilityMap), type);
40 if (idx >= 0) {
41 return visibilityMap[idx].value;
42 }
43 return "Visibility.Visible";
44 }
45
TextDirectionToString(TextDirection type)46 std::string TextDirectionToString(TextDirection type)
47 {
48 static const LinearEnumMapNode<TextDirection, std::string> toStringMap[] = {
49 { TextDirection::LTR, "Direction.Ltr" },
50 { TextDirection::RTL, "Direction.Rtl" },
51 { TextDirection::INHERIT, "Direction.Inherit" },
52 { TextDirection::AUTO, "Direction.Auto" },
53 };
54 auto idx = BinarySearchFindIndex(toStringMap, ArraySize(toStringMap), type);
55 if (idx >= 0) {
56 return toStringMap[idx].value;
57 }
58 return "Direction.Ltr";
59 }
60 } // namespace
61
Reset()62 void LayoutProperty::Reset()
63 {
64 layoutConstraint_.reset();
65 calcLayoutConstraint_.reset();
66 padding_.reset();
67 margin_.reset();
68 borderWidth_.reset();
69 magicItemProperty_.reset();
70 positionProperty_.reset();
71 measureType_.reset();
72 layoutDirection_.reset();
73 propVisibility_.reset();
74 CleanDirty();
75 }
76
ToJsonValue(std::unique_ptr<JsonValue> & json) const77 void LayoutProperty::ToJsonValue(std::unique_ptr<JsonValue>& json) const
78 {
79 ACE_PROPERTY_TO_JSON_VALUE(calcLayoutConstraint_, MeasureProperty);
80 ACE_PROPERTY_TO_JSON_VALUE(positionProperty_, PositionProperty);
81 ACE_PROPERTY_TO_JSON_VALUE(magicItemProperty_, MagicItemProperty);
82 ACE_PROPERTY_TO_JSON_VALUE(flexItemProperty_, FlexItemProperty);
83 ACE_PROPERTY_TO_JSON_VALUE(borderWidth_, BorderWidthProperty);
84 ACE_PROPERTY_TO_JSON_VALUE(gridProperty_, GridProperty);
85
86 if (padding_) {
87 json->Put("padding", padding_->ToJsonString().c_str());
88 } else {
89 json->Put("padding", "0.00vp");
90 }
91
92 if (margin_) {
93 json->Put("margin", margin_->ToJsonString().c_str());
94 } else {
95 json->Put("margin", "0.00vp");
96 }
97
98 json->Put("visibility", VisibleTypeToString(propVisibility_.value_or(VisibleType::VISIBLE)).c_str());
99 json->Put("direction", TextDirectionToString(GetLayoutDirection()).c_str());
100 }
101
Clone() const102 RefPtr<LayoutProperty> LayoutProperty::Clone() const
103 {
104 auto layoutProperty = MakeRefPtr<LayoutProperty>();
105 Clone(layoutProperty);
106 return layoutProperty;
107 }
108
Clone(RefPtr<LayoutProperty> layoutProperty) const109 void LayoutProperty::Clone(RefPtr<LayoutProperty> layoutProperty) const
110 {
111 layoutProperty->UpdateLayoutProperty(this);
112 }
113
UpdateLayoutProperty(const LayoutProperty * layoutProperty)114 void LayoutProperty::UpdateLayoutProperty(const LayoutProperty* layoutProperty)
115 {
116 layoutConstraint_ = layoutProperty->layoutConstraint_;
117 if (layoutProperty->gridProperty_) {
118 gridProperty_ = std::make_unique<GridProperty>(*layoutProperty->gridProperty_);
119 }
120 if (layoutProperty->calcLayoutConstraint_) {
121 calcLayoutConstraint_ = std::make_unique<MeasureProperty>(*layoutProperty->calcLayoutConstraint_);
122 }
123 if (layoutProperty->padding_) {
124 padding_ = std::make_unique<PaddingProperty>(*layoutProperty->padding_);
125 }
126 if (layoutProperty->margin_) {
127 margin_ = std::make_unique<PaddingProperty>(*layoutProperty->margin_);
128 }
129 if (layoutProperty->borderWidth_) {
130 borderWidth_ = std::make_unique<BorderWidthProperty>(*layoutProperty->borderWidth_);
131 }
132 if (layoutProperty->magicItemProperty_) {
133 magicItemProperty_ = std::make_unique<MagicItemProperty>(*layoutProperty->magicItemProperty_);
134 }
135 if (layoutProperty->positionProperty_) {
136 positionProperty_ = std::make_unique<PositionProperty>(*layoutProperty->positionProperty_);
137 }
138 if (layoutProperty->flexItemProperty_) {
139 flexItemProperty_ = std::make_unique<FlexItemProperty>(*layoutProperty->flexItemProperty_);
140 }
141 propVisibility_ = layoutProperty->GetVisibility();
142 measureType_ = layoutProperty->measureType_;
143 layoutDirection_ = layoutProperty->layoutDirection_;
144 propertyChangeFlag_ = layoutProperty->propertyChangeFlag_;
145 }
146
UpdateCalcLayoutProperty(const MeasureProperty & constraint)147 void LayoutProperty::UpdateCalcLayoutProperty(const MeasureProperty& constraint)
148 {
149 if (!calcLayoutConstraint_) {
150 calcLayoutConstraint_ = std::make_unique<MeasureProperty>(constraint);
151 propertyChangeFlag_ = propertyChangeFlag_ | PROPERTY_UPDATE_MEASURE;
152 return;
153 }
154 if (*calcLayoutConstraint_ == constraint) {
155 return;
156 }
157 calcLayoutConstraint_->selfIdealSize = constraint.selfIdealSize;
158 calcLayoutConstraint_->maxSize = constraint.maxSize;
159 calcLayoutConstraint_->minSize = constraint.minSize;
160 propertyChangeFlag_ = propertyChangeFlag_ | PROPERTY_UPDATE_MEASURE;
161 }
162
UpdateLayoutConstraint(const LayoutConstraintF & parentConstraint)163 void LayoutProperty::UpdateLayoutConstraint(const LayoutConstraintF& parentConstraint)
164 {
165 layoutConstraint_ = parentConstraint;
166 if (margin_) {
167 // TODO: add margin is negative case.
168 auto margin = CreateMargin();
169 MinusPaddingToSize(margin, layoutConstraint_->maxSize);
170 MinusPaddingToSize(margin, layoutConstraint_->minSize);
171 MinusPaddingToSize(margin, layoutConstraint_->percentReference);
172 MinusPaddingToSize(margin, layoutConstraint_->selfIdealSize);
173 MinusPaddingToSize(margin, layoutConstraint_->parentIdealSize);
174 }
175 if (calcLayoutConstraint_) {
176 if (calcLayoutConstraint_->maxSize.has_value()) {
177 layoutConstraint_->UpdateMaxSizeWithCheck(ConvertToSize(calcLayoutConstraint_->maxSize.value(),
178 parentConstraint.scaleProperty, parentConstraint.percentReference));
179 }
180 if (calcLayoutConstraint_->minSize.has_value()) {
181 layoutConstraint_->UpdateMinSizeWithCheck(ConvertToSize(calcLayoutConstraint_->minSize.value(),
182 parentConstraint.scaleProperty, parentConstraint.percentReference));
183 }
184 if (calcLayoutConstraint_->selfIdealSize.has_value()) {
185 LOGD("CalcLayoutConstraint->selfIdealSize = %{public}s",
186 calcLayoutConstraint_->selfIdealSize.value().ToString().c_str());
187 layoutConstraint_->UpdateIllegalSelfIdealSizeWithCheck(
188 ConvertToOptionalSize(calcLayoutConstraint_->selfIdealSize.value(), parentConstraint.scaleProperty,
189 parentConstraint.percentReference));
190 }
191 }
192
193 CheckSelfIdealSize();
194 CheckBorderAndPadding();
195 CheckAspectRatio();
196 }
197
CheckBorderAndPadding()198 void LayoutProperty::CheckBorderAndPadding()
199 {
200 auto selfWidth = layoutConstraint_->selfIdealSize.Width();
201 auto selfHeight = layoutConstraint_->selfIdealSize.Height();
202 if (!selfWidth && !selfHeight) {
203 return;
204 }
205 auto selfWidthFloat = selfWidth.value_or(Infinity<float>());
206 auto selfHeightFloat = selfHeight.value_or(Infinity<float>());
207 auto paddingWithBorder = CreatePaddingAndBorder();
208 auto deflateWidthF = paddingWithBorder.Width();
209 auto deflateHeightF = paddingWithBorder.Height();
210 if (LessOrEqual(deflateWidthF, selfWidthFloat) && LessOrEqual(deflateHeightF, selfHeightFloat)) {
211 return;
212 }
213 if (GreatNotEqual(deflateWidthF, selfWidthFloat)) {
214 layoutConstraint_->selfIdealSize.SetWidth(deflateWidthF);
215 }
216 if (GreatNotEqual(deflateHeightF, selfHeightFloat)) {
217 layoutConstraint_->selfIdealSize.SetHeight(deflateHeightF);
218 }
219 }
220
CheckAspectRatio()221 void LayoutProperty::CheckAspectRatio()
222 {
223 auto hasAspectRatio = magicItemProperty_ ? magicItemProperty_->HasAspectRatio() : false;
224 if (!hasAspectRatio) {
225 return;
226 }
227 auto aspectRatio = magicItemProperty_->GetAspectRatioValue();
228 // Adjust by aspect ratio, firstly pick height based on width. It means that when width, height and aspectRatio are
229 // all set, the height is not used.
230 auto maxWidth = layoutConstraint_->maxSize.Width();
231 auto maxHeight = layoutConstraint_->maxSize.Height();
232 if (maxHeight > maxWidth / aspectRatio) {
233 maxHeight = maxWidth / aspectRatio;
234 }
235 layoutConstraint_->maxSize.SetWidth(maxWidth);
236 layoutConstraint_->maxSize.SetHeight(maxHeight);
237 std::optional<float> selfWidth;
238 std::optional<float> selfHeight;
239 if (layoutConstraint_->selfIdealSize.Width()) {
240 selfWidth = layoutConstraint_->selfIdealSize.Width().value();
241 selfHeight = selfWidth.value() / aspectRatio;
242 if (selfHeight > maxHeight) {
243 selfHeight = maxHeight;
244 selfWidth = selfHeight.value() * aspectRatio;
245 }
246 } else if (layoutConstraint_->selfIdealSize.Height()) {
247 selfHeight = layoutConstraint_->selfIdealSize.Height().value();
248 selfWidth = selfHeight.value() * aspectRatio;
249 if (selfWidth > maxWidth) {
250 selfWidth = maxWidth;
251 selfHeight = selfWidth.value() / aspectRatio;
252 }
253 }
254
255 if (selfHeight) {
256 layoutConstraint_->selfIdealSize.SetHeight(selfHeight);
257 }
258 if (selfWidth) {
259 layoutConstraint_->selfIdealSize.SetWidth(selfWidth);
260 }
261 // TODO: after measure done, need to check AspectRatio again.
262 }
263
BuildGridProperty(const RefPtr<FrameNode> & host)264 void LayoutProperty::BuildGridProperty(const RefPtr<FrameNode>& host)
265 {
266 CHECK_NULL_VOID_NOLOG(gridProperty_);
267 auto parent = host->GetAncestorNodeOfFrame();
268 while (parent) {
269 if (parent->GetTag() == V2::GRIDCONTAINER_ETS_TAG) {
270 auto containerLayout = parent->GetLayoutProperty();
271 gridProperty_->UpdateContainer(containerLayout, host);
272 UpdateUserDefinedIdealSize(CalcSize(CalcLength(gridProperty_->GetWidth()), std::nullopt));
273 break;
274 }
275 parent = parent->GetAncestorNodeOfFrame();
276 }
277 }
278
UpdateGridProperty(std::optional<int32_t> span,std::optional<int32_t> offset,GridSizeType type)279 void LayoutProperty::UpdateGridProperty(std::optional<int32_t> span, std::optional<int32_t> offset, GridSizeType type)
280 {
281 if (!gridProperty_) {
282 gridProperty_ = std::make_unique<GridProperty>();
283 }
284
285 bool isSpanUpdated = (span.has_value() && gridProperty_->UpdateSpan(span.value(), type));
286 bool isOffsetUpdated = (offset.has_value() && gridProperty_->UpdateOffset(offset.value(), type));
287 if (isSpanUpdated || isOffsetUpdated) {
288 propertyChangeFlag_ = propertyChangeFlag_ | PROPERTY_UPDATE_MEASURE;
289 }
290 }
291
UpdateGridOffset(const RefPtr<FrameNode> & host)292 bool LayoutProperty::UpdateGridOffset(const RefPtr<FrameNode>& host)
293 {
294 CHECK_NULL_RETURN_NOLOG(gridProperty_, false);
295 auto optOffset = gridProperty_->GetOffset();
296 if (optOffset == UNDEFINED_DIMENSION) {
297 return false;
298 }
299
300 RefPtr<FrameNode> parent = host->GetAncestorNodeOfFrame();
301 auto parentOffset = parent->GetOffsetRelativeToWindow();
302 auto globalOffset = gridProperty_->GetContainerPosition();
303
304 OffsetF offset(optOffset.ConvertToPx(), 0);
305 offset = offset + globalOffset - parentOffset;
306 const auto& geometryNode = host->GetGeometryNode();
307 if (offset.GetX() == geometryNode->GetFrameOffset().GetX()) {
308 return false;
309 }
310 offset.SetY(geometryNode->GetFrameOffset().GetY());
311 geometryNode->SetFrameOffset(offset);
312 return true;
313 }
314
CheckSelfIdealSize()315 void LayoutProperty::CheckSelfIdealSize()
316 {
317 if (measureType_ == MeasureType::MATCH_PARENT) {
318 layoutConstraint_->UpdateIllegalSelfIdealSizeWithCheck(layoutConstraint_->parentIdealSize);
319 }
320 if (!calcLayoutConstraint_) {
321 return;
322 }
323 if (calcLayoutConstraint_->minSize.has_value()) {
324 layoutConstraint_->selfIdealSize.UpdateSizeWhenLarger(ConvertToSize(calcLayoutConstraint_->minSize.value(),
325 layoutConstraint_->scaleProperty, layoutConstraint_->percentReference));
326 }
327 if (calcLayoutConstraint_->maxSize.has_value()) {
328 layoutConstraint_->selfIdealSize.UpdateSizeWhenSmaller(ConvertToSize(calcLayoutConstraint_->maxSize.value(),
329 layoutConstraint_->scaleProperty, layoutConstraint_->percentReference));
330 }
331 }
332
CreateChildConstraint() const333 LayoutConstraintF LayoutProperty::CreateChildConstraint() const
334 {
335 CHECK_NULL_RETURN(layoutConstraint_, {});
336 auto layoutConstraint = contentConstraint_.value();
337 layoutConstraint.parentIdealSize = layoutConstraint.selfIdealSize;
338 // update max size when ideal size has value.
339 if (layoutConstraint.parentIdealSize.Width()) {
340 layoutConstraint.maxSize.SetWidth(layoutConstraint.parentIdealSize.Width().value());
341 layoutConstraint.percentReference.SetWidth(layoutConstraint.parentIdealSize.Width().value());
342 }
343 if (layoutConstraint.parentIdealSize.Height()) {
344 layoutConstraint.maxSize.SetHeight(layoutConstraint.parentIdealSize.Height().value());
345 layoutConstraint.percentReference.SetHeight(layoutConstraint.parentIdealSize.Height().value());
346 }
347 // for child constraint, reset current selfIdealSize and minSize.
348 layoutConstraint.selfIdealSize.Reset();
349 layoutConstraint.minSize.Reset();
350 return layoutConstraint;
351 }
352
UpdateContentConstraint()353 void LayoutProperty::UpdateContentConstraint()
354 {
355 CHECK_NULL_VOID(layoutConstraint_);
356 contentConstraint_ = layoutConstraint_.value();
357 // update percent reference when parent has size.
358 if (contentConstraint_->parentIdealSize.Width()) {
359 contentConstraint_->percentReference.SetWidth(contentConstraint_->parentIdealSize.Width().value());
360 }
361 if (contentConstraint_->parentIdealSize.Height()) {
362 contentConstraint_->percentReference.SetHeight(contentConstraint_->parentIdealSize.Height().value());
363 }
364 if (padding_) {
365 auto paddingF = ConvertToPaddingPropertyF(
366 *padding_, contentConstraint_->scaleProperty, contentConstraint_->percentReference.Width());
367 contentConstraint_->MinusPaddingOnBothSize(paddingF.left, paddingF.right, paddingF.top, paddingF.bottom);
368 }
369 if (borderWidth_) {
370 auto borderWidthF = ConvertToBorderWidthPropertyF(
371 *borderWidth_, contentConstraint_->scaleProperty, contentConstraint_->percentReference.Width());
372 contentConstraint_->MinusPaddingOnBothSize(
373 borderWidthF.leftDimen, borderWidthF.rightDimen, borderWidthF.topDimen, borderWidthF.bottomDimen);
374 }
375 }
376
CreatePaddingAndBorder()377 PaddingPropertyF LayoutProperty::CreatePaddingAndBorder()
378 {
379 if (layoutConstraint_.has_value()) {
380 auto padding = ConvertToPaddingPropertyF(
381 padding_, ScaleProperty::CreateScaleProperty(), layoutConstraint_->percentReference.Width());
382 auto borderWidth = ConvertToBorderWidthPropertyF(
383 borderWidth_, ScaleProperty::CreateScaleProperty(), layoutConstraint_->percentReference.Width());
384
385 return PaddingPropertyF { padding.left.value_or(0) + borderWidth.leftDimen.value_or(0),
386 padding.right.value_or(0) + borderWidth.rightDimen.value_or(0),
387 padding.top.value_or(0) + borderWidth.topDimen.value_or(0),
388 padding.bottom.value_or(0) + borderWidth.bottomDimen.value_or(0) };
389 }
390 auto padding = ConvertToPaddingPropertyF(
391 padding_, ScaleProperty::CreateScaleProperty(), PipelineContext::GetCurrentRootWidth());
392 auto borderWidth = ConvertToBorderWidthPropertyF(
393 borderWidth_, ScaleProperty::CreateScaleProperty(), PipelineContext::GetCurrentRootWidth());
394
395 return PaddingPropertyF { padding.left.value_or(0) + borderWidth.leftDimen.value_or(0),
396 padding.right.value_or(0) + borderWidth.rightDimen.value_or(0),
397 padding.top.value_or(0) + borderWidth.topDimen.value_or(0),
398 padding.bottom.value_or(0) + borderWidth.bottomDimen.value_or(0) };
399 }
400
CreatePaddingAndBorderWithDefault(float paddingHorizontalDefault,float paddingVerticalDefault,float borderHorizontalDefault,float borderVerticalDefault)401 PaddingPropertyF LayoutProperty::CreatePaddingAndBorderWithDefault(float paddingHorizontalDefault,
402 float paddingVerticalDefault, float borderHorizontalDefault, float borderVerticalDefault)
403 {
404 if (layoutConstraint_.has_value()) {
405 auto padding = ConvertToPaddingPropertyF(
406 padding_, ScaleProperty::CreateScaleProperty(), layoutConstraint_->percentReference.Width());
407 auto borderWidth = ConvertToBorderWidthPropertyF(
408 borderWidth_, ScaleProperty::CreateScaleProperty(), layoutConstraint_->percentReference.Width());
409 return PaddingPropertyF { padding.left.value_or(paddingHorizontalDefault) +
410 borderWidth.leftDimen.value_or(borderHorizontalDefault),
411 padding.right.value_or(paddingHorizontalDefault) + borderWidth.rightDimen.value_or(borderHorizontalDefault),
412 padding.top.value_or(paddingVerticalDefault) + borderWidth.topDimen.value_or(borderVerticalDefault),
413 padding.bottom.value_or(paddingVerticalDefault) + borderWidth.bottomDimen.value_or(borderVerticalDefault) };
414 }
415 auto padding = ConvertToPaddingPropertyF(
416 padding_, ScaleProperty::CreateScaleProperty(), PipelineContext::GetCurrentRootWidth());
417 auto borderWidth = ConvertToBorderWidthPropertyF(
418 borderWidth_, ScaleProperty::CreateScaleProperty(), PipelineContext::GetCurrentRootWidth());
419
420 return PaddingPropertyF { padding.left.value_or(paddingHorizontalDefault) +
421 borderWidth.leftDimen.value_or(borderHorizontalDefault),
422 padding.right.value_or(paddingHorizontalDefault) + borderWidth.rightDimen.value_or(borderHorizontalDefault),
423 padding.top.value_or(paddingVerticalDefault) + borderWidth.topDimen.value_or(borderVerticalDefault),
424 padding.bottom.value_or(paddingVerticalDefault) + borderWidth.bottomDimen.value_or(borderVerticalDefault) };
425 }
426
CreatePaddingWithoutBorder()427 PaddingPropertyF LayoutProperty::CreatePaddingWithoutBorder()
428 {
429 if (layoutConstraint_.has_value()) {
430 return ConvertToPaddingPropertyF(
431 padding_, layoutConstraint_->scaleProperty, layoutConstraint_->percentReference.Width());
432 }
433
434 return ConvertToPaddingPropertyF(
435 padding_, ScaleProperty::CreateScaleProperty(), PipelineContext::GetCurrentRootWidth());
436 }
437
CreateMargin()438 MarginPropertyF LayoutProperty::CreateMargin()
439 {
440 if (layoutConstraint_.has_value()) {
441 return ConvertToMarginPropertyF(
442 margin_, layoutConstraint_->scaleProperty, layoutConstraint_->percentReference.Width());
443 }
444
445 return ConvertToMarginPropertyF(
446 margin_, ScaleProperty::CreateScaleProperty(), PipelineContext::GetCurrentRootWidth());
447 }
448
SetHost(const WeakPtr<FrameNode> & host)449 void LayoutProperty::SetHost(const WeakPtr<FrameNode>& host)
450 {
451 host_ = host;
452 }
453
GetHost() const454 RefPtr<FrameNode> LayoutProperty::GetHost() const
455 {
456 return host_.Upgrade();
457 }
458
OnVisibilityUpdate(VisibleType visible) const459 void LayoutProperty::OnVisibilityUpdate(VisibleType visible) const
460 {
461 auto host = GetHost();
462 CHECK_NULL_VOID(host);
463 host->OnVisibleChange(visible == VisibleType::VISIBLE);
464 auto parent = host->GetAncestorNodeOfFrame();
465 if (parent) {
466 parent->MarkNeedSyncRenderTree();
467 parent->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
468 }
469 }
470
471 } // namespace OHOS::Ace::NG
472