1 /*
2 * Copyright (c) 2022-2023 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/menu/menu_layout_algorithm.h"
17
18 #include "base/subwindow/subwindow_manager.h"
19 #include "core/common/ace_engine.h"
20 #include "core/components/common/layout/grid_system_manager.h"
21 #include "core/components/container_modal/container_modal_constants.h"
22 #include "core/components_ng/pattern/menu/menu_theme.h"
23 #include "core/components_ng/pattern/menu/wrapper/menu_wrapper_pattern.h"
24 #include "core/components_ng/pattern/overlay/overlay_manager.h"
25 #include "core/components_ng/pattern/overlay/dialog_manager.h"
26 #include "core/components_ng/pattern/select_overlay/select_overlay_pattern.h"
27 #include "core/components_ng/property/measure_utils.h"
28
29 namespace OHOS::Ace::NG {
30
31 namespace {
32 constexpr uint32_t MIN_GRID_COUNTS = 2;
33 constexpr uint32_t GRID_COUNTS_4 = 4;
34 constexpr uint32_t GRID_COUNTS_6 = 6;
35 constexpr uint32_t GRID_COUNTS_8 = 8;
36 constexpr uint32_t GRID_COUNTS_12 = 12;
37 constexpr size_t ALIGNMENT_STEP_OFFSET = 2;
38 constexpr float HEIGHT_CONSTRAINT_FACTOR = 0.8;
39 constexpr float ARROW_WIDTH_FACTOR = 2.0;
40 constexpr float HALF = 2.0;
41
42 constexpr Dimension ARROW_RADIUS = 2.0_vp;
43 constexpr Dimension ARROW_P1_OFFSET_X = 8.0_vp;
44 constexpr Dimension ARROW_P2_OFFSET_X = 1.5_vp;
45 constexpr Dimension ARROW_P1_OFFSET_Y = 8.0_vp;
46 constexpr Dimension ARROW_P2_OFFSET_Y = 0.68_vp;
47
48 const std::map<Placement, std::vector<Placement>> PLACEMENT_STATES = {
49 { Placement::BOTTOM_LEFT,
50 {
51 Placement::BOTTOM_LEFT,
52 Placement::BOTTOM_RIGHT,
53 Placement::TOP_LEFT,
54 Placement::TOP_RIGHT,
55 Placement::RIGHT_TOP,
56 Placement::RIGHT_BOTTOM,
57 Placement::LEFT_TOP,
58 Placement::LEFT_BOTTOM,
59 Placement::NONE,
60 } },
61 { Placement::BOTTOM,
62 {
63 Placement::BOTTOM,
64 Placement::BOTTOM_LEFT,
65 Placement::BOTTOM_RIGHT,
66 Placement::TOP,
67 Placement::TOP_LEFT,
68 Placement::TOP_RIGHT,
69 Placement::RIGHT,
70 Placement::RIGHT_TOP,
71 Placement::RIGHT_BOTTOM,
72 Placement::LEFT,
73 Placement::LEFT_TOP,
74 Placement::LEFT_BOTTOM,
75 Placement::NONE,
76 } },
77 { Placement::BOTTOM_RIGHT,
78 {
79 Placement::BOTTOM_RIGHT,
80 Placement::BOTTOM_LEFT,
81 Placement::TOP_RIGHT,
82 Placement::TOP_LEFT,
83 Placement::RIGHT_BOTTOM,
84 Placement::RIGHT_TOP,
85 Placement::LEFT_BOTTOM,
86 Placement::LEFT_TOP,
87 Placement::NONE,
88 } },
89 { Placement::TOP_LEFT,
90 {
91 Placement::TOP_LEFT,
92 Placement::TOP_RIGHT,
93 Placement::BOTTOM_LEFT,
94 Placement::BOTTOM_RIGHT,
95 Placement::RIGHT_TOP,
96 Placement::RIGHT_BOTTOM,
97 Placement::LEFT_TOP,
98 Placement::LEFT_BOTTOM,
99 Placement::NONE,
100 } },
101 { Placement::TOP,
102 {
103 Placement::TOP,
104 Placement::TOP_LEFT,
105 Placement::TOP_RIGHT,
106 Placement::BOTTOM,
107 Placement::BOTTOM_LEFT,
108 Placement::BOTTOM_RIGHT,
109 Placement::RIGHT,
110 Placement::RIGHT_TOP,
111 Placement::RIGHT_BOTTOM,
112 Placement::LEFT,
113 Placement::LEFT_TOP,
114 Placement::LEFT_BOTTOM,
115 Placement::NONE,
116 } },
117 { Placement::TOP_RIGHT,
118 {
119 Placement::TOP_RIGHT,
120 Placement::TOP_LEFT,
121 Placement::BOTTOM_RIGHT,
122 Placement::BOTTOM_LEFT,
123 Placement::RIGHT_BOTTOM,
124 Placement::RIGHT_TOP,
125 Placement::LEFT_BOTTOM,
126 Placement::LEFT_TOP,
127 Placement::NONE,
128 } },
129 { Placement::LEFT_TOP,
130 {
131 Placement::LEFT_TOP,
132 Placement::LEFT_BOTTOM,
133 Placement::RIGHT_TOP,
134 Placement::RIGHT_BOTTOM,
135 Placement::BOTTOM_LEFT,
136 Placement::BOTTOM_RIGHT,
137 Placement::TOP_LEFT,
138 Placement::TOP_RIGHT,
139 Placement::NONE,
140 } },
141 { Placement::LEFT,
142 {
143 Placement::LEFT,
144 Placement::LEFT_TOP,
145 Placement::LEFT_BOTTOM,
146 Placement::RIGHT,
147 Placement::RIGHT_TOP,
148 Placement::RIGHT_BOTTOM,
149 Placement::BOTTOM,
150 Placement::BOTTOM_LEFT,
151 Placement::BOTTOM_RIGHT,
152 Placement::TOP,
153 Placement::TOP_LEFT,
154 Placement::TOP_RIGHT,
155 Placement::NONE,
156 } },
157 { Placement::LEFT_BOTTOM,
158 {
159 Placement::LEFT_BOTTOM,
160 Placement::LEFT_TOP,
161 Placement::RIGHT_BOTTOM,
162 Placement::RIGHT_TOP,
163 Placement::BOTTOM_RIGHT,
164 Placement::BOTTOM_LEFT,
165 Placement::TOP_RIGHT,
166 Placement::TOP_LEFT,
167 Placement::NONE,
168 } },
169 { Placement::RIGHT_TOP,
170 {
171 Placement::RIGHT_TOP,
172 Placement::RIGHT_BOTTOM,
173 Placement::LEFT_TOP,
174 Placement::LEFT_BOTTOM,
175 Placement::BOTTOM_LEFT,
176 Placement::BOTTOM_RIGHT,
177 Placement::TOP_LEFT,
178 Placement::TOP_RIGHT,
179 Placement::NONE,
180 } },
181 { Placement::RIGHT,
182 {
183 Placement::RIGHT,
184 Placement::RIGHT_TOP,
185 Placement::RIGHT_BOTTOM,
186 Placement::LEFT,
187 Placement::LEFT_TOP,
188 Placement::LEFT_BOTTOM,
189 Placement::BOTTOM,
190 Placement::BOTTOM_LEFT,
191 Placement::BOTTOM_RIGHT,
192 Placement::TOP,
193 Placement::TOP_LEFT,
194 Placement::TOP_RIGHT,
195 Placement::NONE,
196 } },
197 { Placement::RIGHT_BOTTOM,
198 {
199 Placement::RIGHT_BOTTOM,
200 Placement::RIGHT_TOP,
201 Placement::LEFT_BOTTOM,
202 Placement::LEFT_TOP,
203 Placement::BOTTOM_RIGHT,
204 Placement::BOTTOM_LEFT,
205 Placement::TOP_RIGHT,
206 Placement::TOP_LEFT,
207 Placement::NONE,
208 } },
209 };
210
211 // Provide positional enumeration order for select avoidance mode.
212 const std::map<Placement, std::vector<Placement>> SELECT_PLACEMENT_STATES = {
213 { Placement::BOTTOM_LEFT,
214 {
215 Placement::BOTTOM_LEFT,
216 Placement::BOTTOM_RIGHT,
217 Placement::TOP_LEFT,
218 Placement::TOP_RIGHT,
219 Placement::RIGHT_TOP,
220 Placement::RIGHT_BOTTOM,
221 Placement::LEFT_TOP,
222 Placement::LEFT_BOTTOM,
223 Placement::NONE,
224 } },
225 { Placement::BOTTOM,
226 {
227 Placement::BOTTOM,
228 Placement::BOTTOM_LEFT,
229 Placement::BOTTOM_RIGHT,
230 Placement::TOP,
231 Placement::TOP_LEFT,
232 Placement::TOP_RIGHT,
233 Placement::RIGHT_TOP,
234 Placement::RIGHT_BOTTOM,
235 Placement::LEFT_TOP,
236 Placement::LEFT_BOTTOM,
237 Placement::NONE,
238 } },
239 { Placement::BOTTOM_RIGHT,
240 {
241 Placement::BOTTOM_RIGHT,
242 Placement::BOTTOM_LEFT,
243 Placement::TOP_RIGHT,
244 Placement::TOP_LEFT,
245 Placement::RIGHT_TOP,
246 Placement::RIGHT_BOTTOM,
247 Placement::LEFT_TOP,
248 Placement::LEFT_BOTTOM,
249 Placement::NONE,
250 } },
251 };
252
GetMaxGridCounts(const RefPtr<GridColumnInfo> & columnInfo)253 uint32_t GetMaxGridCounts(const RefPtr<GridColumnInfo>& columnInfo)
254 {
255 CHECK_NULL_RETURN(columnInfo, GRID_COUNTS_8);
256 auto currentColumns = columnInfo->GetParent()->GetColumns();
257 auto maxGridCounts = GRID_COUNTS_8;
258 switch (currentColumns) {
259 case GRID_COUNTS_4:
260 maxGridCounts = GRID_COUNTS_4;
261 break;
262 case GRID_COUNTS_8:
263 maxGridCounts = GRID_COUNTS_6;
264 break;
265 case GRID_COUNTS_12:
266 maxGridCounts = GRID_COUNTS_8;
267 break;
268 case MIN_GRID_COUNTS:
269 maxGridCounts = MIN_GRID_COUNTS;
270 break;
271 default:
272 break;
273 }
274 return maxGridCounts;
275 }
276
GetMenuTheme(const RefPtr<FrameNode> & frameNode)277 RefPtr<NG::MenuTheme> GetMenuTheme(const RefPtr<FrameNode>& frameNode)
278 {
279 CHECK_NULL_RETURN(frameNode, nullptr);
280 auto pipelineContext = frameNode->GetContext();
281 CHECK_NULL_RETURN(pipelineContext, nullptr);
282 return pipelineContext->GetTheme<NG::MenuTheme>();
283 }
284
PrepareExtensionMenuConstraint(LayoutWrapper * layoutWrapper,LayoutConstraintF & childConstraint)285 void PrepareExtensionMenuConstraint(LayoutWrapper* layoutWrapper, LayoutConstraintF& childConstraint)
286 {
287 auto host = layoutWrapper->GetHostNode();
288 CHECK_NULL_VOID(host);
289 auto pattern = host->GetPattern<MenuPattern>();
290 CHECK_NULL_VOID(pattern);
291 auto props = AceType::DynamicCast<MenuLayoutProperty>(layoutWrapper->GetLayoutProperty());
292 if (pattern->IsSelectOverlayExtensionMenu() && props && props->HasMenuWidth() &&
293 props->GetMenuWidth()->ConvertToPx() < childConstraint.maxSize.Width()) {
294 auto minSizeWidth = childConstraint.minSize.Width();
295 minSizeWidth = fmax(static_cast<double>(minSizeWidth), props->GetMenuWidth()->ConvertToPx());
296 minSizeWidth = fmin(minSizeWidth, childConstraint.maxSize.Width());
297 childConstraint.minSize.SetWidth(minSizeWidth);
298 }
299 }
300 } // namespace
301
MenuLayoutAlgorithm(int32_t id,const std::string & tag,const std::optional<OffsetF> & lastPosition)302 MenuLayoutAlgorithm::MenuLayoutAlgorithm(int32_t id, const std::string& tag,
303 const std::optional<OffsetF>& lastPosition) : targetNodeId_(id), targetTag_(tag)
304 {
305 if (lastPosition.has_value()) {
306 lastPosition_ = lastPosition;
307 }
308 placementFuncMap_[Placement::TOP] = &MenuLayoutAlgorithm::GetPositionWithPlacementTop;
309 placementFuncMap_[Placement::TOP_LEFT] = &MenuLayoutAlgorithm::GetPositionWithPlacementTopLeft;
310 placementFuncMap_[Placement::TOP_RIGHT] = &MenuLayoutAlgorithm::GetPositionWithPlacementTopRight;
311 placementFuncMap_[Placement::BOTTOM] = &MenuLayoutAlgorithm::GetPositionWithPlacementBottom;
312 placementFuncMap_[Placement::BOTTOM_LEFT] = &MenuLayoutAlgorithm::GetPositionWithPlacementBottomLeft;
313 placementFuncMap_[Placement::BOTTOM_RIGHT] = &MenuLayoutAlgorithm::GetPositionWithPlacementBottomRight;
314 placementFuncMap_[Placement::LEFT] = &MenuLayoutAlgorithm::GetPositionWithPlacementLeft;
315 placementFuncMap_[Placement::LEFT_TOP] = &MenuLayoutAlgorithm::GetPositionWithPlacementLeftTop;
316 placementFuncMap_[Placement::LEFT_BOTTOM] = &MenuLayoutAlgorithm::GetPositionWithPlacementLeftBottom;
317 placementFuncMap_[Placement::RIGHT] = &MenuLayoutAlgorithm::GetPositionWithPlacementRight;
318 placementFuncMap_[Placement::RIGHT_TOP] = &MenuLayoutAlgorithm::GetPositionWithPlacementRightTop;
319 placementFuncMap_[Placement::RIGHT_BOTTOM] = &MenuLayoutAlgorithm::GetPositionWithPlacementRightBottom;
320
321 setHorizontal_ = { Placement::LEFT, Placement::LEFT_BOTTOM, Placement::LEFT_TOP,
322 Placement::RIGHT, Placement::RIGHT_BOTTOM, Placement::RIGHT_TOP };
323 setVertical_ = { Placement::TOP, Placement::TOP_LEFT, Placement::TOP_RIGHT,
324 Placement::BOTTOM, Placement::BOTTOM_LEFT, Placement::BOTTOM_RIGHT };
325
326 auto pipeline = PipelineContext::GetCurrentContextSafelyWithCheck();
327 CHECK_NULL_VOID(pipeline);
328 auto menuTheme = pipeline->GetTheme<NG::MenuTheme>();
329 CHECK_NULL_VOID(menuTheme);
330 previewScale_ = menuTheme->GetPreviewAfterAnimationScale();
331 if (LessOrEqual(previewScale_, 0.0f)) {
332 previewScale_ = 1.0f;
333 }
334
335 auto theme = pipeline->GetTheme<SelectTheme>();
336 CHECK_NULL_VOID(theme);
337 targetSecurity_ = static_cast<float>(theme->GetMenuTargetSecuritySpace().ConvertToPx());
338 }
339
~MenuLayoutAlgorithm()340 MenuLayoutAlgorithm::~MenuLayoutAlgorithm()
341 {
342 placementFuncMap_.clear();
343 setHorizontal_.clear();
344 setVertical_.clear();
345 }
346
ModifyPreviewMenuPlacement(LayoutWrapper * layoutWrapper)347 void MenuLayoutAlgorithm::ModifyPreviewMenuPlacement(LayoutWrapper* layoutWrapper)
348 {
349 CHECK_NULL_VOID(layoutWrapper);
350 auto props = AceType::DynamicCast<MenuLayoutProperty>(layoutWrapper->GetLayoutProperty());
351 CHECK_NULL_VOID(props);
352 auto hostNode = layoutWrapper->GetHostNode();
353 CHECK_NULL_VOID(hostNode);
354 auto pipeline = hostNode->GetContext();
355 CHECK_NULL_VOID(pipeline);
356 auto menuTheme = pipeline->GetTheme<NG::MenuTheme>();
357 CHECK_NULL_VOID(menuTheme);
358 auto hasPlacement = props->GetMenuPlacement().has_value();
359 if (!hasPlacement) {
360 if (menuTheme->GetNormalPlacement() &&
361 SystemProperties::GetDeviceOrientation() == DeviceOrientation::PORTRAIT) {
362 // for Phone with PORTRAIT orientation, default placement is BOTTOM_LEFT
363 placement_ = Placement::BOTTOM_LEFT;
364 props->UpdateMenuPlacement(placement_);
365 } else {
366 placement_ = Placement::RIGHT_TOP;
367 props->UpdateMenuPlacement(placement_);
368 }
369 }
370 }
371
Initialize(LayoutWrapper * layoutWrapper)372 void MenuLayoutAlgorithm::Initialize(LayoutWrapper* layoutWrapper)
373 {
374 CHECK_NULL_VOID(layoutWrapper);
375 // currently using click point as menu position
376 auto props = AceType::DynamicCast<MenuLayoutProperty>(layoutWrapper->GetLayoutProperty());
377 CHECK_NULL_VOID(props);
378 auto menuNode = layoutWrapper->GetHostNode();
379 CHECK_NULL_VOID(menuNode);
380 auto menuPattern = menuNode->GetPattern<MenuPattern>();
381 CHECK_NULL_VOID(menuPattern);
382 auto menuTheme = GetMenuTheme(menuNode);
383 CHECK_NULL_VOID(menuTheme);
384 auto beforeAnimationScale = menuPattern->GetPreviewBeforeAnimationScale();
385 auto afterAnimationScale = menuPattern->GetPreviewAfterAnimationScale();
386 dumpInfo_.previewBeginScale =
387 LessOrEqual(beforeAnimationScale, 0.0f) ? menuTheme->GetPreviewBeforeAnimationScale() : beforeAnimationScale;
388 dumpInfo_.previewEndScale =
389 LessOrEqual(afterAnimationScale, 0.0f) ? menuTheme->GetPreviewAfterAnimationScale() : afterAnimationScale;
390 previewScale_ = LessOrEqual(afterAnimationScale, 0.0f) ? previewScale_ : afterAnimationScale;
391 position_ = props->GetMenuOffset().value_or(OffsetF());
392 anchorPosition_ = props->GetAnchorPosition();
393 dumpInfo_.globalLocation = position_;
394 // user-set offset
395 positionOffset_ = props->GetPositionOffset().value_or(OffsetF());
396 dumpInfo_.offset = positionOffset_;
397 InitializePadding(layoutWrapper);
398 InitializeParam(layoutWrapper, menuPattern);
399 auto needModify = !menuPattern->IsSelectMenu() && !menuPattern->IsSelectOverlayDefaultModeRightClickMenu();
400 if (needModify) {
401 if (canExpandCurrentWindow_ && isExpandDisplay_) {
402 position_ += displayWindowRect_.GetOffset();
403 TAG_LOGI(AceLogTag::ACE_MENU, "original postion after applying displayWindowRect : %{public}s",
404 position_.ToString().c_str());
405 } else if (isUIExtensionSubWindow_ && !isExpandDisplay_) {
406 position_ += displayWindowRect_.GetOffset() - UIExtensionHostWindowRect_.GetOffset();
407 TAG_LOGI(AceLogTag::ACE_MENU, "original postion after applying UIExtensionHostWindowRect : %{public}s",
408 position_.ToString().c_str());
409 }
410 }
411 dumpInfo_.originPlacement =
412 PlacementUtils::ConvertPlacementToString(props->GetMenuPlacement().value_or(Placement::NONE));
413 placement_ = props->GetMenuPlacement().value_or(Placement::BOTTOM_LEFT);
414 if (menuPattern->IsSubMenu() && Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
415 placement_ = props->GetMenuPlacement().value_or(Placement::BOTTOM_RIGHT);
416 }
417 ModifyPositionToWrapper(layoutWrapper, position_);
418 if (menuPattern->GetPreviewMode() != MenuPreviewMode::NONE) {
419 ModifyPreviewMenuPlacement(layoutWrapper);
420 }
421 dumpInfo_.defaultPlacement = PlacementUtils::ConvertPlacementToString(placement_);
422 InitSpace(props, menuPattern);
423 holdEmbeddedMenuPosition_ = HoldEmbeddedMenuPosition(layoutWrapper);
424 auto previewRect = menuPattern->GetPreviewRect();
425 previewOriginOffset_ = menuPattern->GetPreviewOriginOffset();
426 previewOffset_ = previewRect.GetOffset();
427 previewSize_ = previewRect.GetSize();
428 }
429
InitializeSecurityPadding()430 void MenuLayoutAlgorithm::InitializeSecurityPadding()
431 {
432 float topSecurity = 0.0f;
433 float bottomSecurity = 0.0f;
434 if (SystemProperties::GetDeviceOrientation() == DeviceOrientation::PORTRAIT) {
435 if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_TWELVE)) {
436 topSecurity = static_cast<float>(PORTRAIT_TOP_SECURITY_API12.ConvertToPx());
437 bottomSecurity = static_cast<float>(PORTRAIT_BOTTOM_SECURITY_API12.ConvertToPx());
438 } else {
439 topSecurity = static_cast<float>(PORTRAIT_TOP_SECURITY.ConvertToPx());
440 bottomSecurity = static_cast<float>(PORTRAIT_BOTTOM_SECURITY.ConvertToPx());
441 }
442 } else {
443 topSecurity = static_cast<float>(LANDSCAPE_TOP_SECURITY.ConvertToPx());
444 bottomSecurity = static_cast<float>(LANDSCAPE_BOTTOM_SECURITY.ConvertToPx());
445 }
446 param_.topSecurity = topSecurity;
447 param_.bottomSecurity = bottomSecurity;
448 }
449
InitializeParam(LayoutWrapper * layoutWrapper,const RefPtr<MenuPattern> & menuPattern)450 void MenuLayoutAlgorithm::InitializeParam(LayoutWrapper* layoutWrapper, const RefPtr<MenuPattern>& menuPattern)
451 {
452 CHECK_NULL_VOID(layoutWrapper);
453 auto hostNode = layoutWrapper->GetHostNode();
454 CHECK_NULL_VOID(hostNode);
455 auto pipelineContext = DialogManager::GetMainPipelineContext(hostNode);
456 CHECK_NULL_VOID(pipelineContext);
457 auto safeAreaManager = pipelineContext->GetSafeAreaManager();
458 CHECK_NULL_VOID(safeAreaManager);
459 CHECK_NULL_VOID(menuPattern);
460 auto safeAreaInsets = OverlayManager::GetSafeAreaInsets(menuPattern->GetHost());
461 auto top = safeAreaInsets.top_.Length();
462 auto props = menuPattern->GetLayoutProperty<MenuLayoutProperty>();
463 CHECK_NULL_VOID(props);
464 auto bottom = GetBottomBySafeAreaManager(safeAreaManager, props, menuPattern);
465 InitializeSecurityPadding();
466 auto menuWindowRect = GetMenuWindowRectInfo(menuPattern);
467 float windowsOffsetX = static_cast<float>(menuWindowRect.GetOffset().GetX());
468 float windowsOffsetY = static_cast<float>(menuWindowRect.GetOffset().GetY());
469 SizeF windowGlobalSizeF(menuWindowRect.Width(), menuWindowRect.Height());
470 if (canExpandCurrentWindow_) {
471 param_.windowsOffsetX = windowsOffsetX;
472 param_.windowsOffsetY = windowsOffsetY;
473 } else {
474 param_.windowsOffsetX = 0;
475 param_.windowsOffsetY = 0;
476 }
477 param_.menuWindowRect = menuWindowRect;
478 param_.windowGlobalSizeF = windowGlobalSizeF;
479 param_.top = top;
480 param_.bottom = bottom;
481 param_.left = safeAreaInsets.left_.Length();
482 param_.right = safeAreaInsets.right_.Length();
483 param_.previewMenuGap = targetSecurity_;
484
485 InitWrapperRect(props, menuPattern);
486 InitializeLayoutRegionMargin(menuPattern);
487
488 param_.leftSecurity = paddingStart_;
489 param_.rightSecurity = paddingEnd_;
490 param_.wrapperRect = wrapperRect_;
491 menuPattern->SetMenuLayoutParam(param_);
492 }
493
InitializeLayoutRegionMargin(const RefPtr<MenuPattern> & menuPattern)494 void MenuLayoutAlgorithm::InitializeLayoutRegionMargin(const RefPtr<MenuPattern>& menuPattern)
495 {
496 CHECK_NULL_VOID(menuPattern);
497 auto menuWrapper = menuPattern->GetMenuWrapper();
498 CHECK_NULL_VOID(menuWrapper);
499 auto menuWrapperPattern = menuWrapper->GetPattern<MenuWrapperPattern>();
500 CHECK_NULL_VOID(menuWrapperPattern);
501
502 auto menuParam = menuWrapperPattern->GetMenuParam();
503 isPreviewContainScale_ = menuParam.isPreviewContainScale ||
504 menuParam.previewScaleMode.value_or(PreviewScaleMode::AUTO) == PreviewScaleMode::MAINTAIN;
505 previewScaleMode_ = menuParam.previewScaleMode;
506 availableLayoutAreaMode_ = menuParam.availableLayoutAreaMode;
507 if (!menuParam.layoutRegionMargin.has_value()) {
508 return;
509 }
510
511 auto marginProps = menuParam.layoutRegionMargin.value();
512 float left = marginProps.start.has_value()
513 ? marginProps.start.value().GetDimension().ConvertToPxWithSize(wrapperSize_.Width())
514 : paddingStart_;
515 float right = marginProps.end.has_value()
516 ? marginProps.end.value().GetDimension().ConvertToPxWithSize(wrapperSize_.Width())
517 : paddingEnd_;
518 float top = marginProps.top.has_value()
519 ? marginProps.top.value().GetDimension().ConvertToPxWithSize(wrapperSize_.Height())
520 : param_.topSecurity;
521 float bottom = marginProps.bottom.has_value()
522 ? marginProps.bottom.value().GetDimension().ConvertToPxWithSize(wrapperSize_.Height())
523 : param_.bottomSecurity;
524
525 if (LessNotEqual(left + right, wrapperSize_.Width())) {
526 paddingStart_ = left;
527 paddingEnd_ = right;
528 if (marginProps.start.has_value()) {
529 layoutRegionMargin_.left = left;
530 }
531 if (marginProps.end.has_value()) {
532 layoutRegionMargin_.right = right;
533 }
534 }
535
536 if (LessNotEqual(top + bottom, wrapperSize_.Height())) {
537 param_.topSecurity = top;
538 param_.bottomSecurity = bottom;
539 if (marginProps.top.has_value()) {
540 layoutRegionMargin_.top = top;
541 }
542 if (marginProps.bottom.has_value()) {
543 layoutRegionMargin_.bottom = bottom;
544 }
545 }
546 }
547
InitWrapperRect(const RefPtr<MenuLayoutProperty> & props,const RefPtr<MenuPattern> & menuPattern)548 void MenuLayoutAlgorithm::InitWrapperRect(
549 const RefPtr<MenuLayoutProperty>& props, const RefPtr<MenuPattern>& menuPattern)
550 {
551 if (canExpandCurrentWindow_ && isExpandDisplay_ && !isTargetNodeInSubwindow_) {
552 wrapperRect_ = param_.menuWindowRect;
553 wrapperSize_ = SizeF(wrapperRect_.Width(), wrapperRect_.Height());
554 dumpInfo_.wrapperRect = wrapperRect_;
555 width_ = wrapperRect_.Width();
556 height_ = wrapperRect_.Height();
557 TAG_LOGI(
558 AceLogTag::ACE_MENU, "InitWrapperRect with menuWindowRect : %{public}s", wrapperRect_.ToString().c_str());
559 return;
560 }
561 wrapperRect_.SetRect(0, 0, param_.menuWindowRect.Width(), param_.menuWindowRect.Height());
562 CHECK_NULL_VOID(menuPattern);
563 auto host = menuPattern->GetHost();
564 CHECK_NULL_VOID(host);
565 auto pipelineContext = DialogManager::GetMainPipelineContext(host);
566 CHECK_NULL_VOID(pipelineContext);
567 auto safeAreaManager = pipelineContext->GetSafeAreaManager();
568 CHECK_NULL_VOID(safeAreaManager);
569 // system safeArea(AvoidAreaType.TYPE_SYSTEM) only include status bar,now the bottom is 0
570 auto safeAreaInsets = OverlayManager::GetSafeAreaInsets(menuPattern->GetHost());
571 bottom_ = static_cast<double>(GetBottomBySafeAreaManager(safeAreaManager, props, menuPattern));
572 top_ = static_cast<double>(safeAreaInsets.top_.Length());
573 left_ = static_cast<double>(safeAreaInsets.left_.Length());
574 right_ = static_cast<double>(safeAreaInsets.right_.Length());
575 width_ = wrapperRect_.Width();
576 height_ = wrapperRect_.Height();
577 dumpInfo_.top = top_;
578 dumpInfo_.bottom = bottom_;
579 dumpInfo_.left = left_;
580 dumpInfo_.right = right_;
581 TAG_LOGI(AceLogTag::ACE_MENU,
582 "safeAreaInsets: (top: %{public}f, bottom: %{public}f, left: %{public}f, right: %{public}f)", top_, bottom_,
583 left_, right_);
584 auto windowManager = pipelineContext->GetWindowManager();
585 auto isContainerModal = pipelineContext->GetWindowModal() == WindowModal::CONTAINER_MODAL && windowManager &&
586 windowManager->GetWindowMode() == WindowMode::WINDOW_MODE_FLOATING;
587
588 if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN)) {
589 if (!canExpandCurrentWindow_ && isContainerModal) {
590 LimitContainerModalMenuRect(width_, height_, menuPattern);
591 }
592 }
593 wrapperRect_.SetRect(left_, top_, width_ - left_ - right_, height_ - top_ - bottom_);
594 wrapperSize_ = SizeF(wrapperRect_.Width(), wrapperRect_.Height());
595 dumpInfo_.wrapperRect = wrapperRect_;
596 TAG_LOGI(AceLogTag::ACE_MENU, "InitWrapperRect with safeAreaInsets : %{public}s", wrapperRect_.ToString().c_str());
597 }
598
UpdateWrapperRectForHoverMode(const RefPtr<MenuLayoutProperty> & props,const RefPtr<MenuPattern> & menuPattern,double creaseHeightOffset)599 void MenuLayoutAlgorithm::UpdateWrapperRectForHoverMode(
600 const RefPtr<MenuLayoutProperty>& props, const RefPtr<MenuPattern>& menuPattern, double creaseHeightOffset)
601 {
602 auto container = Container::CurrentSafelyWithCheck();
603 CHECK_NULL_VOID(container);
604 auto displayInfo = container->GetDisplayInfo();
605 CHECK_NULL_VOID(displayInfo);
606 auto foldCreaseRects = displayInfo->GetCurrentFoldCreaseRegion();
607 double creaseTop = 0.0;
608 double creaseBottom = 0.0;
609 double creaseHeight = 0.0;
610 if (!foldCreaseRects.empty()) {
611 auto foldCrease = foldCreaseRects.front();
612 creaseTop = foldCrease.Top() - creaseHeightOffset;
613 creaseBottom = foldCrease.Bottom() - creaseHeightOffset;
614 creaseHeight = foldCrease.Height();
615 }
616 double offsetY = 0.0;
617 if (props->GetMenuPlacement().has_value()) {
618 offsetY = targetOffset_.GetY();
619 } else {
620 offsetY = position_.GetY();
621 }
622 auto isShowInSubWindow = canExpandCurrentWindow_ && isExpandDisplay_ && !isTargetNodeInSubwindow_;
623 auto windowsOffsetY = isShowInSubWindow ? param_.windowsOffsetY : 0;
624 if (LessNotEqual(offsetY, creaseTop)) {
625 wrapperRect_.SetRect(left_, top_ + windowsOffsetY, width_ - left_ - right_, creaseTop - top_);
626 } else if (GreatNotEqual(offsetY, creaseBottom)) {
627 wrapperRect_.SetRect(left_, creaseBottom, width_ - left_ - right_,
628 windowsOffsetY + height_ - creaseBottom - bottom_);
629 } else {
630 wrapperRect_.SetRect(left_, top_ + windowsOffsetY, width_ - left_ - right_, height_ - top_ - bottom_);
631 }
632 dumpInfo_.wrapperRect = wrapperRect_;
633 TAG_LOGI(AceLogTag::ACE_MENU, "Update wrapperRect for hoverMode : %{public}s", wrapperRect_.ToString().c_str());
634 }
635
GetBottomBySafeAreaManager(const RefPtr<SafeAreaManager> & safeAreaManager,const RefPtr<MenuLayoutProperty> & props,const RefPtr<MenuPattern> & menuPattern)636 uint32_t MenuLayoutAlgorithm::GetBottomBySafeAreaManager(const RefPtr<SafeAreaManager>& safeAreaManager,
637 const RefPtr<MenuLayoutProperty>& props, const RefPtr<MenuPattern>& menuPattern)
638 {
639 CHECK_NULL_RETURN(menuPattern, 0);
640 auto safeAreaInsets = OverlayManager::GetSafeAreaInsets(menuPattern->GetHost());
641 auto bottom = safeAreaInsets.bottom_.Length();
642 CHECK_NULL_RETURN(safeAreaManager, 0);
643 auto keyboardHeight = safeAreaManager->GetKeyboardInsetImpl().Length();
644 if ((menuPattern->IsSelectOverlayExtensionMenu() || menuPattern->IsSelectOverlayRightClickMenu()) &&
645 GreatNotEqual(keyboardHeight, 0)) {
646 bottom = keyboardHeight;
647 }
648
649 CHECK_NULL_RETURN(props, 0);
650 // Determine whether the menu is an AI menu
651 if (props->GetIsRectInTargetValue(false)) {
652 if (LessOrEqual(keyboardHeight, 0)) {
653 keyboardHeight = safeAreaManager->GetkeyboardHeightConsideringUIExtension();
654 }
655 if (GreatNotEqual(keyboardHeight, 0)) {
656 bottom = keyboardHeight;
657 }
658 }
659 return bottom;
660 }
661
InitSpace(const RefPtr<MenuLayoutProperty> & props,const RefPtr<MenuPattern> & menuPattern)662 void MenuLayoutAlgorithm::InitSpace(const RefPtr<MenuLayoutProperty>& props, const RefPtr<MenuPattern>& menuPattern)
663 {
664 auto targetSize = props->GetTargetSizeValue(SizeF());
665 if (props->GetMenuPlacement().has_value()) {
666 auto targetSecurity = targetSecurity_;
667 topSpace_ = std::max(0.0, targetOffset_.GetY() - targetSecurity - paddingTop_ - wrapperRect_.Top());
668 bottomSpace_ = std::max(0.0,
669 wrapperRect_.Bottom() - targetOffset_.GetY() - targetSize_.Height() - targetSecurity - paddingBottom_);
670 if (NearZero(topSpace_) && NearZero(bottomSpace_)) {
671 bottomSpace_ = wrapperRect_.Bottom() - position_.GetY() - paddingTop_;
672 }
673 leftSpace_ = std::max(0.0, wrapperRect_.Left() + targetOffset_.GetX() - paddingStart_ - targetSecurity);
674 rightSpace_ = std::max(
675 0.0, wrapperRect_.Right() - targetSize_.Width() - targetSecurity - paddingStart_ - paddingEnd_);
676 if (NearZero(leftSpace_) && NearZero(rightSpace_)) {
677 leftSpace_ = position_.GetX();
678 rightSpace_ = wrapperRect_.Right() - leftSpace_;
679 }
680 } else {
681 if (canExpandCurrentWindow_ || !Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN)) {
682 topSpace_ = position_.GetY() - targetSize.Height() - paddingTop_ - wrapperRect_.Top();
683 bottomSpace_ = wrapperRect_.Bottom() - position_.GetY() - paddingBottom_;
684 } else {
685 topSpace_ = position_.GetY() - wrapperRect_.Top() - paddingTop_;
686 bottomSpace_ = wrapperRect_.Bottom() - position_.GetY() - paddingTop_;
687 }
688 leftSpace_ = position_.GetX() - paddingStart_;
689 rightSpace_ = wrapperRect_.Right() - position_.GetX() - paddingEnd_;
690 }
691 }
692
InitializePadding(LayoutWrapper * layoutWrapper)693 void MenuLayoutAlgorithm::InitializePadding(LayoutWrapper* layoutWrapper)
694 {
695 if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
696 InitializePaddingAPI12(layoutWrapper);
697 return;
698 }
699 auto menuNode = layoutWrapper->GetHostNode();
700 CHECK_NULL_VOID(menuNode);
701 auto menuPattern = menuNode->GetPattern<MenuPattern>();
702 CHECK_NULL_VOID(menuPattern);
703 auto theme = GetCurrentSelectTheme(menuNode);
704 CHECK_NULL_VOID(theme);
705 margin_ = static_cast<float>(theme->GetMenuPadding().ConvertToPx());
706 optionPadding_ = margin_;
707 paddingStart_ = static_cast<float>(theme->GetDefaultPaddingStart().ConvertToPx());
708 paddingEnd_ = static_cast<float>(theme->GetDefaultPaddingEnd().ConvertToPx());
709 paddingTop_ = static_cast<float>(theme->GetDefaultPaddingTop().ConvertToPx());
710 paddingBottom_ = static_cast<float>(theme->GetDefaultPaddingBottomFixed().ConvertToPx());
711 }
712
InitializePaddingAPI12(LayoutWrapper * layoutWrapper)713 void MenuLayoutAlgorithm::InitializePaddingAPI12(LayoutWrapper* layoutWrapper)
714 {
715 auto menuNode = layoutWrapper->GetHostNode();
716 CHECK_NULL_VOID(menuNode);
717 auto menuPattern = menuNode->GetPattern<MenuPattern>();
718 CHECK_NULL_VOID(menuPattern);
719 auto theme = GetCurrentSelectTheme(menuNode);
720 CHECK_NULL_VOID(theme);
721
722 margin_ = static_cast<float>(theme->GetMenuPadding().ConvertToPx());
723 optionPadding_ = margin_;
724 if (!canExpandCurrentWindow_) {
725 paddingStart_ = static_cast<float>(theme->GetMenuLargeMargin().ConvertToPx());
726 paddingEnd_ = static_cast<float>(theme->GetMenuLargeMargin().ConvertToPx());
727 } else {
728 paddingStart_ = static_cast<float>(theme->GetMenuMediumMargin().ConvertToPx());
729 paddingEnd_ = static_cast<float>(theme->GetMenuMediumMargin().ConvertToPx());
730 }
731 }
732
ModifyPositionToWrapper(LayoutWrapper * layoutWrapper,OffsetF & position)733 void MenuLayoutAlgorithm::ModifyPositionToWrapper(LayoutWrapper* layoutWrapper, OffsetF& position)
734 {
735 auto menu = layoutWrapper->GetHostNode();
736 CHECK_NULL_VOID(menu);
737 auto wrapper = AceType::DynamicCast<FrameNode>(menu->GetParent());
738 CHECK_NULL_VOID(wrapper);
739
740 OffsetF wrapperOffset;
741 // minus wrapper offset in LayoutFullScreen
742 auto wrapperLayoutProps = wrapper->GetLayoutProperty();
743 CHECK_NULL_VOID(wrapperLayoutProps);
744 auto&& safeAreaInsets = wrapperLayoutProps->GetSafeAreaInsets();
745 if (safeAreaInsets) {
746 wrapperOffset +=
747 OffsetF(static_cast<float>(safeAreaInsets->left_.end), static_cast<float>(safeAreaInsets->top_.end));
748 position -= wrapperOffset;
749 }
750
751 auto menuPattern = menu->GetPattern<MenuPattern>();
752 CHECK_NULL_VOID(menuPattern);
753 bool isSubMenu = menuPattern->IsSubMenu() || menuPattern->IsSelectOverlaySubMenu();
754 if ((menuPattern->IsContextMenu() || (isSubMenu && Container::CurrentId() >= MIN_SUBCONTAINER_ID) ||
755 canExpandCurrentWindow_) &&
756 (targetTag_ != V2::SELECT_ETS_TAG)) {
757 // no need to modify for context menu, because context menu wrapper is full screen.
758 return;
759 }
760 // minus wrapper offset in floating window
761 auto pipelineContext = DialogManager::GetMainPipelineContext(menu);
762 CHECK_NULL_VOID(pipelineContext);
763 auto windowManager = pipelineContext->GetWindowManager();
764 auto isContainerModal = pipelineContext->GetWindowModal() == WindowModal::CONTAINER_MODAL;
765 if (isContainerModal) {
766 wrapperOffset = OffsetF(0.0f, static_cast<float>(pipelineContext->GetCustomTitleHeight().ConvertToPx()));
767 if (windowManager && windowManager->GetWindowMode() == WindowMode::WINDOW_MODE_FLOATING) {
768 wrapperOffset += OffsetF(static_cast<float>(CONTAINER_BORDER_WIDTH.ConvertToPx()),
769 static_cast<float>(CONTAINER_BORDER_WIDTH.ConvertToPx()));
770 }
771 position -= wrapperOffset;
772 }
773 }
774
IsNodeOnRootTree(const RefPtr<FrameNode> & frameNode)775 bool IsNodeOnRootTree(const RefPtr<FrameNode>& frameNode)
776 {
777 auto parent = frameNode->GetParent();
778 while (parent) {
779 if (parent->GetTag() == V2::ROOT_ETS_TAG) {
780 return true;
781 }
782 parent = parent->GetParent();
783 }
784 TAG_LOGW(AceLogTag::ACE_MENU, "node %{public}d not no root tree", frameNode->GetId());
785 return false;
786 }
787
788 // Called to perform layout render node and child.
Measure(LayoutWrapper * layoutWrapper)789 void MenuLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper)
790 {
791 auto targetNode = FrameNode::GetFrameNode(targetTag_, targetNodeId_);
792 // if targetNode == nullptr, it means the menu is subMenu or multiMenu
793 if (targetNode && !IsNodeOnRootTree(targetNode)) {
794 TAG_LOGW(AceLogTag::ACE_MENU, "measure return because targetNode %{public}d not no root tree", targetNodeId_);
795 return;
796 }
797 // initialize screen size and menu position
798 CHECK_NULL_VOID(layoutWrapper);
799 MenuDumpInfo dumpInfo;
800 auto menuNode = layoutWrapper->GetHostNode();
801 CHECK_NULL_VOID(menuNode);
802 auto menuPattern = menuNode->GetPattern<MenuPattern>();
803 CHECK_NULL_VOID(menuPattern);
804 auto menuLayoutProperty = AceType::DynamicCast<MenuLayoutProperty>(layoutWrapper->GetLayoutProperty());
805 CHECK_NULL_VOID(menuLayoutProperty);
806 auto isContextMenu = menuPattern->IsContextMenu();
807 auto isShowInSubWindow = menuLayoutProperty->GetShowInSubWindowValue(true) || isContextMenu;
808 InitCanExpandCurrentWindow(isShowInSubWindow, layoutWrapper);
809 Initialize(layoutWrapper);
810 if (!targetTag_.empty()) {
811 InitTargetSizeAndPosition(layoutWrapper, menuPattern->IsContextMenu(), menuPattern);
812 }
813 CalcWrapperRectForHoverMode(menuPattern);
814
815 const auto& constraint = menuLayoutProperty->GetLayoutConstraint();
816 if (!constraint) {
817 return;
818 }
819
820 auto idealSize = CreateIdealSize(
821 constraint.value(), Axis::VERTICAL, menuLayoutProperty->GetMeasureType(MeasureType::MATCH_CONTENT), true);
822 const auto& padding = menuLayoutProperty->CreatePaddingAndBorder();
823 MinusPaddingToSize(padding, idealSize);
824
825 // calculate menu main size
826 auto childConstraint = CreateChildConstraint(layoutWrapper);
827 if (menuPattern->IsSelectMenu() && menuPattern->GetHasOptionWidth()) {
828 auto selectMenuWidth = menuPattern->GetSelectMenuWidth();
829 childConstraint.maxSize.SetWidth(selectMenuWidth);
830 childConstraint.parentIdealSize.SetWidth(selectMenuWidth);
831 childConstraint.selfIdealSize.SetWidth(selectMenuWidth);
832 }
833
834 // The menu width child Constraint is added to the 2in1 device in API13
835 UpdateChildConstraintByDevice(menuPattern, childConstraint, constraint.value());
836
837 auto parentItem = menuPattern->GetParentMenuItem();
838 CalculateIdealSize(layoutWrapper, childConstraint, padding, idealSize, parentItem);
839 }
840
CalcWrapperRectForHoverMode(const RefPtr<MenuPattern> & menuPattern)841 void MenuLayoutAlgorithm::CalcWrapperRectForHoverMode(const RefPtr<MenuPattern>& menuPattern)
842 {
843 CHECK_NULL_VOID(menuPattern);
844 auto menuNode = menuPattern->GetHost();
845 CHECK_NULL_VOID(menuNode);
846 auto pipelineContext = menuNode->GetContext();
847 CHECK_NULL_VOID(pipelineContext);
848 auto menuWrapper = menuPattern->GetMenuWrapper();
849 CHECK_NULL_VOID(menuWrapper);
850 auto menuWrapperPattern = menuWrapper->GetPattern<MenuWrapperPattern>();
851 CHECK_NULL_VOID(menuWrapperPattern);
852 auto menuLayoutProperty = menuNode->GetLayoutProperty<MenuLayoutProperty>();
853 CHECK_NULL_VOID(menuLayoutProperty);
854 if (OverlayManager::IsNeedAvoidFoldCrease(menuNode, true, isExpandDisplay_, menuWrapperPattern->GetHoverMode())) {
855 auto creaseHeightOffset =
856 pipelineContext->GetDisplayAvailableRect().Top() + menuNode->GetParentGlobalOffsetWithSafeArea().GetY();
857 UpdateWrapperRectForHoverMode(menuLayoutProperty, menuPattern, creaseHeightOffset);
858 }
859 }
860
CheckChildConstraintCondition(const RefPtr<MenuPattern> & menuPattern)861 bool MenuLayoutAlgorithm::CheckChildConstraintCondition(const RefPtr<MenuPattern>& menuPattern)
862 {
863 CHECK_NULL_RETURN(menuPattern, false);
864 CHECK_NULL_RETURN(Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_THIRTEEN), false);
865 if (menuPattern->IsSubMenu()) {
866 auto parentItem = menuPattern->GetParentMenuItem();
867 CHECK_NULL_RETURN(parentItem, false);
868 auto parentPattern = parentItem->GetPattern<MenuItemPattern>();
869 CHECK_NULL_RETURN(parentPattern, false);
870 auto expandingMode = parentPattern->GetExpandingMode();
871 if (expandingMode == SubMenuExpandingMode::SIDE) {
872 return true;
873 }
874 return false;
875 }
876
877 if (menuPattern->IsMenu() || menuPattern->IsContextMenu()) {
878 return true;
879 }
880 return false;
881 }
882
UpdateChildConstraintByDevice(const RefPtr<MenuPattern> & menuPattern,LayoutConstraintF & childConstraint,const LayoutConstraintF & layoutConstraint)883 void MenuLayoutAlgorithm::UpdateChildConstraintByDevice(const RefPtr<MenuPattern>& menuPattern,
884 LayoutConstraintF& childConstraint, const LayoutConstraintF& layoutConstraint)
885 {
886 CHECK_NULL_VOID(menuPattern);
887
888 // only 2in1 device has restrictions on the menu width in API13
889 if (!CheckChildConstraintCondition(menuPattern)) {
890 return;
891 }
892 auto host = menuPattern->GetHost();
893 CHECK_NULL_VOID(host);
894 auto pipeline = host->GetContext();
895 CHECK_NULL_VOID(pipeline);
896 auto theme = pipeline->GetTheme<SelectTheme>();
897 CHECK_NULL_VOID(theme);
898
899 auto expandDisplay = theme->GetExpandDisplay();
900 CHECK_NULL_VOID(expandDisplay);
901
902 auto menuMinWidth = theme->GetMenuMinWidth().ConvertToPx();
903 auto menuDefaultWidth = theme->GetMenuDefaultWidth().ConvertToPx();
904 auto menuMaxWidth = theme->GetMenuMaxWidthRatio() * pipeline->GetDisplayWindowRectInfo().Width();
905 double minWidth = 0.0f;
906 double maxWidth = 0.0f;
907
908 auto firstMenu = menuPattern->GetFirstInnerMenu();
909 CHECK_NULL_VOID(firstMenu);
910 auto layoutProperty = firstMenu->GetLayoutProperty<MenuLayoutProperty>();
911 CHECK_NULL_VOID(layoutProperty);
912 if (layoutProperty->HasMenuWidth()) {
913 auto menuWidth = layoutProperty->GetMenuWidthValue();
914 auto menuWidthPX = (menuWidth.Unit() == DimensionUnit::PERCENT) ?
915 menuWidth.Value() * layoutConstraint.percentReference.Width() : menuWidth.ConvertToPx();
916 if (LessNotEqual(menuWidthPX, menuMinWidth) || GreatNotEqual(menuWidthPX, menuMaxWidth)) {
917 minWidth = menuDefaultWidth;
918 maxWidth = menuMaxWidth;
919 } else {
920 minWidth = menuWidthPX;
921 maxWidth = menuWidthPX;
922 }
923 } else {
924 minWidth = menuDefaultWidth;
925 maxWidth = menuMaxWidth;
926 }
927 childConstraint.minSize.SetWidth(minWidth);
928 childConstraint.maxSize.SetWidth(maxWidth);
929 }
930
UpdateSelectFocus(LayoutWrapper * layoutWrapper,LayoutConstraintF & childConstraint)931 void MenuLayoutAlgorithm::UpdateSelectFocus(LayoutWrapper* layoutWrapper, LayoutConstraintF& childConstraint)
932 {
933 auto host = layoutWrapper->GetHostNode();
934 CHECK_NULL_VOID(host);
935 auto pattern = host->GetPattern<MenuPattern>();
936 CHECK_NULL_VOID(pattern);
937 if (!pattern->IsSelectMenu()) {
938 return;
939 }
940 for (const auto& child : layoutWrapper->GetAllChildrenWithBuild()) {
941 auto childHost = child->GetHostNode();
942 CHECK_NULL_VOID(childHost);
943 // when use contentModifier the origin scroll node should be hide.
944 auto needHide = pattern->UseContentModifier() && childHost->GetId() != pattern->GetBuilderId();
945 auto layoutProperty = childHost->GetLayoutProperty();
946 CHECK_NULL_VOID(layoutProperty);
947 layoutProperty->UpdateVisibility(needHide ? VisibleType::GONE : VisibleType::VISIBLE);
948 auto focusHub = childHost->GetOrCreateFocusHub();
949 CHECK_NULL_VOID(focusHub);
950 focusHub->SetShow(!needHide);
951 // when use contentModifier the default focus should change to the first custom node.
952 if (childHost->GetChildren().empty()) {
953 return;
954 }
955 auto columnChild = childHost->GetChildAtIndex(0);
956 CHECK_NULL_VOID(columnChild);
957 if (columnChild->GetChildren().empty()) {
958 return;
959 }
960 auto optionChild = columnChild->GetChildAtIndex(0);
961 CHECK_NULL_VOID(optionChild);
962 auto optionNode = AceType::DynamicCast<FrameNode>(optionChild);
963 CHECK_NULL_VOID(optionNode);
964 auto optionFocusHub = optionNode->GetOrCreateFocusHub();
965 CHECK_NULL_VOID(optionFocusHub);
966 optionFocusHub->SetIsDefaultFocus(!needHide);
967 }
968 }
969
GetFirstItemBottomPositionY(const RefPtr<FrameNode> & menu)970 float MenuLayoutAlgorithm::GetFirstItemBottomPositionY(const RefPtr<FrameNode>& menu)
971 {
972 CHECK_NULL_RETURN(menu, 0.0f);
973 auto menuPattern = menu->GetPattern<MenuPattern>();
974 CHECK_NULL_RETURN(menuPattern, 0.0f);
975 auto firstMenuItem = menuPattern->GetFirstMenuItem();
976 CHECK_NULL_RETURN(firstMenuItem, 0.0f);
977 auto geometryNode = firstMenuItem->GetGeometryNode();
978 CHECK_NULL_RETURN(geometryNode, 0.0f);
979 return firstMenuItem->GetPaintRectOffset(false, true).GetY() + geometryNode->GetMarginFrameSize().Height();
980 }
981
GetLastItemTopPositionY(const RefPtr<FrameNode> & menu)982 float MenuLayoutAlgorithm::GetLastItemTopPositionY(const RefPtr<FrameNode>& menu)
983 {
984 CHECK_NULL_RETURN(menu, 0.0f);
985 auto menuPattern = menu->GetPattern<MenuPattern>();
986 CHECK_NULL_RETURN(menuPattern, 0.0f);
987 auto lastMenuItem = menuPattern->GetLastMenuItem();
988 CHECK_NULL_RETURN(lastMenuItem, 0.0f);
989 return lastMenuItem->GetPaintRectOffset(false, true).GetY();
990 }
991
GetMenuBottomPositionY(const RefPtr<FrameNode> & menu)992 float MenuLayoutAlgorithm::GetMenuBottomPositionY(const RefPtr<FrameNode>& menu)
993 {
994 CHECK_NULL_RETURN(menu, 0.0f);
995 auto geometryNode = menu->GetGeometryNode();
996 CHECK_NULL_RETURN(geometryNode, 0.0f);
997 return menu->GetPaintRectOffset(false, true).GetY() + geometryNode->GetMarginFrameSize().Height();
998 }
999
isContainerModal(const RefPtr<FrameNode> & node)1000 bool MenuLayoutAlgorithm::isContainerModal(const RefPtr<FrameNode>& node)
1001 {
1002 CHECK_NULL_RETURN(node, false);
1003 auto pipeline = node->GetContext();
1004 CHECK_NULL_RETURN(pipeline, false);
1005 return (!canExpandCurrentWindow_) && pipeline->GetWindowModal() == WindowModal::CONTAINER_MODAL;
1006 }
1007
GetContainerModalOffsetY(const RefPtr<FrameNode> & node)1008 float MenuLayoutAlgorithm::GetContainerModalOffsetY(const RefPtr<FrameNode>& node)
1009 {
1010 float defaultOffset = 0.0f;
1011 CHECK_NULL_RETURN(node, defaultOffset);
1012 auto pipeline = node->GetContext();
1013 CHECK_NULL_RETURN(pipeline, defaultOffset);
1014 return static_cast<float>(pipeline->GetCustomTitleHeight().ConvertToPx())
1015 + static_cast<float>(CONTAINER_BORDER_WIDTH.ConvertToPx());
1016 }
1017
CalcSubMenuMaxHeightWithPreview(RefPtr<FrameNode> parentMenu,LayoutConstraintF & childConstraint,float lastItemTopPositionY,float firstItemBottomPositionY,float parentMenuPositionY)1018 float MenuLayoutAlgorithm::CalcSubMenuMaxHeightWithPreview(RefPtr<FrameNode> parentMenu,
1019 LayoutConstraintF& childConstraint,
1020 float lastItemTopPositionY, float firstItemBottomPositionY, float parentMenuPositionY)
1021 {
1022 float subMenuMaxHeight = childConstraint.maxSize.Height();
1023 CHECK_NULL_RETURN(parentMenu, subMenuMaxHeight);
1024 auto parentMenuPattern = parentMenu->GetPattern<MenuPattern>();
1025 CHECK_NULL_RETURN(parentMenuPattern, subMenuMaxHeight);
1026 auto parentPlacement = parentMenuPattern->GetLastPlacement().value_or(Placement::NONE);
1027 auto isContainerModalBool = isContainerModal(parentMenu);
1028 auto containerModalOffsetY = GetContainerModalOffsetY(parentMenu);
1029 auto [previewTopPositionY, previewBottomPositionY] = parentMenuPattern->GetPreviewPositionY();
1030 //correct position when window modal is containerModal
1031 if (isContainerModalBool) {
1032 previewTopPositionY -= containerModalOffsetY;
1033 previewBottomPositionY -= containerModalOffsetY;
1034 }
1035 if (parentPlacement == Placement::TOP_LEFT || parentPlacement == Placement::TOP ||
1036 parentPlacement == Placement::TOP_RIGHT) {
1037 subMenuMaxHeight = wrapperRect_.Height() - param_.topSecurity - param_.bottomSecurity
1038 - (previewBottomPositionY - lastItemTopPositionY);
1039 } else if (parentPlacement == Placement::BOTTOM_LEFT || parentPlacement == Placement::BOTTOM ||
1040 parentPlacement == Placement::BOTTOM_RIGHT) {
1041 subMenuMaxHeight = wrapperRect_.Height() - param_.topSecurity - param_.bottomSecurity
1042 - (firstItemBottomPositionY - previewTopPositionY);
1043 } else {
1044 subMenuMaxHeight = wrapperRect_.Height() - param_.topSecurity - param_.bottomSecurity
1045 - (firstItemBottomPositionY - parentMenuPositionY);
1046 }
1047 return subMenuMaxHeight;
1048 }
1049
CalcSubMenuMaxHeightNoPreview(RefPtr<FrameNode> parentItem,LayoutConstraintF & childConstraint,float lastItemTopPositionY,float firstItemBottomPositionY,float parentMenuPositionY)1050 float MenuLayoutAlgorithm::CalcSubMenuMaxHeightNoPreview(RefPtr<FrameNode> parentItem,
1051 LayoutConstraintF& childConstraint,
1052 float lastItemTopPositionY, float firstItemBottomPositionY, float parentMenuPositionY)
1053 {
1054 float subMenuMaxHeight = childConstraint.maxSize.Height();
1055 CHECK_NULL_RETURN(parentItem, subMenuMaxHeight);
1056 auto parentItemGeometryNode = parentItem->GetGeometryNode();
1057 CHECK_NULL_RETURN(parentItemGeometryNode, subMenuMaxHeight);
1058 auto parentItemPattern = parentItem->GetPattern<MenuItemPattern>();
1059 CHECK_NULL_RETURN(parentItemPattern, subMenuMaxHeight);
1060 auto parentMenu = parentItemPattern->GetMenu(true);
1061 CHECK_NULL_RETURN(parentMenu, subMenuMaxHeight);
1062 auto parentMenuPattern = parentMenu->GetPattern<MenuPattern>();
1063 CHECK_NULL_RETURN(parentMenuPattern, subMenuMaxHeight);
1064 auto parentPlacement = parentMenuPattern->GetLastPlacement().value_or(Placement::NONE);
1065 if (parentPlacement == Placement::TOP_LEFT || parentPlacement == Placement::TOP ||
1066 parentPlacement == Placement::TOP_RIGHT) {
1067 auto parentMenuBottomY = GetMenuBottomPositionY(parentMenu);
1068 auto parentItemBottomY = parentItem->GetPaintRectOffset(false, true).GetY()
1069 + parentItemGeometryNode->GetMarginFrameSize().Height();
1070 float bottomSpace1 = parentMenuBottomY - parentItemBottomY;
1071 float bottomSpace2 = lastItemTopPositionY - wrapperRect_.Top() - param_.topSecurity;
1072 subMenuMaxHeight = std::max(bottomSpace1, bottomSpace2);
1073 } else {
1074 subMenuMaxHeight = wrapperRect_.Bottom() - param_.bottomSecurity - firstItemBottomPositionY;
1075 if (subMenuMaxHeight < parentItemGeometryNode->GetMarginFrameSize().Height()) {
1076 subMenuMaxHeight = wrapperRect_.Bottom() - param_.bottomSecurity - parentMenuPositionY;
1077 }
1078 }
1079 return subMenuMaxHeight;
1080 }
1081
CalcSubMenuMaxHeightConstraint(LayoutConstraintF & childConstraint,RefPtr<FrameNode> parentItem)1082 float MenuLayoutAlgorithm::CalcSubMenuMaxHeightConstraint(LayoutConstraintF& childConstraint,
1083 RefPtr<FrameNode> parentItem)
1084 {
1085 float subMenuMaxHeight = childConstraint.maxSize.Height();
1086 CHECK_NULL_RETURN(parentItem, subMenuMaxHeight);
1087 auto parentItemPattern = parentItem->GetPattern<MenuItemPattern>();
1088 CHECK_NULL_RETURN(parentItemPattern, subMenuMaxHeight);
1089 auto parentMenu = parentItemPattern->GetMenu(true);
1090 CHECK_NULL_RETURN(parentMenu, subMenuMaxHeight);
1091 auto parentMenuPattern = parentMenu->GetPattern<MenuPattern>();
1092 CHECK_NULL_RETURN(parentMenuPattern, subMenuMaxHeight);
1093
1094 auto parentFirstMenuItemPositionY = GetFirstItemBottomPositionY(parentMenu);
1095 auto lastMenuItemPositionY = GetLastItemTopPositionY(parentMenu);
1096 auto parentMenuPositionY = parentMenu->GetPaintRectOffset(false, true).GetY();
1097 auto containerModalOffsetY = GetContainerModalOffsetY(parentItem);
1098 if (isContainerModal(parentItem)) {
1099 parentMenuPositionY -= containerModalOffsetY;
1100 parentFirstMenuItemPositionY -= containerModalOffsetY;
1101 lastMenuItemPositionY -= containerModalOffsetY;
1102 }
1103
1104 if (parentMenuPattern->GetPreviewMode() != MenuPreviewMode::NONE) {
1105 subMenuMaxHeight = CalcSubMenuMaxHeightWithPreview(parentMenu, childConstraint, lastMenuItemPositionY,
1106 parentFirstMenuItemPositionY, parentMenuPositionY);
1107 } else {
1108 subMenuMaxHeight = CalcSubMenuMaxHeightNoPreview(parentItem, childConstraint, lastMenuItemPositionY,
1109 parentFirstMenuItemPositionY, parentMenuPositionY);
1110 }
1111 return subMenuMaxHeight;
1112 }
1113
CalculateIdealSize(LayoutWrapper * layoutWrapper,LayoutConstraintF & childConstraint,PaddingPropertyF padding,SizeF & idealSize,RefPtr<FrameNode> parentItem)1114 void MenuLayoutAlgorithm::CalculateIdealSize(LayoutWrapper* layoutWrapper,
1115 LayoutConstraintF& childConstraint, PaddingPropertyF padding, SizeF& idealSize,
1116 RefPtr<FrameNode> parentItem)
1117 {
1118 if (parentItem != nullptr) {
1119 auto parentPattern = parentItem->GetPattern<MenuItemPattern>();
1120 CHECK_NULL_VOID(parentPattern);
1121 auto expandingMode = parentPattern->GetExpandingMode();
1122 if (expandingMode == SubMenuExpandingMode::STACK) {
1123 auto parentPattern = parentItem->GetPattern<MenuItemPattern>();
1124 CHECK_NULL_VOID(parentPattern);
1125 auto parentMenu = parentPattern->GetMenu();
1126 auto parentWidth = parentMenu->GetGeometryNode()->GetFrameSize().Width();
1127 childConstraint.minSize.SetWidth(parentWidth);
1128 childConstraint.maxSize.SetWidth(parentWidth);
1129 childConstraint.selfIdealSize.SetWidth(parentWidth);
1130 auto subMenuMaxHeight = CalcSubMenuMaxHeightConstraint(childConstraint, parentItem);
1131 childConstraint.maxSize.SetHeight(std::min(subMenuMaxHeight, childConstraint.maxSize.Height()));
1132 }
1133 }
1134 PrepareExtensionMenuConstraint(layoutWrapper, childConstraint);
1135
1136 auto host = layoutWrapper->GetHostNode();
1137 CHECK_NULL_VOID(host);
1138 auto pattern = host->GetPattern<MenuPattern>();
1139 CHECK_NULL_VOID(pattern);
1140 float idealHeight = 0.0f;
1141 float idealWidth = 0.0f;
1142 std::list<RefPtr<LayoutWrapper>> builderChildList;
1143 for (const auto& child : layoutWrapper->GetAllChildrenWithBuild()) {
1144 if (pattern->UseContentModifier()) {
1145 if (child->GetHostNode()->GetId() != pattern->GetBuilderId()) {
1146 child->GetGeometryNode()->Reset();
1147 child->GetGeometryNode()->SetContentSize(SizeF());
1148 } else {
1149 child->Measure(childConstraint);
1150 builderChildList.push_back(child);
1151 }
1152 BoxLayoutAlgorithm::PerformMeasureSelfWithChildList(layoutWrapper, builderChildList);
1153 } else {
1154 child->Measure(childConstraint);
1155 }
1156 auto childSize = child->GetGeometryNode()->GetMarginFrameSize();
1157 idealHeight += childSize.Height();
1158 idealWidth = std::max(idealWidth, childSize.Width());
1159 }
1160 idealSize.SetHeight(idealHeight);
1161 idealSize.SetWidth(idealWidth);
1162 AddPaddingToSize(padding, idealSize);
1163
1164 auto geometryNode = layoutWrapper->GetGeometryNode();
1165 CHECK_NULL_VOID(geometryNode);
1166 geometryNode->SetFrameSize(idealSize);
1167 childMarginFrameSize_ = idealSize;
1168 }
1169
CheckPreviewConstraintForConstant(const RefPtr<GeometryNode> & previewGeometryNode)1170 void MenuLayoutAlgorithm::CheckPreviewConstraintForConstant(const RefPtr<GeometryNode>& previewGeometryNode)
1171 {
1172 CHECK_NULL_VOID(previewGeometryNode);
1173 auto maxWidth = (wrapperRect_.Width() - paddingStart_ - paddingEnd_) / previewScale_;
1174 auto maxHeight = (wrapperRect_.Height() - param_.topSecurity - param_.bottomSecurity) / previewScale_;
1175 auto previewSize = previewGeometryNode->GetMarginFrameSize();
1176
1177 auto width = std::min<float>(previewSize.Width(), maxWidth);
1178 auto height = std::min<float>(previewSize.Height(), maxHeight);
1179 previewGeometryNode->SetFrameSize(SizeF(width, height));
1180 }
1181
CheckPreviewConstraint(const RefPtr<FrameNode> & frameNode,const Rect & menuWindowRect)1182 void MenuLayoutAlgorithm::CheckPreviewConstraint(const RefPtr<FrameNode>& frameNode, const Rect& menuWindowRect)
1183 {
1184 CHECK_NULL_VOID(frameNode &&
1185 (frameNode->GetTag() == V2::MENU_PREVIEW_ETS_TAG || frameNode->GetTag() == V2::FLEX_ETS_TAG));
1186 auto geometryNode = frameNode->GetGeometryNode();
1187 CHECK_NULL_VOID(geometryNode);
1188
1189 if (previewScaleMode_.value_or(PreviewScaleMode::AUTO) == PreviewScaleMode::CONSTANT) {
1190 CheckPreviewConstraintForConstant(geometryNode);
1191 return;
1192 }
1193
1194 auto maxWidth = wrapperSize_.Width();
1195 auto hasLayoutMargin = layoutRegionMargin_.left.has_value() || layoutRegionMargin_.right.has_value();
1196 if (hasLayoutMargin || previewScaleMode_.value_or(PreviewScaleMode::AUTO) == PreviewScaleMode::MAINTAIN) {
1197 maxWidth = std::max(0.0f, wrapperSize_.Width() - paddingStart_ - paddingEnd_) / previewScale_;
1198 } else {
1199 RefPtr<GridColumnInfo> columnInfo = GridSystemManager::GetInstance().GetInfoByType(GridColumnType::MENU);
1200 CHECK_NULL_VOID(columnInfo);
1201 auto parent = columnInfo->GetParent();
1202 CHECK_NULL_VOID(parent);
1203 parent->BuildColumnWidth(std::min(menuWindowRect.Width(), menuWindowRect.Height()));
1204 maxWidth = static_cast<float>(columnInfo->GetWidth(GRID_COUNTS_4)) / previewScale_;
1205 }
1206
1207 auto frameSize = geometryNode->GetMarginFrameSize();
1208 if (!isPreviewContainScale_) {
1209 static SizeF previewSize;
1210 static int32_t hostId = -1;
1211 if (previewSize == SizeF(0.0f, 0.0f) || hostId != frameNode->GetId()) {
1212 previewSize = frameSize;
1213 hostId = frameNode->GetId();
1214 } else {
1215 frameSize = previewSize;
1216 }
1217 }
1218
1219 if (LessOrEqual(frameSize.Width(), maxWidth)) {
1220 geometryNode->SetFrameSize(SizeF(frameSize.Width(), frameSize.Height()));
1221 } else if (isPreviewContainScale_) {
1222 geometryNode->SetFrameSize(SizeF(maxWidth, frameSize.Height() * (maxWidth / frameSize.Width())));
1223 } else {
1224 geometryNode->SetFrameSize(SizeF(maxWidth, frameSize.Height()));
1225 }
1226 }
1227
CheckPreviewSize(const RefPtr<LayoutWrapper> & previewLayoutWrapper,const RefPtr<MenuPattern> & menuPattern)1228 void MenuLayoutAlgorithm::CheckPreviewSize(
1229 const RefPtr<LayoutWrapper>& previewLayoutWrapper, const RefPtr<MenuPattern>& menuPattern)
1230 {
1231 CHECK_NULL_VOID(previewLayoutWrapper && menuPattern);
1232 auto previewNode = previewLayoutWrapper->GetHostNode();
1233 CHECK_NULL_VOID(previewNode);
1234 auto tag = previewNode->GetTag();
1235 auto isPreview = tag == V2::IMAGE_ETS_TAG || tag == V2::MENU_PREVIEW_ETS_TAG || tag == V2::FLEX_ETS_TAG;
1236 CHECK_NULL_VOID(isPreview);
1237
1238 auto previewGeometryNode = previewNode->GetGeometryNode();
1239 CHECK_NULL_VOID(previewGeometryNode);
1240 auto previewSize = previewGeometryNode->GetMarginFrameSize();
1241
1242 if (menuPattern->GetIsFirstShow()) {
1243 menuPattern->SetPreviewIdealSize(previewSize);
1244 return;
1245 }
1246
1247 if (previewSize != menuPattern->GetPreviewIdealSize()) {
1248 auto menuWrapper = menuPattern->GetMenuWrapper();
1249 CHECK_NULL_VOID(menuWrapper);
1250 auto menuWrapperPattern = menuWrapper->GetPattern<MenuWrapperPattern>();
1251 CHECK_NULL_VOID(menuWrapperPattern);
1252 auto constraint = menuWrapperPattern->GetChildLayoutConstraint();
1253 CHECK_NULL_VOID(constraint.maxSize.IsPositive() && constraint.percentReference.IsPositive());
1254 auto layoutProperty = previewLayoutWrapper->GetLayoutProperty();
1255 CHECK_NULL_VOID(layoutProperty);
1256 layoutProperty->UpdatePropertyChangeFlag(PROPERTY_UPDATE_MEASURE);
1257 previewLayoutWrapper->Measure(constraint);
1258 menuPattern->SetPreviewIdealSize(previewGeometryNode->GetMarginFrameSize());
1259 }
1260 }
1261
GetPreviewNodeTotalSize(const RefPtr<LayoutWrapper> & child,const Rect & menuWindowRect,RefPtr<LayoutWrapper> & previewLayoutWrapper,SizeF & size,const RefPtr<LayoutWrapper> & menuLayoutWrapper)1262 void MenuLayoutAlgorithm::GetPreviewNodeTotalSize(const RefPtr<LayoutWrapper>& child, const Rect& menuWindowRect,
1263 RefPtr<LayoutWrapper>& previewLayoutWrapper, SizeF& size, const RefPtr<LayoutWrapper>& menuLayoutWrapper)
1264 {
1265 CHECK_NULL_VOID(child);
1266 auto hostNode = child->GetHostNode();
1267 auto geometryNode = child->GetGeometryNode();
1268 if (!hostNode || !geometryNode) {
1269 return;
1270 }
1271
1272 bool isImageNode = hostNode->GetTag() == V2::IMAGE_ETS_TAG;
1273 bool isPreviewNode = hostNode->GetTag() == V2::MENU_PREVIEW_ETS_TAG;
1274 bool isFlexNode = hostNode->GetTag() == V2::FLEX_ETS_TAG;
1275 if (!isPreviewNode && !isImageNode && !isFlexNode) {
1276 return;
1277 }
1278
1279 CHECK_NULL_VOID(menuLayoutWrapper);
1280 auto menuNode = menuLayoutWrapper->GetHostNode();
1281 CHECK_NULL_VOID(menuNode);
1282 auto menuPattern = menuNode->GetPattern<MenuPattern>();
1283 CHECK_NULL_VOID(menuPattern);
1284 CheckPreviewSize(child, menuPattern);
1285
1286 if (isImageNode && menuPattern->GetIsShowHoverImage()) {
1287 return;
1288 }
1289
1290 auto frameSize = geometryNode->GetMarginFrameSize();
1291 if (isPreviewNode || isFlexNode) {
1292 CheckPreviewConstraint(hostNode, menuWindowRect);
1293 } else if (isImageNode && previewScaleMode_.value_or(PreviewScaleMode::AUTO) == PreviewScaleMode::CONSTANT) {
1294 CheckPreviewConstraintForConstant(geometryNode);
1295 } else {
1296 geometryNode->SetFrameSize(frameSize);
1297 }
1298 frameSize = geometryNode->GetMarginFrameSize() * previewScale_;
1299 auto widthLeftSpace = menuWindowRect.Width() - paddingStart_ - paddingEnd_;
1300 if (GreatNotEqual(frameSize.Width(), widthLeftSpace)) {
1301 auto unitSpace = widthLeftSpace / frameSize.Width() / previewScale_;
1302 geometryNode->SetFrameSize(SizeF(widthLeftSpace / previewScale_, unitSpace * frameSize.Height()));
1303 frameSize = geometryNode->GetMarginFrameSize() * previewScale_;
1304 }
1305 previewLayoutWrapper = child;
1306 size += frameSize;
1307 }
1308
GetPreviewNodeAndMenuNodeTotalSize(const RefPtr<FrameNode> & frameNode,RefPtr<LayoutWrapper> & previewLayoutWrapper,RefPtr<LayoutWrapper> & menuLayoutWrapper)1309 SizeF MenuLayoutAlgorithm::GetPreviewNodeAndMenuNodeTotalSize(const RefPtr<FrameNode>& frameNode,
1310 RefPtr<LayoutWrapper>& previewLayoutWrapper, RefPtr<LayoutWrapper>& menuLayoutWrapper)
1311 {
1312 SizeF size;
1313 CHECK_NULL_RETURN(frameNode, size);
1314 auto pipelineContext = DialogManager::GetMainPipelineContext(frameNode);
1315 CHECK_NULL_RETURN(pipelineContext, size);
1316 for (auto& child : frameNode->GetAllChildrenWithBuild()) {
1317 auto hostNode = child->GetHostNode();
1318 auto geometryNode = child->GetGeometryNode();
1319 if (!hostNode || !geometryNode) {
1320 continue;
1321 }
1322 GetPreviewNodeTotalSize(child, param_.menuWindowRect, previewLayoutWrapper, size, menuLayoutWrapper);
1323 auto menuPattern = hostNode->GetPattern<MenuPattern>();
1324 if (hostNode->GetTag() == V2::MENU_ETS_TAG && menuPattern && !menuPattern->IsSubMenu()) {
1325 menuLayoutWrapper = child;
1326 size += geometryNode->GetMarginFrameSize();
1327 }
1328 }
1329 return size;
1330 }
1331
LayoutNormalTopPreviewBottomMenuLessThan(const RefPtr<GeometryNode> & previewGeometryNode,const RefPtr<GeometryNode> & menuGeometryNode,SizeF & totalSize)1332 void MenuLayoutAlgorithm::LayoutNormalTopPreviewBottomMenuLessThan(
1333 const RefPtr<GeometryNode>& previewGeometryNode, const RefPtr<GeometryNode>& menuGeometryNode, SizeF& totalSize)
1334 {
1335 CHECK_NULL_VOID(previewGeometryNode);
1336 CHECK_NULL_VOID(menuGeometryNode);
1337
1338 OffsetF center(
1339 targetOffset_.GetX() + targetSize_.Width() / HALF, targetOffset_.GetY() + targetSize_.Height() / HALF);
1340 targetCenterOffset_ = center;
1341 auto previewSize = previewGeometryNode->GetMarginFrameSize() * previewScale_;
1342 OffsetF offset(center.GetX() - previewSize.Width() / HALF,
1343 std::min<float>(center.GetY() - previewSize.Height() / HALF,
1344 wrapperRect_.Bottom() - param_.bottomSecurity - totalSize.Height() - param_.previewMenuGap));
1345 auto x = std::clamp(offset.GetX(), static_cast<float>(wrapperRect_.Left()) + paddingStart_,
1346 static_cast<float>(wrapperRect_.Right()) - paddingEnd_ - previewSize.Width());
1347 auto y = std::clamp(offset.GetY(), static_cast<float>(wrapperRect_.Top()) + param_.topSecurity,
1348 static_cast<float>(wrapperRect_.Bottom()) - param_.bottomSecurity - previewSize.Height());
1349 x = x + (previewSize.Width() - previewSize.Width() / previewScale_) / HALF;
1350 y = y + (previewSize.Height() - previewSize.Height() / previewScale_) / HALF;
1351 previewGeometryNode->SetMarginFrameOffset(OffsetF(x, y));
1352 }
1353
LayoutNormalTopPreviewBottomMenuGreateThan(const RefPtr<GeometryNode> & previewGeometryNode,const RefPtr<GeometryNode> & menuGeometryNode,SizeF & totalSize)1354 void MenuLayoutAlgorithm::LayoutNormalTopPreviewBottomMenuGreateThan(
1355 const RefPtr<GeometryNode>& previewGeometryNode, const RefPtr<GeometryNode>& menuGeometryNode, SizeF& totalSize)
1356 {
1357 CHECK_NULL_VOID(previewGeometryNode);
1358 CHECK_NULL_VOID(menuGeometryNode);
1359
1360 OffsetF center(targetOffset_.GetX() + targetSize_.Width() / 2, targetOffset_.GetY() + targetSize_.Height() / 2);
1361 targetCenterOffset_ = center;
1362 auto previewSize = previewGeometryNode->GetMarginFrameSize() * previewScale_;
1363 auto menuHeight = totalSize.Height() - previewSize.Height();
1364
1365 if (previewScaleMode_.value_or(PreviewScaleMode::AUTO) == PreviewScaleMode::CONSTANT) {
1366 LayoutPreviewMenuGreateThanForConstant(previewGeometryNode, menuGeometryNode);
1367 return;
1368 }
1369 auto previewHalfHeight = previewSize.Height() / 2;
1370 if (LessNotEqual(menuHeight, previewHalfHeight)) {
1371 auto menuSize = menuGeometryNode->GetMarginFrameSize();
1372 if (GreatNotEqual(param_.menuItemTotalHeight, previewHalfHeight)) {
1373 menuGeometryNode->SetFrameSize(SizeF(menuSize.Width(), previewHalfHeight));
1374 totalSize = SizeF(totalSize.Width(), previewHalfHeight + previewSize.Height());
1375 } else {
1376 menuGeometryNode->SetFrameSize(SizeF(menuSize.Width(), param_.menuItemTotalHeight));
1377 totalSize = SizeF(totalSize.Width(), param_.menuItemTotalHeight + previewSize.Height());
1378 }
1379 }
1380
1381 auto heightLeftSpace = wrapperRect_.Height() - param_.topSecurity - param_.bottomSecurity - param_.previewMenuGap;
1382 auto delta = totalSize.Height() - heightLeftSpace;
1383 if (GreatNotEqual(delta, 0.0f)) {
1384 menuHeight = totalSize.Height() - previewSize.Height();
1385 float unitSpace = 0.0f;
1386 if (LessNotEqual(menuHeight, previewHalfHeight)) {
1387 unitSpace = delta / previewSize.Height();
1388 } else {
1389 unitSpace = delta / totalSize.Height();
1390 auto menuSize = menuGeometryNode->GetMarginFrameSize();
1391 menuGeometryNode->SetFrameSize(SizeF(menuSize.Width(), (1 - unitSpace) * menuSize.Height()));
1392 }
1393 previewGeometryNode->SetFrameSize(SizeF((1 - unitSpace) * previewSize.Width() / previewScale_,
1394 (1 - unitSpace) * previewSize.Height() / previewScale_));
1395 totalSize = totalSize - SizeF(0.0f, delta);
1396 }
1397 previewSize = previewGeometryNode->GetMarginFrameSize() * previewScale_;
1398 OffsetF offset(center.GetX() - previewSize.Width() / 2, 0.0f);
1399 auto x = std::clamp(offset.GetX(), static_cast<float>(wrapperRect_.Left()) + paddingStart_,
1400 static_cast<float>(wrapperRect_.Right()) - paddingEnd_ - previewSize.Width());
1401 auto y = std::clamp(offset.GetY(), static_cast<float>(wrapperRect_.Top()) + param_.topSecurity,
1402 static_cast<float>(wrapperRect_.Bottom()) - param_.bottomSecurity - previewSize.Height());
1403 x = x + (previewSize.Width() - previewSize.Width() / previewScale_) / 2;
1404 y = y + (previewSize.Height() - previewSize.Height() / previewScale_) / 2;
1405 previewGeometryNode->SetMarginFrameOffset(OffsetF(x, y));
1406 }
1407
LayoutNormalTopPreviewBottomMenu(const RefPtr<GeometryNode> & previewGeometryNode,const RefPtr<GeometryNode> & menuGeometryNode,SizeF & totalSize,float menuItemTotalHeight,LayoutWrapper * layoutWrapper)1408 void MenuLayoutAlgorithm::LayoutNormalTopPreviewBottomMenu(const RefPtr<GeometryNode>& previewGeometryNode,
1409 const RefPtr<GeometryNode>& menuGeometryNode, SizeF& totalSize, float menuItemTotalHeight,
1410 LayoutWrapper* layoutWrapper)
1411 {
1412 CHECK_NULL_VOID(previewGeometryNode);
1413 CHECK_NULL_VOID(menuGeometryNode);
1414 param_.menuItemTotalHeight = menuItemTotalHeight;
1415 CHECK_NULL_VOID(layoutWrapper);
1416 auto hostNode = layoutWrapper->GetHostNode();
1417 CHECK_NULL_VOID(hostNode);
1418 auto pipelineContext = DialogManager::GetMainPipelineContext(hostNode);
1419 CHECK_NULL_VOID(pipelineContext);
1420 if (LessNotEqual(totalSize.Height() + targetSecurity_,
1421 wrapperRect_.Height() - paddingTop_ - paddingBottom_ - param_.topSecurity - param_.bottomSecurity)) {
1422 LayoutNormalTopPreviewBottomMenuLessThan(previewGeometryNode, menuGeometryNode, totalSize);
1423 } else {
1424 LayoutNormalTopPreviewBottomMenuGreateThan(previewGeometryNode, menuGeometryNode, totalSize);
1425 }
1426 auto previewSize = previewGeometryNode->GetMarginFrameSize();
1427 auto securityHeight = wrapperRect_.Height() - param_.topSecurity - param_.bottomSecurity;
1428 if (GreatNotEqual(param_.windowGlobalSizeF.Height(), param_.menuWindowRect.Height()) &&
1429 GreatNotEqual(previewSize.Height(), securityHeight)) {
1430 previewGeometryNode->SetFrameSize(SizeF(previewSize.Width(), securityHeight));
1431 }
1432 }
1433
LayoutNormalBottomPreviewTopMenuLessThan(const RefPtr<GeometryNode> & previewGeometryNode,const RefPtr<GeometryNode> & menuGeometryNode,SizeF & totalSize)1434 void MenuLayoutAlgorithm::LayoutNormalBottomPreviewTopMenuLessThan(
1435 const RefPtr<GeometryNode>& previewGeometryNode, const RefPtr<GeometryNode>& menuGeometryNode, SizeF& totalSize)
1436 {
1437 CHECK_NULL_VOID(previewGeometryNode);
1438 CHECK_NULL_VOID(menuGeometryNode);
1439
1440 OffsetF center(
1441 targetOffset_.GetX() + targetSize_.Width() / HALF, targetOffset_.GetY() + targetSize_.Height() / HALF);
1442 targetCenterOffset_ = center;
1443 auto previewSize = previewGeometryNode->GetMarginFrameSize() * previewScale_;
1444 OffsetF offset(center.GetX() - previewSize.Width() / HALF,
1445 std::max<float>(center.GetY() - previewSize.Height() / HALF, wrapperRect_.Top() + param_.topSecurity +
1446 totalSize.Height() - previewSize.Height() +
1447 param_.previewMenuGap));
1448 auto x = std::clamp(offset.GetX(), static_cast<float>(wrapperRect_.Left()) + paddingStart_,
1449 static_cast<float>(wrapperRect_.Right()) - previewSize.Width() - paddingEnd_);
1450 auto y = std::clamp(offset.GetY(), static_cast<float>(wrapperRect_.Top()) + param_.topSecurity,
1451 static_cast<float>(wrapperRect_.Bottom()) - param_.bottomSecurity - previewSize.Height());
1452 x = x + (previewSize.Width() - previewSize.Width() / previewScale_) / HALF;
1453 y = y + (previewSize.Height() - previewSize.Height() / previewScale_) / HALF;
1454 previewGeometryNode->SetMarginFrameOffset(OffsetF(x, y));
1455 }
1456
LayoutNormalBottomPreviewTopMenuGreateThan(const RefPtr<GeometryNode> & previewGeometryNode,const RefPtr<GeometryNode> & menuGeometryNode,SizeF & totalSize)1457 void MenuLayoutAlgorithm::LayoutNormalBottomPreviewTopMenuGreateThan(
1458 const RefPtr<GeometryNode>& previewGeometryNode, const RefPtr<GeometryNode>& menuGeometryNode, SizeF& totalSize)
1459 {
1460 CHECK_NULL_VOID(previewGeometryNode);
1461 CHECK_NULL_VOID(menuGeometryNode);
1462
1463 OffsetF center(targetOffset_.GetX() + targetSize_.Width() / 2, targetOffset_.GetY() + targetSize_.Height() / 2);
1464 targetCenterOffset_ = center;
1465 auto previewSize = previewGeometryNode->GetMarginFrameSize() * previewScale_;
1466 if (previewScaleMode_.value_or(PreviewScaleMode::AUTO) == PreviewScaleMode::CONSTANT) {
1467 LayoutPreviewMenuGreateThanForConstant(previewGeometryNode, menuGeometryNode);
1468 return;
1469 }
1470 auto menuHeight = totalSize.Height() - previewSize.Height();
1471 auto previewHalfHeight = previewSize.Height() / 2;
1472 if (LessNotEqual(menuHeight, previewHalfHeight)) {
1473 auto menuSize = menuGeometryNode->GetMarginFrameSize();
1474 if (GreatNotEqual(param_.menuItemTotalHeight, previewHalfHeight)) {
1475 menuGeometryNode->SetFrameSize(SizeF(menuSize.Width(), previewHalfHeight));
1476 totalSize = SizeF(totalSize.Width(), previewHalfHeight + previewSize.Height());
1477 } else {
1478 menuGeometryNode->SetFrameSize(SizeF(menuSize.Width(), param_.menuItemTotalHeight));
1479 totalSize = SizeF(totalSize.Width(), param_.menuItemTotalHeight + previewSize.Height());
1480 }
1481 }
1482
1483 auto heightLeftSpace = wrapperRect_.Height() - param_.topSecurity - param_.bottomSecurity - param_.previewMenuGap;
1484 auto delta = totalSize.Height() - heightLeftSpace;
1485 if (GreatNotEqual(delta, 0.0f)) {
1486 menuHeight = totalSize.Height() - previewSize.Height();
1487 float unitSpace = 0.0f;
1488 if (LessNotEqual(menuHeight, previewHalfHeight)) {
1489 unitSpace = delta / previewSize.Height();
1490 } else {
1491 unitSpace = delta / totalSize.Height();
1492 auto menuSize = menuGeometryNode->GetMarginFrameSize();
1493 menuGeometryNode->SetFrameSize(SizeF(menuSize.Width(), (1 - unitSpace) * menuSize.Height()));
1494 }
1495 previewGeometryNode->SetFrameSize(SizeF((1 - unitSpace) * previewSize.Width() / previewScale_,
1496 (1 - unitSpace) * previewSize.Height() / previewScale_));
1497 totalSize = totalSize - SizeF(0.0f, delta);
1498 }
1499 previewSize = previewGeometryNode->GetMarginFrameSize() * previewScale_;
1500 OffsetF offset(center.GetX() - previewSize.Width() / 2,
1501 wrapperRect_.Bottom() - param_.bottomSecurity - previewSize.Height());
1502
1503 auto x = std::clamp(offset.GetX(), static_cast<float>(wrapperRect_.Left()) + paddingStart_,
1504 static_cast<float>(wrapperRect_.Right()) - previewSize.Width() - paddingEnd_);
1505 auto y = std::clamp(offset.GetY(), static_cast<float>(wrapperRect_.Top()) + param_.topSecurity,
1506 static_cast<float>(wrapperRect_.Bottom()) - param_.bottomSecurity - previewSize.Height());
1507 x = x + (previewSize.Width() - previewSize.Width() / previewScale_) / 2;
1508 y = y + (previewSize.Height() - previewSize.Height() / previewScale_) / 2;
1509 previewGeometryNode->SetMarginFrameOffset(OffsetF(x, y));
1510 }
1511
LayoutNormalBottomPreviewTopMenu(const RefPtr<GeometryNode> & previewGeometryNode,const RefPtr<GeometryNode> & menuGeometryNode,SizeF & totalSize,float menuItemTotalHeight,LayoutWrapper * layoutWrapper)1512 void MenuLayoutAlgorithm::LayoutNormalBottomPreviewTopMenu(const RefPtr<GeometryNode>& previewGeometryNode,
1513 const RefPtr<GeometryNode>& menuGeometryNode, SizeF& totalSize, float menuItemTotalHeight,
1514 LayoutWrapper* layoutWrapper)
1515 {
1516 CHECK_NULL_VOID(previewGeometryNode);
1517 CHECK_NULL_VOID(menuGeometryNode);
1518 param_.menuItemTotalHeight = menuItemTotalHeight;
1519 CHECK_NULL_VOID(layoutWrapper);
1520 auto hostNode = layoutWrapper->GetHostNode();
1521 CHECK_NULL_VOID(hostNode);
1522 auto pipelineContext = DialogManager::GetMainPipelineContext(hostNode);
1523 CHECK_NULL_VOID(pipelineContext);
1524 if (LessNotEqual(totalSize.Height() + targetSecurity_,
1525 wrapperRect_.Height() - param_.topSecurity - param_.bottomSecurity)) {
1526 LayoutNormalBottomPreviewTopMenuLessThan(previewGeometryNode, menuGeometryNode, totalSize);
1527 } else {
1528 LayoutNormalBottomPreviewTopMenuGreateThan(previewGeometryNode, menuGeometryNode, totalSize);
1529 }
1530 auto previewSize = previewGeometryNode->GetMarginFrameSize();
1531 auto securityHeight =
1532 param_.menuWindowRect.Height() - param_.topSecurity - param_.top - param_.bottomSecurity - param_.bottom;
1533 if (GreatNotEqual(param_.windowGlobalSizeF.Height(), param_.menuWindowRect.Height()) &&
1534 GreatNotEqual(previewSize.Height(), securityHeight)) {
1535 previewGeometryNode->SetFrameSize(SizeF(previewSize.Width(), securityHeight));
1536 }
1537 }
1538
UpdateScrollAndColumnLayoutConstraint(const RefPtr<LayoutWrapper> & previewLayoutWrapper,const RefPtr<LayoutWrapper> & menuLayoutWrapper)1539 void MenuLayoutAlgorithm::UpdateScrollAndColumnLayoutConstraint(
1540 const RefPtr<LayoutWrapper>& previewLayoutWrapper, const RefPtr<LayoutWrapper>& menuLayoutWrapper)
1541 {
1542 CHECK_NULL_VOID(menuLayoutWrapper);
1543 CHECK_NULL_VOID(previewLayoutWrapper);
1544 RefPtr<GeometryNode> menuGeometryNode = menuLayoutWrapper->GetGeometryNode();
1545 RefPtr<GeometryNode> previewGeometryNode = previewLayoutWrapper->GetGeometryNode();
1546 CHECK_NULL_VOID(menuGeometryNode);
1547 CHECK_NULL_VOID(previewGeometryNode);
1548
1549 for (auto& child : menuLayoutWrapper->GetAllChildrenWithBuild()) {
1550 auto geometryNode = child->GetGeometryNode();
1551 if (!geometryNode) {
1552 continue;
1553 }
1554 auto frameSize = menuGeometryNode->GetMarginFrameSize();
1555 geometryNode->SetFrameSize(SizeF(frameSize.Width(), frameSize.Height()));
1556 auto layoutProperty = child->GetLayoutProperty();
1557 CHECK_NULL_VOID(layoutProperty);
1558 auto constraint = layoutProperty->GetLayoutConstraint();
1559 if (constraint.has_value()) {
1560 constraint.value().maxSize.SetWidth(frameSize.Width());
1561 constraint.value().maxSize.SetHeight(frameSize.Height());
1562 constraint.value().selfIdealSize.UpdateSizeWithCheck(SizeF(frameSize.Width(), frameSize.Height()));
1563 layoutProperty->UpdateLayoutConstraint(constraint.value());
1564 child->Measure(constraint);
1565 }
1566 }
1567
1568 for (auto& child : previewLayoutWrapper->GetAllChildrenWithBuild()) {
1569 auto hostNode = child->GetHostNode();
1570 auto geometryNode = child->GetGeometryNode();
1571 if (!hostNode || !geometryNode) {
1572 continue;
1573 }
1574 auto frameSize = previewGeometryNode->GetMarginFrameSize();
1575 geometryNode->SetFrameSize(SizeF(frameSize.Width(), frameSize.Height()));
1576 auto layoutProperty = child->GetLayoutProperty();
1577 CHECK_NULL_VOID(layoutProperty);
1578 auto constraint = layoutProperty->GetLayoutConstraint();
1579 if (constraint.has_value()) {
1580 constraint.value().maxSize.SetWidth(frameSize.Width());
1581 constraint.value().maxSize.SetHeight(frameSize.Height());
1582 constraint.value().selfIdealSize.UpdateSizeWithCheck(SizeF(frameSize.Width(), frameSize.Height()));
1583 layoutProperty->UpdateLayoutConstraint(constraint.value());
1584 hostNode->GetRenderContext()->SetClipToBounds(true);
1585 child->Measure(constraint);
1586 }
1587 }
1588 }
1589
GetMenuItemTotalHeight(const RefPtr<LayoutWrapper> & menuLayoutWrapper)1590 float MenuLayoutAlgorithm::GetMenuItemTotalHeight(const RefPtr<LayoutWrapper>& menuLayoutWrapper)
1591 {
1592 CHECK_NULL_RETURN(menuLayoutWrapper, 0.0f);
1593 RefPtr<GeometryNode> menuGeometryNode = menuLayoutWrapper->GetGeometryNode();
1594 CHECK_NULL_RETURN(menuGeometryNode, 0.0f);
1595 float height = 0.0f;
1596
1597 for (auto& child : menuLayoutWrapper->GetAllChildrenWithBuild()) {
1598 auto geometryNode = child->GetGeometryNode();
1599 if (!geometryNode) {
1600 continue;
1601 }
1602 for (auto& menuItem : child->GetAllChildrenWithBuild()) {
1603 auto itemHostnode = menuItem->GetHostNode();
1604 auto itemGeometryNode = menuItem->GetGeometryNode();
1605 if (!itemHostnode || !itemGeometryNode) {
1606 continue;
1607 }
1608 height += itemGeometryNode->GetMarginFrameSize().Height();
1609 }
1610 }
1611 auto menuHeight = menuGeometryNode->GetMarginFrameSize().Height();
1612 if (LessNotEqual(height, menuHeight)) {
1613 height = menuHeight;
1614 }
1615 return height;
1616 }
1617
CheckHorizontalLayoutPreviewOffsetX(const RefPtr<GeometryNode> & previewGeometryNode,const RefPtr<GeometryNode> & menuGeometryNode,float offsetX)1618 float MenuLayoutAlgorithm::CheckHorizontalLayoutPreviewOffsetX(
1619 const RefPtr<GeometryNode>& previewGeometryNode, const RefPtr<GeometryNode>& menuGeometryNode, float offsetX)
1620 {
1621 if (SystemProperties::GetDeviceOrientation() != DeviceOrientation::LANDSCAPE) {
1622 return offsetX;
1623 }
1624 CHECK_NULL_RETURN(previewGeometryNode, offsetX);
1625 auto previewSize = previewGeometryNode->GetMarginFrameSize() * previewScale_;
1626 CHECK_NULL_RETURN(menuGeometryNode, offsetX);
1627 auto menuSize = menuGeometryNode->GetMarginFrameSize();
1628 // left menu right preview
1629 auto x_min = wrapperRect_.Left() + paddingStart_ + menuSize.Width() + targetSecurity_;
1630 // left preview right menu
1631 auto x_max = wrapperRect_.Right() - paddingEnd_ - menuSize.Width() - previewSize.Width() - targetSecurity_;
1632 auto needAvoid = GreatNotEqual(offsetX, x_max) && LessNotEqual(offsetX, x_min);
1633 return needAvoid ? x_max : offsetX;
1634 }
1635
LayoutOtherDeviceLeftPreviewRightMenuLessThan(const RefPtr<GeometryNode> & previewGeometryNode,const RefPtr<GeometryNode> & menuGeometryNode,SizeF & totalSize)1636 void MenuLayoutAlgorithm::LayoutOtherDeviceLeftPreviewRightMenuLessThan(
1637 const RefPtr<GeometryNode>& previewGeometryNode, const RefPtr<GeometryNode>& menuGeometryNode, SizeF& totalSize)
1638 {
1639 CHECK_NULL_VOID(previewGeometryNode);
1640 CHECK_NULL_VOID(menuGeometryNode);
1641
1642 OffsetF targetCenterOffset(
1643 targetOffset_.GetX() + targetSize_.Width() / HALF, targetOffset_.GetY() + targetSize_.Height() / HALF);
1644 targetCenterOffset_ = targetCenterOffset;
1645 auto previewSize = previewGeometryNode->GetMarginFrameSize() * previewScale_;
1646 auto heightLeftSpace = wrapperRect_.Height() - param_.topSecurity - param_.bottomSecurity;
1647 auto delta = previewSize.Height() - heightLeftSpace;
1648 if (GreatNotEqual(delta, 0.0f)) {
1649 auto unitSpace = delta / previewSize.Height();
1650 previewGeometryNode->SetFrameSize(SizeF((1 - unitSpace) * previewSize.Width() / previewScale_,
1651 (1 - unitSpace) * previewSize.Height() / previewScale_));
1652 totalSize = totalSize - SizeF(unitSpace * previewSize.Width(), unitSpace * previewSize.Height());
1653 }
1654 previewSize = previewGeometryNode->GetMarginFrameSize() * previewScale_;
1655 auto menuSize = menuGeometryNode->GetMarginFrameSize();
1656 menuGeometryNode->SetFrameSize(
1657 SizeF(menuSize.Width(), std::min<float>(param_.menuItemTotalHeight, heightLeftSpace)));
1658 menuSize = menuGeometryNode->GetMarginFrameSize();
1659 auto offsetX = targetCenterOffset.GetX() - previewSize.Width() / HALF;
1660 auto offsetY = std::min<float>(targetCenterOffset.GetY() - previewSize.Height() / HALF,
1661 wrapperRect_.Bottom() - param_.bottomSecurity - menuSize.Height());
1662 offsetX = CheckHorizontalLayoutPreviewOffsetX(previewGeometryNode, menuGeometryNode, offsetX);
1663 auto x = std::clamp(offsetX, static_cast<float>(wrapperRect_.Left()) + paddingStart_,
1664 static_cast<float>(wrapperRect_.Right()) - previewSize.Width() - paddingEnd_);
1665 auto yMax = static_cast<float>(wrapperRect_.Bottom()) - param_.bottomSecurity - previewSize.Height();
1666 if (placement_ == Placement::LEFT_BOTTOM || placement_ == Placement::RIGHT_BOTTOM) {
1667 yMax += previewSize.Height();
1668 offsetY = std::min<float>(targetCenterOffset.GetY() - previewSize.Height() / HALF, yMax);
1669 }
1670 auto y = std::clamp(offsetY, static_cast<float>(wrapperRect_.Top()) + param_.topSecurity, yMax);
1671 x = x + (previewSize.Width() - previewSize.Width() / previewScale_) / HALF;
1672 y = y + (previewSize.Height() - previewSize.Height() / previewScale_) / HALF;
1673 previewGeometryNode->SetMarginFrameOffset(OffsetF(x, y));
1674 }
1675
LayoutOtherDeviceLeftPreviewRightMenuGreateThan(const RefPtr<GeometryNode> & previewGeometryNode,const RefPtr<GeometryNode> & menuGeometryNode,SizeF & totalSize)1676 void MenuLayoutAlgorithm::LayoutOtherDeviceLeftPreviewRightMenuGreateThan(
1677 const RefPtr<GeometryNode>& previewGeometryNode, const RefPtr<GeometryNode>& menuGeometryNode, SizeF& totalSize)
1678 {
1679 CHECK_NULL_VOID(previewGeometryNode);
1680 CHECK_NULL_VOID(menuGeometryNode);
1681
1682 OffsetF targetCenterOffset(
1683 targetOffset_.GetX() + targetSize_.Width() / 2, targetOffset_.GetY() + targetSize_.Height() / 2);
1684 targetCenterOffset_ = targetCenterOffset;
1685 auto previewSize = previewGeometryNode->GetMarginFrameSize() * previewScale_;
1686 auto widthLeftSpace = wrapperRect_.Width() - paddingStart_ - paddingEnd_ - param_.previewMenuGap;
1687 if (previewScaleMode_.value_or(PreviewScaleMode::AUTO) == PreviewScaleMode::CONSTANT) {
1688 LayoutPreviewMenuGreateThanForConstant(previewGeometryNode, menuGeometryNode);
1689 return;
1690 }
1691 auto delta = totalSize.Width() - widthLeftSpace;
1692 if (GreatNotEqual(delta, 0.0f)) {
1693 auto unitSpace = delta / previewSize.Width();
1694 previewGeometryNode->SetFrameSize(SizeF((1 - unitSpace) * previewSize.Width() / previewScale_,
1695 (1 - unitSpace) * previewSize.Height() / previewScale_));
1696 totalSize = totalSize - SizeF(unitSpace * previewSize.Width(), unitSpace * previewSize.Height());
1697 }
1698 previewSize = previewGeometryNode->GetMarginFrameSize() * previewScale_;
1699 auto heightLeftSpace = wrapperRect_.Height() - param_.topSecurity - param_.bottomSecurity;
1700 delta = previewSize.Height() - heightLeftSpace;
1701 if (GreatNotEqual(delta, 0.0f)) {
1702 auto unitSpace = delta / previewSize.Height();
1703 previewGeometryNode->SetFrameSize(SizeF((1 - unitSpace) * previewSize.Width() / previewScale_,
1704 (1 - unitSpace) * previewSize.Height() / previewScale_));
1705 totalSize = totalSize - SizeF(unitSpace * previewSize.Width(), unitSpace * previewSize.Height());
1706 }
1707 previewSize = previewGeometryNode->GetMarginFrameSize() * previewScale_;
1708 auto menuSize = menuGeometryNode->GetMarginFrameSize();
1709 menuGeometryNode->SetFrameSize(
1710 SizeF(menuSize.Width(), std::min<float>(param_.menuItemTotalHeight, heightLeftSpace)));
1711 menuSize = menuGeometryNode->GetMarginFrameSize();
1712 auto offsetX = 0.0f;
1713 auto offsetY = std::min<float>(targetCenterOffset.GetY() - previewSize.Height() / 2,
1714 wrapperRect_.Bottom() - param_.bottomSecurity - menuSize.Height());
1715 auto x = std::clamp(offsetX, static_cast<float>(wrapperRect_.Left()) + paddingStart_,
1716 static_cast<float>(wrapperRect_.Right()) - previewSize.Width() - paddingEnd_);
1717 auto y = std::clamp(offsetY, static_cast<float>(wrapperRect_.Top()) + param_.topSecurity,
1718 static_cast<float>(wrapperRect_.Bottom()) - param_.bottomSecurity - previewSize.Height());
1719 x = x + (previewSize.Width() - previewSize.Width() / previewScale_) / 2;
1720 y = y + (previewSize.Height() - previewSize.Height() / previewScale_) / 2;
1721 previewGeometryNode->SetMarginFrameOffset(OffsetF(x, y));
1722 }
1723
LayoutPreviewMenuGreateThanForConstant(const RefPtr<GeometryNode> & previewGeometryNode,const RefPtr<GeometryNode> & menuGeometryNode)1724 void MenuLayoutAlgorithm::LayoutPreviewMenuGreateThanForConstant(
1725 const RefPtr<GeometryNode>& previewGeometryNode, const RefPtr<GeometryNode>& menuGeometryNode)
1726 {
1727 CHECK_NULL_VOID(previewGeometryNode);
1728 CHECK_NULL_VOID(menuGeometryNode);
1729 auto previewSize = previewGeometryNode->GetMarginFrameSize() * previewScale_;
1730 auto menuSize = menuGeometryNode->GetMarginFrameSize();
1731 OffsetF center(
1732 targetOffset_.GetX() + targetSize_.Width() / HALF, targetOffset_.GetY() + targetSize_.Height() / HALF);
1733 targetCenterOffset_ = center;
1734
1735 RectF contentRect;
1736 contentRect.SetLeft(wrapperRect_.Left() + paddingStart_);
1737 contentRect.SetTop(wrapperRect_.Top() + param_.topSecurity);
1738 contentRect.SetWidth(wrapperRect_.Width() - paddingStart_ - paddingEnd_);
1739 contentRect.SetHeight(wrapperRect_.Height() - param_.topSecurity - param_.bottomSecurity);
1740
1741 auto x = std::clamp(
1742 center.GetX() - previewSize.Width() / HALF, contentRect.Left(), contentRect.Right() - previewSize.Width());
1743 auto y = std::clamp(
1744 center.GetY() - previewSize.Height() / HALF, contentRect.Top(), contentRect.Bottom() - previewSize.Height());
1745
1746 auto heightLeftSpace = 0.0f;
1747 if (placement_ == Placement::BOTTOM_LEFT || placement_ == Placement::BOTTOM ||
1748 placement_ == Placement::BOTTOM_RIGHT) {
1749 y = contentRect.Top();
1750 heightLeftSpace = contentRect.Height() - previewSize.Height() - param_.previewMenuGap;
1751 } else if (placement_ == Placement::TOP_LEFT || placement_ == Placement::TOP ||
1752 placement_ == Placement::TOP_RIGHT) {
1753 y = contentRect.Bottom() - previewSize.Height();
1754 heightLeftSpace = contentRect.Height() - previewSize.Height() - param_.previewMenuGap;
1755 } else { // leftPreviewRightMenu
1756 x = contentRect.Left();
1757 heightLeftSpace = contentRect.Height();
1758 }
1759
1760 if (GreatNotEqual(heightLeftSpace, 0.0f) && GreatNotEqual(menuSize.Height(), heightLeftSpace)) {
1761 menuGeometryNode->SetFrameSize(
1762 SizeF(menuSize.Width(), std::min<float>(param_.menuItemTotalHeight, heightLeftSpace)));
1763 }
1764
1765 x = x + (previewSize.Width() - previewSize.Width() / previewScale_) / HALF;
1766 y = y + (previewSize.Height() - previewSize.Height() / previewScale_) / HALF;
1767 previewGeometryNode->SetMarginFrameOffset(OffsetF(x, y));
1768 }
1769
LayoutOtherDeviceLeftPreviewRightMenu(const RefPtr<GeometryNode> & previewGeometryNode,const RefPtr<GeometryNode> & menuGeometryNode,SizeF & totalSize,float menuItemTotalHeight)1770 void MenuLayoutAlgorithm::LayoutOtherDeviceLeftPreviewRightMenu(const RefPtr<GeometryNode>& previewGeometryNode,
1771 const RefPtr<GeometryNode>& menuGeometryNode, SizeF& totalSize, float menuItemTotalHeight)
1772 {
1773 CHECK_NULL_VOID(previewGeometryNode);
1774 CHECK_NULL_VOID(menuGeometryNode);
1775 param_.menuItemTotalHeight = menuItemTotalHeight;
1776 auto widthLeftSpace = wrapperRect_.Width() - paddingStart_ - paddingEnd_;
1777 if (LessNotEqual(totalSize.Width() + targetSecurity_, widthLeftSpace)) {
1778 LayoutOtherDeviceLeftPreviewRightMenuLessThan(previewGeometryNode, menuGeometryNode, totalSize);
1779 } else {
1780 LayoutOtherDeviceLeftPreviewRightMenuGreateThan(previewGeometryNode, menuGeometryNode, totalSize);
1781 }
1782 auto previewSize = previewGeometryNode->GetMarginFrameSize();
1783 auto securityHeight =
1784 param_.menuWindowRect.Height() - param_.topSecurity - param_.top - param_.bottomSecurity - param_.bottom;
1785 if (GreatNotEqual(param_.windowGlobalSizeF.Height(), param_.menuWindowRect.Height()) &&
1786 GreatNotEqual(previewSize.Height(), securityHeight)) {
1787 previewGeometryNode->SetFrameSize(SizeF(previewSize.Width(), securityHeight));
1788 }
1789 }
1790
LayoutPreviewMenu(LayoutWrapper * layoutWrapper)1791 void MenuLayoutAlgorithm::LayoutPreviewMenu(LayoutWrapper* layoutWrapper)
1792 {
1793 CHECK_NULL_VOID(layoutWrapper);
1794 auto paintProperty = GetPaintProperty(layoutWrapper);
1795 CHECK_NULL_VOID(paintProperty);
1796 paintProperty->UpdateEnableArrow(false);
1797 auto menuNode = layoutWrapper->GetHostNode();
1798 CHECK_NULL_VOID(menuNode);
1799 auto parentNode = AceType::DynamicCast<FrameNode>(menuNode->GetParent());
1800 CHECK_NULL_VOID(parentNode);
1801 RefPtr<LayoutWrapper> menuLayoutWrapper;
1802 RefPtr<LayoutWrapper> previewLayoutWrapper;
1803 SizeF totalSize = GetPreviewNodeAndMenuNodeTotalSize(parentNode, previewLayoutWrapper, menuLayoutWrapper);
1804 CHECK_NULL_VOID(menuLayoutWrapper);
1805 CHECK_NULL_VOID(previewLayoutWrapper);
1806 RefPtr<GeometryNode> menuGeometryNode = menuLayoutWrapper->GetGeometryNode();
1807 RefPtr<GeometryNode> previewGeometryNode = previewLayoutWrapper->GetGeometryNode();
1808 CHECK_NULL_VOID(menuGeometryNode);
1809 CHECK_NULL_VOID(previewGeometryNode);
1810 auto menuItemTotalHeight = GetMenuItemTotalHeight(menuLayoutWrapper);
1811 if (placement_ == Placement::BOTTOM_LEFT || placement_ == Placement::BOTTOM ||
1812 placement_ == Placement::BOTTOM_RIGHT) {
1813 LayoutNormalTopPreviewBottomMenu(
1814 previewGeometryNode, menuGeometryNode, totalSize, menuItemTotalHeight, layoutWrapper);
1815 } else if (placement_ == Placement::TOP_LEFT || placement_ == Placement::TOP ||
1816 placement_ == Placement::TOP_RIGHT) {
1817 LayoutNormalBottomPreviewTopMenu(
1818 previewGeometryNode, menuGeometryNode, totalSize, menuItemTotalHeight, layoutWrapper);
1819 } else {
1820 LayoutOtherDeviceLeftPreviewRightMenu(previewGeometryNode, menuGeometryNode, totalSize, menuItemTotalHeight);
1821 }
1822 UpdateScrollAndColumnLayoutConstraint(previewLayoutWrapper, menuLayoutWrapper);
1823 UpdatePreviewPositionAndOffset(previewLayoutWrapper, menuLayoutWrapper);
1824 }
1825
UpdatePreviewPositionAndOffset(RefPtr<LayoutWrapper> & previewLayoutWrapper,RefPtr<LayoutWrapper> & menuLayoutWrapper)1826 void MenuLayoutAlgorithm::UpdatePreviewPositionAndOffset(
1827 RefPtr<LayoutWrapper>& previewLayoutWrapper, RefPtr<LayoutWrapper>& menuLayoutWrapper)
1828 {
1829 CHECK_NULL_VOID(previewLayoutWrapper);
1830 CHECK_NULL_VOID(menuLayoutWrapper);
1831 RefPtr<GeometryNode> previewGeometryNode = previewLayoutWrapper->GetGeometryNode();
1832 RefPtr<GeometryNode> menuGeometryNode = menuLayoutWrapper->GetGeometryNode();
1833 CHECK_NULL_VOID(previewGeometryNode);
1834 CHECK_NULL_VOID(menuGeometryNode);
1835
1836 auto previewSize = previewGeometryNode->GetMarginFrameSize();
1837 previewOffset_ = previewGeometryNode->GetFrameOffset();
1838 auto previewOffsetX = previewOffset_.GetX();
1839 auto previewOffsetY = previewOffset_.GetY();
1840 if (previewSize.IsPositive()) {
1841 targetSize_ = previewSize * previewScale_;
1842 targetOffset_ = OffsetF(previewOffsetX + (previewSize.Width() - targetSize_.Width()) / HALF,
1843 previewOffsetY + (previewSize.Height() - targetSize_.Height()) / HALF);
1844 }
1845 auto previewHostNode = previewLayoutWrapper->GetHostNode();
1846 CHECK_NULL_VOID(previewHostNode);
1847 auto renderContext = previewHostNode->GetRenderContext();
1848 CHECK_NULL_VOID(renderContext);
1849
1850 auto menuHostNode = menuLayoutWrapper->GetHostNode();
1851 CHECK_NULL_VOID(menuHostNode);
1852 previewOriginOffset_ = targetCenterOffset_ - OffsetF(previewSize.Width() / HALF, previewSize.Height() / HALF);
1853 previewSize_ = previewSize;
1854 auto menuPattern = menuHostNode->GetPattern<MenuPattern>();
1855 CHECK_NULL_VOID(menuPattern);
1856 auto lastPreviewOffset = menuPattern->GetPreviewRect().GetOffset();
1857 menuPattern->SetPreviewOriginOffset(previewOriginOffset_);
1858 menuPattern->SetPreviewRect(RectF(previewOffset_, previewSize_));
1859
1860 if (previewOffset_ != lastPreviewOffset) {
1861 renderContext->UpdatePosition(OffsetT<Dimension>(Dimension(previewOffsetX), Dimension(previewOffsetY)));
1862 }
1863
1864 TAG_LOGD(AceLogTag::ACE_MENU, "previewOriginOffset: %{public}s currentPreviewRect: %{public}s",
1865 previewOriginOffset_.ToString().c_str(), menuPattern->GetPreviewRect().ToString().c_str());
1866 }
1867
FixMenuOriginOffset(float beforeAnimationScale,float afterAnimationScale)1868 OffsetF MenuLayoutAlgorithm::FixMenuOriginOffset(float beforeAnimationScale, float afterAnimationScale)
1869 {
1870 auto beforeRate = (1.0f - beforeAnimationScale) / 2;
1871 auto beforeScalePreviewOffset = OffsetF((previewSize_ * beforeRate).Width(),
1872 (previewSize_ * beforeRate).Height());
1873 auto afterScalePreviewOffset = OffsetF((previewSize_ * ((afterAnimationScale - 1.0f) / 2)).Width(),
1874 (previewSize_ * ((afterAnimationScale - 1.0f) / 2)).Height());
1875 auto scaleOffset = afterScalePreviewOffset + beforeScalePreviewOffset;
1876 float x = 0.0f;
1877 float y = 0.0f;
1878 switch (placement_) {
1879 case Placement::BOTTOM_LEFT:
1880 case Placement::LEFT_BOTTOM:
1881 x += scaleOffset.GetX();
1882 y -= scaleOffset.GetY();
1883 break;
1884 case Placement::TOP_RIGHT:
1885 case Placement::RIGHT_TOP:
1886 x -= scaleOffset.GetX();
1887 y += scaleOffset.GetY();
1888 break;
1889 case Placement::TOP_LEFT:
1890 case Placement::LEFT_TOP:
1891 x += scaleOffset.GetX();
1892 y += scaleOffset.GetY();
1893 break;
1894 case Placement::BOTTOM_RIGHT:
1895 case Placement::RIGHT_BOTTOM:
1896 x -= scaleOffset.GetX();
1897 y -= scaleOffset.GetY();
1898 break;
1899 case Placement::BOTTOM:
1900 y -= scaleOffset.GetY();
1901 break;
1902 case Placement::TOP:
1903 y += scaleOffset.GetY();
1904 break;
1905 case Placement::LEFT:
1906 x += scaleOffset.GetX();
1907 break;
1908 case Placement::RIGHT:
1909 x -= scaleOffset.GetX();
1910 break;
1911 default:
1912 x += scaleOffset.GetX();
1913 y -= scaleOffset.GetY();
1914 break;
1915 }
1916 return OffsetF(x, y);
1917 }
1918
CalculateChildOffset(bool didNeedArrow)1919 void MenuLayoutAlgorithm::CalculateChildOffset(bool didNeedArrow)
1920 {
1921 childOffset_.Reset();
1922 if (didNeedArrow) {
1923 switch (arrowPlacement_) {
1924 case Placement::RIGHT:
1925 case Placement::RIGHT_TOP:
1926 case Placement::RIGHT_BOTTOM:
1927 childOffset_.SetX(ARROW_HIGHT.ConvertToPx());
1928 break;
1929 case Placement::BOTTOM:
1930 case Placement::BOTTOM_LEFT:
1931 case Placement::BOTTOM_RIGHT:
1932 childOffset_.SetY(ARROW_HIGHT.ConvertToPx());
1933 break;
1934 default:
1935 break;
1936 }
1937 }
1938 }
1939
CalculateMenuPositionWithArrow(const OffsetF & menuPosition,bool didNeedArrow)1940 OffsetF MenuLayoutAlgorithm::CalculateMenuPositionWithArrow(const OffsetF& menuPosition, bool didNeedArrow)
1941 {
1942 OffsetF menuPositionWithArrow = menuPosition;
1943 if (didNeedArrow) {
1944 switch (arrowPlacement_) {
1945 case Placement::RIGHT:
1946 case Placement::RIGHT_TOP:
1947 case Placement::RIGHT_BOTTOM:
1948 menuPositionWithArrow -= OffsetF(ARROW_HIGHT.ConvertToPx(), 0);
1949 break;
1950 case Placement::BOTTOM:
1951 case Placement::BOTTOM_LEFT:
1952 case Placement::BOTTOM_RIGHT:
1953 menuPositionWithArrow -= OffsetF(0, ARROW_HIGHT.ConvertToPx());
1954 break;
1955 default:
1956 break;
1957 }
1958 }
1959 return menuPositionWithArrow;
1960 }
1961
UpdateMenuFrameSizeWithArrow(const RefPtr<GeometryNode> & geometryNode,bool didNeedArrow)1962 void MenuLayoutAlgorithm::UpdateMenuFrameSizeWithArrow(const RefPtr<GeometryNode>& geometryNode,
1963 bool didNeedArrow)
1964 {
1965 if (didNeedArrow && arrowPlacement_ != Placement::NONE) {
1966 switch (arrowPlacement_) {
1967 case Placement::LEFT:
1968 case Placement::LEFT_TOP:
1969 case Placement::LEFT_BOTTOM:
1970 case Placement::RIGHT:
1971 case Placement::RIGHT_TOP:
1972 case Placement::RIGHT_BOTTOM:
1973 geometryNode->SetFrameSize(geometryNode->GetFrameSize() + SizeF(ARROW_HIGHT.ConvertToPx(), 0));
1974 break;
1975 case Placement::TOP:
1976 case Placement::TOP_LEFT:
1977 case Placement::TOP_RIGHT:
1978 case Placement::BOTTOM:
1979 case Placement::BOTTOM_LEFT:
1980 case Placement::BOTTOM_RIGHT:
1981 geometryNode->SetFrameSize(geometryNode->GetFrameSize() + SizeF(0, ARROW_HIGHT.ConvertToPx()));
1982 break;
1983 default:
1984 break;
1985 }
1986 }
1987 }
1988
Layout(LayoutWrapper * layoutWrapper)1989 void MenuLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper)
1990 {
1991 auto targetNode = FrameNode::GetFrameNode(targetTag_, targetNodeId_);
1992 // if targetNode == nullptr, it means the menu is subMenu or multiMenu
1993 if (targetNode && !IsNodeOnRootTree(targetNode)) {
1994 TAG_LOGW(AceLogTag::ACE_MENU, "layout return because targetNode %{public}d not no root tree", targetNodeId_);
1995 return;
1996 }
1997 CHECK_NULL_VOID(layoutWrapper);
1998 auto geometryNode = layoutWrapper->GetGeometryNode();
1999 CHECK_NULL_VOID(geometryNode);
2000 auto menuNode = layoutWrapper->GetHostNode();
2001 CHECK_NULL_VOID(menuNode);
2002 auto menuPattern = menuNode->GetPattern<MenuPattern>();
2003 CHECK_NULL_VOID(menuPattern);
2004
2005 if (menuPattern->GetPreviewMode() != MenuPreviewMode::NONE && !holdEmbeddedMenuPosition_) {
2006 LayoutPreviewMenu(layoutWrapper);
2007 }
2008 if (!menuPattern->IsSelectOverlayCustomMenu()) {
2009 auto menuProp = DynamicCast<MenuLayoutProperty>(layoutWrapper->GetLayoutProperty());
2010 CHECK_NULL_VOID(menuProp);
2011 auto menuPosition = UpdateMenuPosition(layoutWrapper, menuNode, menuPattern, menuProp);
2012 auto pipeline = menuNode->GetContext();
2013 CHECK_NULL_VOID(pipeline);
2014 auto menuTheme = pipeline->GetTheme<NG::MenuTheme>();
2015 CHECK_NULL_VOID(menuTheme);
2016 auto beforeAnimationScale = menuTheme->GetPreviewBeforeAnimationScale();
2017 auto afterAnimationScale = menuTheme->GetPreviewAfterAnimationScale();
2018 auto menuOriginOffset = menuPosition - (previewOffset_ - previewOriginOffset_) +
2019 FixMenuOriginOffset(beforeAnimationScale, afterAnimationScale);
2020 menuPattern->SetOriginOffset(menuOriginOffset);
2021 auto previewScale = 1.0f;
2022 if (menuPattern->GetPreviewMode() == MenuPreviewMode::IMAGE &&
2023 !NearEqual(menuPattern->GetTargetSize().Width(), previewSize_.Width())) {
2024 previewScale = menuPattern->GetTargetSize().Width() / previewSize_.Width();
2025 }
2026 auto menuEndOffset = menuPosition - (previewOffset_ - previewOriginOffset_) +
2027 FixMenuOriginOffset(previewScale, afterAnimationScale);
2028 menuPattern->SetEndOffset(menuEndOffset);
2029 menuPattern->SetHasLaid(true);
2030 dumpInfo_.menuPreviewMode = static_cast<uint32_t>(menuPattern->GetPreviewMode());
2031 dumpInfo_.menuType = static_cast<uint32_t>(menuPattern->GetMenuType());
2032 auto menuWrapper = menuPattern->GetMenuWrapper();
2033 CHECK_NULL_VOID(menuWrapper);
2034 auto wrapperPattern = menuWrapper->GetPattern<MenuWrapperPattern>();
2035 CHECK_NULL_VOID(wrapperPattern);
2036 dumpInfo_.anchorPosition = menuProp->GetAnchorPosition().value_or(OffsetF());
2037 wrapperPattern->SetDumpInfo(dumpInfo_);
2038 UpdateMenuFrameSizeWithArrow(geometryNode, didNeedArrow_);
2039 }
2040
2041 TranslateOptions(layoutWrapper);
2042 ClipMenuPath(layoutWrapper);
2043 }
2044
UpdateMenuPosition(LayoutWrapper * layoutWrapper,const RefPtr<FrameNode> & menuNode,RefPtr<MenuPattern> menuPattern,const RefPtr<MenuLayoutProperty> & menuProp)2045 OffsetF MenuLayoutAlgorithm::UpdateMenuPosition(LayoutWrapper* layoutWrapper, const RefPtr<FrameNode>& menuNode,
2046 RefPtr<MenuPattern> menuPattern, const RefPtr<MenuLayoutProperty>& menuProp)
2047 {
2048 OffsetF menuPosition = OffsetF();
2049 auto geometryNode = layoutWrapper->GetGeometryNode();
2050 CHECK_NULL_RETURN(geometryNode, menuPosition);
2051 auto size = geometryNode->GetMarginFrameSize();
2052 didNeedArrow_ = GetIfNeedArrow(layoutWrapper, size);
2053 if (menuPattern->IsSelectMenu()) {
2054 ComputeMenuPositionByAlignType(menuProp, size);
2055 auto offset = ComputeMenuPositionByOffset(menuProp, geometryNode);
2056 position_ += offset;
2057 }
2058 auto useLastPosition = lastPosition_.has_value() && holdEmbeddedMenuPosition_;
2059 auto avoidanceMode = menuProp->GetSelectAvoidanceMode().value_or(AvoidanceMode::COVER_TARGET);
2060 if (useLastPosition) {
2061 menuPosition = lastPosition_.value();
2062 auto lastPlacement = menuPattern->GetLastPlacement();
2063 if (lastPlacement.has_value()) {
2064 placement_ = lastPlacement.value();
2065 arrowPlacement_ = lastPlacement.value();
2066 }
2067 } else if (menuPattern->IsSelectMenu() && avoidanceMode == AvoidanceMode::AVOID_AROUND_TARGET &&
2068 !IsSelectMenuShowInSubWindow(layoutWrapper, menuNode)) {
2069 menuPosition = SelectLayoutAvoidAlgorithm(menuProp, menuPattern, size, didNeedArrow_, layoutWrapper);
2070 } else {
2071 menuPosition = MenuLayoutAvoidAlgorithm(menuProp, menuPattern, size, didNeedArrow_, layoutWrapper);
2072 }
2073 menuPattern->UpdateLastPosition(menuPosition);
2074 menuPattern->UpdateLastPlacement(placement_);
2075 CalculateChildOffset(didNeedArrow_);
2076 OffsetF menuPositionWithArrow = CalculateMenuPositionWithArrow(menuPosition, didNeedArrow_);
2077 TAG_LOGD(AceLogTag::ACE_MENU, "update menu postion: %{public}s", menuPositionWithArrow.ToString().c_str());
2078 auto renderContext = menuNode->GetRenderContext();
2079 CHECK_NULL_RETURN(renderContext, menuPosition);
2080 // show animation will be interrupted by repeated update
2081 if (lastPosition_.value_or(OffsetF()) != menuPositionWithArrow) {
2082 renderContext->UpdatePosition(
2083 OffsetT<Dimension>(Dimension(menuPositionWithArrow.GetX()), Dimension(menuPositionWithArrow.GetY())));
2084 }
2085 dumpInfo_.finalPlacement = PlacementUtils::ConvertPlacementToString(placement_);
2086 dumpInfo_.finalPosition = menuPosition;
2087 SetMenuPlacementForAnimation(layoutWrapper);
2088 if (didNeedArrow_ && arrowPlacement_ != Placement::NONE) {
2089 arrowPosition_ = GetArrowPositionWithPlacement(size, layoutWrapper);
2090 LayoutArrow(layoutWrapper);
2091 }
2092 geometryNode->SetFrameOffset(menuPositionWithArrow);
2093 return menuPosition;
2094 }
2095
IsSelectMenuShowInSubWindow(LayoutWrapper * layoutWrapper,const RefPtr<FrameNode> & menuNode)2096 bool MenuLayoutAlgorithm::IsSelectMenuShowInSubWindow(LayoutWrapper* layoutWrapper, const RefPtr<FrameNode>& menuNode)
2097 {
2098 CHECK_NULL_RETURN(layoutWrapper, false);
2099 CHECK_NULL_RETURN(menuNode, false);
2100 auto pipelineContext = menuNode->GetContext();
2101 CHECK_NULL_RETURN(pipelineContext, false);
2102 auto theme = pipelineContext->GetTheme<SelectTheme>();
2103 CHECK_NULL_RETURN(theme, false);
2104 auto menuLayoutProperty = AceType::DynamicCast<MenuLayoutProperty>(layoutWrapper->GetLayoutProperty());
2105 CHECK_NULL_RETURN(menuLayoutProperty, false);
2106 auto isSelectMenuShowInSubWindow = theme->GetExpandDisplay() && menuLayoutProperty->GetShowInSubWindowValue(false);
2107 return isSelectMenuShowInSubWindow;
2108 }
2109
TranslateOptions(LayoutWrapper * layoutWrapper)2110 void MenuLayoutAlgorithm::TranslateOptions(LayoutWrapper* layoutWrapper)
2111 {
2112 // translate each option by the height of previous options
2113 OffsetF translate = childOffset_;
2114 for (const auto& child : layoutWrapper->GetAllChildrenWithBuild()) {
2115 child->GetGeometryNode()->SetMarginFrameOffset(translate);
2116 child->Layout();
2117 translate += OffsetF(0, child->GetGeometryNode()->GetFrameSize().Height());
2118 }
2119 }
2120
SetMenuPlacementForAnimation(LayoutWrapper * layoutWrapper)2121 void MenuLayoutAlgorithm::SetMenuPlacementForAnimation(LayoutWrapper* layoutWrapper)
2122 {
2123 auto menu = layoutWrapper->GetHostNode();
2124 CHECK_NULL_VOID(menu);
2125 auto menuPattern = menu->GetPattern<MenuPattern>();
2126 CHECK_NULL_VOID(menuPattern);
2127 auto menuWrapper = menuPattern->GetMenuWrapper();
2128 CHECK_NULL_VOID(menuWrapper);
2129 auto wrapperPattern = menuWrapper->GetPattern<MenuWrapperPattern>();
2130 CHECK_NULL_VOID(wrapperPattern);
2131 wrapperPattern->SetMenuPlacementAfterLayout(placement_);
2132 }
2133
LayoutArrow(const LayoutWrapper * layoutWrapper)2134 void MenuLayoutAlgorithm::LayoutArrow(const LayoutWrapper* layoutWrapper)
2135 {
2136 auto paintProperty = GetPaintProperty(layoutWrapper);
2137 CHECK_NULL_VOID(paintProperty);
2138 paintProperty->UpdateArrowPosition(arrowPosition_);
2139 paintProperty->UpdateArrowPlacement(arrowPlacement_);
2140 dumpInfo_.enableArrow = true;
2141 }
2142
GetPaintProperty(const LayoutWrapper * layoutWrapper)2143 RefPtr<MenuPaintProperty> MenuLayoutAlgorithm::GetPaintProperty(const LayoutWrapper* layoutWrapper)
2144 {
2145 auto menuNode = layoutWrapper->GetHostNode();
2146 CHECK_NULL_RETURN(menuNode, nullptr);
2147 auto paintProperty = menuNode->GetPaintProperty<MenuPaintProperty>();
2148 CHECK_NULL_RETURN(paintProperty, nullptr);
2149 return paintProperty;
2150 }
2151
GetMenuRadius(const LayoutWrapper * layoutWrapper,const SizeF & menuSize)2152 BorderRadiusProperty MenuLayoutAlgorithm::GetMenuRadius(const LayoutWrapper* layoutWrapper, const SizeF& menuSize)
2153 {
2154 Dimension defaultDimension(0);
2155 BorderRadiusProperty radius = { defaultDimension, defaultDimension, defaultDimension, defaultDimension };
2156 auto hostNode = layoutWrapper->GetHostNode();
2157 CHECK_NULL_RETURN(hostNode, radius);
2158 auto theme = GetCurrentSelectTheme(hostNode);
2159 CHECK_NULL_RETURN(theme, radius);
2160 auto defaultRadius = Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)
2161 ? theme->GetMenuDefaultRadius()
2162 : theme->GetMenuBorderRadius();
2163 radius.SetRadius(defaultRadius);
2164 auto menuLayoutProp = DynamicCast<MenuLayoutProperty>(layoutWrapper->GetLayoutProperty());
2165 CHECK_NULL_RETURN(menuLayoutProp, radius);
2166 if (menuLayoutProp->GetBorderRadius().has_value()) {
2167 auto menuNode = layoutWrapper->GetHostNode();
2168 CHECK_NULL_RETURN(menuNode, radius);
2169 auto menuPattern = menuNode->GetPattern<MenuPattern>();
2170 CHECK_NULL_RETURN(menuPattern, radius);
2171 radius = menuPattern->CalcIdealBorderRadius(menuLayoutProp->GetBorderRadiusValue(), menuSize);
2172 }
2173
2174 return radius;
2175 }
2176
GetIfNeedArrow(const LayoutWrapper * layoutWrapper,const SizeF & menuSize)2177 bool MenuLayoutAlgorithm::GetIfNeedArrow(const LayoutWrapper* layoutWrapper, const SizeF& menuSize)
2178 {
2179 CHECK_NULL_RETURN(layoutWrapper, false);
2180 auto menuNode = layoutWrapper->GetHostNode();
2181 CHECK_NULL_RETURN(menuNode, false);
2182 auto menuPattern = menuNode->GetPattern<MenuPattern>();
2183 CHECK_NULL_RETURN(menuPattern, false);
2184 auto menuProp = DynamicCast<MenuLayoutProperty>(layoutWrapper->GetLayoutProperty());
2185 CHECK_NULL_RETURN(menuProp, false);
2186 auto paintProperty = GetPaintProperty(layoutWrapper);
2187 CHECK_NULL_RETURN(paintProperty, false);
2188 propNeedArrow_ = paintProperty->GetEnableArrow().value_or(false);
2189
2190 auto hostNode = layoutWrapper->GetHostNode();
2191 CHECK_NULL_RETURN(hostNode, false);
2192 auto selectThemePtr = GetCurrentSelectTheme(menuNode);
2193 CHECK_NULL_RETURN(selectThemePtr, false);
2194 if (!propNeedArrow_ || !menuProp->GetMenuPlacement().has_value()) {
2195 return false;
2196 }
2197
2198 propArrowOffset_ = paintProperty->GetArrowOffset().value_or(Dimension(0));
2199 ProcessArrowParams(layoutWrapper, menuSize);
2200
2201 if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
2202 return (menuPattern->IsContextMenu() || menuPattern->IsMenu()) && !targetTag_.empty() && arrowInMenu_;
2203 }
2204
2205 return menuPattern->IsContextMenu() && !targetTag_.empty() && arrowInMenu_;
2206 }
2207
ProcessArrowParams(const LayoutWrapper * layoutWrapper,const SizeF & menuSize)2208 void MenuLayoutAlgorithm::ProcessArrowParams(const LayoutWrapper* layoutWrapper, const SizeF& menuSize)
2209 {
2210 BorderRadiusProperty menuBorderRadius = GetMenuRadius(layoutWrapper, menuSize);
2211 auto radiusTopLeft = menuBorderRadius.radiusTopLeft.value_or(Dimension()).ConvertToPx();
2212 auto radiusTopRight = menuBorderRadius.radiusTopRight.value_or(Dimension()).ConvertToPx();
2213 auto radiusBottomLeft = menuBorderRadius.radiusBottomLeft.value_or(Dimension()).ConvertToPx();
2214 auto radiusBottomRight = menuBorderRadius.radiusBottomRight.value_or(Dimension()).ConvertToPx();
2215 arrowWidth_ = ARROW_WIDTH.ConvertToPx();
2216
2217 switch (placement_) {
2218 case Placement::LEFT:
2219 case Placement::LEFT_TOP:
2220 case Placement::LEFT_BOTTOM:
2221 if (GreatOrEqual(menuSize.Height(), radiusTopRight + radiusBottomRight + arrowWidth_)) {
2222 arrowInMenu_ = true;
2223 }
2224 break;
2225 case Placement::RIGHT:
2226 case Placement::RIGHT_TOP:
2227 case Placement::RIGHT_BOTTOM:
2228 if (GreatOrEqual(menuSize.Height(), radiusTopLeft + radiusBottomLeft + arrowWidth_)) {
2229 arrowInMenu_ = true;
2230 }
2231 break;
2232 case Placement::TOP:
2233 case Placement::TOP_LEFT:
2234 case Placement::TOP_RIGHT:
2235 if (GreatOrEqual(menuSize.Width(), radiusBottomLeft + radiusBottomRight + arrowWidth_)) {
2236 arrowInMenu_ = true;
2237 }
2238 break;
2239 case Placement::BOTTOM:
2240 case Placement::BOTTOM_LEFT:
2241 case Placement::BOTTOM_RIGHT:
2242 if (GreatOrEqual(menuSize.Width(), radiusTopLeft + radiusTopRight + arrowWidth_)) {
2243 arrowInMenu_ = true;
2244 }
2245 break;
2246 default:
2247 break;
2248 }
2249
2250 if (arrowInMenu_) {
2251 targetSpace_ = TARGET_SPACE.ConvertToPx();
2252 }
2253 }
2254
UpdatePropArrowOffset()2255 void MenuLayoutAlgorithm::UpdatePropArrowOffset()
2256 {
2257 if (propArrowOffset_.IsValid()) {
2258 if (propArrowOffset_.Unit() == DimensionUnit::PERCENT) {
2259 propArrowOffset_.SetValue(std::clamp(propArrowOffset_.Value(), 0.0, 1.0));
2260 }
2261 return;
2262 }
2263 switch (arrowPlacement_) {
2264 case Placement::LEFT:
2265 case Placement::RIGHT:
2266 case Placement::TOP:
2267 case Placement::BOTTOM:
2268 propArrowOffset_ = ARROW_HALF_PERCENT_VALUE;
2269 break;
2270 case Placement::TOP_LEFT:
2271 case Placement::BOTTOM_LEFT:
2272 case Placement::LEFT_TOP:
2273 case Placement::RIGHT_TOP:
2274 propArrowOffset_ = ARROW_ZERO_PERCENT_VALUE;
2275 break;
2276 case Placement::TOP_RIGHT:
2277 case Placement::BOTTOM_RIGHT:
2278 case Placement::LEFT_BOTTOM:
2279 case Placement::RIGHT_BOTTOM:
2280 propArrowOffset_ = ARROW_ONE_HUNDRED_PERCENT_VALUE;
2281 break;
2282 default:
2283 break;
2284 }
2285 }
2286
UpdateArrowOffsetWithMenuLimit(const SizeF & menuSize,const LayoutWrapper * layoutWrapper)2287 void MenuLayoutAlgorithm::UpdateArrowOffsetWithMenuLimit(const SizeF& menuSize, const LayoutWrapper* layoutWrapper)
2288 {
2289 UpdatePropArrowOffset();
2290 BorderRadiusProperty menuBorderRadius = GetMenuRadius(layoutWrapper, menuSize);
2291 auto radiusTopLeft = menuBorderRadius.radiusTopLeft.value_or(Dimension()).ConvertToPx();
2292 auto radiusTopRight = menuBorderRadius.radiusTopRight.value_or(Dimension()).ConvertToPx();
2293 auto radiusBottomLeft = menuBorderRadius.radiusBottomLeft.value_or(Dimension()).ConvertToPx();
2294 auto radiusBottomRight = menuBorderRadius.radiusBottomRight.value_or(Dimension()).ConvertToPx();
2295 float range = -1.0f;
2296
2297 switch (arrowPlacement_) {
2298 case Placement::LEFT:
2299 case Placement::LEFT_TOP:
2300 case Placement::LEFT_BOTTOM:
2301 range = menuSize.Height() - radiusTopRight - radiusBottomRight - arrowWidth_;
2302 arrowMinLimit_ = radiusTopRight + ARROW_WIDTH.ConvertToPx() / ARROW_WIDTH_FACTOR;
2303 break;
2304 case Placement::RIGHT:
2305 case Placement::RIGHT_TOP:
2306 case Placement::RIGHT_BOTTOM:
2307 range = menuSize.Height() - radiusTopLeft - radiusBottomLeft - arrowWidth_;
2308 arrowMinLimit_ = radiusTopLeft + ARROW_WIDTH.ConvertToPx() / ARROW_WIDTH_FACTOR;
2309 break;
2310 case Placement::TOP:
2311 case Placement::TOP_LEFT:
2312 case Placement::TOP_RIGHT:
2313 range = menuSize.Width() - radiusBottomLeft - radiusBottomRight - arrowWidth_;
2314 arrowMinLimit_ = radiusBottomLeft + ARROW_WIDTH.ConvertToPx() / ARROW_WIDTH_FACTOR;
2315 break;
2316 case Placement::BOTTOM:
2317 case Placement::BOTTOM_LEFT:
2318 case Placement::BOTTOM_RIGHT:
2319 range = menuSize.Width() - radiusTopLeft - radiusTopRight - arrowWidth_;
2320 arrowMinLimit_ = radiusTopLeft + ARROW_WIDTH.ConvertToPx() / ARROW_WIDTH_FACTOR;
2321 break;
2322 default:
2323 break;
2324 }
2325 if (range >= 0) {
2326 float tempOffset = propArrowOffset_.Unit() == DimensionUnit::PERCENT ? propArrowOffset_.Value() * range
2327 : propArrowOffset_.ConvertToPx();
2328 arrowOffset_ = std::clamp(tempOffset, 0.0f, range);
2329 }
2330 }
2331
ComputeMenuPositionByAlignType(const RefPtr<MenuLayoutProperty> & menuProp,const SizeF & menuSize)2332 void MenuLayoutAlgorithm::ComputeMenuPositionByAlignType(
2333 const RefPtr<MenuLayoutProperty>& menuProp, const SizeF& menuSize)
2334 {
2335 auto alignType = menuProp->GetAlignType().value_or(MenuAlignType::START);
2336 auto direction = menuProp->GetNonAutoLayoutDirection();
2337 auto targetSize = menuProp->GetTargetSizeValue(SizeF());
2338 switch (alignType) {
2339 case MenuAlignType::CENTER: {
2340 position_.AddX(targetSize.Width() / 2.0f - menuSize.Width() / 2.0f);
2341 break;
2342 }
2343 case MenuAlignType::END: {
2344 if (direction == TextDirection::RTL) {
2345 return;
2346 }
2347 position_.AddX(targetSize.Width() - menuSize.Width());
2348 break;
2349 }
2350 case MenuAlignType::START: {
2351 if (direction != TextDirection::RTL) {
2352 return;
2353 }
2354 position_.AddX(targetSize.Width() - menuSize.Width());
2355 break;
2356 }
2357 default:
2358 break;
2359 }
2360 }
2361
ComputeMenuPositionByOffset(const RefPtr<MenuLayoutProperty> & menuProp,const RefPtr<GeometryNode> & geometryNode)2362 OffsetF MenuLayoutAlgorithm::ComputeMenuPositionByOffset(
2363 const RefPtr<MenuLayoutProperty>& menuProp, const RefPtr<GeometryNode>& geometryNode)
2364 {
2365 CHECK_NULL_RETURN(menuProp, OffsetF(0, 0));
2366 CHECK_NULL_RETURN(geometryNode, OffsetF(0, 0));
2367
2368 const auto& layoutConstraint = menuProp->GetLayoutConstraint();
2369 CHECK_NULL_RETURN(layoutConstraint, OffsetF(0, 0));
2370 auto menuAlignOffset = menuProp->GetOffset().value_or(
2371 DimensionOffset(Dimension(0, DimensionUnit::VP), Dimension(0, DimensionUnit::VP)));
2372
2373 auto menuSize = geometryNode->GetFrameSize();
2374 auto menuTrimOffsetX =
2375 ConvertToPx(CalcLength(menuAlignOffset.GetX()), layoutConstraint->scaleProperty, menuSize.Width());
2376 auto menuTrimOffsetY =
2377 ConvertToPx(CalcLength(menuAlignOffset.GetY()), layoutConstraint->scaleProperty, menuSize.Height());
2378 OffsetF menuTrimOffset = OffsetF(menuTrimOffsetX.value_or(0.0), menuTrimOffsetY.value_or(0.0));
2379 return menuTrimOffset;
2380 }
2381
2382 // Adapt to the menualign specification in select avoidance mode.
ComputePlacementByAlignType(const RefPtr<MenuLayoutProperty> & menuProp)2383 void MenuLayoutAlgorithm::ComputePlacementByAlignType(const RefPtr<MenuLayoutProperty>& menuProp)
2384 {
2385 auto alignType = menuProp->GetAlignType().value_or(MenuAlignType::START);
2386 switch (alignType) {
2387 case MenuAlignType::START:
2388 placement_ = Placement::BOTTOM_LEFT;
2389 break;
2390 case MenuAlignType::CENTER:
2391 placement_ = Placement::BOTTOM;
2392 break;
2393 case MenuAlignType::END:
2394 placement_ = Placement::BOTTOM_RIGHT;
2395 break;
2396 default:
2397 break;
2398 }
2399 }
2400
MenuLayoutAvoidAlgorithm(const RefPtr<MenuLayoutProperty> & menuProp,const RefPtr<MenuPattern> & menuPattern,const SizeF & size,bool didNeedArrow,LayoutWrapper * layoutWrapper)2401 OffsetF MenuLayoutAlgorithm::MenuLayoutAvoidAlgorithm(const RefPtr<MenuLayoutProperty>& menuProp,
2402 const RefPtr<MenuPattern>& menuPattern, const SizeF& size, bool didNeedArrow, LayoutWrapper* layoutWrapper)
2403 {
2404 CHECK_NULL_RETURN(menuProp, OffsetF(0.0f, 0.0f));
2405 CHECK_NULL_RETURN(menuPattern, OffsetF(0.0f, 0.0f));
2406 CHECK_NULL_RETURN(layoutWrapper, OffsetF(0.0f, 0.0f));
2407 auto hostNode = layoutWrapper->GetHostNode();
2408 CHECK_NULL_RETURN(hostNode, OffsetF(0.0f, 0.0f));
2409 auto pipelineContext = DialogManager::GetMainPipelineContext(hostNode);
2410 CHECK_NULL_RETURN(pipelineContext, OffsetF(0.0f, 0.0f));
2411 float x = 0.0f;
2412 float y = 0.0f;
2413 if (menuProp->GetMenuPlacement().has_value() &&
2414 (GreatNotEqual(targetSize_.Width(), 0.0) || GreatNotEqual(targetSize_.Height(), 0.0))) {
2415 placement_ = menuProp->GetMenuPlacement().value();
2416 if (layoutWrapper != nullptr) {
2417 PlacementRTL(layoutWrapper, placement_);
2418 }
2419 auto childOffset = GetChildPosition(size, didNeedArrow);
2420 x = childOffset.GetX();
2421 y = childOffset.GetY();
2422 } else {
2423 x = HorizontalLayout(size, position_.GetX(), menuPattern->IsSelectMenu());
2424 y = VerticalLayout(size, position_.GetY(), menuPattern->IsContextMenu());
2425 }
2426 x = std::clamp(static_cast<double>(x), static_cast<double>(paddingStart_),
2427 static_cast<double>(wrapperRect_.Right() - size.Width() - paddingEnd_));
2428 float yMinAvoid = wrapperRect_.Top() + paddingTop_;
2429 float yMaxAvoid = wrapperRect_.Bottom() - paddingBottom_ - size.Height();
2430 y = std::clamp(y, yMinAvoid, yMaxAvoid);
2431 return { x, y };
2432 }
2433
SelectLayoutAvoidAlgorithm(const RefPtr<MenuLayoutProperty> & menuProp,const RefPtr<MenuPattern> & menuPattern,const SizeF & size,bool didNeedArrow,LayoutWrapper * layoutWrapper)2434 OffsetF MenuLayoutAlgorithm::SelectLayoutAvoidAlgorithm(const RefPtr<MenuLayoutProperty>& menuProp,
2435 const RefPtr<MenuPattern>& menuPattern, const SizeF& size, bool didNeedArrow, LayoutWrapper* layoutWrapper)
2436 {
2437 CHECK_NULL_RETURN(menuProp, OffsetF(0.0f, 0.0f));
2438 CHECK_NULL_RETURN(menuPattern, OffsetF(0.0f, 0.0f));
2439 CHECK_NULL_RETURN(layoutWrapper, OffsetF(0.0f, 0.0f));
2440 auto host = menuPattern->GetHost();
2441 CHECK_NULL_RETURN(host, OffsetF(0.0f, 0.0f));
2442 auto pipelineContext = DialogManager::GetMainPipelineContext(host);
2443 CHECK_NULL_RETURN(pipelineContext, OffsetF(0.0f, 0.0f));
2444 auto geometryNode = layoutWrapper->GetGeometryNode();
2445 CHECK_NULL_RETURN(geometryNode, OffsetF(0.0f, 0.0f));
2446 float x = 0.0f;
2447 float y = 0.0f;
2448 float selectMenuHeight = geometryNode->GetFrameSize().Height();
2449 Rect targetRect(targetOffset_.GetX(), targetOffset_.GetY(), targetSize_.Width(), targetSize_.Height());
2450 float bottomSpace = wrapperRect_.Bottom() - targetRect.Bottom() - targetSecurity_ - paddingBottom_;
2451
2452 float xMinAvoid = paddingStart_;
2453 float xMaxAvoid = wrapperRect_.Right() - size.Width() - paddingEnd_;
2454 float yMinAvoid = wrapperRect_.Top() + paddingTop_;
2455 float yMaxAvoid = wrapperRect_.Bottom() - paddingBottom_ - size.Height();
2456
2457 if (GreatNotEqual(targetSize_.Width(), 0.0) || GreatNotEqual(targetSize_.Height(), 0.0)) {
2458 placement_ = Placement::BOTTOM_LEFT;
2459 ComputePlacementByAlignType(menuProp);
2460 if (layoutWrapper != nullptr) {
2461 PlacementRTL(layoutWrapper, placement_);
2462 }
2463 OffsetF selectChildOffset = GetSelectChildPosition(size, didNeedArrow, layoutWrapper);
2464 x = selectChildOffset.GetX();
2465 y = selectChildOffset.GetY();
2466 OffsetF computedOffset = selectChildOffset + ComputeMenuPositionByOffset(menuProp, geometryNode);
2467 bool isXOut = LessOrEqual(computedOffset.GetX(), xMinAvoid) ||
2468 GreatOrEqual(computedOffset.GetX(), xMaxAvoid);
2469 bool isYOut = LessOrEqual(computedOffset.GetY(), yMinAvoid) ||
2470 GreatOrEqual(computedOffset.GetY(), yMaxAvoid);
2471 bool hasEnoughSpace = GreatOrEqual(bottomSpace, selectMenuHeight);
2472 if (!isXOut && !isYOut && hasEnoughSpace) {
2473 x = computedOffset.GetX();
2474 y = computedOffset.GetY();
2475 return { x, y };
2476 }
2477 }
2478 x = std::clamp(x, xMinAvoid, xMaxAvoid);
2479 y = std::clamp(y, yMinAvoid, yMaxAvoid);
2480 return { x, y };
2481 }
2482
PlacementRTL(LayoutWrapper * layoutWrapper,Placement & placement_)2483 void MenuLayoutAlgorithm::PlacementRTL(LayoutWrapper* layoutWrapper, Placement& placement_)
2484 {
2485 auto menuLayoutProperty = layoutWrapper->GetLayoutProperty();
2486 CHECK_NULL_VOID(menuLayoutProperty);
2487 auto layoutDirection = menuLayoutProperty->GetNonAutoLayoutDirection();
2488 if (layoutDirection == TextDirection::RTL) {
2489 switch (placement_) {
2490 case Placement::LEFT:
2491 placement_ = Placement::RIGHT;
2492 break;
2493 case Placement::RIGHT:
2494 placement_ = Placement::LEFT;
2495 break;
2496 case Placement::TOP_LEFT:
2497 placement_ = Placement::TOP_RIGHT;
2498 break;
2499 case Placement::TOP_RIGHT:
2500 placement_ = Placement::TOP_LEFT;
2501 break;
2502 case Placement::BOTTOM_LEFT:
2503 placement_ = Placement::BOTTOM_RIGHT;
2504 break;
2505 case Placement::BOTTOM_RIGHT:
2506 placement_ = Placement::BOTTOM_LEFT;
2507 break;
2508 case Placement::LEFT_TOP:
2509 placement_ = Placement::RIGHT_TOP;
2510 break;
2511 case Placement::RIGHT_TOP:
2512 placement_ = Placement::LEFT_TOP;
2513 break;
2514 case Placement::LEFT_BOTTOM:
2515 placement_ = Placement::RIGHT_BOTTOM;
2516 break;
2517 case Placement::RIGHT_BOTTOM:
2518 placement_ = Placement::LEFT_BOTTOM;
2519 break;
2520 default:
2521 break;
2522 }
2523 }
2524 }
2525
LimitContainerModalMenuRect(double & rectWidth,double & rectHeight,const RefPtr<MenuPattern> & menuPattern)2526 void MenuLayoutAlgorithm::LimitContainerModalMenuRect(
2527 double& rectWidth, double& rectHeight, const RefPtr<MenuPattern>& menuPattern)
2528 {
2529 auto containerOffsetX = static_cast<float>(CONTAINER_BORDER_WIDTH.ConvertToPx()) +
2530 static_cast<float>(CONTAINER_BORDER_WIDTH.ConvertToPx());
2531 CHECK_NULL_VOID(menuPattern);
2532 auto host = menuPattern->GetHost();
2533 CHECK_NULL_VOID(host);
2534 auto pipeline = DialogManager::GetMainPipelineContext(host);
2535 CHECK_NULL_VOID(pipeline);
2536 auto containerOffsetY = static_cast<float>(pipeline->GetCustomTitleHeight().ConvertToPx()) +
2537 static_cast<float>(CONTAINER_BORDER_WIDTH.ConvertToPx()) +
2538 static_cast<float>(CONTAINER_BORDER_WIDTH.ConvertToPx());
2539 rectWidth -= containerOffsetX;
2540 rectHeight -= containerOffsetY;
2541 }
2542
UpdateConstraintWidth(LayoutWrapper * layoutWrapper,LayoutConstraintF & constraint)2543 void MenuLayoutAlgorithm::UpdateConstraintWidth(LayoutWrapper* layoutWrapper, LayoutConstraintF& constraint)
2544 {
2545 RefPtr<GridColumnInfo> columnInfo;
2546 columnInfo = GridSystemManager::GetInstance().GetInfoByType(GridColumnType::MENU);
2547 auto menuNode = layoutWrapper->GetHostNode();
2548 CHECK_NULL_VOID(menuNode);
2549 auto menuPattern = menuNode->GetPattern<MenuPattern>();
2550 CHECK_NULL_VOID(menuPattern);
2551 columnInfo->GetParent()->BuildColumnWidth(wrapperSize_.Width());
2552 auto menuLayoutProperty = AceType::DynamicCast<MenuLayoutProperty>(layoutWrapper->GetLayoutProperty());
2553 CHECK_NULL_VOID(menuLayoutProperty);
2554 // set max width
2555 const auto& padding = menuLayoutProperty->CreatePaddingAndBorder();
2556 auto maxHorizontalSpace = std::max(leftSpace_, rightSpace_) - 2.0f * padding.Width();
2557 auto maxWidth = static_cast<float>(columnInfo->GetWidth(GetMaxGridCounts(columnInfo)));
2558 if (Container::LessThanAPIVersion(PlatformVersion::VERSION_TEN)) {
2559 maxWidth = std::min(maxHorizontalSpace, maxWidth);
2560 }
2561 maxWidth = std::min(constraint.maxSize.Width(), maxWidth);
2562 constraint.maxSize.SetWidth(maxWidth);
2563 }
2564
UpdateConstraintHeight(LayoutWrapper * layoutWrapper,LayoutConstraintF & constraint)2565 void MenuLayoutAlgorithm::UpdateConstraintHeight(LayoutWrapper* layoutWrapper, LayoutConstraintF& constraint)
2566 {
2567 CHECK_NULL_VOID(layoutWrapper);
2568 auto hostNode = layoutWrapper->GetHostNode();
2569 CHECK_NULL_VOID(hostNode);
2570 auto pipelineContext = DialogManager::GetMainPipelineContext(hostNode);
2571 CHECK_NULL_VOID(pipelineContext);
2572 auto menuPattern = layoutWrapper->GetHostNode()->GetPattern<MenuPattern>();
2573 CHECK_NULL_VOID(menuPattern);
2574
2575 float maxAvailableHeight = wrapperRect_.Height();
2576 float maxSpaceHeight = maxAvailableHeight * HEIGHT_CONSTRAINT_FACTOR;
2577 if (lastPosition_.has_value() && holdEmbeddedMenuPosition_) {
2578 auto spaceToBottom = GetMenuMaxBottom(menuPattern) - lastPosition_.value().GetY();
2579 maxSpaceHeight = std::min(maxSpaceHeight, spaceToBottom);
2580 }
2581 if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN)) {
2582 if (menuPattern->IsHeightModifiedBySelect()) {
2583 auto menuLayoutProps = AceType::DynamicCast<MenuLayoutProperty>(layoutWrapper->GetLayoutProperty());
2584 auto selectModifiedHeight = menuLayoutProps->GetSelectModifiedHeight().value_or(0.0f);
2585 if (LessNotEqual(selectModifiedHeight, maxSpaceHeight)) {
2586 maxSpaceHeight = selectModifiedHeight;
2587 }
2588 }
2589 }
2590 constraint.maxSize.SetHeight(maxSpaceHeight);
2591 }
2592
UpdateConstraintSelectHeight(LayoutWrapper * layoutWrapper,LayoutConstraintF & constraint)2593 void MenuLayoutAlgorithm::UpdateConstraintSelectHeight(LayoutWrapper* layoutWrapper, LayoutConstraintF& constraint)
2594 {
2595 CHECK_NULL_VOID(layoutWrapper);
2596 auto hostNode = layoutWrapper->GetHostNode();
2597 CHECK_NULL_VOID(hostNode);
2598 auto pipelineContext = DialogManager::GetMainPipelineContext(hostNode);
2599 CHECK_NULL_VOID(pipelineContext);
2600 auto menuPattern = layoutWrapper->GetHostNode()->GetPattern<MenuPattern>();
2601 CHECK_NULL_VOID(menuPattern);
2602
2603 float maxAvailableHeight = wrapperRect_.Height();
2604 float maxSpaceHeight = maxAvailableHeight * HEIGHT_CONSTRAINT_FACTOR;
2605 auto layoutProp = layoutWrapper->GetLayoutProperty();
2606 CHECK_NULL_VOID(layoutProp);
2607 auto menuLayoutProps = AceType::DynamicCast<MenuLayoutProperty>(layoutProp);
2608 CHECK_NULL_VOID(menuLayoutProps);
2609 auto geometryNode = layoutWrapper->GetGeometryNode();
2610 CHECK_NULL_VOID(geometryNode);
2611 float selectMenuWidth = geometryNode->GetFrameSize().Width();
2612 float xAvoid = wrapperRect_.Left() + paddingStart_;
2613 float yAvoid = wrapperRect_.Top() + paddingTop_ + param_.topSecurity;
2614 float maxWidth = wrapperSize_.Width() - paddingEnd_ - paddingStart_;
2615 Rect targetRect = Rect(targetOffset_.GetX(), targetOffset_.GetY(), targetSize_.Width(), targetSize_.Height());
2616 auto bottomSpace = std::min<float>(wrapperRect_.Bottom() - targetRect.Bottom() -
2617 targetSecurity_ - paddingBottom_, maxSpaceHeight);
2618 auto topSpace = std::min<float>(targetRect.Top() - yAvoid - targetSecurity_, maxSpaceHeight);
2619 auto rightSpace =
2620 std::min<float>(wrapperRect_.Right() - targetRect.Right() - targetSecurity_ - paddingEnd_, maxWidth);
2621 auto leftSpace = std::min<float>(targetRect.Left() - xAvoid - targetSecurity_, maxWidth);
2622 if (GreatOrEqual(selectMenuWidth, rightSpace) && GreatOrEqual(selectMenuWidth, leftSpace)) {
2623 maxSpaceHeight = std::max(topSpace, bottomSpace);
2624 }
2625 if (lastPosition_.has_value() && holdEmbeddedMenuPosition_) {
2626 auto spaceToBottom = GetMenuMaxBottom(menuPattern) - lastPosition_.value().GetY();
2627 maxSpaceHeight = std::min(maxSpaceHeight, spaceToBottom);
2628 }
2629 if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN)) {
2630 if (menuPattern->IsHeightModifiedBySelect()) {
2631 auto menuLayoutProps = AceType::DynamicCast<MenuLayoutProperty>(layoutWrapper->GetLayoutProperty());
2632 auto selectModifiedHeight = menuLayoutProps->GetSelectModifiedHeight().value_or(0.0f);
2633 if (LessNotEqual(selectModifiedHeight, maxSpaceHeight)) {
2634 maxSpaceHeight = selectModifiedHeight;
2635 }
2636 }
2637 }
2638 constraint.maxSize.SetHeight(maxSpaceHeight);
2639 }
2640
GetMenuMaxBottom(const RefPtr<MenuPattern> & menuPattern)2641 float MenuLayoutAlgorithm::GetMenuMaxBottom(const RefPtr<MenuPattern>& menuPattern)
2642 {
2643 auto bottom = wrapperRect_.Bottom();
2644 CHECK_NULL_RETURN(menuPattern, bottom);
2645 if (menuPattern->GetPreviewMode() != MenuPreviewMode::NONE) {
2646 auto placement = menuPattern->GetLastPlacement().value_or(Placement::NONE);
2647 auto isTopMenuBottomPreview =
2648 placement == Placement::TOP || placement == Placement::TOP_LEFT || placement == Placement::TOP_RIGHT;
2649 auto previewTopSpace = bottom - previewSize_.Height() * previewScale_ - targetSecurity_;
2650 return std::max<float>(0.0f, (isTopMenuBottomPreview ? previewTopSpace : bottom) - param_.bottomSecurity);
2651 }
2652
2653 return bottom;
2654 }
2655
CreateChildConstraint(LayoutWrapper * layoutWrapper)2656 LayoutConstraintF MenuLayoutAlgorithm::CreateChildConstraint(LayoutWrapper* layoutWrapper)
2657 {
2658 auto menuLayoutProperty = AceType::DynamicCast<MenuLayoutProperty>(layoutWrapper->GetLayoutProperty());
2659 CHECK_NULL_RETURN(menuLayoutProperty, LayoutConstraintF());
2660 auto menuNode = layoutWrapper->GetHostNode();
2661 CHECK_NULL_RETURN(menuNode, LayoutConstraintF());
2662 auto menuPattern = menuNode->GetPattern<MenuPattern>();
2663 CHECK_NULL_RETURN(menuPattern, LayoutConstraintF());
2664
2665 auto avoidanceMode = menuLayoutProperty->GetSelectAvoidanceMode().value_or(AvoidanceMode::COVER_TARGET);
2666 auto childConstraint = menuLayoutProperty->CreateChildConstraint();
2667 UpdateConstraintWidth(layoutWrapper, childConstraint);
2668 UpdateConstraintBaseOnOptions(layoutWrapper, childConstraint);
2669 if (menuPattern->IsSelectMenu() && avoidanceMode == AvoidanceMode::AVOID_AROUND_TARGET &&
2670 !IsSelectMenuShowInSubWindow(layoutWrapper, menuNode)) {
2671 UpdateConstraintSelectHeight(layoutWrapper, childConstraint);
2672 } else {
2673 UpdateConstraintHeight(layoutWrapper, childConstraint);
2674 }
2675 return childConstraint;
2676 }
2677
UpdateConstraintBaseOnOptions(LayoutWrapper * layoutWrapper,LayoutConstraintF & constraint)2678 void MenuLayoutAlgorithm::UpdateConstraintBaseOnOptions(LayoutWrapper* layoutWrapper, LayoutConstraintF& constraint)
2679 {
2680 auto menuNode = layoutWrapper->GetHostNode();
2681 CHECK_NULL_VOID(menuNode);
2682 auto menuPattern = menuNode->GetPattern<MenuPattern>();
2683 CHECK_NULL_VOID(menuPattern);
2684 auto options = menuPattern->GetOptions();
2685 if (options.empty()) {
2686 return;
2687 }
2688 auto optionConstraint = constraint;
2689 RefPtr<GridColumnInfo> columnInfo;
2690 columnInfo = GridSystemManager::GetInstance().GetInfoByType(GridColumnType::MENU);
2691 if (!UpdateSelectOverlayMenuColumnInfo(menuPattern, columnInfo)) {
2692 columnInfo->GetParent()->BuildColumnWidth(wrapperSize_.Width());
2693 }
2694 auto minWidth = static_cast<float>(columnInfo->GetWidth(MIN_GRID_COUNTS));
2695 optionConstraint.maxSize.MinusWidth(optionPadding_ * 2.0f);
2696 auto props = AceType::DynamicCast<MenuLayoutProperty>(layoutWrapper->GetLayoutProperty());
2697 if (menuPattern->IsSelectOverlayExtensionMenu() && props && props->HasMenuWidth()) {
2698 minWidth = props->GetMenuWidth()->ConvertToPx();
2699 }
2700 optionConstraint.minSize.SetWidth(minWidth - optionPadding_ * 2.0f);
2701 auto maxChildrenWidth = optionConstraint.minSize.Width();
2702 auto optionsLayoutWrapper = GetOptionsLayoutWrappper(layoutWrapper);
2703 for (const auto& optionWrapper : optionsLayoutWrapper) {
2704 optionWrapper->GetLayoutProperty()->ResetCalcMinSize();
2705 optionWrapper->Measure(optionConstraint);
2706 auto childSize = optionWrapper->GetGeometryNode()->GetMarginFrameSize();
2707 maxChildrenWidth = std::max(maxChildrenWidth, childSize.Width());
2708 }
2709 UpdateOptionConstraint(optionsLayoutWrapper, maxChildrenWidth);
2710 auto pipelineContext = menuNode->GetContext();
2711 CHECK_NULL_VOID(pipelineContext);
2712 auto selectTheme = pipelineContext->GetTheme<SelectTheme>();
2713 CHECK_NULL_VOID(selectTheme);
2714 auto textAlign = static_cast<TextAlign>(selectTheme->GetOptionContentNormalAlign());
2715 if (textAlign == TextAlign::CENTER) {
2716 for (const auto& optionWrapper : optionsLayoutWrapper) {
2717 optionWrapper->Measure(optionConstraint);
2718 }
2719 }
2720 constraint.minSize.SetWidth(maxChildrenWidth + optionPadding_ * 2.0f);
2721 }
2722
GetOptionsLayoutWrappper(LayoutWrapper * layoutWrapper)2723 std::list<RefPtr<LayoutWrapper>> MenuLayoutAlgorithm::GetOptionsLayoutWrappper(LayoutWrapper* layoutWrapper)
2724 {
2725 std::list<RefPtr<LayoutWrapper>> optionsWrapper;
2726 auto scrollWrapper = layoutWrapper->GetOrCreateChildByIndex(0);
2727 CHECK_NULL_RETURN(scrollWrapper, optionsWrapper);
2728 auto columnWrapper = scrollWrapper->GetOrCreateChildByIndex(0);
2729 CHECK_NULL_RETURN(columnWrapper, optionsWrapper);
2730 optionsWrapper = columnWrapper->GetAllChildrenWithBuild();
2731 return optionsWrapper;
2732 }
2733
UpdateOptionConstraint(std::list<RefPtr<LayoutWrapper>> & options,float width)2734 void MenuLayoutAlgorithm::UpdateOptionConstraint(std::list<RefPtr<LayoutWrapper>>& options, float width)
2735 {
2736 for (const auto& option : options) {
2737 auto optionLayoutProps = option->GetLayoutProperty();
2738 CHECK_NULL_VOID(optionLayoutProps);
2739 optionLayoutProps->UpdateCalcMinSize(CalcSize(CalcLength(width), std::nullopt));
2740 }
2741 }
2742
2743 // return vertical offset
VerticalLayout(const SizeF & size,float position,bool isContextMenu)2744 float MenuLayoutAlgorithm::VerticalLayout(const SizeF& size, float position, bool isContextMenu)
2745 {
2746 placement_ = Placement::BOTTOM;
2747 // can put menu below click point
2748 if (GreatOrEqual(bottomSpace_, size.Height())) {
2749 return position + margin_;
2750 }
2751 if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN) && isContextMenu) {
2752 if (LessNotEqual(bottomSpace_, size.Height()) && LessNotEqual(size.Height(), wrapperRect_.Height())) {
2753 return wrapperRect_.Bottom() - size.Height() - paddingBottom_;
2754 }
2755 // can't fit in screen, line up with top of the screen
2756 return wrapperRect_.Top() + paddingTop_;
2757 } else if (anchorPosition_.has_value()) {
2758 // When the component height is less than the bottom margin and the menu height can be lowered,
2759 // or when the anchor point y coordinate is at the bottom of the screen and
2760 // the anchorPosition_ has a set value, the menu should be placed at the bottom of the screen.
2761 bool isOutBottom = GreatOrEqual(position + anchorPosition_->GetY() + size.Height(), wrapperRect_.Bottom());
2762 if (isOutBottom) {
2763 if (((LessNotEqual(bottomSpace_, size.Height()) || GreatOrEqual(position, wrapperRect_.Bottom())) &&
2764 LessNotEqual(size.Height(), wrapperRect_.Height()))) {
2765 return wrapperRect_.Bottom() - size.Height() - paddingBottom_;
2766 }
2767 // can't fit in screen, line up with top of the screen
2768 return wrapperRect_.Top() + paddingTop_;
2769 } else {
2770 return CalcVerticalPosition(size);
2771 }
2772 } else {
2773 return CalcVerticalPosition(size);
2774 }
2775 }
2776
CalcVerticalPosition(const SizeF & size)2777 float MenuLayoutAlgorithm::CalcVerticalPosition(const SizeF& size)
2778 {
2779 float wrapperHeight = wrapperSize_.Height();
2780 // put menu above click point
2781 if (GreatOrEqual(topSpace_, size.Height())) {
2782 // menu show on top
2783 placement_ = Placement::TOP;
2784 return topSpace_ - size.Height() + margin_;
2785 }
2786 // line up bottom of menu with bottom of the screen
2787 if (LessNotEqual(size.Height(), wrapperHeight)) {
2788 return wrapperHeight - size.Height();
2789 }
2790 // can't fit in screen, line up with top of the screen
2791 return 0.0f;
2792 }
2793
2794 // returns horizontal offset
HorizontalLayout(const SizeF & size,float position,bool isSelectMenu)2795 float MenuLayoutAlgorithm::HorizontalLayout(const SizeF& size, float position, bool isSelectMenu)
2796 {
2797 float wrapperWidth = wrapperSize_.Width();
2798 // can fit menu on the right side of position
2799 if (GreatOrEqual(rightSpace_, size.Width())) {
2800 return position + margin_;
2801 }
2802
2803 // fit menu on the left side
2804 if (!isSelectMenu && GreatOrEqual(leftSpace_, size.Width())) {
2805 return position - size.Width();
2806 }
2807
2808 // line up right side of menu with right boundary of the screen
2809 if (LessNotEqual(size.Width(), wrapperWidth)) {
2810 if (isSelectMenu) {
2811 return position + margin_;
2812 }
2813 return wrapperWidth - size.Width();
2814 }
2815
2816 // can't fit in screen, line up with left side of the screen
2817 return 0.0f;
2818 }
2819
GetPositionWithPlacement(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)2820 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacement(
2821 const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
2822 {
2823 OffsetF childPosition;
2824
2825 auto func = placementFuncMap_.find(placement_);
2826 if (func != placementFuncMap_.end()) {
2827 auto placementFunc = func->second;
2828 if (placementFunc != nullptr) {
2829 childPosition = (this->*placementFunc)(childSize, topPosition, bottomPosition);
2830 }
2831 }
2832 return childPosition;
2833 }
2834
GetArrowPositionWithPlacement(const SizeF & menuSize,const LayoutWrapper * layoutWrapper)2835 OffsetF MenuLayoutAlgorithm::GetArrowPositionWithPlacement(const SizeF& menuSize, const LayoutWrapper* layoutWrapper)
2836 {
2837 UpdateArrowOffsetWithMenuLimit(menuSize, layoutWrapper);
2838 auto addArrowOffsetToArrowMin = arrowOffset_ + arrowMinLimit_;
2839 auto space_ = ARROW_HIGHT.ConvertToPx();
2840 OffsetF childPosition;
2841 switch (arrowPlacement_) {
2842 case Placement::TOP:
2843 case Placement::TOP_LEFT:
2844 case Placement::TOP_RIGHT:
2845 childPosition = OffsetF(addArrowOffsetToArrowMin, menuSize.Height() + space_);
2846 break;
2847 case Placement::BOTTOM:
2848 case Placement::BOTTOM_LEFT:
2849 case Placement::BOTTOM_RIGHT:
2850 childPosition = OffsetF(addArrowOffsetToArrowMin, 0);
2851 break;
2852 case Placement::LEFT:
2853 case Placement::LEFT_TOP:
2854 case Placement::LEFT_BOTTOM:
2855 childPosition = OffsetF(menuSize.Width() + space_, addArrowOffsetToArrowMin);
2856 break;
2857 case Placement::RIGHT:
2858 case Placement::RIGHT_TOP:
2859 case Placement::RIGHT_BOTTOM:
2860 childPosition = OffsetF(0, addArrowOffsetToArrowMin);
2861 break;
2862 default:
2863 break;
2864 }
2865 return childPosition;
2866 }
2867
GetMenuWrapperOffset(const LayoutWrapper * layoutWrapper)2868 OffsetF MenuLayoutAlgorithm::GetMenuWrapperOffset(const LayoutWrapper* layoutWrapper)
2869 {
2870 CHECK_NULL_RETURN(layoutWrapper, OffsetF());
2871 auto menuNode = layoutWrapper->GetHostNode();
2872 CHECK_NULL_RETURN(menuNode, OffsetF());
2873 auto menuLayoutProperty = layoutWrapper->GetLayoutProperty();
2874 if (menuLayoutProperty && menuLayoutProperty->GetNonAutoLayoutDirection() == TextDirection::RTL) {
2875 return menuNode->GetPaintRectOffset(true, true);
2876 }
2877 return menuNode->GetParentGlobalOffsetDuringLayout();
2878 }
2879
NeedHoldTargetOffset(const RefPtr<FrameNode> & targetNode,const RefPtr<MenuPattern> & menuPattern)2880 bool MenuLayoutAlgorithm::NeedHoldTargetOffset(
2881 const RefPtr<FrameNode>& targetNode, const RefPtr<MenuPattern>& menuPattern)
2882 {
2883 CHECK_NULL_RETURN(menuPattern, false);
2884 if (!menuPattern->GetTargetSize().IsPositive()) {
2885 return false;
2886 }
2887 auto menuWrapper = menuPattern->GetMenuWrapper();
2888 CHECK_NULL_RETURN(menuWrapper, false);
2889 auto menuWrapperPattern = menuWrapper->GetPattern<MenuWrapperPattern>();
2890 CHECK_NULL_RETURN(menuWrapperPattern, false);
2891 if (menuWrapperPattern->IsHide()) {
2892 TAG_LOGI(AceLogTag::ACE_MENU, "offset hold by menu hide");
2893 return true;
2894 }
2895 if (!targetNode) {
2896 TAG_LOGI(AceLogTag::ACE_MENU, "offset hold by target not exist");
2897 return true;
2898 }
2899 auto targetRenderContext = targetNode->GetRenderContext();
2900 CHECK_NULL_RETURN(targetRenderContext, false);
2901 auto targetOpacity = targetRenderContext->GetOpacityValue(0.0);
2902 if (NearZero(targetOpacity)) {
2903 TAG_LOGI(AceLogTag::ACE_MENU, "offset hold by target opacity");
2904 return true;
2905 }
2906 auto targetVisible = targetNode->IsVisible();
2907 if (!targetVisible) {
2908 TAG_LOGI(AceLogTag::ACE_MENU, "offset hold by target unvisible");
2909 return true;
2910 }
2911 auto targetSize = targetNode->GetPaintRectWithTransform().GetSize();
2912 if (!targetSize.IsPositive()) {
2913 TAG_LOGI(AceLogTag::ACE_MENU, "offset hold by target size");
2914 return true;
2915 }
2916 return false;
2917 }
2918
SkipUpdateTargetNodeSize(const RefPtr<FrameNode> & targetNode,const RefPtr<MenuPattern> & menuPattern)2919 bool MenuLayoutAlgorithm::SkipUpdateTargetNodeSize(
2920 const RefPtr<FrameNode>& targetNode, const RefPtr<MenuPattern>& menuPattern)
2921 {
2922 CHECK_NULL_RETURN(menuPattern, false);
2923 if (NeedHoldTargetOffset(targetNode, menuPattern)) {
2924 targetSize_ = menuPattern->GetTargetSize();
2925 targetOffset_ = menuPattern->GetTargetOffset();
2926 return true;
2927 }
2928 return false;
2929 }
2930
InitTargetSizeAndPosition(const LayoutWrapper * layoutWrapper,bool isContextMenu,const RefPtr<MenuPattern> & menuPattern)2931 void MenuLayoutAlgorithm::InitTargetSizeAndPosition(
2932 const LayoutWrapper* layoutWrapper, bool isContextMenu, const RefPtr<MenuPattern>& menuPattern)
2933 {
2934 CHECK_NULL_VOID(layoutWrapper && menuPattern);
2935 auto targetNode = FrameNode::GetFrameNode(targetTag_, targetNodeId_);
2936 bool holdTargetOffset = SkipUpdateTargetNodeSize(targetNode, menuPattern);
2937 if (!holdTargetOffset) {
2938 CHECK_NULL_VOID(targetNode);
2939 dumpInfo_.targetNode = targetNode->GetTag();
2940 auto props = AceType::DynamicCast<MenuLayoutProperty>(layoutWrapper->GetLayoutProperty());
2941 CHECK_NULL_VOID(props);
2942 if (props->GetIsRectInTargetValue(false)) {
2943 targetSize_ = props->GetTargetSizeValue(SizeF());
2944 targetOffset_ = props->GetMenuOffsetValue(OffsetF());
2945 } else {
2946 targetSize_ = targetNode->GetPaintRectWithTransform().GetSize();
2947 targetOffset_ = targetNode->GetPaintRectOffset(false, true);
2948 }
2949 }
2950 dumpInfo_.targetSize = targetSize_;
2951 dumpInfo_.targetOffset = targetOffset_;
2952 menuPattern->SetTargetSize(targetSize_);
2953 menuPattern->SetTargetOffset(targetOffset_);
2954 TAG_LOGI(AceLogTag::ACE_MENU,
2955 "targetId: %{public}d targetNode: %{public}s, targetSize: %{public}s, targetOffset: %{public}s", targetNodeId_,
2956 targetTag_.c_str(), targetSize_.ToString().c_str(), targetOffset_.ToString().c_str());
2957 auto hostNode = layoutWrapper->GetHostNode();
2958 CHECK_NULL_VOID(hostNode);
2959 auto pipelineContext = DialogManager::GetMainPipelineContext(hostNode);
2960 CHECK_NULL_VOID(pipelineContext);
2961 if (canExpandCurrentWindow_ && targetTag_ != V2::SELECT_ETS_TAG) {
2962 if (!holdTargetOffset) {
2963 ModifyTargetOffset();
2964 menuPattern->SetTargetOffset(targetOffset_);
2965 }
2966 OffsetF offset = GetMenuWrapperOffset(layoutWrapper);
2967 targetOffset_ -= offset;
2968 return;
2969 }
2970
2971 auto windowManager = pipelineContext->GetWindowManager();
2972 auto isContainerModal = pipelineContext->GetWindowModal() == WindowModal::CONTAINER_MODAL && windowManager &&
2973 windowManager->GetWindowMode() == WindowMode::WINDOW_MODE_FLOATING;
2974 if (isContainerModal) {
2975 auto newOffsetX = static_cast<float>(CONTAINER_BORDER_WIDTH.ConvertToPx());
2976 auto newOffsetY = static_cast<float>(pipelineContext->GetCustomTitleHeight().ConvertToPx()) +
2977 static_cast<float>(CONTAINER_BORDER_WIDTH.ConvertToPx());
2978 targetOffset_ -= OffsetF(newOffsetX, newOffsetY);
2979 } else {
2980 OffsetF offset = GetMenuWrapperOffset(layoutWrapper);
2981 targetOffset_ -= offset;
2982 }
2983 }
2984
FitToScreen(const OffsetF & position,const SizeF & childSize,bool didNeedArrow)2985 OffsetF MenuLayoutAlgorithm::FitToScreen(const OffsetF& position, const SizeF& childSize, bool didNeedArrow)
2986 {
2987 OffsetF afterOffsetPosition;
2988 auto originPosition = position;
2989
2990 // add space between targetNode and menu using default value or user-set offset
2991 if (NearEqual(positionOffset_, OffsetF(0.0f, 0.0f))) {
2992 afterOffsetPosition = AddTargetSpace(originPosition);
2993 } else {
2994 afterOffsetPosition = AddOffset(originPosition);
2995 }
2996
2997 if (!CheckPosition(afterOffsetPosition, childSize) || flag_) {
2998 flag_ = false;
2999 return OffsetF(0.0f, 0.0f);
3000 }
3001
3002 return afterOffsetPosition;
3003 }
3004
GetChildPosition(const SizeF & childSize,bool didNeedArrow)3005 OffsetF MenuLayoutAlgorithm::GetChildPosition(const SizeF& childSize, bool didNeedArrow)
3006 {
3007 // add space between arrow and targetNode
3008 OffsetF bottomPosition = OffsetF(targetOffset_.GetX() + (targetSize_.Width() - childSize.Width()) / 2.0,
3009 targetOffset_.GetY() + targetSize_.Height() + targetSpace_);
3010 OffsetF topPosition = OffsetF(targetOffset_.GetX() + (targetSize_.Width() - childSize.Width()) / 2.0,
3011 targetOffset_.GetY() - childSize.Height() - targetSpace_);
3012 // when failed to find place for menu, put menu above targetNode with center-aligned
3013 OffsetF defaultPosition = OffsetF(targetOffset_.GetX() + (targetSize_.Width() - childSize.Width()) / 2.0,
3014 targetOffset_.GetY() + (targetSize_.Height() - childSize.Height()) / 2.0);
3015
3016 OffsetF childPosition;
3017 OffsetF position = defaultPosition;
3018 auto positionOffset = positionOffset_;
3019 std::vector<Placement> currentPlacementStates = PLACEMENT_STATES.find(Placement::BOTTOM_LEFT)->second;
3020 auto it = PLACEMENT_STATES.find(placement_);
3021 if (it != PLACEMENT_STATES.end()) {
3022 currentPlacementStates = it->second;
3023 }
3024 size_t step = ALIGNMENT_STEP_OFFSET;
3025 if (placement_ <= Placement::BOTTOM) {
3026 step += 1;
3027 }
3028 for (size_t i = 0, len = currentPlacementStates.size(); i < len; i++) {
3029 placement_ = currentPlacementStates[i];
3030 if (placement_ == Placement::NONE) {
3031 break;
3032 }
3033 if (i >= step) {
3034 positionOffset_ = OffsetF(0.0f, 0.0f);
3035 }
3036 childPosition = GetPositionWithPlacement(childSize, topPosition, bottomPosition);
3037 position = FitToScreen(childPosition, childSize, didNeedArrow);
3038 if (NearEqual(position, OffsetF(0.0f, 0.0f))) {
3039 continue;
3040 }
3041 break;
3042 }
3043 if (placement_ == Placement::NONE) {
3044 position = GetAdjustPosition(currentPlacementStates, step, childSize, topPosition, bottomPosition);
3045 if (NearEqual(position, OffsetF(0.0f, 0.0f))) {
3046 position = defaultPosition;
3047 }
3048 }
3049 positionOffset_ = positionOffset;
3050 arrowPlacement_ = placement_;
3051
3052 return position;
3053 }
3054
GetSelectChildPosition(const SizeF & childSize,bool didNeedArrow,LayoutWrapper * layoutWrapper)3055 OffsetF MenuLayoutAlgorithm::GetSelectChildPosition(const SizeF& childSize, bool didNeedArrow,
3056 LayoutWrapper* layoutWrapper)
3057 {
3058 OffsetF bottomPosition = OffsetF(targetOffset_.GetX() + (targetSize_.Width() - childSize.Width()) / HALF,
3059 targetOffset_.GetY() + targetSize_.Height() + targetSpace_);
3060 OffsetF topPosition = OffsetF(targetOffset_.GetX() + (targetSize_.Width() - childSize.Width()) / HALF,
3061 targetOffset_.GetY() - childSize.Height() - targetSpace_);
3062
3063 OffsetF childPosition;
3064 OffsetF position;
3065 std::vector<Placement> currentPlacementStates = SELECT_PLACEMENT_STATES.find(Placement::BOTTOM_LEFT)->second;
3066 auto it = SELECT_PLACEMENT_STATES.find(placement_);
3067 if (it != SELECT_PLACEMENT_STATES.end()) {
3068 currentPlacementStates = it->second;
3069 }
3070 for (size_t i = 0, len = currentPlacementStates.size(); i < len; i++) {
3071 placement_ = currentPlacementStates[i];
3072 if (placement_ == Placement::NONE) {
3073 break;
3074 }
3075 childPosition = GetPositionWithPlacement(childSize, topPosition, bottomPosition);
3076 position = FitToScreen(childPosition, childSize, didNeedArrow);
3077 if (NearEqual(position, OffsetF(0.0f, 0.0f))) {
3078 continue;
3079 }
3080 break;
3081 }
3082 if (placement_ == Placement::NONE) {
3083 position = GetSelectAdjustPosition(currentPlacementStates, childSize, topPosition, bottomPosition);
3084 }
3085 return position;
3086 }
3087
GetAdjustPosition(std::vector<Placement> & currentPlacementStates,size_t step,const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)3088 OffsetF MenuLayoutAlgorithm::GetAdjustPosition(std::vector<Placement>& currentPlacementStates, size_t step,
3089 const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
3090 {
3091 OffsetF childPosition;
3092 OffsetF position;
3093 size_t len = currentPlacementStates.size();
3094 for (size_t i = 0; i < len;) {
3095 placement_ = currentPlacementStates[i];
3096 if (placement_ == Placement::NONE) {
3097 break;
3098 }
3099 childPosition = GetPositionWithPlacement(childSize, topPosition, bottomPosition);
3100 position = AdjustPosition(childPosition, childSize.Width(), childSize.Height(), targetSecurity_);
3101 if (!NearEqual(position, OffsetF(0.0f, 0.0f))) {
3102 break;
3103 }
3104 if (len - i <= step) {
3105 break;
3106 }
3107 i += step;
3108 }
3109 return position;
3110 }
3111
GetSelectAdjustPosition(std::vector<Placement> & currentPlacementStates,const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)3112 OffsetF MenuLayoutAlgorithm::GetSelectAdjustPosition(std::vector<Placement>& currentPlacementStates,
3113 const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
3114 {
3115 OffsetF childPosition;
3116 OffsetF position;
3117 Placement firstPlacement = currentPlacementStates[0];
3118 size_t step = firstPlacement == Placement::BOTTOM ? ALIGNMENT_STEP_OFFSET + 1 : ALIGNMENT_STEP_OFFSET;
3119
3120 for (size_t i = 0, len = currentPlacementStates.size(); i < len;) {
3121 placement_ = currentPlacementStates[i];
3122 if (placement_ == Placement::NONE) {
3123 break;
3124 }
3125 if (firstPlacement == Placement::BOTTOM && placement_ == Placement::RIGHT_TOP) {
3126 step = ALIGNMENT_STEP_OFFSET;
3127 }
3128 childPosition = GetPositionWithPlacement(childSize, topPosition, bottomPosition);
3129 position = AdjustPosition(childPosition, childSize.Width(), childSize.Height(), targetSecurity_);
3130 if (NearEqual(position, OffsetF(0.0f, 0.0f))) {
3131 i += step;
3132 continue;
3133 }
3134 break;
3135 }
3136 return position;
3137 }
3138
AdjustPosition(const OffsetF & position,float width,float height,float space)3139 OffsetF MenuLayoutAlgorithm::AdjustPosition(const OffsetF& position, float width, float height, float space)
3140 {
3141 float xMax = 0.0f;
3142 float yMax = 0.0f;
3143 float xMin = wrapperRect_.Left() + paddingStart_;
3144 float yMin = std::max(1.0f, static_cast<float>(wrapperRect_.Top()) + paddingTop_);
3145 float wrapperRight = wrapperRect_.Right();
3146 float wrapperBottom = wrapperRect_.Bottom();
3147 switch (placement_) {
3148 case Placement::LEFT_TOP:
3149 case Placement::LEFT_BOTTOM:
3150 case Placement::LEFT: {
3151 xMax = std::min(targetOffset_.GetX() - width - space, wrapperRight - paddingEnd_ - width);
3152 yMax = wrapperBottom - height - paddingBottom_;
3153 break;
3154 }
3155 case Placement::RIGHT_TOP:
3156 case Placement::RIGHT_BOTTOM:
3157 case Placement::RIGHT: {
3158 xMin = std::max(targetOffset_.GetX() + targetSize_.Width() + space, xMin);
3159 xMax = wrapperRight - width - paddingEnd_;
3160 yMax = wrapperBottom - height - paddingBottom_;
3161 break;
3162 }
3163 case Placement::TOP_LEFT:
3164 case Placement::TOP_RIGHT:
3165 case Placement::TOP: {
3166 xMax = wrapperRight - width - paddingEnd_;
3167 yMax = std::min(targetOffset_.GetY() - height - space, wrapperBottom - paddingBottom_ - height);
3168 break;
3169 }
3170 case Placement::BOTTOM_LEFT:
3171 case Placement::BOTTOM_RIGHT:
3172 case Placement::BOTTOM: {
3173 xMax = wrapperRight - width - paddingEnd_;
3174 yMin = std::max(targetOffset_.GetY() + targetSize_.Height() + space, yMin);
3175 yMax = wrapperBottom - height - paddingBottom_;
3176 break;
3177 }
3178 default:
3179 break;
3180 }
3181 if (LessNotEqual(xMax, xMin) || LessNotEqual(yMax, yMin)) {
3182 return OffsetF(0.0f, 0.0f);
3183 }
3184 auto x = std::clamp(position.GetX(), xMin, xMax);
3185 auto y = std::clamp(position.GetY(), yMin, yMax);
3186 return OffsetF(x, y);
3187 }
3188
AddTargetSpace(const OffsetF & position)3189 OffsetF MenuLayoutAlgorithm::AddTargetSpace(const OffsetF& position)
3190 {
3191 auto x = position.GetX();
3192 auto y = position.GetY();
3193 switch (placement_) {
3194 case Placement::BOTTOM_LEFT:
3195 case Placement::BOTTOM_RIGHT:
3196 case Placement::BOTTOM: {
3197 y += targetSecurity_;
3198 break;
3199 }
3200 case Placement::TOP_LEFT:
3201 case Placement::TOP_RIGHT:
3202 case Placement::TOP: {
3203 y -= targetSecurity_;
3204 break;
3205 }
3206 case Placement::RIGHT_TOP:
3207 case Placement::RIGHT_BOTTOM:
3208 case Placement::RIGHT: {
3209 x += targetSecurity_;
3210 break;
3211 }
3212 case Placement::LEFT_TOP:
3213 case Placement::LEFT_BOTTOM:
3214 case Placement::LEFT: {
3215 x -= targetSecurity_;
3216 break;
3217 }
3218 default: {
3219 y += targetSecurity_;
3220 break;
3221 }
3222 }
3223 return OffsetF(x, y);
3224 }
3225
AddOffset(const OffsetF & position)3226 OffsetF MenuLayoutAlgorithm::AddOffset(const OffsetF& position)
3227 {
3228 auto x = position.GetX();
3229 auto y = position.GetY();
3230 switch (placement_) {
3231 case Placement::BOTTOM_LEFT:
3232 case Placement::BOTTOM_RIGHT:
3233 case Placement::BOTTOM: {
3234 x += positionOffset_.GetX();
3235 y += positionOffset_.GetY();
3236 break;
3237 }
3238 case Placement::TOP_LEFT:
3239 case Placement::TOP_RIGHT:
3240 case Placement::TOP: {
3241 x += positionOffset_.GetX();
3242 y -= positionOffset_.GetY();
3243 break;
3244 }
3245 case Placement::RIGHT_TOP:
3246 case Placement::RIGHT_BOTTOM:
3247 case Placement::RIGHT: {
3248 x += positionOffset_.GetX();
3249 y += positionOffset_.GetY();
3250 break;
3251 }
3252 case Placement::LEFT_TOP:
3253 case Placement::LEFT_BOTTOM:
3254 case Placement::LEFT: {
3255 x -= positionOffset_.GetX();
3256 y += positionOffset_.GetY();
3257 break;
3258 }
3259 default: {
3260 x += positionOffset_.GetX();
3261 y += positionOffset_.GetY();
3262 break;
3263 }
3264 }
3265 return OffsetF(x, y);
3266 }
3267
CheckPositionInPlacementRect(const Rect & rect,const OffsetF & position,const SizeF & childSize)3268 bool MenuLayoutAlgorithm::CheckPositionInPlacementRect(
3269 const Rect& rect, const OffsetF& position, const SizeF& childSize)
3270 {
3271 auto x = position.GetX();
3272 auto y = position.GetY();
3273 auto leftOutside = LessNotEqual(x, rect.Left());
3274 auto rightOutside = GreatNotEqual(x + childSize.Width(), rect.Right());
3275 auto topOutside = LessNotEqual(y, rect.Top());
3276 auto bottomOutside = GreatNotEqual(y + childSize.Height(), rect.Bottom());
3277 TAG_LOGI(AceLogTag::ACE_MENU,
3278 "CheckPositionInPlacementRect, leftOutside : %{public}d, rightOutside : %{public}d, topOutside : %{public}d, "
3279 "bottomOutside : %{public}d",
3280 leftOutside, rightOutside, topOutside, bottomOutside);
3281 auto outside = leftOutside || rightOutside || topOutside || bottomOutside;
3282 if (state_ != prevState_) {
3283 if (prevState_ == -1) {
3284 prevState_ = state_;
3285 preOffset_ = position;
3286 preRect_ = rect;
3287 if (!outside) {
3288 return true;
3289 }
3290 flag_ = true;
3291 positionOffset_ = { 0.0f, 0.0f };
3292 return false;
3293 }
3294 return CheckPlacement(childSize);
3295 }
3296 if (outside) {
3297 preOffset_ = position;
3298 preRect_ = rect;
3299 return false;
3300 }
3301 return true;
3302 }
3303
CheckPlacement(const SizeF & childSize)3304 bool MenuLayoutAlgorithm::CheckPlacement(const SizeF& childSize)
3305 {
3306 auto x = preOffset_.GetX();
3307 auto y = preOffset_.GetY();
3308 auto leftOutside = LessNotEqual(x, preRect_.Left());
3309 auto rightOutside = GreatNotEqual(x + childSize.Width(), preRect_.Right());
3310 auto topOutside = LessNotEqual(y, preRect_.Top());
3311 auto bottomOutside = GreatNotEqual(y + childSize.Height(), preRect_.Bottom());
3312 TAG_LOGI(AceLogTag::ACE_MENU,
3313 "CheckPlacement, leftOutside : %{public}d, rightOutside : %{public}d, topOutside : %{public}d, "
3314 "bottomOutside : %{public}d",
3315 leftOutside, rightOutside, topOutside, bottomOutside);
3316
3317 switch (prevState_) {
3318 case static_cast<int>(DirectionState::Bottom_Direction):
3319 case static_cast<int>(DirectionState::Top_Direction): {
3320 if ((leftOutside || rightOutside) && !(topOutside || bottomOutside)) {
3321 placement_ = Placement::NONE;
3322 return true;
3323 }
3324 break;
3325 }
3326 case static_cast<int>(DirectionState::Right_Direction):
3327 case static_cast<int>(DirectionState::Left_Direction): {
3328 if ((topOutside || bottomOutside) && !(leftOutside || rightOutside)) {
3329 placement_ = Placement::NONE;
3330 return true;
3331 }
3332 break;
3333 }
3334 default:
3335 return false;
3336 }
3337
3338 return false;
3339 }
3340
CheckPosition(const OffsetF & position,const SizeF & childSize)3341 bool MenuLayoutAlgorithm::CheckPosition(const OffsetF& position, const SizeF& childSize)
3342 {
3343 float xAvoid = wrapperRect_.Left() + paddingStart_;
3344 float yAvoid = wrapperRect_.Top() + paddingTop_ + param_.topSecurity;
3345 float maxWidth = wrapperSize_.Width() - paddingEnd_ - paddingStart_;
3346 float maxHeight = wrapperSize_.Height() - paddingTop_ - param_.topSecurity - paddingBottom_ - param_.bottomSecurity;
3347 Rect rect;
3348 Rect targetRect = Rect(targetOffset_.GetX(), targetOffset_.GetY(), targetSize_.Width(), targetSize_.Height());
3349 switch (placement_) {
3350 case Placement::BOTTOM_LEFT:
3351 case Placement::BOTTOM_RIGHT:
3352 case Placement::BOTTOM: {
3353 state_ = static_cast<int>(DirectionState::Bottom_Direction);
3354 auto y = std::max<float>(targetRect.Bottom(), yAvoid);
3355 auto height = std::min<float>(
3356 wrapperRect_.Bottom() - targetRect.Bottom() - paddingBottom_ - param_.bottomSecurity, maxHeight);
3357 rect.SetRect(xAvoid, y, maxWidth, height);
3358 break;
3359 }
3360 case Placement::TOP_LEFT:
3361 case Placement::TOP_RIGHT:
3362 case Placement::TOP: {
3363 state_ = static_cast<int>(DirectionState::Top_Direction);
3364 auto height = std::min<float>(targetRect.Top() - yAvoid, maxHeight);
3365 rect.SetRect(xAvoid, yAvoid, maxWidth, height);
3366 break;
3367 }
3368 case Placement::RIGHT_TOP:
3369 case Placement::RIGHT_BOTTOM:
3370 case Placement::RIGHT: {
3371 state_ = static_cast<int>(DirectionState::Right_Direction);
3372 auto x = std::max<float>(targetRect.Right(), xAvoid);
3373 auto width = std::min<float>(wrapperRect_.Right() - targetRect.Right() - paddingEnd_, maxWidth);
3374 rect.SetRect(x, yAvoid, width, maxHeight);
3375 break;
3376 }
3377 case Placement::LEFT_TOP:
3378 case Placement::LEFT_BOTTOM:
3379 case Placement::LEFT: {
3380 state_ = static_cast<int>(DirectionState::Left_Direction);
3381 auto width = std::min<float>(targetRect.Left() - xAvoid, maxWidth);
3382 rect.SetRect(xAvoid, yAvoid, width, maxHeight);
3383 break;
3384 }
3385 default:
3386 state_ = static_cast<int>(DirectionState::None_Direction);
3387 return false;
3388 }
3389 return CheckPositionInPlacementRect(rect, position, childSize);
3390 }
3391
GetPositionWithPlacementTop(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)3392 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacementTop(
3393 const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
3394 {
3395 TAG_LOGI(AceLogTag::ACE_MENU, "GetPositionWithPlacementTop : %{public}s", topPosition.ToString().c_str());
3396 return topPosition;
3397 }
3398
GetPositionWithPlacementTopLeft(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)3399 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacementTopLeft(
3400 const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
3401 {
3402 OffsetF childPosition;
3403 float marginRight = 0.0f;
3404 float marginBottom = 0.0f;
3405 childPosition = OffsetF(
3406 targetOffset_.GetX() - marginRight, targetOffset_.GetY() - childSize.Height() - marginBottom - targetSpace_);
3407 TAG_LOGI(AceLogTag::ACE_MENU, "GetPositionWithPlacementTopLeft : %{public}s", childPosition.ToString().c_str());
3408 return childPosition;
3409 }
3410
GetPositionWithPlacementTopRight(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)3411 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacementTopRight(
3412 const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
3413 {
3414 OffsetF childPosition;
3415 float marginBottom = 0.0f;
3416 float marginLeft = 0.0f;
3417 childPosition = OffsetF(targetOffset_.GetX() + targetSize_.Width() - childSize.Width() + marginLeft,
3418 targetOffset_.GetY() - childSize.Height() - targetSpace_ - marginBottom);
3419 TAG_LOGI(AceLogTag::ACE_MENU, "GetPositionWithPlacementTopRight : %{public}s", childPosition.ToString().c_str());
3420 return childPosition;
3421 }
3422
GetPositionWithPlacementBottom(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)3423 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacementBottom(
3424 const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
3425 {
3426 TAG_LOGI(AceLogTag::ACE_MENU, "GetPositionWithPlacementBottom : %{public}s", bottomPosition.ToString().c_str());
3427 return bottomPosition;
3428 }
3429
GetPositionWithPlacementBottomLeft(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)3430 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacementBottomLeft(
3431 const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
3432 {
3433 OffsetF childPosition;
3434 float marginRight = 0.0f;
3435 float marginTop = 0.0f;
3436 childPosition = OffsetF(
3437 targetOffset_.GetX() - marginRight, targetOffset_.GetY() + targetSize_.Height() + targetSpace_ + marginTop);
3438 TAG_LOGI(AceLogTag::ACE_MENU, "GetPositionWithPlacementBottomLeft : %{public}s", childPosition.ToString().c_str());
3439 return childPosition;
3440 }
3441
GetPositionWithPlacementBottomRight(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)3442 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacementBottomRight(
3443 const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
3444 {
3445 OffsetF childPosition;
3446 float marginTop = 0.0f;
3447 float marginLeft = 0.0f;
3448 childPosition = OffsetF(targetOffset_.GetX() + targetSize_.Width() - childSize.Width() + marginLeft,
3449 targetOffset_.GetY() + targetSize_.Height() + targetSpace_ + marginTop);
3450 TAG_LOGI(AceLogTag::ACE_MENU, "GetPositionWithPlacementBottomRight : %{public}s", childPosition.ToString().c_str());
3451 return childPosition;
3452 }
3453
GetPositionWithPlacementLeft(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)3454 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacementLeft(
3455 const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
3456 {
3457 OffsetF childPosition;
3458 float marginRight = 0.0f;
3459 childPosition = OffsetF(targetOffset_.GetX() - targetSpace_ - childSize.Width() - marginRight,
3460 targetOffset_.GetY() + targetSize_.Height() / 2.0 - childSize.Height() / 2.0);
3461 TAG_LOGI(AceLogTag::ACE_MENU, "GetPositionWithPlacementLeft : %{public}s", childPosition.ToString().c_str());
3462 return childPosition;
3463 }
3464
GetPositionWithPlacementLeftTop(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)3465 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacementLeftTop(
3466 const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
3467 {
3468 OffsetF childPosition;
3469 float marginRight = 0.0f;
3470 float marginBottom = 0.0f;
3471 childPosition = OffsetF(
3472 targetOffset_.GetX() - targetSpace_ - childSize.Width() - marginRight, targetOffset_.GetY() - marginBottom);
3473 TAG_LOGI(AceLogTag::ACE_MENU, "GetPositionWithPlacementLeftTop : %{public}s", childPosition.ToString().c_str());
3474 return childPosition;
3475 }
3476
GetPositionWithPlacementLeftBottom(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)3477 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacementLeftBottom(
3478 const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
3479 {
3480 OffsetF childPosition;
3481 float marginRight = 0.0f;
3482 float marginTop = 0.0f;
3483 childPosition = OffsetF(targetOffset_.GetX() - targetSpace_ - childSize.Width() - marginRight,
3484 targetOffset_.GetY() + targetSize_.Height() - childSize.Height() - marginTop);
3485 TAG_LOGI(AceLogTag::ACE_MENU, "GetPositionWithPlacementLeftBottom : %{public}s", childPosition.ToString().c_str());
3486 return childPosition;
3487 }
3488
GetPositionWithPlacementRight(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)3489 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacementRight(
3490 const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
3491 {
3492 OffsetF childPosition;
3493 float marginLeft = 0.0f;
3494 childPosition = OffsetF(targetOffset_.GetX() + targetSize_.Width() + targetSpace_ + marginLeft,
3495 targetOffset_.GetY() + targetSize_.Height() / 2.0 - childSize.Height() / 2.0);
3496 TAG_LOGI(AceLogTag::ACE_MENU, "GetPositionWithPlacementRight : %{public}s", childPosition.ToString().c_str());
3497 return childPosition;
3498 }
3499
GetPositionWithPlacementRightTop(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)3500 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacementRightTop(
3501 const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
3502 {
3503 OffsetF childPosition;
3504 float marginBottom = 0.0f;
3505 float marginLeft = 0.0f;
3506 childPosition = OffsetF(
3507 targetOffset_.GetX() + targetSize_.Width() + targetSpace_ + marginLeft, targetOffset_.GetY() - marginBottom);
3508 TAG_LOGI(AceLogTag::ACE_MENU, "GetPositionWithPlacementRightTop : %{public}s", childPosition.ToString().c_str());
3509 return childPosition;
3510 }
3511
GetPositionWithPlacementRightBottom(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)3512 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacementRightBottom(
3513 const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
3514 {
3515 OffsetF childPosition;
3516 float marginTop = 0.0f;
3517 float marginLeft = 0.0f;
3518 childPosition = OffsetF(targetOffset_.GetX() + targetSize_.Width() + targetSpace_ + marginLeft,
3519 targetOffset_.GetY() + targetSize_.Height() - childSize.Height() - marginTop);
3520 TAG_LOGI(AceLogTag::ACE_MENU, "GetPositionWithPlacementRightBottom : %{public}s", childPosition.ToString().c_str());
3521 return childPosition;
3522 }
3523
InitCanExpandCurrentWindow(bool isShowInSubWindow,LayoutWrapper * layoutWrapper)3524 void MenuLayoutAlgorithm::InitCanExpandCurrentWindow(bool isShowInSubWindow, LayoutWrapper* layoutWrapper)
3525 {
3526 CHECK_NULL_VOID(layoutWrapper);
3527 auto hostNode = layoutWrapper->GetHostNode();
3528 CHECK_NULL_VOID(hostNode);
3529 auto pipelineContext = DialogManager::GetMainPipelineContext(hostNode);
3530 CHECK_NULL_VOID(pipelineContext);
3531 auto containerId = Container::CurrentId();
3532 auto container = AceEngine::Get().GetContainer(containerId);
3533 if (containerId >= MIN_SUBCONTAINER_ID) {
3534 auto parentContainerId = SubwindowManager::GetInstance()->GetParentContainerId(containerId);
3535 container = AceEngine::Get().GetContainer(parentContainerId);
3536 }
3537 CHECK_NULL_VOID(container);
3538 // Get FreeMultiWindow status of main window or host window
3539 isFreeMultiWindow_ = container->IsFreeMultiWindow();
3540 auto theme = pipelineContext->GetTheme<SelectTheme>();
3541 CHECK_NULL_VOID(theme);
3542 // false for phone devices
3543 isExpandDisplay_ = theme->GetExpandDisplay() || isFreeMultiWindow_;
3544 auto isUIExtensionSubWindow = container->IsUIExtensionWindow();
3545 if ((isExpandDisplay_ || isUIExtensionSubWindow) && !isShowInSubWindow && containerId >= MIN_SUBCONTAINER_ID) {
3546 canExpandCurrentWindow_ = true;
3547 isTargetNodeInSubwindow_ = true;
3548 return;
3549 }
3550 canExpandCurrentWindow_ = isExpandDisplay_ && isShowInSubWindow;
3551 if (containerId >= MIN_SUBCONTAINER_ID) {
3552 isUIExtensionSubWindow_ = container->IsUIExtensionWindow();
3553 if (isUIExtensionSubWindow_) {
3554 canExpandCurrentWindow_ = true;
3555 auto subwindow = SubwindowManager::GetInstance()->GetSubwindowByType(
3556 containerId, SubwindowType::TYPE_MENU);
3557 CHECK_NULL_VOID(subwindow);
3558 auto rect = subwindow->GetUIExtensionHostWindowRect();
3559 UIExtensionHostWindowRect_ = RectF(rect.Left(), rect.Top(), rect.Width(), rect.Height());
3560 TAG_LOGI(AceLogTag::ACE_MENU, "GetUIExtensionHostWindowRect : %{public}s",
3561 UIExtensionHostWindowRect_.ToString().c_str());
3562 }
3563 }
3564 }
3565
GetMenuWindowRectInfo(const RefPtr<MenuPattern> & menuPattern)3566 Rect MenuLayoutAlgorithm::GetMenuWindowRectInfo(const RefPtr<MenuPattern>& menuPattern)
3567 {
3568 auto menuWindowRect = Rect();
3569 CHECK_NULL_RETURN(menuPattern, menuWindowRect);
3570 auto host = menuPattern->GetHost();
3571 CHECK_NULL_RETURN(host, menuWindowRect);
3572 auto pipelineContext = DialogManager::GetMainPipelineContext(host, isTargetNodeInSubwindow_);
3573 CHECK_NULL_RETURN(pipelineContext, menuWindowRect);
3574 auto rect = pipelineContext->GetDisplayWindowRectInfo();
3575 displayWindowRect_ = RectF(rect.Left(), rect.Top(), rect.Width(), rect.Height());
3576 TAG_LOGI(AceLogTag::ACE_MENU, "GetDisplayWindowRectInfo : %{public}s", displayWindowRect_.ToString().c_str());
3577 menuWindowRect = Rect(rect.Left(), rect.Top(), rect.Width(), rect.Height());
3578 auto availableRect = OverlayManager::GetDisplayAvailableRect(
3579 menuPattern->GetHost(), static_cast<int32_t>(SubwindowType::TYPE_MENU));
3580 TAG_LOGI(AceLogTag::ACE_MENU, "GetDisplayAvailableRect : %{public}s", availableRect.ToString().c_str());
3581 if (canExpandCurrentWindow_ && isExpandDisplay_) {
3582 menuWindowRect = Rect(availableRect.Left(), availableRect.Top(), availableRect.Width(), availableRect.Height());
3583 } else if (isUIExtensionSubWindow_ && !isExpandDisplay_) {
3584 rect = Rect(UIExtensionHostWindowRect_.Left(), UIExtensionHostWindowRect_.Top(),
3585 UIExtensionHostWindowRect_.Width(), UIExtensionHostWindowRect_.Height());
3586 menuWindowRect = rect;
3587 }
3588 TAG_LOGI(AceLogTag::ACE_MENU, "GetMenuWindowRectInfo : %{public}s", menuWindowRect.ToString().c_str());
3589 dumpInfo_.menuWindowRect = menuWindowRect;
3590 menuPattern->SetMenuWindowRect(menuWindowRect);
3591 return menuWindowRect;
3592 }
3593
ModifyTargetOffset()3594 void MenuLayoutAlgorithm::ModifyTargetOffset()
3595 {
3596 TAG_LOGI(AceLogTag::ACE_MENU, "original targetOffset is : %{public}s", targetOffset_.ToString().c_str());
3597 if (canExpandCurrentWindow_ && isExpandDisplay_ && !isTargetNodeInSubwindow_) {
3598 targetOffset_ += displayWindowRect_.GetOffset();
3599 TAG_LOGI(AceLogTag::ACE_MENU, "ModifyTargetOffset for displayAvailableRect : %{public}s",
3600 targetOffset_.ToString().c_str());
3601 } else if (isUIExtensionSubWindow_ && !isExpandDisplay_) {
3602 targetOffset_ += displayWindowRect_.GetOffset() - UIExtensionHostWindowRect_.GetOffset();
3603 TAG_LOGI(AceLogTag::ACE_MENU, "ModifyTargetOffset for UIExtensionHostWindowRect : %{public}s",
3604 targetOffset_.ToString().c_str());
3605 }
3606 }
3607
HoldEmbeddedMenuPosition(LayoutWrapper * layoutWrapper)3608 bool MenuLayoutAlgorithm::HoldEmbeddedMenuPosition(LayoutWrapper* layoutWrapper)
3609 {
3610 auto menuNode = layoutWrapper->GetHostNode();
3611 CHECK_NULL_RETURN(menuNode, false);
3612 auto menuNodePattern = AceType::DynamicCast<MenuPattern>(menuNode->GetPattern());
3613 CHECK_NULL_RETURN(menuNodePattern, false);
3614 auto menuWrapper = menuNodePattern->GetMenuWrapper();
3615 CHECK_NULL_RETURN(menuWrapper, false);
3616 auto menuWrapperPattern = menuWrapper->GetPattern<MenuWrapperPattern>();
3617 CHECK_NULL_RETURN(menuWrapperPattern, false);
3618 auto innerMenu = menuWrapperPattern->GetMenuChild(menuNode);
3619 CHECK_NULL_RETURN(innerMenu, false);
3620 auto innerMenuPattern = AceType::DynamicCast<MenuPattern>(innerMenu->GetPattern());
3621 CHECK_NULL_RETURN(innerMenuPattern, false);
3622 auto layoutProps = innerMenuPattern->GetLayoutProperty<MenuLayoutProperty>();
3623 CHECK_NULL_RETURN(layoutProps, false);
3624 auto isEmbeddedMenu =
3625 layoutProps->GetExpandingMode().value_or(SubMenuExpandingMode::SIDE) == SubMenuExpandingMode::EMBEDDED;
3626 return isEmbeddedMenu && menuWrapperPattern->GetEmbeddedSubMenuExpandTotalCount() > 0 &&
3627 !menuWrapperPattern->GetForceUpdateEmbeddedMenu();
3628 }
3629
MoveTo(double x,double y)3630 std::string MenuLayoutAlgorithm::MoveTo(double x, double y)
3631 {
3632 return "M" + std::to_string(x) + " " + std::to_string(y) + " ";
3633 }
3634
LineTo(double x,double y)3635 std::string MenuLayoutAlgorithm::LineTo(double x, double y)
3636 {
3637 return "L" + std::to_string(x) + " " + std::to_string(y) + " ";
3638 }
3639
ArcTo(double rx,double ry,double rotation,int32_t arc_flag,double x,double y)3640 std::string MenuLayoutAlgorithm::ArcTo(double rx, double ry, double rotation, int32_t arc_flag, double x, double y)
3641 {
3642 int32_t sweep_flag = 1;
3643 return "A" + std::to_string(rx) + " " + std::to_string(ry) + " " + std::to_string(rotation) + " " +
3644 std::to_string(arc_flag) + " " + std::to_string(sweep_flag) + " " + std::to_string(x) + " " +
3645 std::to_string(y) + " ";
3646 }
3647
BuildBottomArrowPath(float arrowX,float arrowY,std::string & path)3648 void MenuLayoutAlgorithm::BuildBottomArrowPath(float arrowX, float arrowY, std::string& path)
3649 {
3650 path += LineTo(arrowX - ARROW_P1_OFFSET_X.ConvertToPx(), arrowY + ARROW_P1_OFFSET_Y.ConvertToPx()); //P1
3651 path += LineTo(arrowX - ARROW_P2_OFFSET_X.ConvertToPx(), arrowY + ARROW_P2_OFFSET_Y.ConvertToPx()); //P2
3652 path += ArcTo(ARROW_RADIUS.ConvertToPx(), ARROW_RADIUS.ConvertToPx(), 0.0f, 0,
3653 arrowX + ARROW_P2_OFFSET_X.ConvertToPx(), arrowY + ARROW_P2_OFFSET_Y.ConvertToPx()); //P4
3654 path += LineTo(arrowX + ARROW_P1_OFFSET_X.ConvertToPx(), arrowY + ARROW_P1_OFFSET_Y.ConvertToPx()); //P5
3655 }
3656
BuildTopArrowPath(float arrowX,float arrowY,std::string & path)3657 void MenuLayoutAlgorithm::BuildTopArrowPath(float arrowX, float arrowY, std::string& path)
3658 {
3659 path += LineTo(arrowX + ARROW_P1_OFFSET_X.ConvertToPx(), arrowY - ARROW_P1_OFFSET_Y.ConvertToPx()); //P1
3660 path += LineTo(arrowX + ARROW_P2_OFFSET_X.ConvertToPx(), arrowY - ARROW_P2_OFFSET_Y.ConvertToPx()); //P2
3661 path += ArcTo(ARROW_RADIUS.ConvertToPx(), ARROW_RADIUS.ConvertToPx(), 0.0f, 0,
3662 arrowX - ARROW_P2_OFFSET_X.ConvertToPx(), arrowY - ARROW_P2_OFFSET_Y.ConvertToPx()); //P4
3663 path += LineTo(arrowX - ARROW_P1_OFFSET_X.ConvertToPx(), arrowY - ARROW_P1_OFFSET_Y.ConvertToPx()); //P5
3664 }
3665
BuildRightArrowPath(float arrowX,float arrowY,std::string & path)3666 void MenuLayoutAlgorithm::BuildRightArrowPath(float arrowX, float arrowY, std::string& path)
3667 {
3668 path += LineTo(arrowX + ARROW_P1_OFFSET_Y.ConvertToPx(), arrowY + ARROW_P1_OFFSET_X.ConvertToPx()); //P1
3669 path += LineTo(arrowX + ARROW_P2_OFFSET_Y.ConvertToPx(), arrowY + ARROW_P2_OFFSET_X.ConvertToPx()); //P2
3670 path += ArcTo(ARROW_RADIUS.ConvertToPx(), ARROW_RADIUS.ConvertToPx(), 0.0f, 0,
3671 arrowX + ARROW_P2_OFFSET_Y.ConvertToPx(), arrowY - ARROW_P2_OFFSET_X.ConvertToPx()); //P4
3672 path += LineTo(arrowX + ARROW_P1_OFFSET_Y.ConvertToPx(), arrowY - ARROW_P1_OFFSET_X.ConvertToPx()); //P5
3673 }
3674
BuildLeftArrowPath(float arrowX,float arrowY,std::string & path)3675 void MenuLayoutAlgorithm::BuildLeftArrowPath(float arrowX, float arrowY, std::string& path)
3676 {
3677 path += LineTo(arrowX - ARROW_P1_OFFSET_Y.ConvertToPx(), arrowY - ARROW_P1_OFFSET_X.ConvertToPx()); //P1
3678 path += LineTo(arrowX - ARROW_P2_OFFSET_Y.ConvertToPx(), arrowY - ARROW_P2_OFFSET_X.ConvertToPx()); //P2
3679 path += ArcTo(ARROW_RADIUS.ConvertToPx(), ARROW_RADIUS.ConvertToPx(), 0.0f, 0,
3680 arrowX - ARROW_P2_OFFSET_Y.ConvertToPx(), arrowY + ARROW_P2_OFFSET_X.ConvertToPx()); //P4
3681 path += LineTo(arrowX - ARROW_P1_OFFSET_Y.ConvertToPx(), arrowY + ARROW_P1_OFFSET_X.ConvertToPx()); //P5
3682 }
3683
BuildTopLinePath(const OffsetF & arrowPosition,float radiusPx,Placement arrowBuildPlacement,bool didNeedArrow)3684 std::string MenuLayoutAlgorithm::BuildTopLinePath(const OffsetF& arrowPosition, float radiusPx,
3685 Placement arrowBuildPlacement, bool didNeedArrow)
3686 {
3687 std::string path;
3688 if (didNeedArrow) {
3689 switch (arrowBuildPlacement) {
3690 case Placement::BOTTOM:
3691 case Placement::BOTTOM_LEFT:
3692 case Placement::BOTTOM_RIGHT:
3693 BuildBottomArrowPath(arrowPosition.GetX(), arrowPosition.GetY(), path);
3694 break;
3695 default:
3696 break;
3697 }
3698 }
3699 path += LineTo(childOffset_.GetX() + childMarginFrameSize_.Width() - radiusPx, childOffset_.GetY());
3700 path += ArcTo(radiusPx, radiusPx, 0.0f, 0, childOffset_.GetX() + childMarginFrameSize_.Width(),
3701 childOffset_.GetY() + radiusPx);
3702 return path;
3703 }
3704
BuildRightLinePath(const OffsetF & arrowPosition,float radiusPx,Placement arrowBuildPlacement,bool didNeedArrow)3705 std::string MenuLayoutAlgorithm::BuildRightLinePath(const OffsetF& arrowPosition, float radiusPx,
3706 Placement arrowBuildPlacement, bool didNeedArrow)
3707 {
3708 std::string path;
3709 if (didNeedArrow) {
3710 switch (arrowBuildPlacement) {
3711 case Placement::LEFT:
3712 case Placement::LEFT_TOP:
3713 case Placement::LEFT_BOTTOM:
3714 BuildLeftArrowPath(arrowPosition.GetX(), arrowPosition.GetY(), path);
3715 break;
3716 default:
3717 break;
3718 }
3719 }
3720 path += LineTo(childOffset_.GetX() + childMarginFrameSize_.Width(),
3721 childOffset_.GetY() + childMarginFrameSize_.Height() - radiusPx);
3722 path += ArcTo(radiusPx, radiusPx, 0.0f, 0, childOffset_.GetX() + childMarginFrameSize_.Width() - radiusPx,
3723 childOffset_.GetY() + childMarginFrameSize_.Height());
3724 return path;
3725 }
3726
BuildBottomLinePath(const OffsetF & arrowPosition,float radiusPx,Placement arrowBuildPlacement,bool didNeedArrow)3727 std::string MenuLayoutAlgorithm::BuildBottomLinePath(const OffsetF& arrowPosition, float radiusPx,
3728 Placement arrowBuildPlacement, bool didNeedArrow)
3729 {
3730 std::string path;
3731 if (didNeedArrow) {
3732 switch (arrowBuildPlacement) {
3733 case Placement::TOP:
3734 case Placement::TOP_LEFT:
3735 case Placement::TOP_RIGHT:
3736 BuildTopArrowPath(arrowPosition.GetX(), arrowPosition.GetY(), path);
3737 break;
3738 default:
3739 break;
3740 }
3741 }
3742 path += LineTo(childOffset_.GetX() + radiusPx, childOffset_.GetY() + childMarginFrameSize_.Height());
3743 path += ArcTo(radiusPx, radiusPx, 0.0f, 0, childOffset_.GetX(),
3744 childOffset_.GetY() + childMarginFrameSize_.Height() - radiusPx);
3745 return path;
3746 }
3747
BuildLeftLinePath(const OffsetF & arrowPosition,float radiusPx,Placement arrowBuildPlacement,bool didNeedArrow)3748 std::string MenuLayoutAlgorithm::BuildLeftLinePath(const OffsetF& arrowPosition, float radiusPx,
3749 Placement arrowBuildPlacement, bool didNeedArrow)
3750 {
3751 std::string path;
3752 if (didNeedArrow) {
3753 switch (arrowBuildPlacement) {
3754 case Placement::RIGHT:
3755 case Placement::RIGHT_TOP:
3756 case Placement::RIGHT_BOTTOM:
3757 BuildRightArrowPath(arrowPosition.GetX(), arrowPosition.GetY(), path);
3758 break;
3759 default:
3760 break;
3761 }
3762 }
3763 path += LineTo(childOffset_.GetX(), childOffset_.GetY() + radiusPx);
3764 path += ArcTo(radiusPx, radiusPx, 0.0f, 0, childOffset_.GetX() + radiusPx, childOffset_.GetY());
3765 return path;
3766 }
3767
NormalizeBorderRadius(float & radiusTopLeftPx,float & radiusTopRightPx,float & radiusBottomLeftPx,float & radiusBottomRightPx)3768 void MenuLayoutAlgorithm::NormalizeBorderRadius(float& radiusTopLeftPx, float& radiusTopRightPx,
3769 float& radiusBottomLeftPx, float& radiusBottomRightPx)
3770 {
3771 float childMarginFrameWidth = childMarginFrameSize_.Width();
3772 float childMarginFrameHeight = childMarginFrameSize_.Height();
3773 if (NearZero(childMarginFrameWidth) || NearZero(childMarginFrameHeight)) {
3774 radiusTopLeftPx = 0.0f;
3775 radiusTopRightPx = 0.0f;
3776 radiusBottomLeftPx = 0.0f;
3777 radiusBottomRightPx = 0.0f;
3778 return;
3779 }
3780 float topRatio = (radiusTopLeftPx + radiusTopRightPx) / childMarginFrameWidth;
3781 float bottomRatio = (radiusBottomLeftPx + radiusBottomRightPx) / childMarginFrameWidth;
3782 float leftRatio = (radiusTopLeftPx + radiusBottomLeftPx) / childMarginFrameHeight;
3783 float rightRatio = (radiusTopRightPx + radiusBottomRightPx) / childMarginFrameHeight;
3784 float maxRatio = std::max(std::max(topRatio, bottomRatio), std::max(leftRatio, rightRatio));
3785 if (GreatNotEqual(maxRatio, 1.0f)) {
3786 radiusTopLeftPx = radiusTopLeftPx / maxRatio;
3787 radiusTopRightPx = radiusTopRightPx / maxRatio;
3788 radiusBottomLeftPx = radiusBottomLeftPx / maxRatio;
3789 radiusBottomRightPx = radiusBottomRightPx / maxRatio;
3790 }
3791 }
3792
CalculateMenuPath(LayoutWrapper * layoutWrapper,bool didNeedArrow)3793 std::string MenuLayoutAlgorithm::CalculateMenuPath(LayoutWrapper* layoutWrapper, bool didNeedArrow)
3794 {
3795 CHECK_NULL_RETURN(layoutWrapper, "");
3796 auto menuNode = layoutWrapper->GetHostNode();
3797 CHECK_NULL_RETURN(menuNode, "");
3798 auto menuPattern = menuNode->GetPattern<MenuPattern>();
3799 CHECK_NULL_RETURN(menuPattern, "");
3800 auto renderContext = menuNode->GetRenderContext();
3801 CHECK_NULL_RETURN(renderContext, "");
3802 auto menuPosition = renderContext->GetPositionValue(OffsetT<Dimension>());
3803 BorderRadiusProperty menuBorderRadius = GetMenuRadius(layoutWrapper, childMarginFrameSize_);
3804 float radiusTopLeftPx = menuBorderRadius.radiusTopLeft.value_or(Dimension())
3805 .ConvertToPxWithSize(childMarginFrameSize_.Width());
3806 float radiusTopRightPx = menuBorderRadius.radiusTopRight.value_or(Dimension())
3807 .ConvertToPxWithSize(childMarginFrameSize_.Width());
3808 float radiusBottomLeftPx = menuBorderRadius.radiusBottomLeft.value_or(Dimension())
3809 .ConvertToPxWithSize(childMarginFrameSize_.Width());
3810 float radiusBottomRightPx = menuBorderRadius.radiusBottomRight.value_or(Dimension())
3811 .ConvertToPxWithSize(childMarginFrameSize_.Width());
3812 NormalizeBorderRadius(radiusTopLeftPx, radiusTopRightPx, radiusBottomLeftPx, radiusBottomRightPx);
3813 auto targetOffset = OffsetF(menuPosition.GetX().ConvertToPx(), menuPosition.GetY().ConvertToPx());
3814 if (menuPattern->GetMenuType() == MenuType::SUB_MENU) {
3815 const auto& geometryNode = layoutWrapper->GetGeometryNode();
3816 CHECK_NULL_RETURN(geometryNode, "");
3817 targetOffset = geometryNode->GetMarginFrameOffset();
3818 }
3819 auto childOffset = targetOffset + childOffset_;
3820 auto arrowPosition = targetOffset + arrowPosition_;
3821 MenuPathParams params = {
3822 radiusTopLeftPx,
3823 radiusTopRightPx,
3824 radiusBottomLeftPx,
3825 radiusBottomRightPx,
3826 childOffset,
3827 childMarginFrameSize_,
3828 arrowPosition,
3829 arrowPlacement_,
3830 didNeedArrow,
3831 };
3832 menuPattern->UpdateMenuPathParams(params);
3833 if (!didNeedArrow) {
3834 return "";
3835 }
3836 std::string path;
3837 path += MoveTo(childOffset_.GetX() + radiusTopLeftPx, childOffset_.GetY());
3838 path += BuildTopLinePath(arrowPosition_, radiusTopRightPx, arrowPlacement_, didNeedArrow);
3839 path += BuildRightLinePath(arrowPosition_, radiusBottomRightPx, arrowPlacement_, didNeedArrow);
3840 path += BuildBottomLinePath(arrowPosition_, radiusBottomLeftPx, arrowPlacement_, didNeedArrow);
3841 path += BuildLeftLinePath(arrowPosition_, radiusTopLeftPx, arrowPlacement_, didNeedArrow);
3842 return path + "Z";
3843 }
3844
ClipMenuPath(LayoutWrapper * layoutWrapper)3845 void MenuLayoutAlgorithm::ClipMenuPath(LayoutWrapper* layoutWrapper)
3846 {
3847 bool didNeedArrow = GetIfNeedArrow(layoutWrapper, childMarginFrameSize_);
3848 clipPath_ = CalculateMenuPath(layoutWrapper, didNeedArrow);
3849 }
3850
UpdateSelectOverlayMenuColumnInfo(const RefPtr<MenuPattern> & pattern,const RefPtr<GridColumnInfo> & columnInfo)3851 bool MenuLayoutAlgorithm::UpdateSelectOverlayMenuColumnInfo(
3852 const RefPtr<MenuPattern>& pattern, const RefPtr<GridColumnInfo>& columnInfo)
3853 {
3854 CHECK_NULL_RETURN(pattern, false);
3855 CHECK_NULL_RETURN(pattern->IsSelectOverlayExtensionMenu(), false);
3856 auto menuWrapper = pattern->GetMenuWrapper();
3857 CHECK_NULL_RETURN(menuWrapper, false);
3858 if (menuWrapper->GetTag() != V2::SELECT_OVERLAY_ETS_TAG) {
3859 return false;
3860 }
3861 auto selectOverlayPattern = menuWrapper->GetPattern<SelectOverlayPattern>();
3862 CHECK_NULL_RETURN(selectOverlayPattern, false);
3863 CHECK_NULL_RETURN(selectOverlayPattern->GetIsMenuShowInSubWindow(), false);
3864 auto mainWindowContainerId = selectOverlayPattern->GetContainerId();
3865 auto container = Container::GetContainer(mainWindowContainerId);
3866 CHECK_NULL_RETURN(container, false);
3867 auto pipelineContext = AceType::DynamicCast<PipelineContext>(container->GetPipelineContext());
3868 CHECK_NULL_RETURN(pipelineContext, false);
3869 auto displayWindowRect = pipelineContext->GetDisplayWindowRectInfo();
3870 auto mainWindowWidth = displayWindowRect.Width();
3871 if (Positive(mainWindowWidth)) {
3872 auto parent = columnInfo->GetParent();
3873 CHECK_NULL_RETURN(parent, false);
3874 parent->BuildColumnWidth(mainWindowWidth);
3875 TAG_LOGD(
3876 AceLogTag::ACE_MENU, "Update select overlay extension menu min width with main window width constraint.");
3877 return true;
3878 }
3879 return false;
3880 }
3881
GetCurrentSelectTheme(const RefPtr<FrameNode> & frameNode)3882 RefPtr<SelectTheme> MenuLayoutAlgorithm::GetCurrentSelectTheme(const RefPtr<FrameNode>& frameNode)
3883 {
3884 CHECK_NULL_RETURN(frameNode, nullptr);
3885 auto pipeline = frameNode->GetContext();
3886 CHECK_NULL_RETURN(pipeline, nullptr);
3887 auto theme = pipeline->GetTheme<SelectTheme>();
3888 CHECK_NULL_RETURN(theme, nullptr);
3889 return theme;
3890 }
3891 } // namespace OHOS::Ace::NG
3892