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