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 auto container = Container::Current();
100 if (container && systemSafeArea_.bottom_.IsValid() && !container->IsSceneBoardEnabled()) {
101 bottom = systemSafeArea_.bottom_.start;
102 ACE_SCOPED_TRACE("calc keyboardRect use systemSafeArea_.bottom_");
103 } else {
104 bottom = rootHeight.has_value() ? rootHeight.value() : PipelineContext::GetCurrentRootHeight();
105 }
106 SafeAreaInsets::Inset inset = { .start = bottom - keyboardHeight, .end = bottom };
107 if (inset == keyboardInset_) {
108 return false;
109 }
110 keyboardInset_ = inset;
111 ACE_SCOPED_TRACE("SafeAreaManager::UpdateKeyboardSafeArea %s", inset.ToString().c_str());
112 return true;
113 }
114
GetCombinedSafeArea(const SafeAreaExpandOpts & opts) const115 SafeAreaInsets SafeAreaManager::GetCombinedSafeArea(const SafeAreaExpandOpts& opts) const
116 {
117 SafeAreaInsets res;
118 if (!IsSafeAreaValid()) {
119 return {};
120 }
121 if (opts.type & SAFE_AREA_TYPE_CUTOUT) {
122 res = res.Combine(cutoutSafeArea_);
123 }
124 if (opts.type & SAFE_AREA_TYPE_SYSTEM) {
125 res = res.Combine(systemSafeArea_).Combine(navSafeArea_);
126 }
127 if (keyboardAvoidMode_ == KeyBoardAvoidMode::NONE) {
128 return res;
129 }
130 if ((keyboardAvoidMode_ == KeyBoardAvoidMode::RESIZE ||
131 keyboardAvoidMode_ == KeyBoardAvoidMode::RESIZE_WITH_CARET) && (opts.type & SAFE_AREA_TYPE_KEYBOARD)) {
132 res.bottom_ = res.bottom_.Combine(keyboardInset_);
133 }
134 return res;
135 }
136
IsSafeAreaValid() const137 bool SafeAreaManager::IsSafeAreaValid() const
138 {
139 #ifdef PREVIEW
140 return !ignoreSafeArea_;
141 #else
142 return !(ignoreSafeArea_ || (!isFullScreen_ && !isNeedAvoidWindow_));
143 #endif
144 }
145
SetIsFullScreen(bool value)146 bool SafeAreaManager::SetIsFullScreen(bool value)
147 {
148 if (isFullScreen_ == value) {
149 return false;
150 }
151 isFullScreen_ = value;
152 LOGI("SafeAreaManager::SetIsFullScreen %{public}d", isFullScreen_);
153 return true;
154 }
155
SetIsNeedAvoidWindow(bool value)156 bool SafeAreaManager::SetIsNeedAvoidWindow(bool value)
157 {
158 if (isNeedAvoidWindow_ == value) {
159 return false;
160 }
161 isNeedAvoidWindow_ = value;
162 LOGI("SafeAreaManager::SetIsNeedAvoidWindow %{public}d", isNeedAvoidWindow_);
163 return true;
164 }
165
SetIgnoreSafeArea(bool value)166 bool SafeAreaManager::SetIgnoreSafeArea(bool value)
167 {
168 if (ignoreSafeArea_ == value) {
169 return false;
170 }
171 ignoreSafeArea_ = value;
172 LOGI("SafeAreaManager::SetIgnoreSafeArea %{public}d", ignoreSafeArea_);
173 return true;
174 }
175
SetKeyBoardAvoidMode(KeyBoardAvoidMode value)176 bool SafeAreaManager::SetKeyBoardAvoidMode(KeyBoardAvoidMode value)
177 {
178 if (keyboardAvoidMode_ == value) {
179 return false;
180 }
181 if (keyboardAvoidMode_ == KeyBoardAvoidMode::NONE || value == KeyBoardAvoidMode::NONE) {
182 keyboardOffset_ = 0.0f;
183 }
184 keyboardAvoidMode_ = value;
185 keyboardSafeAreaEnabled_ = keyboardAvoidMode_ == KeyBoardAvoidMode::RESIZE
186 || keyboardAvoidMode_ == KeyBoardAvoidMode::RESIZE_WITH_CARET;
187 TAG_LOGI(ACE_LAYOUT, "SetKeyBoardAvoidMode %{public}d", keyboardAvoidMode_);
188 return true;
189 }
190
GetKeyBoardAvoidMode()191 KeyBoardAvoidMode SafeAreaManager::GetKeyBoardAvoidMode()
192 {
193 return keyboardAvoidMode_;
194 }
195
SetIsAtomicService(bool value)196 bool SafeAreaManager::SetIsAtomicService(bool value)
197 {
198 if (isAtomicService_ == value) {
199 return false;
200 }
201 isAtomicService_ = value;
202 LOGI("SafeAreaManager::SetIsAtomicService %{public}d", isAtomicService_);
203 return true;
204 }
205
IsAtomicService() const206 bool SafeAreaManager::IsAtomicService() const
207 {
208 return isAtomicService_;
209 }
210
GetSystemSafeArea() const211 SafeAreaInsets SafeAreaManager::GetSystemSafeArea() const
212 {
213 return systemSafeArea_;
214 }
215
GetCutoutSafeArea() const216 SafeAreaInsets SafeAreaManager::GetCutoutSafeArea() const
217 {
218 if (!IsSafeAreaValid()) {
219 return {};
220 }
221 return cutoutSafeArea_;
222 }
223
GetSafeArea() const224 SafeAreaInsets SafeAreaManager::GetSafeArea() const
225 {
226 if (!IsSafeAreaValid()) {
227 return {};
228 }
229 return systemSafeArea_.Combine(cutoutSafeArea_).Combine(navSafeArea_);
230 }
231
GetSafeAreaWithoutCutout() const232 SafeAreaInsets SafeAreaManager::GetSafeAreaWithoutCutout() const
233 {
234 if (!IsSafeAreaValid()) {
235 return {};
236 }
237 return systemSafeArea_.Combine(navSafeArea_);
238 }
239
GetSafeAreaWithoutProcess() const240 SafeAreaInsets SafeAreaManager::GetSafeAreaWithoutProcess() const
241 {
242 return systemSafeArea_.Combine(cutoutSafeArea_).Combine(navSafeArea_);
243 }
244
SafeAreaToPadding(bool withoutProcess)245 PaddingPropertyF SafeAreaManager::SafeAreaToPadding(bool withoutProcess)
246 {
247 if (!withoutProcess) {
248 #ifdef PREVIEW
249 if (ignoreSafeArea_) {
250 return {};
251 }
252 #else
253 if (ignoreSafeArea_ || (!isFullScreen_ && !isNeedAvoidWindow_)) {
254 return {};
255 }
256 #endif
257 }
258 auto combinedSafeArea = systemSafeArea_.Combine(cutoutSafeArea_).Combine(navSafeArea_);
259 PaddingPropertyF result;
260 if (combinedSafeArea.left_.IsValid()) {
261 result.left = combinedSafeArea.left_.Length();
262 }
263 if (combinedSafeArea.top_.IsValid()) {
264 result.top = combinedSafeArea.top_.Length();
265 }
266 if (combinedSafeArea.right_.IsValid()) {
267 result.right = combinedSafeArea.right_.Length();
268 }
269 if (combinedSafeArea.bottom_.IsValid()) {
270 result.bottom = combinedSafeArea.bottom_.Length();
271 }
272 return result;
273 }
274
GetKeyboardOffset(bool withoutProcess) const275 float SafeAreaManager::GetKeyboardOffset(bool withoutProcess) const
276 {
277 if (withoutProcess) {
278 return keyboardOffset_;
279 }
280 if (keyboardAvoidMode_ == KeyBoardAvoidMode::RESIZE ||
281 keyboardAvoidMode_ == KeyBoardAvoidMode::RESIZE_WITH_CARET ||
282 keyboardAvoidMode_ == KeyBoardAvoidMode::NONE) {
283 return 0.0f;
284 }
285 return keyboardOffset_;
286 }
287
GetWindowWrapperOffset()288 OffsetF SafeAreaManager::GetWindowWrapperOffset()
289 {
290 auto pipelineContext = PipelineContext::GetCurrentContext();
291 CHECK_NULL_RETURN(pipelineContext, OffsetF());
292 auto windowManager = pipelineContext->GetWindowManager();
293 auto isContainerModal = pipelineContext->GetWindowModal() == WindowModal::CONTAINER_MODAL && windowManager &&
294 windowManager->GetWindowMode() == WindowMode::WINDOW_MODE_FLOATING;
295 if (isContainerModal) {
296 auto wrapperOffset = OffsetF(static_cast<float>((CONTAINER_BORDER_WIDTH + CONTENT_PADDING).ConvertToPx()),
297 static_cast<float>((pipelineContext->GetCustomTitleHeight() + CONTAINER_BORDER_WIDTH).ConvertToPx()));
298 return wrapperOffset;
299 }
300 return OffsetF();
301 }
302
ExpandSafeArea()303 void SafeAreaManager::ExpandSafeArea()
304 {
305 ACE_SCOPED_TRACE("ExpandSafeArea node count %zu, IsSafeAreaValid: %d, ignoreSafeArea: %d, isFullScreen: %d, "
306 "isNeedAvoidWindow %d",
307 needExpandNodes_.size(), IsSafeAreaValid(), ignoreSafeArea_, isFullScreen_, isNeedAvoidWindow_);
308 auto pipeline = PipelineContext::GetCurrentContext();
309 CHECK_NULL_VOID(pipeline);
310 auto manager = pipeline->GetSafeAreaManager();
311 auto iter = needExpandNodes_.begin();
312 while (iter != needExpandNodes_.end()) {
313 auto frameNode = (*iter).Upgrade();
314 if (frameNode) {
315 manager->AddGeoRestoreNode(frameNode);
316 frameNode->ExpandSafeArea();
317 }
318 ++iter;
319 }
320 ClearNeedExpandNode();
321 }
322
AddNodeToExpandListIfNeeded(const WeakPtr<FrameNode> & node)323 bool SafeAreaManager::AddNodeToExpandListIfNeeded(const WeakPtr<FrameNode>& node)
324 {
325 if (needExpandNodes_.find(node) == needExpandNodes_.end()) {
326 AddNeedExpandNode(node);
327 return true;
328 }
329 return false;
330 }
331
CheckPageNeedAvoidKeyboard(const RefPtr<FrameNode> & frameNode)332 bool SafeAreaManager::CheckPageNeedAvoidKeyboard(const RefPtr<FrameNode>& frameNode)
333 {
334 if (frameNode->GetTag() != V2::PAGE_ETS_TAG) {
335 return false;
336 }
337 // page will not avoid keyboard when lastChild is sheet
338 RefPtr<OverlayManager> overlay;
339 if (frameNode->RootNodeIsPage()) {
340 auto pattern = frameNode->GetPattern<PagePattern>();
341 CHECK_NULL_RETURN(pattern, true);
342 overlay = pattern->GetOverlayManager();
343 } else {
344 auto navNode = FrameNode::GetFrameNode(V2::NAVDESTINATION_VIEW_ETS_TAG, frameNode->GetRootNodeId());
345 CHECK_NULL_RETURN(navNode, true);
346 auto pattern = navNode->GetPattern<NavDestinationPattern>();
347 CHECK_NULL_RETURN(pattern, true);
348 overlay = pattern->GetOverlayManager();
349 }
350 CHECK_NULL_RETURN(overlay, true);
351 return overlay->CheckPageNeedAvoidKeyboard();
352 }
353 } // namespace OHOS::Ace::NG
354