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