1 /*
2 * Copyright (c) 2023 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 #include "safe_area_manager.h"
16
17 #include "core/components/container_modal/container_modal_constants.h"
18 #include "core/components_ng/pattern/navrouter/navdestination_pattern.h"
19
20 namespace OHOS::Ace::NG {
GenerateCutOutAreaWithRoot(const SafeAreaInsets & safeArea,NG::OptionalSize<uint32_t> rootSize)21 SafeAreaInsets GenerateCutOutAreaWithRoot(const SafeAreaInsets& safeArea, NG::OptionalSize<uint32_t> rootSize)
22 {
23 // cutout regions adjacent to edges.
24 auto cutoutArea = safeArea;
25
26 if (cutoutArea.top_.IsValid()) {
27 cutoutArea.top_.start = 0;
28 }
29 if (cutoutArea.bottom_.IsValid()) {
30 cutoutArea.bottom_.end = rootSize.Height().has_value() ? rootSize.Height().value()
31 : PipelineContext::GetCurrentRootHeight();
32 }
33 if (cutoutArea.left_.IsValid()) {
34 cutoutArea.left_.start = 0;
35 }
36 if (cutoutArea.right_.IsValid()) {
37 cutoutArea.right_.end = rootSize.Width().has_value() ? rootSize.Width().value()
38 : PipelineContext::GetCurrentRootWidth();
39 }
40 return cutoutArea;
41 }
42
CheckCutoutSafeArea(const SafeAreaInsets & safeArea,NG::OptionalSize<uint32_t> rootSize)43 bool SafeAreaManager::CheckCutoutSafeArea(const SafeAreaInsets& safeArea, NG::OptionalSize<uint32_t> rootSize)
44 {
45 return cutoutSafeArea_ != GenerateCutOutAreaWithRoot(safeArea, rootSize);
46 }
47
UpdateCutoutSafeArea(const SafeAreaInsets & safeArea,NG::OptionalSize<uint32_t> rootSize)48 bool SafeAreaManager::UpdateCutoutSafeArea(const SafeAreaInsets& safeArea, NG::OptionalSize<uint32_t> rootSize)
49 {
50 auto safeAreaWithRoot = GenerateCutOutAreaWithRoot(safeArea, rootSize);
51 if (cutoutSafeArea_ == safeAreaWithRoot) {
52 return false;
53 }
54 ACE_SCOPED_TRACE("SafeAreaManager::UpdateCutoutSafeArea %s, safeAreaWithRoot %s", safeArea.ToString().c_str(),
55 safeAreaWithRoot.ToString().c_str());
56 cutoutSafeArea_ = safeAreaWithRoot;
57 return true;
58 }
59
CheckSystemSafeArea(const SafeAreaInsets & safeArea)60 bool SafeAreaManager::CheckSystemSafeArea(const SafeAreaInsets& safeArea)
61 {
62 return systemSafeArea_ != safeArea;
63 }
64
UpdateSystemSafeArea(const SafeAreaInsets & safeArea)65 bool SafeAreaManager::UpdateSystemSafeArea(const SafeAreaInsets& safeArea)
66 {
67 if (systemSafeArea_ == safeArea) {
68 return false;
69 }
70 ACE_SCOPED_TRACE("SafeAreaManager::UpdateSystemSafeArea %s", safeArea.ToString().c_str());
71 systemSafeArea_ = safeArea;
72 return true;
73 }
74
CheckNavSafeArea(const SafeAreaInsets & safeArea)75 bool SafeAreaManager::CheckNavSafeArea(const SafeAreaInsets& safeArea)
76 {
77 return navSafeArea_ != safeArea;
78 }
79
UpdateNavSafeArea(const SafeAreaInsets & safeArea)80 bool SafeAreaManager::UpdateNavSafeArea(const SafeAreaInsets& safeArea)
81 {
82 if (navSafeArea_ == safeArea) {
83 return false;
84 }
85 ACE_SCOPED_TRACE("SafeAreaManager::UpdateNavSafeArea %s", safeArea.ToString().c_str());
86 navSafeArea_ = safeArea;
87 return true;
88 }
89
UpdateKeyboardSafeArea(float keyboardHeight,std::optional<uint32_t> rootHeight)90 bool SafeAreaManager::UpdateKeyboardSafeArea(float keyboardHeight, std::optional<uint32_t> rootHeight)
91 {
92 uint32_t bottom;
93 auto container = Container::Current();
94 if (container && systemSafeArea_.bottom_.IsValid() && !container->IsSceneBoardEnabled()) {
95 bottom = systemSafeArea_.bottom_.start;
96 ACE_SCOPED_TRACE("calc keyboardRect use systemSafeArea_.bottom_");
97 } else {
98 bottom = rootHeight.has_value() ? rootHeight.value() : PipelineContext::GetCurrentRootHeight();
99 }
100 SafeAreaInsets::Inset inset = { .start = bottom - keyboardHeight, .end = bottom };
101 if (inset == keyboardInset_) {
102 return false;
103 }
104 keyboardInset_ = inset;
105 ACE_SCOPED_TRACE("SafeAreaManager::UpdateKeyboardSafeArea %s", inset.ToString().c_str());
106 return true;
107 }
108
GetCombinedSafeArea(const SafeAreaExpandOpts & opts) const109 SafeAreaInsets SafeAreaManager::GetCombinedSafeArea(const SafeAreaExpandOpts& opts) const
110 {
111 SafeAreaInsets res;
112 if (!IsSafeAreaValid()) {
113 return {};
114 }
115 if ((opts.type & SAFE_AREA_TYPE_CUTOUT) && useCutout_) {
116 res = res.Combine(cutoutSafeArea_);
117 }
118 if (opts.type & SAFE_AREA_TYPE_SYSTEM) {
119 res = res.Combine(systemSafeArea_).Combine(navSafeArea_);
120 }
121 if (keyboardAvoidMode_ == KeyBoardAvoidMode::NONE) {
122 return res;
123 }
124 if ((keyboardAvoidMode_ == KeyBoardAvoidMode::RESIZE ||
125 keyboardAvoidMode_ == KeyBoardAvoidMode::RESIZE_WITH_CARET) && (opts.type & SAFE_AREA_TYPE_KEYBOARD)) {
126 res.bottom_ = res.bottom_.Combine(keyboardInset_);
127 }
128 return res;
129 }
130
UpdateScbSystemSafeArea(const SafeAreaInsets & safeArea)131 bool SafeAreaManager::UpdateScbSystemSafeArea(const SafeAreaInsets& safeArea)
132 {
133 if (scbSystemSafeArea_.has_value() && scbSystemSafeArea_.value() == safeArea) {
134 return false;
135 }
136 ACE_SCOPED_TRACE("SafeAreaManager::UpdateScbSystemSafeArea %s", safeArea.ToString().c_str());
137 scbSystemSafeArea_ = safeArea;
138 return true;
139 }
140
UpdateScbCutoutSafeArea(const SafeAreaInsets & safeArea,NG::OptionalSize<uint32_t> rootSize)141 bool SafeAreaManager::UpdateScbCutoutSafeArea(const SafeAreaInsets& safeArea, NG::OptionalSize<uint32_t> rootSize)
142 {
143 auto safeAreaWithRoot = GenerateCutOutAreaWithRoot(safeArea, rootSize);
144 if (scbCutoutSafeArea_.has_value() && scbCutoutSafeArea_.value() == safeAreaWithRoot) {
145 return false;
146 }
147 ACE_SCOPED_TRACE("SafeAreaManager::UpdateScbCutoutSafeArea %s", safeAreaWithRoot.ToString().c_str());
148 scbCutoutSafeArea_ = safeAreaWithRoot;
149 return true;
150 }
151
UpdateScbNavSafeArea(const SafeAreaInsets & safeArea)152 bool SafeAreaManager::UpdateScbNavSafeArea(const SafeAreaInsets& safeArea)
153 {
154 if (scbNavSafeArea_.has_value() && scbNavSafeArea_.value() == safeArea) {
155 return false;
156 }
157 ACE_SCOPED_TRACE("SafeAreaManager::UpdateScbNavSafeArea %s", safeArea.ToString().c_str());
158 scbNavSafeArea_ = safeArea;
159 return true;
160 }
161
IsSafeAreaValid() const162 bool SafeAreaManager::IsSafeAreaValid() const
163 {
164 #ifdef PREVIEW
165 return !ignoreSafeArea_;
166 #else
167 return !(ignoreSafeArea_ || (!isFullScreen_ && !isNeedAvoidWindow_));
168 #endif
169 }
170
SetIsFullScreen(bool value)171 bool SafeAreaManager::SetIsFullScreen(bool value)
172 {
173 if (isFullScreen_ == value) {
174 return false;
175 }
176 isFullScreen_ = value;
177 TAG_LOGI(ACE_LAYOUT, "SetIsFullScreen %{public}d", isFullScreen_);
178 return true;
179 }
180
SetIsNeedAvoidWindow(bool value)181 bool SafeAreaManager::SetIsNeedAvoidWindow(bool value)
182 {
183 if (isNeedAvoidWindow_ == value) {
184 return false;
185 }
186 isNeedAvoidWindow_ = value;
187 TAG_LOGI(ACE_LAYOUT, "SetIsNeedAvoidWindow %{public}d", isNeedAvoidWindow_);
188 return true;
189 }
190
SetIgnoreSafeArea(bool value)191 bool SafeAreaManager::SetIgnoreSafeArea(bool value)
192 {
193 if (ignoreSafeArea_ == value) {
194 return false;
195 }
196 ignoreSafeArea_ = value;
197 TAG_LOGI(ACE_LAYOUT, "SetIgnoreSafeArea %{public}d", ignoreSafeArea_);
198 return true;
199 }
200
SetKeyBoardAvoidMode(KeyBoardAvoidMode value)201 bool SafeAreaManager::SetKeyBoardAvoidMode(KeyBoardAvoidMode value)
202 {
203 if (keyboardAvoidMode_ == value) {
204 return false;
205 }
206 if (keyboardAvoidMode_ == KeyBoardAvoidMode::NONE || value == KeyBoardAvoidMode::NONE) {
207 keyboardOffset_ = 0.0f;
208 }
209 keyboardAvoidMode_ = value;
210 keyboardSafeAreaEnabled_ = keyboardAvoidMode_ == KeyBoardAvoidMode::RESIZE
211 || keyboardAvoidMode_ == KeyBoardAvoidMode::RESIZE_WITH_CARET;
212 TAG_LOGI(ACE_LAYOUT, "SetKeyBoardAvoidMode %{public}d", keyboardAvoidMode_);
213 return true;
214 }
215
GetKeyBoardAvoidMode()216 KeyBoardAvoidMode SafeAreaManager::GetKeyBoardAvoidMode()
217 {
218 return keyboardAvoidMode_;
219 }
220
SetIsAtomicService(bool value)221 bool SafeAreaManager::SetIsAtomicService(bool value)
222 {
223 if (isAtomicService_ == value) {
224 return false;
225 }
226 isAtomicService_ = value;
227 TAG_LOGI(ACE_LAYOUT, "SetIsAtomicService %{public}d", isAtomicService_);
228 return true;
229 }
230
IsAtomicService() const231 bool SafeAreaManager::IsAtomicService() const
232 {
233 return isAtomicService_;
234 }
235
GetSystemSafeArea() const236 SafeAreaInsets SafeAreaManager::GetSystemSafeArea() const
237 {
238 if (windowTypeConfig_.isSceneBoardWindow && scbSystemSafeArea_.has_value()) {
239 return scbSystemSafeArea_.value();
240 }
241 return systemSafeArea_;
242 }
243
GetCutoutSafeArea() const244 SafeAreaInsets SafeAreaManager::GetCutoutSafeArea() const
245 {
246 if (IsSafeAreaValid() && useCutout_) {
247 if (windowTypeConfig_.isSceneBoardWindow && scbCutoutSafeArea_.has_value()) {
248 return scbCutoutSafeArea_.value();
249 }
250 return cutoutSafeArea_;
251 }
252 return {};
253 }
254
GetCutoutSafeAreaWithoutProcess() const255 SafeAreaInsets SafeAreaManager::GetCutoutSafeAreaWithoutProcess() const
256 {
257 if (windowTypeConfig_.isSceneBoardWindow && scbCutoutSafeArea_.has_value()) {
258 return scbCutoutSafeArea_.value();
259 }
260 return cutoutSafeArea_;
261 }
262
GetSafeArea() const263 SafeAreaInsets SafeAreaManager::GetSafeArea() const
264 {
265 if (!IsSafeAreaValid()) {
266 return {};
267 }
268 auto cutoutSafeArea = useCutout_ ? cutoutSafeArea_ : SafeAreaInsets();
269 return systemSafeArea_.Combine(cutoutSafeArea).Combine(navSafeArea_);
270 }
271
GetSafeAreaWithoutCutout() const272 SafeAreaInsets SafeAreaManager::GetSafeAreaWithoutCutout() const
273 {
274 if (!IsSafeAreaValid()) {
275 return {};
276 }
277 return systemSafeArea_.Combine(navSafeArea_);
278 }
279
GetSafeAreaWithoutProcess() const280 SafeAreaInsets SafeAreaManager::GetSafeAreaWithoutProcess() const
281 {
282 auto cutoutSafeArea = useCutout_ ? cutoutSafeArea_ : SafeAreaInsets();
283 if (!windowTypeConfig_.isSceneBoardWindow) {
284 return systemSafeArea_.Combine(cutoutSafeArea).Combine(navSafeArea_);
285 }
286 SafeAreaInsets scbSafeArea;
287 if (scbSystemSafeArea_.has_value()) {
288 scbSafeArea = scbSafeArea.Combine(scbSystemSafeArea_.value());
289 }
290 if (scbCutoutSafeArea_.has_value() && useCutout_) {
291 scbSafeArea = scbSafeArea.Combine(scbCutoutSafeArea_.value());
292 }
293 if (scbNavSafeArea_.has_value()) {
294 scbSafeArea = scbSafeArea.Combine(scbNavSafeArea_.value());
295 }
296 return scbSafeArea;
297 }
298
SafeAreaToPadding(bool withoutProcess)299 PaddingPropertyF SafeAreaManager::SafeAreaToPadding(bool withoutProcess)
300 {
301 if (!withoutProcess) {
302 #ifdef PREVIEW
303 if (ignoreSafeArea_) {
304 return {};
305 }
306 #else
307 if (ignoreSafeArea_ || (!isFullScreen_ && !isNeedAvoidWindow_)) {
308 return {};
309 }
310 #endif
311 }
312 auto cutoutSafeArea = useCutout_ ? cutoutSafeArea_ : SafeAreaInsets();
313 auto combinedSafeArea = systemSafeArea_.Combine(cutoutSafeArea).Combine(navSafeArea_);
314 PaddingPropertyF result;
315 if (combinedSafeArea.left_.IsValid()) {
316 result.left = combinedSafeArea.left_.Length();
317 }
318 if (combinedSafeArea.top_.IsValid()) {
319 result.top = combinedSafeArea.top_.Length();
320 }
321 if (combinedSafeArea.right_.IsValid()) {
322 result.right = combinedSafeArea.right_.Length();
323 }
324 if (combinedSafeArea.bottom_.IsValid()) {
325 result.bottom = combinedSafeArea.bottom_.Length();
326 }
327 return result;
328 }
329
GetKeyboardOffset(bool withoutProcess) const330 float SafeAreaManager::GetKeyboardOffset(bool withoutProcess) const
331 {
332 if (withoutProcess) {
333 return keyboardOffset_;
334 }
335 if (keyboardAvoidMode_ == KeyBoardAvoidMode::RESIZE ||
336 keyboardAvoidMode_ == KeyBoardAvoidMode::RESIZE_WITH_CARET ||
337 keyboardAvoidMode_ == KeyBoardAvoidMode::NONE) {
338 return 0.0f;
339 }
340 return keyboardOffset_;
341 }
342
GetWindowWrapperOffset()343 OffsetF SafeAreaManager::GetWindowWrapperOffset()
344 {
345 auto pipelineContext = PipelineContext::GetCurrentContext();
346 CHECK_NULL_RETURN(pipelineContext, OffsetF());
347 auto windowManager = pipelineContext->GetWindowManager();
348 auto isContainerModal = pipelineContext->GetWindowModal() == WindowModal::CONTAINER_MODAL && windowManager &&
349 windowManager->GetWindowMode() == WindowMode::WINDOW_MODE_FLOATING;
350 if (isContainerModal) {
351 auto wrapperOffset = OffsetF(static_cast<float>((CONTAINER_BORDER_WIDTH + CONTENT_PADDING).ConvertToPx()),
352 static_cast<float>((pipelineContext->GetCustomTitleHeight() + CONTAINER_BORDER_WIDTH).ConvertToPx()));
353 return wrapperOffset;
354 }
355 return OffsetF();
356 }
357
ExpandSafeArea()358 void SafeAreaManager::ExpandSafeArea()
359 {
360 ACE_SCOPED_TRACE("ExpandSafeArea node count %zu, IsSafeAreaValid: %d, ignoreSafeArea: %d, isFullScreen: %d, "
361 "isNeedAvoidWindow %d",
362 needExpandNodes_.size(), IsSafeAreaValid(), ignoreSafeArea_, isFullScreen_, isNeedAvoidWindow_);
363 auto pipeline = PipelineContext::GetCurrentContext();
364 CHECK_NULL_VOID(pipeline);
365 auto manager = pipeline->GetSafeAreaManager();
366 auto iter = needExpandNodes_.begin();
367 while (iter != needExpandNodes_.end()) {
368 auto frameNode = (*iter).Upgrade();
369 if (frameNode) {
370 manager->AddGeoRestoreNode(frameNode);
371 frameNode->ExpandSafeArea();
372 }
373 ++iter;
374 }
375 ClearNeedExpandNode();
376 }
377
AddNodeToExpandListIfNeeded(const WeakPtr<FrameNode> & node)378 bool SafeAreaManager::AddNodeToExpandListIfNeeded(const WeakPtr<FrameNode>& node)
379 {
380 if (needExpandNodes_.find(node) == needExpandNodes_.end()) {
381 AddNeedExpandNode(node);
382 return true;
383 }
384 return false;
385 }
386
GetExpandNodeSet()387 std::vector<WeakPtr<FrameNode>> SafeAreaManager::GetExpandNodeSet()
388 {
389 // To isolate set comparator, use vector to collect a copy of nodes
390 std::vector<WeakPtr<FrameNode>> result;
391 std::copy(needExpandNodes_.begin(), needExpandNodes_.end(), std::back_inserter(result));
392 return result;
393 }
394
SetKeyboardInfo(float height)395 void SafeAreaManager::SetKeyboardInfo(float height)
396 {
397 SetRawKeyboardHeight(height);
398 keyboardOrientation_ = -1;
399 auto container = Container::Current();
400 CHECK_NULL_VOID(container);
401 auto displayInfo = container->GetDisplayInfo();
402 CHECK_NULL_VOID(displayInfo);
403 keyboardOrientation_ = static_cast<int32_t>(displayInfo->GetRotation());
404 }
405
CheckPageNeedAvoidKeyboard(const RefPtr<FrameNode> & frameNode)406 bool SafeAreaManager::CheckPageNeedAvoidKeyboard(const RefPtr<FrameNode>& frameNode)
407 {
408 if (frameNode->GetTag() != V2::PAGE_ETS_TAG) {
409 return false;
410 }
411 // page will not avoid keyboard when lastChild is sheet
412 RefPtr<OverlayManager> overlay;
413 if (frameNode->RootNodeIsPage()) {
414 auto pattern = frameNode->GetPattern<PagePattern>();
415 CHECK_NULL_RETURN(pattern, true);
416 overlay = pattern->GetOverlayManager();
417 } else {
418 auto navNode = FrameNode::GetFrameNode(V2::NAVDESTINATION_VIEW_ETS_TAG, frameNode->GetRootNodeId());
419 CHECK_NULL_RETURN(navNode, true);
420 auto pattern = navNode->GetPattern<NavDestinationPattern>();
421 CHECK_NULL_RETURN(pattern, true);
422 overlay = pattern->GetOverlayManager();
423 }
424 CHECK_NULL_RETURN(overlay, true);
425 return overlay->CheckPageNeedAvoidKeyboard();
426 }
427 } // namespace OHOS::Ace::NG
428