1 /*
2 * Copyright (c) 2023 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/manager/select_overlay/select_overlay_client.h"
17
18 #include "base/memory/ace_type.h"
19 #include "base/utils/utils.h"
20 #include "core/components_ng/base/frame_node.h"
21 #include "core/components_ng/pattern/scrollable/nestable_scroll_container.h"
22 #include "core/components_ng/pattern/scrollable/scrollable_pattern.h"
23 #include "core/components_v2/inspector/inspector_constants.h"
24 #include "core/pipeline_ng/pipeline_context.h"
25
26 namespace OHOS::Ace::NG {
InitSelectOverlay()27 void SelectOverlayClient::InitSelectOverlay()
28 {
29 selectOverlayInfo_.menuCallback.onCopy = [weak = WeakClaim(this)]() {
30 auto client = weak.Upgrade();
31 CHECK_NULL_VOID(client);
32 client->OnSelectOverlayMenuClicked(SelectOverlayMenuId::COPY);
33 };
34 selectOverlayInfo_.menuCallback.onCut = [weak = WeakClaim(this)]() {
35 auto client = weak.Upgrade();
36 CHECK_NULL_VOID(client);
37 client->OnSelectOverlayMenuClicked(SelectOverlayMenuId::CUT);
38 };
39 selectOverlayInfo_.menuCallback.onSelectAll = [weak = WeakClaim(this)]() {
40 auto client = weak.Upgrade();
41 CHECK_NULL_VOID(client);
42 client->OnSelectOverlayMenuClicked(SelectOverlayMenuId::SELECT_ALL);
43 };
44 selectOverlayInfo_.menuCallback.onPaste = [weak = WeakClaim(this)]() {
45 auto client = weak.Upgrade();
46 CHECK_NULL_VOID(client);
47 client->OnSelectOverlayMenuClicked(SelectOverlayMenuId::PASTE);
48 };
49 selectOverlayInfo_.menuCallback.onCameraInput = [weak = WeakClaim(this)]() {
50 auto client = weak.Upgrade();
51 CHECK_NULL_VOID(client);
52 client->OnSelectOverlayMenuClicked(SelectOverlayMenuId::CAMERA_INPUT);
53 };
54 selectOverlayInfo_.onHandleMoveStart = [weak = WeakClaim(this)](bool isFirst) {
55 auto client = weak.Upgrade();
56 CHECK_NULL_VOID(client);
57 client->OnHandleMoveStart(isFirst);
58 };
59 selectOverlayInfo_.onHandleMove = [weak = WeakClaim(this)](const RectF& rect, bool isFirst) {
60 auto client = weak.Upgrade();
61 CHECK_NULL_VOID(client);
62 client->OnHandleMove(rect, isFirst);
63 };
64 selectOverlayInfo_.onHandleMoveDone = [weak = WeakClaim(this)](const RectF& rect, bool isFirst) {
65 auto client = weak.Upgrade();
66 CHECK_NULL_VOID(client);
67 client->OnHandleMoveDone(rect, isFirst);
68 };
69 selectOverlayInfo_.onClose = [weak = WeakClaim(this)](bool closedByGlobalEvent) {
70 auto client = weak.Upgrade();
71 CHECK_NULL_VOID(client);
72 client->OnHandleClosed(closedByGlobalEvent);
73 };
74 selectOverlayInfo_.callerFrameNode = GetClientHost();
75 selectOverlayInfo_.firstHandle.isShow = false;
76 selectOverlayInfo_.secondHandle.isShow = false;
77 selectOverlayInfo_.hitTestMode = HitTestMode::HTMDEFAULT;
78 }
79
RequestOpenSelectOverlay(ClientOverlayInfo & showOverlayInfo)80 void SelectOverlayClient::RequestOpenSelectOverlay(ClientOverlayInfo& showOverlayInfo)
81 {
82 LOGI("first handle %{public}d, second handle %{public}d",
83 showOverlayInfo.firstHandleInfo.has_value(), showOverlayInfo.secondHandleInfo.has_value());
84 if (SelectOverlayIsOn()) {
85 UpdateShowingSelectOverlay(showOverlayInfo);
86 } else {
87 CreateSelectOverlay(showOverlayInfo);
88 }
89 }
90
CreateSelectOverlay(const ClientOverlayInfo & clientOverlayInfo)91 void SelectOverlayClient::CreateSelectOverlay(const ClientOverlayInfo& clientOverlayInfo)
92 {
93 auto pipeline = PipelineContext::GetCurrentContext();
94 CHECK_NULL_VOID(pipeline);
95 auto overlayInfo = GetSelectOverlayInfo(clientOverlayInfo);
96 CHECK_NULL_VOID(overlayInfo);
97 originIsMenuShow_ = overlayInfo->menuInfo.menuIsShow;
98 LOGI("first handle %{public}d, second handle %{public}d, select rect %{public}d", overlayInfo->firstHandle.isShow,
99 overlayInfo->secondHandle.isShow, overlayInfo->isSelectRegionVisible);
100 selectOverlayProxy_ = pipeline->GetSelectOverlayManager()->CreateAndShowSelectOverlay(
101 *overlayInfo, WeakClaim(this), clientOverlayInfo.animation);
102 if (!overlayInfo->isUsingMouse) {
103 StartListeningScrollableParent(GetClientHost());
104 }
105 }
106
GetSelectOverlayInfo(const ClientOverlayInfo & clientInfo)107 std::optional<SelectOverlayInfo> SelectOverlayClient::GetSelectOverlayInfo(const ClientOverlayInfo& clientInfo)
108 {
109 auto firstHandleInfo = clientInfo.firstHandleInfo;
110 auto secondHandleInfo = clientInfo.secondHandleInfo;
111 if (firstHandleInfo.has_value()) {
112 firstHandleInfo->isShow = CheckHandleVisible(firstHandleInfo->paintRect);
113 }
114 if (secondHandleInfo.has_value()) {
115 secondHandleInfo->isShow = CheckHandleVisible(secondHandleInfo->paintRect);
116 }
117 SelectOverlayInfo overlayInfo = selectOverlayInfo_;
118 overlayInfo.firstHandle = firstHandleInfo.has_value() ? *firstHandleInfo : overlayInfo.firstHandle;
119 overlayInfo.secondHandle = secondHandleInfo.has_value() ? *secondHandleInfo : overlayInfo.secondHandle;
120 overlayInfo.isSingleHandle = !firstHandleInfo && secondHandleInfo;
121 overlayInfo.isSelectRegionVisible = CheckSelectionRectVisible();
122 overlayInfo.selectArea = clientInfo.selectArea;
123 overlayInfo.isNewAvoid = clientInfo.isNewAvoid;
124 if (!clientInfo.isUpdateMenu) {
125 return overlayInfo;
126 }
127 if (!GetMenuOptionItems().empty()) {
128 overlayInfo.menuOptionItems = GetMenuOptionItems();
129 }
130 if (OnPreShowSelectOverlay(overlayInfo, clientInfo, SelectOverlayIsOn())) {
131 overlayInfo.menuInfo.singleHandleMenuIsShow = overlayInfo.menuInfo.menuIsShow;
132 return overlayInfo;
133 }
134 return std::nullopt;
135 }
136
UpdateShowingSelectOverlay(ClientOverlayInfo & clientInfo)137 void SelectOverlayClient::UpdateShowingSelectOverlay(ClientOverlayInfo& clientInfo)
138 {
139 LOGI("update select overlay");
140 auto isCurrentSingleHandle = IsShowingSingleHandle();
141 auto hasRequestSingleHandle = !clientInfo.firstHandleInfo && clientInfo.secondHandleInfo;
142 if (clientInfo.isShowMouseMenu || (isCurrentSingleHandle ^ hasRequestSingleHandle)) {
143 LOGI("force close and create new select overlay");
144 RequestCloseSelectOverlay(true);
145 clientInfo.isUpdateMenu = true;
146 CreateSelectOverlay(clientInfo);
147 return;
148 }
149 auto selectOverlayInfo = GetSelectOverlayInfo(clientInfo);
150 CHECK_NULL_VOID(selectOverlayInfo);
151 auto proxy = GetSelectOverlayProxy();
152 CHECK_NULL_VOID(proxy);
153 if (clientInfo.isNewAvoid) {
154 proxy->UpdateSelectArea(selectOverlayInfo->selectArea);
155 }
156 if (hasRequestSingleHandle) {
157 if (clientInfo.isUpdateMenu) {
158 proxy->UpdateSelectMenuInfo([newMenuInfo = selectOverlayInfo->menuInfo](SelectMenuInfo& menuInfo) {
159 menuInfo.showPaste = newMenuInfo.showPaste;
160 menuInfo.showCopyAll = newMenuInfo.showCopyAll;
161 });
162 }
163 selectOverlayInfo->secondHandle.needLayout = true;
164 proxy->UpdateSecondSelectHandleInfo(selectOverlayInfo->secondHandle);
165 } else {
166 if (clientInfo.isUpdateMenu) {
167 proxy->UpdateSelectMenuInfo([newMenuInfo = selectOverlayInfo->menuInfo](SelectMenuInfo& menuInfo) {
168 menuInfo.showPaste = newMenuInfo.showPaste;
169 menuInfo.showCopyAll = newMenuInfo.showCopyAll;
170 menuInfo.showCopy = newMenuInfo.showCopy;
171 menuInfo.showCut = newMenuInfo.showCut;
172 });
173 }
174 proxy->UpdateFirstAndSecondHandleInfo(selectOverlayInfo->firstHandle, selectOverlayInfo->secondHandle);
175 }
176 }
177
RequestCloseSelectOverlay(bool animation)178 void SelectOverlayClient::RequestCloseSelectOverlay(bool animation)
179 {
180 StopListeningScrollableParent(GetClientHost());
181 if (selectOverlayProxy_ && !selectOverlayProxy_->IsClosed()) {
182 selectOverlayProxy_->Close(animation);
183 }
184 }
185
SelectOverlayIsOn()186 bool SelectOverlayClient::SelectOverlayIsOn()
187 {
188 auto pipeline = PipelineContext::GetCurrentContext();
189 CHECK_NULL_RETURN(pipeline, false);
190 CHECK_NULL_RETURN(selectOverlayProxy_, false);
191 auto overlayId = selectOverlayProxy_->GetSelectOverlayId();
192 return pipeline->GetSelectOverlayManager()->HasSelectOverlay(overlayId);
193 }
194
UpdateSelectInfo(const std::string & selectInfo)195 void SelectOverlayClient::UpdateSelectInfo(const std::string& selectInfo)
196 {
197 auto selectOverlay = GetSelectOverlayProxy();
198 CHECK_NULL_VOID(selectOverlay);
199 selectOverlay->SetSelectInfo(selectInfo);
200 }
201
UpdateSelectMenuInfo(std::function<void (SelectMenuInfo &)> updateAction)202 void SelectOverlayClient::UpdateSelectMenuInfo(std::function<void(SelectMenuInfo&)> updateAction)
203 {
204 auto selectOverlay = GetSelectOverlayProxy();
205 CHECK_NULL_VOID(selectOverlay);
206 selectOverlay->UpdateSelectMenuInfo(updateAction);
207 }
208
UpdateSelectMenuVisibility(bool isVisible)209 void SelectOverlayClient::UpdateSelectMenuVisibility(bool isVisible)
210 {
211 auto selectOverlay = GetSelectOverlayProxy();
212 CHECK_NULL_VOID(selectOverlay);
213 selectOverlay->ShowOrHiddenMenu(!isVisible);
214 }
215
StartListeningScrollableParent(const RefPtr<FrameNode> & host)216 void SelectOverlayClient::StartListeningScrollableParent(const RefPtr<FrameNode>& host)
217 {
218 if (!scrollableParentInfo_.hasParent) {
219 LOGI("has no scrollable parent");
220 return;
221 }
222 CHECK_NULL_VOID(host);
223 auto context = host->GetContext();
224 CHECK_NULL_VOID(context);
225 if (scrollableParentInfo_.parentIds.empty()) {
226 auto parent = host->GetParent();
227 while (parent && parent->GetTag() != V2::PAGE_ETS_TAG) {
228 auto parentNode = AceType::DynamicCast<FrameNode>(parent);
229 if (parentNode) {
230 auto pattern = parentNode->GetPattern<ScrollablePattern>();
231 if (pattern) {
232 scrollableParentInfo_.parentIds.emplace_back(parentNode->GetId());
233 RegisterParentScrollCallback(parentNode->GetId(), host->GetId());
234 }
235 }
236 parent = parent->GetParent();
237 }
238 scrollableParentInfo_.hasParent = !scrollableParentInfo_.parentIds.empty();
239 LOGI("find scrollable parent %{public}d", scrollableParentInfo_.hasParent);
240 } else {
241 for (const auto& scrollId : scrollableParentInfo_.parentIds) {
242 RegisterParentScrollCallback(scrollId, host->GetId());
243 }
244 }
245 }
246
RegisterParentScrollCallback(int32_t parentId,int32_t callbackId)247 void SelectOverlayClient::RegisterParentScrollCallback(int32_t parentId, int32_t callbackId)
248 {
249 auto host = GetClientHost();
250 CHECK_NULL_VOID(host);
251 auto context = host->GetContext();
252 CHECK_NULL_VOID(context);
253 auto scrollCallback = [weak = WeakClaim(this)](Axis axis, bool offset, int32_t source) {
254 auto client = weak.Upgrade();
255 CHECK_NULL_VOID(client);
256 if (source == SCROLL_FROM_START) {
257 client->OnParentScrollStartOrEnd(false);
258 } else if (source == -1) {
259 client->OnParentScrollStartOrEnd(true);
260 } else {
261 client->OnParentScrollCallback(axis, offset);
262 }
263 };
264 context->GetSelectOverlayManager()->RegisterScrollCallback(parentId, callbackId, scrollCallback);
265 }
266
StopListeningScrollableParent(const RefPtr<FrameNode> & host)267 void SelectOverlayClient::StopListeningScrollableParent(const RefPtr<FrameNode>& host)
268 {
269 CHECK_NULL_VOID(host);
270 auto context = host->GetContext();
271 CHECK_NULL_VOID(context);
272 context->GetSelectOverlayManager()->RemoveScrollCallback(host->GetId());
273 }
274
OnParentScrollStartOrEnd(bool isEnd,bool noAnimation)275 void SelectOverlayClient::OnParentScrollStartOrEnd(bool isEnd, bool noAnimation)
276 {
277 if (!SelectOverlayIsOn()) {
278 return;
279 }
280 auto proxy = GetSelectOverlayProxy();
281 CHECK_NULL_VOID(proxy);
282 if (!isEnd) {
283 proxy->ShowOrHiddenMenu(true, noAnimation);
284 return;
285 }
286 if (proxy->IsSingleHandle() && !proxy->IsSingleHandleMenuShow()) {
287 UpdateSelectMenuInfo([](SelectMenuInfo& menuInfo) { menuInfo.menuIsShow = false; });
288 } else {
289 auto info = proxy->GetSelectOverlayMangerInfo();
290 if (!info.isNewAvoid) {
291 proxy->ShowOrHiddenMenu(!originIsMenuShow_);
292 }
293 }
294 }
295
GetVisibleContentRect(WeakPtr<FrameNode> parent,RectF visibleRect)296 RectF SelectOverlayClient::GetVisibleContentRect(WeakPtr<FrameNode> parent, RectF visibleRect)
297 {
298 auto parentNode = parent.Upgrade();
299 CHECK_NULL_RETURN(parentNode, visibleRect);
300 if (parentNode->GetTag() == V2::PAGE_ETS_TAG) {
301 return visibleRect;
302 }
303 auto intersectRect = visibleRect;
304 auto scrollablePattern = AceType::DynamicCast<NestableScrollContainer>(parentNode->GetPattern());
305 auto geometryNode = parentNode->GetGeometryNode();
306 if (scrollablePattern && geometryNode) {
307 auto parentViewPort = RectF(parentNode->GetTransformRelativeOffset(), geometryNode->GetFrameSize());
308 if (parentViewPort.IsIntersectWith(visibleRect)) {
309 intersectRect = parentViewPort.IntersectRectT(visibleRect);
310 } else {
311 return RectF(0, 0, 0, 0);
312 }
313 }
314 parentNode = parentNode->GetAncestorNodeOfFrame();
315 return GetVisibleContentRect(parentNode, intersectRect);
316 }
317 } // namespace OHOS::Ace::NG