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