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/pattern/dialog/dialog_layout_algorithm.h"
17
18 #include "base/subwindow/subwindow_manager.h"
19 #include "core/common/ace_engine.h"
20 #include "core/components/container_modal/container_modal_constants.h"
21 #include "core/components_ng/pattern/dialog/dialog_pattern.h"
22 #include "core/components_ng/pattern/overlay/overlay_manager.h"
23 #include "core/components_ng/pattern/text/text_layout_algorithm.h"
24 #include "core/components_ng/property/measure_utils.h"
25 #include "core/components_ng/pattern/overlay/dialog_manager.h"
26
27 namespace OHOS::Ace::NG {
28 namespace {
29
30 // Using UX spec: Constrain max height within 4/5 of screen height.
31 constexpr double DIALOG_HEIGHT_RATIO = 0.8;
32 constexpr double DIALOG_HEIGHT_RATIO_FOR_LANDSCAPE = 0.9;
33 constexpr double DIALOG_HEIGHT_RATIO_FOR_CAR = 0.95;
34 constexpr Dimension DIALOG_MIN_HEIGHT = 70.0_vp;
35 constexpr Dimension FULLSCREEN = 100.0_pct;
36 constexpr Dimension MULTIPLE_DIALOG_OFFSET_X = 48.0_vp;
37 constexpr Dimension MULTIPLE_DIALOG_OFFSET_Y = 48.0_vp;
38 constexpr Dimension SUBWINDOW_DIALOG_DEFAULT_WIDTH = 400.0_vp;
39 constexpr Dimension AVOID_LIMIT_PADDING = 8.0_vp;
40 constexpr double EXPAND_DISPLAY_WINDOW_HEIGHT_RATIO = 0.67;
41 constexpr double EXPAND_DISPLAY_DIALOG_HEIGHT_RATIO = 0.9;
42 constexpr double HALF = 2.0;
43 constexpr double LANDSCAPE_DIALOG_WIDTH_RATIO = 0.75;
44 constexpr Dimension SCROLL_MIN_HEIGHT_SUITOLD = 100.0_vp;
45 constexpr int32_t TEXT_ALIGN_CONTENT_CENTER = 1;
46 } // namespace
47
Measure(LayoutWrapper * layoutWrapper)48 void DialogLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper)
49 {
50 CHECK_NULL_VOID(layoutWrapper);
51 auto hostNode = layoutWrapper->GetHostNode();
52 CHECK_NULL_VOID(hostNode);
53 auto pipeline = hostNode->GetContext();
54 CHECK_NULL_VOID(pipeline);
55 auto dialogTheme = pipeline->GetTheme<DialogTheme>();
56 CHECK_NULL_VOID(dialogTheme);
57 auto dialogProp = AceType::DynamicCast<DialogLayoutProperty>(layoutWrapper->GetLayoutProperty());
58 CHECK_NULL_VOID(dialogProp);
59 auto dialogPattern = hostNode->GetPattern<DialogPattern>();
60 CHECK_NULL_VOID(dialogPattern);
61 NG::RectF floatButtons;
62 dialogPattern->GetWindowButtonRect(floatButtons);
63 floatButtonsHeight_ = floatButtons.Height();
64 auto parent = hostNode->GetParent();
65 expandDisplay_ = dialogTheme->GetExpandDisplay() || dialogPattern->IsShowInFreeMultiWindow();
66 keyboardAvoidMode_ = dialogPattern->GetDialogProperties().keyboardAvoidMode;
67 keyboardAvoidDistance_ = dialogPattern->GetDialogProperties().keyboardAvoidDistance;
68 isUIExtensionSubWindow_ = dialogPattern->IsUIExtensionSubWindow();
69 hostWindowRect_ = dialogPattern->GetHostWindowRect();
70 customSize_ = dialogProp->GetUseCustomStyle().value_or(false);
71 gridCount_ = dialogProp->GetGridCount().value_or(-1);
72 isShowInSubWindow_ = dialogProp->GetShowInSubWindowValue(false);
73 isModal_ = dialogProp->GetIsModal().value_or(true);
74 hasAddMaskNode_ = (dialogPattern->GetDialogProperties().maskTransitionEffect != nullptr ||
75 dialogPattern->GetDialogProperties().dialogTransitionEffect != nullptr) &&
76 isModal_ && !isShowInSubWindow_;
77 auto enableHoverMode = dialogProp->GetEnableHoverMode().value_or(false);
78 hoverModeArea_ = dialogProp->GetHoverModeArea().value_or(HoverModeAreaType::BOTTOM_SCREEN);
79 auto safeAreaManager = pipeline->GetSafeAreaManager();
80 auto keyboardInsert = safeAreaManager->GetKeyboardInset();
81 isKeyBoardShow_ = keyboardInsert.IsValid();
82 isHoverMode_ = enableHoverMode ? pipeline->IsHalfFoldHoverStatus() : false;
83 AdjustHoverModeForWaterfall(hostNode);
84
85 auto windowManager = pipeline->GetWindowManager();
86 CHECK_NULL_VOID(windowManager);
87 dialogPattern->UpdateFontScale();
88 isSuitOldMeasure_ = dialogPattern->GetIsSuitOldMeasure();
89 auto dialogContext = dialogPattern->GetContext();
90 CHECK_NULL_VOID(dialogContext);
91 isSuitableForElderly_ = (dialogPattern->GetIsSuitableForAging() || dialogPattern->GetCustomNode()) &&
92 windowManager->GetWindowMode() != WindowMode::WINDOW_MODE_FLOATING &&
93 GreatOrEqual(dialogContext->GetFontScale(), 1.75f);
94 auto isPickerDialog = dialogPattern->GetIsPickerDialog();
95 if (isPickerDialog || customSize_) {
96 isSuitableForElderly_ = false;
97 }
98 if (isSuitableForElderly_ || GreatOrEqual(dialogContext->GetFontScale(), 1.75f)) {
99 dialogPattern->UpdateDeviceOrientation(SystemProperties::GetDeviceOrientation());
100 }
101 UpdateSafeArea(hostNode);
102 isShowInFloatingWindow_ = dialogPattern->IsShowInFloatingWindow();
103 ResizeDialogSubwindow(dialogPattern->IsShowInFreeMultiWindow(), isShowInSubWindow_, isShowInFloatingWindow_);
104 const auto& layoutConstraint = dialogProp->GetLayoutConstraint();
105 const auto& parentIdealSize = layoutConstraint->parentIdealSize;
106 OptionalSizeF realSize;
107 // dialog size fit screen.
108 realSize.UpdateIllegalSizeWithCheck(parentIdealSize);
109 embeddedDialogOffsetY_ = 0.0f;
110 stackRootDialogOffsetY_ = 0.0f;
111 if (IsEmbeddedDialog(hostNode)) {
112 if (!realSize.IsValid()) {
113 realSize.UpdateIllegalSizeWithCheck(layoutConstraint->maxSize);
114 }
115 if (dialogPattern->GetDialogProperties().dialogImmersiveMode == ImmersiveMode::EXTEND) {
116 SafeAreaExpandOpts opts = { .type = SAFE_AREA_TYPE_SYSTEM,
117 .edges = SAFE_AREA_EDGE_TOP | SAFE_AREA_EDGE_BOTTOM };
118 dialogProp->UpdateSafeAreaExpandOpts(opts);
119 }
120 embeddedDialogOffsetY_ = GetEmbeddedDialogOffsetY(hostNode);
121 } else {
122 stackRootDialogOffsetY_ = GetStackRootDialogOffsetY(hostNode);
123 }
124 auto currentWindowOffset = pipeline->GetCurrentWindowRect().GetOffset();
125 wrapperOffset_ = OffsetF(currentWindowOffset.GetX(), currentWindowOffset.GetY() + stackRootDialogOffsetY_);
126 layoutWrapper->GetGeometryNode()->SetFrameSize(realSize.ConvertToSizeT());
127 layoutWrapper->GetGeometryNode()->SetContentSize(realSize.ConvertToSizeT());
128 // update child layout constraint
129 auto childLayoutConstraint = layoutWrapper->GetLayoutProperty()->CreateChildConstraint();
130 const auto& children = layoutWrapper->GetAllChildrenWithBuild();
131 if (children.empty()) {
132 return;
133 }
134 auto child = children.front();
135 // constraint child size unless developer is using customStyle
136 if (!customSize_) {
137 auto maxSize = layoutConstraint->maxSize;
138 if (isSuitOldMeasure_) {
139 maxSize.SetWidth(pipeline->GetRootWidth());
140 maxSize.SetHeight(pipeline->GetRootHeight());
141 }
142 UpdateChildMaxSizeHeight(maxSize);
143 childLayoutConstraint.UpdateMaxSizeWithCheck(maxSize);
144 ComputeInnerLayoutParam(childLayoutConstraint, dialogProp);
145 UpdateChildLayoutConstraint(dialogProp, childLayoutConstraint, child);
146 }
147
148 if (isSuitableForElderly_ && SystemProperties::GetDeviceOrientation() == DeviceOrientation::LANDSCAPE) {
149 childLayoutConstraint.maxSize.SetWidth(LANDSCAPE_DIALOG_WIDTH_RATIO * pipeline->GetRootWidth());
150 }
151 // childSize_ and childOffset_ is used in Layout.
152 child->Measure(childLayoutConstraint);
153 if (!layoutWrapper->GetHostNode()->GetPattern<DialogPattern>()->GetCustomNode()) {
154 if (isSuitOldMeasure_) {
155 dialogMaxHeight_ = childLayoutConstraint.maxSize.Height();
156 }
157 AnalysisHeightOfChild(layoutWrapper);
158 }
159 }
160
AdjustHoverModeForWaterfall(const RefPtr<FrameNode> & frameNode)161 void DialogLayoutAlgorithm::AdjustHoverModeForWaterfall(const RefPtr<FrameNode>& frameNode)
162 {
163 CHECK_NULL_VOID(expandDisplay_);
164 auto pattern = frameNode->GetPattern<DialogPattern>();
165 CHECK_NULL_VOID(pattern);
166 auto dialogProp = DynamicCast<DialogLayoutProperty>(frameNode->GetLayoutProperty());
167 CHECK_NULL_VOID(dialogProp);
168 auto enableHoverMode = dialogProp->GetEnableHoverMode().value_or(false);
169 if (!OverlayManager::IsNeedAvoidFoldCrease(frameNode, false, expandDisplay_, dialogProp->GetEnableHoverMode())) {
170 return;
171 }
172 TAG_LOGI(AceLogTag::ACE_DIALOG, "enableHoverMode for waterfallMode, isShowInSubWindow: %{public}d",
173 isShowInSubWindow_);
174 if (enableHoverMode) {
175 isHoverMode_ = true;
176 hoverModeArea_ = dialogProp->GetHoverModeArea().value_or(HoverModeAreaType::TOP_SCREEN);
177 } else if (IsDefaultPosition(dialogProp) && !dialogProp->GetEnableHoverMode().has_value()) {
178 isHoverMode_ = true;
179 hoverModeArea_ = HoverModeAreaType::TOP_SCREEN;
180 }
181 }
182
IsDefaultPosition(const RefPtr<DialogLayoutProperty> & dialogProp)183 bool DialogLayoutAlgorithm::IsDefaultPosition(const RefPtr<DialogLayoutProperty>& dialogProp)
184 {
185 CHECK_NULL_RETURN(dialogProp, false);
186 auto alignment = dialogProp->GetDialogAlignment().value_or(DialogAlignment::DEFAULT);
187 auto offset = dialogProp->GetDialogOffset().value_or(DimensionOffset());
188 return alignment == DialogAlignment::DEFAULT && NearZero(offset.GetX().Value()) && NearZero(offset.GetY().Value());
189 }
190
ResizeDialogSubwindow(bool expandDisplay,bool isShowInSubWindow,bool isShowInFloatingWindow)191 void DialogLayoutAlgorithm::ResizeDialogSubwindow(
192 bool expandDisplay, bool isShowInSubWindow, bool isShowInFloatingWindow)
193 {
194 if (expandDisplay && isShowInSubWindow && isShowInFloatingWindow) {
195 auto currentId = Container::CurrentId();
196 auto subWindow = SubwindowManager::GetInstance()->GetSubwindowByType(currentId, SubwindowType::TYPE_DIALOG);
197 CHECK_NULL_VOID(subWindow);
198 subWindow->ResizeDialogSubwindow();
199 }
200 }
201
UpdateChildMaxSizeHeight(SizeT<float> & maxSize)202 void DialogLayoutAlgorithm::UpdateChildMaxSizeHeight(SizeT<float>& maxSize)
203 {
204 if (!isHoverMode_) {
205 maxSize.MinusPadding(0, 0, safeAreaInsets_.top_.Length(), 0);
206 if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_TWELVE) && LessNotEqual(gridCount_, 0)) {
207 maxSize.MinusPadding(0, 0, 0, safeAreaInsets_.bottom_.Length());
208 }
209 return;
210 }
211 alignBottomScreen_ = !isKeyBoardShow_ && hoverModeArea_ == HoverModeAreaType::BOTTOM_SCREEN;
212 if (alignBottomScreen_) {
213 maxSize.MinusPadding(0, 0, foldCreaseRect.Bottom(), safeAreaInsets_.bottom_.Length());
214 return;
215 }
216 maxSize.SetHeight(foldCreaseRect.Top() - safeAreaInsets_.top_.Length());
217 }
218
UpdateChildLayoutConstraint(const RefPtr<DialogLayoutProperty> & dialogProp,LayoutConstraintF & childLayoutConstraint,RefPtr<LayoutWrapper> & childLayoutWrapper)219 void DialogLayoutAlgorithm::UpdateChildLayoutConstraint(const RefPtr<DialogLayoutProperty>& dialogProp,
220 LayoutConstraintF& childLayoutConstraint, RefPtr<LayoutWrapper>& childLayoutWrapper)
221 {
222 CHECK_NULL_VOID(childLayoutWrapper && dialogProp);
223 auto childLayoutProperty = childLayoutWrapper->GetLayoutProperty();
224 CHECK_NULL_VOID(childLayoutProperty);
225 auto dialogWidth = dialogProp->GetWidth().value_or(Dimension(-1, DimensionUnit::VP));
226 auto dialogHeight = dialogProp->GetHeight().value_or(Dimension(-1, DimensionUnit::VP));
227 if (NonNegative(dialogHeight.Value())) {
228 childLayoutProperty->UpdateUserDefinedIdealSize(CalcSize(std::nullopt, CalcLength(dialogHeight)));
229 }
230 if (NonNegative(dialogWidth.Value())) {
231 childLayoutProperty->UpdateUserDefinedIdealSize(CalcSize(CalcLength(dialogWidth), std::nullopt));
232 }
233 childLayoutConstraint.UpdateMaxSizeWithCheck(SizeF(
234 dialogWidth.ConvertToPxWithSize(childLayoutConstraint.maxSize.Width()),
235 dialogHeight.ConvertToPxWithSize(childLayoutConstraint.maxSize.Height())));
236 }
237
AnalysisHeightOfChild(LayoutWrapper * layoutWrapper)238 void DialogLayoutAlgorithm::AnalysisHeightOfChild(LayoutWrapper* layoutWrapper)
239 {
240 float scrollHeight = 0.0f;
241 float listHeight = 0.0f;
242 float restHeight = 0.0f;
243 float restWidth = 0.0f;
244 RefPtr<LayoutWrapper> scroll;
245 RefPtr<LayoutWrapper> list;
246 auto child = layoutWrapper->GetAllChildrenWithBuild().front();
247 CHECK_NULL_VOID(child);
248 restWidth = child->GetLayoutProperty()->GetContentLayoutConstraint()->maxSize.Width();
249 restHeight = child->GetLayoutProperty()->GetContentLayoutConstraint()->maxSize.Height();
250 for (const auto& grandson : child->GetAllChildrenWithBuild()) {
251 if (grandson->GetHostTag() == V2::SCROLL_ETS_TAG) {
252 scroll = grandson;
253 scrollHeight = grandson->GetGeometryNode()->GetMarginFrameSize().Height();
254 } else if (grandson->GetHostTag() == V2::LIST_ETS_TAG) {
255 list = grandson;
256 listHeight = grandson->GetGeometryNode()->GetMarginFrameSize().Height();
257 } else {
258 restHeight -= grandson->GetGeometryNode()->GetMarginFrameSize().Height();
259 }
260 }
261
262 if (scroll != nullptr) {
263 AnalysisLayoutOfContent(layoutWrapper, scroll);
264 }
265
266 if (scroll != nullptr && list != nullptr) {
267 Distribute(scrollHeight, listHeight, restHeight);
268 auto childConstraint = CreateDialogChildConstraint(layoutWrapper, scrollHeight, restWidth);
269 scroll->Measure(childConstraint);
270 childConstraint = CreateDialogChildConstraint(layoutWrapper, listHeight, restWidth);
271 list->Measure(childConstraint);
272 } else {
273 if (scroll != nullptr) {
274 auto childConstraint =
275 CreateDialogChildConstraint(layoutWrapper, std::min(restHeight, scrollHeight), restWidth);
276 UpdateIsScrollHeightNegative(layoutWrapper, std::min(restHeight, scrollHeight));
277 scroll->Measure(childConstraint);
278 }
279 if (list != nullptr) {
280 auto childConstraint =
281 CreateDialogChildConstraint(layoutWrapper, std::min(restHeight, listHeight), restWidth);
282 list->Measure(childConstraint);
283 }
284 }
285 }
286
AnalysisLayoutOfContent(LayoutWrapper * layoutWrapper,const RefPtr<LayoutWrapper> & scroll)287 void DialogLayoutAlgorithm::AnalysisLayoutOfContent(LayoutWrapper* layoutWrapper, const RefPtr<LayoutWrapper>& scroll)
288 {
289 auto hostNode = layoutWrapper->GetHostNode();
290 CHECK_NULL_VOID(hostNode);
291 auto dialogPattern = hostNode->GetPattern<DialogPattern>();
292 CHECK_NULL_VOID(dialogPattern);
293 auto text = scroll->GetAllChildrenWithBuild().front();
294 CHECK_NULL_VOID(text);
295 auto textLayoutProperty = DynamicCast<TextLayoutProperty>(text->GetLayoutProperty());
296 CHECK_NULL_VOID(textLayoutProperty);
297 textLayoutProperty->UpdateWordBreak(dialogPattern->GetDialogProperties().wordBreak);
298 auto layoutAlgorithmWrapper = DynamicCast<LayoutAlgorithmWrapper>(text->GetLayoutAlgorithm());
299 CHECK_NULL_VOID(layoutAlgorithmWrapper);
300 auto textLayoutAlgorithm = DynamicCast<TextLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
301 CHECK_NULL_VOID(textLayoutAlgorithm);
302 auto pipelineContext = GetPipelineContext();
303 CHECK_NULL_VOID(pipelineContext);
304 auto dialogTheme = pipelineContext->GetTheme<DialogTheme>();
305 CHECK_NULL_VOID(dialogTheme);
306 auto scrollPropery = scroll->GetLayoutProperty();
307 CHECK_NULL_VOID(scrollPropery);
308 if ((dialogPattern->GetTitle().empty() && dialogPattern->GetSubtitle().empty()) ||
309 dialogTheme->GetTextAlignContent() == TEXT_ALIGN_CONTENT_CENTER) {
310 if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN) &&
311 GreatNotEqual(textLayoutAlgorithm->GetLineCount(), 1)) {
312 scrollPropery->UpdateAlignment(Alignment::CENTER_LEFT);
313 } else {
314 scrollPropery->UpdateAlignment(Alignment::CENTER);
315 }
316 } else {
317 scrollPropery->UpdateAlignment(Alignment::CENTER_LEFT);
318 }
319 }
320
Distribute(float & scrollHeight,float & listHeight,float restHeight)321 void DialogLayoutAlgorithm::Distribute(float& scrollHeight, float& listHeight, float restHeight)
322 {
323 if (scrollHeight + listHeight > restHeight) {
324 if (scrollHeight > restHeight / 2.0 && listHeight > restHeight / 2.0) {
325 scrollHeight = restHeight / 2.0;
326 listHeight = restHeight / 2.0;
327 } else if (scrollHeight > restHeight / 2.0) {
328 scrollHeight = restHeight - listHeight;
329 } else {
330 listHeight = restHeight - scrollHeight;
331 }
332 }
333 }
334
CreateDialogChildConstraint(LayoutWrapper * layoutWrapper,float height,float width)335 LayoutConstraintF DialogLayoutAlgorithm::CreateDialogChildConstraint(
336 LayoutWrapper* layoutWrapper, float height, float width)
337 {
338 LayoutConstraintF childConstraint;
339 auto dialogLayoutProperty = layoutWrapper->GetLayoutProperty();
340 CHECK_NULL_RETURN(dialogLayoutProperty, childConstraint);
341 childConstraint = dialogLayoutProperty->CreateChildConstraint();
342 childConstraint.minSize.SetHeight(height);
343 childConstraint.maxSize.SetHeight(height);
344 childConstraint.percentReference.SetHeight(height);
345 childConstraint.minSize.SetWidth(width);
346 childConstraint.maxSize.SetWidth(width);
347 childConstraint.percentReference.SetWidth(width);
348 return childConstraint;
349 }
350
ComputeInnerLayoutSizeParam(LayoutConstraintF & innerLayout,const RefPtr<DialogLayoutProperty> & dialogProp)351 bool DialogLayoutAlgorithm::ComputeInnerLayoutSizeParam(LayoutConstraintF& innerLayout,
352 const RefPtr<DialogLayoutProperty>& dialogProp)
353 {
354 // when width is valid, gridCount_ is -1
355 if (GreatOrEqual(gridCount_, 0)) {
356 return false;
357 }
358 CHECK_NULL_RETURN(Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_TWELVE), false);
359 auto pipeline = GetPipelineContext();
360 CHECK_NULL_RETURN(pipeline, false);
361 auto dialogTheme = pipeline->GetTheme<DialogTheme>();
362 CHECK_NULL_RETURN(dialogTheme, false);
363
364 auto maxSize = innerLayout.maxSize;
365 auto width =
366 maxSize.Width() - dialogTheme->GetMarginLeft().ConvertToPx() - dialogTheme->GetMarginRight().ConvertToPx();
367 auto defaultMaxWidth = dialogTheme->GetContainerMaxWidth().ConvertToPx();
368 width = defaultMaxWidth < width ? defaultMaxWidth : width;
369 if (dialogProp->GetWidth().has_value()) {
370 auto dialogWidth = dialogProp->GetWidth().value_or(Dimension(-1, DimensionUnit::VP));
371 auto widthVal = dialogWidth.Unit() == DimensionUnit::PERCENT ? maxSize.Width() : dialogWidth.ConvertToPx();
372 if (Positive(widthVal)) {
373 width = widthVal;
374 }
375 }
376
377 auto defaultMinHeight = DIALOG_MIN_HEIGHT.ConvertToPx();
378 auto defaultMaxHeight = IsGetExpandDisplayValidHeight(dialogProp) ? expandDisplayValidHeight_ : maxSize.Height();
379 innerLayout.minSize = SizeF(width, defaultMinHeight);
380 double ratioHeight = dialogTheme->GetDialogRatioHeight();
381 innerLayout.maxSize = SizeF(width, defaultMaxHeight * ratioHeight);
382
383 if (dialogProp->GetHeight().has_value()) {
384 auto dialogHeight = dialogProp->GetHeight().value_or(Dimension(-1, DimensionUnit::VP));
385 // covert user input height to px
386 auto realHeight = dialogHeight.Unit() == DimensionUnit::PERCENT ?
387 dialogHeight.ConvertToPxWithSize(defaultMaxHeight) : dialogHeight.ConvertToPx();
388 // percent and abs height default max value
389 auto height = dialogHeight.Unit() == DimensionUnit::PERCENT ? defaultMaxHeight : realHeight;
390 // abnormal height proc
391 if (NonPositive(realHeight)) {
392 height = defaultMaxHeight * ratioHeight;
393 }
394 innerLayout.minSize = SizeF(width, 0.0);
395 innerLayout.maxSize = SizeF(width, height);
396 }
397 if (isSuitableForElderly_) {
398 if (SystemProperties::GetDeviceOrientation() == DeviceOrientation::LANDSCAPE) {
399 innerLayout.minSize = SizeF(width, 0.0);
400 innerLayout.maxSize.SetWidth(pipeline->GetRootWidth() * LANDSCAPE_DIALOG_WIDTH_RATIO);
401 }
402 }
403 // update percentRef
404 innerLayout.percentReference = innerLayout.maxSize;
405 return true;
406 }
407
IsGetExpandDisplayValidHeight(const RefPtr<DialogLayoutProperty> & dialogProp)408 bool DialogLayoutAlgorithm::IsGetExpandDisplayValidHeight(const RefPtr<DialogLayoutProperty>& dialogProp)
409 {
410 CHECK_NULL_RETURN(
411 expandDisplay_ && isShowInSubWindow_ && dialogProp && !(isModal_ && isUIExtensionSubWindow_), false);
412 auto dialog = dialogProp->GetHost();
413 CHECK_NULL_RETURN(dialog, false);
414 auto pipelineContext = DialogManager::GetMainPipelineContext(dialog);
415 CHECK_NULL_RETURN(pipelineContext, false);
416 auto expandDisplayValidHeight =
417 isHoverMode_ ? pipelineContext->GetDisplayAvailableRect().Height()
418 : OverlayManager::GetDisplayAvailableRect(dialog, static_cast<int32_t>(SubwindowType::TYPE_DIALOG))
419 .Height();
420 if (Positive(expandDisplayValidHeight)) {
421 expandDisplayValidHeight_ = expandDisplayValidHeight;
422 return true;
423 }
424 return false;
425 }
426
ComputeInnerLayoutParam(LayoutConstraintF & innerLayout,const RefPtr<DialogLayoutProperty> & dialogProp)427 void DialogLayoutAlgorithm::ComputeInnerLayoutParam(LayoutConstraintF& innerLayout,
428 const RefPtr<DialogLayoutProperty>& dialogProp)
429 {
430 CHECK_EQUAL_VOID(ComputeInnerLayoutSizeParam(innerLayout, dialogProp), true);
431 auto maxSize = innerLayout.maxSize;
432 // Set different layout param for different devices
433 // need to use theme json to replace this function.
434 // get grid size type based on the screen where the dialog locate
435 auto gridSizeType = ScreenSystemManager::GetInstance().GetSize(maxSize.Width());
436 RefPtr<GridColumnInfo> columnInfo;
437 if (SystemProperties::GetDeviceType() == DeviceType::CAR) {
438 columnInfo = GridSystemManager::GetInstance().GetInfoByType(GridColumnType::CAR_DIALOG);
439 } else {
440 columnInfo = GridSystemManager::GetInstance().GetInfoByType(GridColumnType::DIALOG);
441 }
442 columnInfo->GetParent()->BuildColumnWidth(maxSize.Width());
443 auto pipelineContext = GetPipelineContext();
444 CHECK_NULL_VOID(pipelineContext);
445 auto width = GetMaxWidthBasedOnGridType(columnInfo, gridSizeType, SystemProperties::GetDeviceType());
446 if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN)) {
447 width =
448 SUBWINDOW_DIALOG_DEFAULT_WIDTH.ConvertToPx() < width ? SUBWINDOW_DIALOG_DEFAULT_WIDTH.ConvertToPx() : width;
449 }
450 if (SystemProperties::GetDeviceType() == DeviceType::WATCH) {
451 innerLayout.minSize = SizeF(width, 0.0);
452 innerLayout.maxSize = SizeF(width, maxSize.Height());
453 } else if (SystemProperties::GetDeviceType() == DeviceType::PHONE) {
454 if (SystemProperties::GetDeviceOrientation() == DeviceOrientation::LANDSCAPE) {
455 innerLayout.minSize = SizeF(width, 0.0);
456 innerLayout.maxSize = SizeF(width, maxSize.Height() * DIALOG_HEIGHT_RATIO_FOR_LANDSCAPE);
457 } else {
458 innerLayout.minSize = SizeF(width, 0.0);
459 innerLayout.maxSize = SizeF(width, maxSize.Height() * DIALOG_HEIGHT_RATIO);
460 }
461 } else if (SystemProperties::GetDeviceType() == DeviceType::CAR) {
462 innerLayout.minSize = SizeF(width, 0.0);
463 innerLayout.maxSize = SizeF(width, maxSize.Height() * DIALOG_HEIGHT_RATIO_FOR_CAR);
464 } else {
465 innerLayout.minSize = SizeF(width, 0.0);
466 innerLayout.maxSize = SizeF(width, maxSize.Height() * DIALOG_HEIGHT_RATIO);
467 }
468 if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN) && expandDisplay_) {
469 auto maxHeight = SystemProperties::GetDevicePhysicalHeight() *
470 EXPAND_DISPLAY_WINDOW_HEIGHT_RATIO * EXPAND_DISPLAY_DIALOG_HEIGHT_RATIO;
471 innerLayout.minSize = SizeF(SUBWINDOW_DIALOG_DEFAULT_WIDTH.ConvertToPx(), 0.0);
472 innerLayout.maxSize = SizeF(SUBWINDOW_DIALOG_DEFAULT_WIDTH.ConvertToPx(), maxHeight);
473 }
474 if (isSuitableForElderly_) {
475 if (SystemProperties::GetDeviceOrientation() == DeviceOrientation::LANDSCAPE) {
476 innerLayout.minSize = SizeF(width, 0.0);
477 innerLayout.maxSize.SetWidth(pipelineContext->GetRootWidth() * LANDSCAPE_DIALOG_WIDTH_RATIO);
478 }
479 }
480 // update percentRef
481 innerLayout.percentReference = innerLayout.maxSize;
482 }
483
GetMaxWidthBasedOnGridType(const RefPtr<GridColumnInfo> & info,GridSizeType type,DeviceType deviceType)484 double DialogLayoutAlgorithm::GetMaxWidthBasedOnGridType(
485 const RefPtr<GridColumnInfo>& info, GridSizeType type, DeviceType deviceType)
486 {
487 auto parentColumns = info->GetParent()->GetColumns();
488 if (gridCount_ >= 0) {
489 return info->GetWidth(std::min(gridCount_, parentColumns));
490 }
491
492 return info->GetWidth(std::min(GetDeviceColumns(type, deviceType), parentColumns));
493 }
494
GetDeviceColumns(GridSizeType type,DeviceType deviceType)495 int32_t DialogLayoutAlgorithm::GetDeviceColumns(GridSizeType type, DeviceType deviceType)
496 {
497 int32_t deviceColumns;
498 if (deviceType == DeviceType::WATCH) {
499 if (type == GridSizeType::SM) {
500 deviceColumns = 3; // 3: the number of deviceColumns
501 } else if (type == GridSizeType::MD) {
502 deviceColumns = 4; // 4: the number of deviceColumns
503 } else {
504 deviceColumns = 5; // 5: the number of deviceColumns
505 }
506 } else if (deviceType == DeviceType::PHONE) {
507 if (type == GridSizeType::SM) {
508 deviceColumns = 4; // 4: the number of deviceColumns
509 } else if (type == GridSizeType::MD) {
510 deviceColumns = 5; // 5: the number of deviceColumns
511 } else {
512 deviceColumns = 6; // 6: the number of deviceColumns
513 }
514 } else if (deviceType == DeviceType::CAR) {
515 if (type == GridSizeType::SM) {
516 deviceColumns = 4; // 4: the number of deviceColumns
517 } else if (type == GridSizeType::MD) {
518 deviceColumns = 6; // 6: the number of deviceColumns
519 } else {
520 deviceColumns = 8; // 8: the number of deviceColumns
521 }
522 } else if (deviceType == DeviceType::TABLET && type == GridSizeType::MD &&
523 Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN)) {
524 deviceColumns = 5; // 5: the number of deviceColumns
525 } else {
526 if (type == GridSizeType::SM) {
527 deviceColumns = 2;
528 } else if (type == GridSizeType::MD) {
529 deviceColumns = 3;
530 } else {
531 deviceColumns = 4;
532 }
533 }
534 return deviceColumns;
535 }
536
ClipCustomMaskNode(const RefPtr<FrameNode> & dialog,const RectF & rect)537 void DialogLayoutAlgorithm::ClipCustomMaskNode(const RefPtr<FrameNode>& dialog, const RectF& rect)
538 {
539 auto maskNode = AceType::DynamicCast<FrameNode>(dialog->GetChildByIndex(1));
540 CHECK_NULL_VOID(maskNode);
541 auto ctx = maskNode->GetRenderContext();
542 CHECK_NULL_VOID(ctx);
543 ctx->ClipWithRect(rect);
544 ctx->UpdateClipEdge(true);
545 }
546
ProcessMaskRect(std::optional<DimensionRect> maskRect,const RefPtr<FrameNode> & dialog,bool isMask)547 void DialogLayoutAlgorithm::ProcessMaskRect(
548 std::optional<DimensionRect> maskRect, const RefPtr<FrameNode>& dialog, bool isMask)
549 {
550 auto dialogContext = dialog->GetRenderContext();
551 CHECK_NULL_VOID(dialogContext);
552 auto hub = dialog->GetOrCreateEventHub<DialogEventHub>();
553 auto width = maskRect->GetWidth();
554 auto height = maskRect->GetHeight();
555 auto offset = maskRect->GetOffset();
556 if (width.IsNegative()) {
557 width = FULLSCREEN;
558 }
559 if (height.IsNegative()) {
560 height = FULLSCREEN;
561 }
562 auto rootWidth = PipelineContext::GetCurrentRootWidth();
563 auto rootHeight = PipelineContext::GetCurrentRootHeight();
564 RectF rect = RectF(offset.GetX().ConvertToPxWithSize(rootWidth), offset.GetY().ConvertToPxWithSize(rootHeight),
565 width.ConvertToPxWithSize(rootWidth), height.ConvertToPxWithSize(rootHeight));
566 auto isMaskFullScreen =
567 rect == RectF(0.0, 0.0, PipelineContext::GetCurrentRootWidth(), PipelineContext::GetCurrentRootHeight());
568 auto clipMask = isModal_ && isMask && !isMaskFullScreen;
569 if (!isShowInSubWindow_ && clipMask) {
570 if (hasAddMaskNode_) {
571 ClipCustomMaskNode(dialog, rect);
572 } else {
573 dialogContext->ClipWithRect(rect);
574 dialogContext->UpdateClipEdge(true);
575 }
576 }
577 auto gestureHub = hub->GetOrCreateGestureEventHub();
578 std::vector<DimensionRect> mouseResponseRegion;
579 mouseResponseRegion.emplace_back(width, height, offset);
580 gestureHub->SetMouseResponseRegion(mouseResponseRegion);
581 gestureHub->SetResponseRegion(mouseResponseRegion);
582 }
583
GetMaskRect(const RefPtr<FrameNode> & dialog)584 std::optional<DimensionRect> DialogLayoutAlgorithm::GetMaskRect(const RefPtr<FrameNode>& dialog)
585 {
586 std::optional<DimensionRect> maskRect;
587 auto dialogPattern = dialog->GetPattern<DialogPattern>();
588 CHECK_NULL_RETURN(dialogPattern, maskRect);
589 maskRect = dialogPattern->GetDialogProperties().maskRect;
590 if (!isUIExtensionSubWindow_) {
591 return maskRect;
592 }
593
594 if (expandDisplay_ && hostWindowRect_.GetSize().IsPositive()) {
595 auto offset = DimensionOffset(Dimension(hostWindowRect_.GetX()), Dimension(hostWindowRect_.GetY()));
596 maskRect = DimensionRect(Dimension(hostWindowRect_.Width()), Dimension(hostWindowRect_.Height()), offset);
597 } else {
598 maskRect = DimensionRect(CalcDimension(1, DimensionUnit::PERCENT), CalcDimension(1, DimensionUnit::PERCENT),
599 DimensionOffset(CalcDimension(0, DimensionUnit::VP), CalcDimension(0, DimensionUnit::VP)));
600 }
601 return maskRect;
602 }
603
UpdateCustomMaskNodeLayout(const RefPtr<FrameNode> & dialog)604 void DialogLayoutAlgorithm::UpdateCustomMaskNodeLayout(const RefPtr<FrameNode>& dialog)
605 {
606 auto maskNodePtr = dialog->GetChildByIndex(1);
607 CHECK_NULL_VOID(maskNodePtr);
608 auto maskNode = AceType::DynamicCast<FrameNode>(maskNodePtr);
609 CHECK_NULL_VOID(maskNode);
610 auto maskNodeLayoutProp = maskNode->GetLayoutProperty();
611 CHECK_NULL_VOID(maskNodeLayoutProp);
612 auto maskGeometryNode = maskNode->GetGeometryNode();
613 CHECK_NULL_VOID(maskGeometryNode);
614 maskNodeLayoutProp->UpdateUserDefinedIdealSize(
615 CalcSize(CalcLength(1.0, DimensionUnit::PERCENT), CalcLength(1.0, DimensionUnit::PERCENT)));
616 maskGeometryNode->SetFrameOffset(OffsetF(0, 0));
617 maskNode->Measure(dialog->GetLayoutConstraint());
618 maskNode->Layout();
619 }
620
Layout(LayoutWrapper * layoutWrapper)621 void DialogLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper)
622 {
623 CHECK_NULL_VOID(layoutWrapper);
624 auto frameNode = layoutWrapper->GetHostNode();
625 CHECK_NULL_VOID(frameNode);
626 auto dialogProp = DynamicCast<DialogLayoutProperty>(layoutWrapper->GetLayoutProperty());
627 CHECK_NULL_VOID(dialogProp);
628 auto pipelineContext = GetPipelineContext();
629 CHECK_NULL_VOID(pipelineContext);
630 auto dialogTheme = pipelineContext->GetTheme<DialogTheme>();
631 CHECK_NULL_VOID(dialogTheme);
632 ParseSubwindowId(dialogProp);
633 auto selfSize = layoutWrapper->GetGeometryNode()->GetFrameSize();
634 const auto& children = layoutWrapper->GetAllChildrenWithBuild();
635 if (children.empty()) {
636 return;
637 }
638 auto dialogPattern = frameNode->GetPattern<DialogPattern>();
639 CHECK_NULL_VOID(dialogPattern);
640 if (isModal_ && dialogPattern->GetDialogProperties().maskRect.has_value()) {
641 std::optional<DimensionRect> maskRect = GetMaskRect(frameNode);
642 ProcessMaskRect(maskRect, frameNode, true);
643 }
644 if (hasAddMaskNode_) {
645 UpdateCustomMaskNodeLayout(frameNode);
646 }
647 auto child = children.front();
648 auto childSize = child->GetGeometryNode()->GetMarginFrameSize();
649 dialogChildSize_ = childSize;
650 // is PcDevice MultipleDialog Offset to the bottom right
651 if (dialogTheme->GetMultipleDialogDisplay() != "stack" && !dialogProp->GetIsModal().value_or(true) &&
652 dialogProp->GetShowInSubWindowValue(false)) {
653 auto pipeline = frameNode->GetContextRefPtr();
654 auto currentId = pipeline ? pipeline->GetInstanceId() : Container::CurrentIdSafely();
655 auto subWindow = SubwindowManager::GetInstance()->GetSubwindowByType(currentId, SubwindowType::TYPE_DIALOG);
656 CHECK_NULL_VOID(subWindow);
657 auto subOverlayManager = subWindow->GetOverlayManager();
658 CHECK_NULL_VOID(subOverlayManager);
659 MultipleDialog(dialogProp, childSize, selfSize, subOverlayManager);
660 }
661 dialogOffset_ = dialogProp->GetDialogOffset().value_or(DimensionOffset());
662 alignment_ = dialogProp->GetDialogAlignment().value_or(DialogAlignment::DEFAULT);
663 topLeftPoint_ = ComputeChildPosition(childSize, dialogProp, selfSize);
664 auto isNonUIExtensionSubwindow = isShowInSubWindow_ && !isUIExtensionSubWindow_;
665 if ((!isModal_ || isNonUIExtensionSubwindow) && !dialogProp->GetIsSceneBoardDialog().value_or(false)) {
666 ProcessMaskRect(
667 DimensionRect(Dimension(childSize.Width()), Dimension(childSize.Height()), DimensionOffset(topLeftPoint_)),
668 frameNode);
669 }
670 child->GetGeometryNode()->SetMarginFrameOffset(topLeftPoint_);
671 AdjustHeightForKeyboard(layoutWrapper, child);
672 child->Layout();
673 SetSubWindowHotarea(dialogProp, childSize, selfSize, frameNode->GetId());
674 }
675
AvoidScreen(OffsetF & topLeftPoint,const RefPtr<DialogLayoutProperty> & dialogProp,SizeF childSize)676 void DialogLayoutAlgorithm::AvoidScreen(
677 OffsetF& topLeftPoint, const RefPtr<DialogLayoutProperty>& dialogProp, SizeF childSize)
678 {
679 CHECK_NULL_VOID(dialogProp);
680 auto dialogNode = dialogProp->GetHost();
681 CHECK_NULL_VOID(dialogNode);
682 auto pipelineContext = dialogNode->GetContextRefPtr();
683 CHECK_NULL_VOID(pipelineContext);
684 auto containerId = pipelineContext->GetInstanceId();
685 auto container = AceEngine::Get().GetContainer(containerId);
686 Rect availableRect;
687 // In superFoldDisplayDevice, the rect is the full screen's available rect when the displayId is 0.
688 if (SystemProperties::IsSuperFoldDisplayDevice() && container->GetDisplayId() == 0 &&
689 (!isShowInSubWindow_ || (isUIExtensionSubWindow_ && isModal_))) {
690 availableRect = container->GetFoldExpandAvailableRect();
691 } else {
692 availableRect = OverlayManager::GetDisplayAvailableRect(dialogNode,
693 static_cast<int32_t>(SubwindowType::TYPE_DIALOG));
694 }
695 auto overScreen = LessNotEqual(availableRect.Width(), childSize.Width()) ||
696 LessNotEqual(availableRect.Height(), childSize.Height());
697 auto needAvoidScreen = DialogManager::GetInstance().IsPcOrFreeMultiWindow(dialogNode) && !overScreen;
698 if (!needAvoidScreen) {
699 return;
700 }
701 auto realTopLeftPoint = topLeftPoint + wrapperOffset_;
702 auto left = std::clamp(realTopLeftPoint.GetX(), static_cast<float>(availableRect.Left()),
703 static_cast<float>(availableRect.Right() - childSize.Width()));
704 auto top = std::clamp(realTopLeftPoint.GetY(), static_cast<float>(availableRect.Top()),
705 static_cast<float>(availableRect.Bottom() - childSize.Height()));
706 left = std::clamp(static_cast<float>(left - wrapperOffset_.GetX()), 0.0f,
707 static_cast<float>(wrapperSize_.Width() - childSize.Width()));
708 top = std::clamp(static_cast<float>(top - wrapperOffset_.GetY()), floatButtonsHeight_,
709 static_cast<float>(wrapperSize_.Height() - childSize.Height()));
710 topLeftPoint.SetX(left);
711 topLeftPoint.SetY(top);
712 TAG_LOGI(AceLogTag::ACE_DIALOG, "dialog avoid screen, wrapperOffset_: %{public}s, realTopLeftPoint: %{public}s, "
713 "topLeftPoint: %{public}s, stackRootDialogOffsetY_: %{public}f", wrapperOffset_.ToString().c_str(),
714 realTopLeftPoint.ToString().c_str(), topLeftPoint.ToString().c_str(), stackRootDialogOffsetY_);
715 }
716
ParseSubwindowId(const RefPtr<DialogLayoutProperty> & dialogProp)717 void DialogLayoutAlgorithm::ParseSubwindowId(const RefPtr<DialogLayoutProperty>& dialogProp)
718 {
719 CHECK_NULL_VOID(dialogProp);
720 if (!dialogProp->GetShowInSubWindowValue(false)) {
721 return;
722 }
723
724 subWindowId_ = Container::CurrentId();
725 auto dialogNode = dialogProp->GetHost();
726 CHECK_NULL_VOID(dialogNode);
727 auto pipeline = dialogNode->GetContextRefPtr();
728 CHECK_NULL_VOID(pipeline);
729 subWindowId_ = pipeline->GetInstanceId();
730 }
731
AdjustHeightForKeyboard(LayoutWrapper * layoutWrapper,const RefPtr<LayoutWrapper> & child)732 void DialogLayoutAlgorithm::AdjustHeightForKeyboard(LayoutWrapper* layoutWrapper, const RefPtr<LayoutWrapper>& child)
733 {
734 if (Container::LessThanAPITargetVersion(PlatformVersion::VERSION_TWELVE) || !child || !resizeFlag_ ||
735 keyboardAvoidMode_ == KeyboardAvoidMode::NONE) {
736 return;
737 }
738 auto childLayoutProperty = child->GetLayoutProperty();
739 auto dialogProp = DynamicCast<DialogLayoutProperty>(layoutWrapper->GetLayoutProperty());
740 CHECK_NULL_VOID(childLayoutProperty);
741 CHECK_NULL_VOID(dialogProp);
742 auto childConstraint =
743 CreateDialogChildConstraint(layoutWrapper, dialogChildSize_.Height(), dialogChildSize_.Width());
744 auto dialogHeight = Dimension(dialogChildSize_.Height(), DimensionUnit::PX);
745 auto dialogWidth = Dimension(dialogChildSize_.Width(), DimensionUnit::PX);
746 if (!customSize_ && dialogProp->GetWidth().has_value()) {
747 childLayoutProperty->UpdateUserDefinedIdealSize(CalcSize(CalcLength(dialogWidth), std::nullopt));
748 }
749 if (!customSize_ && dialogProp->GetHeight().has_value()) {
750 childLayoutProperty->UpdateUserDefinedIdealSize(CalcSize(std::nullopt, CalcLength(dialogHeight)));
751 }
752 child->Measure(childConstraint);
753 child->GetGeometryNode()->SetFrameSize(dialogChildSize_);
754 auto renderContext = child->GetHostNode()->GetRenderContext();
755 CHECK_NULL_VOID(renderContext);
756 renderContext->SetClipToFrame(true);
757 renderContext->UpdateClipEdge(true);
758 }
759
SetSubWindowHotarea(const RefPtr<DialogLayoutProperty> & dialogProp,SizeF childSize,SizeF selfSize,int32_t frameNodeId)760 void DialogLayoutAlgorithm::SetSubWindowHotarea(
761 const RefPtr<DialogLayoutProperty>& dialogProp, SizeF childSize, SizeF selfSize, int32_t frameNodeId)
762 {
763 if (!dialogProp->GetShowInSubWindowValue(false)) {
764 return;
765 }
766
767 std::vector<Rect> rects;
768 Rect rect;
769 if (!dialogProp->GetIsSceneBoardDialog().value_or(false)) {
770 rect = Rect(topLeftPoint_.GetX(), topLeftPoint_.GetY(), childSize.Width(), childSize.Height());
771 } else {
772 rect = Rect(0.0f, 0.0f, selfSize.Width(), selfSize.Height());
773 }
774 if (isUIExtensionSubWindow_ && isModal_) {
775 rect = Rect(0.0f, 0.0f, selfSize.Width(), selfSize.Height());
776 }
777 rects.emplace_back(rect);
778 SubwindowManager::GetInstance()->SetHotAreas(rects, SubwindowType::TYPE_DIALOG, frameNodeId, subWindowId_);
779 }
780
IsDialogTouchingBoundary(OffsetF topLeftPoint,SizeF childSize,SizeF selfSize)781 bool DialogLayoutAlgorithm::IsDialogTouchingBoundary(OffsetF topLeftPoint, SizeF childSize, SizeF selfSize)
782 {
783 auto pipelineContext = GetPipelineContext();
784 CHECK_NULL_RETURN(pipelineContext, false);
785 auto safeAreaInsets = pipelineContext->GetSafeArea();
786 float bottomSecurity = static_cast<float>(PORTRAIT_BOTTOM_SECURITY.ConvertToPx());
787 auto height = safeAreaInsets.bottom_.start == 0 ? selfSize.Height() - bottomSecurity : safeAreaInsets.bottom_.start;
788 auto width = selfSize.Width();
789 if (topLeftPoint.GetY() + childSize.Height() >= height) {
790 touchingBoundaryFlag_ = TouchingBoundaryType::TouchBottomBoundary;
791 } else if (topLeftPoint.GetX() + childSize.Width() >= width) {
792 touchingBoundaryFlag_ = TouchingBoundaryType::TouchRightBoundary;
793 } else {
794 touchingBoundaryFlag_ = TouchingBoundaryType::NotTouchBoundary;
795 return false;
796 }
797 return true;
798 }
799
MultipleDialog(const RefPtr<DialogLayoutProperty> & dialogProp,const SizeF & childSize,const SizeF & selfSize,const RefPtr<OverlayManager> subOverlayManager)800 void DialogLayoutAlgorithm::MultipleDialog(const RefPtr<DialogLayoutProperty>& dialogProp, const SizeF& childSize,
801 const SizeF& selfSize, const RefPtr<OverlayManager> subOverlayManager)
802 {
803 std::map<int32_t, RefPtr<FrameNode>> DialogMap(
804 subOverlayManager->GetDialogMap().begin(), subOverlayManager->GetDialogMap().end());
805 int dialogMapSize = static_cast<int>(DialogMap.size());
806 if (dialogMapSize > 1) {
807 auto it = DialogMap.begin();
808 for (int i = 1; i < dialogMapSize - 1; i++) {
809 it++;
810 }
811 auto predialogProp = DynamicCast<DialogLayoutProperty>(it->second->GetLayoutProperty());
812 auto firstdialogProp = DynamicCast<DialogLayoutProperty>(DialogMap.begin()->second->GetLayoutProperty());
813 dialogProp->UpdateDialogOffset(predialogProp->GetDialogOffset().value_or(DimensionOffset()) +
814 DimensionOffset(MULTIPLE_DIALOG_OFFSET_X, MULTIPLE_DIALOG_OFFSET_Y));
815 dialogOffset_ = dialogProp->GetDialogOffset().value_or(DimensionOffset());
816 alignment_ = dialogProp->GetDialogAlignment().value_or(DialogAlignment::DEFAULT);
817 topLeftPoint_ = ComputeChildPosition(childSize, dialogProp, selfSize);
818 if (IsDialogTouchingBoundary(topLeftPoint_, childSize, selfSize)) {
819 if (touchingBoundaryFlag_ == TouchingBoundaryType::TouchBottomBoundary) {
820 dialogProp->UpdateDialogOffset(
821 DimensionOffset(predialogProp->GetDialogOffset().value_or(DimensionOffset()).GetX(),
822 firstdialogProp->GetDialogOffset().value_or(DimensionOffset()).GetY()));
823 } else if (touchingBoundaryFlag_ == TouchingBoundaryType::TouchRightBoundary) {
824 dialogProp->UpdateDialogOffset(firstdialogProp->GetDialogOffset().value_or(DimensionOffset()));
825 }
826 }
827 }
828 }
829
ComputeChildPosition(const SizeF & childSize,const RefPtr<DialogLayoutProperty> & prop,const SizeF & selfSize)830 OffsetF DialogLayoutAlgorithm::ComputeChildPosition(
831 const SizeF& childSize, const RefPtr<DialogLayoutProperty>& prop, const SizeF& selfSize)
832 {
833 OffsetF topLeftPoint;
834 auto pipelineContext = GetPipelineContext();
835 CHECK_NULL_RETURN(pipelineContext, OffsetF());
836 auto dialogTheme = pipelineContext->GetTheme<DialogTheme>();
837 const auto& layoutConstraint = prop->GetLayoutConstraint();
838 CHECK_NULL_RETURN(dialogTheme, OffsetF());
839 auto dialogOffsetX =
840 ConvertToPx(CalcLength(dialogOffset_.GetX()), layoutConstraint->scaleProperty, selfSize.Width());
841 auto dialogOffsetY =
842 ConvertToPx(CalcLength(dialogOffset_.GetY()), layoutConstraint->scaleProperty, selfSize.Height());
843 OffsetF dialogOffset = OffsetF(dialogOffsetX.value_or(0.0), dialogOffsetY.value_or(0.0));
844 auto isHostWindowAlign = isUIExtensionSubWindow_ && expandDisplay_ && hostWindowRect_.GetSize().IsPositive();
845 auto maxSize = isHostWindowAlign ? hostWindowRect_.GetSize() : layoutConstraint->maxSize;
846 wrapperSize_ = layoutConstraint->maxSize;
847 if (!SetAlignmentSwitch(maxSize, childSize, topLeftPoint)) {
848 topLeftPoint = OffsetF(maxSize.Width() - childSize.Width(), maxSize.Height() - childSize.Height()) / HALF;
849 }
850 if (isHostWindowAlign && !isModal_) {
851 topLeftPoint += hostWindowRect_.GetOffset();
852 }
853 const auto& expandSafeAreaOpts = prop->GetSafeAreaExpandOpts();
854 bool needAvoidKeyboard = true;
855 if ((expandSafeAreaOpts && (expandSafeAreaOpts->type & SAFE_AREA_TYPE_KEYBOARD)) ||
856 keyboardAvoidMode_ == KeyboardAvoidMode::NONE) {
857 needAvoidKeyboard = false;
858 }
859 auto childOffset = AdjustChildPosition(topLeftPoint, dialogOffset, childSize, needAvoidKeyboard);
860 AvoidScreen(childOffset, prop, childSize);
861 return childOffset;
862 }
863
IsAlignmentByWholeScreen()864 bool DialogLayoutAlgorithm::IsAlignmentByWholeScreen()
865 {
866 if (Container::LessThanAPIVersion(PlatformVersion::VERSION_TWELVE)) {
867 return false;
868 }
869
870 switch (alignment_) {
871 case DialogAlignment::TOP:
872 case DialogAlignment::TOP_START:
873 case DialogAlignment::TOP_END:
874 case DialogAlignment::BOTTOM:
875 case DialogAlignment::BOTTOM_START:
876 case DialogAlignment::BOTTOM_END:
877 return false;
878 case DialogAlignment::CENTER:
879 case DialogAlignment::CENTER_START:
880 case DialogAlignment::CENTER_END:
881 default:
882 return true;
883 }
884 }
885
CaculateMaxSize(SizeF & maxSize)886 void DialogLayoutAlgorithm::CaculateMaxSize(SizeF& maxSize)
887 {
888 auto halfScreenHeight = maxSize.Height() / HALF;
889 if (!customSize_ && isHoverMode_) {
890 maxSize.SetHeight(halfScreenHeight);
891 }
892 if (!customSize_ && !IsAlignmentByWholeScreen()) {
893 if (isHoverMode_ && hoverModeArea_ == HoverModeAreaType::TOP_SCREEN) {
894 maxSize.SetHeight(foldCreaseRect.Top());
895 return;
896 }
897 maxSize.MinusHeight(safeAreaBottomLength_);
898 }
899 }
900
SetAlignmentSwitch(SizeF & maxSize,const SizeF & childSize,OffsetF & topLeftPoint)901 bool DialogLayoutAlgorithm::SetAlignmentSwitch(SizeF& maxSize, const SizeF& childSize, OffsetF& topLeftPoint)
902 {
903 auto halfScreenHeight = maxSize.Height() / HALF;
904 CaculateMaxSize(maxSize);
905 if (alignment_ != DialogAlignment::DEFAULT || Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_TWELVE)) {
906 switch (alignment_) {
907 case DialogAlignment::TOP:
908 topLeftPoint = OffsetF((maxSize.Width() - childSize.Width()) / HALF, 0.0);
909 break;
910 case DialogAlignment::CENTER:
911 topLeftPoint =
912 OffsetF(maxSize.Width() - childSize.Width(), maxSize.Height() - childSize.Height()) / HALF;
913 break;
914 case DialogAlignment::BOTTOM:
915 topLeftPoint =
916 OffsetF((maxSize.Width() - childSize.Width()) / HALF, maxSize.Height() - childSize.Height());
917 break;
918 case DialogAlignment::TOP_START:
919 topLeftPoint = OffsetF(0.0, 0.0);
920 break;
921 case DialogAlignment::TOP_END:
922 topLeftPoint = OffsetF(maxSize.Width() - childSize.Width(), 0.0);
923 break;
924 case DialogAlignment::CENTER_START:
925 topLeftPoint = OffsetF(0.0, maxSize.Height() - childSize.Height()) / HALF;
926 break;
927 case DialogAlignment::CENTER_END:
928 topLeftPoint =
929 OffsetF(maxSize.Width() - childSize.Width(), (maxSize.Height() - childSize.Height()) / HALF);
930 break;
931 case DialogAlignment::BOTTOM_START:
932 topLeftPoint = OffsetF(0.0, maxSize.Height() - childSize.Height());
933 break;
934 case DialogAlignment::BOTTOM_END:
935 topLeftPoint = OffsetF(maxSize.Width() - childSize.Width(), maxSize.Height() - childSize.Height());
936 break;
937 default:
938 topLeftPoint =
939 OffsetF(maxSize.Width() - childSize.Width(), maxSize.Height() - childSize.Height()) / HALF;
940 break;
941 }
942 if (alignBottomScreen_) {
943 topLeftPoint.SetY(topLeftPoint.GetY() + halfScreenHeight);
944 }
945 return true;
946 }
947
948 return SetAlignmentSwitchLessThanAPITwelve(maxSize, childSize, topLeftPoint);
949 }
950
SetAlignmentSwitchLessThanAPITwelve(const SizeF & maxSize,const SizeF & childSize,OffsetF & topLeftPoint)951 bool DialogLayoutAlgorithm::SetAlignmentSwitchLessThanAPITwelve(const SizeF& maxSize, const SizeF& childSize,
952 OffsetF& topLeftPoint)
953 {
954 auto container = Container::Current();
955 CHECK_NULL_RETURN(container, false);
956 auto displayInfo = container->GetDisplayInfo();
957 CHECK_NULL_RETURN(displayInfo, false);
958 auto foldStatus = displayInfo->GetFoldStatus();
959 if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN) && displayInfo->GetIsFoldable() &&
960 (foldStatus == FoldStatus::EXPAND || foldStatus == FoldStatus::HALF_FOLD)) {
961 topLeftPoint = OffsetF(maxSize.Width() - childSize.Width(), maxSize.Height() - childSize.Height()) / HALF;
962 return true;
963 }
964
965 bool version10OrLarger = Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_TEN);
966 if (version10OrLarger && SystemProperties::GetDeviceType() == DeviceType::PHONE) {
967 if (SystemProperties::GetDeviceOrientation() == DeviceOrientation::LANDSCAPE) {
968 topLeftPoint = OffsetF(maxSize.Width() - childSize.Width(), maxSize.Height() - childSize.Height()) / HALF;
969 return true;
970 }
971 if (SystemProperties::GetDeviceOrientation() == DeviceOrientation::PORTRAIT) {
972 topLeftPoint = OffsetF((maxSize.Width() - childSize.Width()) / HALF,
973 std::max(maxSize.Height() - childSize.Height() - GetPaddingBottom(), 0.0));
974 return true;
975 }
976 }
977 return false;
978 }
979
UpdateTouchRegion()980 void DialogLayoutAlgorithm::UpdateTouchRegion()
981 {
982 //update touch region is not completed.
983 }
984
GetPaddingBottom() const985 double DialogLayoutAlgorithm::GetPaddingBottom() const
986 {
987 auto pipelineContext = GetPipelineContext();
988 CHECK_NULL_RETURN(pipelineContext, 0);
989 auto dialogTheme = pipelineContext->GetTheme<DialogTheme>();
990 CHECK_NULL_RETURN(dialogTheme, 0);
991 auto bottom = dialogTheme->GetDefaultDialogMarginBottom();
992 if (keyboardAvoidDistance_.has_value()) {
993 return pipelineContext->NormalizeToPx(keyboardAvoidDistance_.value());
994 } else {
995 return pipelineContext->NormalizeToPx(bottom);
996 }
997 }
998
AdjustChildPosition(OffsetF & topLeftPoint,const OffsetF & dialogOffset,const SizeF & childSize,bool needAvoidKeyboard)999 OffsetF DialogLayoutAlgorithm::AdjustChildPosition(
1000 OffsetF& topLeftPoint, const OffsetF& dialogOffset, const SizeF& childSize, bool needAvoidKeyboard)
1001 {
1002 auto pipelineContext = GetPipelineContext();
1003 CHECK_NULL_RETURN(pipelineContext, topLeftPoint + dialogOffset);
1004 if (!customSize_ && LessNotEqual(topLeftPoint.GetY() + embeddedDialogOffsetY_, safeAreaInsets_.top_.end)) {
1005 topLeftPoint.SetY(safeAreaInsets_.top_.end);
1006 }
1007 if (alignBottomScreen_) {
1008 bool alignTop = alignment_ == DialogAlignment::TOP || alignment_ == DialogAlignment::TOP_START ||
1009 alignment_ == DialogAlignment::TOP_END;
1010 if (topLeftPoint.GetY() < foldCreaseRect.Bottom() || alignTop) {
1011 topLeftPoint.SetY(foldCreaseRect.Bottom());
1012 }
1013 }
1014 auto childOffset = topLeftPoint + dialogOffset;
1015 auto manager = pipelineContext->GetSafeAreaManager();
1016 auto keyboardInsert = manager->GetKeyboardInset();
1017 auto childBottom = childOffset.GetY() + childSize.Height() + embeddedDialogOffsetY_ + stackRootDialogOffsetY_;
1018 auto paddingBottom = static_cast<float>(GetPaddingBottom());
1019 if (needAvoidKeyboard && keyboardInsert.Length() > 0 && childBottom > (keyboardInsert.start - paddingBottom)) {
1020 auto limitPos = std::min(childOffset.GetY(),
1021 static_cast<float>(safeAreaInsets_.top_.Length() + AVOID_LIMIT_PADDING.ConvertToPx()));
1022 childOffset.SetY(childOffset.GetY() - (childBottom - (keyboardInsert.start - paddingBottom)));
1023
1024 if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE) && childOffset.GetY() < limitPos) {
1025 resizeFlag_ = true;
1026 dialogChildSize_ = childSize;
1027 if (limitPos - childOffset.GetY() > dialogChildSize_.Height()) {
1028 dialogChildSize_.MinusHeight(dialogChildSize_.Height());
1029 } else {
1030 dialogChildSize_.MinusHeight(limitPos - childOffset.GetY());
1031 }
1032 childOffset.SetY(limitPos);
1033 }
1034 }
1035 return childOffset;
1036 }
1037
UpdateSafeArea(const RefPtr<FrameNode> & frameNode)1038 void DialogLayoutAlgorithm::UpdateSafeArea(const RefPtr<FrameNode>& frameNode)
1039 {
1040 auto container = Container::Current();
1041 auto currentId = Container::CurrentId();
1042 CHECK_NULL_VOID(container);
1043 if (container->IsSubContainer()) {
1044 currentId = SubwindowManager::GetInstance()->GetParentContainerId(Container::CurrentId());
1045 container = AceEngine::Get().GetContainer(currentId);
1046 CHECK_NULL_VOID(container);
1047 ContainerScope scope(currentId);
1048 }
1049 safeAreaInsets_ = OverlayManager::GetSafeAreaInsets(frameNode);
1050 if (!IsEmbeddedDialog(frameNode)) {
1051 safeAreaBottomLength_ = safeAreaInsets_.bottom_.Length();
1052 }
1053 if (isHoverMode_) {
1054 auto displayInfo = container->GetDisplayInfo();
1055 CHECK_NULL_VOID(displayInfo);
1056 auto foldCreaseRects = displayInfo->GetCurrentFoldCreaseRegion();
1057 if (!foldCreaseRects.empty()) {
1058 foldCreaseRect = foldCreaseRects.front();
1059 }
1060 }
1061 TAG_LOGI(AceLogTag::ACE_DIALOG, "safeAreaInsets: %{public}s", safeAreaInsets_.ToString().c_str());
1062 }
1063
UpdateIsScrollHeightNegative(LayoutWrapper * layoutWrapper,float height)1064 void DialogLayoutAlgorithm::UpdateIsScrollHeightNegative(LayoutWrapper* layoutWrapper, float height)
1065 {
1066 if (height < SCROLL_MIN_HEIGHT_SUITOLD.ConvertToPx()) {
1067 const auto& children = layoutWrapper->GetAllChildrenWithBuild();
1068 auto child = children.front();
1069 auto childSize = child->GetGeometryNode()->GetMarginFrameSize();
1070 if (childSize.Height() == dialogMaxHeight_ && childSize.Height() > 0) {
1071 auto hostNode = layoutWrapper->GetHostNode();
1072 CHECK_NULL_VOID(hostNode);
1073 auto dialogPattern = hostNode->GetPattern<DialogPattern>();
1074 CHECK_NULL_VOID(dialogPattern);
1075 dialogPattern->SetIsScrollHeightNegative(true);
1076 }
1077 }
1078 }
1079
IsEmbeddedDialog(const RefPtr<FrameNode> & frameNode)1080 bool DialogLayoutAlgorithm::IsEmbeddedDialog(const RefPtr<FrameNode>& frameNode)
1081 {
1082 auto parent = frameNode->GetParent();
1083 if (parent && (parent->GetTag() == V2::PAGE_ETS_TAG || parent->GetTag() == V2::NAVDESTINATION_VIEW_ETS_TAG)) {
1084 return true;
1085 }
1086 return false;
1087 }
1088
GetEmbeddedDialogOffsetY(const RefPtr<FrameNode> & frameNode)1089 float DialogLayoutAlgorithm::GetEmbeddedDialogOffsetY(const RefPtr<FrameNode>& frameNode)
1090 {
1091 auto parent = AceType::DynamicCast<FrameNode>(frameNode->GetParent());
1092 CHECK_NULL_RETURN(parent, 0.0f);
1093 if (parent->GetTag() == V2::PAGE_ETS_TAG) {
1094 return parent->GetOffsetRelativeToWindow().GetY();
1095 }
1096 if (parent->GetTag() == V2::NAVDESTINATION_VIEW_ETS_TAG) {
1097 return parent->GetGeometryNode()->GetParentAdjust().GetY();
1098 }
1099 return 0.0f;
1100 }
1101
GetStackRootDialogOffsetY(const RefPtr<FrameNode> & frameNode)1102 float DialogLayoutAlgorithm::GetStackRootDialogOffsetY(const RefPtr<FrameNode>& frameNode)
1103 {
1104 auto parent = AceType::DynamicCast<FrameNode>(frameNode->GetParent());
1105 CHECK_NULL_RETURN(parent, 0.0f);
1106 if (parent->GetTag() == V2::STACK_ETS_TAG && expandDisplay_ == true) {
1107 return parent->GetOffsetRelativeToWindow().GetY();
1108 }
1109 return 0.0f;
1110 }
1111
GetPipelineContext() const1112 RefPtr<PipelineContext> DialogLayoutAlgorithm::GetPipelineContext() const
1113 {
1114 auto context = context_.Upgrade();
1115 CHECK_NULL_RETURN(context, PipelineContext::GetCurrentContextSafelyWithCheck());
1116 return context;
1117 }
1118 } // namespace OHOS::Ace::NG
1119