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 <optional>
19 #include <vector>
20
21 #include "base/geometry/dimension.h"
22 #include "base/geometry/ng/offset_t.h"
23 #include "base/geometry/ng/size_t.h"
24 #include "base/geometry/size.h"
25 #include "base/memory/ace_type.h"
26 #include "base/memory/referenced.h"
27 #include "base/subwindow/subwindow_manager.h"
28 #include "base/utils/utils.h"
29 #include "core/common/ace_engine.h"
30 #include "core/components/common/layout/grid_system_manager.h"
31 #include "core/components/common/properties/placement.h"
32 #include "core/components/container_modal/container_modal_constants.h"
33 #include "core/components_ng/pattern/linear_layout/linear_layout_property.h"
34 #include "core/components_ng/pattern/menu/menu_layout_property.h"
35 #include "core/components_ng/pattern/menu/menu_pattern.h"
36 #include "core/components_ng/pattern/menu/menu_theme.h"
37 #include "core/components_ng/pattern/menu/preview/menu_preview_pattern.h"
38 #include "core/components_ng/pattern/menu/wrapper/menu_wrapper_pattern.h"
39 #include "core/components_ng/pattern/scroll/scroll_layout_property.h"
40 #include "core/components_ng/property/border_property.h"
41 #include "core/components_ng/property/layout_constraint.h"
42 #include "core/components_ng/property/measure_property.h"
43 #include "core/pipeline/pipeline_base.h"
44 #include "core/pipeline_ng/pipeline_context.h"
45 namespace OHOS::Ace::NG {
46
47 namespace {
48 constexpr uint32_t MIN_GRID_COUNTS = 2;
49 constexpr uint32_t GRID_COUNTS_4 = 4;
50 constexpr uint32_t GRID_COUNTS_6 = 6;
51 constexpr uint32_t GRID_COUNTS_8 = 8;
52 constexpr uint32_t GRID_COUNTS_12 = 12;
53 constexpr size_t ALIGNMENT_STEP_OFFSET = 2;
54 constexpr float HEIGHT_CONSTRAINT_FACTOR = 0.8;
55 constexpr float ARROW_WIDTH_FACTOR = 2.0;
56 constexpr double HALF = 2.0;
57
58 const std::map<Placement, std::vector<Placement>> PLACEMENT_STATES = {
59 { Placement::BOTTOM_LEFT,
60 {
61 Placement::BOTTOM_LEFT,
62 Placement::BOTTOM_RIGHT,
63 Placement::TOP_LEFT,
64 Placement::TOP_RIGHT,
65 Placement::RIGHT_TOP,
66 Placement::RIGHT_BOTTOM,
67 Placement::LEFT_TOP,
68 Placement::LEFT_BOTTOM,
69 Placement::NONE,
70 } },
71 { Placement::BOTTOM,
72 {
73 Placement::BOTTOM,
74 Placement::BOTTOM_LEFT,
75 Placement::BOTTOM_RIGHT,
76 Placement::TOP,
77 Placement::TOP_LEFT,
78 Placement::TOP_RIGHT,
79 Placement::RIGHT,
80 Placement::RIGHT_TOP,
81 Placement::RIGHT_BOTTOM,
82 Placement::LEFT,
83 Placement::LEFT_TOP,
84 Placement::LEFT_BOTTOM,
85 Placement::NONE,
86 } },
87 { Placement::BOTTOM_RIGHT,
88 {
89 Placement::BOTTOM_RIGHT,
90 Placement::BOTTOM_LEFT,
91 Placement::TOP_RIGHT,
92 Placement::TOP_LEFT,
93 Placement::RIGHT_BOTTOM,
94 Placement::RIGHT_TOP,
95 Placement::LEFT_BOTTOM,
96 Placement::LEFT_TOP,
97 Placement::NONE,
98 } },
99 { Placement::TOP_LEFT,
100 {
101 Placement::TOP_LEFT,
102 Placement::TOP_RIGHT,
103 Placement::BOTTOM_LEFT,
104 Placement::BOTTOM_RIGHT,
105 Placement::RIGHT_TOP,
106 Placement::RIGHT_BOTTOM,
107 Placement::LEFT_TOP,
108 Placement::LEFT_BOTTOM,
109 Placement::NONE,
110 } },
111 { Placement::TOP,
112 {
113 Placement::TOP,
114 Placement::TOP_LEFT,
115 Placement::TOP_RIGHT,
116 Placement::BOTTOM,
117 Placement::BOTTOM_LEFT,
118 Placement::BOTTOM_RIGHT,
119 Placement::RIGHT,
120 Placement::RIGHT_TOP,
121 Placement::RIGHT_BOTTOM,
122 Placement::LEFT,
123 Placement::LEFT_TOP,
124 Placement::LEFT_BOTTOM,
125 Placement::NONE,
126 } },
127 { Placement::TOP_RIGHT,
128 {
129 Placement::TOP_RIGHT,
130 Placement::TOP_LEFT,
131 Placement::BOTTOM_RIGHT,
132 Placement::BOTTOM_LEFT,
133 Placement::RIGHT_BOTTOM,
134 Placement::RIGHT_TOP,
135 Placement::LEFT_BOTTOM,
136 Placement::LEFT_TOP,
137 Placement::NONE,
138 } },
139 { Placement::LEFT_TOP,
140 {
141 Placement::LEFT_TOP,
142 Placement::LEFT_BOTTOM,
143 Placement::RIGHT_TOP,
144 Placement::RIGHT_BOTTOM,
145 Placement::BOTTOM_LEFT,
146 Placement::BOTTOM_RIGHT,
147 Placement::TOP_LEFT,
148 Placement::TOP_RIGHT,
149 Placement::NONE,
150 } },
151 { Placement::LEFT,
152 {
153 Placement::LEFT,
154 Placement::LEFT_TOP,
155 Placement::LEFT_BOTTOM,
156 Placement::RIGHT,
157 Placement::RIGHT_TOP,
158 Placement::RIGHT_BOTTOM,
159 Placement::BOTTOM,
160 Placement::BOTTOM_LEFT,
161 Placement::BOTTOM_RIGHT,
162 Placement::TOP,
163 Placement::TOP_LEFT,
164 Placement::TOP_RIGHT,
165 Placement::NONE,
166 } },
167 { Placement::LEFT_BOTTOM,
168 {
169 Placement::LEFT_BOTTOM,
170 Placement::LEFT_TOP,
171 Placement::RIGHT_BOTTOM,
172 Placement::RIGHT_TOP,
173 Placement::BOTTOM_RIGHT,
174 Placement::BOTTOM_LEFT,
175 Placement::TOP_RIGHT,
176 Placement::TOP_LEFT,
177 Placement::NONE,
178 } },
179 { Placement::RIGHT_TOP,
180 {
181 Placement::RIGHT_TOP,
182 Placement::RIGHT_BOTTOM,
183 Placement::LEFT_TOP,
184 Placement::LEFT_BOTTOM,
185 Placement::BOTTOM_LEFT,
186 Placement::BOTTOM_RIGHT,
187 Placement::TOP_LEFT,
188 Placement::TOP_RIGHT,
189 Placement::NONE,
190 } },
191 { Placement::RIGHT,
192 {
193 Placement::RIGHT,
194 Placement::RIGHT_TOP,
195 Placement::RIGHT_BOTTOM,
196 Placement::LEFT,
197 Placement::LEFT_TOP,
198 Placement::LEFT_BOTTOM,
199 Placement::BOTTOM,
200 Placement::BOTTOM_LEFT,
201 Placement::BOTTOM_RIGHT,
202 Placement::TOP,
203 Placement::TOP_LEFT,
204 Placement::TOP_RIGHT,
205 Placement::NONE,
206 } },
207 { Placement::RIGHT_BOTTOM,
208 {
209 Placement::RIGHT_BOTTOM,
210 Placement::RIGHT_TOP,
211 Placement::LEFT_BOTTOM,
212 Placement::LEFT_TOP,
213 Placement::BOTTOM_RIGHT,
214 Placement::BOTTOM_LEFT,
215 Placement::TOP_RIGHT,
216 Placement::TOP_LEFT,
217 Placement::NONE,
218 } },
219 };
220
GetMaxGridCounts(const RefPtr<GridColumnInfo> & columnInfo)221 uint32_t GetMaxGridCounts(const RefPtr<GridColumnInfo>& columnInfo)
222 {
223 CHECK_NULL_RETURN(columnInfo, GRID_COUNTS_8);
224 auto currentColumns = columnInfo->GetParent()->GetColumns();
225 auto maxGridCounts = GRID_COUNTS_8;
226 switch (currentColumns) {
227 case GRID_COUNTS_4:
228 maxGridCounts = GRID_COUNTS_4;
229 break;
230 case GRID_COUNTS_8:
231 maxGridCounts = GRID_COUNTS_6;
232 break;
233 case GRID_COUNTS_12:
234 maxGridCounts = GRID_COUNTS_8;
235 break;
236 case MIN_GRID_COUNTS:
237 maxGridCounts = MIN_GRID_COUNTS;
238 break;
239 default:
240 break;
241 }
242 return maxGridCounts;
243 }
244 } // namespace
245
MenuLayoutAlgorithm(int32_t id,const std::string & tag,const std::optional<OffsetF> & lastPosition)246 MenuLayoutAlgorithm::MenuLayoutAlgorithm(int32_t id, const std::string& tag, const std::optional<OffsetF>& lastPosition)
247 : targetNodeId_(id), targetTag_(tag)
248 {
249 if (lastPosition.has_value()) {
250 lastPosition_ = lastPosition;
251 }
252 placementFuncMap_[Placement::TOP] = &MenuLayoutAlgorithm::GetPositionWithPlacementTop;
253 placementFuncMap_[Placement::TOP_LEFT] = &MenuLayoutAlgorithm::GetPositionWithPlacementTopLeft;
254 placementFuncMap_[Placement::TOP_RIGHT] = &MenuLayoutAlgorithm::GetPositionWithPlacementTopRight;
255 placementFuncMap_[Placement::BOTTOM] = &MenuLayoutAlgorithm::GetPositionWithPlacementBottom;
256 placementFuncMap_[Placement::BOTTOM_LEFT] = &MenuLayoutAlgorithm::GetPositionWithPlacementBottomLeft;
257 placementFuncMap_[Placement::BOTTOM_RIGHT] = &MenuLayoutAlgorithm::GetPositionWithPlacementBottomRight;
258 placementFuncMap_[Placement::LEFT] = &MenuLayoutAlgorithm::GetPositionWithPlacementLeft;
259 placementFuncMap_[Placement::LEFT_TOP] = &MenuLayoutAlgorithm::GetPositionWithPlacementLeftTop;
260 placementFuncMap_[Placement::LEFT_BOTTOM] = &MenuLayoutAlgorithm::GetPositionWithPlacementLeftBottom;
261 placementFuncMap_[Placement::RIGHT] = &MenuLayoutAlgorithm::GetPositionWithPlacementRight;
262 placementFuncMap_[Placement::RIGHT_TOP] = &MenuLayoutAlgorithm::GetPositionWithPlacementRightTop;
263 placementFuncMap_[Placement::RIGHT_BOTTOM] = &MenuLayoutAlgorithm::GetPositionWithPlacementRightBottom;
264
265 setHorizontal_ = { Placement::LEFT, Placement::LEFT_BOTTOM, Placement::LEFT_TOP, Placement::RIGHT,
266 Placement::RIGHT_BOTTOM, Placement::RIGHT_TOP };
267 setVertical_ = { Placement::TOP, Placement::TOP_LEFT, Placement::TOP_RIGHT, Placement::BOTTOM,
268 Placement::BOTTOM_LEFT, Placement::BOTTOM_RIGHT };
269
270 auto pipeline = PipelineBase::GetCurrentContext();
271 CHECK_NULL_VOID(pipeline);
272 auto menuTheme = pipeline->GetTheme<NG::MenuTheme>();
273 CHECK_NULL_VOID(menuTheme);
274 previewScale_ = menuTheme->GetPreviewAfterAnimationScale();
275 if (LessOrEqual(previewScale_, 0.0f)) {
276 previewScale_ = 1.0f;
277 }
278 }
279
~MenuLayoutAlgorithm()280 MenuLayoutAlgorithm::~MenuLayoutAlgorithm()
281 {
282 placementFuncMap_.clear();
283 setHorizontal_.clear();
284 setVertical_.clear();
285 }
286
ModifyPreviewMenuPlacement(LayoutWrapper * layoutWrapper)287 void MenuLayoutAlgorithm::ModifyPreviewMenuPlacement(LayoutWrapper* layoutWrapper)
288 {
289 CHECK_NULL_VOID(layoutWrapper);
290 auto props = AceType::DynamicCast<MenuLayoutProperty>(layoutWrapper->GetLayoutProperty());
291 CHECK_NULL_VOID(props);
292 auto pipeline = PipelineBase::GetCurrentContext();
293 CHECK_NULL_VOID(pipeline);
294 auto menuTheme = pipeline->GetTheme<NG::MenuTheme>();
295 CHECK_NULL_VOID(menuTheme);
296 auto hasPlacement = props->GetMenuPlacement().has_value();
297 if (!hasPlacement) {
298 if (menuTheme->GetNormalPlacement() &&
299 SystemProperties::GetDeviceOrientation() == DeviceOrientation::PORTRAIT) {
300 // for Phone with PORTRAIT orientation, default placement is BOTTOM_LEFT
301 placement_ = Placement::BOTTOM_LEFT;
302 props->UpdateMenuPlacement(placement_);
303 } else {
304 placement_ = Placement::RIGHT_TOP;
305 props->UpdateMenuPlacement(placement_);
306 }
307 }
308 }
309
Initialize(LayoutWrapper * layoutWrapper)310 void MenuLayoutAlgorithm::Initialize(LayoutWrapper* layoutWrapper)
311 {
312 CHECK_NULL_VOID(layoutWrapper);
313 // currently using click point as menu position
314 auto props = AceType::DynamicCast<MenuLayoutProperty>(layoutWrapper->GetLayoutProperty());
315 CHECK_NULL_VOID(props);
316 auto menuNode = layoutWrapper->GetHostNode();
317 CHECK_NULL_VOID(menuNode);
318 auto menuPattern = menuNode->GetPattern<MenuPattern>();
319 CHECK_NULL_VOID(menuPattern);
320 auto pipeline = PipelineBase::GetCurrentContext();
321 CHECK_NULL_VOID(pipeline);
322 auto menuTheme = pipeline->GetTheme<NG::MenuTheme>();
323 CHECK_NULL_VOID(menuTheme);
324 auto beforeAnimationScale = menuPattern->GetPreviewBeforeAnimationScale();
325 auto afterAnimationScale = menuPattern->GetPreviewAfterAnimationScale();
326 dumpInfo_.previewBeginScale =
327 LessOrEqual(beforeAnimationScale, 0.0f) ? menuTheme->GetPreviewBeforeAnimationScale() : beforeAnimationScale;
328 dumpInfo_.previewEndScale =
329 LessOrEqual(afterAnimationScale, 0.0f) ? menuTheme->GetPreviewAfterAnimationScale() : afterAnimationScale;
330 previewScale_ = LessOrEqual(afterAnimationScale, 0.0f) ? previewScale_ : afterAnimationScale;
331 position_ = props->GetMenuOffset().value_or(OffsetF());
332 dumpInfo_.globalLocation = position_;
333 // user-set offset
334 positionOffset_ = props->GetPositionOffset().value_or(OffsetF());
335 dumpInfo_.offset = positionOffset_;
336 if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
337 InitializePaddingAPI12(layoutWrapper);
338 } else {
339 InitializePadding(layoutWrapper);
340 }
341 InitializeParam(menuPattern);
342 dumpInfo_.originPlacement =
343 PlacementUtils::ConvertPlacementToString(props->GetMenuPlacement().value_or(Placement::NONE));
344 placement_ = props->GetMenuPlacement().value_or(Placement::BOTTOM_LEFT);
345 if ((menuPattern->IsSelectOverlayExtensionMenu() || menuPattern->IsSubMenu()) &&
346 Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
347 placement_ = props->GetMenuPlacement().value_or(Placement::BOTTOM_RIGHT);
348 }
349 ModifyPositionToWrapper(layoutWrapper, position_);
350 if (menuPattern->GetPreviewMode() != MenuPreviewMode::NONE) {
351 ModifyPreviewMenuPlacement(layoutWrapper);
352 }
353 dumpInfo_.defaultPlacement = PlacementUtils::ConvertPlacementToString(placement_);
354 InitSpace(props, menuPattern);
355 }
356
InitializeParam(const RefPtr<MenuPattern> & menuPattern)357 void MenuLayoutAlgorithm::InitializeParam(const RefPtr<MenuPattern>& menuPattern)
358 {
359 auto pipelineContext = GetCurrentPipelineContext();
360 CHECK_NULL_VOID(pipelineContext);
361 auto safeAreaManager = pipelineContext->GetSafeAreaManager();
362 CHECK_NULL_VOID(safeAreaManager);
363 auto safeAreaInsets = safeAreaManager->GetSafeAreaWithoutProcess();
364 auto top = safeAreaInsets.top_.Length();
365 auto props = menuPattern->GetLayoutProperty<MenuLayoutProperty>();
366 CHECK_NULL_VOID(props);
367 auto bottom = GetBottomBySafeAreaManager(safeAreaManager, props, menuPattern);
368 auto menuWindowRect = GetMenuWindowRectInfo(menuPattern);
369 float windowsOffsetX = static_cast<float>(menuWindowRect.GetOffset().GetX());
370 float windowsOffsetY = static_cast<float>(menuWindowRect.GetOffset().GetY());
371 SizeF windowGlobalSizeF(menuWindowRect.Width(), menuWindowRect.Height());
372 float topSecurity = 0.0f;
373 float bottomSecurity = 0.0f;
374 if (SystemProperties::GetDeviceOrientation() == DeviceOrientation::PORTRAIT) {
375 if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_TWELVE)) {
376 topSecurity = static_cast<float>(PORTRAIT_TOP_SECURITY_API12.ConvertToPx());
377 bottomSecurity = static_cast<float>(PORTRAIT_BOTTOM_SECURITY_API12.ConvertToPx());
378 } else {
379 topSecurity = static_cast<float>(PORTRAIT_TOP_SECURITY.ConvertToPx());
380 bottomSecurity = static_cast<float>(PORTRAIT_BOTTOM_SECURITY.ConvertToPx());
381 }
382 } else {
383 topSecurity = static_cast<float>(LANDSCAPE_TOP_SECURITY.ConvertToPx());
384 bottomSecurity = static_cast<float>(LANDSCAPE_BOTTOM_SECURITY.ConvertToPx());
385 }
386 if (canExpandCurrentWindow_) {
387 param_.windowsOffsetX = windowsOffsetX;
388 param_.windowsOffsetY = windowsOffsetY;
389 } else {
390 param_.windowsOffsetX = 0;
391 param_.windowsOffsetY = 0;
392 }
393 param_.menuWindowRect = menuWindowRect;
394 param_.windowGlobalSizeF = windowGlobalSizeF;
395 param_.top = top;
396 param_.bottom = bottom;
397 param_.left = safeAreaInsets.left_.Length();
398 param_.right = safeAreaInsets.right_.Length();
399 param_.topSecurity = topSecurity;
400 param_.bottomSecurity = bottomSecurity;
401 param_.previewMenuGap = targetSecurity_;
402
403 InitWrapperRect(props, menuPattern);
404 InitializeLayoutRegionMargin(menuPattern);
405 }
406
InitializeLayoutRegionMargin(const RefPtr<MenuPattern> & menuPattern)407 void MenuLayoutAlgorithm::InitializeLayoutRegionMargin(const RefPtr<MenuPattern>& menuPattern)
408 {
409 CHECK_NULL_VOID(menuPattern);
410 auto menuWrapper = menuPattern->GetMenuWrapper();
411 CHECK_NULL_VOID(menuWrapper);
412 auto menuWrapperPattern = menuWrapper->GetPattern<MenuWrapperPattern>();
413 CHECK_NULL_VOID(menuWrapperPattern);
414
415 auto menuParam = menuWrapperPattern->GetMenuParam();
416 isPreviewContainScale_ = menuParam.isPreviewContainScale;
417 if (!menuParam.layoutRegionMargin.has_value()) {
418 return;
419 }
420
421 auto marginProps = menuParam.layoutRegionMargin.value();
422 float left = marginProps.start.has_value()
423 ? marginProps.start.value().GetDimension().ConvertToPxWithSize(wrapperSize_.Width())
424 : paddingStart_;
425 float right = marginProps.end.has_value()
426 ? marginProps.end.value().GetDimension().ConvertToPxWithSize(wrapperSize_.Width())
427 : paddingEnd_;
428 float top = marginProps.top.has_value()
429 ? marginProps.top.value().GetDimension().ConvertToPxWithSize(wrapperSize_.Height())
430 : param_.topSecurity;
431 float bottom = marginProps.bottom.has_value()
432 ? marginProps.bottom.value().GetDimension().ConvertToPxWithSize(wrapperSize_.Height())
433 : param_.bottomSecurity;
434
435 if (LessNotEqual(left + right, wrapperSize_.Width())) {
436 paddingStart_ = left;
437 paddingEnd_ = right;
438 if (marginProps.start.has_value()) {
439 layoutRegionMargin_.left = left;
440 }
441 if (marginProps.end.has_value()) {
442 layoutRegionMargin_.right = right;
443 }
444 }
445
446 if (LessNotEqual(top + bottom, wrapperSize_.Height())) {
447 param_.topSecurity = top;
448 param_.bottomSecurity = bottom;
449 if (marginProps.top.has_value()) {
450 layoutRegionMargin_.top = top;
451 }
452 if (marginProps.bottom.has_value()) {
453 layoutRegionMargin_.bottom = bottom;
454 }
455 }
456 }
457
InitWrapperRect(const RefPtr<MenuLayoutProperty> & props,const RefPtr<MenuPattern> & menuPattern)458 void MenuLayoutAlgorithm::InitWrapperRect(
459 const RefPtr<MenuLayoutProperty>& props, const RefPtr<MenuPattern>& menuPattern)
460 {
461 wrapperRect_.SetRect(0, 0, param_.menuWindowRect.Width(), param_.menuWindowRect.Height());
462 auto pipelineContext = GetCurrentPipelineContext();
463 CHECK_NULL_VOID(pipelineContext);
464 auto safeAreaManager = pipelineContext->GetSafeAreaManager();
465 CHECK_NULL_VOID(safeAreaManager);
466 // system safeArea(AvoidAreaType.TYPE_SYSTEM) only include status bar,now the bottom is 0
467 auto safeAreaInsets = safeAreaManager->GetSafeAreaWithoutProcess();
468 auto bottom = GetBottomBySafeAreaManager(safeAreaManager, props, menuPattern);
469 auto top = safeAreaInsets.top_.Length();
470 auto left = safeAreaInsets.left_.Length();
471 auto right = safeAreaInsets.right_.Length();
472 dumpInfo_.top = top;
473 dumpInfo_.bottom = bottom;
474 double width = param_.menuWindowRect.Width();
475 double height = param_.menuWindowRect.Height();
476 auto windowManager = pipelineContext->GetWindowManager();
477 auto isContainerModal = pipelineContext->GetWindowModal() == WindowModal::CONTAINER_MODAL && windowManager &&
478 windowManager->GetWindowMode() == WindowMode::WINDOW_MODE_FLOATING;
479 if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN)) {
480 if (!canExpandCurrentWindow_ && isContainerModal) {
481 LimitContainerModalMenuRect(width, height);
482 }
483 }
484 wrapperRect_.SetRect(left, top, width - left - right, height - top - bottom);
485 wrapperSize_ = SizeF(wrapperRect_.Width(), wrapperRect_.Height());
486 dumpInfo_.wrapperRect = wrapperRect_;
487 }
488
GetBottomBySafeAreaManager(const RefPtr<SafeAreaManager> & safeAreaManager,const RefPtr<MenuLayoutProperty> & props,const RefPtr<MenuPattern> & menuPattern)489 uint32_t MenuLayoutAlgorithm::GetBottomBySafeAreaManager(const RefPtr<SafeAreaManager>& safeAreaManager,
490 const RefPtr<MenuLayoutProperty>& props, const RefPtr<MenuPattern>& menuPattern)
491 {
492 auto safeAreaInsets = safeAreaManager->GetSafeAreaWithoutProcess();
493 auto bottom = safeAreaInsets.bottom_.Length();
494 auto keyboardHeight = safeAreaManager->GetKeyboardInset().Length();
495 if ((menuPattern->IsSelectOverlayExtensionMenu() || menuPattern->IsSelectOverlayRightClickMenu()) &&
496 GreatNotEqual(keyboardHeight, 0)) {
497 bottom = keyboardHeight;
498 }
499
500 // Determine whether the menu is an AI menu
501 if (props->GetIsRectInTargetValue(false) && !safeAreaManager->IsNeedAvoidWindow()) {
502 if (LessOrEqual(keyboardHeight, 0)) {
503 keyboardHeight = safeAreaManager->GetkeyboardHeightConsideringUIExtension();
504 }
505 if (GreatNotEqual(keyboardHeight, 0)) {
506 bottom = keyboardHeight;
507 }
508 }
509 return bottom;
510 }
511
InitSpace(const RefPtr<MenuLayoutProperty> & props,const RefPtr<MenuPattern> & menuPattern)512 void MenuLayoutAlgorithm::InitSpace(const RefPtr<MenuLayoutProperty>& props, const RefPtr<MenuPattern>& menuPattern)
513 {
514 auto targetSize = props->GetTargetSizeValue(SizeF());
515 if (props->GetMenuPlacement().has_value()) {
516 auto targetSecurity = targetSecurity_;
517 topSpace_ = std::max(0.0, targetOffset_.GetY() - targetSecurity - paddingTop_ - wrapperRect_.Top());
518 bottomSpace_ = std::max(
519 0.0, wrapperRect_.Bottom() - targetOffset_.GetY() - targetSize_.Height() - targetSecurity - paddingBottom_);
520 if (NearZero(topSpace_) && NearZero(bottomSpace_)) {
521 bottomSpace_ = wrapperRect_.Bottom() - position_.GetY() - paddingTop_;
522 }
523 leftSpace_ = std::max(0.0, wrapperRect_.Left() + targetOffset_.GetX() - paddingStart_ - targetSecurity);
524 rightSpace_ =
525 std::max(0.0, wrapperRect_.Right() - targetSize_.Width() - targetSecurity - paddingStart_ - paddingEnd_);
526 if (NearZero(leftSpace_) && NearZero(rightSpace_)) {
527 leftSpace_ = position_.GetX();
528 rightSpace_ = wrapperRect_.Right() - leftSpace_;
529 }
530 } else {
531 if (canExpandCurrentWindow_ || !Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN)) {
532 topSpace_ = position_.GetY() - targetSize.Height() - paddingTop_ - wrapperRect_.Top();
533 bottomSpace_ = wrapperRect_.Bottom() - position_.GetY() - paddingBottom_;
534 } else {
535 topSpace_ = position_.GetY() - wrapperRect_.Top() - paddingTop_;
536 bottomSpace_ = wrapperRect_.Bottom() - position_.GetY() - paddingTop_;
537 }
538 leftSpace_ = position_.GetX() - paddingStart_;
539 rightSpace_ = wrapperRect_.Right() - position_.GetX() - paddingEnd_;
540 }
541 }
542
InitializePadding(LayoutWrapper * layoutWrapper)543 void MenuLayoutAlgorithm::InitializePadding(LayoutWrapper* layoutWrapper)
544 {
545 auto menuNode = layoutWrapper->GetHostNode();
546 CHECK_NULL_VOID(menuNode);
547 auto menuPattern = menuNode->GetPattern<MenuPattern>();
548 CHECK_NULL_VOID(menuPattern);
549 auto pipeline = PipelineBase::GetCurrentContext();
550 CHECK_NULL_VOID(pipeline);
551 auto theme = pipeline->GetTheme<SelectTheme>();
552 CHECK_NULL_VOID(theme);
553 margin_ = static_cast<float>(theme->GetOutPadding().ConvertToPx());
554 optionPadding_ = margin_;
555 paddingStart_ = static_cast<float>(theme->GetDefaultPaddingStart().ConvertToPx());
556 paddingEnd_ = static_cast<float>(theme->GetDefaultPaddingEnd().ConvertToPx());
557 paddingTop_ = static_cast<float>(theme->GetDefaultPaddingTop().ConvertToPx());
558 paddingBottom_ = static_cast<float>(theme->GetDefaultPaddingBottomFixed().ConvertToPx());
559 }
560
InitializePaddingAPI12(LayoutWrapper * layoutWrapper)561 void MenuLayoutAlgorithm::InitializePaddingAPI12(LayoutWrapper* layoutWrapper)
562 {
563 auto menuNode = layoutWrapper->GetHostNode();
564 CHECK_NULL_VOID(menuNode);
565 auto menuPattern = menuNode->GetPattern<MenuPattern>();
566 CHECK_NULL_VOID(menuPattern);
567 auto pipeline = PipelineBase::GetCurrentContext();
568 CHECK_NULL_VOID(pipeline);
569 auto theme = pipeline->GetTheme<SelectTheme>();
570 CHECK_NULL_VOID(theme);
571
572 margin_ = static_cast<float>(theme->GetOutPadding().ConvertToPx());
573 optionPadding_ = margin_;
574 if (!canExpandCurrentWindow_) {
575 paddingStart_ = static_cast<float>(theme->GetMenuLargeMargin().ConvertToPx());
576 paddingEnd_ = static_cast<float>(theme->GetMenuLargeMargin().ConvertToPx());
577 } else {
578 paddingStart_ = static_cast<float>(theme->GetMenuMediumMargin().ConvertToPx());
579 paddingEnd_ = static_cast<float>(theme->GetMenuMediumMargin().ConvertToPx());
580 }
581 }
582
ModifyPositionToWrapper(LayoutWrapper * layoutWrapper,OffsetF & position)583 void MenuLayoutAlgorithm::ModifyPositionToWrapper(LayoutWrapper* layoutWrapper, OffsetF& position)
584 {
585 auto menu = layoutWrapper->GetHostNode();
586 CHECK_NULL_VOID(menu);
587 auto wrapper = AceType::DynamicCast<FrameNode>(menu->GetParent());
588 CHECK_NULL_VOID(wrapper);
589
590 OffsetF wrapperOffset;
591 // minus wrapper offset in LayoutFullScreen
592 auto wrapperLayoutProps = wrapper->GetLayoutProperty();
593 CHECK_NULL_VOID(wrapperLayoutProps);
594 auto&& safeAreaInsets = wrapperLayoutProps->GetSafeAreaInsets();
595 if (safeAreaInsets) {
596 wrapperOffset +=
597 OffsetF(static_cast<float>(safeAreaInsets->left_.end), static_cast<float>(safeAreaInsets->top_.end));
598 position -= wrapperOffset;
599 }
600
601 auto menuPattern = menu->GetPattern<MenuPattern>();
602 CHECK_NULL_VOID(menuPattern);
603 bool isSubMenu = menuPattern->IsSubMenu() || menuPattern->IsSelectOverlaySubMenu();
604 if ((menuPattern->IsContextMenu() || (isSubMenu && Container::CurrentId() >= MIN_SUBCONTAINER_ID) ||
605 canExpandCurrentWindow_) &&
606 (targetTag_ != V2::SELECT_ETS_TAG)) {
607 // no need to modify for context menu, because context menu wrapper is full screen.
608 return;
609 }
610 // minus wrapper offset in floating window
611 auto pipelineContext = GetCurrentPipelineContext();
612 CHECK_NULL_VOID(pipelineContext);
613 auto windowManager = pipelineContext->GetWindowManager();
614 auto isContainerModal = pipelineContext->GetWindowModal() == WindowModal::CONTAINER_MODAL;
615 if (isContainerModal) {
616 wrapperOffset = OffsetF(0.0f, static_cast<float>(pipelineContext->GetCustomTitleHeight().ConvertToPx()));
617 if (windowManager && windowManager->GetWindowMode() == WindowMode::WINDOW_MODE_FLOATING) {
618 wrapperOffset += OffsetF(static_cast<float>((CONTAINER_BORDER_WIDTH + CONTENT_PADDING).ConvertToPx()),
619 static_cast<float>(CONTAINER_BORDER_WIDTH.ConvertToPx()));
620 }
621 position -= wrapperOffset;
622 }
623 }
624
625 // Called to perform layout render node and child.
Measure(LayoutWrapper * layoutWrapper)626 void MenuLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper)
627 {
628 // initialize screen size and menu position
629 CHECK_NULL_VOID(layoutWrapper);
630 MenuDumpInfo dumpInfo;
631 auto menuNode = layoutWrapper->GetHostNode();
632 CHECK_NULL_VOID(menuNode);
633 auto menuPattern = menuNode->GetPattern<MenuPattern>();
634 CHECK_NULL_VOID(menuPattern);
635 auto menuLayoutProperty = AceType::DynamicCast<MenuLayoutProperty>(layoutWrapper->GetLayoutProperty());
636 CHECK_NULL_VOID(menuLayoutProperty);
637 auto isShowInSubWindow = menuLayoutProperty->GetShowInSubWindowValue(true);
638 InitCanExpandCurrentWindow(isShowInSubWindow);
639 Initialize(layoutWrapper);
640 if (!targetTag_.empty()) {
641 InitTargetSizeAndPosition(layoutWrapper, menuPattern->IsContextMenu(), menuPattern);
642 }
643
644 const auto& constraint = menuLayoutProperty->GetLayoutConstraint();
645 if (!constraint)
646 return;
647
648 auto idealSize = CreateIdealSize(
649 constraint.value(), Axis::VERTICAL, menuLayoutProperty->GetMeasureType(MeasureType::MATCH_CONTENT), true);
650 const auto& padding = menuLayoutProperty->CreatePaddingAndBorder();
651 MinusPaddingToSize(padding, idealSize);
652
653 // calculate menu main size
654 auto childConstraint = CreateChildConstraint(layoutWrapper);
655 if (menuPattern->IsSelectMenu() && menuPattern->GetHasOptionWidth()) {
656 auto selectMenuWidth = menuPattern->GetSelectMenuWidth();
657 childConstraint.maxSize.SetWidth(selectMenuWidth);
658 childConstraint.parentIdealSize.SetWidth(selectMenuWidth);
659 childConstraint.selfIdealSize.SetWidth(selectMenuWidth);
660 }
661
662 // The menu width child Constraint is added to the 2in1 device in API13
663 UpdateChildConstraintByDevice(menuPattern, childConstraint, constraint.value());
664
665 auto parentItem = menuPattern->GetParentMenuItem();
666 CalculateIdealSize(layoutWrapper, childConstraint, padding, idealSize, parentItem);
667 }
668
CheckChildConstraintCondition(const RefPtr<MenuPattern> & menuPattern)669 bool MenuLayoutAlgorithm::CheckChildConstraintCondition(const RefPtr<MenuPattern>& menuPattern)
670 {
671 CHECK_NULL_RETURN(menuPattern, false);
672 CHECK_NULL_RETURN(Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_THIRTEEN), false);
673 if (menuPattern->IsSubMenu()) {
674 auto parentItem = menuPattern->GetParentMenuItem();
675 CHECK_NULL_RETURN(parentItem, false);
676 auto parentPattern = parentItem->GetPattern<MenuItemPattern>();
677 CHECK_NULL_RETURN(parentPattern, false);
678 auto expandingMode = parentPattern->GetExpandingMode();
679 if (expandingMode == SubMenuExpandingMode::SIDE) {
680 return true;
681 }
682 return false;
683 }
684
685 if (menuPattern->IsMenu() || menuPattern->IsContextMenu()) {
686 return true;
687 }
688 return false;
689 }
690
UpdateChildConstraintByDevice(const RefPtr<MenuPattern> & menuPattern,LayoutConstraintF & childConstraint,const LayoutConstraintF & layoutConstraint)691 void MenuLayoutAlgorithm::UpdateChildConstraintByDevice(const RefPtr<MenuPattern>& menuPattern,
692 LayoutConstraintF& childConstraint, const LayoutConstraintF& layoutConstraint)
693 {
694 CHECK_NULL_VOID(menuPattern);
695
696 // only 2in1 device has restrictions on the menu width in API13
697 if (!CheckChildConstraintCondition(menuPattern)) {
698 return;
699 }
700 auto pipeline = PipelineBase::GetCurrentContext();
701 CHECK_NULL_VOID(pipeline);
702 auto theme = pipeline->GetTheme<SelectTheme>();
703 CHECK_NULL_VOID(theme);
704
705 auto expandDisplay = theme->GetExpandDisplay();
706 CHECK_NULL_VOID(expandDisplay);
707
708 auto menuMinWidth = theme->GetMenuMinWidth().ConvertToPx();
709 auto menuDefaultWidth = theme->GetMenuDefaultWidth().ConvertToPx();
710 auto menuMaxWidth = theme->GetMenuMaxWidthRatio() * SystemProperties::GetDeviceWidth();
711 double minWidth = 0.0f;
712 double maxWidth = 0.0f;
713
714 auto firstMenu = menuPattern->GetFirstInnerMenu();
715 CHECK_NULL_VOID(firstMenu);
716 auto layoutProperty = firstMenu->GetLayoutProperty<MenuLayoutProperty>();
717 CHECK_NULL_VOID(layoutProperty);
718 if (layoutProperty->HasMenuWidth()) {
719 auto menuWidth = layoutProperty->GetMenuWidthValue();
720 auto menuWidthPX = (menuWidth.Unit() == DimensionUnit::PERCENT) ?
721 menuWidth.Value() * layoutConstraint.percentReference.Width() : menuWidth.ConvertToPx();
722 if (LessNotEqual(menuWidthPX, menuMinWidth) || GreatNotEqual(menuWidthPX, menuMaxWidth)) {
723 minWidth = menuDefaultWidth;
724 maxWidth = menuMaxWidth;
725 } else {
726 minWidth = menuWidthPX;
727 maxWidth = menuWidthPX;
728 }
729 } else {
730 minWidth = menuDefaultWidth;
731 maxWidth = menuMaxWidth;
732 }
733 childConstraint.minSize.SetWidth(minWidth);
734 childConstraint.maxSize.SetWidth(maxWidth);
735 }
736
CalculateIdealSize(LayoutWrapper * layoutWrapper,LayoutConstraintF & childConstraint,PaddingPropertyF padding,SizeF & idealSize,RefPtr<FrameNode> parentItem)737 void MenuLayoutAlgorithm::CalculateIdealSize(LayoutWrapper* layoutWrapper, LayoutConstraintF& childConstraint,
738 PaddingPropertyF padding, SizeF& idealSize, RefPtr<FrameNode> parentItem)
739 {
740 if (parentItem != nullptr) {
741 auto parentPattern = parentItem->GetPattern<MenuItemPattern>();
742 CHECK_NULL_VOID(parentPattern);
743 auto expandingMode = parentPattern->GetExpandingMode();
744 if (expandingMode == SubMenuExpandingMode::STACK) {
745 auto parentPattern = parentItem->GetPattern<MenuItemPattern>();
746 CHECK_NULL_VOID(parentPattern);
747 auto parentMenu = parentPattern->GetMenu();
748 auto parentWidth = parentMenu->GetGeometryNode()->GetFrameSize().Width();
749 childConstraint.minSize.SetWidth(parentWidth);
750 childConstraint.maxSize.SetWidth(parentWidth);
751 childConstraint.selfIdealSize.SetWidth(parentWidth);
752 }
753 }
754
755 float idealHeight = 0.0f;
756 float idealWidth = 0.0f;
757 auto host = layoutWrapper->GetHostNode();
758 CHECK_NULL_VOID(host);
759 auto pattern = host->GetPattern<MenuPattern>();
760 CHECK_NULL_VOID(pattern);
761 std::list<RefPtr<LayoutWrapper>> builderChildList;
762 for (const auto& child : layoutWrapper->GetAllChildrenWithBuild()) {
763 if (pattern->UseContentModifier()) {
764 if (child->GetHostNode()->GetId() != pattern->GetBuilderId()) {
765 child->GetGeometryNode()->Reset();
766 child->GetGeometryNode()->SetContentSize(SizeF());
767 } else {
768 child->Measure(childConstraint);
769 builderChildList.push_back(child);
770 }
771 BoxLayoutAlgorithm::PerformMeasureSelfWithChildList(layoutWrapper, builderChildList);
772 } else {
773 child->Measure(childConstraint);
774 }
775 auto childSize = child->GetGeometryNode()->GetMarginFrameSize();
776 idealHeight += childSize.Height();
777 idealWidth = std::max(idealWidth, childSize.Width());
778 }
779 idealSize.SetHeight(idealHeight);
780 idealSize.SetWidth(idealWidth);
781 AddPaddingToSize(padding, idealSize);
782
783 auto geometryNode = layoutWrapper->GetGeometryNode();
784 CHECK_NULL_VOID(geometryNode);
785 geometryNode->SetFrameSize(idealSize);
786 }
787
CheckPreviewConstraint(const RefPtr<FrameNode> & frameNode,const Rect & menuWindowRect)788 void MenuLayoutAlgorithm::CheckPreviewConstraint(const RefPtr<FrameNode>& frameNode, const Rect& menuWindowRect)
789 {
790 CHECK_NULL_VOID(
791 frameNode && (frameNode->GetTag() == V2::MENU_PREVIEW_ETS_TAG || frameNode->GetTag() == V2::FLEX_ETS_TAG));
792 auto geometryNode = frameNode->GetGeometryNode();
793 CHECK_NULL_VOID(geometryNode);
794
795 auto maxWidth = wrapperSize_.Width();
796 if (layoutRegionMargin_.left.has_value() || layoutRegionMargin_.right.has_value()) {
797 maxWidth = std::max(0.0f, wrapperSize_.Width() - paddingStart_ - paddingEnd_) / previewScale_;
798 } else {
799 RefPtr<GridColumnInfo> columnInfo = GridSystemManager::GetInstance().GetInfoByType(GridColumnType::MENU);
800 CHECK_NULL_VOID(columnInfo);
801 auto parent = columnInfo->GetParent();
802 CHECK_NULL_VOID(parent);
803 parent->BuildColumnWidth(std::min(menuWindowRect.Width(), menuWindowRect.Height()));
804 maxWidth = static_cast<float>(columnInfo->GetWidth(GRID_COUNTS_4)) / previewScale_;
805 }
806
807 auto frameSize = geometryNode->GetMarginFrameSize();
808 if (!isPreviewContainScale_) {
809 static SizeF previewSize;
810 static int32_t hostId = -1;
811 if (previewSize == SizeF(0.0f, 0.0f) || hostId != frameNode->GetId()) {
812 previewSize = frameSize;
813 hostId = frameNode->GetId();
814 } else {
815 frameSize = previewSize;
816 }
817 }
818
819 if (LessOrEqual(frameSize.Width(), maxWidth)) {
820 geometryNode->SetFrameSize(SizeF(frameSize.Width(), frameSize.Height()));
821 } else if (isPreviewContainScale_) {
822 geometryNode->SetFrameSize(SizeF(maxWidth, frameSize.Height() * (maxWidth / frameSize.Width())));
823 } else {
824 geometryNode->SetFrameSize(SizeF(maxWidth, frameSize.Height()));
825 }
826 }
827
GetPreviewNodeTotalSize(const RefPtr<LayoutWrapper> & child,const Rect & menuWindowRect,RefPtr<LayoutWrapper> & previewLayoutWrapper,SizeF & size,bool isShowHoverImage)828 void MenuLayoutAlgorithm::GetPreviewNodeTotalSize(const RefPtr<LayoutWrapper>& child, const Rect& menuWindowRect,
829 RefPtr<LayoutWrapper>& previewLayoutWrapper, SizeF& size, bool isShowHoverImage)
830 {
831 auto hostNode = child->GetHostNode();
832 auto geometryNode = child->GetGeometryNode();
833 if (!hostNode || !geometryNode) {
834 return;
835 }
836
837 bool isImageNode = hostNode->GetTag() == V2::IMAGE_ETS_TAG;
838 bool isPreviewNode = hostNode->GetTag() == V2::MENU_PREVIEW_ETS_TAG;
839 bool isFlexNode = hostNode->GetTag() == V2::FLEX_ETS_TAG;
840 if (!isPreviewNode && !isImageNode && !isFlexNode) {
841 return;
842 }
843
844 if (isImageNode && isShowHoverImage) {
845 return;
846 }
847
848 auto frameSize = geometryNode->GetMarginFrameSize();
849 if (isPreviewNode || isFlexNode) {
850 CheckPreviewConstraint(hostNode, menuWindowRect);
851 } else {
852 geometryNode->SetFrameSize(frameSize);
853 }
854 frameSize = geometryNode->GetMarginFrameSize() * previewScale_;
855 auto widthLeftSpace = menuWindowRect.Width() - paddingStart_ - paddingEnd_;
856 if (GreatNotEqual(frameSize.Width(), widthLeftSpace)) {
857 auto unitSpace = widthLeftSpace / frameSize.Width() / previewScale_;
858 geometryNode->SetFrameSize(SizeF(widthLeftSpace / previewScale_, unitSpace * frameSize.Height()));
859 frameSize = geometryNode->GetMarginFrameSize() * previewScale_;
860 }
861 previewLayoutWrapper = child;
862 size += frameSize;
863 }
864
GetPreviewNodeAndMenuNodeTotalSize(const RefPtr<FrameNode> & frameNode,RefPtr<LayoutWrapper> & previewLayoutWrapper,RefPtr<LayoutWrapper> & menuLayoutWrapper)865 SizeF MenuLayoutAlgorithm::GetPreviewNodeAndMenuNodeTotalSize(const RefPtr<FrameNode>& frameNode,
866 RefPtr<LayoutWrapper>& previewLayoutWrapper, RefPtr<LayoutWrapper>& menuLayoutWrapper)
867 {
868 SizeF size;
869 CHECK_NULL_RETURN(frameNode, size);
870 auto pipelineContext = GetCurrentPipelineContext();
871 CHECK_NULL_RETURN(pipelineContext, size);
872 bool isShowHoverImage = false;
873 for (auto& child : frameNode->GetAllChildrenWithBuild()) {
874 auto hostNode = child->GetHostNode();
875 auto geometryNode = child->GetGeometryNode();
876 if (!hostNode || !geometryNode) {
877 continue;
878 }
879 GetPreviewNodeTotalSize(child, param_.menuWindowRect, previewLayoutWrapper, size, isShowHoverImage);
880 auto menuPattern = hostNode->GetPattern<MenuPattern>();
881 if (hostNode->GetTag() == V2::MENU_ETS_TAG && menuPattern && !menuPattern->IsSubMenu()) {
882 menuLayoutWrapper = child;
883 size += geometryNode->GetMarginFrameSize();
884 isShowHoverImage = menuPattern->GetIsShowHoverImage();
885 }
886 }
887 return size;
888 }
889
LayoutNormalTopPreviewBottomMenuLessThan(const RefPtr<GeometryNode> & previewGeometryNode,const RefPtr<GeometryNode> & menuGeometryNode,SizeF & totalSize)890 void MenuLayoutAlgorithm::LayoutNormalTopPreviewBottomMenuLessThan(
891 const RefPtr<GeometryNode>& previewGeometryNode, const RefPtr<GeometryNode>& menuGeometryNode, SizeF& totalSize)
892 {
893 CHECK_NULL_VOID(previewGeometryNode);
894 CHECK_NULL_VOID(menuGeometryNode);
895
896 OffsetF center(
897 targetOffset_.GetX() + targetSize_.Width() / HALF, targetOffset_.GetY() + targetSize_.Height() / HALF);
898 targetCenterOffset_ = center;
899 auto previewSize = previewGeometryNode->GetMarginFrameSize() * previewScale_;
900 OffsetF offset(center.GetX() - previewSize.Width() / HALF,
901 std::min<float>(center.GetY() - previewSize.Height() / HALF, param_.windowGlobalSizeF.Height() -
902 param_.bottomSecurity - param_.bottom -
903 totalSize.Height() - param_.previewMenuGap));
904 auto x = std::clamp(offset.GetX(), static_cast<float>(wrapperRect_.Left()) + paddingStart_,
905 static_cast<float>(wrapperRect_.Right()) - paddingEnd_ - previewSize.Width());
906 auto y = std::clamp(offset.GetY(), static_cast<float>(wrapperRect_.Top()) + param_.topSecurity,
907 static_cast<float>(wrapperRect_.Bottom()) - param_.bottomSecurity - previewSize.Height());
908 x = x + (previewSize.Width() - previewSize.Width() / previewScale_) / HALF;
909 y = y + (previewSize.Height() - previewSize.Height() / previewScale_) / HALF;
910 previewGeometryNode->SetMarginFrameOffset(OffsetF(x, y));
911 }
912
LayoutNormalTopPreviewBottomMenuGreateThan(const RefPtr<GeometryNode> & previewGeometryNode,const RefPtr<GeometryNode> & menuGeometryNode,SizeF & totalSize)913 void MenuLayoutAlgorithm::LayoutNormalTopPreviewBottomMenuGreateThan(
914 const RefPtr<GeometryNode>& previewGeometryNode, const RefPtr<GeometryNode>& menuGeometryNode, SizeF& totalSize)
915 {
916 CHECK_NULL_VOID(previewGeometryNode);
917 CHECK_NULL_VOID(menuGeometryNode);
918
919 OffsetF center(targetOffset_.GetX() + targetSize_.Width() / 2, targetOffset_.GetY() + targetSize_.Height() / 2);
920 targetCenterOffset_ = center;
921 auto previewSize = previewGeometryNode->GetMarginFrameSize() * previewScale_;
922 auto menuHeight = totalSize.Height() - previewSize.Height();
923 auto previewHalfHeight = previewSize.Height() / 2;
924 if (LessNotEqual(menuHeight, previewHalfHeight)) {
925 auto menuSize = menuGeometryNode->GetMarginFrameSize();
926 if (GreatNotEqual(param_.menuItemTotalHeight, previewHalfHeight)) {
927 menuGeometryNode->SetFrameSize(SizeF(menuSize.Width(), previewHalfHeight));
928 totalSize = SizeF(totalSize.Width(), previewHalfHeight + previewSize.Height());
929 } else {
930 menuGeometryNode->SetFrameSize(SizeF(menuSize.Width(), param_.menuItemTotalHeight));
931 totalSize = SizeF(totalSize.Width(), param_.menuItemTotalHeight + previewSize.Height());
932 }
933 }
934
935 auto heightLeftSpace = wrapperRect_.Height() - param_.topSecurity - param_.bottomSecurity - param_.previewMenuGap;
936 auto delta = totalSize.Height() - heightLeftSpace;
937 if (GreatNotEqual(delta, 0.0f)) {
938 menuHeight = totalSize.Height() - previewSize.Height();
939 float unitSpace = 0.0f;
940 if (LessNotEqual(menuHeight, previewHalfHeight)) {
941 unitSpace = delta / previewSize.Height();
942 } else {
943 unitSpace = delta / totalSize.Height();
944 auto menuSize = menuGeometryNode->GetMarginFrameSize();
945 menuGeometryNode->SetFrameSize(SizeF(menuSize.Width(), (1 - unitSpace) * menuSize.Height()));
946 }
947 previewGeometryNode->SetFrameSize(SizeF((1 - unitSpace) * previewSize.Width() / previewScale_,
948 (1 - unitSpace) * previewSize.Height() / previewScale_));
949 totalSize = totalSize - SizeF(0.0f, delta);
950 }
951 previewSize = previewGeometryNode->GetMarginFrameSize() * previewScale_;
952 OffsetF offset(center.GetX() - previewSize.Width() / 2, 0.0f);
953 auto x = std::clamp(offset.GetX(), static_cast<float>(wrapperRect_.Left()) + paddingStart_,
954 static_cast<float>(wrapperRect_.Right()) - paddingEnd_ - previewSize.Width());
955 auto y = std::clamp(offset.GetY(), static_cast<float>(wrapperRect_.Top()) + param_.topSecurity,
956 static_cast<float>(wrapperRect_.Bottom()) - param_.bottomSecurity - previewSize.Height());
957 x = x + (previewSize.Width() - previewSize.Width() / previewScale_) / 2;
958 y = y + (previewSize.Height() - previewSize.Height() / previewScale_) / 2;
959 previewGeometryNode->SetMarginFrameOffset(OffsetF(x, y));
960 }
961
LayoutNormalTopPreviewBottomMenu(const RefPtr<GeometryNode> & previewGeometryNode,const RefPtr<GeometryNode> & menuGeometryNode,SizeF & totalSize,float menuItemTotalHeight)962 void MenuLayoutAlgorithm::LayoutNormalTopPreviewBottomMenu(const RefPtr<GeometryNode>& previewGeometryNode,
963 const RefPtr<GeometryNode>& menuGeometryNode, SizeF& totalSize, float menuItemTotalHeight)
964 {
965 CHECK_NULL_VOID(previewGeometryNode);
966 CHECK_NULL_VOID(menuGeometryNode);
967 param_.menuItemTotalHeight = menuItemTotalHeight;
968 auto pipelineContext = GetCurrentPipelineContext();
969 CHECK_NULL_VOID(pipelineContext);
970 if (LessNotEqual(totalSize.Height() + targetSecurity_,
971 wrapperRect_.Height() - paddingTop_ - paddingBottom_ - param_.topSecurity - param_.bottomSecurity)) {
972 LayoutNormalTopPreviewBottomMenuLessThan(previewGeometryNode, menuGeometryNode, totalSize);
973 } else {
974 LayoutNormalTopPreviewBottomMenuGreateThan(previewGeometryNode, menuGeometryNode, totalSize);
975 }
976 auto previewSize = previewGeometryNode->GetMarginFrameSize();
977 auto securityHeight = wrapperRect_.Height() - param_.topSecurity - param_.bottomSecurity;
978 if (GreatNotEqual(param_.windowGlobalSizeF.Height(), param_.menuWindowRect.Height()) &&
979 GreatNotEqual(previewSize.Height(), securityHeight)) {
980 previewGeometryNode->SetFrameSize(SizeF(previewSize.Width(), securityHeight));
981 }
982 }
983
LayoutNormalBottomPreviewTopMenuLessThan(const RefPtr<GeometryNode> & previewGeometryNode,const RefPtr<GeometryNode> & menuGeometryNode,SizeF & totalSize)984 void MenuLayoutAlgorithm::LayoutNormalBottomPreviewTopMenuLessThan(
985 const RefPtr<GeometryNode>& previewGeometryNode, const RefPtr<GeometryNode>& menuGeometryNode, SizeF& totalSize)
986 {
987 CHECK_NULL_VOID(previewGeometryNode);
988 CHECK_NULL_VOID(menuGeometryNode);
989
990 OffsetF center(targetOffset_.GetX() + targetSize_.Width() / 2, targetOffset_.GetY() + targetSize_.Height() / 2);
991 targetCenterOffset_ = center;
992 auto previewSize = previewGeometryNode->GetMarginFrameSize() * previewScale_;
993 OffsetF offset(center.GetX() - previewSize.Width() / 2,
994 std::max<float>(center.GetY() - previewSize.Height() / 2,
995 param_.top + param_.topSecurity + totalSize.Height() - previewSize.Height() + param_.previewMenuGap));
996 auto x = std::clamp(offset.GetX(), static_cast<float>(wrapperRect_.Left()) + paddingStart_,
997 static_cast<float>(wrapperRect_.Right()) - previewSize.Width() - paddingEnd_);
998 auto y = std::clamp(offset.GetY(), static_cast<float>(wrapperRect_.Top()) + param_.topSecurity,
999 static_cast<float>(wrapperRect_.Bottom()) - param_.bottomSecurity - previewSize.Height());
1000 x = x + (previewSize.Width() - previewSize.Width() / previewScale_) / 2;
1001 y = y + (previewSize.Height() - previewSize.Height() / previewScale_) / 2;
1002 previewGeometryNode->SetMarginFrameOffset(OffsetF(x, y));
1003 }
1004
LayoutNormalBottomPreviewTopMenuGreateThan(const RefPtr<GeometryNode> & previewGeometryNode,const RefPtr<GeometryNode> & menuGeometryNode,SizeF & totalSize)1005 void MenuLayoutAlgorithm::LayoutNormalBottomPreviewTopMenuGreateThan(
1006 const RefPtr<GeometryNode>& previewGeometryNode, const RefPtr<GeometryNode>& menuGeometryNode, SizeF& totalSize)
1007 {
1008 CHECK_NULL_VOID(previewGeometryNode);
1009 CHECK_NULL_VOID(menuGeometryNode);
1010
1011 OffsetF center(targetOffset_.GetX() + targetSize_.Width() / 2, targetOffset_.GetY() + targetSize_.Height() / 2);
1012 targetCenterOffset_ = center;
1013 auto previewSize = previewGeometryNode->GetMarginFrameSize() * previewScale_;
1014 auto menuHeight = totalSize.Height() - previewSize.Height();
1015 auto previewHalfHeight = previewSize.Height() / 2;
1016 if (LessNotEqual(menuHeight, previewHalfHeight)) {
1017 auto menuSize = menuGeometryNode->GetMarginFrameSize();
1018 if (GreatNotEqual(param_.menuItemTotalHeight, previewHalfHeight)) {
1019 menuGeometryNode->SetFrameSize(SizeF(menuSize.Width(), previewHalfHeight));
1020 totalSize = SizeF(totalSize.Width(), previewHalfHeight + previewSize.Height());
1021 } else {
1022 menuGeometryNode->SetFrameSize(SizeF(menuSize.Width(), param_.menuItemTotalHeight));
1023 totalSize = SizeF(totalSize.Width(), param_.menuItemTotalHeight + previewSize.Height());
1024 }
1025 }
1026
1027 auto heightLeftSpace = param_.windowGlobalSizeF.Height() - param_.top - param_.topSecurity - param_.bottom -
1028 param_.bottomSecurity - param_.previewMenuGap;
1029 auto delta = totalSize.Height() - heightLeftSpace;
1030 if (GreatNotEqual(delta, 0.0f)) {
1031 menuHeight = totalSize.Height() - previewSize.Height();
1032 float unitSpace = 0.0f;
1033 if (LessNotEqual(menuHeight, previewHalfHeight)) {
1034 unitSpace = delta / previewSize.Height();
1035 } else {
1036 unitSpace = delta / totalSize.Height();
1037 auto menuSize = menuGeometryNode->GetMarginFrameSize();
1038 menuGeometryNode->SetFrameSize(SizeF(menuSize.Width(), (1 - unitSpace) * menuSize.Height()));
1039 }
1040 previewGeometryNode->SetFrameSize(SizeF((1 - unitSpace) * previewSize.Width() / previewScale_,
1041 (1 - unitSpace) * previewSize.Height() / previewScale_));
1042 totalSize = totalSize - SizeF(0.0f, delta);
1043 }
1044 previewSize = previewGeometryNode->GetMarginFrameSize() * previewScale_;
1045 OffsetF offset(center.GetX() - previewSize.Width() / 2,
1046 param_.windowGlobalSizeF.Height() - param_.bottomSecurity - param_.bottom - previewSize.Height());
1047
1048 auto x = std::clamp(offset.GetX(), static_cast<float>(wrapperRect_.Left()) + paddingStart_,
1049 static_cast<float>(wrapperRect_.Right()) - previewSize.Width() - paddingEnd_);
1050 auto y = std::clamp(offset.GetY(), static_cast<float>(wrapperRect_.Top()) + param_.topSecurity,
1051 static_cast<float>(wrapperRect_.Bottom()) - param_.bottomSecurity - previewSize.Height());
1052 x = x + (previewSize.Width() - previewSize.Width() / previewScale_) / 2;
1053 y = y + (previewSize.Height() - previewSize.Height() / previewScale_) / 2;
1054 previewGeometryNode->SetMarginFrameOffset(OffsetF(x, y));
1055 }
1056
LayoutNormalBottomPreviewTopMenu(const RefPtr<GeometryNode> & previewGeometryNode,const RefPtr<GeometryNode> & menuGeometryNode,SizeF & totalSize,float menuItemTotalHeight)1057 void MenuLayoutAlgorithm::LayoutNormalBottomPreviewTopMenu(const RefPtr<GeometryNode>& previewGeometryNode,
1058 const RefPtr<GeometryNode>& menuGeometryNode, SizeF& totalSize, float menuItemTotalHeight)
1059 {
1060 CHECK_NULL_VOID(previewGeometryNode);
1061 CHECK_NULL_VOID(menuGeometryNode);
1062 param_.menuItemTotalHeight = menuItemTotalHeight;
1063 auto pipelineContext = GetCurrentPipelineContext();
1064 CHECK_NULL_VOID(pipelineContext);
1065 if (LessNotEqual(totalSize.Height() + targetSecurity_, param_.windowGlobalSizeF.Height() - param_.topSecurity -
1066 param_.bottomSecurity - param_.top - param_.bottom)) {
1067 LayoutNormalBottomPreviewTopMenuLessThan(previewGeometryNode, menuGeometryNode, totalSize);
1068 } else {
1069 LayoutNormalBottomPreviewTopMenuGreateThan(previewGeometryNode, menuGeometryNode, totalSize);
1070 }
1071 auto previewSize = previewGeometryNode->GetMarginFrameSize();
1072 auto securityHeight =
1073 param_.menuWindowRect.Height() - param_.topSecurity - param_.top - param_.bottomSecurity - param_.bottom;
1074 if (GreatNotEqual(param_.windowGlobalSizeF.Height(), param_.menuWindowRect.Height()) &&
1075 GreatNotEqual(previewSize.Height(), securityHeight)) {
1076 previewGeometryNode->SetFrameSize(SizeF(previewSize.Width(), securityHeight));
1077 }
1078 }
1079
UpdateScrollAndColumnLayoutConstraint(const RefPtr<LayoutWrapper> & previewLayoutWrapper,const RefPtr<LayoutWrapper> & menuLayoutWrapper)1080 void MenuLayoutAlgorithm::UpdateScrollAndColumnLayoutConstraint(
1081 const RefPtr<LayoutWrapper>& previewLayoutWrapper, const RefPtr<LayoutWrapper>& menuLayoutWrapper)
1082 {
1083 CHECK_NULL_VOID(menuLayoutWrapper);
1084 CHECK_NULL_VOID(previewLayoutWrapper);
1085 RefPtr<GeometryNode> menuGeometryNode = menuLayoutWrapper->GetGeometryNode();
1086 RefPtr<GeometryNode> previewGeometryNode = previewLayoutWrapper->GetGeometryNode();
1087 CHECK_NULL_VOID(menuGeometryNode);
1088 CHECK_NULL_VOID(previewGeometryNode);
1089
1090 for (auto& child : menuLayoutWrapper->GetAllChildrenWithBuild()) {
1091 auto geometryNode = child->GetGeometryNode();
1092 if (!geometryNode) {
1093 continue;
1094 }
1095 auto frameSize = menuGeometryNode->GetMarginFrameSize();
1096 geometryNode->SetFrameSize(SizeF(frameSize.Width(), frameSize.Height()));
1097 auto layoutProperty = child->GetLayoutProperty();
1098 CHECK_NULL_VOID(layoutProperty);
1099 auto constraint = layoutProperty->GetLayoutConstraint();
1100 if (constraint.has_value()) {
1101 constraint.value().maxSize.SetWidth(frameSize.Width());
1102 constraint.value().maxSize.SetHeight(frameSize.Height());
1103 constraint.value().selfIdealSize.UpdateSizeWithCheck(SizeF(frameSize.Width(), frameSize.Height()));
1104 layoutProperty->UpdateLayoutConstraint(constraint.value());
1105 child->Measure(constraint);
1106 }
1107 }
1108
1109 for (auto& child : previewLayoutWrapper->GetAllChildrenWithBuild()) {
1110 auto hostNode = child->GetHostNode();
1111 auto geometryNode = child->GetGeometryNode();
1112 if (!hostNode || !geometryNode) {
1113 continue;
1114 }
1115 auto frameSize = previewGeometryNode->GetMarginFrameSize();
1116 geometryNode->SetFrameSize(SizeF(frameSize.Width(), frameSize.Height()));
1117 auto layoutProperty = child->GetLayoutProperty();
1118 CHECK_NULL_VOID(layoutProperty);
1119 auto constraint = layoutProperty->GetLayoutConstraint();
1120 if (constraint.has_value()) {
1121 constraint.value().maxSize.SetWidth(frameSize.Width());
1122 constraint.value().maxSize.SetHeight(frameSize.Height());
1123 constraint.value().selfIdealSize.UpdateSizeWithCheck(SizeF(frameSize.Width(), frameSize.Height()));
1124 layoutProperty->UpdateLayoutConstraint(constraint.value());
1125 hostNode->GetRenderContext()->SetClipToBounds(true);
1126 child->Measure(constraint);
1127 }
1128 }
1129 }
1130
GetMenuItemTotalHeight(const RefPtr<LayoutWrapper> & menuLayoutWrapper)1131 float MenuLayoutAlgorithm::GetMenuItemTotalHeight(const RefPtr<LayoutWrapper>& menuLayoutWrapper)
1132 {
1133 CHECK_NULL_RETURN(menuLayoutWrapper, 0.0f);
1134 RefPtr<GeometryNode> menuGeometryNode = menuLayoutWrapper->GetGeometryNode();
1135 CHECK_NULL_RETURN(menuGeometryNode, 0.0f);
1136 float height = 0.0f;
1137
1138 for (auto& child : menuLayoutWrapper->GetAllChildrenWithBuild()) {
1139 auto geometryNode = child->GetGeometryNode();
1140 if (!geometryNode) {
1141 continue;
1142 }
1143 for (auto& menuItem : child->GetAllChildrenWithBuild()) {
1144 auto itemHostnode = menuItem->GetHostNode();
1145 auto itemGeometryNode = menuItem->GetGeometryNode();
1146 if (!itemHostnode || !itemGeometryNode) {
1147 continue;
1148 }
1149 height += itemGeometryNode->GetMarginFrameSize().Height();
1150 }
1151 }
1152 auto menuHeight = menuGeometryNode->GetMarginFrameSize().Height();
1153 if (LessNotEqual(height, menuHeight)) {
1154 height = menuHeight;
1155 }
1156 return height;
1157 }
1158
LayoutOtherDeviceLeftPreviewRightMenuLessThan(const RefPtr<GeometryNode> & previewGeometryNode,const RefPtr<GeometryNode> & menuGeometryNode,SizeF & totalSize)1159 void MenuLayoutAlgorithm::LayoutOtherDeviceLeftPreviewRightMenuLessThan(
1160 const RefPtr<GeometryNode>& previewGeometryNode, const RefPtr<GeometryNode>& menuGeometryNode, SizeF& totalSize)
1161 {
1162 CHECK_NULL_VOID(previewGeometryNode);
1163 CHECK_NULL_VOID(menuGeometryNode);
1164
1165 OffsetF targetCenterOffset(
1166 targetOffset_.GetX() + targetSize_.Width() / 2, targetOffset_.GetY() + targetSize_.Height() / 2);
1167 targetCenterOffset_ = targetCenterOffset;
1168 auto previewSize = previewGeometryNode->GetMarginFrameSize() * previewScale_;
1169 auto heightLeftSpace =
1170 param_.windowGlobalSizeF.Height() - param_.top - param_.topSecurity - param_.bottomSecurity - param_.bottom;
1171 auto delta = previewSize.Height() - heightLeftSpace;
1172 if (GreatNotEqual(delta, 0.0f)) {
1173 auto unitSpace = delta / previewSize.Height();
1174 previewGeometryNode->SetFrameSize(SizeF((1 - unitSpace) * previewSize.Width() / previewScale_,
1175 (1 - unitSpace) * previewSize.Height() / previewScale_));
1176 totalSize = totalSize - SizeF(unitSpace * previewSize.Width(), unitSpace * previewSize.Height());
1177 }
1178 previewSize = previewGeometryNode->GetMarginFrameSize() * previewScale_;
1179 auto menuSize = menuGeometryNode->GetMarginFrameSize();
1180 menuGeometryNode->SetFrameSize(
1181 SizeF(menuSize.Width(), std::min<float>(param_.menuItemTotalHeight, heightLeftSpace)));
1182 menuSize = menuGeometryNode->GetMarginFrameSize();
1183 auto offsetX = targetCenterOffset.GetX() - previewSize.Width() / 2;
1184 auto offsetY = std::min<float>(targetCenterOffset.GetY() - previewSize.Height() / 2,
1185 param_.windowGlobalSizeF.Height() - param_.bottomSecurity - param_.bottom - menuSize.Height());
1186 auto x = std::clamp(offsetX, static_cast<float>(wrapperRect_.Left()) + paddingStart_,
1187 static_cast<float>(wrapperRect_.Right()) - previewSize.Width() - paddingEnd_);
1188 auto y = std::clamp(offsetY, static_cast<float>(wrapperRect_.Top()) + param_.topSecurity,
1189 static_cast<float>(wrapperRect_.Bottom()) - param_.bottomSecurity - previewSize.Height());
1190 x = x + (previewSize.Width() - previewSize.Width() / previewScale_) / 2;
1191 y = y + (previewSize.Height() - previewSize.Height() / previewScale_) / 2;
1192 previewGeometryNode->SetMarginFrameOffset(OffsetF(x, y));
1193 }
1194
LayoutOtherDeviceLeftPreviewRightMenuGreateThan(const RefPtr<GeometryNode> & previewGeometryNode,const RefPtr<GeometryNode> & menuGeometryNode,SizeF & totalSize)1195 void MenuLayoutAlgorithm::LayoutOtherDeviceLeftPreviewRightMenuGreateThan(
1196 const RefPtr<GeometryNode>& previewGeometryNode, const RefPtr<GeometryNode>& menuGeometryNode, SizeF& totalSize)
1197 {
1198 CHECK_NULL_VOID(previewGeometryNode);
1199 CHECK_NULL_VOID(menuGeometryNode);
1200
1201 OffsetF targetCenterOffset(
1202 targetOffset_.GetX() + targetSize_.Width() / 2, targetOffset_.GetY() + targetSize_.Height() / 2);
1203 targetCenterOffset_ = targetCenterOffset;
1204 auto previewSize = previewGeometryNode->GetMarginFrameSize() * previewScale_;
1205 auto widthLeftSpace = param_.windowGlobalSizeF.Width() - paddingStart_ - paddingEnd_ - param_.previewMenuGap -
1206 param_.left - param_.right;
1207 auto delta = totalSize.Width() - widthLeftSpace;
1208 if (GreatNotEqual(delta, 0.0f)) {
1209 auto unitSpace = delta / previewSize.Width();
1210 previewGeometryNode->SetFrameSize(SizeF((1 - unitSpace) * previewSize.Width() / previewScale_,
1211 (1 - unitSpace) * previewSize.Height() / previewScale_));
1212 totalSize = totalSize - SizeF(unitSpace * previewSize.Width(), unitSpace * previewSize.Height());
1213 }
1214 previewSize = previewGeometryNode->GetMarginFrameSize() * previewScale_;
1215 auto heightLeftSpace =
1216 param_.windowGlobalSizeF.Height() - param_.topSecurity - param_.top - param_.bottomSecurity - param_.bottom;
1217 delta = previewSize.Height() - heightLeftSpace;
1218 if (GreatNotEqual(delta, 0.0f)) {
1219 auto unitSpace = delta / previewSize.Height();
1220 previewGeometryNode->SetFrameSize(SizeF((1 - unitSpace) * previewSize.Width() / previewScale_,
1221 (1 - unitSpace) * previewSize.Height() / previewScale_));
1222 totalSize = totalSize - SizeF(unitSpace * previewSize.Width(), unitSpace * previewSize.Height());
1223 }
1224 previewSize = previewGeometryNode->GetMarginFrameSize() * previewScale_;
1225 auto menuSize = menuGeometryNode->GetMarginFrameSize();
1226 menuGeometryNode->SetFrameSize(
1227 SizeF(menuSize.Width(), std::min<float>(param_.menuItemTotalHeight, heightLeftSpace)));
1228 menuSize = menuGeometryNode->GetMarginFrameSize();
1229 auto offsetX = 0.0f;
1230 auto offsetY = std::min<float>(targetCenterOffset.GetY() - previewSize.Height() / 2,
1231 param_.windowGlobalSizeF.Height() - param_.bottomSecurity - param_.bottom - menuSize.Height());
1232 auto x = std::clamp(offsetX, static_cast<float>(wrapperRect_.Left()) + paddingStart_,
1233 static_cast<float>(wrapperRect_.Right()) - previewSize.Width() - paddingEnd_);
1234 auto y = std::clamp(offsetY, static_cast<float>(wrapperRect_.Top()) + param_.topSecurity,
1235 static_cast<float>(wrapperRect_.Bottom()) - param_.bottomSecurity - previewSize.Height());
1236 x = x + (previewSize.Width() - previewSize.Width() / previewScale_) / 2;
1237 y = y + (previewSize.Height() - previewSize.Height() / previewScale_) / 2;
1238 previewGeometryNode->SetMarginFrameOffset(OffsetF(x, y));
1239 }
1240
LayoutOtherDeviceLeftPreviewRightMenu(const RefPtr<GeometryNode> & previewGeometryNode,const RefPtr<GeometryNode> & menuGeometryNode,SizeF & totalSize,float menuItemTotalHeight)1241 void MenuLayoutAlgorithm::LayoutOtherDeviceLeftPreviewRightMenu(const RefPtr<GeometryNode>& previewGeometryNode,
1242 const RefPtr<GeometryNode>& menuGeometryNode, SizeF& totalSize, float menuItemTotalHeight)
1243 {
1244 CHECK_NULL_VOID(previewGeometryNode);
1245 CHECK_NULL_VOID(menuGeometryNode);
1246 param_.menuItemTotalHeight = menuItemTotalHeight;
1247 auto safeAreaWidth = param_.left + param_.right;
1248 auto maxRectRight = param_.windowGlobalSizeF.Width() - paddingStart_ - paddingEnd_ - safeAreaWidth;
1249 if (LessNotEqual(totalSize.Width() + targetSecurity_, maxRectRight)) {
1250 LayoutOtherDeviceLeftPreviewRightMenuLessThan(previewGeometryNode, menuGeometryNode, totalSize);
1251 } else {
1252 LayoutOtherDeviceLeftPreviewRightMenuGreateThan(previewGeometryNode, menuGeometryNode, totalSize);
1253 }
1254 auto previewSize = previewGeometryNode->GetMarginFrameSize();
1255 auto securityHeight =
1256 param_.menuWindowRect.Height() - param_.topSecurity - param_.top - param_.bottomSecurity - param_.bottom;
1257 if (GreatNotEqual(param_.windowGlobalSizeF.Height(), param_.menuWindowRect.Height()) &&
1258 GreatNotEqual(previewSize.Height(), securityHeight)) {
1259 previewGeometryNode->SetFrameSize(SizeF(previewSize.Width(), securityHeight));
1260 }
1261 }
1262
LayoutPreviewMenu(LayoutWrapper * layoutWrapper)1263 void MenuLayoutAlgorithm::LayoutPreviewMenu(LayoutWrapper* layoutWrapper)
1264 {
1265 CHECK_NULL_VOID(layoutWrapper);
1266 auto paintProperty = GetPaintProperty(layoutWrapper);
1267 CHECK_NULL_VOID(paintProperty);
1268 paintProperty->UpdateEnableArrow(false);
1269 auto menuNode = layoutWrapper->GetHostNode();
1270 CHECK_NULL_VOID(menuNode);
1271 auto parentNode = AceType::DynamicCast<FrameNode>(menuNode->GetParent());
1272 CHECK_NULL_VOID(parentNode);
1273 RefPtr<LayoutWrapper> menuLayoutWrapper;
1274 RefPtr<LayoutWrapper> previewLayoutWrapper;
1275 SizeF totalSize = GetPreviewNodeAndMenuNodeTotalSize(parentNode, previewLayoutWrapper, menuLayoutWrapper);
1276 CHECK_NULL_VOID(menuLayoutWrapper);
1277 CHECK_NULL_VOID(previewLayoutWrapper);
1278 RefPtr<GeometryNode> menuGeometryNode = menuLayoutWrapper->GetGeometryNode();
1279 RefPtr<GeometryNode> previewGeometryNode = previewLayoutWrapper->GetGeometryNode();
1280 CHECK_NULL_VOID(menuGeometryNode);
1281 CHECK_NULL_VOID(previewGeometryNode);
1282 auto menuItemTotalHeight = GetMenuItemTotalHeight(menuLayoutWrapper);
1283 if (placement_ == Placement::BOTTOM_LEFT || placement_ == Placement::BOTTOM ||
1284 placement_ == Placement::BOTTOM_RIGHT) {
1285 LayoutNormalTopPreviewBottomMenu(previewGeometryNode, menuGeometryNode, totalSize, menuItemTotalHeight);
1286 } else if (placement_ == Placement::TOP_LEFT || placement_ == Placement::TOP ||
1287 placement_ == Placement::TOP_RIGHT) {
1288 LayoutNormalBottomPreviewTopMenu(previewGeometryNode, menuGeometryNode, totalSize, menuItemTotalHeight);
1289 } else {
1290 LayoutOtherDeviceLeftPreviewRightMenu(previewGeometryNode, menuGeometryNode, totalSize, menuItemTotalHeight);
1291 }
1292 UpdateScrollAndColumnLayoutConstraint(previewLayoutWrapper, menuLayoutWrapper);
1293 UpdatePreviewPositionAndOffset(previewLayoutWrapper, menuLayoutWrapper);
1294 }
1295
UpdatePreviewPositionAndOffset(RefPtr<LayoutWrapper> & previewLayoutWrapper,RefPtr<LayoutWrapper> & menuLayoutWrapper)1296 void MenuLayoutAlgorithm::UpdatePreviewPositionAndOffset(
1297 RefPtr<LayoutWrapper>& previewLayoutWrapper, RefPtr<LayoutWrapper>& menuLayoutWrapper)
1298 {
1299 CHECK_NULL_VOID(previewLayoutWrapper);
1300 CHECK_NULL_VOID(menuLayoutWrapper);
1301 RefPtr<GeometryNode> previewGeometryNode = previewLayoutWrapper->GetGeometryNode();
1302 RefPtr<GeometryNode> menuGeometryNode = menuLayoutWrapper->GetGeometryNode();
1303 CHECK_NULL_VOID(previewGeometryNode);
1304 CHECK_NULL_VOID(menuGeometryNode);
1305
1306 auto previewSize = previewGeometryNode->GetMarginFrameSize();
1307 previewOffset_ = previewGeometryNode->GetFrameOffset();
1308 auto previewOffsetX = previewOffset_.GetX();
1309 auto previewOffsetY = previewOffset_.GetY();
1310 if (previewSize.IsPositive()) {
1311 targetSize_ = previewSize * previewScale_;
1312 targetOffset_ = OffsetF(previewOffsetX + (previewSize.Width() - targetSize_.Width()) / HALF,
1313 previewOffsetY + (previewSize.Height() - targetSize_.Height()) / HALF);
1314 }
1315 auto previewHostNode = previewLayoutWrapper->GetHostNode();
1316 CHECK_NULL_VOID(previewHostNode);
1317 auto renderContext = previewHostNode->GetRenderContext();
1318 CHECK_NULL_VOID(renderContext);
1319 renderContext->UpdatePosition(OffsetT<Dimension>(Dimension(previewOffsetX), Dimension(previewOffsetY)));
1320
1321 auto menuHostNode = menuLayoutWrapper->GetHostNode();
1322 CHECK_NULL_VOID(menuHostNode);
1323 previewOriginOffset_ = targetCenterOffset_ - OffsetF(previewSize.Width() / HALF, previewSize.Height() / HALF);
1324 previewSize_ = previewSize;
1325 auto menuPattern = menuHostNode->GetPattern<MenuPattern>();
1326 CHECK_NULL_VOID(menuPattern);
1327 menuPattern->SetPreviewOriginOffset(previewOriginOffset_);
1328 }
1329
FixMenuOriginOffset(float beforeAnimationScale,float afterAnimationScale)1330 OffsetF MenuLayoutAlgorithm::FixMenuOriginOffset(float beforeAnimationScale, float afterAnimationScale)
1331 {
1332 auto beforeRate = (1.0f - beforeAnimationScale) / 2;
1333 auto beforeScalePreviewOffset = OffsetF((previewSize_ * beforeRate).Width(), (previewSize_ * beforeRate).Height());
1334 auto afterScalePreviewOffset = OffsetF((previewSize_ * ((afterAnimationScale - 1.0f) / 2)).Width(),
1335 (previewSize_ * ((afterAnimationScale - 1.0f) / 2)).Height());
1336 auto scaleOffset = afterScalePreviewOffset + beforeScalePreviewOffset;
1337 float x = 0.0f;
1338 float y = 0.0f;
1339 switch (placement_) {
1340 case Placement::BOTTOM_LEFT:
1341 case Placement::LEFT_BOTTOM:
1342 x += scaleOffset.GetX();
1343 y -= scaleOffset.GetY();
1344 break;
1345 case Placement::TOP_RIGHT:
1346 case Placement::RIGHT_TOP:
1347 x -= scaleOffset.GetX();
1348 y += scaleOffset.GetY();
1349 break;
1350 case Placement::TOP_LEFT:
1351 case Placement::LEFT_TOP:
1352 x += scaleOffset.GetX();
1353 y += scaleOffset.GetY();
1354 break;
1355 case Placement::BOTTOM_RIGHT:
1356 case Placement::RIGHT_BOTTOM:
1357 x -= scaleOffset.GetX();
1358 y -= scaleOffset.GetY();
1359 break;
1360 case Placement::BOTTOM:
1361 y -= scaleOffset.GetY();
1362 break;
1363 case Placement::TOP:
1364 y += scaleOffset.GetY();
1365 break;
1366 case Placement::LEFT:
1367 x += scaleOffset.GetX();
1368 break;
1369 case Placement::RIGHT:
1370 x -= scaleOffset.GetX();
1371 break;
1372 default:
1373 x += scaleOffset.GetX();
1374 y -= scaleOffset.GetY();
1375 break;
1376 }
1377 return OffsetF(x, y);
1378 }
1379
Layout(LayoutWrapper * layoutWrapper)1380 void MenuLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper)
1381 {
1382 CHECK_NULL_VOID(layoutWrapper);
1383 auto menuProp = DynamicCast<MenuLayoutProperty>(layoutWrapper->GetLayoutProperty());
1384 CHECK_NULL_VOID(menuProp);
1385 auto menuNode = layoutWrapper->GetHostNode();
1386 CHECK_NULL_VOID(menuNode);
1387 auto menuPattern = menuNode->GetPattern<MenuPattern>();
1388 CHECK_NULL_VOID(menuPattern);
1389
1390 if (menuPattern->GetPreviewMode() != MenuPreviewMode::NONE) {
1391 LayoutPreviewMenu(layoutWrapper);
1392 }
1393 if (!menuPattern->IsSelectOverlayCustomMenu()) {
1394 auto geometryNode = layoutWrapper->GetGeometryNode();
1395 CHECK_NULL_VOID(geometryNode);
1396 auto size = geometryNode->GetMarginFrameSize();
1397 bool didNeedArrow = GetIfNeedArrow(layoutWrapper, size);
1398 if (menuPattern->IsSelectMenu()) {
1399 ComputeMenuPositionByAlignType(menuProp, size);
1400 auto offset = ComputeMenuPositionByOffset(menuProp, geometryNode);
1401 position_ += offset;
1402 }
1403 auto menuPosition = lastPosition_.has_value() && CheckIsEmbeddedMode(layoutWrapper)
1404 ? lastPosition_.value()
1405 : MenuLayoutAvoidAlgorithm(menuProp, menuPattern, size, didNeedArrow, layoutWrapper);
1406 menuPattern->UpdateLastPosition(menuPosition);
1407 TAG_LOGI(AceLogTag::ACE_MENU, "update menu position : %{public}s", menuPosition.ToString().c_str());
1408 auto renderContext = menuNode->GetRenderContext();
1409 CHECK_NULL_VOID(renderContext);
1410 renderContext->UpdatePosition(
1411 OffsetT<Dimension>(Dimension(menuPosition.GetX()), Dimension(menuPosition.GetY())));
1412 dumpInfo_.finalPlacement = PlacementUtils::ConvertPlacementToString(placement_);
1413 dumpInfo_.finalPosition = menuPosition;
1414 SetMenuPlacementForAnimation(layoutWrapper);
1415 if (didNeedArrow && arrowPlacement_ != Placement::NONE) {
1416 arrowPosition_ = GetArrowPositionWithPlacement(size, layoutWrapper);
1417 LayoutArrow(layoutWrapper);
1418 }
1419 geometryNode->SetFrameOffset(menuPosition);
1420 auto pipeline = PipelineBase::GetCurrentContext();
1421 CHECK_NULL_VOID(pipeline);
1422 auto menuTheme = pipeline->GetTheme<NG::MenuTheme>();
1423 CHECK_NULL_VOID(menuTheme);
1424 auto beforeAnimationScale = menuTheme->GetPreviewBeforeAnimationScale();
1425 auto afterAnimationScale = menuTheme->GetPreviewAfterAnimationScale();
1426 auto menuOriginOffset = menuPosition - (previewOffset_ - previewOriginOffset_) +
1427 FixMenuOriginOffset(beforeAnimationScale, afterAnimationScale);
1428 menuPattern->SetOriginOffset(menuOriginOffset);
1429 auto previewScale = 1.0f;
1430 if (menuPattern->GetPreviewMode() == MenuPreviewMode::IMAGE &&
1431 !NearEqual(menuPattern->GetTargetSize().Width(), previewSize_.Width())) {
1432 previewScale = menuPattern->GetTargetSize().Width() / previewSize_.Width();
1433 }
1434 auto menuEndOffset = menuPosition - (previewOffset_ - previewOriginOffset_) +
1435 FixMenuOriginOffset(previewScale, afterAnimationScale);
1436 menuPattern->SetEndOffset(menuEndOffset);
1437 menuPattern->SetHasLaid(true);
1438 dumpInfo_.menuPreviewMode = static_cast<uint32_t>(menuPattern->GetPreviewMode());
1439 dumpInfo_.menuType = static_cast<uint32_t>(menuPattern->GetMenuType());
1440 auto menuWrapper = menuPattern->GetMenuWrapper();
1441 CHECK_NULL_VOID(menuWrapper);
1442 auto wrapperPattern = menuWrapper->GetPattern<MenuWrapperPattern>();
1443 CHECK_NULL_VOID(wrapperPattern);
1444 wrapperPattern->SetDumpInfo(dumpInfo_);
1445 }
1446
1447 TranslateOptions(layoutWrapper);
1448 }
1449
TranslateOptions(LayoutWrapper * layoutWrapper)1450 void MenuLayoutAlgorithm::TranslateOptions(LayoutWrapper* layoutWrapper)
1451 {
1452 // translate each option by the height of previous options
1453 OffsetF translate;
1454 for (const auto& child : layoutWrapper->GetAllChildrenWithBuild()) {
1455 child->GetGeometryNode()->SetMarginFrameOffset(translate);
1456 child->Layout();
1457 translate += OffsetF(0, child->GetGeometryNode()->GetFrameSize().Height());
1458 }
1459 }
1460
SetMenuPlacementForAnimation(LayoutWrapper * layoutWrapper)1461 void MenuLayoutAlgorithm::SetMenuPlacementForAnimation(LayoutWrapper* layoutWrapper)
1462 {
1463 auto menu = layoutWrapper->GetHostNode();
1464 CHECK_NULL_VOID(menu);
1465 auto menuPattern = menu->GetPattern<MenuPattern>();
1466 CHECK_NULL_VOID(menuPattern);
1467 auto menuWrapper = menuPattern->GetMenuWrapper();
1468 CHECK_NULL_VOID(menuWrapper);
1469 auto wrapperPattern = menuWrapper->GetPattern<MenuWrapperPattern>();
1470 CHECK_NULL_VOID(wrapperPattern);
1471 wrapperPattern->SetMenuPlacementAfterLayout(placement_);
1472 }
1473
LayoutArrow(const LayoutWrapper * layoutWrapper)1474 void MenuLayoutAlgorithm::LayoutArrow(const LayoutWrapper* layoutWrapper)
1475 {
1476 auto paintProperty = GetPaintProperty(layoutWrapper);
1477 CHECK_NULL_VOID(paintProperty);
1478 paintProperty->UpdateArrowPosition(arrowPosition_);
1479 paintProperty->UpdateArrowPlacement(arrowPlacement_);
1480 dumpInfo_.enableArrow = true;
1481 }
1482
GetPaintProperty(const LayoutWrapper * layoutWrapper)1483 RefPtr<MenuPaintProperty> MenuLayoutAlgorithm::GetPaintProperty(const LayoutWrapper* layoutWrapper)
1484 {
1485 auto menuNode = layoutWrapper->GetHostNode();
1486 CHECK_NULL_RETURN(menuNode, nullptr);
1487 auto paintProperty = menuNode->GetPaintProperty<MenuPaintProperty>();
1488 CHECK_NULL_RETURN(paintProperty, nullptr);
1489 return paintProperty;
1490 }
1491
GetMenuRadius(const LayoutWrapper * layoutWrapper,const SizeF & menuSize)1492 BorderRadiusProperty MenuLayoutAlgorithm::GetMenuRadius(const LayoutWrapper* layoutWrapper, const SizeF& menuSize)
1493 {
1494 Dimension defaultDimension(0);
1495 BorderRadiusProperty radius = { defaultDimension, defaultDimension, defaultDimension, defaultDimension };
1496 auto pipeline = PipelineBase::GetCurrentContext();
1497 CHECK_NULL_RETURN(pipeline, radius);
1498 auto theme = pipeline->GetTheme<SelectTheme>();
1499 CHECK_NULL_RETURN(theme, radius);
1500 auto defaultRadius = Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)
1501 ? theme->GetMenuDefaultRadius()
1502 : theme->GetMenuBorderRadius();
1503 radius.SetRadius(defaultRadius);
1504 auto menuLayoutProp = DynamicCast<MenuLayoutProperty>(layoutWrapper->GetLayoutProperty());
1505 CHECK_NULL_RETURN(menuLayoutProp, radius);
1506 if (menuLayoutProp->GetBorderRadius().has_value()) {
1507 auto menuNode = layoutWrapper->GetHostNode();
1508 CHECK_NULL_RETURN(menuNode, radius);
1509 auto menuPattern = menuNode->GetPattern<MenuPattern>();
1510 CHECK_NULL_RETURN(menuPattern, radius);
1511 radius = menuPattern->CalcIdealBorderRadius(menuLayoutProp->GetBorderRadiusValue(), menuSize);
1512 }
1513
1514 return radius;
1515 }
1516
GetIfNeedArrow(const LayoutWrapper * layoutWrapper,const SizeF & menuSize)1517 bool MenuLayoutAlgorithm::GetIfNeedArrow(const LayoutWrapper* layoutWrapper, const SizeF& menuSize)
1518 {
1519 CHECK_NULL_RETURN(layoutWrapper, false);
1520 auto menuNode = layoutWrapper->GetHostNode();
1521 CHECK_NULL_RETURN(menuNode, false);
1522 auto menuPattern = menuNode->GetPattern<MenuPattern>();
1523 CHECK_NULL_RETURN(menuPattern, false);
1524 auto menuProp = DynamicCast<MenuLayoutProperty>(layoutWrapper->GetLayoutProperty());
1525 CHECK_NULL_RETURN(menuProp, false);
1526 auto paintProperty = GetPaintProperty(layoutWrapper);
1527 CHECK_NULL_RETURN(paintProperty, false);
1528 propNeedArrow_ = paintProperty->GetEnableArrow().value_or(false);
1529
1530 auto pipeline = PipelineBase::GetCurrentContext();
1531 CHECK_NULL_RETURN(pipeline, false);
1532 auto selectThemePtr = pipeline->GetTheme<SelectTheme>();
1533 CHECK_NULL_RETURN(selectThemePtr, false);
1534 if (!propNeedArrow_ || !menuProp->GetMenuPlacement().has_value()) {
1535 return false;
1536 }
1537
1538 propArrowOffset_ = paintProperty->GetArrowOffset().value_or(Dimension(0));
1539 ProcessArrowParams(layoutWrapper, menuSize);
1540
1541 if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
1542 return (menuPattern->IsContextMenu() || menuPattern->IsMenu()) && !targetTag_.empty() && arrowInMenu_;
1543 }
1544
1545 return menuPattern->IsContextMenu() && !targetTag_.empty() && arrowInMenu_;
1546 }
1547
ProcessArrowParams(const LayoutWrapper * layoutWrapper,const SizeF & menuSize)1548 void MenuLayoutAlgorithm::ProcessArrowParams(const LayoutWrapper* layoutWrapper, const SizeF& menuSize)
1549 {
1550 BorderRadiusProperty menuBorderRadius = GetMenuRadius(layoutWrapper, menuSize);
1551 auto radiusTopLeft = menuBorderRadius.radiusTopLeft.value_or(Dimension()).ConvertToPx();
1552 auto radiusTopRight = menuBorderRadius.radiusTopRight.value_or(Dimension()).ConvertToPx();
1553 auto radiusBottomLeft = menuBorderRadius.radiusBottomLeft.value_or(Dimension()).ConvertToPx();
1554 auto radiusBottomRight = menuBorderRadius.radiusBottomRight.value_or(Dimension()).ConvertToPx();
1555 arrowWidth_ = ARROW_WIDTH.ConvertToPx();
1556
1557 switch (placement_) {
1558 case Placement::LEFT:
1559 case Placement::LEFT_TOP:
1560 case Placement::LEFT_BOTTOM:
1561 if (menuSize.Height() >= radiusTopRight + radiusBottomRight + arrowWidth_) {
1562 arrowInMenu_ = true;
1563 }
1564 break;
1565 case Placement::RIGHT:
1566 case Placement::RIGHT_TOP:
1567 case Placement::RIGHT_BOTTOM:
1568 if (menuSize.Height() >= radiusTopLeft + radiusBottomLeft + arrowWidth_) {
1569 arrowInMenu_ = true;
1570 }
1571 break;
1572 case Placement::TOP:
1573 case Placement::TOP_LEFT:
1574 case Placement::TOP_RIGHT:
1575 if (menuSize.Width() >= radiusBottomLeft + radiusBottomRight + arrowWidth_) {
1576 arrowInMenu_ = true;
1577 }
1578 break;
1579 case Placement::BOTTOM:
1580 case Placement::BOTTOM_LEFT:
1581 case Placement::BOTTOM_RIGHT:
1582 if (menuSize.Width() >= radiusTopLeft + radiusTopRight + arrowWidth_) {
1583 arrowInMenu_ = true;
1584 }
1585 break;
1586 default:
1587 break;
1588 }
1589
1590 if (arrowInMenu_) {
1591 targetSpace_ = TARGET_SPACE.ConvertToPx();
1592 }
1593 }
1594
UpdatePropArrowOffset()1595 void MenuLayoutAlgorithm::UpdatePropArrowOffset()
1596 {
1597 if (propArrowOffset_.IsValid()) {
1598 if (propArrowOffset_.Unit() == DimensionUnit::PERCENT) {
1599 propArrowOffset_.SetValue(std::clamp(propArrowOffset_.Value(), 0.0, 1.0));
1600 }
1601 return;
1602 }
1603 switch (arrowPlacement_) {
1604 case Placement::LEFT:
1605 case Placement::RIGHT:
1606 case Placement::TOP:
1607 case Placement::BOTTOM:
1608 propArrowOffset_ = ARROW_HALF_PERCENT_VALUE;
1609 break;
1610 case Placement::TOP_LEFT:
1611 case Placement::BOTTOM_LEFT:
1612 case Placement::LEFT_TOP:
1613 case Placement::RIGHT_TOP:
1614 propArrowOffset_ = ARROW_ZERO_PERCENT_VALUE;
1615 break;
1616 case Placement::TOP_RIGHT:
1617 case Placement::BOTTOM_RIGHT:
1618 case Placement::LEFT_BOTTOM:
1619 case Placement::RIGHT_BOTTOM:
1620 propArrowOffset_ = ARROW_ONE_HUNDRED_PERCENT_VALUE;
1621 break;
1622 default:
1623 break;
1624 }
1625 }
1626
UpdateArrowOffsetWithMenuLimit(const SizeF & menuSize,const LayoutWrapper * layoutWrapper)1627 void MenuLayoutAlgorithm::UpdateArrowOffsetWithMenuLimit(const SizeF& menuSize, const LayoutWrapper* layoutWrapper)
1628 {
1629 UpdatePropArrowOffset();
1630 BorderRadiusProperty menuBorderRadius = GetMenuRadius(layoutWrapper, menuSize);
1631 auto radiusTopLeft = menuBorderRadius.radiusTopLeft.value_or(Dimension()).ConvertToPx();
1632 auto radiusTopRight = menuBorderRadius.radiusTopRight.value_or(Dimension()).ConvertToPx();
1633 auto radiusBottomLeft = menuBorderRadius.radiusBottomLeft.value_or(Dimension()).ConvertToPx();
1634 auto radiusBottomRight = menuBorderRadius.radiusBottomRight.value_or(Dimension()).ConvertToPx();
1635 float range = -1.0f;
1636
1637 switch (arrowPlacement_) {
1638 case Placement::LEFT:
1639 case Placement::LEFT_TOP:
1640 case Placement::LEFT_BOTTOM:
1641 range = menuSize.Height() - radiusTopRight - radiusBottomRight - arrowWidth_;
1642 arrowMinLimit_ = radiusTopRight + ARROW_WIDTH.ConvertToPx() / ARROW_WIDTH_FACTOR;
1643 break;
1644 case Placement::RIGHT:
1645 case Placement::RIGHT_TOP:
1646 case Placement::RIGHT_BOTTOM:
1647 range = menuSize.Height() - radiusTopLeft - radiusBottomLeft - arrowWidth_;
1648 arrowMinLimit_ = radiusTopLeft + ARROW_WIDTH.ConvertToPx() / ARROW_WIDTH_FACTOR;
1649 break;
1650 case Placement::TOP:
1651 case Placement::TOP_LEFT:
1652 case Placement::TOP_RIGHT:
1653 range = menuSize.Width() - radiusBottomLeft - radiusBottomRight - arrowWidth_;
1654 arrowMinLimit_ = radiusBottomLeft + ARROW_WIDTH.ConvertToPx() / ARROW_WIDTH_FACTOR;
1655 break;
1656 case Placement::BOTTOM:
1657 case Placement::BOTTOM_LEFT:
1658 case Placement::BOTTOM_RIGHT:
1659 range = menuSize.Width() - radiusTopLeft - radiusTopRight - arrowWidth_;
1660 arrowMinLimit_ = radiusTopLeft + ARROW_WIDTH.ConvertToPx() / ARROW_WIDTH_FACTOR;
1661 break;
1662 default:
1663 break;
1664 }
1665 if (range >= 0) {
1666 float tempOffset = propArrowOffset_.Unit() == DimensionUnit::PERCENT ? propArrowOffset_.Value() * range
1667 : propArrowOffset_.ConvertToPx();
1668 arrowOffset_ = std::clamp(tempOffset, 0.0f, range);
1669 }
1670 }
1671
ComputeMenuPositionByAlignType(const RefPtr<MenuLayoutProperty> & menuProp,const SizeF & menuSize)1672 void MenuLayoutAlgorithm::ComputeMenuPositionByAlignType(
1673 const RefPtr<MenuLayoutProperty>& menuProp, const SizeF& menuSize)
1674 {
1675 auto alignType = menuProp->GetAlignType().value_or(MenuAlignType::START);
1676 auto direction = menuProp->GetNonAutoLayoutDirection();
1677 auto targetSize = menuProp->GetTargetSizeValue(SizeF());
1678 switch (alignType) {
1679 case MenuAlignType::CENTER: {
1680 position_.AddX(targetSize.Width() / 2.0f - menuSize.Width() / 2.0f);
1681 break;
1682 }
1683 case MenuAlignType::END: {
1684 if (direction == TextDirection::RTL) {
1685 return;
1686 }
1687 position_.AddX(targetSize.Width() - menuSize.Width());
1688 break;
1689 }
1690 case MenuAlignType::START: {
1691 if (direction != TextDirection::RTL) {
1692 return;
1693 }
1694 position_.AddX(targetSize.Width() - menuSize.Width());
1695 break;
1696 }
1697 default:
1698 break;
1699 }
1700 }
1701
ComputeMenuPositionByOffset(const RefPtr<MenuLayoutProperty> & menuProp,const RefPtr<GeometryNode> & geometryNode)1702 OffsetF MenuLayoutAlgorithm::ComputeMenuPositionByOffset(
1703 const RefPtr<MenuLayoutProperty>& menuProp, const RefPtr<GeometryNode>& geometryNode)
1704 {
1705 CHECK_NULL_RETURN(menuProp, OffsetF(0, 0));
1706 CHECK_NULL_RETURN(geometryNode, OffsetF(0, 0));
1707
1708 const auto& layoutConstraint = menuProp->GetLayoutConstraint();
1709 CHECK_NULL_RETURN(layoutConstraint, OffsetF(0, 0));
1710 auto menuAlignOffset = menuProp->GetOffset().value_or(
1711 DimensionOffset(Dimension(0, DimensionUnit::VP), Dimension(0, DimensionUnit::VP)));
1712
1713 auto menuSize = geometryNode->GetFrameSize();
1714 auto menuTrimOffsetX =
1715 ConvertToPx(CalcLength(menuAlignOffset.GetX()), layoutConstraint->scaleProperty, menuSize.Width());
1716 auto menuTrimOffsetY =
1717 ConvertToPx(CalcLength(menuAlignOffset.GetY()), layoutConstraint->scaleProperty, menuSize.Height());
1718 OffsetF menuTrimOffset = OffsetF(menuTrimOffsetX.value_or(0.0), menuTrimOffsetY.value_or(0.0));
1719 return menuTrimOffset;
1720 }
1721
GetCurrentPipelineContext()1722 RefPtr<PipelineContext> MenuLayoutAlgorithm::GetCurrentPipelineContext()
1723 {
1724 // Get pipelineContext of main window or host window for UIExtension
1725 auto containerId = Container::CurrentId();
1726 RefPtr<PipelineContext> context;
1727 if (containerId >= MIN_SUBCONTAINER_ID) {
1728 auto parentContainerId = SubwindowManager::GetInstance()->GetParentContainerId(containerId);
1729 auto parentContainer = AceEngine::Get().GetContainer(parentContainerId);
1730 CHECK_NULL_RETURN(parentContainer, nullptr);
1731 context = DynamicCast<PipelineContext>(parentContainer->GetPipelineContext());
1732 } else {
1733 context = PipelineContext::GetCurrentContext();
1734 }
1735 return context;
1736 }
1737
MenuLayoutAvoidAlgorithm(const RefPtr<MenuLayoutProperty> & menuProp,const RefPtr<MenuPattern> & menuPattern,const SizeF & size,bool didNeedArrow,LayoutWrapper * layoutWrapper)1738 OffsetF MenuLayoutAlgorithm::MenuLayoutAvoidAlgorithm(const RefPtr<MenuLayoutProperty>& menuProp,
1739 const RefPtr<MenuPattern>& menuPattern, const SizeF& size, bool didNeedArrow, LayoutWrapper* layoutWrapper)
1740 {
1741 auto pipelineContext = GetCurrentPipelineContext();
1742 CHECK_NULL_RETURN(pipelineContext, OffsetF(0, 0));
1743 CHECK_NULL_RETURN(menuProp, OffsetF(0, 0));
1744 CHECK_NULL_RETURN(menuPattern, OffsetF(0, 0));
1745 float x = 0.0f;
1746 float y = 0.0f;
1747 if (menuProp->GetMenuPlacement().has_value() && (targetSize_.Width() > 0.0 || targetSize_.Height() > 0.0)) {
1748 placement_ = menuProp->GetMenuPlacement().value();
1749 if (layoutWrapper != nullptr) {
1750 PlacementRTL(layoutWrapper, placement_);
1751 }
1752 auto childOffset = GetChildPosition(size, didNeedArrow);
1753 x = childOffset.GetX();
1754 y = childOffset.GetY();
1755 } else {
1756 x = HorizontalLayout(size, position_.GetX(), menuPattern->IsSelectMenu()) + positionOffset_.GetX();
1757 y = VerticalLayout(size, position_.GetY(), menuPattern->IsContextMenu()) + positionOffset_.GetY();
1758 }
1759 x = std::clamp(static_cast<double>(x), static_cast<double>(paddingStart_),
1760 static_cast<double>(wrapperRect_.Right() - size.Width() - paddingEnd_));
1761 float yMinAvoid = wrapperRect_.Top() + paddingTop_;
1762 float yMaxAvoid = wrapperRect_.Bottom() - paddingBottom_ - size.Height();
1763 y = std::clamp(y, yMinAvoid, yMaxAvoid);
1764 return { x, y };
1765 }
1766
PlacementRTL(LayoutWrapper * layoutWrapper,Placement & placement_)1767 void MenuLayoutAlgorithm::PlacementRTL(LayoutWrapper* layoutWrapper, Placement& placement_)
1768 {
1769 auto menuLayoutProperty = layoutWrapper->GetLayoutProperty();
1770 CHECK_NULL_VOID(menuLayoutProperty);
1771 auto layoutDirection = menuLayoutProperty->GetNonAutoLayoutDirection();
1772 if (layoutDirection == TextDirection::RTL) {
1773 switch (placement_) {
1774 case Placement::LEFT:
1775 placement_ = Placement::RIGHT;
1776 break;
1777 case Placement::RIGHT:
1778 placement_ = Placement::LEFT;
1779 break;
1780 case Placement::TOP_LEFT:
1781 placement_ = Placement::TOP_RIGHT;
1782 break;
1783 case Placement::TOP_RIGHT:
1784 placement_ = Placement::TOP_LEFT;
1785 break;
1786 case Placement::BOTTOM_LEFT:
1787 placement_ = Placement::BOTTOM_RIGHT;
1788 break;
1789 case Placement::BOTTOM_RIGHT:
1790 placement_ = Placement::BOTTOM_LEFT;
1791 break;
1792 case Placement::LEFT_TOP:
1793 placement_ = Placement::RIGHT_TOP;
1794 break;
1795 case Placement::RIGHT_TOP:
1796 placement_ = Placement::LEFT_TOP;
1797 break;
1798 case Placement::LEFT_BOTTOM:
1799 placement_ = Placement::RIGHT_BOTTOM;
1800 break;
1801 case Placement::RIGHT_BOTTOM:
1802 placement_ = Placement::LEFT_BOTTOM;
1803 break;
1804 default:
1805 break;
1806 }
1807 }
1808 }
1809
LimitContainerModalMenuRect(double & rectWidth,double & rectHeight)1810 void MenuLayoutAlgorithm::LimitContainerModalMenuRect(double& rectWidth, double& rectHeight)
1811 {
1812 auto containerOffsetX = static_cast<float>(CONTAINER_BORDER_WIDTH.ConvertToPx()) +
1813 static_cast<float>(CONTAINER_BORDER_WIDTH.ConvertToPx()) +
1814 static_cast<float>(CONTENT_PADDING.ConvertToPx());
1815 auto pipeline = NG::PipelineContext::GetCurrentContext();
1816 CHECK_NULL_VOID(pipeline);
1817 auto containerOffsetY = static_cast<float>(pipeline->GetCustomTitleHeight().ConvertToPx()) +
1818 static_cast<float>(CONTAINER_BORDER_WIDTH.ConvertToPx()) +
1819 static_cast<float>(CONTAINER_BORDER_WIDTH.ConvertToPx());
1820 rectWidth -= containerOffsetX;
1821 rectHeight -= containerOffsetY;
1822 }
1823
UpdateConstraintWidth(LayoutWrapper * layoutWrapper,LayoutConstraintF & constraint)1824 void MenuLayoutAlgorithm::UpdateConstraintWidth(LayoutWrapper* layoutWrapper, LayoutConstraintF& constraint)
1825 {
1826 RefPtr<GridColumnInfo> columnInfo;
1827 columnInfo = GridSystemManager::GetInstance().GetInfoByType(GridColumnType::MENU);
1828 auto menuNode = layoutWrapper->GetHostNode();
1829 CHECK_NULL_VOID(menuNode);
1830 auto menuPattern = menuNode->GetPattern<MenuPattern>();
1831 CHECK_NULL_VOID(menuPattern);
1832 columnInfo->GetParent()->BuildColumnWidth(wrapperSize_.Width());
1833 auto menuLayoutProperty = AceType::DynamicCast<MenuLayoutProperty>(layoutWrapper->GetLayoutProperty());
1834 CHECK_NULL_VOID(menuLayoutProperty);
1835 // set max width
1836 const auto& padding = menuLayoutProperty->CreatePaddingAndBorder();
1837 auto maxHorizontalSpace = std::max(leftSpace_, rightSpace_) - 2.0f * padding.Width();
1838 auto maxWidth = static_cast<float>(columnInfo->GetWidth(GetMaxGridCounts(columnInfo)));
1839 if (Container::LessThanAPIVersion(PlatformVersion::VERSION_TEN)) {
1840 maxWidth = std::min(maxHorizontalSpace, maxWidth);
1841 }
1842 maxWidth = std::min(constraint.maxSize.Width(), maxWidth);
1843 constraint.maxSize.SetWidth(maxWidth);
1844 }
1845
UpdateConstraintHeight(LayoutWrapper * layoutWrapper,LayoutConstraintF & constraint)1846 void MenuLayoutAlgorithm::UpdateConstraintHeight(LayoutWrapper* layoutWrapper, LayoutConstraintF& constraint)
1847 {
1848 auto pipelineContext = GetCurrentPipelineContext();
1849 CHECK_NULL_VOID(pipelineContext);
1850 auto menuPattern = layoutWrapper->GetHostNode()->GetPattern<MenuPattern>();
1851 CHECK_NULL_VOID(menuPattern);
1852
1853 float maxAvailableHeight = wrapperRect_.Height();
1854 float maxSpaceHeight = maxAvailableHeight * HEIGHT_CONSTRAINT_FACTOR;
1855 if (lastPosition_.has_value() && CheckIsEmbeddedMode(layoutWrapper)) {
1856 auto spaceToBottom = static_cast<float>(wrapperRect_.Bottom()) - lastPosition_.value().GetY();
1857 maxSpaceHeight = std::min(maxSpaceHeight, spaceToBottom);
1858 }
1859 if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN)) {
1860 if (menuPattern->IsHeightModifiedBySelect()) {
1861 auto menuLayoutProps = AceType::DynamicCast<MenuLayoutProperty>(layoutWrapper->GetLayoutProperty());
1862 auto selectModifiedHeight = menuLayoutProps->GetSelectModifiedHeight().value();
1863 if (selectModifiedHeight < maxSpaceHeight) {
1864 maxSpaceHeight = selectModifiedHeight;
1865 }
1866 }
1867 }
1868 constraint.maxSize.SetHeight(maxSpaceHeight);
1869 }
1870
CreateChildConstraint(LayoutWrapper * layoutWrapper)1871 LayoutConstraintF MenuLayoutAlgorithm::CreateChildConstraint(LayoutWrapper* layoutWrapper)
1872 {
1873 auto menuLayoutProperty = AceType::DynamicCast<MenuLayoutProperty>(layoutWrapper->GetLayoutProperty());
1874 CHECK_NULL_RETURN(menuLayoutProperty, LayoutConstraintF());
1875
1876 auto childConstraint = menuLayoutProperty->CreateChildConstraint();
1877 UpdateConstraintWidth(layoutWrapper, childConstraint);
1878 UpdateConstraintHeight(layoutWrapper, childConstraint);
1879 UpdateConstraintBaseOnOptions(layoutWrapper, childConstraint);
1880 return childConstraint;
1881 }
1882
UpdateConstraintBaseOnOptions(LayoutWrapper * layoutWrapper,LayoutConstraintF & constraint)1883 void MenuLayoutAlgorithm::UpdateConstraintBaseOnOptions(LayoutWrapper* layoutWrapper, LayoutConstraintF& constraint)
1884 {
1885 auto menuNode = layoutWrapper->GetHostNode();
1886 CHECK_NULL_VOID(menuNode);
1887 auto menuPattern = menuNode->GetPattern<MenuPattern>();
1888 CHECK_NULL_VOID(menuPattern);
1889 auto options = menuPattern->GetOptions();
1890 if (options.empty()) {
1891 return;
1892 }
1893 auto optionConstraint = constraint;
1894 RefPtr<GridColumnInfo> columnInfo;
1895 columnInfo = GridSystemManager::GetInstance().GetInfoByType(GridColumnType::MENU);
1896 columnInfo->GetParent()->BuildColumnWidth(wrapperSize_.Width());
1897 auto minWidth = static_cast<float>(columnInfo->GetWidth(MIN_GRID_COUNTS));
1898 optionConstraint.maxSize.MinusWidth(optionPadding_ * 2.0f);
1899 optionConstraint.minSize.SetWidth(minWidth - optionPadding_ * 2.0f);
1900 auto maxChildrenWidth = optionConstraint.minSize.Width();
1901 auto optionsLayoutWrapper = GetOptionsLayoutWrappper(layoutWrapper);
1902 for (const auto& optionWrapper : optionsLayoutWrapper) {
1903 optionWrapper->GetLayoutProperty()->ResetCalcMinSize();
1904 optionWrapper->Measure(optionConstraint);
1905 auto childSize = optionWrapper->GetGeometryNode()->GetMarginFrameSize();
1906 maxChildrenWidth = std::max(maxChildrenWidth, childSize.Width());
1907 }
1908 UpdateOptionConstraint(optionsLayoutWrapper, maxChildrenWidth);
1909 constraint.minSize.SetWidth(maxChildrenWidth + optionPadding_ * 2.0f);
1910 }
1911
GetOptionsLayoutWrappper(LayoutWrapper * layoutWrapper)1912 std::list<RefPtr<LayoutWrapper>> MenuLayoutAlgorithm::GetOptionsLayoutWrappper(LayoutWrapper* layoutWrapper)
1913 {
1914 std::list<RefPtr<LayoutWrapper>> optionsWrapper;
1915 auto scrollWrapper = layoutWrapper->GetOrCreateChildByIndex(0);
1916 CHECK_NULL_RETURN(scrollWrapper, optionsWrapper);
1917 auto columnWrapper = scrollWrapper->GetOrCreateChildByIndex(0);
1918 CHECK_NULL_RETURN(columnWrapper, optionsWrapper);
1919 optionsWrapper = columnWrapper->GetAllChildrenWithBuild();
1920 return optionsWrapper;
1921 }
1922
UpdateOptionConstraint(std::list<RefPtr<LayoutWrapper>> & options,float width)1923 void MenuLayoutAlgorithm::UpdateOptionConstraint(std::list<RefPtr<LayoutWrapper>>& options, float width)
1924 {
1925 for (const auto& option : options) {
1926 auto optionLayoutProps = option->GetLayoutProperty();
1927 CHECK_NULL_VOID(optionLayoutProps);
1928 optionLayoutProps->UpdateCalcMinSize(CalcSize(CalcLength(width), std::nullopt));
1929 }
1930 }
1931
1932 // return vertical offset
VerticalLayout(const SizeF & size,float position,bool isContextMenu)1933 float MenuLayoutAlgorithm::VerticalLayout(const SizeF& size, float position, bool isContextMenu)
1934 {
1935 placement_ = Placement::BOTTOM;
1936 // can put menu below click point
1937 if (GreatOrEqual(bottomSpace_, size.Height())) {
1938 return position + margin_;
1939 }
1940 if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN) && isContextMenu) {
1941 if (LessNotEqual(bottomSpace_, size.Height()) && LessNotEqual(size.Height(), wrapperRect_.Height())) {
1942 return wrapperRect_.Bottom() - size.Height() - paddingBottom_;
1943 }
1944 // can't fit in screen, line up with top of the screen
1945 return wrapperRect_.Top() + paddingTop_;
1946 } else {
1947 float wrapperHeight = wrapperSize_.Height();
1948 // put menu above click point
1949 if (GreatOrEqual(topSpace_, size.Height())) {
1950 // menu show on top
1951 placement_ = Placement::TOP;
1952 return topSpace_ - size.Height() + margin_;
1953 }
1954 // line up bottom of menu with bottom of the screen
1955 if (LessNotEqual(size.Height(), wrapperHeight)) {
1956 return wrapperHeight - size.Height();
1957 }
1958 // can't fit in screen, line up with top of the screen
1959 return 0.0f;
1960 }
1961 }
1962
1963 // returns horizontal offset
HorizontalLayout(const SizeF & size,float position,bool isSelectMenu)1964 float MenuLayoutAlgorithm::HorizontalLayout(const SizeF& size, float position, bool isSelectMenu)
1965 {
1966 float wrapperWidth = wrapperSize_.Width();
1967 // can fit menu on the right side of position
1968 if (rightSpace_ >= size.Width()) {
1969 return position + margin_;
1970 }
1971
1972 // fit menu on the left side
1973 if (!isSelectMenu && leftSpace_ >= size.Width()) {
1974 return position - size.Width();
1975 }
1976
1977 // line up right side of menu with right boundary of the screen
1978 if (size.Width() < wrapperWidth) {
1979 if (isSelectMenu) {
1980 return position + margin_;
1981 }
1982 return wrapperWidth - size.Width();
1983 }
1984
1985 // can't fit in screen, line up with left side of the screen
1986 return 0.0f;
1987 }
1988
GetPositionWithPlacement(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)1989 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacement(
1990 const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
1991 {
1992 OffsetF childPosition;
1993
1994 auto func = placementFuncMap_.find(placement_);
1995 if (func != placementFuncMap_.end()) {
1996 auto placementFunc = func->second;
1997 if (placementFunc != nullptr) {
1998 childPosition = (this->*placementFunc)(childSize, topPosition, bottomPosition);
1999 }
2000 }
2001 return childPosition;
2002 }
2003
GetArrowPositionWithPlacement(const SizeF & menuSize,const LayoutWrapper * layoutWrapper)2004 OffsetF MenuLayoutAlgorithm::GetArrowPositionWithPlacement(const SizeF& menuSize, const LayoutWrapper* layoutWrapper)
2005 {
2006 UpdateArrowOffsetWithMenuLimit(menuSize, layoutWrapper);
2007 auto addArrowOffsetToArrowMin = arrowOffset_ + arrowMinLimit_;
2008 auto space_ = ARROW_HIGHT.ConvertToPx();
2009 OffsetF childPosition;
2010 switch (arrowPlacement_) {
2011 case Placement::TOP:
2012 case Placement::TOP_LEFT:
2013 case Placement::TOP_RIGHT:
2014 childPosition = OffsetF(addArrowOffsetToArrowMin, menuSize.Height() + space_);
2015 break;
2016 case Placement::BOTTOM:
2017 case Placement::BOTTOM_LEFT:
2018 case Placement::BOTTOM_RIGHT:
2019 childPosition = OffsetF(addArrowOffsetToArrowMin, -space_);
2020 break;
2021 case Placement::LEFT:
2022 case Placement::LEFT_TOP:
2023 case Placement::LEFT_BOTTOM:
2024 childPosition = OffsetF(menuSize.Width() + space_, addArrowOffsetToArrowMin);
2025 break;
2026 case Placement::RIGHT:
2027 case Placement::RIGHT_TOP:
2028 case Placement::RIGHT_BOTTOM:
2029 childPosition = OffsetF(-space_, addArrowOffsetToArrowMin);
2030 break;
2031 default:
2032 break;
2033 }
2034 return childPosition;
2035 }
2036
GetMenuWrapperOffset(const LayoutWrapper * layoutWrapper)2037 OffsetF MenuLayoutAlgorithm::GetMenuWrapperOffset(const LayoutWrapper* layoutWrapper)
2038 {
2039 CHECK_NULL_RETURN(layoutWrapper, OffsetF());
2040 auto menuNode = layoutWrapper->GetHostNode();
2041 CHECK_NULL_RETURN(menuNode, OffsetF());
2042 return menuNode->GetParentGlobalOffsetDuringLayout();
2043 }
2044
InitTargetSizeAndPosition(const LayoutWrapper * layoutWrapper,bool isContextMenu,const RefPtr<MenuPattern> & menuPattern)2045 void MenuLayoutAlgorithm::InitTargetSizeAndPosition(
2046 const LayoutWrapper* layoutWrapper, bool isContextMenu, const RefPtr<MenuPattern>& menuPattern)
2047 {
2048 CHECK_NULL_VOID(layoutWrapper && menuPattern);
2049 auto targetNode = FrameNode::GetFrameNode(targetTag_, targetNodeId_);
2050 CHECK_NULL_VOID(targetNode);
2051 dumpInfo_.targetNode = targetNode->GetTag();
2052 auto geometryNode = targetNode->GetGeometryNode();
2053 CHECK_NULL_VOID(geometryNode);
2054 auto props = AceType::DynamicCast<MenuLayoutProperty>(layoutWrapper->GetLayoutProperty());
2055 CHECK_NULL_VOID(props);
2056 bool expandDisplay = menuPattern->GetMenuExpandDisplay();
2057 if (props->GetIsRectInTargetValue(false)) {
2058 targetSize_ = props->GetTargetSizeValue(SizeF());
2059 targetOffset_ = props->GetMenuOffsetValue(OffsetF());
2060 } else {
2061 targetSize_ = targetNode->GetPaintRectWithTransform().GetSize();
2062 targetOffset_ = targetNode->GetPaintRectOffset();
2063 }
2064 dumpInfo_.targetSize = targetSize_;
2065 dumpInfo_.targetOffset = targetOffset_;
2066 menuPattern->SetTargetSize(targetSize_);
2067 TAG_LOGI(AceLogTag::ACE_MENU, "targetNode: %{public}s, targetSize: %{public}s, targetOffset: %{public}s",
2068 targetNode->GetTag().c_str(), targetSize_.ToString().c_str(), targetOffset_.ToString().c_str());
2069 auto pipelineContext = GetCurrentPipelineContext();
2070 CHECK_NULL_VOID(pipelineContext);
2071 if (Container::LessThanAPIVersion(PlatformVersion::VERSION_ELEVEN)) {
2072 expandDisplay = true;
2073 }
2074 if (canExpandCurrentWindow_ && targetTag_ != V2::SELECT_ETS_TAG) {
2075 ModifyTargetOffset();
2076 OffsetF offset = GetMenuWrapperOffset(layoutWrapper);
2077 targetOffset_ -= offset;
2078 return;
2079 }
2080
2081 auto windowManager = pipelineContext->GetWindowManager();
2082 auto isContainerModal = pipelineContext->GetWindowModal() == WindowModal::CONTAINER_MODAL && windowManager &&
2083 windowManager->GetWindowMode() == WindowMode::WINDOW_MODE_FLOATING;
2084 if (isContainerModal) {
2085 auto newOffsetX = static_cast<float>(CONTAINER_BORDER_WIDTH.ConvertToPx()) +
2086 static_cast<float>(CONTENT_PADDING.ConvertToPx());
2087 auto newOffsetY = static_cast<float>(pipelineContext->GetCustomTitleHeight().ConvertToPx()) +
2088 static_cast<float>(CONTAINER_BORDER_WIDTH.ConvertToPx());
2089 targetOffset_ -= OffsetF(newOffsetX, newOffsetY);
2090 } else {
2091 OffsetF offset = GetMenuWrapperOffset(layoutWrapper);
2092 targetOffset_ -= offset;
2093 }
2094 }
2095
FitToScreen(const OffsetF & position,const SizeF & childSize,bool didNeedArrow)2096 OffsetF MenuLayoutAlgorithm::FitToScreen(const OffsetF& position, const SizeF& childSize, bool didNeedArrow)
2097 {
2098 OffsetF afterOffsetPosition;
2099 auto originPosition = position;
2100
2101 if (NearEqual(positionOffset_, OffsetF(0.0f, 0.0f))) {
2102 afterOffsetPosition = AddTargetSpace(originPosition);
2103 } else {
2104 afterOffsetPosition = AddOffset(originPosition);
2105 }
2106
2107 if (!CheckPosition(afterOffsetPosition, childSize) || flag_) {
2108 flag_ = false;
2109 return OffsetF(0.0f, 0.0f);
2110 }
2111
2112 return afterOffsetPosition;
2113 }
2114
GetChildPosition(const SizeF & childSize,bool didNeedArrow)2115 OffsetF MenuLayoutAlgorithm::GetChildPosition(const SizeF& childSize, bool didNeedArrow)
2116 {
2117 OffsetF bottomPosition = OffsetF(targetOffset_.GetX() + (targetSize_.Width() - childSize.Width()) / 2.0,
2118 targetOffset_.GetY() + targetSize_.Height() + targetSpace_);
2119 OffsetF topPosition = OffsetF(targetOffset_.GetX() + (targetSize_.Width() - childSize.Width()) / 2.0,
2120 targetOffset_.GetY() - childSize.Height() - targetSpace_);
2121 OffsetF defaultPosition = OffsetF(targetOffset_.GetX() + (targetSize_.Width() - childSize.Width()) / 2.0,
2122 targetOffset_.GetY() + (targetSize_.Height() - childSize.Height()) / 2.0);
2123
2124 OffsetF childPosition;
2125 OffsetF position = defaultPosition;
2126 auto positionOffset = positionOffset_;
2127 std::vector<Placement> currentPlacementStates = PLACEMENT_STATES.find(Placement::BOTTOM_LEFT)->second;
2128 auto it = PLACEMENT_STATES.find(placement_);
2129 if (it != PLACEMENT_STATES.end()) {
2130 currentPlacementStates = it->second;
2131 }
2132 size_t step = ALIGNMENT_STEP_OFFSET;
2133 if (placement_ <= Placement::BOTTOM) {
2134 step += 1;
2135 }
2136 for (size_t i = 0, len = currentPlacementStates.size(); i < len; i++) {
2137 placement_ = currentPlacementStates[i];
2138 if (placement_ == Placement::NONE) {
2139 break;
2140 }
2141 if (i >= step) {
2142 positionOffset_ = OffsetF(0.0f, 0.0f);
2143 }
2144 childPosition = GetPositionWithPlacement(childSize, topPosition, bottomPosition);
2145 position = FitToScreen(childPosition, childSize, didNeedArrow);
2146 if (NearEqual(position, OffsetF(0.0f, 0.0f))) {
2147 continue;
2148 }
2149 break;
2150 }
2151 if (placement_ == Placement::NONE) {
2152 position = GetAdjustPosition(currentPlacementStates, step, childSize, topPosition, bottomPosition);
2153 if (NearEqual(position, OffsetF(0.0f, 0.0f))) {
2154 position = defaultPosition;
2155 }
2156 }
2157 positionOffset_ = positionOffset;
2158 arrowPlacement_ = placement_;
2159
2160 return position;
2161 }
2162
GetAdjustPosition(std::vector<Placement> & currentPlacementStates,size_t step,const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)2163 OffsetF MenuLayoutAlgorithm::GetAdjustPosition(std::vector<Placement>& currentPlacementStates, size_t step,
2164 const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
2165 {
2166 OffsetF childPosition;
2167 OffsetF position;
2168 for (size_t i = 0, len = currentPlacementStates.size(); i < len;) {
2169 placement_ = currentPlacementStates[i];
2170 if (placement_ == Placement::NONE) {
2171 break;
2172 }
2173 childPosition = GetPositionWithPlacement(childSize, topPosition, bottomPosition);
2174 position = AdjustPosition(childPosition, childSize.Width(), childSize.Height(), targetSecurity_);
2175 if (NearEqual(position, OffsetF(0.0f, 0.0f))) {
2176 i += step;
2177 continue;
2178 }
2179 break;
2180 }
2181 return position;
2182 }
2183
AdjustPosition(const OffsetF & position,float width,float height,float space)2184 OffsetF MenuLayoutAlgorithm::AdjustPosition(const OffsetF& position, float width, float height, float space)
2185 {
2186 float xMax = 0.0f;
2187 float yMax = 0.0f;
2188 float xMin = paddingStart_;
2189 float yMin = std::max(1.0f, static_cast<float>(wrapperRect_.Top()) + paddingTop_);
2190 float wrapperBottom = wrapperRect_.Bottom();
2191 switch (placement_) {
2192 case Placement::LEFT_TOP:
2193 case Placement::LEFT_BOTTOM:
2194 case Placement::LEFT: {
2195 xMax = std::min(targetOffset_.GetX() - width - space, wrapperSize_.Width() - paddingEnd_ - width);
2196 yMax = wrapperBottom - height - paddingBottom_;
2197 break;
2198 }
2199 case Placement::RIGHT_TOP:
2200 case Placement::RIGHT_BOTTOM:
2201 case Placement::RIGHT: {
2202 xMin = std::max(targetOffset_.GetX() + targetSize_.Width() + space, paddingStart_);
2203 xMax = wrapperSize_.Width() - width - paddingEnd_;
2204 yMax = wrapperBottom - height - paddingBottom_;
2205 break;
2206 }
2207 case Placement::TOP_LEFT:
2208 case Placement::TOP_RIGHT:
2209 case Placement::TOP: {
2210 xMax = wrapperSize_.Width() - width - paddingEnd_;
2211 yMax = std::min(targetOffset_.GetY() - height - space, wrapperBottom - paddingBottom_ - height);
2212 break;
2213 }
2214 case Placement::BOTTOM_LEFT:
2215 case Placement::BOTTOM_RIGHT:
2216 case Placement::BOTTOM: {
2217 xMax = wrapperRect_.Right() - width - paddingEnd_;
2218 yMin = std::max(targetOffset_.GetY() + targetSize_.Height() + space, yMin);
2219 yMax = wrapperBottom - height - paddingBottom_;
2220 break;
2221 }
2222 default:
2223 break;
2224 }
2225 if (xMax < xMin || yMax < yMin) {
2226 return OffsetF(0.0f, 0.0f);
2227 }
2228 auto x = std::clamp(position.GetX(), xMin, xMax);
2229 auto y = std::clamp(position.GetY(), yMin, yMax);
2230 return OffsetF(x, y);
2231 }
2232
AddTargetSpace(const OffsetF & position)2233 OffsetF MenuLayoutAlgorithm::AddTargetSpace(const OffsetF& position)
2234 {
2235 auto x = position.GetX();
2236 auto y = position.GetY();
2237 switch (placement_) {
2238 case Placement::BOTTOM_LEFT:
2239 case Placement::BOTTOM_RIGHT:
2240 case Placement::BOTTOM: {
2241 y += targetSecurity_;
2242 break;
2243 }
2244 case Placement::TOP_LEFT:
2245 case Placement::TOP_RIGHT:
2246 case Placement::TOP: {
2247 y -= targetSecurity_;
2248 break;
2249 }
2250 case Placement::RIGHT_TOP:
2251 case Placement::RIGHT_BOTTOM:
2252 case Placement::RIGHT: {
2253 x += targetSecurity_;
2254 break;
2255 }
2256 case Placement::LEFT_TOP:
2257 case Placement::LEFT_BOTTOM:
2258 case Placement::LEFT: {
2259 x -= targetSecurity_;
2260 break;
2261 }
2262 default: {
2263 y += targetSecurity_;
2264 break;
2265 }
2266 }
2267 return OffsetF(x, y);
2268 }
2269
AddOffset(const OffsetF & position)2270 OffsetF MenuLayoutAlgorithm::AddOffset(const OffsetF& position)
2271 {
2272 auto x = position.GetX();
2273 auto y = position.GetY();
2274 switch (placement_) {
2275 case Placement::BOTTOM_LEFT:
2276 case Placement::BOTTOM_RIGHT:
2277 case Placement::BOTTOM: {
2278 x += positionOffset_.GetX();
2279 y += positionOffset_.GetY();
2280 break;
2281 }
2282 case Placement::TOP_LEFT:
2283 case Placement::TOP_RIGHT:
2284 case Placement::TOP: {
2285 x += positionOffset_.GetX();
2286 y -= positionOffset_.GetY();
2287 break;
2288 }
2289 case Placement::RIGHT_TOP:
2290 case Placement::RIGHT_BOTTOM:
2291 case Placement::RIGHT: {
2292 x += positionOffset_.GetX();
2293 y += positionOffset_.GetY();
2294 break;
2295 }
2296 case Placement::LEFT_TOP:
2297 case Placement::LEFT_BOTTOM:
2298 case Placement::LEFT: {
2299 x -= positionOffset_.GetX();
2300 y += positionOffset_.GetY();
2301 break;
2302 }
2303 default: {
2304 x += positionOffset_.GetX();
2305 y += positionOffset_.GetY();
2306 break;
2307 }
2308 }
2309 return OffsetF(x, y);
2310 }
2311
CheckPositionInPlacementRect(const Rect & rect,const OffsetF & position,const SizeF & childSize)2312 bool MenuLayoutAlgorithm::CheckPositionInPlacementRect(
2313 const Rect& rect, const OffsetF& position, const SizeF& childSize)
2314 {
2315 auto x = position.GetX();
2316 auto y = position.GetY();
2317 OffsetF tempPos = position;
2318 if (state_ != prevState_) {
2319 if (prevState_ == -1) {
2320 prevState_ = state_;
2321 preOffset_ = position;
2322 preOffset_.SetX(x);
2323 preOffset_.SetY(y);
2324 preRect_.SetOffset(rect.GetOffset());
2325 preRect_.SetSize(rect.GetSize());
2326 auto outside = LessNotEqual(x, rect.Left()) || GreatNotEqual(x + childSize.Width(), rect.Right()) ||
2327 LessNotEqual(y, rect.Top()) || GreatNotEqual(y + childSize.Height(), rect.Bottom());
2328 if (!outside) {
2329 preOffset_ = position;
2330 preOffset_.SetX(x);
2331 preOffset_.SetY(y);
2332 preRect_.SetOffset(rect.GetOffset());
2333 preRect_.SetSize(rect.GetSize());
2334 return true;
2335 }
2336 flag_ = true;
2337 positionOffset_ = { 0.0f, 0.0f };
2338 return false;
2339 }
2340 return CheckPlacement(childSize);
2341 }
2342 x = tempPos.GetX();
2343 y = tempPos.GetY();
2344 if (LessNotEqual(x, rect.Left()) || GreatNotEqual(x + childSize.Width(), rect.Right()) ||
2345 LessNotEqual(y, rect.Top()) || GreatNotEqual(y + childSize.Height(), rect.Bottom())) {
2346 preOffset_ = position;
2347 preOffset_.SetX(x);
2348 preOffset_.SetY(y);
2349 preRect_.SetOffset(rect.GetOffset());
2350 preRect_.SetSize(rect.GetSize());
2351 return false;
2352 }
2353 return true;
2354 }
2355
CheckPlacement(const SizeF & childSize)2356 bool MenuLayoutAlgorithm::CheckPlacement(const SizeF& childSize)
2357 {
2358 auto x = preOffset_.GetX();
2359 auto y = preOffset_.GetY();
2360
2361 switch (prevState_) {
2362 case static_cast<int>(DirectionState::Bottom_Direction):
2363 case static_cast<int>(DirectionState::Top_Direction): {
2364 if ((LessNotEqual(x, preRect_.Left()) || GreatNotEqual(x + childSize.Width(), preRect_.Right())) &&
2365 !(LessNotEqual(y, preRect_.Top()) || GreatNotEqual(y + childSize.Height(), preRect_.Bottom()))) {
2366 placement_ = Placement::NONE;
2367 return true;
2368 }
2369 break;
2370 }
2371 case static_cast<int>(DirectionState::Right_Direction):
2372 case static_cast<int>(DirectionState::Left_Direction): {
2373 if ((LessNotEqual(y, preRect_.Top()) || GreatNotEqual(y + childSize.Height(), preRect_.Bottom())) &&
2374 !(LessNotEqual(x, preRect_.Left()) || GreatNotEqual(x + childSize.Width(), preRect_.Right()))) {
2375 placement_ = Placement::NONE;
2376 return true;
2377 }
2378 break;
2379 }
2380 default:
2381 return false;
2382 }
2383
2384 return false;
2385 }
2386
CheckPosition(const OffsetF & position,const SizeF & childSize)2387 bool MenuLayoutAlgorithm::CheckPosition(const OffsetF& position, const SizeF& childSize)
2388 {
2389 float xAvoid = wrapperRect_.Left() + paddingStart_;
2390 float yAvoid = wrapperRect_.Top() + paddingTop_ + param_.topSecurity;
2391 float maxWidth = wrapperSize_.Width() - paddingEnd_ - paddingStart_;
2392 float maxHeight = wrapperSize_.Height() - paddingTop_ - param_.topSecurity - paddingBottom_ - param_.bottomSecurity;
2393 Rect rect;
2394 Rect targetRect = Rect(targetOffset_.GetX(), targetOffset_.GetY(), targetSize_.Width(), targetSize_.Height());
2395 switch (placement_) {
2396 case Placement::BOTTOM_LEFT:
2397 case Placement::BOTTOM_RIGHT:
2398 case Placement::BOTTOM: {
2399 state_ = static_cast<int>(DirectionState::Bottom_Direction);
2400 auto y = std::max<float>(targetRect.Bottom(), yAvoid);
2401 auto height = std::min<float>(
2402 wrapperRect_.Bottom() - targetRect.Bottom() - paddingBottom_ - param_.bottomSecurity, maxHeight);
2403 rect.SetRect(xAvoid, y, maxWidth, height);
2404 break;
2405 }
2406 case Placement::TOP_LEFT:
2407 case Placement::TOP_RIGHT:
2408 case Placement::TOP: {
2409 state_ = static_cast<int>(DirectionState::Top_Direction);
2410 auto height = std::min<float>(targetRect.Top() - yAvoid, maxHeight);
2411 rect.SetRect(xAvoid, yAvoid, maxWidth, height);
2412 break;
2413 }
2414 case Placement::RIGHT_TOP:
2415 case Placement::RIGHT_BOTTOM:
2416 case Placement::RIGHT: {
2417 state_ = static_cast<int>(DirectionState::Right_Direction);
2418 auto x = std::max<float>(targetRect.Right(), xAvoid);
2419 auto width = std::min<float>(wrapperRect_.Right() - targetRect.Right() - paddingEnd_, maxWidth);
2420 rect.SetRect(x, yAvoid, width, maxHeight);
2421 break;
2422 }
2423 case Placement::LEFT_TOP:
2424 case Placement::LEFT_BOTTOM:
2425 case Placement::LEFT: {
2426 state_ = static_cast<int>(DirectionState::Left_Direction);
2427 auto width = std::min<float>(targetRect.Left() - xAvoid, maxWidth);
2428 rect.SetRect(xAvoid, yAvoid, width, maxHeight);
2429 break;
2430 }
2431 default:
2432 state_ = static_cast<int>(DirectionState::None_Direction);
2433 return false;
2434 }
2435 return CheckPositionInPlacementRect(rect, position, childSize);
2436 }
2437
GetPositionWithPlacementTop(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)2438 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacementTop(
2439 const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
2440 {
2441 return topPosition;
2442 }
2443
GetPositionWithPlacementTopLeft(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)2444 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacementTopLeft(
2445 const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
2446 {
2447 OffsetF childPosition;
2448 float marginRight = 0.0f;
2449 float marginBottom = 0.0f;
2450 childPosition = OffsetF(
2451 targetOffset_.GetX() - marginRight, targetOffset_.GetY() - childSize.Height() - marginBottom - targetSpace_);
2452 return childPosition;
2453 }
2454
GetPositionWithPlacementTopRight(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)2455 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacementTopRight(
2456 const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
2457 {
2458 OffsetF childPosition;
2459 float marginBottom = 0.0f;
2460 float marginLeft = 0.0f;
2461 childPosition = OffsetF(targetOffset_.GetX() + targetSize_.Width() - childSize.Width() + marginLeft,
2462 targetOffset_.GetY() - childSize.Height() - targetSpace_ - marginBottom);
2463 return childPosition;
2464 }
2465
GetPositionWithPlacementBottom(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)2466 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacementBottom(
2467 const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
2468 {
2469 return bottomPosition;
2470 }
2471
GetPositionWithPlacementBottomLeft(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)2472 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacementBottomLeft(
2473 const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
2474 {
2475 OffsetF childPosition;
2476 float marginRight = 0.0f;
2477 float marginTop = 0.0f;
2478 childPosition = OffsetF(
2479 targetOffset_.GetX() - marginRight, targetOffset_.GetY() + targetSize_.Height() + targetSpace_ + marginTop);
2480 return childPosition;
2481 }
2482
GetPositionWithPlacementBottomRight(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)2483 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacementBottomRight(
2484 const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
2485 {
2486 OffsetF childPosition;
2487 float marginTop = 0.0f;
2488 float marginLeft = 0.0f;
2489 childPosition = OffsetF(targetOffset_.GetX() + targetSize_.Width() - childSize.Width() + marginLeft,
2490 targetOffset_.GetY() + targetSize_.Height() + targetSpace_ + marginTop);
2491 return childPosition;
2492 }
2493
GetPositionWithPlacementLeft(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)2494 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacementLeft(
2495 const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
2496 {
2497 OffsetF childPosition;
2498 float marginRight = 0.0f;
2499 childPosition = OffsetF(targetOffset_.GetX() - targetSpace_ - childSize.Width() - marginRight,
2500 targetOffset_.GetY() + targetSize_.Height() / 2.0 - childSize.Height() / 2.0);
2501 return childPosition;
2502 }
2503
GetPositionWithPlacementLeftTop(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)2504 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacementLeftTop(
2505 const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
2506 {
2507 OffsetF childPosition;
2508 float marginRight = 0.0f;
2509 float marginBottom = 0.0f;
2510 childPosition = OffsetF(
2511 targetOffset_.GetX() - targetSpace_ - childSize.Width() - marginRight, targetOffset_.GetY() - marginBottom);
2512 return childPosition;
2513 }
2514
GetPositionWithPlacementLeftBottom(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)2515 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacementLeftBottom(
2516 const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
2517 {
2518 OffsetF childPosition;
2519 float marginRight = 0.0f;
2520 float marginTop = 0.0f;
2521 childPosition = OffsetF(targetOffset_.GetX() - targetSpace_ - childSize.Width() - marginRight,
2522 targetOffset_.GetY() + targetSize_.Height() - childSize.Height() - marginTop);
2523 return childPosition;
2524 }
2525
GetPositionWithPlacementRight(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)2526 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacementRight(
2527 const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
2528 {
2529 OffsetF childPosition;
2530 float marginLeft = 0.0f;
2531 childPosition = OffsetF(targetOffset_.GetX() + targetSize_.Width() + targetSpace_ + marginLeft,
2532 targetOffset_.GetY() + targetSize_.Height() / 2.0 - childSize.Height() / 2.0);
2533 return childPosition;
2534 }
2535
GetPositionWithPlacementRightTop(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)2536 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacementRightTop(
2537 const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
2538 {
2539 OffsetF childPosition;
2540 float marginBottom = 0.0f;
2541 float marginLeft = 0.0f;
2542 childPosition = OffsetF(
2543 targetOffset_.GetX() + targetSize_.Width() + targetSpace_ + marginLeft, targetOffset_.GetY() - marginBottom);
2544 return childPosition;
2545 }
2546
GetPositionWithPlacementRightBottom(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)2547 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacementRightBottom(
2548 const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
2549 {
2550 OffsetF childPosition;
2551 float marginTop = 0.0f;
2552 float marginLeft = 0.0f;
2553 childPosition = OffsetF(targetOffset_.GetX() + targetSize_.Width() + targetSpace_ + marginLeft,
2554 targetOffset_.GetY() + targetSize_.Height() - childSize.Height() - marginTop);
2555 return childPosition;
2556 }
2557
InitCanExpandCurrentWindow(bool isShowInSubWindow)2558 void MenuLayoutAlgorithm::InitCanExpandCurrentWindow(bool isShowInSubWindow)
2559 {
2560 auto pipelineContext = GetCurrentPipelineContext();
2561 CHECK_NULL_VOID(pipelineContext);
2562 auto containerId = Container::CurrentId();
2563 auto container = AceEngine::Get().GetContainer(containerId);
2564 if (containerId >= MIN_SUBCONTAINER_ID) {
2565 auto parentContainerId = SubwindowManager::GetInstance()->GetParentContainerId(containerId);
2566 container = AceEngine::Get().GetContainer(parentContainerId);
2567 }
2568 CHECK_NULL_VOID(container);
2569 // Get FreeMultiWindow status of main window or host window
2570 isFreeMultiWindow_ = container->IsFreeMultiWindow();
2571 auto theme = pipelineContext->GetTheme<SelectTheme>();
2572 CHECK_NULL_VOID(theme);
2573 // false for phone devices
2574 isExpandDisplay_ = theme->GetExpandDisplay() || isFreeMultiWindow_;
2575 if (isExpandDisplay_ && !isShowInSubWindow && containerId >= MIN_SUBCONTAINER_ID) {
2576 canExpandCurrentWindow_ = true;
2577 return;
2578 }
2579 canExpandCurrentWindow_ = isExpandDisplay_ && isShowInSubWindow;
2580 if (containerId >= MIN_SUBCONTAINER_ID) {
2581 auto parentContainerId = SubwindowManager::GetInstance()->GetParentContainerId(containerId);
2582 container = AceEngine::Get().GetContainer(parentContainerId);
2583 CHECK_NULL_VOID(container);
2584 isUIExtensionSubWindow_ = container->IsUIExtensionWindow();
2585 if (isUIExtensionSubWindow_) {
2586 canExpandCurrentWindow_ = true;
2587 auto subwindow = SubwindowManager::GetInstance()->GetSubwindow(parentContainerId);
2588 CHECK_NULL_VOID(subwindow);
2589 auto rect = subwindow->GetUIExtensionHostWindowRect();
2590 UIExtensionHostWindowRect_ = RectF(rect.Left(), rect.Top(), rect.Width(), rect.Height());
2591 TAG_LOGI(AceLogTag::ACE_MENU, "GetUIExtensionHostWindowRect : %{public}s",
2592 UIExtensionHostWindowRect_.ToString().c_str());
2593 }
2594 }
2595 }
2596
GetMenuWindowRectInfo(const RefPtr<MenuPattern> & menuPattern)2597 Rect MenuLayoutAlgorithm::GetMenuWindowRectInfo(const RefPtr<MenuPattern>& menuPattern)
2598 {
2599 auto menuWindowRect = Rect();
2600 CHECK_NULL_RETURN(menuPattern, menuWindowRect);
2601 auto pipelineContext = GetCurrentPipelineContext();
2602 CHECK_NULL_RETURN(pipelineContext, menuWindowRect);
2603 auto rect = pipelineContext->GetDisplayWindowRectInfo();
2604 displayWindowRect_ = RectF(rect.Left(), rect.Top(), rect.Width(), rect.Height());
2605 TAG_LOGI(AceLogTag::ACE_MENU, "GetDisplayWindowRectInfo : %{public}s", displayWindowRect_.ToString().c_str());
2606 menuWindowRect = Rect(rect.Left(), rect.Top(), rect.Width(), rect.Height());
2607 auto availableRect = pipelineContext->GetDisplayAvailableRect();
2608 TAG_LOGI(AceLogTag::ACE_MENU, "GetDisplayAvailableRect : %{public}s", availableRect.ToString().c_str());
2609 if (canExpandCurrentWindow_ && isExpandDisplay_) {
2610 menuWindowRect = Rect(availableRect.Left(), availableRect.Top(), availableRect.Width(), availableRect.Height());
2611 } else if (isUIExtensionSubWindow_ && !isExpandDisplay_) {
2612 rect = Rect(UIExtensionHostWindowRect_.Left(), UIExtensionHostWindowRect_.Top(),
2613 UIExtensionHostWindowRect_.Width(), UIExtensionHostWindowRect_.Height());
2614 menuWindowRect = rect;
2615 }
2616 TAG_LOGI(AceLogTag::ACE_MENU, "GetMenuWindowRectInfo : %{public}s", menuWindowRect.ToString().c_str());
2617 dumpInfo_.menuWindowRect = menuWindowRect;
2618 menuPattern->SetMenuWindowRect(menuWindowRect);
2619 return menuWindowRect;
2620 }
2621
ModifyTargetOffset()2622 void MenuLayoutAlgorithm::ModifyTargetOffset()
2623 {
2624 TAG_LOGI(AceLogTag::ACE_MENU, "original targetOffset is : %{public}s", targetOffset_.ToString().c_str());
2625 if (canExpandCurrentWindow_ && isExpandDisplay_) {
2626 targetOffset_ += displayWindowRect_.GetOffset();
2627 TAG_LOGI(AceLogTag::ACE_MENU, "ModifyTargetOffset for displayAvailableRect : %{public}s",
2628 targetOffset_.ToString().c_str());
2629 } else if (isUIExtensionSubWindow_ && !isExpandDisplay_) {
2630 targetOffset_ += displayWindowRect_.GetOffset() - UIExtensionHostWindowRect_.GetOffset();
2631 TAG_LOGI(AceLogTag::ACE_MENU, "ModifyTargetOffset for UIExtensionHostWindowRect : %{public}s",
2632 targetOffset_.ToString().c_str());
2633 }
2634 }
2635
CheckIsEmbeddedMode(LayoutWrapper * layoutWrapper)2636 bool MenuLayoutAlgorithm::CheckIsEmbeddedMode(LayoutWrapper* layoutWrapper)
2637 {
2638 auto menuNode = layoutWrapper->GetHostNode();
2639 CHECK_NULL_RETURN(menuNode, false);
2640 auto menuNodePattern = AceType::DynamicCast<MenuPattern>(menuNode->GetPattern());
2641 CHECK_NULL_RETURN(menuNodePattern, false);
2642 auto menuWrapper = menuNodePattern->GetMenuWrapper();
2643 CHECK_NULL_RETURN(menuWrapper, false);
2644 auto menuWrapperPattern = menuWrapper->GetPattern<MenuWrapperPattern>();
2645 CHECK_NULL_RETURN(menuWrapperPattern, false);
2646 auto innerMenu = menuWrapperPattern->GetMenuChild(menuNode);
2647 CHECK_NULL_RETURN(innerMenu, false);
2648 auto innerMenuPattern = AceType::DynamicCast<MenuPattern>(innerMenu->GetPattern());
2649 CHECK_NULL_RETURN(innerMenuPattern, false);
2650 auto layoutProps = innerMenuPattern->GetLayoutProperty<MenuLayoutProperty>();
2651 CHECK_NULL_RETURN(layoutProps, false);
2652 return layoutProps->GetExpandingMode().value_or(SubMenuExpandingMode::SIDE) == SubMenuExpandingMode::EMBEDDED;
2653 }
2654
2655 } // namespace OHOS::Ace::NG
2656