• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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