• 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/geometry/dimension_offset.h"
19 #include "base/geometry/ng/point_t.h"
20 #include "base/geometry/ng/size_t.h"
21 #include "base/memory/ace_type.h"
22 #include "base/subwindow/subwindow_manager.h"
23 #include "base/utils/device_config.h"
24 #include "base/utils/system_properties.h"
25 #include "base/utils/utils.h"
26 #include "core/common/ace_engine.h"
27 #include "core/components/container_modal/container_modal_constants.h"
28 #include "core/common/container.h"
29 #include "core/components/common/layout/grid_system_manager.h"
30 #include "core/components/common/properties/placement.h"
31 #include "core/components/dialog/dialog_theme.h"
32 #include "core/components_ng/base/frame_node.h"
33 #include "core/components_ng/layout/layout_algorithm.h"
34 #include "core/components_ng/pattern/dialog/dialog_layout_property.h"
35 #include "core/components_ng/pattern/dialog/dialog_pattern.h"
36 #include "core/components_ng/pattern/scroll/scroll_layout_property.h"
37 #include "core/components_ng/pattern/text/text_layout_algorithm.h"
38 #include "core/components_ng/property/measure_utils.h"
39 #include "core/components_ng/render/paragraph.h"
40 #include "core/components_v2/inspector/inspector_constants.h"
41 #include "core/pipeline/base/constants.h"
42 #include "core/pipeline/pipeline_base.h"
43 #include "core/pipeline_ng/pipeline_context.h"
44 #include "core/pipeline_ng/ui_task_scheduler.h"
45 
46 namespace OHOS::Ace::NG {
47 namespace {
48 
49 // Using UX spec: Constrain max height within 4/5 of screen height.
50 // TODO: move these values to theme.
51 constexpr double DIALOG_HEIGHT_RATIO = 0.8;
52 constexpr double DIALOG_HEIGHT_RATIO_FOR_LANDSCAPE = 0.9;
53 constexpr double DIALOG_HEIGHT_RATIO_FOR_CAR = 0.95;
54 constexpr Dimension FULLSCREEN = 100.0_pct;
55 constexpr Dimension MULTIPLE_DIALOG_OFFSET_X = 48.0_vp;
56 constexpr Dimension MULTIPLE_DIALOG_OFFSET_Y = 48.0_vp;
57 constexpr Dimension SUBWINDOW_DIALOG_DEFAULT_WIDTH = 400.0_vp;
58 constexpr double EXPAND_DISPLAY_WINDOW_HEIGHT_RATIO = 0.67;
59 constexpr double EXPAND_DISPLAY_DIALOG_HEIGHT_RATIO = 0.9;
60 constexpr double HALF = 2.0;
61 } // namespace
62 
Measure(LayoutWrapper * layoutWrapper)63 void DialogLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper)
64 {
65     CHECK_NULL_VOID(layoutWrapper);
66     auto pipeline = PipelineContext::GetCurrentContext();
67     CHECK_NULL_VOID(pipeline);
68     auto dialogTheme = pipeline->GetTheme<DialogTheme>();
69     CHECK_NULL_VOID(dialogTheme);
70     expandDisplay_ = dialogTheme->GetExpandDisplay();
71     auto dialogProp = AceType::DynamicCast<DialogLayoutProperty>(layoutWrapper->GetLayoutProperty());
72     customSize_ = dialogProp->GetUseCustomStyle().value_or(false);
73     gridCount_ = dialogProp->GetGridCount().value_or(-1);
74     const auto& layoutConstraint = dialogProp->GetLayoutConstraint();
75     auto parentIdealSize = UpdateHeightWithSafeArea(layoutConstraint->parentIdealSize.ConvertToSizeT());
76     OptionalSizeF realSize;
77     // dialog size fit screen.
78     realSize.UpdateIllegalSizeWithCheck(parentIdealSize);
79     layoutWrapper->GetGeometryNode()->SetFrameSize(realSize.ConvertToSizeT());
80     layoutWrapper->GetGeometryNode()->SetContentSize(realSize.ConvertToSizeT());
81     // update child layout constraint
82     auto childLayoutConstraint = layoutWrapper->GetLayoutProperty()->CreateChildConstraint();
83 
84     // constraint child size unless developer is using customStyle
85     if (!customSize_) {
86         auto inset = pipeline->GetSafeArea();
87         auto maxSize = UpdateHeightWithSafeArea(layoutConstraint->maxSize);
88         maxSize.MinusPadding(0, 0, inset.top_.Length(), 0);
89         childLayoutConstraint.UpdateMaxSizeWithCheck(maxSize);
90         ComputeInnerLayoutParam(childLayoutConstraint);
91     }
92     const auto& children = layoutWrapper->GetAllChildrenWithBuild();
93     if (children.empty()) {
94         return;
95     }
96     auto child = children.front();
97     // childSize_ and childOffset_ is used in Layout.
98     child->Measure(childLayoutConstraint);
99 
100     if (!layoutWrapper->GetHostNode()->GetPattern<DialogPattern>()->GetCustomNode()) {
101         AnalysisHeightOfChild(layoutWrapper);
102     }
103 }
104 
AnalysisHeightOfChild(LayoutWrapper * layoutWrapper)105 void DialogLayoutAlgorithm::AnalysisHeightOfChild(LayoutWrapper* layoutWrapper)
106 {
107     float scrollHeight = 0.0f;
108     float listHeight = 0.0f;
109     float restHeight = 0.0f;
110     float restWidth = 0.0f;
111     RefPtr<LayoutWrapper> scroll;
112     RefPtr<LayoutWrapper> list;
113     for (const auto& children : layoutWrapper->GetAllChildrenWithBuild()) {
114         restWidth = children->GetGeometryNode()->GetMarginFrameSize().Width();
115         restHeight = children->GetGeometryNode()->GetMarginFrameSize().Height();
116         for (const auto& grandson : children->GetAllChildrenWithBuild()) {
117             if (grandson->GetHostTag() == V2::SCROLL_ETS_TAG) {
118                 scroll = grandson;
119                 scrollHeight = grandson->GetGeometryNode()->GetMarginFrameSize().Height();
120             } else if (grandson->GetHostTag() == V2::LIST_ETS_TAG) {
121                 list = grandson;
122                 listHeight = grandson->GetGeometryNode()->GetMarginFrameSize().Height();
123             } else {
124                 restHeight -= grandson->GetGeometryNode()->GetMarginFrameSize().Height();
125             }
126         }
127     }
128 
129     if (scroll != nullptr) {
130         AnalysisLayoutOfContent(layoutWrapper, scroll);
131     }
132 
133     if (scroll != nullptr && list != nullptr) {
134         Distribute(scrollHeight, listHeight, restHeight);
135         auto childConstraint = CreateDialogChildConstraint(layoutWrapper, scrollHeight, restWidth);
136         scroll->Measure(childConstraint);
137         childConstraint = CreateDialogChildConstraint(layoutWrapper, listHeight, restWidth);
138         list->Measure(childConstraint);
139     } else {
140         if (scroll != nullptr) {
141             auto childConstraint =
142                 CreateDialogChildConstraint(layoutWrapper, std::min(restHeight, scrollHeight), restWidth);
143             scroll->Measure(childConstraint);
144         }
145         if (list != nullptr) {
146             auto childConstraint =
147                 CreateDialogChildConstraint(layoutWrapper, std::min(restHeight, listHeight), restWidth);
148             list->Measure(childConstraint);
149         }
150     }
151 }
152 
AnalysisLayoutOfContent(LayoutWrapper * layoutWrapper,const RefPtr<LayoutWrapper> & scroll)153 void DialogLayoutAlgorithm::AnalysisLayoutOfContent(LayoutWrapper* layoutWrapper, const RefPtr<LayoutWrapper>& scroll)
154 {
155     auto text = scroll->GetAllChildrenWithBuild().front();
156     CHECK_NULL_VOID(text);
157     auto textLayoutProperty = DynamicCast<TextLayoutProperty>(text->GetLayoutProperty());
158     CHECK_NULL_VOID(textLayoutProperty);
159     textLayoutProperty->UpdateWordBreak(WordBreak::BREAK_ALL);
160     auto layoutAlgorithmWrapper = DynamicCast<LayoutAlgorithmWrapper>(text->GetLayoutAlgorithm());
161     CHECK_NULL_VOID(layoutAlgorithmWrapper);
162     auto textLayoutAlgorithm = DynamicCast<TextLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
163     CHECK_NULL_VOID(textLayoutAlgorithm);
164     auto hostNode = layoutWrapper->GetHostNode();
165     CHECK_NULL_VOID(hostNode);
166     auto dialogPattern = hostNode->GetPattern<DialogPattern>();
167     CHECK_NULL_VOID(dialogPattern);
168     if (dialogPattern->GetTitle().empty() && dialogPattern->GetSubtitle().empty()) {
169         if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN) &&
170             GreatNotEqual(textLayoutAlgorithm->GetLineCount(), 1)) {
171             scroll->GetLayoutProperty()->UpdateAlignment(Alignment::CENTER_LEFT);
172         } else {
173             scroll->GetLayoutProperty()->UpdateAlignment(Alignment::CENTER);
174         }
175     } else {
176         scroll->GetLayoutProperty()->UpdateAlignment(Alignment::CENTER_LEFT);
177     }
178 }
179 
Distribute(float & scrollHeight,float & listHeight,float restHeight)180 void DialogLayoutAlgorithm::Distribute(float& scrollHeight, float& listHeight, float restHeight)
181 {
182     if (scrollHeight + listHeight > restHeight) {
183         if (scrollHeight > restHeight / 2.0 && listHeight > restHeight / 2.0) {
184             scrollHeight = restHeight / 2.0;
185             listHeight = restHeight / 2.0;
186         } else if (scrollHeight > restHeight / 2.0) {
187             scrollHeight = restHeight - listHeight;
188         } else {
189             listHeight = restHeight - scrollHeight;
190         }
191     }
192 }
193 
CreateDialogChildConstraint(LayoutWrapper * layoutWrapper,float height,float width)194 LayoutConstraintF DialogLayoutAlgorithm::CreateDialogChildConstraint(
195     LayoutWrapper* layoutWrapper, float height, float width)
196 {
197     auto childConstraint = layoutWrapper->GetLayoutProperty()->CreateChildConstraint();
198     childConstraint.minSize.SetHeight(height);
199     childConstraint.maxSize.SetHeight(height);
200     childConstraint.percentReference.SetHeight(height);
201     childConstraint.minSize.SetWidth(width);
202     childConstraint.maxSize.SetWidth(width);
203     childConstraint.percentReference.SetWidth(width);
204     return childConstraint;
205 }
206 
ComputeInnerLayoutParam(LayoutConstraintF & innerLayout)207 void DialogLayoutAlgorithm::ComputeInnerLayoutParam(LayoutConstraintF& innerLayout)
208 {
209     auto maxSize = innerLayout.maxSize;
210     // Set different layout param for different devices
211     // TODO: need to use theme json to replace this function.
212     // get grid size type based on the screen where the dialog locate
213     auto gridSizeType = ScreenSystemManager::GetInstance().GetSize(maxSize.Width());
214     RefPtr<GridColumnInfo> columnInfo;
215     if (SystemProperties::GetDeviceType() == DeviceType::CAR) {
216         columnInfo = GridSystemManager::GetInstance().GetInfoByType(GridColumnType::CAR_DIALOG);
217     } else {
218         columnInfo = GridSystemManager::GetInstance().GetInfoByType(GridColumnType::DIALOG);
219     }
220     columnInfo->GetParent()->BuildColumnWidth(maxSize.Width());
221     auto width = GetMaxWidthBasedOnGridType(columnInfo, gridSizeType, SystemProperties::GetDeviceType());
222     if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN)) {
223         width =
224             SUBWINDOW_DIALOG_DEFAULT_WIDTH.ConvertToPx() < width ? SUBWINDOW_DIALOG_DEFAULT_WIDTH.ConvertToPx() : width;
225     }
226     if (SystemProperties::GetDeviceType() == DeviceType::WATCH) {
227         innerLayout.minSize = SizeF(width, 0.0);
228         innerLayout.maxSize = SizeF(width, maxSize.Height());
229     } else if (SystemProperties::GetDeviceType() == DeviceType::PHONE) {
230         if (SystemProperties::GetDeviceOrientation() == DeviceOrientation::LANDSCAPE) {
231             innerLayout.minSize = SizeF(width, 0.0);
232             innerLayout.maxSize = SizeF(width, maxSize.Height() * DIALOG_HEIGHT_RATIO_FOR_LANDSCAPE);
233         } else {
234             innerLayout.minSize = SizeF(width, 0.0);
235             innerLayout.maxSize = SizeF(width, maxSize.Height() * DIALOG_HEIGHT_RATIO);
236         }
237     } else if (SystemProperties::GetDeviceType() == DeviceType::CAR) {
238         innerLayout.minSize = SizeF(width, 0.0);
239         innerLayout.maxSize = SizeF(width, maxSize.Height() * DIALOG_HEIGHT_RATIO_FOR_CAR);
240     } else {
241         innerLayout.minSize = SizeF(width, 0.0);
242         innerLayout.maxSize = SizeF(width, maxSize.Height() * DIALOG_HEIGHT_RATIO);
243     }
244     if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN) && expandDisplay_) {
245         auto maxHeight = SystemProperties::GetDevicePhysicalHeight() *
246             EXPAND_DISPLAY_WINDOW_HEIGHT_RATIO * EXPAND_DISPLAY_DIALOG_HEIGHT_RATIO;
247         innerLayout.minSize = SizeF(SUBWINDOW_DIALOG_DEFAULT_WIDTH.ConvertToPx(), 0.0);
248         innerLayout.maxSize = SizeF(SUBWINDOW_DIALOG_DEFAULT_WIDTH.ConvertToPx(), maxHeight);
249     }
250     // update percentRef
251     innerLayout.percentReference = innerLayout.maxSize;
252 }
253 
GetMaxWidthBasedOnGridType(const RefPtr<GridColumnInfo> & info,GridSizeType type,DeviceType deviceType)254 double DialogLayoutAlgorithm::GetMaxWidthBasedOnGridType(
255     const RefPtr<GridColumnInfo>& info, GridSizeType type, DeviceType deviceType)
256 {
257     auto parentColumns = info->GetParent()->GetColumns();
258     if (gridCount_ >= 0) {
259         return info->GetWidth(std::min(gridCount_, parentColumns));
260     }
261 
262     int32_t deviceColumns;
263     if (deviceType == DeviceType::WATCH) {
264         if (type == GridSizeType::SM) {
265             deviceColumns = 3;
266         } else if (type == GridSizeType::MD) {
267             deviceColumns = 4;
268         } else if (type == GridSizeType::LG) {
269             deviceColumns = 5;
270         } else {
271             deviceColumns = 5;
272         }
273     } else if (deviceType == DeviceType::PHONE) {
274         if (type == GridSizeType::SM) {
275             deviceColumns = 4;
276         } else if (type == GridSizeType::MD) {
277             deviceColumns = 5;
278         } else if (type == GridSizeType::LG) {
279             deviceColumns = 6;
280         } else {
281             deviceColumns = 6;
282         }
283     } else if (deviceType == DeviceType::CAR) {
284         if (type == GridSizeType::SM) {
285             deviceColumns = 4;
286         } else if (type == GridSizeType::MD) {
287             deviceColumns = 6;
288         } else if (type == GridSizeType::LG) {
289             deviceColumns = 8;
290         } else {
291             deviceColumns = 8;
292         }
293     } else {
294         if (type == GridSizeType::SM) {
295             deviceColumns = 2;
296         } else if (type == GridSizeType::MD) {
297             deviceColumns = 3;
298         } else if (type == GridSizeType::LG) {
299             deviceColumns = 4;
300         } else {
301             deviceColumns = 4;
302         }
303     }
304     return info->GetWidth(std::min(deviceColumns, parentColumns));
305 }
306 
ProcessMaskRect(std::optional<DimensionRect> maskRect,const RefPtr<FrameNode> & dialog)307 void DialogLayoutAlgorithm::ProcessMaskRect(std::optional<DimensionRect> maskRect, const RefPtr<FrameNode>& dialog)
308 {
309     auto dialogContext = dialog->GetRenderContext();
310     CHECK_NULL_VOID(dialogContext);
311     auto hub = dialog->GetEventHub<DialogEventHub>();
312     auto width = maskRect->GetWidth();
313     auto height = maskRect->GetHeight();
314     auto offset = maskRect->GetOffset();
315     if (width.IsNegative()) {
316         width = FULLSCREEN;
317     }
318     if (height.IsNegative()) {
319         height = FULLSCREEN;
320     }
321     auto rootWidth = PipelineContext::GetCurrentRootWidth();
322     auto rootHeight = PipelineContext::GetCurrentRootHeight();
323     RectF rect = RectF(offset.GetX().ConvertToPxWithSize(rootWidth), offset.GetY().ConvertToPxWithSize(rootHeight),
324         width.ConvertToPxWithSize(rootWidth), height.ConvertToPxWithSize(rootHeight));
325     dialogContext->ClipWithRect(rect);
326     dialogContext->UpdateClipEdge(true);
327     auto gestureHub = hub->GetOrCreateGestureEventHub();
328     std::vector<DimensionRect> mouseResponseRegion;
329     mouseResponseRegion.emplace_back(width, height, offset);
330     gestureHub->SetMouseResponseRegion(mouseResponseRegion);
331     gestureHub->SetResponseRegion(mouseResponseRegion);
332 }
333 
Layout(LayoutWrapper * layoutWrapper)334 void DialogLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper)
335 {
336     subWindowId_ = SubwindowManager::GetInstance()->GetDialogSubWindowId();
337     CHECK_NULL_VOID(layoutWrapper);
338     auto frameNode = layoutWrapper->GetHostNode();
339     CHECK_NULL_VOID(frameNode);
340     auto dialogProp = DynamicCast<DialogLayoutProperty>(layoutWrapper->GetLayoutProperty());
341     CHECK_NULL_VOID(dialogProp);
342     auto pipelineContext = PipelineContext::GetCurrentContext();
343     CHECK_NULL_VOID(pipelineContext);
344     auto dialogTheme = pipelineContext->GetTheme<DialogTheme>();
345     CHECK_NULL_VOID(dialogTheme);
346     auto selfSize = layoutWrapper->GetGeometryNode()->GetFrameSize();
347     const auto& children = layoutWrapper->GetAllChildrenWithBuild();
348     if (children.empty()) {
349         return;
350     }
351     auto dialogPattern = frameNode->GetPattern<DialogPattern>();
352     CHECK_NULL_VOID(dialogPattern);
353     if (dialogPattern->GetDialogProperties().maskRect.has_value()) {
354         ProcessMaskRect(dialogPattern->GetDialogProperties().maskRect, frameNode);
355     }
356     auto child = children.front();
357     auto childSize = child->GetGeometryNode()->GetMarginFrameSize();
358     // is PcDevice MultipleDialog Offset to the bottom right
359     if (dialogTheme->GetMultipleDialogDisplay() != "stack" && !dialogProp->GetIsModal().value_or(true) &&
360         dialogProp->GetShowInSubWindowValue(false)) {
361         auto subWindow = SubwindowManager::GetInstance()->GetSubwindow(subWindowId_);
362         CHECK_NULL_VOID(subWindow);
363         auto subOverlayManager = subWindow->GetOverlayManager();
364         CHECK_NULL_VOID(subOverlayManager);
365         MultipleDialog(dialogProp, childSize, selfSize, subOverlayManager);
366     }
367     dialogOffset_ = dialogProp->GetDialogOffset().value_or(DimensionOffset());
368     alignment_ = dialogProp->GetDialogAlignment().value_or(DialogAlignment::DEFAULT);
369     topLeftPoint_ = ComputeChildPosition(childSize, dialogProp, selfSize);
370     if ((!dialogProp->GetIsModal().value_or(true) ||
371             (dialogProp->GetIsModal().value_or(true) && dialogProp->GetShowInSubWindowValue(false))) &&
372         !dialogProp->GetIsScenceBoardDialog().value_or(false)) {
373         ProcessMaskRect(
374             DimensionRect(Dimension(childSize.Width()), Dimension(childSize.Height()), DimensionOffset(topLeftPoint_)),
375             frameNode);
376     }
377     child->GetGeometryNode()->SetMarginFrameOffset(topLeftPoint_);
378     child->Layout();
379     SetSubWindowHotarea(dialogProp, childSize, selfSize, frameNode->GetId());
380 }
381 
SetSubWindowHotarea(const RefPtr<DialogLayoutProperty> & dialogProp,SizeF childSize,SizeF selfSize,int32_t frameNodeId)382 void DialogLayoutAlgorithm::SetSubWindowHotarea(
383     const RefPtr<DialogLayoutProperty>& dialogProp, SizeF childSize, SizeF selfSize, int32_t frameNodeId)
384 {
385     if (dialogProp->GetShowInSubWindowValue(false)) {
386         std::vector<Rect> rects;
387         Rect rect;
388         if (!dialogProp->GetIsScenceBoardDialog().value_or(false)) {
389             rect = Rect(topLeftPoint_.GetX(), topLeftPoint_.GetY(), childSize.Width(), childSize.Height());
390         } else {
391             rect = Rect(0.0f, 0.0f, selfSize.Width(), selfSize.Height());
392         }
393         rects.emplace_back(rect);
394         SubwindowManager::GetInstance()->SetDialogHotAreas(rects, frameNodeId, subWindowId_);
395     }
396 }
397 
IsDialogTouchingBoundary(OffsetF topLeftPoint,SizeF childSize,SizeF selfSize)398 bool DialogLayoutAlgorithm::IsDialogTouchingBoundary(OffsetF topLeftPoint, SizeF childSize, SizeF selfSize)
399 {
400     auto pipelineContext = PipelineContext::GetCurrentContext();
401     CHECK_NULL_RETURN(pipelineContext, false);
402     auto safeAreaInsets = pipelineContext->GetSafeArea();
403     float bottomSecurity = static_cast<float>(PORTRAIT_BOTTOM_SECURITY.ConvertToPx());
404     auto height = safeAreaInsets.bottom_.start == 0 ? selfSize.Height() - bottomSecurity : safeAreaInsets.bottom_.start;
405     auto width = selfSize.Width();
406     if (topLeftPoint.GetY() + childSize.Height() >= height) {
407         touchingBoundaryFlag_ = TouchingBoundaryType::TouchBottomBoundary;
408     } else if (topLeftPoint.GetX() + childSize.Width() >= width) {
409         touchingBoundaryFlag_ = TouchingBoundaryType::TouchRightBoundary;
410     } else {
411         touchingBoundaryFlag_ = TouchingBoundaryType::NotTouchBoundary;
412         return false;
413     }
414     return true;
415 }
416 
MultipleDialog(const RefPtr<DialogLayoutProperty> & dialogProp,const SizeF & childSize,const SizeF & selfSize,const RefPtr<OverlayManager> subOverlayManager)417 void DialogLayoutAlgorithm::MultipleDialog(const RefPtr<DialogLayoutProperty>& dialogProp, const SizeF& childSize,
418     const SizeF& selfSize, const RefPtr<OverlayManager> subOverlayManager)
419 {
420     std::map<int32_t, RefPtr<FrameNode>> DialogMap(
421         subOverlayManager->GetDialogMap().begin(), subOverlayManager->GetDialogMap().end());
422     int dialogMapSize = static_cast<int>(DialogMap.size());
423     if (dialogMapSize > 1) {
424         auto it = DialogMap.begin();
425         for (int i = 1; i < dialogMapSize - 1; i++) {
426             it++;
427         }
428         auto predialogProp = DynamicCast<DialogLayoutProperty>(it->second->GetLayoutProperty());
429         auto firstdialogProp = DynamicCast<DialogLayoutProperty>(DialogMap.begin()->second->GetLayoutProperty());
430         dialogProp->UpdateDialogOffset(predialogProp->GetDialogOffset().value_or(DimensionOffset()) +
431                                        DimensionOffset(MULTIPLE_DIALOG_OFFSET_X, MULTIPLE_DIALOG_OFFSET_Y));
432         dialogOffset_ = dialogProp->GetDialogOffset().value_or(DimensionOffset());
433         alignment_ = dialogProp->GetDialogAlignment().value_or(DialogAlignment::DEFAULT);
434         topLeftPoint_ = ComputeChildPosition(childSize, dialogProp, selfSize);
435         if (IsDialogTouchingBoundary(topLeftPoint_, childSize, selfSize)) {
436             if (touchingBoundaryFlag_ == TouchingBoundaryType::TouchBottomBoundary) {
437                 dialogProp->UpdateDialogOffset(
438                     DimensionOffset(predialogProp->GetDialogOffset().value_or(DimensionOffset()).GetX(),
439                         firstdialogProp->GetDialogOffset().value_or(DimensionOffset()).GetY()));
440             } else if (touchingBoundaryFlag_ == TouchingBoundaryType::TouchRightBoundary) {
441                 dialogProp->UpdateDialogOffset(firstdialogProp->GetDialogOffset().value_or(DimensionOffset()));
442             }
443         }
444     }
445 }
446 
ComputeChildPosition(const SizeF & childSize,const RefPtr<DialogLayoutProperty> & prop,const SizeF & selfSize)447 OffsetF DialogLayoutAlgorithm::ComputeChildPosition(
448     const SizeF& childSize, const RefPtr<DialogLayoutProperty>& prop, const SizeF& selfSize)
449 {
450     OffsetF topLeftPoint;
451     auto pipelineContext = PipelineContext::GetCurrentContext();
452     CHECK_NULL_RETURN(pipelineContext, OffsetF());
453     auto dialogTheme = pipelineContext->GetTheme<DialogTheme>();
454     const auto& layoutConstraint = prop->GetLayoutConstraint();
455     CHECK_NULL_RETURN(dialogTheme, OffsetF());
456     auto dialogOffsetX =
457         ConvertToPx(CalcLength(dialogOffset_.GetX()), layoutConstraint->scaleProperty, selfSize.Width());
458     auto dialogOffsetY =
459         ConvertToPx(CalcLength(dialogOffset_.GetY()), layoutConstraint->scaleProperty, selfSize.Height());
460     OffsetF dialogOffset = OffsetF(dialogOffsetX.value_or(0.0), dialogOffsetY.value_or(0.0));
461     auto maxSize = UpdateHeightWithSafeArea(layoutConstraint->maxSize);
462     if (!SetAlignmentSwitch(maxSize, childSize, topLeftPoint)) {
463         topLeftPoint = OffsetF(maxSize.Width() - childSize.Width(), maxSize.Height() - childSize.Height()) / HALF;
464     }
465     const auto& expandSafeAreaOpts = prop->GetSafeAreaExpandOpts();
466     bool needAvoidKeyboard = true;
467     if (expandSafeAreaOpts && (expandSafeAreaOpts->type | SAFE_AREA_TYPE_KEYBOARD)) {
468         needAvoidKeyboard = false;
469     }
470     return AdjustChildPosition(topLeftPoint, dialogOffset, childSize, needAvoidKeyboard);
471 }
472 
SetAlignmentSwitch(const SizeF & maxSize,const SizeF & childSize,OffsetF & topLeftPoint)473 bool DialogLayoutAlgorithm::SetAlignmentSwitch(const SizeF& maxSize, const SizeF& childSize, OffsetF& topLeftPoint)
474 {
475     if (alignment_ != DialogAlignment::DEFAULT) {
476         switch (alignment_) {
477             case DialogAlignment::TOP:
478                 topLeftPoint = OffsetF((maxSize.Width() - childSize.Width()) / 2.0, 0.0);
479                 break;
480             case DialogAlignment::CENTER:
481                 topLeftPoint =
482                     OffsetF(maxSize.Width() - childSize.Width(), maxSize.Height() - childSize.Height()) / 2.0;
483                 break;
484             case DialogAlignment::BOTTOM:
485                 topLeftPoint =
486                     OffsetF((maxSize.Width() - childSize.Width()) / 2.0, maxSize.Height() - childSize.Height());
487                 break;
488             case DialogAlignment::TOP_START:
489                 topLeftPoint = OffsetF(0.0, 0.0);
490                 break;
491             case DialogAlignment::TOP_END:
492                 topLeftPoint = OffsetF(maxSize.Width() - childSize.Width(), 0.0);
493                 break;
494             case DialogAlignment::CENTER_START:
495                 topLeftPoint = OffsetF(0.0, maxSize.Height() - childSize.Height()) / 2.0;
496                 break;
497             case DialogAlignment::CENTER_END:
498                 topLeftPoint =
499                     OffsetF(maxSize.Width() - childSize.Width(), (maxSize.Height() - childSize.Height()) / 2.0);
500                 break;
501             case DialogAlignment::BOTTOM_START:
502                 topLeftPoint = OffsetF(0.0, maxSize.Height() - childSize.Height());
503                 break;
504             case DialogAlignment::BOTTOM_END:
505                 topLeftPoint = OffsetF(maxSize.Width() - childSize.Width(), maxSize.Height() - childSize.Height());
506                 break;
507             default:
508                 topLeftPoint =
509                     OffsetF(maxSize.Width() - childSize.Width(), maxSize.Height() - childSize.Height()) / 2.0;
510                 break;
511         }
512         return true;
513     }
514 
515     auto container = Container::Current();
516     CHECK_NULL_RETURN(container, false);
517     auto displayInfo = container->GetDisplayInfo();
518     CHECK_NULL_RETURN(displayInfo, false);
519     auto foldStatus = displayInfo->GetFoldStatus();
520     if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN) && displayInfo->GetIsFoldable() &&
521         (foldStatus == FoldStatus::EXPAND || foldStatus == FoldStatus::HALF_FOLD)) {
522         topLeftPoint = OffsetF(maxSize.Width() - childSize.Width(), maxSize.Height() - childSize.Height()) / 2.0;
523         return true;
524     }
525 
526     bool version10OrLarger = Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_TEN);
527     if (version10OrLarger && SystemProperties::GetDeviceType() == DeviceType::PHONE) {
528         if (SystemProperties::GetDeviceOrientation() == DeviceOrientation::LANDSCAPE) {
529             topLeftPoint = OffsetF(maxSize.Width() - childSize.Width(), maxSize.Height() - childSize.Height()) / 2.0;
530             return true;
531         }
532         if (SystemProperties::GetDeviceOrientation() == DeviceOrientation::PORTRAIT) {
533             topLeftPoint = OffsetF((maxSize.Width() - childSize.Width()) / 2.0,
534                 maxSize.Height() - childSize.Height() - GetPaddingBottom());
535             return true;
536         }
537     }
538     return false;
539 }
540 
UpdateTouchRegion()541 void DialogLayoutAlgorithm::UpdateTouchRegion()
542 {
543     // TODO: update touch region is not completed.
544 }
545 
GetPaddingBottom() const546 double DialogLayoutAlgorithm::GetPaddingBottom() const
547 {
548     auto pipelineContext = PipelineContext::GetCurrentContext();
549     CHECK_NULL_RETURN(pipelineContext, 0);
550     auto dialogTheme = pipelineContext->GetTheme<DialogTheme>();
551     CHECK_NULL_RETURN(dialogTheme, 0);
552     auto bottom = dialogTheme->GetDefaultDialogMarginBottom();
553     return pipelineContext->NormalizeToPx(bottom);
554 }
555 
AdjustChildPosition(OffsetF & topLeftPoint,const OffsetF & dialogOffset,const SizeF & childSize,bool needAvoidKeyboard) const556 OffsetF DialogLayoutAlgorithm::AdjustChildPosition(
557     OffsetF& topLeftPoint, const OffsetF& dialogOffset, const SizeF& childSize, bool needAvoidKeyboard) const
558 {
559     auto pipelineContext = PipelineContext::GetCurrentContext();
560     CHECK_NULL_RETURN(pipelineContext, topLeftPoint + dialogOffset);
561     auto systemInset = pipelineContext->GetSafeArea();
562     if (!customSize_ && topLeftPoint.GetY() < systemInset.top_.end) {
563         topLeftPoint.SetY(systemInset.top_.end);
564     }
565     auto childOffset = topLeftPoint + dialogOffset;
566 
567     auto manager = pipelineContext->GetSafeAreaManager();
568     auto keyboardInsert = manager->GetKeyboardInset();
569     auto childBottom = childOffset.GetY() + childSize.Height();
570     auto paddingBottom = static_cast<float>(GetPaddingBottom());
571     if (needAvoidKeyboard && keyboardInsert.Length() > 0 && childBottom > (keyboardInsert.start - paddingBottom)) {
572         childOffset.SetY(childOffset.GetY() - (childBottom - (keyboardInsert.start - paddingBottom)));
573     }
574     return childOffset;
575 }
576 
UpdateHeightWithSafeArea(SizeF size)577 SizeF DialogLayoutAlgorithm::UpdateHeightWithSafeArea(SizeF size)
578 {
579     auto container = Container::Current();
580     auto currentId = Container::CurrentId();
581     CHECK_NULL_RETURN(container, size);
582     if (container->IsSubContainer()) {
583         currentId = SubwindowManager::GetInstance()->GetParentContainerId(Container::CurrentId());
584         container = AceEngine::Get().GetContainer(currentId);
585         ContainerScope scope(currentId);
586         auto pipelineContext = container->GetPipelineContext();
587         CHECK_NULL_RETURN(pipelineContext, size);
588         auto context = AceType::DynamicCast<NG::PipelineContext>(pipelineContext);
589         CHECK_NULL_RETURN(context, size);
590         auto safeArea = context->GetSafeArea();
591         size.MinusHeight(safeArea.bottom_.Length());
592     }
593     return size;
594 }
595 } // namespace OHOS::Ace::NG
596