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