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