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