• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_wrapper.h"
17 
18 #include <algorithm>
19 
20 #include "base/log/ace_checker.h"
21 #include "base/utils/utils.h"
22 #include "core/common/container.h"
23 #include "core/components/common/properties/alignment.h"
24 #include "core/components_ng/base/frame_node.h"
25 #include "core/components_ng/layout/layout_property.h"
26 #include "core/components_ng/layout/layout_wrapper_builder.h"
27 #include "core/components_ng/pattern/scrollable/scrollable_pattern.h"
28 #include "core/components_ng/property/layout_constraint.h"
29 #include "core/components_ng/property/measure_property.h"
30 #include "core/components_ng/property/property.h"
31 #include "core/components_ng/property/safe_area_insets.h"
32 #include "core/components_v2/inspector/inspector_constants.h"
33 #include "core/pipeline_ng/pipeline_context.h"
34 
35 namespace OHOS::Ace::NG {
36 namespace {
IsSyntaxNode(const std::string & tag)37 bool IsSyntaxNode(const std::string& tag)
38 {
39     return tag == V2::JS_VIEW_ETS_TAG || tag == V2::JS_IF_ELSE_ETS_TAG || tag == V2::JS_FOR_EACH_ETS_TAG ||
40            tag == V2::JS_LAZY_FOR_EACH_ETS_TAG || tag == V2::JS_SYNTAX_ITEM_ETS_TAG;
41 }
42 } // namespace
43 
SkipMeasureContent() const44 bool LayoutWrapper::SkipMeasureContent() const
45 {
46     return skipMeasureContent_ == true;
47 }
48 
ApplySafeArea(const SafeAreaInsets & insets,LayoutConstraintF & constraint)49 void LayoutWrapper::ApplySafeArea(const SafeAreaInsets& insets, LayoutConstraintF& constraint)
50 {
51     constraint.MinusPadding(
52         insets.left_.Length(), insets.right_.Length(), insets.top_.Length(), insets.bottom_.Length());
53 }
54 
OffsetNodeToSafeArea()55 void LayoutWrapper::OffsetNodeToSafeArea()
56 {
57     auto&& insets = GetLayoutProperty()->GetSafeAreaInsets();
58     CHECK_NULL_VOID(insets);
59     auto geometryNode = GetGeometryNode();
60     auto offset = geometryNode->GetMarginFrameOffset();
61     if (offset.GetX() < insets->left_.end) {
62         offset.SetX(insets->left_.end);
63     }
64     if (offset.GetY() < insets->top_.end) {
65         offset.SetY(insets->top_.end);
66     }
67 
68     auto right = offset.GetX() + geometryNode->GetMarginFrameSize().Width();
69     auto rightBound = insets->right_.IsValid() ? insets->right_.start : PipelineContext::GetCurrentRootWidth();
70     if (right > rightBound) {
71         offset.SetX(rightBound - geometryNode->GetMarginFrameSize().Width());
72     }
73     auto bottomBound = insets->bottom_.IsValid() ? insets->bottom_.start : PipelineContext::GetCurrentRootHeight();
74     auto bottom = offset.GetY() + geometryNode->GetMarginFrameSize().Height();
75     if (bottom > bottomBound) {
76         offset.SetY(bottomBound - geometryNode->GetMarginFrameSize().Height());
77     }
78     geometryNode->SetMarginFrameOffset(offset);
79 }
80 
RestoreGeoState()81 void LayoutWrapper::RestoreGeoState()
82 {
83     if (GetGeometryNode()) {
84         GetGeometryNode()->Restore();
85     }
86 }
87 
AvoidKeyboard(bool isFocusOnPage)88 void LayoutWrapper::AvoidKeyboard(bool isFocusOnPage)
89 {
90     // apply keyboard avoidance on Page
91     if (GetHostTag() == V2::PAGE_ETS_TAG) {
92         auto pipeline = PipelineContext::GetCurrentContext();
93         CHECK_NULL_VOID(pipeline);
94         auto manager = pipeline->GetSafeAreaManager();
95         if (!isFocusOnPage && LessNotEqual(manager->GetKeyboardOffset(), 0.0)) {
96             return;
97         }
98         auto safeArea = manager->GetSafeArea();
99         if (manager->IsAtomicService()) {
100             GetGeometryNode()->SetFrameOffset(OffsetF(0, manager->GetKeyboardOffset()));
101             return;
102         }
103         GetGeometryNode()->SetFrameOffset(OffsetF(0, safeArea.top_.Length() + manager->GetKeyboardOffset()));
104     }
105 }
106 
SaveGeoState()107 void LayoutWrapper::SaveGeoState()
108 {
109     // save geometry state before SafeArea expansion / keyboard avoidance.
110     GetGeometryNode()->Save();
111     // record nodes whose geometry states need to be restored, to speed up RestoreGeoState
112     auto pipeline = PipelineContext::GetCurrentContext();
113     CHECK_NULL_VOID(pipeline);
114     auto manager = pipeline->GetSafeAreaManager();
115     manager->AddGeoRestoreNode(GetHostNode());
116 }
117 
CheckValidSafeArea()118 bool LayoutWrapper::CheckValidSafeArea()
119 {
120     auto host = GetHostNode();
121     CHECK_NULL_RETURN(host, false);
122     auto pipeline = PipelineContext::GetCurrentContext();
123     CHECK_NULL_RETURN(pipeline, false);
124     auto safeAreaManager = pipeline->GetSafeAreaManager();
125     CHECK_NULL_RETURN(safeAreaManager, false);
126     SafeAreaInsets safeArea;
127     auto&& opts = GetLayoutProperty()->GetSafeAreaExpandOpts();
128     // if self does not have opts, check parent's
129     if (!opts) {
130         auto parent = host->GetAncestorNodeOfFrame();
131         CHECK_NULL_RETURN(parent, false);
132         CHECK_NULL_RETURN(parent->GetLayoutProperty(), false);
133         auto&& parentOpts = parent->GetLayoutProperty()->GetSafeAreaExpandOpts();
134         CHECK_NULL_RETURN(parentOpts, false);
135         safeArea = safeAreaManager->GetCombinedSafeArea(*parentOpts);
136     } else {
137         safeArea = safeAreaManager->GetCombinedSafeArea(*opts);
138     }
139     return safeArea.IsValid();
140 }
141 
ExpandSafeArea(bool isFocusOnPage)142 void LayoutWrapper::ExpandSafeArea(bool isFocusOnPage)
143 {
144     auto host = GetHostNode();
145     CHECK_NULL_VOID(host);
146     auto&& opts = GetLayoutProperty()->GetSafeAreaExpandOpts();
147     auto selfExpansive = host->SelfExpansive();
148     if (!selfExpansive && !host->NeedRestoreSafeArea()) {
149         // if safeArea switch from valid to not valid, keep node restored and return
150         // otherwise if node restored, meaning expansive parent did not adjust child or so on, restore cache
151         if (CheckValidSafeArea()) {
152             auto syncCasheSuccess = GetGeometryNode()->RestoreCache();
153             auto renderContext = host->GetRenderContext();
154             CHECK_NULL_VOID(renderContext);
155             if (syncCasheSuccess) {
156                 renderContext->SavePaintRect();
157             }
158         }
159         return;
160     }
161     CHECK_NULL_VOID(selfExpansive);
162     auto parent = host->GetAncestorNodeOfFrame();
163     if (parent && parent->GetPattern<ScrollablePattern>()) {
164         return;
165     }
166 
167     if ((opts->edges & SAFE_AREA_EDGE_BOTTOM) && (opts->type & SAFE_AREA_TYPE_KEYBOARD) && isFocusOnPage) {
168         ExpandIntoKeyboard();
169     }
170 
171     // get frame in global offset
172     auto parentGlobalOffset = host->GetParentGlobalOffsetDuringLayout();
173     auto geometryNode = GetGeometryNode();
174     auto frame = geometryNode->GetFrameRect() + parentGlobalOffset;
175     auto pipeline = PipelineContext::GetCurrentContext();
176     CHECK_NULL_VOID(pipeline);
177     auto safeArea = pipeline->GetSafeAreaManager()->GetCombinedSafeArea(*opts);
178     if ((opts->edges & SAFE_AREA_EDGE_START) && safeArea.left_.IsOverlapped(frame.Left())) {
179         frame.SetWidth(frame.Width() + frame.Left() - safeArea.left_.start);
180         frame.SetLeft(safeArea.left_.start);
181     }
182     if ((opts->edges & SAFE_AREA_EDGE_TOP) && safeArea.top_.IsOverlapped(frame.Top())) {
183         frame.SetHeight(frame.Height() + frame.Top() - safeArea.top_.start);
184         frame.SetTop(safeArea.top_.start);
185     }
186 
187     if ((opts->edges & SAFE_AREA_EDGE_END) && safeArea.right_.IsOverlapped(frame.Right())) {
188         frame.SetWidth(frame.Width() + (safeArea.right_.end - frame.Right()));
189     }
190     if ((opts->edges & SAFE_AREA_EDGE_BOTTOM) && safeArea.bottom_.IsOverlapped(frame.Bottom())) {
191         frame.SetHeight(frame.Height() + (safeArea.bottom_.end - frame.Bottom()));
192     }
193 
194     // reset if User has fixed size
195     auto layoutProperty = GetLayoutProperty();
196     if (layoutProperty->HasFixedWidth()) {
197         frame.SetWidth(geometryNode->GetFrameRect().Width());
198     }
199     if (layoutProperty->HasFixedHeight()) {
200         frame.SetHeight(geometryNode->GetFrameRect().Height());
201     }
202     if (layoutProperty->HasAspectRatio()) {
203         frame.SetHeight(frame.Width() / layoutProperty->GetAspectRatio());
204     }
205     // restore to local offset
206     frame -= parentGlobalOffset;
207     auto diff = geometryNode->GetFrameOffset() - frame.GetOffset();
208     if (!diff.NonOffset()) {
209         // children's position should remain the same.
210         AdjustChildren(diff);
211     } else {
212         RestoreExpansiveChildren();
213     }
214     geometryNode->SetFrameOffset(frame.GetOffset());
215     geometryNode->SetFrameSize(frame.GetSize());
216     host->SetNeedRestoreSafeArea(true);
217     auto renderContext = host->GetRenderContext();
218     CHECK_NULL_VOID(renderContext);
219     renderContext->SavePaintRect();
220 }
221 
AdjustChildren(const OffsetF & offset)222 void LayoutWrapper::AdjustChildren(const OffsetF& offset)
223 {
224     for (const auto& childUI : GetHostNode()->GetChildren()) {
225         AdjustChild(childUI, offset);
226     }
227 }
228 
AdjustChild(RefPtr<UINode> childUI,const OffsetF & offset)229 void LayoutWrapper::AdjustChild(RefPtr<UINode> childUI, const OffsetF& offset)
230 {
231     auto child = DynamicCast<FrameNode>(childUI);
232     if (!child) {
233         if (!IsSyntaxNode(childUI->GetTag())) {
234             return;
235         }
236         for (const auto& syntaxChild : childUI->GetChildren()) {
237             AdjustChild(syntaxChild, offset);
238         }
239         return;
240     }
241     if (child->NeedRestoreSafeArea()) {
242         return;
243     }
244     auto childGeo = child->GetGeometryNode();
245     child->SaveGeoState();
246     childGeo->SetFrameOffset(childGeo->GetFrameOffset() + offset);
247     child->SetNeedRestoreSafeArea(true);
248     auto renderContext = child->GetRenderContext();
249     CHECK_NULL_VOID(renderContext);
250     renderContext->SavePaintRect();
251 }
252 
RestoreExpansiveChildren()253 void LayoutWrapper::RestoreExpansiveChildren()
254 {
255     for (const auto& childUI : GetHostNode()->GetChildren()) {
256         RestoreExpansiveChild(childUI);
257     }
258 }
259 
RestoreExpansiveChild(const RefPtr<UINode> & childUI)260 void LayoutWrapper::RestoreExpansiveChild(const RefPtr<UINode>& childUI)
261 {
262     auto child = DynamicCast<FrameNode>(childUI);
263     if (!child) {
264         if (!IsSyntaxNode(childUI->GetTag())) {
265             return;
266         }
267         for (const auto& syntaxChild : childUI->GetChildren()) {
268             RestoreExpansiveChild(syntaxChild);
269         }
270         return;
271     }
272     if (!child->SelfExpansive() || !child->NeedRestoreSafeArea()) {
273         return;
274     }
275     auto childGeo = child->GetGeometryNode();
276     childGeo->Restore();
277     child->SetNeedRestoreSafeArea(false);
278     auto renderContext = child->GetRenderContext();
279     CHECK_NULL_VOID(renderContext);
280     renderContext->SavePaintRect();
281 }
282 
ExpandIntoKeyboard()283 void LayoutWrapper::ExpandIntoKeyboard()
284 {
285     // if parent already expanded into keyboard, offset shouldn't be applied again
286     auto parent = GetHostNode()->GetAncestorNodeOfFrame();
287     while (parent) {
288         auto&& opts = parent->GetLayoutProperty()->GetSafeAreaExpandOpts();
289         if (opts && (opts->edges & SAFE_AREA_EDGE_BOTTOM) && opts->type & SAFE_AREA_TYPE_KEYBOARD) {
290             return;
291         }
292         parent = parent->GetAncestorNodeOfFrame();
293     }
294 
295     auto pipeline = PipelineContext::GetCurrentContext();
296     CHECK_NULL_VOID(pipeline);
297     auto geometryNode = GetGeometryNode();
298     geometryNode->SetFrameOffset(
299         geometryNode->GetFrameOffset() - OffsetF(0, pipeline->GetSafeAreaManager()->GetKeyboardOffset()));
300 }
301 
ApplyConstraint(LayoutConstraintF constraint)302 void LayoutWrapper::ApplyConstraint(LayoutConstraintF constraint)
303 {
304     GetGeometryNode()->SetParentLayoutConstraint(constraint);
305 
306     auto layoutProperty = GetLayoutProperty();
307     auto& magicItemProperty = layoutProperty->GetMagicItemProperty();
308     if (magicItemProperty.HasAspectRatio()) {
309         std::optional<CalcSize> idealSize = std::nullopt;
310         if (layoutProperty->GetCalcLayoutConstraint()) {
311             idealSize = layoutProperty->GetCalcLayoutConstraint()->selfIdealSize;
312         }
313         auto greaterThanApiTen = Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_TEN);
314         constraint.ApplyAspectRatio(magicItemProperty.GetAspectRatioValue(), idealSize, greaterThanApiTen);
315     }
316 
317     auto&& insets = layoutProperty->GetSafeAreaInsets();
318     if (insets) {
319         ApplySafeArea(*insets, constraint);
320     }
321 
322     layoutProperty->UpdateLayoutConstraint(constraint);
323 }
324 
CreateRootConstraint()325 void LayoutWrapper::CreateRootConstraint()
326 {
327     LayoutConstraintF layoutConstraint;
328     layoutConstraint.percentReference.SetWidth(PipelineContext::GetCurrentRootWidth());
329     auto layoutProperty = GetLayoutProperty();
330     auto& magicItemProperty = layoutProperty->GetMagicItemProperty();
331     if (magicItemProperty.HasAspectRatio()) {
332         auto aspectRatio = magicItemProperty.GetAspectRatioValue();
333         if (Positive(aspectRatio)) {
334             auto height = PipelineContext::GetCurrentRootHeight() / aspectRatio;
335             layoutConstraint.percentReference.SetHeight(height);
336         }
337     } else {
338         layoutConstraint.percentReference.SetHeight(PipelineContext::GetCurrentRootHeight());
339     }
340     layoutProperty->UpdateLayoutConstraint(layoutConstraint);
341 }
342 
GetHostNode() const343 RefPtr<FrameNode> LayoutWrapper::GetHostNode() const
344 {
345     return hostNode_.Upgrade();
346 }
347 
AddNodeFlexLayouts()348 void LayoutWrapper::AddNodeFlexLayouts()
349 {
350     if (!AceChecker::IsPerformanceCheckEnabled()) {
351         return;
352     }
353     auto host = GetHostNode();
354     CHECK_NULL_VOID(host);
355     auto frameNodeParent = host->GetAncestorNodeOfFrame();
356     CHECK_NULL_VOID(frameNodeParent);
357     if (frameNodeParent->GetTag() == V2::FLEX_ETS_TAG) {
358         auto parent = host->GetParent();
359         CHECK_NULL_VOID(parent);
360         if (parent->GetTag() == V2::JS_VIEW_ETS_TAG) {
361             parent->AddFlexLayouts();
362         } else if (host->GetTag() == V2::COMMON_VIEW_ETS_TAG) {
363             auto children = host->GetChildren();
364             if (!children.empty()) {
365                 auto begin = children.begin();
366                 (*begin)->AddFlexLayouts();
367             }
368         } else {
369             host->AddFlexLayouts();
370         }
371     }
372 }
373 
AddNodeLayoutTime(int64_t time)374 void LayoutWrapper::AddNodeLayoutTime(int64_t time)
375 {
376     if (!AceChecker::IsPerformanceCheckEnabled()) {
377         return;
378     }
379     auto host = GetHostNode();
380     CHECK_NULL_VOID(host);
381     host->SetLayoutTime(time);
382 }
383 
384 } // namespace OHOS::Ace::NG
385