• 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/components/common/properties/alignment.h"
23 #include "core/components_ng/base/frame_node.h"
24 #include "core/components_ng/layout/layout_property.h"
25 #include "core/components_ng/layout/layout_wrapper_builder.h"
26 #include "core/components_ng/pattern/scrollable/scrollable_pattern.h"
27 #include "core/components_ng/property/layout_constraint.h"
28 #include "core/components_ng/property/measure_property.h"
29 #include "core/components_ng/property/property.h"
30 #include "core/components_ng/property/safe_area_insets.h"
31 #include "core/components_v2/inspector/inspector_constants.h"
32 #include "core/pipeline_ng/pipeline_context.h"
33 
34 namespace OHOS::Ace::NG {
35 
SkipMeasureContent() const36 bool LayoutWrapper::SkipMeasureContent() const
37 {
38     return skipMeasureContent_ == true;
39 }
40 
ApplySafeArea(const SafeAreaInsets & insets,LayoutConstraintF & constraint)41 void LayoutWrapper::ApplySafeArea(const SafeAreaInsets& insets, LayoutConstraintF& constraint)
42 {
43     constraint.MinusPadding(
44         insets.left_.Length(), insets.right_.Length(), insets.top_.Length(), insets.bottom_.Length());
45 }
46 
OffsetNodeToSafeArea()47 void LayoutWrapper::OffsetNodeToSafeArea()
48 {
49     auto&& insets = GetLayoutProperty()->GetSafeAreaInsets();
50     CHECK_NULL_VOID_NOLOG(insets);
51     auto geometryNode = GetGeometryNode();
52     auto offset = geometryNode->GetMarginFrameOffset();
53     if (offset.GetX() < insets->left_.end) {
54         offset.SetX(insets->left_.end);
55     }
56     if (offset.GetY() < insets->top_.end) {
57         offset.SetY(insets->top_.end);
58     }
59 
60     auto right = offset.GetX() + geometryNode->GetMarginFrameSize().Width();
61     auto rightBound = insets->right_.IsValid() ? insets->right_.start : PipelineContext::GetCurrentRootWidth();
62     if (right > rightBound) {
63         offset.SetX(rightBound - geometryNode->GetMarginFrameSize().Width());
64     }
65     auto bottomBound = insets->bottom_.IsValid() ? insets->bottom_.start : PipelineContext::GetCurrentRootHeight();
66     auto bottom = offset.GetY() + geometryNode->GetMarginFrameSize().Height();
67     if (bottom > bottomBound) {
68         offset.SetY(bottomBound - geometryNode->GetMarginFrameSize().Height());
69     }
70     geometryNode->SetMarginFrameOffset(offset);
71 }
72 
RestoreGeoState()73 void LayoutWrapper::RestoreGeoState()
74 {
75     auto pipeline = PipelineContext::GetCurrentContext();
76     CHECK_NULL_VOID(pipeline);
77     auto manager = pipeline->GetSafeAreaManager();
78     auto&& restoreNodes = manager->GetGeoRestoreNodes();
79     if (restoreNodes.find(hostNode_) != restoreNodes.end()) {
80         GetGeometryNode()->Restore();
81         manager->RemoveRestoreNode(hostNode_);
82     }
83 }
84 
AvoidKeyboard()85 void LayoutWrapper::AvoidKeyboard()
86 {
87     // apply keyboard avoidance on Page
88     if (GetHostTag() == V2::PAGE_ETS_TAG) {
89         auto pipeline = PipelineContext::GetCurrentContext();
90         CHECK_NULL_VOID(pipeline);
91         auto manager = pipeline->GetSafeAreaManager();
92         GetGeometryNode()->SetFrameOffset(
93             GetGeometryNode()->GetFrameOffset() + OffsetF(0, manager->GetKeyboardOffset()));
94     }
95 }
96 
SaveGeoState()97 void LayoutWrapper::SaveGeoState()
98 {
99     auto&& expandOpts = GetLayoutProperty()->GetSafeAreaExpandOpts();
100     if ((expandOpts && expandOpts->Expansive()) || GetHostTag() == V2::PAGE_ETS_TAG) {
101         // save geometry state before SafeArea expansion / keyboard avoidance.
102         GetGeometryNode()->Save();
103         // record nodes whose geometry states need to be restored, to speed up RestoreGeoState
104         auto pipeline = PipelineContext::GetCurrentContext();
105         CHECK_NULL_VOID(pipeline);
106         auto manager = pipeline->GetSafeAreaManager();
107         manager->AddGeoRestoreNode(GetHostNode());
108     }
109 }
110 
ExpandSafeArea()111 void LayoutWrapper::ExpandSafeArea()
112 {
113     auto&& opts = GetLayoutProperty()->GetSafeAreaExpandOpts();
114     CHECK_NULL_VOID_NOLOG(opts && opts->Expansive());
115 
116     // children of Scrollable nodes don't support expandSafeArea
117     auto host = GetHostNode();
118     CHECK_NULL_VOID(host);
119     auto parent = host->GetAncestorNodeOfFrame();
120     if (parent && parent->GetPattern<ScrollablePattern>()) {
121         return;
122     }
123 
124     if ((opts->edges & SAFE_AREA_EDGE_BOTTOM) && (opts->type & SAFE_AREA_TYPE_KEYBOARD)) {
125         ExpandIntoKeyboard();
126     }
127 
128     if (!(opts->type & SAFE_AREA_TYPE_SYSTEM) && !(opts->type & SAFE_AREA_TYPE_CUTOUT)) {
129         return;
130     }
131     // expand System and Cutout safeArea
132     // get frame in global offset
133     auto parentGlobalOffset = host->GetParentGlobalOffsetDuringLayout();
134     auto geometryNode = GetGeometryNode();
135     auto frame = geometryNode->GetFrameRect() + parentGlobalOffset;
136     auto pipeline = PipelineContext::GetCurrentContext();
137     CHECK_NULL_VOID(pipeline);
138     auto safeArea = pipeline->GetSafeAreaManager()->GetCombinedSafeArea(*opts);
139     if ((opts->edges & SAFE_AREA_EDGE_START) && safeArea.left_.IsOverlapped(frame.Left())) {
140         frame.SetWidth(frame.Width() + frame.Left() - safeArea.left_.start);
141         frame.SetLeft(safeArea.left_.start);
142     }
143     if ((opts->edges & SAFE_AREA_EDGE_TOP) && safeArea.top_.IsOverlapped(frame.Top())) {
144         frame.SetHeight(frame.Height() + frame.Top() - safeArea.top_.start);
145         frame.SetTop(safeArea.top_.start);
146     }
147 
148     if ((opts->edges & SAFE_AREA_EDGE_END) && safeArea.right_.IsOverlapped(frame.Right())) {
149         frame.SetWidth(frame.Width() + (safeArea.right_.end - frame.Right()));
150     }
151     if ((opts->edges & SAFE_AREA_EDGE_BOTTOM) && safeArea.bottom_.IsOverlapped(frame.Bottom())) {
152         frame.SetHeight(frame.Height() + (safeArea.bottom_.end - frame.Bottom()));
153     }
154 
155     // reset if User has fixed size
156     auto layoutProperty = GetLayoutProperty();
157     if (layoutProperty->HasFixedWidth()) {
158         frame.SetWidth(geometryNode->GetFrameRect().Width());
159     }
160     if (layoutProperty->HasFixedHeight()) {
161         frame.SetHeight(geometryNode->GetFrameRect().Height());
162     }
163     if (layoutProperty->HasAspectRatio()) {
164         frame.SetHeight(frame.Width() / layoutProperty->GetAspectRatio());
165     }
166     // restore to local offset
167     frame -= parentGlobalOffset;
168     geometryNode->SetFrameOffset(frame.GetOffset());
169     geometryNode->SetFrameSize(frame.GetSize());
170 }
171 
ExpandIntoKeyboard()172 void LayoutWrapper::ExpandIntoKeyboard()
173 {
174     // if parent already expanded into keyboard, offset shouldn't be applied again
175     auto parent = GetHostNode()->GetAncestorNodeOfFrame();
176     while (parent) {
177         auto&& opts = parent->GetLayoutProperty()->GetSafeAreaExpandOpts();
178         if (opts && (opts->edges & SAFE_AREA_EDGE_BOTTOM) && opts->type & SAFE_AREA_TYPE_KEYBOARD) {
179             return;
180         }
181         parent = parent->GetAncestorNodeOfFrame();
182     }
183 
184     auto pipeline = PipelineContext::GetCurrentContext();
185     CHECK_NULL_VOID(pipeline);
186     auto geometryNode = GetGeometryNode();
187     geometryNode->SetFrameOffset(
188         geometryNode->GetFrameOffset() - OffsetF(0, pipeline->GetSafeAreaManager()->GetKeyboardOffset()));
189 }
190 
ApplyConstraint(LayoutConstraintF constraint)191 void LayoutWrapper::ApplyConstraint(LayoutConstraintF constraint)
192 {
193     GetGeometryNode()->SetParentLayoutConstraint(constraint);
194 
195     auto layoutProperty = GetLayoutProperty();
196     const auto& magicItemProperty = layoutProperty->GetMagicItemProperty();
197     if (magicItemProperty && magicItemProperty->HasAspectRatio()) {
198         std::optional<CalcSize> idealSize = std::nullopt;
199         if (layoutProperty->GetCalcLayoutConstraint()) {
200             idealSize = layoutProperty->GetCalcLayoutConstraint()->selfIdealSize;
201         }
202         constraint.ApplyAspectRatio(magicItemProperty->GetAspectRatioValue(), idealSize);
203     }
204 
205     auto&& insets = layoutProperty->GetSafeAreaInsets();
206     if (insets) {
207         ApplySafeArea(*insets, constraint);
208     }
209 
210     layoutProperty->UpdateLayoutConstraint(constraint);
211 }
212 
CreateRootConstraint()213 void LayoutWrapper::CreateRootConstraint()
214 {
215     LayoutConstraintF layoutConstraint;
216     layoutConstraint.percentReference.SetWidth(PipelineContext::GetCurrentRootWidth());
217     auto layoutProperty = GetLayoutProperty();
218     const auto& magicItemProperty = layoutProperty->GetMagicItemProperty();
219     auto hasAspectRatio = magicItemProperty && magicItemProperty->HasAspectRatio();
220     if (hasAspectRatio) {
221         auto aspectRatio = magicItemProperty->GetAspectRatioValue();
222         if (Positive(aspectRatio)) {
223             auto height = PipelineContext::GetCurrentRootHeight() / aspectRatio;
224             layoutConstraint.percentReference.SetHeight(height);
225         }
226     } else {
227         layoutConstraint.percentReference.SetHeight(PipelineContext::GetCurrentRootHeight());
228     }
229     layoutProperty->UpdateLayoutConstraint(layoutConstraint);
230 }
231 
GetHostNode() const232 RefPtr<FrameNode> LayoutWrapper::GetHostNode() const
233 {
234     return hostNode_.Upgrade();
235 }
236 
AddNodeFlexLayouts()237 void LayoutWrapper::AddNodeFlexLayouts()
238 {
239     if (!AceChecker::IsPerformanceCheckEnabled()) {
240         return;
241     }
242     auto host = GetHostNode();
243     CHECK_NULL_VOID(host);
244     auto frameNodeParent = host->GetAncestorNodeOfFrame();
245     CHECK_NULL_VOID(frameNodeParent);
246     if (frameNodeParent->GetTag() == V2::FLEX_ETS_TAG) {
247         auto parent = host->GetParent();
248         CHECK_NULL_VOID(parent);
249         if (parent->GetTag() == V2::JS_VIEW_ETS_TAG) {
250             parent->AddFlexLayouts();
251         } else if (host->GetTag() == V2::COMMON_VIEW_ETS_TAG) {
252             auto children = host->GetChildren();
253             if (!children.empty()) {
254                 auto begin = children.begin();
255                 (*begin)->AddFlexLayouts();
256             }
257         } else {
258             host->AddFlexLayouts();
259         }
260     }
261 }
262 
AddNodeLayoutTime(int64_t time)263 void LayoutWrapper::AddNodeLayoutTime(int64_t time)
264 {
265     if (!AceChecker::IsPerformanceCheckEnabled()) {
266         return;
267     }
268     auto host = GetHostNode();
269     CHECK_NULL_VOID(host);
270     host->SetLayoutTime(time);
271 }
272 
273 } // namespace OHOS::Ace::NG
274