• 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->GetKeyboardInset().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->GetKeyboardInset();
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     AdjustMenuInRootRect(menuPosition, menuRect.GetSize(), layoutWrapper->GetGeometryNode()->GetFrameSize());
453 
454     defaultMenuStartOffset_ = menuPosition;
455     defaultMenuEndOffset_ = menuPosition + OffsetF(menuWidth, 0.0f);
456     // back and more button layout is on the left side of the selectmenu when reverse layout.
457     if (isExtension && !isReverse) {
458         OffsetF position = defaultMenuEndOffset_ - OffsetF(width, 0);
459         TAG_LOGD(AceLogTag::ACE_SELECT_OVERLAY, "ComputeSelectMenuPosition isExtension menuPosition : %{public}s",
460             position.ToString().c_str());
461         return position;
462     }
463     TAG_LOGD(AceLogTag::ACE_SELECT_OVERLAY, "ComputeSelectMenuPosition final menuPosition : %{public}s",
464         menuPosition.ToString().c_str());
465     return menuPosition;
466 }
467 
AdjustMenuInRootRect(OffsetF & menuOffset,const SizeF & menuSize,const SizeF & rootSize)468 void SelectOverlayLayoutAlgorithm::AdjustMenuInRootRect(
469     OffsetF& menuOffset, const SizeF& menuSize, const SizeF& rootSize)
470 {
471     auto pipeline = PipelineContext::GetCurrentContextSafelyWithCheck();
472     CHECK_NULL_VOID(pipeline);
473     auto theme = pipeline->GetTheme<TextOverlayTheme>();
474     CHECK_NULL_VOID(theme);
475     // adjust x
476     auto defaultPositionX = theme->GetDefaultMenuPositionX();
477     auto menuX = LessOrEqual(menuOffset.GetX(), defaultPositionX) ? defaultPositionX : menuOffset.GetX();
478     menuX = GreatOrEqual(menuX + menuSize.Width(), rootSize.Width() - defaultPositionX)
479                 ? rootSize.Width() - defaultPositionX - menuSize.Width()
480                 : menuX;
481     menuOffset.SetX(menuX);
482     // adjust y
483     auto menuY = LessNotEqual(menuOffset.GetY(), 0.0f) ? 0.0f : menuOffset.GetY();
484     menuY = GreatNotEqual(menuY + menuSize.Height(), rootSize.Height()) ? rootSize.Height() - menuSize.Height() : menuY;
485     menuOffset.SetY(menuY);
486 }
487 
AdjustSelectMenuOffset(LayoutWrapper * layoutWrapper,const RectF & menuRect,double spaceBetweenText,double spaceBetweenHandle)488 OffsetF SelectOverlayLayoutAlgorithm::AdjustSelectMenuOffset(
489     LayoutWrapper* layoutWrapper, const RectF& menuRect, double spaceBetweenText, double spaceBetweenHandle)
490 {
491     auto menuOffset = menuRect.GetOffset();
492     CHECK_NULL_RETURN((info_->firstHandle.isShow || info_->secondHandle.isShow),
493         AdjustSelectMenuOffsetWhenHandlesUnshown(menuRect, spaceBetweenText));
494     auto offset = layoutWrapper->GetGeometryNode()->GetFrameOffset();
495     auto upHandle = info_->handleReverse ? info_->secondHandle : info_->firstHandle;
496     auto downHandle = info_->handleReverse ? info_->firstHandle : info_->secondHandle;
497     AdjustMenuTooFarAway(layoutWrapper, menuOffset, menuRect);
498     // menu cover up handle
499     auto windowOffset = mainWindowOffset_ + containerModalOffset_;
500     auto upPaint = upHandle.GetPaintRect() - offset + windowOffset;
501     auto downPaint = downHandle.GetPaintRect() - offset + windowOffset;
502     if (!info_->isSingleHandle && upHandle.isShow && !downHandle.isShow) {
503         auto circleOffset = OffsetF(
504             upPaint.GetX() - (spaceBetweenHandle - upPaint.Width()) / 2.0f, upPaint.GetY() - spaceBetweenHandle);
505         auto upCircleRect = RectF(circleOffset, SizeF(spaceBetweenHandle, spaceBetweenHandle));
506         if (menuRect.IsIntersectWith(upPaint) || menuRect.IsIntersectWith(upCircleRect)) {
507             menuOffset.SetY(upPaint.Bottom() + spaceBetweenText + spaceBetweenHandle);
508         }
509         return menuOffset;
510     }
511     // avoid soft keyboard and root bottom
512     if ((!upHandle.isShow && downHandle.isShow) || info_->menuInfo.menuBuilder) {
513         auto pipeline = PipelineContext::GetCurrentContextSafelyWithCheck();
514         CHECK_NULL_RETURN(pipeline, menuOffset);
515         auto safeAreaManager = pipeline->GetSafeAreaManager();
516         CHECK_NULL_RETURN(safeAreaManager, menuOffset);
517         auto keyboardInsert = safeAreaManager->GetKeyboardInset();
518         auto shouldAvoidKeyboard =
519             GreatNotEqual(keyboardInsert.Length(), 0.0f) && GreatNotEqual(menuRect.Bottom(), keyboardInsert.start);
520         auto rootRect = layoutWrapper->GetGeometryNode()->GetFrameRect();
521         auto shouldAvoidBottom = GreatNotEqual(menuRect.Bottom(), rootRect.Height());
522         auto menuSpace = NearEqual(upPaint.Top(), downPaint.Top()) ? spaceBetweenHandle : spaceBetweenText;
523         auto offsetY = downPaint.GetY() - menuSpace - menuRect.Height();
524         auto topArea = safeAreaManager->GetSystemSafeArea().top_.Length();
525         if ((shouldAvoidKeyboard || shouldAvoidBottom) && GreatNotEqual(offsetY, 0)) {
526             if (GreatNotEqual(topArea, offsetY)) {
527                 offsetY = downPaint.Bottom() - spaceBetweenText - menuRect.Height();
528             }
529             menuOffset.SetY(offsetY);
530         } else {
531             if (GreatNotEqual(topArea, menuOffset.GetY()) && info_->isSingleHandle) {
532                 menuOffset.SetY(downPaint.Bottom() + spaceBetweenText + spaceBetweenHandle);
533             }
534             AdjustMenuOffsetAtSingleHandleBottom(downPaint, menuRect, menuOffset, spaceBetweenText);
535         }
536     }
537     return menuOffset;
538 }
539 
AdjustMenuOffsetAtSingleHandleBottom(const RectF handleRect,const RectF & menuRect,OffsetF & menuOffset,double spaceBetweenText)540 void SelectOverlayLayoutAlgorithm::AdjustMenuOffsetAtSingleHandleBottom(const RectF handleRect, const RectF& menuRect,
541     OffsetF& menuOffset, double spaceBetweenText)
542 {
543     CHECK_NULL_VOID(info_->isSingleHandle);
544     auto pipeline = PipelineContext::GetCurrentContextSafelyWithCheck();
545     CHECK_NULL_VOID(pipeline);
546     auto safeAreaManager = pipeline->GetSafeAreaManager();
547     CHECK_NULL_VOID(safeAreaManager);
548     auto keyboardInsert = safeAreaManager->GetKeyboardInset();
549     auto shouldAvoidKeyboard = GreatNotEqual(keyboardInsert.Length(), 0.0f) &&
550                                GreatNotEqual(menuOffset.GetY() + menuRect.Height(), keyboardInsert.start);
551     if (shouldAvoidKeyboard) {
552         menuOffset.SetY(handleRect.Bottom() - spaceBetweenText - menuRect.Height());
553     }
554 }
555 
AdjustSelectMenuOffsetWhenHandlesUnshown(const RectF & menuRect,double spaceBetweenText)556 OffsetF SelectOverlayLayoutAlgorithm::AdjustSelectMenuOffsetWhenHandlesUnshown(const RectF& menuRect,
557     double spaceBetweenText)
558 {
559     auto menuOffset = menuRect.GetOffset();
560     CHECK_NULL_RETURN(info_->isSingleHandle, menuOffset);
561     auto pipeline = PipelineContext::GetCurrentContextSafelyWithCheck();
562     CHECK_NULL_RETURN(pipeline, menuOffset);
563     auto safeAreaManager = pipeline->GetSafeAreaManager();
564     CHECK_NULL_RETURN(safeAreaManager, menuOffset);
565     auto topArea = safeAreaManager->GetSystemSafeArea().top_.Length();
566     auto selectArea = info_->selectArea;
567     selectArea += mainWindowOffset_ + containerModalOffset_;
568     if (topArea > menuOffset.GetY()) {
569         menuOffset.SetY((selectArea.Top() + selectArea.Bottom() - menuRect.Height()) / 2.0f);
570         return menuOffset;
571     }
572     auto keyboardInsert = safeAreaManager->GetKeyboardInset();
573     auto shouldAvoidKeyboard = GreatNotEqual(keyboardInsert.Length(), 0.0f) &&
574         GreatNotEqual(menuRect.Bottom(), keyboardInsert.start);
575     auto isBottomTouchKeyboard = GreatNotEqual(keyboardInsert.Length(), 0.0f) &&
576         GreatNotEqual(selectArea.Bottom(), keyboardInsert.start);
577     if (!isBottomTouchKeyboard && shouldAvoidKeyboard) {
578         menuOffset.SetY(selectArea.Bottom() - spaceBetweenText - menuRect.Height());
579         return menuOffset;
580     }
581     if (shouldAvoidKeyboard) {
582         menuOffset.SetY((selectArea.Top() + selectArea.Bottom() - menuRect.Height()) / 2.0f);
583     }
584     return menuOffset;
585 }
586 
IsMenuAreaSmallerHandleArea(RectF handleRect,float menuHeight,float menuDistance)587 bool SelectOverlayLayoutAlgorithm::IsMenuAreaSmallerHandleArea(RectF handleRect, float menuHeight, float menuDistance)
588 {
589     return handleRect.Height() > menuHeight + menuDistance;
590 }
591 
AdjustMenuTooFarAway(LayoutWrapper * layoutWrapper,OffsetF & menuOffset,const RectF & menuRect)592 void SelectOverlayLayoutAlgorithm::AdjustMenuTooFarAway(
593     LayoutWrapper* layoutWrapper, OffsetF& menuOffset, const RectF& menuRect)
594 {
595     // the menu is too far away.
596     auto hostFrameNode = info_->callerFrameNode.Upgrade();
597     CHECK_NULL_VOID(hostFrameNode);
598     auto pipeline = hostFrameNode->GetContext();
599     CHECK_NULL_VOID(pipeline);
600     auto hostFrameRect = hostFrameNode->GetGeometryNode()->GetFrameRect();
601     auto hostGlobalOffset = hostFrameNode->GetPaintRectOffset(false, true) - pipeline->GetRootRect().GetOffset();
602     bool isMenuShowInsubWindow = GetIsMenuShowInSubWindow(layoutWrapper);
603     if (isMenuShowInsubWindow) {
604         hostGlobalOffset = hostFrameNode->GetPaintRectOffset(false, true) + mainWindowOffset_;
605     }
606     auto centerX = menuRect.Width() / 2.0f;
607     if (info_->callerNodeInfo) {
608         hostFrameRect = info_->callerNodeInfo->paintFrameRect;
609         hostGlobalOffset = info_->callerNodeInfo->paintOffset;
610         if (isMenuShowInsubWindow) {
611             hostGlobalOffset =
612                 info_->callerNodeInfo->paintOffset + pipeline->GetRootRect().GetOffset() + mainWindowOffset_;
613         }
614     }
615     if (GreatNotEqual(menuRect.GetX() + centerX, hostGlobalOffset.GetX() + hostFrameRect.Width())) {
616         menuOffset.SetX(hostGlobalOffset.GetX() + hostFrameRect.Width() - centerX);
617         return;
618     }
619     if (LessNotEqual(menuRect.GetX() + centerX, hostGlobalOffset.GetX())) {
620         menuOffset.SetX(hostGlobalOffset.GetX() - centerX);
621     }
622 }
623 
ComputeExtensionMenuPosition(LayoutWrapper * layoutWrapper,const OffsetF & offset)624 OffsetF SelectOverlayLayoutAlgorithm::ComputeExtensionMenuPosition(LayoutWrapper* layoutWrapper, const OffsetF& offset)
625 {
626     auto extensionItem = layoutWrapper->GetOrCreateChildByIndex(2);
627     CHECK_NULL_RETURN(extensionItem, OffsetF());
628     auto extensionLayoutConstraint = extensionItem->GetLayoutProperty()->GetLayoutConstraint();
629     auto extensionLayoutConstraintMaxSize = extensionLayoutConstraint->maxSize;
630     auto extensionWidth = extensionItem->GetGeometryNode()->GetMarginFrameSize().Width();
631     auto extensionHeight = extensionItem->GetGeometryNode()->GetMarginFrameSize().Height();
632     auto menuItem = layoutWrapper->GetOrCreateChildByIndex(0);
633     CHECK_NULL_RETURN(menuItem, OffsetF());
634     auto menuHeight = menuItem->GetGeometryNode()->GetMarginFrameSize().Height();
635     auto extensionOffset =
636         defaultMenuEndOffset_ - OffsetF(extensionWidth, -menuHeight - MORE_MENU_INTERVAL.ConvertToPx());
637     auto extensionBottom = extensionOffset.GetY() + extensionHeight;
638     auto isCoveredBySoftKeyBoard = [extensionBottom]() -> bool {
639         auto pipeline = PipelineContext::GetCurrentContextSafelyWithCheck();
640         CHECK_NULL_RETURN(pipeline, false);
641         auto safeAreaManager = pipeline->GetSafeAreaManager();
642         CHECK_NULL_RETURN(safeAreaManager, false);
643         auto keyboardInsert = safeAreaManager->GetKeyboardInset();
644         return GreatNotEqual(keyboardInsert.Length(), 0.0f) && GreatNotEqual(extensionBottom, keyboardInsert.start);
645     };
646     if (GreatNotEqual(extensionBottom, extensionLayoutConstraintMaxSize.Height()) || isCoveredBySoftKeyBoard()) {
647         extensionOffset =
648             defaultMenuEndOffset_ - OffsetF(extensionWidth, extensionHeight + MORE_MENU_INTERVAL.ConvertToPx());
649     }
650     return extensionOffset;
651 }
652 
IsTextAreaSelectAll()653 bool SelectOverlayLayoutAlgorithm::IsTextAreaSelectAll()
654 {
655     return info_->menuInfo.menuOffset.has_value() && (!info_->firstHandle.isShow || !info_->secondHandle.isShow);
656 }
657 
NewMenuAvoidStrategy(LayoutWrapper * layoutWrapper,float menuWidth,float menuHeight)658 OffsetF SelectOverlayLayoutAlgorithm::NewMenuAvoidStrategy(
659     LayoutWrapper* layoutWrapper, float menuWidth, float menuHeight)
660 {
661     auto pipeline = PipelineContext::GetCurrentContextSafelyWithCheck();
662     CHECK_NULL_RETURN(pipeline, OffsetF());
663     auto theme = pipeline->GetTheme<TextOverlayTheme>();
664     CHECK_NULL_RETURN(theme, OffsetF());
665     double menuSpacingBetweenText = theme->GetMenuSpacingWithText().ConvertToPx();
666     double menuSpacingBetweenHandle = theme->GetHandleDiameter().ConvertToPx() +
667                                       theme->GetHandleDiameterStrokeWidth().ConvertToPx();
668     double safeSpacing = theme->GetMenuSafeSpacing().ConvertToPx();
669     auto windowOffset = mainWindowOffset_ + containerModalOffset_;
670     auto selectArea = info_->selectArea + windowOffset;
671     // 安全区域
672     auto safeAreaManager = pipeline->GetSafeAreaManager();
673     CHECK_NULL_RETURN(safeAreaManager, OffsetF());
674     auto topArea = safeAreaManager->GetSystemSafeArea().top_.Length();
675     auto keyboardInsert = safeAreaManager->GetKeyboardInset();
676     float positionX = (selectArea.Left() + selectArea.Right() - menuWidth) / 2.0f;
677     auto hasKeyboard = GreatNotEqual(keyboardInsert.Length(), 0.0f);
678     auto downHandle = info_->handleReverse ? info_->firstHandle : info_->secondHandle;
679     auto downHandlePaint = downHandle.paintRect + windowOffset;
680     auto downHandleIsReallyShow = hasKeyboard ? ((LessOrEqual((double)downHandlePaint.Bottom(),
681         (double)keyboardInsert.start)) ? true : false) : downHandle.isShow;
682     auto upHandle = info_->handleReverse ? info_->secondHandle : info_->firstHandle;
683     auto offset = layoutWrapper->GetGeometryNode()->GetFrameOffset();
684     auto upPaint = upHandle.GetPaintRect() - offset + windowOffset;
685     auto downPaint = downHandle.GetPaintRect() - offset + windowOffset;
686     auto viewPort = pipeline->GetRootRect();
687     auto selectAndRootRectArea = selectArea.IntersectRectT(viewPort);
688     auto safeAreaBottom = safeAreaManager->GetSafeAreaWithoutProcess().bottom_.start;
689     auto menuAvoidBottomY = GreatNotEqual(safeAreaBottom, 0.0f) ? (safeAreaBottom - menuHeight)
690         : (viewPort.Bottom() - menuHeight);
691     auto bottomLimitOffsetY = hasKeyboard ? std::max(keyboardInsert.start - safeSpacing - menuHeight, (double)topArea)
692         : menuAvoidBottomY;
693 
694     AvoidStrategyMember avoidStrategyMember;
695     avoidStrategyMember.menuHeight = menuHeight;
696     avoidStrategyMember.menuSpacingBetweenText = menuSpacingBetweenText;
697     avoidStrategyMember.bottomLimitOffsetY = bottomLimitOffsetY;
698     avoidStrategyMember.menuSpacing = static_cast<float>(menuSpacingBetweenText + menuSpacingBetweenHandle);
699     avoidStrategyMember.hasKeyboard = GreatNotEqual(keyboardInsert.Length(), 0.0f);
700     avoidStrategyMember.keyboardInsertStart = keyboardInsert.start;
701     avoidStrategyMember.downHandleIsReallyShow = downHandle.isShow && downHandleIsReallyShow;
702     avoidStrategyMember.selectAreaTop = selectArea.Top();
703     avoidStrategyMember.selectAndRootRectAreaTop = upHandle.isShow ? upPaint.Top() : selectAndRootRectArea.Top();
704     avoidStrategyMember.selectAndRootRectAreaBottom =
705         avoidStrategyMember.downHandleIsReallyShow ? downPaint.Bottom() : selectAndRootRectArea.Bottom();
706     float offsetY = 0.0f;
707     NewMenuAvoidStrategyGetY(avoidStrategyMember, offsetY);
708     return OffsetF(positionX, offsetY);
709 }
710 
NewMenuAvoidStrategyGetY(const AvoidStrategyMember & avoidStrategyMember,float & offsetY)711 void SelectOverlayLayoutAlgorithm::NewMenuAvoidStrategyGetY(const AvoidStrategyMember& avoidStrategyMember,
712                                                             float& offsetY)
713 {
714     auto pipeline = PipelineContext::GetCurrentContextSafelyWithCheck();
715     CHECK_NULL_VOID(pipeline);
716     auto safeAreaManager = pipeline->GetSafeAreaManager();
717     CHECK_NULL_VOID(safeAreaManager);
718     auto topArea = safeAreaManager->GetSystemSafeArea().top_.Length();
719     auto upHandle = info_->handleReverse ? info_->secondHandle : info_->firstHandle;
720     // 顶部避让
721     offsetY = upHandle.isShow ? (avoidStrategyMember.selectAreaTop - avoidStrategyMember.menuSpacing -
722                   avoidStrategyMember.menuHeight) : (avoidStrategyMember.selectAreaTop -
723                   avoidStrategyMember.menuSpacingBetweenText - avoidStrategyMember.menuHeight);
724     if (!upHandle.isShow || LessOrEqual(offsetY, topArea)) {
725         auto selectBottom = avoidStrategyMember.hasKeyboard ? std::min(avoidStrategyMember.selectAndRootRectAreaBottom,
726             (double)avoidStrategyMember.keyboardInsertStart) : avoidStrategyMember.selectAndRootRectAreaBottom;
727         auto offsetBetweenSelectArea =
728             std::clamp((double)(avoidStrategyMember.selectAndRootRectAreaTop + selectBottom -
729                 avoidStrategyMember.menuHeight) / 2.0f, (double)topArea, avoidStrategyMember.bottomLimitOffsetY);
730         auto offsetYTmp = avoidStrategyMember.downHandleIsReallyShow ?
731                                   (avoidStrategyMember.selectAndRootRectAreaBottom + avoidStrategyMember.menuSpacing) :
732                                   (avoidStrategyMember.selectAndRootRectAreaBottom +
733                                   avoidStrategyMember.menuSpacingBetweenText);
734         if (avoidStrategyMember.downHandleIsReallyShow) {
735             bool isOffsetYInBottom = false;
736             // The upper handle is not visible and not in a single row, or offsetY <= topArea
737             if ((!upHandle.isShow && !info_->isSingleLine) || (LessOrEqual(offsetY, topArea))) {
738                 offsetY = offsetYTmp;
739                 isOffsetYInBottom = true;
740             }
741             if (isOffsetYInBottom && GreatNotEqual(offsetY, avoidStrategyMember.bottomLimitOffsetY)) {
742                 offsetY = offsetBetweenSelectArea;
743             }
744         } else {
745             if (info_->isSingleLine) {
746                 offsetY = LessOrEqual(offsetY, topArea) ?
747                     ((GreatNotEqual(offsetYTmp, avoidStrategyMember.bottomLimitOffsetY)) ?
748                         offsetBetweenSelectArea : offsetYTmp) : offsetY;
749             } else {
750                 offsetY = offsetBetweenSelectArea;
751             }
752         }
753     }
754     if (avoidStrategyMember.hasKeyboard && GreatNotEqual(offsetY, avoidStrategyMember.bottomLimitOffsetY)) {
755         offsetY = avoidStrategyMember.bottomLimitOffsetY;
756     }
757 }
758 
IsReverseLayout(LayoutWrapper * layoutWrapper) const759 bool SelectOverlayLayoutAlgorithm::IsReverseLayout(LayoutWrapper* layoutWrapper) const
760 {
761     CHECK_NULL_RETURN(layoutWrapper, false);
762     auto layoutProperty = layoutWrapper->GetLayoutProperty();
763     CHECK_NULL_RETURN(layoutProperty, false);
764     return layoutProperty->GetNonAutoLayoutDirection() == TextDirection::RTL;
765 }
766 
CheckHandleIsInClipViewPort()767 void SelectOverlayLayoutAlgorithm::CheckHandleIsInClipViewPort()
768 {
769     if (!info_->clipHandleDrawRect || info_->secondHandle.isPaintHandleWithPoints ||
770         info_->handleLevelMode == HandleLevelMode::EMBED) {
771         return;
772     }
773     if (!info_->isSingleHandle) {
774         RectF viewPort;
775         info_->GetCallerNodeAncestorViewPort(viewPort);
776         auto isInRegion = [](const RectF& viewPort, float left, float right, float verticalY) {
777             return LessOrEqual(left, viewPort.Right()) &&
778                    GreatOrEqual(right, viewPort.Left()) &&
779                    GreatOrEqual(verticalY, viewPort.Top() - ROUND_EPSILON) &&
780                    LessOrEqual(verticalY, viewPort.Bottom() + ROUND_EPSILON);
781         };
782         auto& handleOnTop = !info_->handleReverse ? info_->firstHandle : info_->secondHandle;
783         handleOnTop.isShow = isInRegion(
784             viewPort, handleOnTop.paintRect.Left(), handleOnTop.paintRect.Right(), handleOnTop.paintRect.Top());
785         auto& handleOnBottom = !info_->handleReverse ? info_->secondHandle : info_->firstHandle;
786         handleOnBottom.isShow = isInRegion(viewPort, handleOnBottom.paintRect.Left(), handleOnBottom.paintRect.Right(),
787             handleOnBottom.paintRect.Bottom());
788     }
789 }
790 
UpdateMainWindowOffset(LayoutWrapper * layoutWrapper)791 void SelectOverlayLayoutAlgorithm::UpdateMainWindowOffset(LayoutWrapper* layoutWrapper)
792 {
793     CHECK_NULL_VOID(layoutWrapper);
794     auto host = layoutWrapper->GetHostNode();
795     CHECK_NULL_VOID(host);
796     auto pattern = host->GetPattern<SelectOverlayPattern>();
797     CHECK_NULL_VOID(pattern);
798     bool isMenuShowInsubWindow = pattern->GetIsMenuShowInSubWindow();
799     if (!isMenuShowInsubWindow) {
800         mainWindowOffset_ = OffsetF(0.0, 0.0);
801         containerModalOffset_ = OffsetF(0.0, 0.0);
802         return;
803     }
804     auto containerId = pattern->GetContainerId();
805     if (containerId == -1) {
806         mainWindowOffset_ = OffsetF(0.0, 0.0);
807         containerModalOffset_ = OffsetF(0.0, 0.0);
808         TAG_LOGW(AceLogTag::ACE_SELECT_OVERLAY, "UpdateMainWindowOffset containerId is invalid.");
809         return;
810     }
811     auto container = Container::GetContainer(containerId);
812     CHECK_NULL_VOID(container);
813     auto pipelineContext = AceType::DynamicCast<PipelineContext>(container->GetPipelineContext());
814     CHECK_NULL_VOID(pipelineContext);
815     auto selectTheme = pipelineContext->GetTheme<SelectTheme>();
816     CHECK_NULL_VOID(selectTheme);
817     auto isExpandDisplay = selectTheme->GetExpandDisplay();
818     auto displayWindowRect = pipelineContext->GetDisplayWindowRectInfo();
819     if (isExpandDisplay || container->IsFreeMultiWindow()) {
820         mainWindowOffset_ = OffsetF(displayWindowRect.Left(), displayWindowRect.Top());
821     }
822     containerModalOffset_ = info_->containerModalOffset;
823     TAG_LOGI(AceLogTag::ACE_SELECT_OVERLAY,
824         "UpdateMainWindowOffset mainWindowOffset : %{public}s containerModalOffset : %{public}s",
825         mainWindowOffset_.ToString().c_str(), containerModalOffset_.ToString().c_str());
826 }
827 
GetIsMenuShowInSubWindow(LayoutWrapper * layoutWrapper)828 bool SelectOverlayLayoutAlgorithm::GetIsMenuShowInSubWindow(LayoutWrapper* layoutWrapper)
829 {
830     CHECK_NULL_RETURN(layoutWrapper, false);
831     auto host = layoutWrapper->GetHostNode();
832     CHECK_NULL_RETURN(host, false);
833     auto pattern = host->GetPattern<SelectOverlayPattern>();
834     CHECK_NULL_RETURN(pattern, false);
835     return pattern->GetIsMenuShowInSubWindow();
836 }
837 } // namespace OHOS::Ace::NG
838