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