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