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/memory/ace_type.h"
24 #include "base/memory/referenced.h"
25 #include "base/subwindow/subwindow_manager.h"
26 #include "base/utils/utils.h"
27 #include "core/common/ace_engine.h"
28 #include "core/components/common/layout/grid_system_manager.h"
29 #include "core/components/common/properties/placement.h"
30 #include "core/components/container_modal/container_modal_constants.h"
31 #include "core/components_ng/pattern/menu/menu_layout_property.h"
32 #include "core/components_ng/pattern/menu/menu_pattern.h"
33 #include "core/components_ng/pattern/menu/wrapper/menu_wrapper_pattern.h"
34 #include "core/components_ng/property/layout_constraint.h"
35 #include "core/components_ng/property/measure_property.h"
36 #include "core/pipeline/pipeline_base.h"
37 #include "core/pipeline_ng/pipeline_context.h"
38 namespace OHOS::Ace::NG {
39
40 namespace {
41 constexpr uint32_t MIN_GRID_COUNTS = 2;
42 constexpr uint32_t GRID_COUNTS_4 = 4;
43 constexpr uint32_t GRID_COUNTS_6 = 6;
44 constexpr uint32_t GRID_COUNTS_8 = 8;
45 constexpr uint32_t GRID_COUNTS_12 = 12;
46 constexpr int32_t PLATFORM_VERSION_TEN = 10;
47 constexpr size_t ALIGNMENT_STEP_OFFSET = 2;
48
49 const std::map<Placement, std::vector<Placement>> PLACEMENT_STATES = {
50 { Placement::BOTTOM_LEFT,
51 {
52 Placement::BOTTOM_LEFT,
53 Placement::BOTTOM_RIGHT,
54 Placement::TOP_LEFT,
55 Placement::TOP_RIGHT,
56 Placement::RIGHT_TOP,
57 Placement::RIGHT_BOTTOM,
58 Placement::LEFT_TOP,
59 Placement::LEFT_BOTTOM,
60 Placement::NONE,
61 } },
62 { Placement::BOTTOM,
63 {
64 Placement::BOTTOM,
65 Placement::BOTTOM_LEFT,
66 Placement::BOTTOM_RIGHT,
67 Placement::TOP,
68 Placement::TOP_LEFT,
69 Placement::TOP_RIGHT,
70 Placement::RIGHT,
71 Placement::RIGHT_TOP,
72 Placement::RIGHT_BOTTOM,
73 Placement::LEFT,
74 Placement::LEFT_TOP,
75 Placement::LEFT_BOTTOM,
76 Placement::NONE,
77 } },
78 { Placement::BOTTOM_RIGHT,
79 {
80 Placement::BOTTOM_RIGHT,
81 Placement::BOTTOM_LEFT,
82 Placement::TOP_RIGHT,
83 Placement::TOP_LEFT,
84 Placement::RIGHT_BOTTOM,
85 Placement::RIGHT_TOP,
86 Placement::LEFT_BOTTOM,
87 Placement::LEFT_TOP,
88 Placement::NONE,
89 } },
90 { Placement::TOP_LEFT,
91 {
92 Placement::TOP_LEFT,
93 Placement::TOP_RIGHT,
94 Placement::BOTTOM_LEFT,
95 Placement::BOTTOM_RIGHT,
96 Placement::RIGHT_TOP,
97 Placement::RIGHT_BOTTOM,
98 Placement::LEFT_TOP,
99 Placement::LEFT_BOTTOM,
100 Placement::NONE,
101 } },
102 { Placement::TOP,
103 {
104 Placement::TOP,
105 Placement::TOP_LEFT,
106 Placement::TOP_RIGHT,
107 Placement::BOTTOM,
108 Placement::BOTTOM_LEFT,
109 Placement::BOTTOM_RIGHT,
110 Placement::RIGHT,
111 Placement::RIGHT_TOP,
112 Placement::RIGHT_BOTTOM,
113 Placement::LEFT,
114 Placement::LEFT_TOP,
115 Placement::LEFT_BOTTOM,
116 Placement::NONE,
117 } },
118 { Placement::TOP_RIGHT,
119 {
120 Placement::TOP_RIGHT,
121 Placement::TOP_LEFT,
122 Placement::BOTTOM_RIGHT,
123 Placement::BOTTOM_LEFT,
124 Placement::RIGHT_BOTTOM,
125 Placement::RIGHT_TOP,
126 Placement::LEFT_BOTTOM,
127 Placement::LEFT_TOP,
128 Placement::NONE,
129 } },
130 { Placement::LEFT_TOP,
131 {
132 Placement::LEFT_TOP,
133 Placement::LEFT_BOTTOM,
134 Placement::RIGHT_TOP,
135 Placement::RIGHT_BOTTOM,
136 Placement::BOTTOM_LEFT,
137 Placement::BOTTOM_RIGHT,
138 Placement::TOP_LEFT,
139 Placement::TOP_RIGHT,
140 Placement::NONE,
141 } },
142 { Placement::LEFT,
143 {
144 Placement::LEFT,
145 Placement::LEFT_TOP,
146 Placement::LEFT_BOTTOM,
147 Placement::RIGHT,
148 Placement::RIGHT_TOP,
149 Placement::RIGHT_BOTTOM,
150 Placement::BOTTOM,
151 Placement::BOTTOM_LEFT,
152 Placement::BOTTOM_RIGHT,
153 Placement::TOP,
154 Placement::TOP_LEFT,
155 Placement::TOP_RIGHT,
156 Placement::NONE,
157 } },
158 { Placement::LEFT_BOTTOM,
159 {
160 Placement::LEFT_BOTTOM,
161 Placement::LEFT_TOP,
162 Placement::RIGHT_BOTTOM,
163 Placement::RIGHT_TOP,
164 Placement::BOTTOM_RIGHT,
165 Placement::BOTTOM_LEFT,
166 Placement::TOP_RIGHT,
167 Placement::TOP_LEFT,
168 Placement::NONE,
169 } },
170 { Placement::RIGHT_TOP,
171 {
172 Placement::RIGHT_TOP,
173 Placement::RIGHT_BOTTOM,
174 Placement::LEFT_TOP,
175 Placement::LEFT_BOTTOM,
176 Placement::BOTTOM_LEFT,
177 Placement::BOTTOM_RIGHT,
178 Placement::TOP_LEFT,
179 Placement::TOP_RIGHT,
180 Placement::NONE,
181 } },
182 { Placement::RIGHT,
183 {
184 Placement::RIGHT,
185 Placement::RIGHT_TOP,
186 Placement::RIGHT_BOTTOM,
187 Placement::LEFT,
188 Placement::LEFT_TOP,
189 Placement::LEFT_BOTTOM,
190 Placement::BOTTOM,
191 Placement::BOTTOM_LEFT,
192 Placement::BOTTOM_RIGHT,
193 Placement::TOP,
194 Placement::TOP_LEFT,
195 Placement::TOP_RIGHT,
196 Placement::NONE,
197 } },
198 { Placement::RIGHT_BOTTOM,
199 {
200 Placement::RIGHT_BOTTOM,
201 Placement::RIGHT_TOP,
202 Placement::LEFT_BOTTOM,
203 Placement::LEFT_TOP,
204 Placement::BOTTOM_RIGHT,
205 Placement::BOTTOM_LEFT,
206 Placement::TOP_RIGHT,
207 Placement::TOP_LEFT,
208 Placement::NONE,
209 } },
210 };
211
GetMaxGridCounts(const RefPtr<GridColumnInfo> & columnInfo)212 uint32_t GetMaxGridCounts(const RefPtr<GridColumnInfo>& columnInfo)
213 {
214 CHECK_NULL_RETURN(columnInfo, GRID_COUNTS_8);
215 auto currentColumns = columnInfo->GetParent()->GetColumns();
216 auto maxGridCounts = GRID_COUNTS_8;
217 switch (currentColumns) {
218 case GRID_COUNTS_4:
219 maxGridCounts = GRID_COUNTS_4;
220 break;
221 case GRID_COUNTS_8:
222 maxGridCounts = GRID_COUNTS_6;
223 break;
224 case GRID_COUNTS_12:
225 maxGridCounts = GRID_COUNTS_8;
226 break;
227 default:
228 break;
229 }
230 return maxGridCounts;
231 }
232 } // namespace
233
MenuLayoutAlgorithm(int32_t id,const std::string & tag)234 MenuLayoutAlgorithm::MenuLayoutAlgorithm(int32_t id, const std::string& tag) : targetNodeId_(id), targetTag_(tag)
235 {
236 placementFuncMap_[Placement::TOP] = &MenuLayoutAlgorithm::GetPositionWithPlacementTop;
237 placementFuncMap_[Placement::TOP_LEFT] = &MenuLayoutAlgorithm::GetPositionWithPlacementTopLeft;
238 placementFuncMap_[Placement::TOP_RIGHT] = &MenuLayoutAlgorithm::GetPositionWithPlacementTopRight;
239 placementFuncMap_[Placement::BOTTOM] = &MenuLayoutAlgorithm::GetPositionWithPlacementBottom;
240 placementFuncMap_[Placement::BOTTOM_LEFT] = &MenuLayoutAlgorithm::GetPositionWithPlacementBottomLeft;
241 placementFuncMap_[Placement::BOTTOM_RIGHT] = &MenuLayoutAlgorithm::GetPositionWithPlacementBottomRight;
242 placementFuncMap_[Placement::LEFT] = &MenuLayoutAlgorithm::GetPositionWithPlacementLeft;
243 placementFuncMap_[Placement::LEFT_TOP] = &MenuLayoutAlgorithm::GetPositionWithPlacementLeftTop;
244 placementFuncMap_[Placement::LEFT_BOTTOM] = &MenuLayoutAlgorithm::GetPositionWithPlacementLeftBottom;
245 placementFuncMap_[Placement::RIGHT] = &MenuLayoutAlgorithm::GetPositionWithPlacementRight;
246 placementFuncMap_[Placement::RIGHT_TOP] = &MenuLayoutAlgorithm::GetPositionWithPlacementRightTop;
247 placementFuncMap_[Placement::RIGHT_BOTTOM] = &MenuLayoutAlgorithm::GetPositionWithPlacementRightBottom;
248
249 setHorizontal_ = { Placement::LEFT, Placement::LEFT_BOTTOM, Placement::LEFT_TOP,
250 Placement::RIGHT, Placement::RIGHT_BOTTOM, Placement::RIGHT_TOP };
251 setVertical_ = { Placement::TOP, Placement::TOP_LEFT, Placement::TOP_RIGHT,
252 Placement::BOTTOM, Placement::BOTTOM_LEFT, Placement::BOTTOM_RIGHT };
253 }
254
~MenuLayoutAlgorithm()255 MenuLayoutAlgorithm::~MenuLayoutAlgorithm()
256 {
257 placementFuncMap_.clear();
258 setHorizontal_.clear();
259 setVertical_.clear();
260 }
261
Initialize(LayoutWrapper * layoutWrapper)262 void MenuLayoutAlgorithm::Initialize(LayoutWrapper* layoutWrapper)
263 {
264 CHECK_NULL_VOID(layoutWrapper);
265 // currently using click point as menu position
266 auto props = AceType::DynamicCast<MenuLayoutProperty>(layoutWrapper->GetLayoutProperty());
267 CHECK_NULL_VOID(props);
268 auto menuPattern = layoutWrapper->GetHostNode()->GetPattern<MenuPattern>();
269 auto targetSize = props->GetTargetSizeValue(SizeF());
270 position_ = props->GetMenuOffset().value_or(OffsetF());
271 positionOffset_ = props->GetPositionOffset().value_or(OffsetF());
272 LOGD("menu position_ = %{public}s, targetSize = %{public}s", position_.ToString().c_str(),
273 targetSize.ToString().c_str());
274 InitializePadding(layoutWrapper);
275 auto constraint = props->GetLayoutConstraint();
276 auto wrapperIdealSize =
277 CreateIdealSize(constraint.value(), Axis::FREE, props->GetMeasureType(MeasureType::MATCH_PARENT), true);
278 wrapperSize_ = wrapperIdealSize;
279
280 ModifyPositionToWrapper(layoutWrapper, position_);
281 if (menuPattern->IsSelectOverlayExtensionMenu()) {
282 topSpace_ = 0.0f;
283 bottomSpace_ = constraint->maxSize.Height() - position_.GetY();
284 leftSpace_ = Infinity<float>();
285 } else {
286 if (props->GetMenuPlacement().has_value()) {
287 auto targetSecurity = static_cast<float>(TARGET_SECURITY.ConvertToPx());
288 topSpace_ = std::max(0.0f, targetOffset_.GetY() - targetSecurity - paddingTop_);
289 bottomSpace_ = std::max(0.0f,
290 wrapperSize_.Height() - targetOffset_.GetY() - targetSize_.Height() - targetSecurity - paddingBottom_);
291 leftSpace_ = std::max(0.0f, targetOffset_.GetX() - paddingStart_ - targetSecurity);
292 rightSpace_ = std::max(
293 0.0f, wrapperSize_.Width() - targetSize_.Width() - targetSecurity - paddingStart_ - paddingEnd_);
294 } else {
295 topSpace_ = position_.GetY() - targetSize.Height() - margin_ * 2.0f;
296 bottomSpace_ = wrapperSize_.Height() - position_.GetY() - margin_ * 2.0f;
297 leftSpace_ = position_.GetX();
298 rightSpace_ = wrapperSize_.Width() - leftSpace_;
299 }
300 }
301
302 placement_ = props->GetMenuPlacement().value_or(Placement::BOTTOM_LEFT);
303 }
304
InitializePadding(LayoutWrapper * layoutWrapper)305 void MenuLayoutAlgorithm::InitializePadding(LayoutWrapper* layoutWrapper)
306 {
307 auto menuPattern = layoutWrapper->GetHostNode()->GetPattern<MenuPattern>();
308 CHECK_NULL_VOID(menuPattern);
309 auto pipeline = PipelineBase::GetCurrentContext();
310 CHECK_NULL_VOID(pipeline);
311 auto theme = pipeline->GetTheme<SelectTheme>();
312 CHECK_NULL_VOID(theme);
313 if (!menuPattern->IsSelectOverlayExtensionMenu()) {
314 margin_ = static_cast<float>(theme->GetOutPadding().ConvertToPx());
315 optionPadding_ = margin_;
316 paddingStart_ = static_cast<float>(theme->GetDefaultPaddingStart().ConvertToPx());
317 paddingEnd_ = static_cast<float>(theme->GetDefaultPaddingEnd().ConvertToPx());
318 paddingTop_ = static_cast<float>(theme->GetDefaultPaddingTop().ConvertToPx());
319 paddingBottom_ = static_cast<float>(theme->GetDefaultPaddingBottomFixed().ConvertToPx());
320 } else {
321 optionPadding_ = static_cast<float>(theme->GetOutPadding().ConvertToPx());
322 }
323 }
324
ModifyPositionToWrapper(LayoutWrapper * layoutWrapper,OffsetF & position)325 void MenuLayoutAlgorithm::ModifyPositionToWrapper(LayoutWrapper* layoutWrapper, OffsetF& position)
326 {
327 auto menu = layoutWrapper->GetHostNode();
328 auto wrapper = AceType::DynamicCast<FrameNode>(menu->GetParent());
329 CHECK_NULL_VOID(wrapper);
330
331 OffsetF wrapperOffset;
332 // minus wrapper offset in LayoutFullScreen
333 auto wrapperLayoutProps = wrapper->GetLayoutProperty();
334 CHECK_NULL_VOID(wrapperLayoutProps);
335 auto&& safeAreaInsets = wrapperLayoutProps->GetSafeAreaInsets();
336 if (safeAreaInsets) {
337 wrapperOffset +=
338 OffsetF(static_cast<float>(safeAreaInsets->left_.end), static_cast<float>(safeAreaInsets->top_.end));
339 position -= wrapperOffset;
340 }
341
342 auto menuPattern = menu->GetPattern<MenuPattern>();
343 CHECK_NULL_VOID(menuPattern);
344 bool isSubMenu = menuPattern->IsSubMenu() || menuPattern->IsSelectOverlaySubMenu();
345 if (menuPattern->IsContextMenu() || (isSubMenu && Container::CurrentId() >= MIN_SUBCONTAINER_ID)) {
346 // no need to modify for context menu, because context menu wrapper is full screen.
347 return;
348 }
349 // minus wrapper offset in floating window
350 auto pipelineContext = GetCurrentPipelineContext();
351 CHECK_NULL_VOID(pipelineContext);
352 auto windowManager = pipelineContext->GetWindowManager();
353 auto isContainerModal = pipelineContext->GetWindowModal() == WindowModal::CONTAINER_MODAL && windowManager &&
354 windowManager->GetWindowMode() == WindowMode::WINDOW_MODE_FLOATING;
355 if (isContainerModal) {
356 wrapperOffset = OffsetF(static_cast<float>((CONTAINER_BORDER_WIDTH + CONTENT_PADDING).ConvertToPx()),
357 static_cast<float>((CONTAINER_TITLE_HEIGHT + CONTAINER_BORDER_WIDTH).ConvertToPx()));
358 position -= wrapperOffset;
359 }
360 }
361
362 // Called to perform layout render node and child.
Measure(LayoutWrapper * layoutWrapper)363 void MenuLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper)
364 {
365 // initialize screen size and menu position
366 CHECK_NULL_VOID(layoutWrapper);
367 auto menuNode = layoutWrapper->GetHostNode();
368 CHECK_NULL_VOID(menuNode);
369 auto menuPattern = menuNode->GetPattern<MenuPattern>();
370 CHECK_NULL_VOID(menuPattern);
371 if (!targetTag_.empty()) {
372 InitTargetSizeAndPosition(layoutWrapper, menuPattern->IsContextMenu());
373 }
374 Initialize(layoutWrapper);
375
376 auto menuLayoutProperty = AceType::DynamicCast<MenuLayoutProperty>(layoutWrapper->GetLayoutProperty());
377 CHECK_NULL_VOID(menuLayoutProperty);
378 const auto& constraint = menuLayoutProperty->GetLayoutConstraint();
379 if (!constraint) {
380 LOGE("fail to measure menu due to layoutConstraint is nullptr");
381 return;
382 }
383 auto idealSize = CreateIdealSize(
384 constraint.value(), Axis::VERTICAL, menuLayoutProperty->GetMeasureType(MeasureType::MATCH_CONTENT), true);
385 const auto& padding = menuLayoutProperty->CreatePaddingAndBorder();
386 MinusPaddingToSize(padding, idealSize);
387
388 // calculate menu main size
389 auto childConstraint = CreateChildConstraint(layoutWrapper);
390 float idealHeight = 0.0f;
391 float idealWidth = 0.0f;
392 for (const auto& child : layoutWrapper->GetAllChildrenWithBuild()) {
393 child->Measure(childConstraint);
394 auto childSize = child->GetGeometryNode()->GetMarginFrameSize();
395 LOGD("child finish measure, child %{public}s size = %{public}s", child->GetHostTag().c_str(),
396 child->GetGeometryNode()->GetMarginFrameSize().ToString().c_str());
397 idealHeight += childSize.Height();
398 idealWidth = std::max(idealWidth, childSize.Width());
399 }
400 idealSize.SetHeight(idealHeight);
401 idealSize.SetWidth(idealWidth);
402 AddPaddingToSize(padding, idealSize);
403
404 auto geometryNode = layoutWrapper->GetGeometryNode();
405 CHECK_NULL_VOID(geometryNode);
406 LOGD("finish measure, menu size = %{public}f x %{public}f", idealSize.Width(), idealSize.Height());
407 geometryNode->SetFrameSize(idealSize);
408 }
409
Layout(LayoutWrapper * layoutWrapper)410 void MenuLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper)
411 {
412 CHECK_NULL_VOID(layoutWrapper);
413 auto menuProp = DynamicCast<MenuLayoutProperty>(layoutWrapper->GetLayoutProperty());
414 CHECK_NULL_VOID(menuProp);
415 auto menuNode = layoutWrapper->GetHostNode();
416 CHECK_NULL_VOID(menuNode);
417 auto menuPattern = menuNode->GetPattern<MenuPattern>();
418 CHECK_NULL_VOID(menuPattern);
419
420 if (!menuPattern->IsSelectOverlayExtensionMenu()) {
421 auto geometryNode = layoutWrapper->GetGeometryNode();
422 CHECK_NULL_VOID(geometryNode);
423 auto size = geometryNode->GetMarginFrameSize();
424 bool didNeedArrow = GetIfNeedArrow(layoutWrapper, size);
425 if (menuPattern->IsSelectMenu()) {
426 ComputeMenuPositionByAlignType(menuProp, size);
427 auto offset = ComputeMenuPositionByOffset(menuProp, geometryNode);
428 position_ += offset;
429 }
430 auto menuPosition = MenuLayoutAvoidAlgorithm(menuProp, menuPattern, size, didNeedArrow);
431 SetMenuPlacementForAnimation(layoutWrapper);
432 arrowPosition_ = GetArrowPositionWithPlacement(size);
433 if (didNeedArrow && arrowPlacement_ != Placement::NONE) {
434 LayoutArrow(layoutWrapper);
435 }
436 LOGD("Menu layout, offset = %{public}s", menuPosition.ToString().c_str());
437 geometryNode->SetFrameOffset(menuPosition);
438 }
439
440 // translate each option by the height of previous options
441 OffsetF translate;
442 for (const auto& child : layoutWrapper->GetAllChildrenWithBuild()) {
443 child->GetGeometryNode()->SetMarginFrameOffset(translate);
444 child->Layout();
445 translate += OffsetF(0, child->GetGeometryNode()->GetFrameSize().Height());
446 }
447 }
448
SetMenuPlacementForAnimation(LayoutWrapper * layoutWrapper)449 void MenuLayoutAlgorithm::SetMenuPlacementForAnimation(LayoutWrapper* layoutWrapper)
450 {
451 auto menu = layoutWrapper->GetHostNode();
452 CHECK_NULL_VOID(menu);
453 auto menuPattern = menu->GetPattern<MenuPattern>();
454 CHECK_NULL_VOID(menuPattern);
455 auto menuWrapper = menuPattern->GetMenuWrapper();
456 CHECK_NULL_VOID(menuWrapper);
457 auto wrapperPattern = menuWrapper->GetPattern<MenuWrapperPattern>();
458 CHECK_NULL_VOID(wrapperPattern);
459 wrapperPattern->SetMenuPlacementAfterLayout(placement_);
460 }
461
LayoutArrow(const LayoutWrapper * layoutWrapper)462 void MenuLayoutAlgorithm::LayoutArrow(const LayoutWrapper* layoutWrapper)
463 {
464 auto paintProperty = GetPaintProperty(layoutWrapper);
465 CHECK_NULL_VOID(paintProperty);
466 paintProperty->UpdateArrowPosition(arrowPosition_);
467 paintProperty->UpdateArrowPlacement(arrowPlacement_);
468 }
469
GetPaintProperty(const LayoutWrapper * layoutWrapper)470 RefPtr<MenuPaintProperty> MenuLayoutAlgorithm::GetPaintProperty(const LayoutWrapper* layoutWrapper)
471 {
472 auto menuNode = layoutWrapper->GetHostNode();
473 CHECK_NULL_RETURN(menuNode, nullptr);
474 auto paintProperty = menuNode->GetPaintProperty<MenuPaintProperty>();
475 CHECK_NULL_RETURN(paintProperty, nullptr);
476 return paintProperty;
477 }
478
GetIfNeedArrow(const LayoutWrapper * layoutWrapper,const SizeF & menuSize)479 bool MenuLayoutAlgorithm::GetIfNeedArrow(const LayoutWrapper* layoutWrapper, const SizeF& menuSize)
480 {
481 CHECK_NULL_RETURN(layoutWrapper, false);
482 auto menuNode = layoutWrapper->GetHostNode();
483 CHECK_NULL_RETURN(menuNode, false);
484 auto menuPattern = menuNode->GetPattern<MenuPattern>();
485 CHECK_NULL_RETURN(menuPattern, false);
486 auto menuProp = DynamicCast<MenuLayoutProperty>(layoutWrapper->GetLayoutProperty());
487 CHECK_NULL_RETURN(menuProp, false);
488 auto paintProperty = GetPaintProperty(layoutWrapper);
489 CHECK_NULL_RETURN(paintProperty, false);
490 propNeedArrow_ = paintProperty->GetEnableArrow().value_or(false);
491
492 auto pipeline = PipelineBase::GetCurrentContext();
493 CHECK_NULL_RETURN(pipeline, false);
494 auto selectThemePtr = pipeline->GetTheme<SelectTheme>();
495 CHECK_NULL_RETURN(selectThemePtr, false);
496 if (!propNeedArrow_ || !menuProp->GetMenuPlacement().has_value()) {
497 return false;
498 }
499
500 propArrowOffset_ = paintProperty->GetArrowOffset().value_or(Dimension(0));
501 menuRadius_ = selectThemePtr->GetMenuBorderRadius().ConvertToPx();
502 arrowMinLimit_ = menuRadius_ + ARROW_WIDTH.ConvertToPx() / 2.0;
503 arrowWidth_ = ARROW_WIDTH.ConvertToPx();
504 auto targetSpaceReal = TARGET_SPACE.ConvertToPx();
505
506 if (setHorizontal_.find(placement_) != setHorizontal_.end()) {
507 if (menuSize.Height() >= menuRadius_ * 2 + arrowWidth_) {
508 arrowInMenu_ = true;
509 targetSpace_ = targetSpaceReal;
510 }
511 }
512
513 if (setVertical_.find(placement_) != setVertical_.end()) {
514 if (menuSize.Width() >= menuRadius_ * 2 + arrowWidth_) {
515 arrowInMenu_ = true;
516 targetSpace_ = targetSpaceReal;
517 }
518 }
519
520 return menuPattern->IsContextMenu() && !targetTag_.empty() && arrowInMenu_;
521 }
522
UpdatePropArrowOffset()523 void MenuLayoutAlgorithm::UpdatePropArrowOffset()
524 {
525 if (propArrowOffset_.IsValid()) {
526 if (propArrowOffset_.Unit() == DimensionUnit::PERCENT) {
527 propArrowOffset_.SetValue(std::clamp(propArrowOffset_.Value(), 0.0, 1.0));
528 }
529 return;
530 }
531 switch (arrowPlacement_) {
532 case Placement::LEFT:
533 case Placement::RIGHT:
534 case Placement::TOP:
535 case Placement::BOTTOM:
536 propArrowOffset_ = ARROW_HALF_PERCENT_VALUE;
537 break;
538 case Placement::TOP_LEFT:
539 case Placement::BOTTOM_LEFT:
540 case Placement::LEFT_TOP:
541 case Placement::RIGHT_TOP:
542 propArrowOffset_ = ARROW_ZERO_PERCENT_VALUE;
543 break;
544 case Placement::TOP_RIGHT:
545 case Placement::BOTTOM_RIGHT:
546 case Placement::LEFT_BOTTOM:
547 case Placement::RIGHT_BOTTOM:
548 propArrowOffset_ = ARROW_ONE_HUNDRED_PERCENT_VALUE;
549 break;
550 default:
551 break;
552 }
553 }
554
UpdateArrowOffsetWithMenuLimit(const SizeF & menuSize)555 void MenuLayoutAlgorithm::UpdateArrowOffsetWithMenuLimit(const SizeF& menuSize)
556 {
557 UpdatePropArrowOffset();
558
559 if (setHorizontal_.find(arrowPlacement_) != setHorizontal_.end()) {
560 if (menuSize.Height() >= menuRadius_ * 2 + arrowWidth_) {
561 float range = menuSize.Height() - menuRadius_ * 2 - arrowWidth_;
562 float tempOffset = propArrowOffset_.Unit() == DimensionUnit::PERCENT ? propArrowOffset_.Value() * range :
563 propArrowOffset_.ConvertToPx();
564 arrowOffset_ = std::clamp(tempOffset, 0.0f, range);
565 }
566 }
567
568 if (setVertical_.find(arrowPlacement_) != setVertical_.end()) {
569 if (menuSize.Width() >= menuRadius_ * 2 + arrowWidth_) {
570 arrowInMenu_ = true;
571 float range = menuSize.Width() - menuRadius_ * 2 - arrowWidth_;
572 float tempOffset = propArrowOffset_.Unit() == DimensionUnit::PERCENT ? propArrowOffset_.Value() * range :
573 propArrowOffset_.ConvertToPx();
574 arrowOffset_ = std::clamp(tempOffset, 0.0f, range);
575 }
576 }
577 }
578
ComputeMenuPositionByAlignType(const RefPtr<MenuLayoutProperty> & menuProp,const SizeF & menuSize)579 void MenuLayoutAlgorithm::ComputeMenuPositionByAlignType(
580 const RefPtr<MenuLayoutProperty>& menuProp, const SizeF& menuSize)
581 {
582 auto alignType = menuProp->GetAlignType().value_or(MenuAlignType::START);
583 auto targetSize = menuProp->GetTargetSizeValue(SizeF());
584 switch (alignType) {
585 case MenuAlignType::CENTER: {
586 position_.AddX(targetSize.Width() / 2.0f - menuSize.Width() / 2.0f);
587 break;
588 }
589 case MenuAlignType::END: {
590 position_.AddX(targetSize.Width() - menuSize.Width());
591 break;
592 }
593 default:
594 break;
595 }
596 }
597
ComputeMenuPositionByOffset(const RefPtr<MenuLayoutProperty> & menuProp,const RefPtr<GeometryNode> & geometryNode)598 OffsetF MenuLayoutAlgorithm::ComputeMenuPositionByOffset(
599 const RefPtr<MenuLayoutProperty>& menuProp, const RefPtr<GeometryNode>& geometryNode)
600 {
601 CHECK_NULL_RETURN(menuProp, OffsetF(0, 0));
602 CHECK_NULL_RETURN(geometryNode, OffsetF(0, 0));
603
604 const auto& layoutConstraint = menuProp->GetLayoutConstraint();
605 CHECK_NULL_RETURN(layoutConstraint, OffsetF(0, 0));
606 auto menuAlignOffset = menuProp->GetOffset().value_or(
607 DimensionOffset(Dimension(0, DimensionUnit::VP), Dimension(0, DimensionUnit::VP)));
608
609 auto menuSize = geometryNode->GetFrameSize();
610 auto menuTrimOffsetX =
611 ConvertToPx(CalcLength(menuAlignOffset.GetX()), layoutConstraint->scaleProperty, menuSize.Width());
612 auto menuTrimOffsetY =
613 ConvertToPx(CalcLength(menuAlignOffset.GetY()), layoutConstraint->scaleProperty, menuSize.Height());
614 OffsetF menuTrimOffset = OffsetF(menuTrimOffsetX.value_or(0.0), menuTrimOffsetY.value_or(0.0));
615 return menuTrimOffset;
616 }
617
GetCurrentPipelineContext()618 RefPtr<PipelineContext> MenuLayoutAlgorithm::GetCurrentPipelineContext()
619 {
620 auto containerId = Container::CurrentId();
621 RefPtr<PipelineContext> context;
622 if (containerId >= MIN_SUBCONTAINER_ID) {
623 auto parentContainerId = SubwindowManager::GetInstance()->GetParentContainerId(containerId);
624 auto parentContainer = AceEngine::Get().GetContainer(parentContainerId);
625 CHECK_NULL_RETURN(parentContainer, nullptr);
626 context = DynamicCast<PipelineContext>(parentContainer->GetPipelineContext());
627 } else {
628 context = PipelineContext::GetCurrentContext();
629 }
630 return context;
631 }
632
MenuLayoutAvoidAlgorithm(const RefPtr<MenuLayoutProperty> & menuProp,const RefPtr<MenuPattern> & menuPattern,const SizeF & size,bool didNeedArrow)633 OffsetF MenuLayoutAlgorithm::MenuLayoutAvoidAlgorithm(const RefPtr<MenuLayoutProperty>& menuProp,
634 const RefPtr<MenuPattern>& menuPattern, const SizeF& size, bool didNeedArrow)
635 {
636 CHECK_NULL_RETURN(menuProp, OffsetF(0, 0));
637 CHECK_NULL_RETURN(menuPattern, OffsetF(0, 0));
638 float x = 0.0f;
639 float y = 0.0f;
640 if (menuProp->GetMenuPlacement().has_value() && (targetSize_.Width() > 0.0 || targetSize_.Height() > 0.0)) {
641 placement_ = menuProp->GetMenuPlacement().value();
642 auto childOffset = GetChildPosition(size, didNeedArrow);
643 x = childOffset.GetX();
644 y = childOffset.GetY();
645 } else {
646 x = HorizontalLayout(size, position_.GetX(), menuPattern->IsSelectMenu()) + positionOffset_.GetX();
647 y = VerticalLayout(size, position_.GetY()) + positionOffset_.GetY();
648 x = std::clamp(x, margin_, wrapperSize_.Width() - size.Width() - margin_);
649 y = std::clamp(y, margin_, wrapperSize_.Height() - size.Height() - margin_);
650 }
651
652 return { x, y };
653 }
654
UpdateConstraintWidth(LayoutWrapper * layoutWrapper,LayoutConstraintF & constraint)655 void MenuLayoutAlgorithm::UpdateConstraintWidth(LayoutWrapper* layoutWrapper, LayoutConstraintF& constraint)
656 {
657 RefPtr<GridColumnInfo> columnInfo;
658 columnInfo = GridSystemManager::GetInstance().GetInfoByType(GridColumnType::MENU);
659 auto menuPattern = layoutWrapper->GetHostNode()->GetPattern<MenuPattern>();
660 if (menuPattern && menuPattern->IsSelectOverlayExtensionMenu()) {
661 columnInfo->GetParent()->BuildColumnWidth();
662 } else {
663 columnInfo->GetParent()->BuildColumnWidth(wrapperSize_.Width());
664 }
665 auto menuLayoutProperty = AceType::DynamicCast<MenuLayoutProperty>(layoutWrapper->GetLayoutProperty());
666 CHECK_NULL_VOID(menuLayoutProperty);
667 // set max width
668 const auto& padding = menuLayoutProperty->CreatePaddingAndBorder();
669 auto maxHorizontalSpace = std::max(leftSpace_, rightSpace_) - 2.0f * padding.Width();
670 auto maxWidth = static_cast<float>(columnInfo->GetWidth(GetMaxGridCounts(columnInfo)));
671 if (PipelineBase::GetCurrentContext() &&
672 PipelineBase::GetCurrentContext()->GetMinPlatformVersion() < PLATFORM_VERSION_TEN) {
673 maxWidth = std::min(maxHorizontalSpace, maxWidth);
674 }
675 maxWidth = std::min(constraint.maxSize.Width(), maxWidth);
676 constraint.maxSize.SetWidth(maxWidth);
677 }
678
UpdateConstraintHeight(LayoutWrapper * layoutWrapper,LayoutConstraintF & constraint)679 void MenuLayoutAlgorithm::UpdateConstraintHeight(LayoutWrapper* layoutWrapper, LayoutConstraintF& constraint)
680 {
681 auto maxSpaceHeight = std::max(topSpace_, bottomSpace_);
682 constraint.maxSize.SetHeight(maxSpaceHeight);
683 }
684
CreateChildConstraint(LayoutWrapper * layoutWrapper)685 LayoutConstraintF MenuLayoutAlgorithm::CreateChildConstraint(LayoutWrapper* layoutWrapper)
686 {
687 auto menuLayoutProperty = AceType::DynamicCast<MenuLayoutProperty>(layoutWrapper->GetLayoutProperty());
688 CHECK_NULL_RETURN(menuLayoutProperty, LayoutConstraintF());
689
690 auto childConstraint = menuLayoutProperty->CreateChildConstraint();
691 UpdateConstraintWidth(layoutWrapper, childConstraint);
692 UpdateConstraintHeight(layoutWrapper, childConstraint);
693 UpdateConstraintBaseOnOptions(layoutWrapper, childConstraint);
694 return childConstraint;
695 }
696
UpdateConstraintBaseOnOptions(LayoutWrapper * layoutWrapper,LayoutConstraintF & constraint)697 void MenuLayoutAlgorithm::UpdateConstraintBaseOnOptions(LayoutWrapper* layoutWrapper, LayoutConstraintF& constraint)
698 {
699 auto menuPattern = layoutWrapper->GetHostNode()->GetPattern<MenuPattern>();
700 CHECK_NULL_VOID(menuPattern);
701 auto options = menuPattern->GetOptions();
702 if (options.empty()) {
703 LOGD("options is empty, no need to update constraint.");
704 return;
705 }
706 auto optionConstraint = constraint;
707 RefPtr<GridColumnInfo> columnInfo;
708 columnInfo = GridSystemManager::GetInstance().GetInfoByType(GridColumnType::MENU);
709 columnInfo->GetParent()->BuildColumnWidth(wrapperSize_.Width());
710 auto minWidth = static_cast<float>(columnInfo->GetWidth(MIN_GRID_COUNTS));
711 optionConstraint.maxSize.MinusWidth(optionPadding_ * 2.0f);
712 optionConstraint.minSize.SetWidth(minWidth - optionPadding_ * 2.0f);
713 auto maxChildrenWidth = optionConstraint.minSize.Width();
714 auto optionsLayoutWrapper = GetOptionsLayoutWrappper(layoutWrapper);
715 for (const auto& optionWrapper : optionsLayoutWrapper) {
716 optionWrapper->GetLayoutProperty()->ResetCalcMinSize();
717 optionWrapper->Measure(optionConstraint);
718 auto childSize = optionWrapper->GetGeometryNode()->GetMarginFrameSize();
719 maxChildrenWidth = std::max(maxChildrenWidth, childSize.Width());
720 }
721 if (menuPattern->IsSelectOverlayExtensionMenu()) {
722 maxChildrenWidth = std::min(maxChildrenWidth, optionConstraint.maxSize.Width());
723 UpdateOptionConstraint(optionsLayoutWrapper, maxChildrenWidth);
724 constraint.minSize.SetWidth(maxChildrenWidth);
725 return;
726 }
727 UpdateOptionConstraint(optionsLayoutWrapper, maxChildrenWidth);
728 constraint.minSize.SetWidth(maxChildrenWidth + optionPadding_ * 2.0f);
729 }
730
GetOptionsLayoutWrappper(LayoutWrapper * layoutWrapper)731 std::list<RefPtr<LayoutWrapper>> MenuLayoutAlgorithm::GetOptionsLayoutWrappper(LayoutWrapper* layoutWrapper)
732 {
733 std::list<RefPtr<LayoutWrapper>> optionsWrapper;
734 auto scrollWrapper = layoutWrapper->GetOrCreateChildByIndex(0);
735 CHECK_NULL_RETURN(scrollWrapper, optionsWrapper);
736 auto columnWrapper = scrollWrapper->GetOrCreateChildByIndex(0);
737 CHECK_NULL_RETURN(columnWrapper, optionsWrapper);
738 optionsWrapper = columnWrapper->GetAllChildrenWithBuild();
739 return optionsWrapper;
740 }
741
UpdateOptionConstraint(std::list<RefPtr<LayoutWrapper>> & options,float width)742 void MenuLayoutAlgorithm::UpdateOptionConstraint(std::list<RefPtr<LayoutWrapper>>& options, float width)
743 {
744 for (const auto& option : options) {
745 auto optionLayoutProps = option->GetLayoutProperty();
746 CHECK_NULL_VOID(optionLayoutProps);
747 optionLayoutProps->UpdateCalcMinSize(CalcSize(CalcLength(width), std::nullopt));
748 }
749 }
750
751 // return vertical offset
VerticalLayout(const SizeF & size,float position)752 float MenuLayoutAlgorithm::VerticalLayout(const SizeF& size, float position)
753 {
754 float wrapperHeight = wrapperSize_.Height();
755 placement_ = Placement::BOTTOM;
756 // can put menu below click point
757 if (bottomSpace_ >= size.Height()) {
758 return position + margin_;
759 }
760
761 // put menu above click point
762 if (topSpace_ >= size.Height()) {
763 // menu show on top
764 placement_ = Placement::TOP;
765 return topSpace_ - size.Height() + margin_;
766 }
767
768 // line up bottom of menu with bottom of the screen
769 if (size.Height() < wrapperHeight) {
770 return wrapperHeight - size.Height();
771 }
772 // can't fit in screen, line up with top of the screen
773 return 0.0f;
774 }
775
776 // returns horizontal offset
HorizontalLayout(const SizeF & size,float position,bool isSelectMenu)777 float MenuLayoutAlgorithm::HorizontalLayout(const SizeF& size, float position, bool isSelectMenu)
778 {
779 float wrapperWidth = wrapperSize_.Width();
780 // can fit menu on the right side of position
781 if (rightSpace_ >= size.Width()) {
782 return position + margin_;
783 }
784
785 // fit menu on the left side
786 if (!isSelectMenu && leftSpace_ >= size.Width()) {
787 return position - size.Width();
788 }
789
790 // line up right side of menu with right boundary of the screen
791 if (size.Width() < wrapperWidth) {
792 return wrapperWidth - size.Width();
793 }
794
795 // can't fit in screen, line up with left side of the screen
796 return 0.0f;
797 }
798
GetPositionWithPlacement(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)799 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacement(
800 const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
801 {
802 OffsetF childPosition;
803
804 auto func = placementFuncMap_.find(placement_);
805 if (func != placementFuncMap_.end()) {
806 auto placementFunc = func->second;
807 if (placementFunc != nullptr) {
808 childPosition = (this->*placementFunc)(childSize, topPosition, bottomPosition);
809 } else {
810 LOGE("Invalid Placement of menu layout.");
811 }
812 } else {
813 LOGE("Invalid Placement of menu layout.");
814 }
815
816 return childPosition;
817 }
818
GetArrowPositionWithPlacement(const SizeF & menuSize)819 OffsetF MenuLayoutAlgorithm::GetArrowPositionWithPlacement(const SizeF& menuSize)
820 {
821 UpdateArrowOffsetWithMenuLimit(menuSize);
822 auto addArrowOffsetToArrowMin = arrowOffset_ + arrowMinLimit_;
823 auto space_ = ARROW_HIGHT.ConvertToPx();
824 OffsetF childPosition;
825 switch (arrowPlacement_) {
826 case Placement::TOP:
827 case Placement::TOP_LEFT:
828 case Placement::TOP_RIGHT:
829 childPosition = OffsetF(addArrowOffsetToArrowMin, menuSize.Height() + space_);
830 break;
831 case Placement::BOTTOM:
832 case Placement::BOTTOM_LEFT:
833 case Placement::BOTTOM_RIGHT:
834 childPosition = OffsetF(addArrowOffsetToArrowMin, -space_);
835 break;
836 case Placement::LEFT:
837 case Placement::LEFT_TOP:
838 case Placement::LEFT_BOTTOM:
839 childPosition = OffsetF(menuSize.Width() + space_, addArrowOffsetToArrowMin);
840 break;
841 case Placement::RIGHT:
842 case Placement::RIGHT_TOP:
843 case Placement::RIGHT_BOTTOM:
844 childPosition = OffsetF(-space_, addArrowOffsetToArrowMin);
845 break;
846 default:
847 break;
848 }
849 return childPosition;
850 }
851
GetMenuWrapperOffset(const LayoutWrapper * layoutWrapper)852 OffsetF MenuLayoutAlgorithm::GetMenuWrapperOffset(const LayoutWrapper* layoutWrapper)
853 {
854 CHECK_NULL_RETURN(layoutWrapper, OffsetF());
855 auto menuNode = layoutWrapper->GetHostNode();
856 CHECK_NULL_RETURN(menuNode, OffsetF());
857 return menuNode->GetParentGlobalOffsetDuringLayout();
858 }
859
InitTargetSizeAndPosition(const LayoutWrapper * layoutWrapper,bool isContextMenu)860 void MenuLayoutAlgorithm::InitTargetSizeAndPosition(const LayoutWrapper* layoutWrapper, bool isContextMenu)
861 {
862 auto targetNode = FrameNode::GetFrameNode(targetTag_, targetNodeId_);
863 CHECK_NULL_VOID(targetNode);
864 auto geometryNode = targetNode->GetGeometryNode();
865 CHECK_NULL_VOID(geometryNode);
866 targetSize_ = geometryNode->GetFrameSize();
867 auto pipelineContext = GetCurrentPipelineContext();
868 CHECK_NULL_VOID(pipelineContext);
869
870 targetOffset_ = targetNode->GetPaintRectOffset();
871 if (isContextMenu) {
872 auto windowGlobalRect = pipelineContext->GetDisplayWindowRectInfo();
873 float windowsOffsetX = static_cast<float>(windowGlobalRect.GetOffset().GetX());
874 float windowsOffsetY = static_cast<float>(windowGlobalRect.GetOffset().GetY());
875 targetOffset_ += OffsetF(windowsOffsetX, windowsOffsetY);
876
877 OffsetF offset = GetMenuWrapperOffset(layoutWrapper);
878 targetOffset_ -= offset;
879 return;
880 }
881
882 auto windowManager = pipelineContext->GetWindowManager();
883 auto isContainerModal = pipelineContext->GetWindowModal() == WindowModal::CONTAINER_MODAL && windowManager &&
884 windowManager->GetWindowMode() == WindowMode::WINDOW_MODE_FLOATING;
885 if (isContainerModal) {
886 auto newOffsetX = static_cast<float>(CONTAINER_BORDER_WIDTH.ConvertToPx()) +
887 static_cast<float>(CONTENT_PADDING.ConvertToPx());
888 auto newOffsetY = static_cast<float>(CONTAINER_TITLE_HEIGHT.ConvertToPx()) +
889 static_cast<float>(CONTAINER_BORDER_WIDTH.ConvertToPx());
890 targetOffset_ -= OffsetF(newOffsetX, newOffsetY);
891 } else {
892 OffsetF offset = GetMenuWrapperOffset(layoutWrapper);
893 targetOffset_ -= offset;
894 }
895 }
896
FitToScreen(const OffsetF & position,const SizeF & childSize,bool didNeedArrow)897 OffsetF MenuLayoutAlgorithm::FitToScreen(const OffsetF& position, const SizeF& childSize, bool didNeedArrow)
898 {
899 OffsetF afterOffsetPosition;
900 auto originPosition = position;
901
902 if (NearEqual(positionOffset_, OffsetF(0.0f, 0.0f)) && (!didNeedArrow || arrowPlacement_ == Placement::NONE)) {
903 afterOffsetPosition = AddTargetSpace(originPosition);
904 } else {
905 afterOffsetPosition = AddOffset(originPosition);
906 }
907
908 if (!CheckPosition(afterOffsetPosition, childSize)) {
909 return OffsetF(0.0f, 0.0f);
910 }
911
912 return afterOffsetPosition;
913 }
914
GetChildPosition(const SizeF & childSize,bool didNeedArrow)915 OffsetF MenuLayoutAlgorithm::GetChildPosition(const SizeF& childSize, bool didNeedArrow)
916 {
917 OffsetF bottomPosition = OffsetF(targetOffset_.GetX() + (targetSize_.Width() - childSize.Width()) / 2.0,
918 targetOffset_.GetY() + targetSize_.Height() + targetSpace_);
919 OffsetF topPosition = OffsetF(targetOffset_.GetX() + (targetSize_.Width() - childSize.Width()) / 2.0,
920 targetOffset_.GetY() - childSize.Height() - targetSpace_);
921 OffsetF defaultPosition = OffsetF(targetOffset_.GetX() + (targetSize_.Width() - childSize.Width()) / 2.0,
922 targetOffset_.GetY() + (targetSize_.Height() - childSize.Height()) / 2.0);
923
924 OffsetF childPosition;
925 OffsetF position = defaultPosition;
926 auto positionOffset = positionOffset_;
927 std::vector<Placement> currentPlacementStates = PLACEMENT_STATES.find(Placement::BOTTOM_LEFT)->second;
928 if (PLACEMENT_STATES.find(placement_) != PLACEMENT_STATES.end()) {
929 currentPlacementStates = PLACEMENT_STATES.find(placement_)->second;
930 }
931 size_t step = ALIGNMENT_STEP_OFFSET;
932 if (placement_ <= Placement::BOTTOM) {
933 step += 1;
934 }
935 for (size_t i = 0, len = currentPlacementStates.size(); i < len; i++) {
936 placement_ = currentPlacementStates[i];
937 if (placement_ == Placement::NONE) {
938 break;
939 }
940 if (i >= step) {
941 positionOffset_ = OffsetF(0.0f, 0.0f);
942 }
943 childPosition = GetPositionWithPlacement(childSize, topPosition, bottomPosition);
944 position = FitToScreen(childPosition, childSize, didNeedArrow);
945 if (NearEqual(position, OffsetF(0.0f, 0.0f))) {
946 continue;
947 }
948 break;
949 }
950 if (placement_ == Placement::NONE) {
951 position = GetAdjustPosition(currentPlacementStates, step, childSize, topPosition, bottomPosition);
952 if (NearEqual(position, OffsetF(0.0f, 0.0f))) {
953 position = defaultPosition;
954 }
955 }
956 positionOffset_ = positionOffset;
957 arrowPlacement_ = placement_;
958
959 return position;
960 }
961
GetAdjustPosition(std::vector<Placement> & currentPlacementStates,size_t step,const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)962 OffsetF MenuLayoutAlgorithm::GetAdjustPosition(std::vector<Placement>& currentPlacementStates, size_t step,
963 const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
964 {
965 OffsetF childPosition;
966 OffsetF position;
967 for (size_t i = 0, len = currentPlacementStates.size(); i < len;) {
968 placement_ = currentPlacementStates[i];
969 if (placement_ == Placement::NONE) {
970 break;
971 }
972 childPosition = GetPositionWithPlacement(childSize, topPosition, bottomPosition);
973 position = AdjustPosition(
974 childPosition, childSize.Width(), childSize.Height(), static_cast<float>(TARGET_SECURITY.ConvertToPx()));
975 if (NearEqual(position, OffsetF(0.0f, 0.0f))) {
976 i += step;
977 continue;
978 }
979 break;
980 }
981 return position;
982 }
983
AdjustPosition(const OffsetF & position,float width,float height,float space)984 OffsetF MenuLayoutAlgorithm::AdjustPosition(const OffsetF& position, float width, float height, float space)
985 {
986 float xMax = 0.0f;
987 float yMax = 0.0f;
988 float xMin = 1.0f;
989 float yMin = 1.0f;
990 switch (placement_) {
991 case Placement::LEFT_TOP:
992 case Placement::LEFT_BOTTOM:
993 case Placement::LEFT: {
994 xMin = paddingStart_;
995 xMax = std::min(targetOffset_.GetX() - width - space, wrapperSize_.Width() - paddingEnd_ - width);
996 yMin = paddingTop_;
997 yMax = wrapperSize_.Height() - height - paddingBottom_;
998 break;
999 }
1000 case Placement::RIGHT_TOP:
1001 case Placement::RIGHT_BOTTOM:
1002 case Placement::RIGHT: {
1003 xMin = std::max(targetOffset_.GetX() + targetSize_.Width() + space, paddingStart_);
1004 xMax = wrapperSize_.Width() - width - paddingEnd_;
1005 yMin = paddingTop_;
1006 yMax = wrapperSize_.Height() - height - paddingBottom_;
1007 break;
1008 }
1009 case Placement::TOP_LEFT:
1010 case Placement::TOP_RIGHT:
1011 case Placement::TOP: {
1012 xMin = paddingStart_;
1013 xMax = wrapperSize_.Width() - width - paddingEnd_;
1014 yMin = paddingTop_;
1015 yMax = std::min(targetOffset_.GetY() - height - space, wrapperSize_.Height() - paddingBottom_ - height);
1016 break;
1017 }
1018 case Placement::BOTTOM_LEFT:
1019 case Placement::BOTTOM_RIGHT:
1020 case Placement::BOTTOM: {
1021 xMin = paddingStart_;
1022 xMax = wrapperSize_.Width() - width - paddingEnd_;
1023 yMin = std::max(targetOffset_.GetY() + targetSize_.Height() + space, paddingTop_);
1024 yMax = wrapperSize_.Height() - height - paddingBottom_;
1025 break;
1026 }
1027 default:
1028 break;
1029 }
1030 if (xMax < xMin || yMax < yMin) {
1031 return OffsetF(0.0f, 0.0f);
1032 }
1033 auto x = std::clamp(position.GetX(), xMin, xMax);
1034 auto y = std::clamp(position.GetY(), yMin, yMax);
1035 return OffsetF(x, y);
1036 }
1037
AddTargetSpace(const OffsetF & position)1038 OffsetF MenuLayoutAlgorithm::AddTargetSpace(const OffsetF& position)
1039 {
1040 auto x = position.GetX();
1041 auto y = position.GetY();
1042 switch (placement_) {
1043 case Placement::BOTTOM_LEFT:
1044 case Placement::BOTTOM_RIGHT:
1045 case Placement::BOTTOM: {
1046 y += TARGET_SECURITY.ConvertToPx();
1047 break;
1048 }
1049 case Placement::TOP_LEFT:
1050 case Placement::TOP_RIGHT:
1051 case Placement::TOP: {
1052 y -= TARGET_SECURITY.ConvertToPx();
1053 break;
1054 }
1055 case Placement::RIGHT_TOP:
1056 case Placement::RIGHT_BOTTOM:
1057 case Placement::RIGHT: {
1058 x += TARGET_SECURITY.ConvertToPx();
1059 break;
1060 }
1061 case Placement::LEFT_TOP:
1062 case Placement::LEFT_BOTTOM:
1063 case Placement::LEFT: {
1064 x -= TARGET_SECURITY.ConvertToPx();
1065 break;
1066 }
1067 default: {
1068 y += TARGET_SECURITY.ConvertToPx();
1069 break;
1070 }
1071 }
1072 return OffsetF(x, y);
1073 }
1074
AddOffset(const OffsetF & position)1075 OffsetF MenuLayoutAlgorithm::AddOffset(const OffsetF& position)
1076 {
1077 auto x = position.GetX();
1078 auto y = position.GetY();
1079 switch (placement_) {
1080 case Placement::BOTTOM_LEFT:
1081 case Placement::BOTTOM_RIGHT:
1082 case Placement::BOTTOM: {
1083 x += positionOffset_.GetX();
1084 y += positionOffset_.GetY();
1085 break;
1086 }
1087 case Placement::TOP_LEFT:
1088 case Placement::TOP_RIGHT:
1089 case Placement::TOP: {
1090 x += positionOffset_.GetX();
1091 y -= positionOffset_.GetY();
1092 break;
1093 }
1094 case Placement::RIGHT_TOP:
1095 case Placement::RIGHT_BOTTOM:
1096 case Placement::RIGHT: {
1097 x += positionOffset_.GetX();
1098 y += positionOffset_.GetY();
1099 break;
1100 }
1101 case Placement::LEFT_TOP:
1102 case Placement::LEFT_BOTTOM:
1103 case Placement::LEFT: {
1104 x -= positionOffset_.GetX();
1105 y += positionOffset_.GetY();
1106 break;
1107 }
1108 default: {
1109 x += positionOffset_.GetX();
1110 y += positionOffset_.GetY();
1111 break;
1112 }
1113 }
1114 return OffsetF(x, y);
1115 }
1116
CheckPositionInPlacementRect(const Rect & rect,const OffsetF & position,const SizeF & childSize)1117 bool MenuLayoutAlgorithm::CheckPositionInPlacementRect(
1118 const Rect& rect, const OffsetF& position, const SizeF& childSize)
1119 {
1120 auto x = position.GetX();
1121 auto y = position.GetY();
1122 if (x < rect.Left() || (x + childSize.Width()) > rect.Right() || y < rect.Top() ||
1123 (y + childSize.Height()) > rect.Bottom()) {
1124 return false;
1125 }
1126 return true;
1127 }
1128
CheckPosition(const OffsetF & position,const SizeF & childSize)1129 bool MenuLayoutAlgorithm::CheckPosition(const OffsetF& position, const SizeF& childSize)
1130 {
1131 float targetOffsetX = targetOffset_.GetX();
1132 float targetOffsetY = targetOffset_.GetY();
1133
1134 Rect rect;
1135 switch (placement_) {
1136 case Placement::BOTTOM_LEFT:
1137 case Placement::BOTTOM_RIGHT:
1138 case Placement::BOTTOM: {
1139 auto y = std::max(targetOffsetY + targetSize_.Height(), paddingTop_);
1140 auto height =
1141 std::min(wrapperSize_.Height() - paddingBottom_ - targetOffsetY - targetSize_.Height(),
1142 wrapperSize_.Height() - paddingBottom_ - paddingTop_);
1143 rect.SetRect(
1144 paddingStart_, y, wrapperSize_.Width() - paddingEnd_ - paddingStart_, height);
1145 break;
1146 }
1147 case Placement::TOP_LEFT:
1148 case Placement::TOP_RIGHT:
1149 case Placement::TOP: {
1150 auto height = std::min(targetOffsetY - paddingTop_,
1151 wrapperSize_.Height() - paddingTop_ - paddingBottom_);
1152 rect.SetRect(paddingStart_, paddingTop_,
1153 wrapperSize_.Width() - paddingEnd_ - paddingStart_, height);
1154 break;
1155 }
1156 case Placement::RIGHT_TOP:
1157 case Placement::RIGHT_BOTTOM:
1158 case Placement::RIGHT: {
1159 auto x = std::max(targetOffsetX + targetSize_.Width(), paddingStart_);
1160 auto width = std::min(wrapperSize_.Width() - targetOffsetX - targetSize_.Width() - paddingEnd_,
1161 wrapperSize_.Width() - paddingStart_ - paddingEnd_);
1162 rect.SetRect(
1163 x, paddingTop_, width, wrapperSize_.Height() - paddingBottom_ - paddingTop_);
1164 break;
1165 }
1166 case Placement::LEFT_TOP:
1167 case Placement::LEFT_BOTTOM:
1168 case Placement::LEFT: {
1169 auto width = std::min(
1170 targetOffsetX - paddingStart_, wrapperSize_.Width() - paddingEnd_ - paddingStart_);
1171 rect.SetRect(paddingStart_, paddingTop_, width,
1172 wrapperSize_.Height() - paddingBottom_ - paddingTop_);
1173 break;
1174 }
1175 default:
1176 return false;
1177 }
1178 return CheckPositionInPlacementRect(rect, position, childSize);
1179 }
1180
GetPositionWithPlacementTop(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)1181 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacementTop(
1182 const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
1183 {
1184 return topPosition;
1185 }
1186
GetPositionWithPlacementTopLeft(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)1187 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacementTopLeft(
1188 const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
1189 {
1190 OffsetF childPosition;
1191 float marginRight = 0.0f;
1192 float marginBottom = 0.0f;
1193 childPosition = OffsetF(targetOffset_.GetX() - marginRight,
1194 targetOffset_.GetY() - childSize.Height() - marginBottom - targetSpace_);
1195 return childPosition;
1196 }
1197
GetPositionWithPlacementTopRight(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)1198 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacementTopRight(
1199 const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
1200 {
1201 OffsetF childPosition;
1202 float marginBottom = 0.0f;
1203 float marginLeft = 0.0f;
1204 childPosition = OffsetF(targetOffset_.GetX() + targetSize_.Width() - childSize.Width() + marginLeft,
1205 targetOffset_.GetY() - childSize.Height() - targetSpace_ - marginBottom);
1206 return childPosition;
1207 }
1208
GetPositionWithPlacementBottom(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)1209 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacementBottom(
1210 const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
1211 {
1212 return bottomPosition;
1213 }
1214
GetPositionWithPlacementBottomLeft(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)1215 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacementBottomLeft(
1216 const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
1217 {
1218 OffsetF childPosition;
1219 float marginRight = 0.0f;
1220 float marginTop = 0.0f;
1221 childPosition = OffsetF(targetOffset_.GetX() - marginRight,
1222 targetOffset_.GetY() + targetSize_.Height() + targetSpace_ + marginTop);
1223 return childPosition;
1224 }
1225
GetPositionWithPlacementBottomRight(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)1226 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacementBottomRight(
1227 const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
1228 {
1229 OffsetF childPosition;
1230 float marginTop = 0.0f;
1231 float marginLeft = 0.0f;
1232 childPosition = OffsetF(targetOffset_.GetX() + targetSize_.Width() - childSize.Width() + marginLeft,
1233 targetOffset_.GetY() + targetSize_.Height() + targetSpace_ + marginTop);
1234 return childPosition;
1235 }
1236
GetPositionWithPlacementLeft(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)1237 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacementLeft(
1238 const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
1239 {
1240 OffsetF childPosition;
1241 float marginRight = 0.0f;
1242 childPosition =
1243 OffsetF(targetOffset_.GetX() - targetSpace_ - childSize.Width() - marginRight,
1244 targetOffset_.GetY() + targetSize_.Height() / 2.0 - childSize.Height() / 2.0);
1245 return childPosition;
1246 }
1247
GetPositionWithPlacementLeftTop(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)1248 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacementLeftTop(
1249 const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
1250 {
1251 OffsetF childPosition;
1252 float marginRight = 0.0f;
1253 float marginBottom = 0.0f;
1254 childPosition =
1255 OffsetF(targetOffset_.GetX() - targetSpace_ - childSize.Width() - marginRight,
1256 targetOffset_.GetY() - marginBottom);
1257 return childPosition;
1258 }
1259
GetPositionWithPlacementLeftBottom(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)1260 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacementLeftBottom(
1261 const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
1262 {
1263 OffsetF childPosition;
1264 float marginRight = 0.0f;
1265 float marginTop = 0.0f;
1266 childPosition =
1267 OffsetF(targetOffset_.GetX() - targetSpace_ - childSize.Width() - marginRight,
1268 targetOffset_.GetY() + targetSize_.Height() - childSize.Height() - marginTop);
1269 return childPosition;
1270 }
1271
GetPositionWithPlacementRight(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)1272 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacementRight(
1273 const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
1274 {
1275 OffsetF childPosition;
1276 float marginLeft = 0.0f;
1277 childPosition =
1278 OffsetF(targetOffset_.GetX() + targetSize_.Width() + targetSpace_ + marginLeft,
1279 targetOffset_.GetY() + targetSize_.Height() / 2.0 - childSize.Height() / 2.0);
1280 return childPosition;
1281 }
1282
GetPositionWithPlacementRightTop(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)1283 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacementRightTop(
1284 const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
1285 {
1286 OffsetF childPosition;
1287 float marginBottom = 0.0f;
1288 float marginLeft = 0.0f;
1289 childPosition =
1290 OffsetF(targetOffset_.GetX() + targetSize_.Width() + targetSpace_ + marginLeft,
1291 targetOffset_.GetY() - marginBottom);
1292 return childPosition;
1293 }
1294
GetPositionWithPlacementRightBottom(const SizeF & childSize,const OffsetF & topPosition,const OffsetF & bottomPosition)1295 OffsetF MenuLayoutAlgorithm::GetPositionWithPlacementRightBottom(
1296 const SizeF& childSize, const OffsetF& topPosition, const OffsetF& bottomPosition)
1297 {
1298 OffsetF childPosition;
1299 float marginTop = 0.0f;
1300 float marginLeft = 0.0f;
1301 childPosition =
1302 OffsetF(targetOffset_.GetX() + targetSize_.Width() + targetSpace_ + marginLeft,
1303 targetOffset_.GetY() + targetSize_.Height() - childSize.Height() - marginTop);
1304 return childPosition;
1305 }
1306
1307 } // namespace OHOS::Ace::NG
1308