• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022 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/select_overlay/select_overlay_layout_algorithm.h"
17 
18 #include <cmath>
19 #include <optional>
20 
21 #include "base/geometry/ng/offset_t.h"
22 #include "base/utils/string_utils.h"
23 #include "base/utils/utils.h"
24 #include "core/components/text_overlay/text_overlay_theme.h"
25 #include "core/components_ng/pattern/linear_layout/linear_layout_pattern.h"
26 #include "core/components_ng/pattern/linear_layout/linear_layout_property.h"
27 #include "core/components_ng/pattern/select_overlay/select_overlay_pattern.h"
28 #include "core/components_ng/pattern/select_overlay/select_overlay_property.h"
29 #include "core/pipeline_ng/pipeline_context.h"
30 
31 namespace OHOS::Ace::NG {
32 namespace {
33 constexpr Dimension MORE_MENU_INTERVAL = 8.0_vp;
34 constexpr float ROUND_EPSILON = 0.5f;
35 constexpr float CUSTOM_MENU_HEIGHT_CONSTRAINT_FACTOR = 0.8;
36 }
37 
Measure(LayoutWrapper * layoutWrapper)38 void SelectOverlayLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper)
39 {
40     auto host = layoutWrapper->GetHostNode();
41     CHECK_NULL_VOID(host);
42     auto pattern = host->GetPattern<SelectOverlayPattern>();
43     CHECK_NULL_VOID(pattern);
44     if (pattern->GetMode() != SelectOverlayMode::HANDLE_ONLY) {
45         MeasureChild(layoutWrapper);
46     }
47     PerformMeasureSelf(layoutWrapper);
48     // match parent.
49     if (pattern->GetMode() == SelectOverlayMode::HANDLE_ONLY) {
50         auto geometryNode = layoutWrapper->GetGeometryNode();
51         CHECK_NULL_VOID(geometryNode);
52         auto parentNode = host->GetAncestorNodeOfFrame(true);
53         CHECK_NULL_VOID(parentNode);
54         auto parentGeo = parentNode->GetGeometryNode();
55         CHECK_NULL_VOID(parentGeo);
56         geometryNode->SetFrameSize(parentGeo->GetFrameSize());
57     }
58 }
59 
MeasureChild(LayoutWrapper * layoutWrapper)60 void SelectOverlayLayoutAlgorithm::MeasureChild(LayoutWrapper* layoutWrapper)
61 {
62     auto layoutProperty = layoutWrapper->GetLayoutProperty();
63     CHECK_NULL_VOID(layoutProperty);
64     auto layoutConstraint = layoutProperty->CreateChildConstraint();
65     auto pipeline = PipelineContext::GetCurrentContextSafelyWithCheck();
66     CHECK_NULL_VOID(pipeline);
67     auto safeAreaManager = pipeline->GetSafeAreaManager();
68     CHECK_NULL_VOID(safeAreaManager);
69     auto keyboardHeight = safeAreaManager->GetKeyboardInsetImpl().Length();
70     auto safeAreaInsets = safeAreaManager->GetSafeAreaWithoutProcess();
71     auto top = safeAreaInsets.top_.Length();
72     auto bottom = GreatNotEqual(keyboardHeight, 0.0) ? 0.0 : safeAreaInsets.bottom_.Length();
73     auto maxSize =
74         SizeF(layoutConstraint.maxSize.Width(), layoutConstraint.maxSize.Height() - keyboardHeight - top - bottom);
75     layoutConstraint.maxSize = maxSize;
76     bool isMouseCustomMenu = false;
77     if (info_->menuInfo.menuBuilder) {
78         auto customMenuLayoutWrapper = layoutWrapper->GetChildByIndex(0);
79         CHECK_NULL_VOID(customMenuLayoutWrapper);
80         auto customNode = customMenuLayoutWrapper->GetHostNode();
81         if (customNode && customNode->GetTag() != "SelectMenu") {
82             auto customMenuLayoutConstraint = layoutConstraint;
83             auto customMenuMaxHeight = GetCustomMenuMaxHeight(top, safeAreaInsets.bottom_.Length());
84             if (GreatNotEqual(customMenuMaxHeight, 0.0)) {
85                 auto maxHeight = std::min(maxSize.Height(), customMenuMaxHeight);
86                 customMenuLayoutConstraint.maxSize.SetHeight(maxHeight);
87             }
88             customMenuLayoutConstraint.selfIdealSize =
89                 OptionalSizeF(std::nullopt, customMenuLayoutConstraint.maxSize.Height());
90             customMenuLayoutWrapper->Measure(customMenuLayoutConstraint);
91             isMouseCustomMenu = true;
92         }
93     }
94     auto childIndex = -1;
95     for (const auto& child : layoutWrapper->GetAllChildrenWithBuild()) {
96         childIndex++;
97         if (childIndex == 0 && isMouseCustomMenu) {
98             continue;
99         }
100         child->Measure(layoutConstraint);
101     }
102 }
103 
GetCustomMenuMaxHeight(float topSafeArea,float bottomSafeArea)104 float SelectOverlayLayoutAlgorithm::GetCustomMenuMaxHeight(float topSafeArea, float bottomSafeArea)
105 {
106     float defaultHeight = 0.0;
107     auto pipelineContext = PipelineContext::GetCurrentContextSafelyWithCheck();
108     CHECK_NULL_RETURN(pipelineContext, defaultHeight);
109     auto rect = pipelineContext->GetDisplayWindowRectInfo();
110     return std::max(defaultHeight, static_cast<float>(rect.Height() - topSafeArea - bottomSafeArea)) *
111            CUSTOM_MENU_HEIGHT_CONSTRAINT_FACTOR;
112 }
113 
CalculateCustomMenuLayoutConstraint(LayoutWrapper * layoutWrapper,LayoutConstraintF & layoutConstraint)114 void SelectOverlayLayoutAlgorithm::CalculateCustomMenuLayoutConstraint(
115     LayoutWrapper* layoutWrapper, LayoutConstraintF& layoutConstraint)
116 {
117     auto pipeline = PipelineContext::GetCurrentContextSafelyWithCheck();
118     CHECK_NULL_VOID(pipeline);
119     auto theme = pipeline->GetTheme<TextOverlayTheme>();
120     CHECK_NULL_VOID(theme);
121     // Calculate the spacing with text and handle, menu is fixed up the handle and text.
122     double menuSpacingBetweenText = theme->GetMenuSpacingWithText().ConvertToPx();
123     double menuSpacingBetweenHandle = theme->GetHandleDiameter().ConvertToPx();
124 
125     // paint rect is in global position, need to convert to local position
126     auto offset = layoutWrapper->GetGeometryNode()->GetFrameOffset();
127     const auto firstHandleRect = info_->firstHandle.GetPaintRect() - offset;
128     const auto secondHandleRect = info_->secondHandle.GetPaintRect() - offset;
129 
130     auto top = info_->isNewAvoid ? info_->selectArea.Top() : firstHandleRect.Top();
131     auto bottom = info_->isNewAvoid ? info_->selectArea.Bottom() : secondHandleRect.Bottom();
132     auto topSpace = top - menuSpacingBetweenText - menuSpacingBetweenHandle;
133     auto bottomSpace = layoutConstraint.maxSize.Height() -
134                        (bottom + menuSpacingBetweenText + menuSpacingBetweenHandle);
135     if (info_->isUsingMouse) {
136         layoutConstraint.selfIdealSize = OptionalSizeF(std::nullopt, layoutConstraint.maxSize.Height());
137     } else {
138         layoutConstraint.selfIdealSize = OptionalSizeF(std::nullopt, std::max(topSpace, bottomSpace));
139     }
140 }
141 
CalculateCustomMenuByMouseOffset(LayoutWrapper * layoutWrapper)142 OffsetF SelectOverlayLayoutAlgorithm::CalculateCustomMenuByMouseOffset(LayoutWrapper* layoutWrapper)
143 {
144     auto menuOffset = info_->rightClickOffset + mainWindowOffset_ + containerModalOffset_;
145     auto layoutProperty = layoutWrapper->GetLayoutProperty();
146     CHECK_NULL_RETURN(layoutProperty, menuOffset);
147     auto layoutConstraint = layoutProperty->GetLayoutConstraint();
148     CHECK_NULL_RETURN(layoutConstraint, menuOffset);
149     auto menu = layoutWrapper->GetOrCreateChildByIndex(0);
150     CHECK_NULL_RETURN(menu, menuOffset);
151     auto maxWidth = layoutConstraint->selfIdealSize.Width().value_or(0.0f);
152     auto menuSize = menu->GetGeometryNode()->GetFrameSize();
153     if (GreatNotEqual(menuOffset.GetX() + menuSize.Width(), maxWidth)) {
154         if (GreatOrEqual(menuOffset.GetX(), menuSize.Width())) {
155             menuOffset.SetX(menuOffset.GetX() - menuSize.Width());
156         } else if (LessOrEqual(menuSize.Width(), maxWidth)) {
157             menuOffset.SetX(maxWidth - menuSize.Width());
158         } else if (GreatNotEqual(menuSize.Width(), maxWidth)) {
159             menuOffset.SetX(menuOffset.GetX() - menuSize.Width() / 2.0f);
160         }
161     }
162     auto maxHeight = layoutConstraint->selfIdealSize.Height().value_or(0.0f);
163     auto pipeline = PipelineContext::GetCurrentContextSafelyWithCheck();
164     CHECK_NULL_RETURN(pipeline, menuOffset);
165     auto safeAreaManager = pipeline->GetSafeAreaManager();
166     CHECK_NULL_RETURN(safeAreaManager, menuOffset);
167     auto keyboardInsert = safeAreaManager->GetKeyboardInsetImpl();
168     auto keyboardY = maxHeight - keyboardInsert.Length();
169     uint32_t top = safeAreaManager->GetSystemSafeArea().top_.Length();
170     if (GreatNotEqual(menuOffset.GetY() + menuSize.Height(), keyboardY)) {
171         auto currentY = menuOffset.GetY();
172         if (GreatOrEqual(currentY, menuSize.Height())) {
173             currentY = menuOffset.GetY() - menuSize.Height();
174         } else if (LessOrEqual(menuSize.Height(), keyboardY)) {
175             currentY = keyboardY - menuSize.Height();
176         } else if (GreatNotEqual(menuSize.Height(), keyboardY)) {
177             currentY = menuOffset.GetY() - menuSize.Height() / 2.0f;
178         }
179         if (GreatNotEqual(top, currentY)) {
180             currentY = top;
181         }
182         menuOffset.SetY(currentY);
183     }
184     return menuOffset;
185 }
186 
Layout(LayoutWrapper * layoutWrapper)187 void SelectOverlayLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper)
188 {
189     auto host = layoutWrapper->GetHostNode();
190     CHECK_NULL_VOID(host);
191     auto pattern = host->GetPattern<SelectOverlayPattern>();
192     CHECK_NULL_VOID(pattern);
193     if (pattern->GetMode() != SelectOverlayMode::HANDLE_ONLY) {
194         CheckHandleIsInClipViewPort();
195         LayoutChild(layoutWrapper, pattern->GetMode());
196     }
197 }
198 
LayoutChild(LayoutWrapper * layoutWrapper,SelectOverlayMode mode)199 void SelectOverlayLayoutAlgorithm::LayoutChild(LayoutWrapper* layoutWrapper, SelectOverlayMode mode)
200 {
201     auto menu = layoutWrapper->GetOrCreateChildByIndex(0);
202     CHECK_NULL_VOID(menu);
203     auto shouldInActiveByHandle =
204         !info_->firstHandle.isShow && !info_->secondHandle.isShow && !info_->isSelectRegionVisible;
205     if ((!CheckInShowArea(*info_) || (!info_->isNewAvoid && shouldInActiveByHandle)) && !info_->isUsingMouse) {
206         menu->SetActive(false);
207         return;
208     }
209     menu->SetActive(true);
210     UpdateMainWindowOffset(layoutWrapper);
211     OffsetF menuOffset = info_->isUsingMouse ? CalculateCustomMenuByMouseOffset(layoutWrapper)
212                                              : ComputeSelectMenuPosition(layoutWrapper);
213     auto menuGetGeometryNode = menu->GetGeometryNode();
214     CHECK_NULL_VOID(menuGetGeometryNode);
215     menuGetGeometryNode->SetMarginFrameOffset(menuOffset);
216     // custom menu need to layout all menu and submenu
217     if (info_->menuInfo.menuBuilder) {
218         for (const auto& child : layoutWrapper->GetAllChildrenWithBuild()) {
219             child->Layout();
220         }
221         return;
222     }
223     menu->Layout();
224     auto button = layoutWrapper->GetOrCreateChildByIndex(1);
225     CHECK_NULL_VOID(button);
226     if ((!info_->menuInfo.menuIsShow || info_->menuInfo.menuDisable) && mode != SelectOverlayMode::MENU_ONLY) {
227         hasExtensionMenu_ = false;
228         return;
229     }
230     hasExtensionMenu_ = true;
231     auto buttonSize = button->GetGeometryNode()->GetMarginFrameSize();
232     auto menuSize = menu->GetGeometryNode()->GetMarginFrameSize();
233     bool isReverse = IsReverseLayout(layoutWrapper);
234     OffsetF buttonOffset;
235     if (GreatNotEqual(menuSize.Width(), menuSize.Height())) {
236         auto diffY = std::max((menuSize.Height() - buttonSize.Height()) / 2.0f, 0.0f);
237         buttonOffset = isReverse ? OffsetF(menuOffset.GetX(), menuOffset.GetY() + diffY) :
238             OffsetF(menuOffset.GetX() + menuSize.Width() - buttonSize.Width(), menuOffset.GetY() + diffY);
239     } else {
240         buttonOffset = menuOffset;
241     }
242     button->GetGeometryNode()->SetMarginFrameOffset(buttonOffset);
243     button->Layout();
244 
245     LayoutExtensionMenu(layoutWrapper, button);
246 }
247 
LayoutExtensionMenu(LayoutWrapper * layoutWrapper,const RefPtr<LayoutWrapper> & button)248 void SelectOverlayLayoutAlgorithm::LayoutExtensionMenu(
249     LayoutWrapper* layoutWrapper, const RefPtr<LayoutWrapper>& button)
250 {
251     auto host = layoutWrapper->GetHostNode();
252     CHECK_NULL_VOID(host);
253     auto selectOverlayNode = DynamicCast<SelectOverlayNode>(host);
254     CHECK_NULL_VOID(selectOverlayNode);
255     // Avoid menu layout while the back animation is playing.
256     if (!selectOverlayNode->GetIsExtensionMenu() && selectOverlayNode->GetAnimationStatus()) {
257         return;
258     }
259     auto extensionMenu = layoutWrapper->GetOrCreateChildByIndex(2);
260     CHECK_NULL_VOID(extensionMenu);
261     extensionMenu->Layout();
262     CheckHideBackOrMoreButton(extensionMenu, button);
263 }
264 
CheckHideBackOrMoreButton(const RefPtr<LayoutWrapper> & extensionMenu,const RefPtr<LayoutWrapper> & button)265 void SelectOverlayLayoutAlgorithm::CheckHideBackOrMoreButton(
266     const RefPtr<LayoutWrapper>& extensionMenu, const RefPtr<LayoutWrapper>& button)
267 {
268     auto extensionMenuRect = extensionMenu->GetGeometryNode()->GetFrameRect();
269     auto buttonRect = button->GetGeometryNode()->GetFrameRect();
270     auto constraintRect = extensionMenuRect.Constrain(buttonRect);
271     if (GreatNotEqual(constraintRect.Width(), 0.0f) && GreatNotEqual(constraintRect.Height(), 0.0f)) {
272         hideMoreOrBack_ = true;
273     }
274 }
275 
CheckInShowArea(const SelectOverlayInfo & info)276 bool SelectOverlayLayoutAlgorithm::CheckInShowArea(const SelectOverlayInfo& info)
277 {
278     if (info.useFullScreen) {
279         return true;
280     }
281     if (info.isSingleHandle) {
282         return info.firstHandle.GetPaintRect().IsWrappedBy(info.showArea);
283     }
284     return info.firstHandle.GetPaintRect().IsWrappedBy(info.showArea) &&
285            info.secondHandle.GetPaintRect().IsWrappedBy(info.showArea);
286 }
287 
ComputeSelectMenuPosition(LayoutWrapper * layoutWrapper)288 OffsetF SelectOverlayLayoutAlgorithm::ComputeSelectMenuPosition(LayoutWrapper* layoutWrapper)
289 {
290     auto menuItem = layoutWrapper->GetOrCreateChildByIndex(0);
291     CHECK_NULL_RETURN(menuItem, OffsetF());
292     auto pipeline = PipelineContext::GetCurrentContextSafelyWithCheck();
293     CHECK_NULL_RETURN(pipeline, OffsetF());
294     auto theme = pipeline->GetTheme<TextOverlayTheme>();
295     CHECK_NULL_RETURN(theme, OffsetF());
296     OffsetF menuPosition;
297     bool isExtension = false;
298     bool isReverse = IsReverseLayout(layoutWrapper);
299 
300     // Calculate the spacing with text and handle, menu is fixed up the handle and text.
301     double menuSpacingBetweenText = theme->GetMenuSpacingWithText().ConvertToPx();
302     double menuSpacingBetweenHandle = theme->GetHandleDiameter().ConvertToPx();
303 
304     // When the extended menu is displayed, the default menu becomes circular, but the position of the circle is aligned
305     // with the end of the original menu.
306     auto width = menuItem->GetGeometryNode()->GetMarginFrameSize().Width();
307     auto height = menuItem->GetGeometryNode()->GetMarginFrameSize().Height();
308 
309     auto backButton = layoutWrapper->GetOrCreateChildByIndex(1);
310     bool isBackButtonVisible = false;
311     if (backButton) {
312         isBackButtonVisible =
313             backButton->GetLayoutProperty()->GetVisibilityValue(VisibleType::INVISIBLE) == VisibleType::VISIBLE;
314     }
315     auto host = layoutWrapper->GetHostNode();
316     CHECK_NULL_RETURN(host, OffsetF());
317     auto selectOverlayNode = DynamicCast<SelectOverlayNode>(host);
318     CHECK_NULL_RETURN(selectOverlayNode, OffsetF());
319     auto isExtensionMEnu = selectOverlayNode->GetIsExtensionMenu();
320     if (!isBackButtonVisible && !isExtensionMEnu) {
321         menuWidth_ = width;
322         menuHeight_ = height;
323     } else {
324         isExtension = true;
325     }
326     auto menuWidth = menuWidth_.value_or(width);
327     auto menuHeight = menuHeight_.value_or(height);
328 
329     // paint rect is in global position, need to convert to local position
330     auto offset = layoutWrapper->GetGeometryNode()->GetFrameOffset();
331     auto windowOffset = mainWindowOffset_ + containerModalOffset_;
332     const auto firstHandleRect = info_->firstHandle.GetPaintRect() + windowOffset - offset;
333     const auto secondHandleRect = info_->secondHandle.GetPaintRect() + windowOffset - offset;
334 
335     auto singleHandle = firstHandleRect;
336     if (!info_->firstHandle.isShow) {
337         singleHandle = secondHandleRect;
338     }
339     if (IsTextAreaSelectAll()) {
340         singleHandle = RectF(info_->menuInfo.menuOffset.value().GetX() + windowOffset.GetX(),
341             info_->menuInfo.menuOffset.value().GetY() + windowOffset.GetY(), singleHandle.Width(),
342             singleHandle.Height());
343         TAG_LOGD(AceLogTag::ACE_SELECT_OVERLAY, "ComputeSelectMenuPosition singleHandle : %{public}s",
344             singleHandle.ToString().c_str());
345     }
346     if (info_->isSingleHandle) {
347         auto menuSpacing = static_cast<float>(menuSpacingBetweenText);
348         menuPosition = OffsetF((singleHandle.Left() + singleHandle.Right() - menuWidth) / 2.0f,
349             static_cast<float>(singleHandle.Top() - menuSpacing - menuHeight));
350         TAG_LOGD(AceLogTag::ACE_SELECT_OVERLAY,
351             "ComputeSelectMenuPosition single handle init menuPosition : %{public}s", menuPosition.ToString().c_str());
352     } else {
353         auto menuSpacing = static_cast<float>(menuSpacingBetweenText + menuSpacingBetweenHandle);
354         menuPosition = OffsetF((firstHandleRect.Left() + secondHandleRect.Left() - menuWidth) / 2.0f,
355             static_cast<float>(firstHandleRect.Top() - menuSpacing - menuHeight));
356 
357         if (!info_->firstHandle.isShow && info_->secondHandle.isShow && !info_->handleReverse) {
358             menuPosition.SetY(secondHandleRect.Bottom() + menuSpacing);
359         }
360         if (info_->firstHandle.isShow && !info_->secondHandle.isShow && info_->handleReverse) {
361             menuPosition.SetY(firstHandleRect.Bottom() + menuSpacing);
362         }
363         if (info_->firstHandle.isShow && info_->secondHandle.isShow &&
364             !NearEqual(firstHandleRect.Top(), secondHandleRect.Top())) {
365             auto top = std::min(firstHandleRect.Top(), secondHandleRect.Top());
366             menuPosition.SetY(static_cast<float>(top - menuSpacing - menuHeight));
367         }
368         if (!info_->firstHandle.isShow && info_->secondHandle.isShow) {
369             menuPosition.SetX(secondHandleRect.Left() - menuWidth / 2.0f);
370         }
371         if (info_->firstHandle.isShow && !info_->secondHandle.isShow) {
372             menuPosition.SetX(firstHandleRect.Left() - menuWidth / 2.0f);
373         }
374         TAG_LOGD(AceLogTag::ACE_SELECT_OVERLAY,
375             "ComputeSelectMenuPosition double handle init menuPosition : %{public}s", menuPosition.ToString().c_str());
376     }
377 
378     auto overlayWidth = layoutWrapper->GetGeometryNode()->GetFrameSize().Width();
379     RectF viewPort = layoutWrapper->GetGeometryNode()->GetFrameRect() - offset;
380     auto overlayVP = viewPort;
381     info_->GetCallerNodeAncestorViewPort(viewPort);
382     // viewPort rect is in main window position, need to convert to subwindow position
383     viewPort += windowOffset;
384     TAG_LOGD(AceLogTag::ACE_SELECT_OVERLAY, "ComputeSelectMenuPosition ancestor viewPort : %{public}s",
385         viewPort.ToString().c_str());
386     // Adjust position of overlay.
387     auto adjustPositionXWithViewPort = [&](OffsetF& menuPosition) {
388         auto defaultMenuPositionX = theme->GetDefaultMenuPositionX();
389         if (LessOrEqual(menuPosition.GetX(), defaultMenuPositionX)) {
390             menuPosition.SetX(defaultMenuPositionX);
391         } else if (GreatOrEqual(
392             menuPosition.GetX() + menuWidth, overlayVP.GetX() + overlayVP.Width() - defaultMenuPositionX)) {
393             menuPosition.SetX(overlayWidth - menuWidth - defaultMenuPositionX);
394         }
395     };
396     adjustPositionXWithViewPort(menuPosition);
397     auto safeAreaManager = pipeline->GetSafeAreaManager();
398     if (LessNotEqual(menuPosition.GetY(), menuHeight)) {
399         if (IsTextAreaSelectAll()) {
400             menuPosition.SetY(singleHandle.Top());
401         } else if (info_->isSingleHandle &&
402             IsMenuAreaSmallerHandleArea(singleHandle, menuHeight, menuSpacingBetweenText)) {
403             if (safeAreaManager && safeAreaManager->GetSystemSafeArea().top_.Length() > singleHandle.Top()) {
404                 menuPosition.SetY(
405                     static_cast<float>(singleHandle.Bottom() + menuSpacingBetweenText + menuSpacingBetweenHandle));
406             }
407         } else {
408             menuPosition.SetY(
409                 static_cast<float>(singleHandle.Bottom() + menuSpacingBetweenText + menuSpacingBetweenHandle));
410         }
411     }
412     auto spaceBetweenViewPort = menuSpacingBetweenText + menuSpacingBetweenHandle;
413     if (LessNotEqual(menuPosition.GetY(), viewPort.GetY() - spaceBetweenViewPort - menuHeight) ||
414         LessNotEqual(menuPosition.GetY(), menuSpacingBetweenText)) {
415         auto menuOffsetY = viewPort.GetY() - spaceBetweenViewPort - menuHeight;
416         if (GreatNotEqual(menuOffsetY, menuSpacingBetweenText)) {
417             menuPosition.SetY(menuOffsetY);
418         } else {
419             menuPosition.SetY(menuSpacingBetweenText);
420         }
421     } else if (GreatOrEqual(menuPosition.GetY(), viewPort.GetY() + viewPort.Height() + spaceBetweenViewPort)) {
422         menuPosition.SetY(viewPort.GetY() + viewPort.Height() + spaceBetweenViewPort);
423     }
424 
425     if (safeAreaManager && !(info_->isSingleHandle &&
426         IsMenuAreaSmallerHandleArea(singleHandle, menuHeight, menuSpacingBetweenText))) {
427         // ignore status bar
428         auto top = safeAreaManager->GetSystemSafeArea().top_.Length();
429         if (menuPosition.GetY() < top) {
430             menuPosition.SetY(top);
431         }
432     }
433     if (info_->firstHandle.isShow && info_->secondHandle.isShow &&
434         !NearEqual(firstHandleRect.Top(), secondHandleRect.Top())) {
435         auto menuRect = RectF(menuPosition, SizeF(menuWidth, menuHeight));
436         auto downHandleRect =
437             LessNotEqual(firstHandleRect.Top(), secondHandleRect.Top()) ? secondHandleRect : firstHandleRect;
438         auto circleDiameter = menuSpacingBetweenHandle;
439         auto circleOffset =
440             OffsetF(downHandleRect.GetX() - (circleDiameter - downHandleRect.Width()) / 2.0f, downHandleRect.Bottom());
441         auto downHandleCircleRect = RectF(circleOffset, SizeF(circleDiameter, circleDiameter));
442         if (menuRect.IsIntersectWith(downHandleRect) || menuRect.IsInnerIntersectWith(downHandleCircleRect)) {
443             auto menuSpacing = static_cast<float>(menuSpacingBetweenText + circleDiameter);
444             menuPosition.SetY(downHandleRect.Bottom() + menuSpacing);
445         }
446     }
447     auto menuRect = RectF(menuPosition, SizeF(menuWidth, menuHeight));
448     menuPosition =
449         info_->isNewAvoid && !info_->isSingleHandle
450             ? NewMenuAvoidStrategy(layoutWrapper, menuWidth, menuHeight)
451             : AdjustSelectMenuOffset(layoutWrapper, menuRect, menuSpacingBetweenText, menuSpacingBetweenHandle);
452     AdjustToInfo(layoutWrapper, menuPosition, menuRect, windowOffset, info_);
453     AdjustMenuInRootRect(menuPosition, menuRect.GetSize(), layoutWrapper->GetGeometryNode()->GetFrameSize());
454 
455     defaultMenuStartOffset_ = menuPosition;
456     defaultMenuEndOffset_ = menuPosition + OffsetF(menuWidth, 0.0f);
457     // back and more button layout is on the left side of the selectmenu when reverse layout.
458     if (isExtension && !isReverse) {
459         OffsetF position = defaultMenuEndOffset_ - OffsetF(width, 0);
460         TAG_LOGD(AceLogTag::ACE_SELECT_OVERLAY, "ComputeSelectMenuPosition isExtension menuPosition : %{public}s",
461             position.ToString().c_str());
462         return position;
463     }
464     TAG_LOGD(AceLogTag::ACE_SELECT_OVERLAY, "ComputeSelectMenuPosition final menuPosition : %{public}s",
465         menuPosition.ToString().c_str());
466     return menuPosition;
467 }
468 
AdjustMenuInRootRect(OffsetF & menuOffset,const SizeF & menuSize,const SizeF & rootSize)469 void SelectOverlayLayoutAlgorithm::AdjustMenuInRootRect(
470     OffsetF& menuOffset, const SizeF& menuSize, const SizeF& rootSize)
471 {
472     auto pipeline = PipelineContext::GetCurrentContextSafelyWithCheck();
473     CHECK_NULL_VOID(pipeline);
474     auto theme = pipeline->GetTheme<TextOverlayTheme>();
475     CHECK_NULL_VOID(theme);
476     // adjust x
477     auto defaultPositionX = theme->GetDefaultMenuPositionX();
478     auto menuX = LessOrEqual(menuOffset.GetX(), defaultPositionX) ? defaultPositionX : menuOffset.GetX();
479     menuX = GreatOrEqual(menuX + menuSize.Width(), rootSize.Width() - defaultPositionX)
480                 ? rootSize.Width() - defaultPositionX - menuSize.Width()
481                 : menuX;
482     menuOffset.SetX(menuX);
483     // adjust y
484     auto menuY = LessNotEqual(menuOffset.GetY(), 0.0f) ? 0.0f : menuOffset.GetY();
485     menuY = GreatNotEqual(menuY + menuSize.Height(), rootSize.Height()) ? rootSize.Height() - menuSize.Height() : menuY;
486     menuOffset.SetY(menuY);
487 }
488 
AdjustSelectMenuOffset(LayoutWrapper * layoutWrapper,const RectF & menuRect,double spaceBetweenText,double spaceBetweenHandle)489 OffsetF SelectOverlayLayoutAlgorithm::AdjustSelectMenuOffset(
490     LayoutWrapper* layoutWrapper, const RectF& menuRect, double spaceBetweenText, double spaceBetweenHandle)
491 {
492     auto menuOffset = menuRect.GetOffset();
493     CHECK_NULL_RETURN((info_->firstHandle.isShow || info_->secondHandle.isShow),
494         AdjustSelectMenuOffsetWhenHandlesUnshown(menuRect, spaceBetweenText));
495     auto offset = layoutWrapper->GetGeometryNode()->GetFrameOffset();
496     auto upHandle = info_->handleReverse ? info_->secondHandle : info_->firstHandle;
497     auto downHandle = info_->handleReverse ? info_->firstHandle : info_->secondHandle;
498     AdjustMenuTooFarAway(layoutWrapper, menuOffset, menuRect);
499     // menu cover up handle
500     auto windowOffset = mainWindowOffset_ + containerModalOffset_;
501     auto upPaint = upHandle.GetPaintRect() - offset + windowOffset;
502     auto downPaint = downHandle.GetPaintRect() - offset + windowOffset;
503     if (!info_->isSingleHandle && upHandle.isShow && !downHandle.isShow) {
504         auto circleOffset = OffsetF(
505             upPaint.GetX() - (spaceBetweenHandle - upPaint.Width()) / 2.0f, upPaint.GetY() - spaceBetweenHandle);
506         auto upCircleRect = RectF(circleOffset, SizeF(spaceBetweenHandle, spaceBetweenHandle));
507         if (menuRect.IsIntersectWith(upPaint) || menuRect.IsIntersectWith(upCircleRect)) {
508             menuOffset.SetY(upPaint.Bottom() + spaceBetweenText + spaceBetweenHandle);
509         }
510         return menuOffset;
511     }
512     // avoid soft keyboard and root bottom
513     if ((!upHandle.isShow && downHandle.isShow) || info_->menuInfo.menuBuilder) {
514         auto pipeline = PipelineContext::GetCurrentContextSafelyWithCheck();
515         CHECK_NULL_RETURN(pipeline, menuOffset);
516         auto safeAreaManager = pipeline->GetSafeAreaManager();
517         CHECK_NULL_RETURN(safeAreaManager, menuOffset);
518         auto keyboardInsert = safeAreaManager->GetKeyboardInsetImpl();
519         auto shouldAvoidKeyboard =
520             GreatNotEqual(keyboardInsert.Length(), 0.0f) && GreatNotEqual(menuRect.Bottom(), keyboardInsert.start);
521         auto rootRect = layoutWrapper->GetGeometryNode()->GetFrameRect();
522         auto shouldAvoidBottom = GreatNotEqual(menuRect.Bottom(), rootRect.Height());
523         auto menuSpace = NearEqual(upPaint.Top(), downPaint.Top()) ? spaceBetweenHandle : spaceBetweenText;
524         auto offsetY = downPaint.GetY() - menuSpace - menuRect.Height();
525         auto topArea = safeAreaManager->GetSystemSafeArea().top_.Length();
526         if ((shouldAvoidKeyboard || shouldAvoidBottom) && GreatNotEqual(offsetY, 0)) {
527             if (GreatNotEqual(topArea, offsetY)) {
528                 offsetY = downPaint.Bottom() - spaceBetweenText - menuRect.Height();
529             }
530             menuOffset.SetY(offsetY);
531         } else {
532             if (GreatNotEqual(topArea, menuOffset.GetY()) && info_->isSingleHandle) {
533                 menuOffset.SetY(downPaint.Bottom() + spaceBetweenText + spaceBetweenHandle);
534             }
535             AdjustMenuOffsetAtSingleHandleBottom(downPaint, menuRect, menuOffset, spaceBetweenText);
536         }
537     }
538     return menuOffset;
539 }
540 
AdjustMenuOffsetAtSingleHandleBottom(const RectF handleRect,const RectF & menuRect,OffsetF & menuOffset,double spaceBetweenText)541 void SelectOverlayLayoutAlgorithm::AdjustMenuOffsetAtSingleHandleBottom(const RectF handleRect, const RectF& menuRect,
542     OffsetF& menuOffset, double spaceBetweenText)
543 {
544     CHECK_NULL_VOID(info_->isSingleHandle);
545     auto pipeline = PipelineContext::GetCurrentContextSafelyWithCheck();
546     CHECK_NULL_VOID(pipeline);
547     auto safeAreaManager = pipeline->GetSafeAreaManager();
548     CHECK_NULL_VOID(safeAreaManager);
549     auto keyboardInsert = safeAreaManager->GetKeyboardInsetImpl();
550     auto shouldAvoidKeyboard = GreatNotEqual(keyboardInsert.Length(), 0.0f) &&
551                                GreatNotEqual(menuOffset.GetY() + menuRect.Height(), keyboardInsert.start);
552     if (shouldAvoidKeyboard) {
553         menuOffset.SetY(handleRect.Bottom() - spaceBetweenText - menuRect.Height());
554     }
555 }
556 
AdjustSelectMenuOffsetWhenHandlesUnshown(const RectF & menuRect,double spaceBetweenText)557 OffsetF SelectOverlayLayoutAlgorithm::AdjustSelectMenuOffsetWhenHandlesUnshown(const RectF& menuRect,
558     double spaceBetweenText)
559 {
560     auto menuOffset = menuRect.GetOffset();
561     CHECK_NULL_RETURN(info_->isSingleHandle, menuOffset);
562     auto pipeline = PipelineContext::GetCurrentContextSafelyWithCheck();
563     CHECK_NULL_RETURN(pipeline, menuOffset);
564     auto safeAreaManager = pipeline->GetSafeAreaManager();
565     CHECK_NULL_RETURN(safeAreaManager, menuOffset);
566     auto topArea = safeAreaManager->GetSystemSafeArea().top_.Length();
567     auto selectArea = info_->selectArea;
568     selectArea += mainWindowOffset_ + containerModalOffset_;
569     if (topArea > menuOffset.GetY()) {
570         menuOffset.SetY((selectArea.Top() + selectArea.Bottom() - menuRect.Height()) / 2.0f);
571         return menuOffset;
572     }
573     auto keyboardInsert = safeAreaManager->GetKeyboardInsetImpl();
574     auto shouldAvoidKeyboard = GreatNotEqual(keyboardInsert.Length(), 0.0f) &&
575         GreatNotEqual(menuRect.Bottom(), keyboardInsert.start);
576     auto isBottomTouchKeyboard = GreatNotEqual(keyboardInsert.Length(), 0.0f) &&
577         GreatNotEqual(selectArea.Bottom(), keyboardInsert.start);
578     if (!isBottomTouchKeyboard && shouldAvoidKeyboard) {
579         menuOffset.SetY(selectArea.Bottom() - spaceBetweenText - menuRect.Height());
580         return menuOffset;
581     }
582     if (shouldAvoidKeyboard) {
583         menuOffset.SetY((selectArea.Top() + selectArea.Bottom() - menuRect.Height()) / 2.0f);
584     }
585     return menuOffset;
586 }
587 
IsMenuAreaSmallerHandleArea(RectF handleRect,float menuHeight,float menuDistance)588 bool SelectOverlayLayoutAlgorithm::IsMenuAreaSmallerHandleArea(RectF handleRect, float menuHeight, float menuDistance)
589 {
590     return handleRect.Height() > menuHeight + menuDistance;
591 }
592 
AdjustMenuTooFarAway(LayoutWrapper * layoutWrapper,OffsetF & menuOffset,const RectF & menuRect)593 void SelectOverlayLayoutAlgorithm::AdjustMenuTooFarAway(
594     LayoutWrapper* layoutWrapper, OffsetF& menuOffset, const RectF& menuRect)
595 {
596     // the menu is too far away.
597     auto hostFrameNode = info_->callerFrameNode.Upgrade();
598     CHECK_NULL_VOID(hostFrameNode);
599     auto pipeline = hostFrameNode->GetContext();
600     CHECK_NULL_VOID(pipeline);
601     auto hostFrameRect = hostFrameNode->GetGeometryNode()->GetFrameRect();
602     auto hostGlobalOffset = hostFrameNode->GetPaintRectOffset(false, true) - pipeline->GetRootRect().GetOffset();
603     bool isMenuShowInsubWindow = GetIsMenuShowInSubWindow(layoutWrapper);
604     if (isMenuShowInsubWindow) {
605         hostGlobalOffset = hostFrameNode->GetPaintRectOffset(false, true) + mainWindowOffset_;
606     }
607     auto centerX = menuRect.Width() / 2.0f;
608     if (info_->callerNodeInfo) {
609         hostFrameRect = info_->callerNodeInfo->paintFrameRect;
610         hostGlobalOffset = info_->callerNodeInfo->paintOffset;
611         if (isMenuShowInsubWindow) {
612             hostGlobalOffset =
613                 info_->callerNodeInfo->paintOffset + pipeline->GetRootRect().GetOffset() + mainWindowOffset_;
614         }
615     }
616     if (GreatNotEqual(menuRect.GetX() + centerX, hostGlobalOffset.GetX() + hostFrameRect.Width())) {
617         menuOffset.SetX(hostGlobalOffset.GetX() + hostFrameRect.Width() - centerX);
618         return;
619     }
620     if (LessNotEqual(menuRect.GetX() + centerX, hostGlobalOffset.GetX())) {
621         menuOffset.SetX(hostGlobalOffset.GetX() - centerX);
622     }
623 }
624 
ComputeExtensionMenuPosition(LayoutWrapper * layoutWrapper,const OffsetF & offset)625 OffsetF SelectOverlayLayoutAlgorithm::ComputeExtensionMenuPosition(LayoutWrapper* layoutWrapper, const OffsetF& offset)
626 {
627     auto extensionItem = layoutWrapper->GetOrCreateChildByIndex(2);
628     CHECK_NULL_RETURN(extensionItem, OffsetF());
629     auto extensionLayoutConstraint = extensionItem->GetLayoutProperty()->GetLayoutConstraint();
630     auto extensionLayoutConstraintMaxSize = extensionLayoutConstraint->maxSize;
631     auto extensionWidth = extensionItem->GetGeometryNode()->GetMarginFrameSize().Width();
632     auto extensionHeight = extensionItem->GetGeometryNode()->GetMarginFrameSize().Height();
633     auto menuItem = layoutWrapper->GetOrCreateChildByIndex(0);
634     CHECK_NULL_RETURN(menuItem, OffsetF());
635     auto menuHeight = menuItem->GetGeometryNode()->GetMarginFrameSize().Height();
636     auto extensionOffset =
637         defaultMenuEndOffset_ - OffsetF(extensionWidth, -menuHeight - MORE_MENU_INTERVAL.ConvertToPx());
638     auto extensionBottom = extensionOffset.GetY() + extensionHeight;
639     auto isCoveredBySoftKeyBoard = [extensionBottom]() -> bool {
640         auto pipeline = PipelineContext::GetCurrentContextSafelyWithCheck();
641         CHECK_NULL_RETURN(pipeline, false);
642         auto safeAreaManager = pipeline->GetSafeAreaManager();
643         CHECK_NULL_RETURN(safeAreaManager, false);
644         auto keyboardInsert = safeAreaManager->GetKeyboardInsetImpl();
645         return GreatNotEqual(keyboardInsert.Length(), 0.0f) && GreatNotEqual(extensionBottom, keyboardInsert.start);
646     };
647     if (GreatNotEqual(extensionBottom, extensionLayoutConstraintMaxSize.Height()) || isCoveredBySoftKeyBoard()) {
648         extensionOffset =
649             defaultMenuEndOffset_ - OffsetF(extensionWidth, extensionHeight + MORE_MENU_INTERVAL.ConvertToPx());
650     }
651     return extensionOffset;
652 }
653 
IsTextAreaSelectAll()654 bool SelectOverlayLayoutAlgorithm::IsTextAreaSelectAll()
655 {
656     return info_->menuInfo.menuOffset.has_value() && (!info_->firstHandle.isShow || !info_->secondHandle.isShow);
657 }
658 
NewMenuAvoidStrategy(LayoutWrapper * layoutWrapper,float menuWidth,float menuHeight)659 OffsetF SelectOverlayLayoutAlgorithm::NewMenuAvoidStrategy(
660     LayoutWrapper* layoutWrapper, float menuWidth, float menuHeight)
661 {
662     auto pipeline = PipelineContext::GetCurrentContextSafelyWithCheck();
663     CHECK_NULL_RETURN(pipeline, OffsetF());
664     auto theme = pipeline->GetTheme<TextOverlayTheme>();
665     CHECK_NULL_RETURN(theme, OffsetF());
666     double menuSpacingBetweenText = theme->GetMenuSpacingWithText().ConvertToPx();
667     double menuSpacingBetweenHandle = theme->GetHandleDiameter().ConvertToPx() +
668                                       theme->GetHandleDiameterStrokeWidth().ConvertToPx();
669     double safeSpacing = theme->GetMenuSafeSpacing().ConvertToPx();
670     auto windowOffset = mainWindowOffset_ + containerModalOffset_;
671     auto selectArea = info_->selectArea + windowOffset;
672     // 安全区域
673     auto safeAreaManager = pipeline->GetSafeAreaManager();
674     CHECK_NULL_RETURN(safeAreaManager, OffsetF());
675     auto topArea = safeAreaManager->GetSystemSafeArea().top_.Length();
676     auto keyboardInsert = safeAreaManager->GetKeyboardInsetImpl();
677     float positionX = (selectArea.Left() + selectArea.Right() - menuWidth) / 2.0f;
678     auto hasKeyboard = GreatNotEqual(keyboardInsert.Length(), 0.0f);
679     auto downHandle = info_->handleReverse ? info_->firstHandle : info_->secondHandle;
680     auto downHandlePaint = downHandle.paintRect + windowOffset;
681     auto downHandleIsReallyShow = hasKeyboard ? ((LessOrEqual((double)downHandlePaint.Bottom(),
682         (double)keyboardInsert.start)) ? true : false) : downHandle.isShow;
683     auto upHandle = info_->handleReverse ? info_->secondHandle : info_->firstHandle;
684     auto offset = layoutWrapper->GetGeometryNode()->GetFrameOffset();
685     auto upPaint = upHandle.GetPaintRect() - offset + windowOffset;
686     auto downPaint = downHandle.GetPaintRect() - offset + windowOffset;
687     auto viewPort = pipeline->GetRootRect();
688     auto selectAndRootRectArea = selectArea.IntersectRectT(viewPort);
689     auto safeAreaBottom = safeAreaManager->GetSafeAreaWithoutProcess().bottom_.start;
690     auto menuAvoidBottomY = GreatNotEqual(safeAreaBottom, 0.0f) ? (safeAreaBottom - menuHeight)
691         : (viewPort.Bottom() - menuHeight);
692     auto bottomLimitOffsetY = hasKeyboard ? std::max(keyboardInsert.start - safeSpacing - menuHeight, (double)topArea)
693         : menuAvoidBottomY;
694 
695     AvoidStrategyMember avoidStrategyMember;
696     avoidStrategyMember.menuHeight = menuHeight;
697     avoidStrategyMember.menuSpacingBetweenText = menuSpacingBetweenText;
698     avoidStrategyMember.bottomLimitOffsetY = bottomLimitOffsetY;
699     avoidStrategyMember.menuSpacing = static_cast<float>(menuSpacingBetweenText + menuSpacingBetweenHandle);
700     avoidStrategyMember.hasKeyboard = GreatNotEqual(keyboardInsert.Length(), 0.0f);
701     avoidStrategyMember.keyboardInsertStart = keyboardInsert.start;
702     avoidStrategyMember.downHandleIsReallyShow = downHandle.isShow && downHandleIsReallyShow;
703     avoidStrategyMember.selectAreaTop = selectArea.Top();
704     avoidStrategyMember.selectAndRootRectAreaTop = upHandle.isShow ? upPaint.Top() : selectAndRootRectArea.Top();
705     avoidStrategyMember.selectAndRootRectAreaBottom =
706         avoidStrategyMember.downHandleIsReallyShow ? downPaint.Bottom() : selectAndRootRectArea.Bottom();
707     float offsetY = 0.0f;
708     NewMenuAvoidStrategyGetY(avoidStrategyMember, offsetY);
709     return OffsetF(positionX, offsetY);
710 }
711 
NewMenuAvoidStrategyGetY(const AvoidStrategyMember & avoidStrategyMember,float & offsetY)712 void SelectOverlayLayoutAlgorithm::NewMenuAvoidStrategyGetY(const AvoidStrategyMember& avoidStrategyMember,
713                                                             float& offsetY)
714 {
715     auto pipeline = PipelineContext::GetCurrentContextSafelyWithCheck();
716     CHECK_NULL_VOID(pipeline);
717     auto safeAreaManager = pipeline->GetSafeAreaManager();
718     CHECK_NULL_VOID(safeAreaManager);
719     auto topArea = safeAreaManager->GetSystemSafeArea().top_.Length();
720     auto upHandle = info_->handleReverse ? info_->secondHandle : info_->firstHandle;
721     // 顶部避让
722     offsetY = upHandle.isShow ? (avoidStrategyMember.selectAreaTop - avoidStrategyMember.menuSpacing -
723                   avoidStrategyMember.menuHeight) : (avoidStrategyMember.selectAreaTop -
724                   avoidStrategyMember.menuSpacingBetweenText - avoidStrategyMember.menuHeight);
725     if (!upHandle.isShow || LessOrEqual(offsetY, topArea)) {
726         auto selectBottom = avoidStrategyMember.hasKeyboard ? std::min(avoidStrategyMember.selectAndRootRectAreaBottom,
727             (double)avoidStrategyMember.keyboardInsertStart) : avoidStrategyMember.selectAndRootRectAreaBottom;
728         auto offsetBetweenSelectArea =
729             std::clamp((double)(avoidStrategyMember.selectAndRootRectAreaTop + selectBottom -
730                 avoidStrategyMember.menuHeight) / 2.0f, (double)topArea, avoidStrategyMember.bottomLimitOffsetY);
731         auto offsetYTmp = avoidStrategyMember.downHandleIsReallyShow ?
732                                   (avoidStrategyMember.selectAndRootRectAreaBottom + avoidStrategyMember.menuSpacing) :
733                                   (avoidStrategyMember.selectAndRootRectAreaBottom +
734                                   avoidStrategyMember.menuSpacingBetweenText);
735         if (avoidStrategyMember.downHandleIsReallyShow) {
736             bool isOffsetYInBottom = false;
737             // The upper handle is not visible and not in a single row, or offsetY <= topArea
738             if ((!upHandle.isShow && !info_->isSingleLine) || (LessOrEqual(offsetY, topArea))) {
739                 offsetY = offsetYTmp;
740                 isOffsetYInBottom = true;
741             }
742             if (isOffsetYInBottom && GreatNotEqual(offsetY, avoidStrategyMember.bottomLimitOffsetY)) {
743                 offsetY = offsetBetweenSelectArea;
744             }
745         } else {
746             if (info_->isSingleLine) {
747                 offsetY = LessOrEqual(offsetY, topArea) ?
748                     ((GreatNotEqual(offsetYTmp, avoidStrategyMember.bottomLimitOffsetY)) ?
749                         offsetBetweenSelectArea : offsetYTmp) : offsetY;
750             } else {
751                 offsetY = offsetBetweenSelectArea;
752             }
753         }
754     }
755     if (avoidStrategyMember.hasKeyboard && GreatNotEqual(offsetY, avoidStrategyMember.bottomLimitOffsetY)) {
756         offsetY = avoidStrategyMember.bottomLimitOffsetY;
757     }
758 }
759 
IsReverseLayout(LayoutWrapper * layoutWrapper) const760 bool SelectOverlayLayoutAlgorithm::IsReverseLayout(LayoutWrapper* layoutWrapper) const
761 {
762     CHECK_NULL_RETURN(layoutWrapper, false);
763     auto layoutProperty = layoutWrapper->GetLayoutProperty();
764     CHECK_NULL_RETURN(layoutProperty, false);
765     return layoutProperty->GetNonAutoLayoutDirection() == TextDirection::RTL;
766 }
767 
CheckHandleIsInClipViewPort()768 void SelectOverlayLayoutAlgorithm::CheckHandleIsInClipViewPort()
769 {
770     if (!info_->clipHandleDrawRect || info_->secondHandle.isPaintHandleWithPoints ||
771         info_->handleLevelMode == HandleLevelMode::EMBED) {
772         return;
773     }
774     if (!info_->isSingleHandle) {
775         RectF viewPort;
776         info_->GetCallerNodeAncestorViewPort(viewPort);
777         auto isInRegion = [](const RectF& viewPort, float left, float right, float verticalY) {
778             return LessOrEqual(left, viewPort.Right()) &&
779                    GreatOrEqual(right, viewPort.Left()) &&
780                    GreatOrEqual(verticalY, viewPort.Top() - ROUND_EPSILON) &&
781                    LessOrEqual(verticalY, viewPort.Bottom() + ROUND_EPSILON);
782         };
783         auto& handleOnTop = !info_->handleReverse ? info_->firstHandle : info_->secondHandle;
784         handleOnTop.isShow = isInRegion(
785             viewPort, handleOnTop.paintRect.Left(), handleOnTop.paintRect.Right(), handleOnTop.paintRect.Top());
786         auto& handleOnBottom = !info_->handleReverse ? info_->secondHandle : info_->firstHandle;
787         handleOnBottom.isShow = isInRegion(viewPort, handleOnBottom.paintRect.Left(), handleOnBottom.paintRect.Right(),
788             handleOnBottom.paintRect.Bottom());
789     }
790 }
791 
UpdateMainWindowOffset(LayoutWrapper * layoutWrapper)792 void SelectOverlayLayoutAlgorithm::UpdateMainWindowOffset(LayoutWrapper* layoutWrapper)
793 {
794     CHECK_NULL_VOID(layoutWrapper);
795     auto host = layoutWrapper->GetHostNode();
796     CHECK_NULL_VOID(host);
797     auto pattern = host->GetPattern<SelectOverlayPattern>();
798     CHECK_NULL_VOID(pattern);
799     bool isMenuShowInsubWindow = pattern->GetIsMenuShowInSubWindow();
800     if (!isMenuShowInsubWindow) {
801         mainWindowOffset_ = OffsetF(0.0, 0.0);
802         containerModalOffset_ = OffsetF(0.0, 0.0);
803         return;
804     }
805     auto containerId = pattern->GetContainerId();
806     if (containerId == -1) {
807         mainWindowOffset_ = OffsetF(0.0, 0.0);
808         containerModalOffset_ = OffsetF(0.0, 0.0);
809         TAG_LOGW(AceLogTag::ACE_SELECT_OVERLAY, "UpdateMainWindowOffset containerId is invalid.");
810         return;
811     }
812     auto container = Container::GetContainer(containerId);
813     CHECK_NULL_VOID(container);
814     auto pipelineContext = AceType::DynamicCast<PipelineContext>(container->GetPipelineContext());
815     CHECK_NULL_VOID(pipelineContext);
816     auto selectTheme = pipelineContext->GetTheme<SelectTheme>();
817     CHECK_NULL_VOID(selectTheme);
818     auto isExpandDisplay = selectTheme->GetExpandDisplay();
819     auto displayWindowRect = pipelineContext->GetDisplayWindowRectInfo();
820     if (isExpandDisplay || container->IsFreeMultiWindow()) {
821         mainWindowOffset_ = OffsetF(displayWindowRect.Left(), displayWindowRect.Top());
822     }
823     containerModalOffset_ = info_->containerModalOffset;
824     TAG_LOGI(AceLogTag::ACE_SELECT_OVERLAY,
825         "UpdateMainWindowOffset mainWindowOffset : %{public}s containerModalOffset : %{public}s",
826         mainWindowOffset_.ToString().c_str(), containerModalOffset_.ToString().c_str());
827 }
828 
GetIsMenuShowInSubWindow(LayoutWrapper * layoutWrapper)829 bool SelectOverlayLayoutAlgorithm::GetIsMenuShowInSubWindow(LayoutWrapper* layoutWrapper)
830 {
831     CHECK_NULL_RETURN(layoutWrapper, false);
832     auto host = layoutWrapper->GetHostNode();
833     CHECK_NULL_RETURN(host, false);
834     auto pattern = host->GetPattern<SelectOverlayPattern>();
835     CHECK_NULL_RETURN(pattern, false);
836     return pattern->GetIsMenuShowInSubWindow();
837 }
838 
AdjustToInfo(LayoutWrapper * layoutWrapper,OffsetF & menuOffset,const RectF & menuRect,OffsetF & windowOffset,std::shared_ptr<SelectOverlayInfo> & info)839 bool SelectOverlayLayoutAlgorithm::AdjustToInfo(LayoutWrapper *layoutWrapper, OffsetF &menuOffset,
840     const RectF &menuRect, OffsetF &windowOffset, std::shared_ptr<SelectOverlayInfo> &info)
841 {
842     CHECK_NULL_RETURN(info, false);
843     CHECK_NULL_RETURN(info->computeMenuOffset, false);
844     return info_->computeMenuOffset(layoutWrapper, menuOffset, menuRect, windowOffset, info);
845 }
846 } // namespace OHOS::Ace::NG
847