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