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 "base/utils/utils.h"
18 #include "core/components/container_modal/container_modal_constants.h"
19 #include "core/components_ng/pattern/navrouter/navdestination_pattern.h"
20 #include "core/components_ng/pattern/stage/page_pattern.h"
21 #include "core/components_ng/property/safe_area_insets.h"
22 #include "core/pipeline_ng/pipeline_context.h"
23
24 namespace OHOS::Ace::NG {
GenerateCutOutAreaWithRoot(const SafeAreaInsets & safeArea,NG::OptionalSize<uint32_t> rootSize)25 SafeAreaInsets GenerateCutOutAreaWithRoot(const SafeAreaInsets& safeArea, NG::OptionalSize<uint32_t> rootSize)
26 {
27 auto pipeline = PipelineContext::GetCurrentContext();
28 CHECK_NULL_RETURN(pipeline, {});
29 CHECK_NULL_RETURN(pipeline->GetUseCutout(), {});
30 // cutout regions adjacent to edges.
31 auto cutoutArea = safeArea;
32
33 if (cutoutArea.top_.IsValid()) {
34 cutoutArea.top_.start = 0;
35 }
36 if (safeArea.bottom_.IsValid()) {
37 cutoutArea.bottom_.end = rootSize.Height().has_value() ? rootSize.Height().value()
38 : PipelineContext::GetCurrentRootHeight();
39 }
40 if (cutoutArea.left_.IsValid()) {
41 cutoutArea.left_.start = 0;
42 }
43 if (cutoutArea.right_.IsValid()) {
44 cutoutArea.right_.end = rootSize.Width().has_value() ? rootSize.Width().value()
45 : PipelineContext::GetCurrentRootWidth();
46 }
47 return cutoutArea;
48 }
49
CheckCutoutSafeArea(const SafeAreaInsets & safeArea,NG::OptionalSize<uint32_t> rootSize)50 bool SafeAreaManager::CheckCutoutSafeArea(const SafeAreaInsets& safeArea, NG::OptionalSize<uint32_t> rootSize)
51 {
52 return cutoutSafeArea_ != GenerateCutOutAreaWithRoot(safeArea, rootSize);
53 }
54
UpdateCutoutSafeArea(const SafeAreaInsets & safeArea,NG::OptionalSize<uint32_t> rootSize)55 bool SafeAreaManager::UpdateCutoutSafeArea(const SafeAreaInsets& safeArea, NG::OptionalSize<uint32_t> rootSize)
56 {
57 auto safeAreaWithRoot = GenerateCutOutAreaWithRoot(safeArea, rootSize);
58 if (cutoutSafeArea_ == safeAreaWithRoot) {
59 return false;
60 }
61 ACE_SCOPED_TRACE("SafeAreaManager::UpdateCutoutSafeArea %s", safeAreaWithRoot.ToString().c_str());
62 cutoutSafeArea_ = safeAreaWithRoot;
63 return true;
64 }
65
CheckSystemSafeArea(const SafeAreaInsets & safeArea)66 bool SafeAreaManager::CheckSystemSafeArea(const SafeAreaInsets& safeArea)
67 {
68 return systemSafeArea_ != safeArea;
69 }
70
UpdateSystemSafeArea(const SafeAreaInsets & safeArea)71 bool SafeAreaManager::UpdateSystemSafeArea(const SafeAreaInsets& safeArea)
72 {
73 if (systemSafeArea_ == safeArea) {
74 return false;
75 }
76 ACE_SCOPED_TRACE("SafeAreaManager::UpdateSystemSafeArea %s", safeArea.ToString().c_str());
77 systemSafeArea_ = safeArea;
78 return true;
79 }
80
CheckNavArea(const SafeAreaInsets & safeArea)81 bool SafeAreaManager::CheckNavArea(const SafeAreaInsets& safeArea)
82 {
83 return navSafeArea_ != safeArea;
84 }
85
UpdateNavArea(const SafeAreaInsets & safeArea)86 bool SafeAreaManager::UpdateNavArea(const SafeAreaInsets& safeArea)
87 {
88 if (navSafeArea_ == safeArea) {
89 return false;
90 }
91 ACE_SCOPED_TRACE("SafeAreaManager::UpdateNavArea %s", safeArea.ToString().c_str());
92 navSafeArea_ = safeArea;
93 return true;
94 }
95
UpdateKeyboardSafeArea(float keyboardHeight,std::optional<uint32_t> rootHeight)96 bool SafeAreaManager::UpdateKeyboardSafeArea(float keyboardHeight, std::optional<uint32_t> rootHeight)
97 {
98 uint32_t bottom;
99 if (systemSafeArea_.bottom_.IsValid()) {
100 bottom = systemSafeArea_.bottom_.start;
101 } else {
102 bottom = rootHeight.has_value() ? rootHeight.value() : PipelineContext::GetCurrentRootHeight();
103 }
104 SafeAreaInsets::Inset inset = { .start = bottom - keyboardHeight, .end = bottom };
105 if (inset == keyboardInset_) {
106 return false;
107 }
108 keyboardInset_ = inset;
109 return true;
110 }
111
GetCombinedSafeArea(const SafeAreaExpandOpts & opts) const112 SafeAreaInsets SafeAreaManager::GetCombinedSafeArea(const SafeAreaExpandOpts& opts) const
113 {
114 SafeAreaInsets res;
115 if (!IsSafeAreaValid()) {
116 return {};
117 }
118 if (opts.type & SAFE_AREA_TYPE_CUTOUT) {
119 res = res.Combine(cutoutSafeArea_);
120 }
121 if (opts.type & SAFE_AREA_TYPE_SYSTEM) {
122 res = res.Combine(systemSafeArea_).Combine(navSafeArea_);
123 }
124 if (keyboardAvoidMode_ == KeyBoardAvoidMode::NONE) {
125 return res;
126 }
127 if ((keyboardAvoidMode_ == KeyBoardAvoidMode::RESIZE ||
128 keyboardAvoidMode_ == KeyBoardAvoidMode::RESIZE_WITH_CARET) && (opts.type & SAFE_AREA_TYPE_KEYBOARD)) {
129 res.bottom_ = res.bottom_.Combine(keyboardInset_);
130 }
131 return res;
132 }
133
IsSafeAreaValid() const134 bool SafeAreaManager::IsSafeAreaValid() const
135 {
136 #ifdef PREVIEW
137 return !ignoreSafeArea_;
138 #else
139 return !(ignoreSafeArea_ || (!isFullScreen_ && !isNeedAvoidWindow_));
140 #endif
141 }
142
SetIsFullScreen(bool value)143 bool SafeAreaManager::SetIsFullScreen(bool value)
144 {
145 if (isFullScreen_ == value) {
146 return false;
147 }
148 isFullScreen_ = value;
149 LOGI("SafeAreaManager::SetIsFullScreen %{public}d", isFullScreen_);
150 return true;
151 }
152
SetIsNeedAvoidWindow(bool value)153 bool SafeAreaManager::SetIsNeedAvoidWindow(bool value)
154 {
155 if (isNeedAvoidWindow_ == value) {
156 return false;
157 }
158 isNeedAvoidWindow_ = value;
159 LOGI("SafeAreaManager::SetIsNeedAvoidWindow %{public}d", isNeedAvoidWindow_);
160 return true;
161 }
162
SetIgnoreSafeArea(bool value)163 bool SafeAreaManager::SetIgnoreSafeArea(bool value)
164 {
165 if (ignoreSafeArea_ == value) {
166 return false;
167 }
168 ignoreSafeArea_ = value;
169 LOGI("SafeAreaManager::SetIgnoreSafeArea %{public}d", ignoreSafeArea_);
170 return true;
171 }
172
SetKeyBoardAvoidMode(KeyBoardAvoidMode value)173 bool SafeAreaManager::SetKeyBoardAvoidMode(KeyBoardAvoidMode value)
174 {
175 if (keyboardAvoidMode_ == value) {
176 return false;
177 }
178 if (keyboardAvoidMode_ == KeyBoardAvoidMode::NONE || value == KeyBoardAvoidMode::NONE) {
179 keyboardOffset_ = 0.0f;
180 }
181 keyboardAvoidMode_ = value;
182 keyboardSafeAreaEnabled_ = keyboardAvoidMode_ == KeyBoardAvoidMode::RESIZE
183 || keyboardAvoidMode_ == KeyBoardAvoidMode::RESIZE_WITH_CARET;
184 TAG_LOGI(ACE_LAYOUT, "SetKeyBoardAvoidMode %{public}d", keyboardAvoidMode_);
185 return true;
186 }
187
GetKeyBoardAvoidMode()188 KeyBoardAvoidMode SafeAreaManager::GetKeyBoardAvoidMode()
189 {
190 return keyboardAvoidMode_;
191 }
192
SetIsAtomicService(bool value)193 bool SafeAreaManager::SetIsAtomicService(bool value)
194 {
195 if (isAtomicService_ == value) {
196 return false;
197 }
198 isAtomicService_ = value;
199 LOGI("SafeAreaManager::SetIsAtomicService %{public}d", isAtomicService_);
200 return true;
201 }
202
IsAtomicService() const203 bool SafeAreaManager::IsAtomicService() const
204 {
205 return isAtomicService_;
206 }
207
GetSystemSafeArea() const208 SafeAreaInsets SafeAreaManager::GetSystemSafeArea() const
209 {
210 return systemSafeArea_;
211 }
212
GetCutoutSafeArea() const213 SafeAreaInsets SafeAreaManager::GetCutoutSafeArea() const
214 {
215 if (!IsSafeAreaValid()) {
216 return {};
217 }
218 return cutoutSafeArea_;
219 }
220
GetSafeArea() const221 SafeAreaInsets SafeAreaManager::GetSafeArea() const
222 {
223 if (!IsSafeAreaValid()) {
224 return {};
225 }
226 return systemSafeArea_.Combine(cutoutSafeArea_).Combine(navSafeArea_);
227 }
228
GetSafeAreaWithoutCutout() const229 SafeAreaInsets SafeAreaManager::GetSafeAreaWithoutCutout() const
230 {
231 if (!IsSafeAreaValid()) {
232 return {};
233 }
234 return systemSafeArea_.Combine(navSafeArea_);
235 }
236
GetSafeAreaWithoutProcess() const237 SafeAreaInsets SafeAreaManager::GetSafeAreaWithoutProcess() const
238 {
239 return systemSafeArea_.Combine(cutoutSafeArea_).Combine(navSafeArea_);
240 }
241
SafeAreaToPadding(bool withoutProcess)242 PaddingPropertyF SafeAreaManager::SafeAreaToPadding(bool withoutProcess)
243 {
244 if (!withoutProcess) {
245 #ifdef PREVIEW
246 if (ignoreSafeArea_) {
247 return {};
248 }
249 #else
250 if (ignoreSafeArea_ || (!isFullScreen_ && !isNeedAvoidWindow_)) {
251 return {};
252 }
253 #endif
254 }
255 auto combinedSafeArea = systemSafeArea_.Combine(cutoutSafeArea_).Combine(navSafeArea_);
256 PaddingPropertyF result;
257 if (combinedSafeArea.left_.IsValid()) {
258 result.left = combinedSafeArea.left_.Length();
259 }
260 if (combinedSafeArea.top_.IsValid()) {
261 result.top = combinedSafeArea.top_.Length();
262 }
263 if (combinedSafeArea.right_.IsValid()) {
264 result.right = combinedSafeArea.right_.Length();
265 }
266 if (combinedSafeArea.bottom_.IsValid()) {
267 result.bottom = combinedSafeArea.bottom_.Length();
268 }
269 return result;
270 }
271
GetKeyboardOffset(bool withoutProcess) const272 float SafeAreaManager::GetKeyboardOffset(bool withoutProcess) const
273 {
274 if (withoutProcess) {
275 return keyboardOffset_;
276 }
277 if (keyboardAvoidMode_ == KeyBoardAvoidMode::RESIZE ||
278 keyboardAvoidMode_ == KeyBoardAvoidMode::RESIZE_WITH_CARET ||
279 keyboardAvoidMode_ == KeyBoardAvoidMode::NONE) {
280 return 0.0f;
281 }
282 return keyboardOffset_;
283 }
284
GetWindowWrapperOffset()285 OffsetF SafeAreaManager::GetWindowWrapperOffset()
286 {
287 auto pipelineContext = PipelineContext::GetCurrentContext();
288 CHECK_NULL_RETURN(pipelineContext, OffsetF());
289 auto windowManager = pipelineContext->GetWindowManager();
290 auto isContainerModal = pipelineContext->GetWindowModal() == WindowModal::CONTAINER_MODAL && windowManager &&
291 windowManager->GetWindowMode() == WindowMode::WINDOW_MODE_FLOATING;
292 if (isContainerModal) {
293 auto wrapperOffset = OffsetF(static_cast<float>((CONTAINER_BORDER_WIDTH + CONTENT_PADDING).ConvertToPx()),
294 static_cast<float>((pipelineContext->GetCustomTitleHeight() + CONTAINER_BORDER_WIDTH).ConvertToPx()));
295 return wrapperOffset;
296 }
297 return OffsetF();
298 }
299
ExpandSafeArea()300 void SafeAreaManager::ExpandSafeArea()
301 {
302 ACE_SCOPED_TRACE("ExpandSafeArea node count %zu, IsSafeAreaValid: %d, ignoreSafeArea: %d, isFullScreen: %d, "
303 "isNeedAvoidWindow %d",
304 needExpandNodes_.size(), IsSafeAreaValid(), ignoreSafeArea_, isFullScreen_, isNeedAvoidWindow_);
305 auto pipeline = PipelineContext::GetCurrentContext();
306 CHECK_NULL_VOID(pipeline);
307 auto manager = pipeline->GetSafeAreaManager();
308 auto iter = needExpandNodes_.begin();
309 while (iter != needExpandNodes_.end()) {
310 auto frameNode = (*iter).Upgrade();
311 if (frameNode) {
312 manager->AddGeoRestoreNode(frameNode);
313 frameNode->ExpandSafeArea();
314 }
315 ++iter;
316 }
317 ClearNeedExpandNode();
318 }
319
AddNodeToExpandListIfNeeded(const WeakPtr<FrameNode> & node)320 bool SafeAreaManager::AddNodeToExpandListIfNeeded(const WeakPtr<FrameNode>& node)
321 {
322 if (needExpandNodes_.find(node) == needExpandNodes_.end()) {
323 AddNeedExpandNode(node);
324 return true;
325 }
326 return false;
327 }
328
CheckPageNeedAvoidKeyboard(const RefPtr<FrameNode> & frameNode)329 bool SafeAreaManager::CheckPageNeedAvoidKeyboard(const RefPtr<FrameNode>& frameNode)
330 {
331 if (frameNode->GetTag() != V2::PAGE_ETS_TAG) {
332 return false;
333 }
334 // page will not avoid keyboard when lastChild is sheet
335 RefPtr<OverlayManager> overlay;
336 if (frameNode->RootNodeIsPage()) {
337 auto pattern = frameNode->GetPattern<PagePattern>();
338 CHECK_NULL_RETURN(pattern, true);
339 overlay = pattern->GetOverlayManager();
340 } else {
341 auto navNode = FrameNode::GetFrameNode(V2::NAVDESTINATION_VIEW_ETS_TAG, frameNode->GetRootNodeId());
342 CHECK_NULL_RETURN(navNode, true);
343 auto pattern = navNode->GetPattern<NavDestinationPattern>();
344 CHECK_NULL_RETURN(pattern, true);
345 overlay = pattern->GetOverlayManager();
346 }
347 CHECK_NULL_RETURN(overlay, true);
348 return overlay->CheckPageNeedAvoidKeyboard();
349 }
350 } // namespace OHOS::Ace::NG
351