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