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
IsModeResize()43 bool SafeAreaManager::IsModeResize()
44 {
45 return keyboardAvoidMode_ == KeyBoardAvoidMode::RESIZE ||
46 keyboardAvoidMode_ == KeyBoardAvoidMode::RESIZE_WITH_CARET;
47 }
48
IsModeOffset()49 bool SafeAreaManager::IsModeOffset()
50 {
51 return keyboardAvoidMode_ == KeyBoardAvoidMode::OFFSET ||
52 keyboardAvoidMode_ == KeyBoardAvoidMode::OFFSET_WITH_CARET;
53 }
54
CheckCutoutSafeArea(const SafeAreaInsets & safeArea,NG::OptionalSize<uint32_t> rootSize)55 bool SafeAreaManager::CheckCutoutSafeArea(const SafeAreaInsets& safeArea, NG::OptionalSize<uint32_t> rootSize)
56 {
57 return cutoutSafeArea_ != GenerateCutOutAreaWithRoot(safeArea, rootSize);
58 }
59
UpdateCutoutSafeArea(const SafeAreaInsets & safeArea,NG::OptionalSize<uint32_t> rootSize)60 bool SafeAreaManager::UpdateCutoutSafeArea(const SafeAreaInsets& safeArea, NG::OptionalSize<uint32_t> rootSize)
61 {
62 auto safeAreaWithRoot = GenerateCutOutAreaWithRoot(safeArea, rootSize);
63 if (cutoutSafeArea_ == safeAreaWithRoot) {
64 return false;
65 }
66 ACE_SCOPED_TRACE("SafeAreaManager::UpdateCutoutSafeArea %s, safeAreaWithRoot %s", safeArea.ToString().c_str(),
67 safeAreaWithRoot.ToString().c_str());
68 cutoutSafeArea_ = safeAreaWithRoot;
69 return true;
70 }
71
CheckSystemSafeArea(const SafeAreaInsets & safeArea)72 bool SafeAreaManager::CheckSystemSafeArea(const SafeAreaInsets& safeArea)
73 {
74 return systemSafeArea_ != safeArea;
75 }
76
UpdateSystemSafeArea(const SafeAreaInsets & safeArea)77 bool SafeAreaManager::UpdateSystemSafeArea(const SafeAreaInsets& safeArea)
78 {
79 if (systemSafeArea_ == safeArea) {
80 return false;
81 }
82 ACE_SCOPED_TRACE("SafeAreaManager::UpdateSystemSafeArea %s", safeArea.ToString().c_str());
83 systemSafeArea_ = safeArea;
84 return true;
85 }
86
CheckNavSafeArea(const SafeAreaInsets & safeArea)87 bool SafeAreaManager::CheckNavSafeArea(const SafeAreaInsets& safeArea)
88 {
89 return navSafeArea_ != safeArea;
90 }
91
UpdateNavSafeArea(const SafeAreaInsets & safeArea)92 bool SafeAreaManager::UpdateNavSafeArea(const SafeAreaInsets& safeArea)
93 {
94 if (navSafeArea_ == safeArea) {
95 return false;
96 }
97 ACE_SCOPED_TRACE("SafeAreaManager::UpdateNavSafeArea %s", safeArea.ToString().c_str());
98 navSafeArea_ = safeArea;
99 return true;
100 }
101
UpdateKeyboardSafeArea(float keyboardHeight,std::optional<uint32_t> rootHeight)102 bool SafeAreaManager::UpdateKeyboardSafeArea(float keyboardHeight, std::optional<uint32_t> rootHeight)
103 {
104 uint32_t bottom;
105 auto container = Container::Current();
106 if (container && systemSafeArea_.bottom_.IsValid() && !container->IsSceneBoardEnabled()) {
107 bottom = systemSafeArea_.bottom_.start;
108 ACE_SCOPED_TRACE("calc keyboardRect use systemSafeArea_.bottom_");
109 } else {
110 bottom = rootHeight.has_value() ? rootHeight.value() : PipelineContext::GetCurrentRootHeight();
111 }
112 SafeAreaInsets::Inset inset = { .start = bottom - keyboardHeight, .end = bottom };
113 if (inset == keyboardInset_) {
114 return false;
115 }
116 keyboardInset_ = inset;
117 ACE_SCOPED_TRACE("SafeAreaManager::UpdateKeyboardSafeArea %s", inset.ToString().c_str());
118 return true;
119 }
120
UpdateKeyboardWebSafeArea(float keyboardHeight,std::optional<uint32_t> rootHeight)121 bool SafeAreaManager::UpdateKeyboardWebSafeArea(float keyboardHeight, std::optional<uint32_t> rootHeight)
122 {
123 uint32_t bottom;
124 auto container = Container::Current();
125 if (container && systemSafeArea_.bottom_.IsValid() && !container->IsSceneBoardEnabled()) {
126 bottom = systemSafeArea_.bottom_.start;
127 ACE_SCOPED_TRACE("calc keyboardWebRect use systemSafeArea_.bottom_");
128 } else {
129 bottom = rootHeight.has_value() ? rootHeight.value() : PipelineContext::GetCurrentRootHeight();
130 }
131 SafeAreaInsets::Inset inset = { .start = bottom - keyboardHeight, .end = bottom };
132 if (inset == keyboardWebInset_) {
133 return false;
134 }
135 keyboardWebInset_ = inset;
136 ACE_SCOPED_TRACE("SafeAreaManager::UpdateKeyboardWebSafeArea %s", inset.ToString().c_str());
137 return true;
138 }
139
GetCombinedSafeArea(const SafeAreaExpandOpts & opts) const140 SafeAreaInsets SafeAreaManager::GetCombinedSafeArea(const SafeAreaExpandOpts& opts) const
141 {
142 SafeAreaInsets res;
143 if (!IsSafeAreaValid()) {
144 return {};
145 }
146 if ((opts.type & SAFE_AREA_TYPE_CUTOUT) && useCutout_) {
147 res = res.Combine(cutoutSafeArea_);
148 }
149 if (opts.type & SAFE_AREA_TYPE_SYSTEM) {
150 res = res.Combine(systemSafeArea_).Combine(navSafeArea_);
151 }
152 if (keyboardAvoidMode_ == KeyBoardAvoidMode::NONE) {
153 return res;
154 }
155 if ((keyboardAvoidMode_ == KeyBoardAvoidMode::RESIZE ||
156 keyboardAvoidMode_ == KeyBoardAvoidMode::RESIZE_WITH_CARET) && (opts.type & SAFE_AREA_TYPE_KEYBOARD)) {
157 res.bottom_ = res.bottom_.Combine(keyboardInset_);
158 }
159 return res;
160 }
161
UpdateScbSystemSafeArea(const SafeAreaInsets & safeArea)162 bool SafeAreaManager::UpdateScbSystemSafeArea(const SafeAreaInsets& safeArea)
163 {
164 if (scbSystemSafeArea_.has_value() && scbSystemSafeArea_.value() == safeArea) {
165 return false;
166 }
167 ACE_SCOPED_TRACE("SafeAreaManager::UpdateScbSystemSafeArea %s", safeArea.ToString().c_str());
168 scbSystemSafeArea_ = safeArea;
169 return true;
170 }
171
UpdateScbCutoutSafeArea(const SafeAreaInsets & safeArea,NG::OptionalSize<uint32_t> rootSize)172 bool SafeAreaManager::UpdateScbCutoutSafeArea(const SafeAreaInsets& safeArea, NG::OptionalSize<uint32_t> rootSize)
173 {
174 auto safeAreaWithRoot = GenerateCutOutAreaWithRoot(safeArea, rootSize);
175 if (scbCutoutSafeArea_.has_value() && scbCutoutSafeArea_.value() == safeAreaWithRoot) {
176 return false;
177 }
178 ACE_SCOPED_TRACE("SafeAreaManager::UpdateScbCutoutSafeArea %s", safeAreaWithRoot.ToString().c_str());
179 scbCutoutSafeArea_ = safeAreaWithRoot;
180 return true;
181 }
182
UpdateScbNavSafeArea(const SafeAreaInsets & safeArea)183 bool SafeAreaManager::UpdateScbNavSafeArea(const SafeAreaInsets& safeArea)
184 {
185 if (scbNavSafeArea_.has_value() && scbNavSafeArea_.value() == safeArea) {
186 return false;
187 }
188 ACE_SCOPED_TRACE("SafeAreaManager::UpdateScbNavSafeArea %s", safeArea.ToString().c_str());
189 scbNavSafeArea_ = safeArea;
190 return true;
191 }
192
IsSafeAreaValid() const193 bool SafeAreaManager::IsSafeAreaValid() const
194 {
195 #ifdef PREVIEW
196 return !ignoreSafeArea_;
197 #else
198 return !(ignoreSafeArea_ || (!isFullScreen_ && !isNeedAvoidWindow_));
199 #endif
200 }
201
SetIsFullScreen(bool value)202 bool SafeAreaManager::SetIsFullScreen(bool value)
203 {
204 if (isFullScreen_ == value) {
205 return false;
206 }
207 isFullScreen_ = value;
208 TAG_LOGI(ACE_SAFE_AREA, "SetIsFullScreen %{public}d", isFullScreen_);
209 return true;
210 }
211
SetIsNeedAvoidWindow(bool value)212 bool SafeAreaManager::SetIsNeedAvoidWindow(bool value)
213 {
214 if (isNeedAvoidWindow_ == value) {
215 return false;
216 }
217 isNeedAvoidWindow_ = value;
218 TAG_LOGI(ACE_SAFE_AREA, "SetIsNeedAvoidWindow %{public}d", isNeedAvoidWindow_);
219 return true;
220 }
221
SetIgnoreSafeArea(bool value)222 bool SafeAreaManager::SetIgnoreSafeArea(bool value)
223 {
224 if (ignoreSafeArea_ == value) {
225 return false;
226 }
227 ignoreSafeArea_ = value;
228 TAG_LOGI(ACE_SAFE_AREA, "SetIgnoreSafeArea %{public}d", ignoreSafeArea_);
229 return true;
230 }
231
SetKeyBoardAvoidMode(KeyBoardAvoidMode value)232 bool SafeAreaManager::SetKeyBoardAvoidMode(KeyBoardAvoidMode value)
233 {
234 if (keyboardAvoidMode_ == value) {
235 return false;
236 }
237 if (keyboardAvoidMode_ == KeyBoardAvoidMode::NONE || value == KeyBoardAvoidMode::NONE) {
238 keyboardOffset_ = 0.0f;
239 }
240 keyboardAvoidMode_ = value;
241 keyboardSafeAreaEnabled_ = keyboardAvoidMode_ == KeyBoardAvoidMode::RESIZE
242 || keyboardAvoidMode_ == KeyBoardAvoidMode::RESIZE_WITH_CARET;
243 TAG_LOGI(ACE_SAFE_AREA, "SetKeyBoardAvoidMode %{public}d", keyboardAvoidMode_);
244 return true;
245 }
246
GetKeyBoardAvoidMode()247 KeyBoardAvoidMode SafeAreaManager::GetKeyBoardAvoidMode()
248 {
249 return keyboardAvoidMode_;
250 }
251
SetIsAtomicService(bool value)252 bool SafeAreaManager::SetIsAtomicService(bool value)
253 {
254 if (isAtomicService_ == value) {
255 return false;
256 }
257 isAtomicService_ = value;
258 TAG_LOGI(ACE_SAFE_AREA, "SetIsAtomicService %{public}d", isAtomicService_);
259 return true;
260 }
261
IsAtomicService() const262 bool SafeAreaManager::IsAtomicService() const
263 {
264 return isAtomicService_;
265 }
266
GetSystemSafeArea() const267 SafeAreaInsets SafeAreaManager::GetSystemSafeArea() const
268 {
269 if (windowTypeConfig_.isSceneBoardWindow && scbSystemSafeArea_.has_value()) {
270 return scbSystemSafeArea_.value();
271 }
272 return systemSafeArea_;
273 }
274
GetCutoutSafeArea() const275 SafeAreaInsets SafeAreaManager::GetCutoutSafeArea() const
276 {
277 if (IsSafeAreaValid() && useCutout_) {
278 if (windowTypeConfig_.isSceneBoardWindow && scbCutoutSafeArea_.has_value()) {
279 return scbCutoutSafeArea_.value();
280 }
281 return cutoutSafeArea_;
282 }
283 return {};
284 }
285
GetCutoutSafeAreaWithoutProcess() const286 SafeAreaInsets SafeAreaManager::GetCutoutSafeAreaWithoutProcess() const
287 {
288 if (windowTypeConfig_.isSceneBoardWindow && scbCutoutSafeArea_.has_value()) {
289 return scbCutoutSafeArea_.value();
290 }
291 return cutoutSafeArea_;
292 }
293
GetSafeArea() const294 SafeAreaInsets SafeAreaManager::GetSafeArea() const
295 {
296 if (!IsSafeAreaValid()) {
297 return {};
298 }
299 auto cutoutSafeArea = useCutout_ ? cutoutSafeArea_ : SafeAreaInsets();
300 return systemSafeArea_.Combine(cutoutSafeArea).Combine(navSafeArea_);
301 }
302
GetSafeAreaWithoutCutout() const303 SafeAreaInsets SafeAreaManager::GetSafeAreaWithoutCutout() const
304 {
305 if (!IsSafeAreaValid()) {
306 return {};
307 }
308 return systemSafeArea_.Combine(navSafeArea_);
309 }
310
GetSafeAreaWithoutProcess() const311 SafeAreaInsets SafeAreaManager::GetSafeAreaWithoutProcess() const
312 {
313 auto cutoutSafeArea = useCutout_ ? cutoutSafeArea_ : SafeAreaInsets();
314 if (!windowTypeConfig_.isSceneBoardWindow) {
315 return systemSafeArea_.Combine(cutoutSafeArea).Combine(navSafeArea_);
316 }
317 SafeAreaInsets scbSafeArea;
318 if (scbSystemSafeArea_.has_value()) {
319 scbSafeArea = scbSafeArea.Combine(scbSystemSafeArea_.value());
320 }
321 if (scbCutoutSafeArea_.has_value() && useCutout_) {
322 scbSafeArea = scbSafeArea.Combine(scbCutoutSafeArea_.value());
323 }
324 if (scbNavSafeArea_.has_value()) {
325 scbSafeArea = scbSafeArea.Combine(scbNavSafeArea_.value());
326 }
327 return scbSafeArea;
328 }
329
SafeAreaToPadding(bool withoutProcess,LayoutSafeAreaType ignoreType)330 PaddingPropertyF SafeAreaManager::SafeAreaToPadding(bool withoutProcess, LayoutSafeAreaType ignoreType)
331 {
332 if (!withoutProcess) {
333 #ifdef PREVIEW
334 if (ignoreSafeArea_) {
335 return {};
336 }
337 #else
338 if (ignoreSafeArea_ || (!isFullScreen_ && !isNeedAvoidWindow_)) {
339 return {};
340 }
341 #endif
342 }
343 SafeAreaInsets combinedSafeArea;
344 auto cutoutSafeArea = useCutout_ ? cutoutSafeArea_ : SafeAreaInsets();
345
346 bool includeSystem = ignoreType & LAYOUT_SAFE_AREA_TYPE_SYSTEM;
347 bool includeKeyboard = ignoreType & LAYOUT_SAFE_AREA_TYPE_KEYBOARD;
348
349 if (includeSystem) {
350 combinedSafeArea = systemSafeArea_.Combine(cutoutSafeArea).Combine(navSafeArea_);
351 }
352 if (includeKeyboard) {
353 if (IsModeResize()) {
354 combinedSafeArea.bottom_ = combinedSafeArea.bottom_.Combine(keyboardInset_);
355 } else if (IsModeOffset()) {
356 auto keyboardHeight = keyboardInset_.Length();
357 auto bottomLength = GetSafeArea().bottom_.Length();
358 auto distance = bottomLength - GetKeyboardOffset(withoutProcess);
359 if (GreatNotEqual(keyboardHeight, 0.0f) && distance <= keyboardHeight) {
360 combinedSafeArea.bottom_ = GetSafeArea().bottom_;
361 }
362 }
363 }
364
365 PaddingPropertyF result;
366 if (combinedSafeArea.left_.IsValid()) {
367 result.left = combinedSafeArea.left_.Length();
368 }
369 if (combinedSafeArea.top_.IsValid()) {
370 result.top = combinedSafeArea.top_.Length();
371 }
372 if (combinedSafeArea.right_.IsValid()) {
373 result.right = combinedSafeArea.right_.Length();
374 }
375 if (combinedSafeArea.bottom_.IsValid()) {
376 result.bottom = combinedSafeArea.bottom_.Length();
377 }
378 return result;
379 }
380
GetKeyboardOffset(bool withoutProcess) const381 float SafeAreaManager::GetKeyboardOffset(bool withoutProcess) const
382 {
383 if (withoutProcess) {
384 return keyboardOffset_;
385 }
386 if (keyboardAvoidMode_ == KeyBoardAvoidMode::RESIZE ||
387 keyboardAvoidMode_ == KeyBoardAvoidMode::RESIZE_WITH_CARET ||
388 keyboardAvoidMode_ == KeyBoardAvoidMode::NONE) {
389 return 0.0f;
390 }
391 return keyboardOffset_;
392 }
393
GetWindowWrapperOffset()394 OffsetF SafeAreaManager::GetWindowWrapperOffset()
395 {
396 auto pipelineContext = PipelineContext::GetCurrentContext();
397 CHECK_NULL_RETURN(pipelineContext, OffsetF());
398 auto windowManager = pipelineContext->GetWindowManager();
399 auto isContainerModal = pipelineContext->GetWindowModal() == WindowModal::CONTAINER_MODAL && windowManager &&
400 windowManager->GetWindowMode() == WindowMode::WINDOW_MODE_FLOATING;
401 if (isContainerModal) {
402 auto wrapperOffset = OffsetF(static_cast<float>((CONTAINER_BORDER_WIDTH + CONTENT_PADDING).ConvertToPx()),
403 static_cast<float>((pipelineContext->GetCustomTitleHeight() + CONTAINER_BORDER_WIDTH).ConvertToPx()));
404 return wrapperOffset;
405 }
406 return OffsetF();
407 }
408
ExpandSafeArea()409 void SafeAreaManager::ExpandSafeArea()
410 {
411 ACE_SCOPED_TRACE("ExpandSafeArea node count %zu, IsSafeAreaValid: %d, ignoreSafeArea: %d, isFullScreen: %d, "
412 "isNeedAvoidWindow %d",
413 needExpandNodes_.size(), IsSafeAreaValid(), ignoreSafeArea_, isFullScreen_, isNeedAvoidWindow_);
414 auto pipeline = PipelineContext::GetCurrentContext();
415 CHECK_NULL_VOID(pipeline);
416 auto manager = pipeline->GetSafeAreaManager();
417 auto iter = needExpandNodes_.begin();
418 while (iter != needExpandNodes_.end()) {
419 auto frameNode = (*iter).Upgrade();
420 if (frameNode) {
421 manager->AddGeoRestoreNode(frameNode);
422 frameNode->ExpandSafeArea();
423 }
424 ++iter;
425 }
426 ClearNeedExpandNode();
427 }
428
AddNodeToExpandListIfNeeded(const WeakPtr<FrameNode> & node)429 bool SafeAreaManager::AddNodeToExpandListIfNeeded(const WeakPtr<FrameNode>& node)
430 {
431 if (needExpandNodes_.find(node) == needExpandNodes_.end()) {
432 AddNeedExpandNode(node);
433 return true;
434 }
435 return false;
436 }
437
GetExpandNodeSet()438 std::vector<WeakPtr<FrameNode>> SafeAreaManager::GetExpandNodeSet()
439 {
440 // To isolate set comparator, use vector to collect a copy of nodes
441 std::vector<WeakPtr<FrameNode>> result;
442 std::copy(needExpandNodes_.begin(), needExpandNodes_.end(), std::back_inserter(result));
443 return result;
444 }
445
SetKeyboardInfo(float height)446 void SafeAreaManager::SetKeyboardInfo(float height)
447 {
448 keyboardOrientation_ = -1;
449 auto container = Container::Current();
450 CHECK_NULL_VOID(container);
451 auto displayInfo = container->GetDisplayInfo();
452 CHECK_NULL_VOID(displayInfo);
453 auto keyboardOrientation = static_cast<int32_t>(displayInfo->GetRotation());
454 ACE_LAYOUT_SCOPED_TRACE("SetKeyboardInfo keyboardOrientation %d, rawKeyboardHeight %f",
455 keyboardOrientation, height);
456 SetRawKeyboardHeight(height);
457 keyboardOrientation_ = keyboardOrientation;
458 auto pipeline = container->GetPipelineContext();
459 CHECK_NULL_VOID(pipeline);
460 pipeline->OnRawKeyboardChangedCallback();
461 }
462
CheckPageNeedAvoidKeyboard(const RefPtr<FrameNode> & frameNode)463 bool SafeAreaManager::CheckPageNeedAvoidKeyboard(const RefPtr<FrameNode>& frameNode)
464 {
465 if (frameNode->GetTag() != V2::PAGE_ETS_TAG) {
466 return false;
467 }
468 // page will not avoid keyboard when lastChild is sheet
469 RefPtr<OverlayManager> overlay;
470 if (frameNode->RootNodeIsPage()) {
471 auto pattern = frameNode->GetPattern<PagePattern>();
472 CHECK_NULL_RETURN(pattern, true);
473 overlay = pattern->GetOverlayManager();
474 } else {
475 auto navNode = FrameNode::GetFrameNode(V2::NAVDESTINATION_VIEW_ETS_TAG, frameNode->GetRootNodeId());
476 CHECK_NULL_RETURN(navNode, true);
477 auto pattern = navNode->GetPattern<NavDestinationPattern>();
478 CHECK_NULL_RETURN(pattern, true);
479 overlay = pattern->GetOverlayManager();
480 }
481 CHECK_NULL_RETURN(overlay, true);
482 return overlay->CheckPageNeedAvoidKeyboard();
483 }
484 } // namespace OHOS::Ace::NG
485