• 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 <optional>
19 
20 #include "base/geometry/ng/offset_t.h"
21 #include "base/utils/string_utils.h"
22 #include "base/utils/utils.h"
23 #include "core/components/text_overlay/text_overlay_theme.h"
24 #include "core/components_ng/pattern/linear_layout/linear_layout_pattern.h"
25 #include "core/components_ng/pattern/linear_layout/linear_layout_property.h"
26 #include "core/pipeline_ng/pipeline_context.h"
27 
28 namespace OHOS::Ace::NG {
29 namespace {
30 constexpr Dimension MORE_MENU_INTERVAL = 8.0_vp;
31 }
Layout(LayoutWrapper * layoutWrapper)32 void SelectOverlayLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper)
33 {
34     auto menu = layoutWrapper->GetOrCreateChildByIndex(0);
35     CHECK_NULL_VOID(menu);
36     if (!CheckInShowArea(*info_) || (!info_->firstHandle.isShow && !info_->secondHandle.isShow)) {
37         menu->SetActive(false);
38         return;
39     } else {
40         menu->SetActive(true);
41     }
42 
43     // custom menu need to layout all menu and submenu
44     if (info_->menuInfo.menuBuilder) {
45         for (const auto& child : layoutWrapper->GetAllChildrenWithBuild()) {
46             child->Layout();
47         }
48         return;
49     }
50 
51     auto menuOffset = ComputeSelectMenuPosition(layoutWrapper);
52     menu->GetGeometryNode()->SetMarginFrameOffset(menuOffset);
53     menu->Layout();
54     auto button = layoutWrapper->GetOrCreateChildByIndex(1);
55     CHECK_NULL_VOID(button);
56     auto menuNode = menu->GetHostNode();
57     CHECK_NULL_VOID(menuNode);
58     auto menuContext = menuNode->GetRenderContext();
59     CHECK_NULL_VOID(menuContext);
60     auto offset = OffsetF();
61     if (menuContext->GetOffset()) {
62         offset =
63             OffsetF(menuContext->GetOffset()->GetX().ConvertToPx(), menuContext->GetOffset()->GetY().ConvertToPx());
64     }
65     if (!info_->menuInfo.menuIsShow) {
66         hasExtensionMenu_ = false;
67         return;
68     }
69     hasExtensionMenu_ = true;
70     button->GetGeometryNode()->SetMarginFrameOffset(menuOffset);
71     button->Layout();
72     auto extensionMenuOffset = ComputeExtensionMenuPosition(layoutWrapper, offset);
73 
74     auto extensionMenu = layoutWrapper->GetOrCreateChildByIndex(2);
75     CHECK_NULL_VOID(extensionMenu);
76     extensionMenu->GetGeometryNode()->SetMarginFrameOffset(extensionMenuOffset);
77     extensionMenu->Layout();
78 }
79 
CheckInShowArea(const SelectOverlayInfo & info)80 bool SelectOverlayLayoutAlgorithm::CheckInShowArea(const SelectOverlayInfo& info)
81 {
82     if (info.useFullScreen) {
83         return true;
84     }
85     if (info.isSingleHandle) {
86         return info.firstHandle.paintRect.IsWrappedBy(info.showArea);
87     }
88     return info.firstHandle.paintRect.IsWrappedBy(info.showArea) &&
89            info.secondHandle.paintRect.IsWrappedBy(info.showArea);
90 }
91 
ComputeSelectMenuPosition(LayoutWrapper * layoutWrapper)92 OffsetF SelectOverlayLayoutAlgorithm::ComputeSelectMenuPosition(LayoutWrapper* layoutWrapper)
93 {
94     auto menuItem = layoutWrapper->GetOrCreateChildByIndex(0);
95     CHECK_NULL_RETURN(menuItem, OffsetF());
96     auto pipeline = PipelineContext::GetCurrentContext();
97     CHECK_NULL_RETURN(pipeline, OffsetF());
98     auto theme = pipeline->GetTheme<TextOverlayTheme>();
99     CHECK_NULL_RETURN(theme, OffsetF());
100     OffsetF menuPosition;
101     bool isExtension = false;
102 
103     // Calculate the spacing with text and handle, menu is fixed up the handle and text.
104     double menuSpacingBetweenText = theme->GetMenuSpacingWithText().ConvertToPx();
105     double menuSpacingBetweenHandle = theme->GetHandleDiameter().ConvertToPx();
106 
107     auto width = menuItem->GetGeometryNode()->GetMarginFrameSize().Width();
108     auto height = menuItem->GetGeometryNode()->GetMarginFrameSize().Height();
109 
110     // When the extended menu is displayed, the default menu becomes circular, but the position of the circle is aligned
111     // with the end of the original menu.
112     if (GreatNotEqual(width, height)) {
113         menuWidth_ = width;
114         menuHeight_ = height;
115     } else {
116         isExtension = true;
117     }
118     auto menuWidth = menuWidth_.value_or(width);
119     auto menuHeight = menuHeight_.value_or(height);
120 
121     // paint rect is in global position, need to convert to local position
122     auto offset = layoutWrapper->GetGeometryNode()->GetFrameOffset();
123     const auto firstHandleRect = info_->firstHandle.paintRect - offset;
124     const auto secondHandleRect = info_->secondHandle.paintRect - offset;
125 
126     auto singleHandle = firstHandleRect;
127     if (!info_->firstHandle.isShow) {
128         singleHandle = secondHandleRect;
129     }
130     if (IsTextAreaSelectAll()) {
131         singleHandle = RectF(info_->menuInfo.menuOffset.value().GetX(), info_->menuInfo.menuOffset.value().GetY(),
132             singleHandle.Width(), singleHandle.Height());
133     }
134 
135     if (info_->isSingleHandle) {
136         auto menuSpacing = static_cast<float>(menuSpacingBetweenText);
137         menuPosition = OffsetF((singleHandle.Left() + singleHandle.Right() - menuWidth) / 2.0f,
138             static_cast<float>(singleHandle.Top() - menuSpacing - menuHeight));
139     } else {
140         auto menuSpacing = static_cast<float>(menuSpacingBetweenText + menuSpacingBetweenHandle);
141         menuPosition = OffsetF((firstHandleRect.Left() + secondHandleRect.Left() - menuWidth) / 2.0f,
142             static_cast<float>(firstHandleRect.Top() - menuSpacing - menuHeight));
143     }
144 
145     auto overlayWidth = layoutWrapper->GetGeometryNode()->GetFrameSize().Width();
146     RectF viewPort = layoutWrapper->GetGeometryNode()->GetFrameRect() - offset;
147     auto frameNode = info_->callerFrameNode.Upgrade();
148     if (frameNode) {
149         auto viewPortOption = frameNode->GetViewPort();
150         if (viewPortOption.has_value()) {
151             viewPort = viewPortOption.value();
152         }
153     }
154     LOGD("select_overlay viewPort Rect: %{public}s", viewPort.ToString().c_str());
155 
156     // Adjust position of overlay.
157     if (LessOrEqual(menuPosition.GetX(), viewPort.GetX())) {
158         menuPosition.SetX(theme->GetDefaultMenuPositionX());
159     } else if (GreatOrEqual(menuPosition.GetX() + menuWidth, viewPort.GetX() + viewPort.Width())) {
160         menuPosition.SetX(overlayWidth - menuWidth - theme->GetDefaultMenuPositionX());
161     }
162     if (LessNotEqual(menuPosition.GetY(), menuHeight)) {
163         if (IsTextAreaSelectAll()) {
164             menuPosition.SetY(singleHandle.Top());
165         } else {
166             menuPosition.SetY(
167                 static_cast<float>(singleHandle.Bottom() + menuSpacingBetweenText + menuSpacingBetweenHandle));
168         }
169     }
170     if (LessNotEqual(menuPosition.GetY(), viewPort.GetY() - menuSpacingBetweenText - menuHeight) ||
171         LessNotEqual(menuPosition.GetY(), menuSpacingBetweenText)) {
172         auto menuOffsetY = viewPort.GetY() - menuSpacingBetweenText - menuHeight;
173         if (menuOffsetY > menuSpacingBetweenText) {
174             menuPosition.SetY(menuOffsetY);
175         } else {
176             menuPosition.SetY(menuSpacingBetweenText);
177         }
178     } else if (GreatOrEqual(menuPosition.GetY(), viewPort.GetY() + viewPort.Height() + menuSpacingBetweenText)) {
179         menuPosition.SetY(viewPort.GetY() + viewPort.Height() + menuSpacingBetweenText);
180     }
181     LOGD("select_overlay menuPosition: %{public}s", menuPosition.ToString().c_str());
182     defaultMenuEndOffset_ = menuPosition + OffsetF(menuWidth, 0.0f);
183     if (isExtension) {
184         return defaultMenuEndOffset_ - OffsetF(width, 0);
185     }
186     return menuPosition;
187 }
188 
ComputeExtensionMenuPosition(LayoutWrapper * layoutWrapper,const OffsetF & offset)189 OffsetF SelectOverlayLayoutAlgorithm::ComputeExtensionMenuPosition(LayoutWrapper* layoutWrapper, const OffsetF& offset)
190 {
191     auto extensionItem = layoutWrapper->GetOrCreateChildByIndex(2);
192     CHECK_NULL_RETURN(extensionItem, OffsetF());
193     auto extensionLayoutConstraint = extensionItem->GetLayoutProperty()->GetLayoutConstraint();
194     auto extensionLayoutConstraintMaxSize = extensionLayoutConstraint->maxSize;
195     auto extensionWidth = extensionItem->GetGeometryNode()->GetMarginFrameSize().Width();
196     auto extensionHeight = extensionItem->GetGeometryNode()->GetMarginFrameSize().Height();
197     auto menuItem = layoutWrapper->GetOrCreateChildByIndex(0);
198     CHECK_NULL_RETURN(menuItem, OffsetF());
199     auto menuHeight = menuItem->GetGeometryNode()->GetMarginFrameSize().Height();
200     auto extensionOffset =
201         defaultMenuEndOffset_ - OffsetF(extensionWidth, -menuHeight - MORE_MENU_INTERVAL.ConvertToPx());
202     if (extensionOffset.GetY() + extensionHeight > extensionLayoutConstraintMaxSize.Height()) {
203         extensionOffset =
204             defaultMenuEndOffset_ - OffsetF(extensionWidth, extensionHeight + MORE_MENU_INTERVAL.ConvertToPx());
205     }
206     return extensionOffset;
207 }
208 
IsTextAreaSelectAll()209 bool SelectOverlayLayoutAlgorithm::IsTextAreaSelectAll()
210 {
211     return info_->menuInfo.menuOffset.has_value() && (!info_->firstHandle.isShow || !info_->secondHandle.isShow);
212 }
213 
214 } // namespace OHOS::Ace::NG