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