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/pipeline_ng/pipeline_context.h"
17 #include "core/components_ng/layout/box_layout_algorithm.h"
18
19 #include "core/components_ng/base/frame_node.h"
20 #include "core/components_ng/pattern/pattern.h"
21 #include "core/components_ng/property/measure_utils.h"
22 #include "core/pipeline/pipeline_base.h"
23
24 namespace OHOS::Ace::NG {
25
Measure(LayoutWrapper * layoutWrapper)26 void BoxLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper)
27 {
28 const auto& layoutProperty = layoutWrapper->GetLayoutProperty();
29 CHECK_NULL_VOID(layoutProperty);
30 auto layoutConstraint = layoutProperty->CreateChildConstraint();
31 auto host = layoutWrapper->GetHostNode();
32 CHECK_NULL_VOID(host);
33 auto pattern = host->GetPattern();
34 CHECK_NULL_VOID(pattern);
35 bool isEnableChildrenMatchParent = pattern->IsEnableChildrenMatchParent();
36 for (auto&& child : layoutWrapper->GetAllChildrenWithBuild()) {
37 auto childLayoutProperty = child->GetLayoutProperty();
38 CHECK_NULL_CONTINUE(childLayoutProperty);
39 auto layoutPolicy = childLayoutProperty->GetLayoutPolicyProperty();
40 if (isEnableChildrenMatchParent && layoutPolicy.has_value()) {
41 if (layoutPolicy->IsMatch()) {
42 layoutPolicyChildren_.emplace_back(child);
43 continue;
44 }
45 }
46 child->Measure(layoutConstraint);
47 }
48 PerformMeasureSelf(layoutWrapper);
49 if (isEnableChildrenMatchParent) {
50 auto frameSize = layoutWrapper->GetGeometryNode()->GetFrameSize();
51 MeasureAdaptiveLayoutChildren(layoutWrapper, frameSize);
52 }
53 }
54
Layout(LayoutWrapper * layoutWrapper)55 void BoxLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper)
56 {
57 PerformLayout(layoutWrapper);
58 for (auto&& child : layoutWrapper->GetAllChildrenWithBuild()) {
59 child->Layout();
60 }
61 }
62
MeasureContent(const LayoutConstraintF & contentConstraint,LayoutWrapper * layoutWrapper)63 std::optional<SizeF> BoxLayoutAlgorithm::MeasureContent(
64 const LayoutConstraintF& contentConstraint, LayoutWrapper* layoutWrapper)
65 {
66 return PerformMeasureContent(contentConstraint, layoutWrapper);
67 }
68
PerformMeasureSelfWithChildList(LayoutWrapper * layoutWrapper,const std::list<RefPtr<LayoutWrapper>> & childList)69 void BoxLayoutAlgorithm::PerformMeasureSelfWithChildList(
70 LayoutWrapper* layoutWrapper, const std::list<RefPtr<LayoutWrapper>>& childList)
71 {
72 const auto& hostLayoutProperty = layoutWrapper->GetLayoutProperty();
73 CHECK_NULL_VOID(hostLayoutProperty);
74 const auto& layoutConstraint = hostLayoutProperty->GetLayoutConstraint();
75 const auto& minSize = layoutConstraint->minSize;
76 const auto& maxSize = layoutConstraint->maxSize;
77 const auto& padding = hostLayoutProperty->CreatePaddingAndBorder();
78 auto measureType = hostLayoutProperty->GetMeasureType();
79 OptionalSizeF frameSize;
80 auto host = layoutWrapper->GetHostNode();
81 CHECK_NULL_VOID(host);
82 bool version10OrLarger = host->GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TEN);
83 const auto& pattern = host->GetPattern();
84 bool isEnableMatchParent = pattern && pattern->IsEnableMatchParent();
85 bool isEnableFix = pattern && pattern->IsEnableFix();
86 auto widthLayoutPolicy = LayoutCalPolicy::NO_MATCH;
87 auto heightLayoutPolicy = LayoutCalPolicy::NO_MATCH;
88 auto layoutPolicy = hostLayoutProperty->GetLayoutPolicyProperty();
89 if (layoutPolicy.has_value()) {
90 widthLayoutPolicy = layoutPolicy.value().widthLayoutPolicy_.value_or(LayoutCalPolicy::NO_MATCH);
91 heightLayoutPolicy = layoutPolicy.value().heightLayoutPolicy_.value_or(LayoutCalPolicy::NO_MATCH);
92 }
93 do {
94 // Use idea size first if it is valid.
95 frameSize.UpdateSizeWithCheck(layoutConstraint->selfIdealSize);
96 if (frameSize.IsValid()) {
97 break;
98 }
99
100 if (measureType == MeasureType::MATCH_PARENT) {
101 frameSize.UpdateIllegalSizeWithCheck(layoutConstraint->parentIdealSize);
102 if (frameSize.IsValid()) {
103 frameSize.Constrain(minSize, maxSize, version10OrLarger);
104 break;
105 }
106 }
107
108 const auto& content = layoutWrapper->GetGeometryNode()->GetContent();
109 auto fixIdealSize = OptionalSizeF();
110 if (content) {
111 // use content size.
112 auto contentSize = content->GetRect().GetSize();
113 AddPaddingToSize(padding, contentSize);
114 frameSize.UpdateIllegalSizeWithCheck(contentSize);
115 fixIdealSize.UpdateIllegalSizeWithCheck(contentSize);
116 } else {
117 // use the max child size.
118 auto childFrame = SizeF();
119 float maxWidth = 0.0f;
120 float maxHeight = 0.0f;
121 for (const auto& child : childList) {
122 if (!child) {
123 continue;
124 }
125 auto layoutProperty = child->GetLayoutProperty();
126 CHECK_NULL_CONTINUE(layoutProperty);
127 if (layoutProperty && layoutProperty->GetVisibilityValue(VisibleType::VISIBLE) == VisibleType::GONE) {
128 continue;
129 }
130 const auto& layoutPolicy = layoutProperty->GetLayoutPolicyProperty();
131 auto singleSideFrame = CalcLayoutPolicySingleSide(layoutPolicy,
132 layoutProperty->GetCalcLayoutConstraint(),
133 hostLayoutProperty->CreateChildConstraint(),
134 layoutProperty->GetMagicItemProperty());
135 if (singleSideFrame.AtLeastOneValid()) {
136 auto margin = layoutProperty->CreateMargin();
137 CalcSingleSideMarginFrame(margin, singleSideFrame, maxWidth, maxHeight);
138 }
139 auto childSize = (layoutPolicy.has_value() && layoutPolicy->IsMatch())
140 ? SizeF()
141 : child->GetGeometryNode()->GetMarginFrameSize();
142 if (maxWidth < childSize.Width()) {
143 maxWidth = childSize.Width();
144 }
145 if (maxHeight < childSize.Height()) {
146 maxHeight = childSize.Height();
147 }
148 childFrame.SetSizeT(SizeF { maxWidth, maxHeight });
149 }
150 AddPaddingToSize(padding, childFrame);
151 frameSize.UpdateIllegalSizeWithCheck(childFrame);
152 fixIdealSize =
153 UpdateOptionSizeByCalcLayoutConstraint(OptionalSizeF(childFrame.Width(), childFrame.Height()),
154 hostLayoutProperty->GetCalcLayoutConstraint(),
155 hostLayoutProperty->GetLayoutConstraint()->percentReference);
156 }
157 if (layoutConstraint->selfIdealSize.Width()) {
158 frameSize.ConstrainFloat(minSize, maxSize, false, version10OrLarger);
159 } else if (layoutConstraint->selfIdealSize.Height()) {
160 frameSize.ConstrainFloat(minSize, maxSize, true, version10OrLarger);
161 } else {
162 frameSize.Constrain(minSize, maxSize, version10OrLarger);
163 }
164 if (isEnableFix) {
165 if (widthLayoutPolicy == LayoutCalPolicy::FIX_AT_IDEAL_SIZE) {
166 frameSize.SetWidth(fixIdealSize.Width());
167 }
168 if (heightLayoutPolicy == LayoutCalPolicy::FIX_AT_IDEAL_SIZE) {
169 frameSize.SetHeight(fixIdealSize.Height());
170 }
171 }
172 frameSize.UpdateIllegalSizeWithCheck(SizeF { 0.0f, 0.0f });
173 } while (false);
174 if (isEnableMatchParent && layoutPolicy.has_value()) {
175 auto layoutPolicySize = ConstrainIdealSizeByLayoutPolicy(
176 layoutConstraint.value(), widthLayoutPolicy, heightLayoutPolicy, Axis::HORIZONTAL)
177 .ConvertToSizeT();
178 frameSize.UpdateSizeWithCheck(layoutPolicySize);
179 }
180 layoutWrapper->GetGeometryNode()->SetFrameSize(frameSize.ConvertToSizeT());
181 }
182
CalcSingleSideMarginFrame(MarginPropertyF & margin,const OptionalSizeF & singleSideFrame,float & maxWidth,float & maxHeight)183 void BoxLayoutAlgorithm::CalcSingleSideMarginFrame(
184 MarginPropertyF& margin, const OptionalSizeF& singleSideFrame, float& maxWidth, float& maxHeight)
185 {
186 if (singleSideFrame.Width().has_value()) {
187 maxWidth = std::max(singleSideFrame.Width().value() + margin.Width(), maxWidth);
188 }
189 if (singleSideFrame.Height().has_value()) {
190 maxHeight = std::max(singleSideFrame.Height().value() + margin.Height(), maxHeight);
191 }
192 }
193
194 // Called to perform measure current render node.
PerformMeasureSelf(LayoutWrapper * layoutWrapper)195 void BoxLayoutAlgorithm::PerformMeasureSelf(LayoutWrapper* layoutWrapper)
196 {
197 CHECK_NULL_VOID(layoutWrapper);
198 PerformMeasureSelfWithChildList(layoutWrapper, layoutWrapper->GetAllChildrenWithBuild());
199 }
200
MeasureAdaptiveLayoutChildren(LayoutWrapper * layoutWrapper,SizeF & frameSize)201 void BoxLayoutAlgorithm::MeasureAdaptiveLayoutChildren(LayoutWrapper* layoutWrapper, SizeF& frameSize)
202 {
203 const auto& layoutProperty = layoutWrapper->GetLayoutProperty();
204 CHECK_NULL_VOID(layoutProperty);
205 auto layoutConstraint = layoutProperty->CreateChildConstraint();
206 auto padding = layoutProperty->CreatePaddingAndBorder();
207 MinusPaddingToNonNegativeSize(padding, frameSize);
208 layoutConstraint.parentIdealSize.SetSize(frameSize);
209 auto host = layoutWrapper->GetHostNode();
210 IgnoreLayoutSafeAreaBundle bundle;
211 for (const auto& child : layoutPolicyChildren_) {
212 auto childNode = child->GetHostNode();
213 if (childNode && childNode->GetLayoutProperty() && childNode->GetLayoutProperty()->IsExpandConstraintNeeded()) {
214 bundle.first.emplace_back(childNode);
215 child->SetDelaySelfLayoutForIgnore();
216 child->GetGeometryNode()->SetParentLayoutConstraint(layoutConstraint);
217 continue;
218 }
219 child->Measure(layoutConstraint);
220 }
221 if (host && host->GetContext() && !bundle.first.empty()) {
222 host->SetDelaySelfLayoutForIgnore();
223 auto context = host->GetContext();
224 bundle.second = host;
225 context->AddIgnoreLayoutSafeAreaBundle(std::move(bundle));
226 }
227 }
228
229 // Called to perform layout render node and child.
PerformLayout(LayoutWrapper * layoutWrapper)230 void BoxLayoutAlgorithm::PerformLayout(LayoutWrapper* layoutWrapper)
231 {
232 CHECK_NULL_VOID(layoutWrapper);
233 // update child position.
234 auto size = layoutWrapper->GetGeometryNode()->GetFrameSize();
235 const auto& layoutProperty = layoutWrapper->GetLayoutProperty();
236 CHECK_NULL_VOID(layoutProperty);
237 const auto& padding = layoutProperty->CreatePaddingAndBorder();
238 MinusPaddingToSize(padding, size);
239 auto left = padding.left.value_or(0);
240 auto top = padding.top.value_or(0);
241 auto paddingOffset = OffsetF(left, top);
242 auto align = Alignment::CENTER;
243 const auto& positionProperty = layoutProperty->GetPositionProperty();
244 if (positionProperty) {
245 auto isMirrorable = positionProperty->GetIsMirrorable()
246 .value_or(false);
247 if (isMirrorable) {
248 auto alignment = positionProperty->GetLocalizedAlignment()
249 .value_or("center");
250 align= MapLocalizedToAlignment(alignment);
251 } else {
252 align = positionProperty->GetAlignment().value_or(align);
253 }
254 }
255 // Update child position.
256 for (const auto& child : layoutWrapper->GetAllChildrenWithBuild()) {
257 SizeF childSize = child->GetGeometryNode()->GetMarginFrameSize();
258 auto host = layoutWrapper->GetHostNode();
259 auto childNode = child->GetHostNode();
260 if (host && childNode && childNode->GetLayoutProperty() &&
261 childNode->GetLayoutProperty()->IsIgnoreOptsValid()) {
262 IgnoreLayoutSafeAreaOpts& opts = *(childNode->GetLayoutProperty()->GetIgnoreLayoutSafeAreaOpts());
263 auto sae = host->GetAccumulatedSafeAreaExpand(true, opts);
264 auto adjustSize = size + sae.Size();
265 auto translate = Alignment::GetAlignPosition(adjustSize, childSize, align) + paddingOffset;
266 translate -= sae.Offset();
267 child->GetGeometryNode()->SetMarginFrameOffset(translate);
268 } else {
269 auto translate = Alignment::GetAlignPosition(size, childSize, align) + paddingOffset;
270 child->GetGeometryNode()->SetMarginFrameOffset(translate);
271 }
272 }
273 // Update content position.
274 const auto& content = layoutWrapper->GetGeometryNode()->GetContent();
275 if (content) {
276 auto translate = Alignment::GetAlignPosition(size, content->GetRect().GetSize(), align) + paddingOffset;
277 content->SetOffset(translate);
278 }
279 }
280
PerformMeasureContent(const LayoutConstraintF & contentConstraint,LayoutWrapper * layoutWrapper)281 std::optional<SizeF> BoxLayoutAlgorithm::PerformMeasureContent(
282 const LayoutConstraintF& contentConstraint, LayoutWrapper* layoutWrapper)
283 {
284 auto host = layoutWrapper->GetHostNode();
285 CHECK_NULL_RETURN(host, std::nullopt);
286 if (!host->IsAtomicNode()) {
287 return std::nullopt;
288 }
289 const auto& layoutProperty = layoutWrapper->GetLayoutProperty();
290 CHECK_NULL_RETURN(layoutProperty, std::nullopt);
291 auto measureType = layoutProperty->GetMeasureType(MeasureType::MATCH_CONTENT);
292 OptionalSizeF contentSize;
293 do {
294 // Use idea size first if it is valid.
295 contentSize.UpdateSizeWithCheck(contentConstraint.selfIdealSize);
296 if (contentSize.IsValid()) {
297 break;
298 }
299
300 if (measureType == MeasureType::MATCH_PARENT) {
301 contentSize.UpdateIllegalSizeWithCheck(contentConstraint.parentIdealSize);
302 // use max is parent ideal size is invalid.
303 contentSize.UpdateIllegalSizeWithCheck(contentConstraint.percentReference);
304 break;
305 }
306
307 // wrap content case use min size default.
308 contentSize.UpdateIllegalSizeWithCheck(contentConstraint.minSize);
309 } while (false);
310 return contentSize.ConvertToSizeT();
311 }
312
MapLocalizedToAlignment(std::string localizedAlignment)313 Alignment BoxLayoutAlgorithm::MapLocalizedToAlignment(std::string localizedAlignment)
314 {
315 static const std::unordered_map<std::string, Alignment> alignmentMap = {
316 {"top_start", Alignment::TOP_LEFT},
317 {"top", Alignment::TOP_CENTER},
318 {"top_end", Alignment::TOP_RIGHT},
319 {"start", Alignment::CENTER_LEFT},
320 {"center", Alignment::CENTER},
321 {"end", Alignment::CENTER_RIGHT},
322 {"bottom_start", Alignment::BOTTOM_LEFT},
323 {"bottom", Alignment::BOTTOM_CENTER},
324 {"bottom_end", Alignment::BOTTOM_RIGHT}
325 };
326
327 auto it = alignmentMap.find(localizedAlignment);
328 if (it != alignmentMap.end()) {
329 return it->second;
330 }
331 return Alignment::CENTER;
332 }
333 } // namespace OHOS::Ace::NG
334