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