• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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