• 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     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