1 /*
2 * Copyright (c) 2022 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_manager.h"
17
18 #include "core/pipeline_ng/pipeline_context.h"
19 #include "core/event/event_info_convertor.h"
20
21 namespace OHOS::Ace::NG {
CreateAndShowSelectOverlay(const SelectOverlayInfo & info,const WeakPtr<SelectionHost> & host,bool animation)22 RefPtr<SelectOverlayProxy> SelectOverlayManager::CreateAndShowSelectOverlay(
23 const SelectOverlayInfo& info, const WeakPtr<SelectionHost>& host, bool animation)
24 {
25 host_ = host;
26 auto current = selectOverlayItem_.Upgrade();
27 if (selectedByMouseInfo_.selectedNode.Upgrade() && info.callerFrameNode.Upgrade() &&
28 selectedByMouseInfo_.selectedNode.Upgrade() != info.callerFrameNode.Upgrade()) {
29 if (selectedByMouseInfo_.onResetSelection) {
30 selectedByMouseInfo_.onResetSelection();
31 }
32 selectedByMouseInfo_.clear();
33 if (selectContentManager_) {
34 selectContentManager_->ResetSelectionRect();
35 }
36 }
37 if (current) {
38 if (info.isUsingMouse && IsSameSelectOverlayInfo(info)) {
39 auto proxy = MakeRefPtr<SelectOverlayProxy>(current->GetId());
40 return proxy;
41 }
42 auto frameNode = GetCallerHost();
43 CHECK_NULL_RETURN(frameNode, nullptr);
44 if (frameNode->GetTag() != V2::RICH_EDITOR_ETS_TAG) {
45 NotifyOverlayClosed(true);
46 }
47 DestroySelectOverlay(current->GetId());
48 }
49 if (selectContentManager_) {
50 selectContentManager_->CloseCurrent(false, CloseReason::CLOSE_REASON_HOLD_BY_OTHER);
51 }
52 selectOverlayInfo_ = info;
53 SelectOverlayInfo selectInfo = info;
54 if (selectInfo.callerFrameNode.Invalid()) {
55 selectInfo.callerFrameNode = GetCallerHost();
56 }
57 auto infoPtr = std::make_shared<SelectOverlayInfo>(selectInfo);
58 auto selectOverlayNode = SelectOverlayNode::CreateSelectOverlayNode(infoPtr);
59 selectOverlayItem_ = selectOverlayNode;
60
61 auto taskExecutor = Container::CurrentTaskExecutor();
62 CHECK_NULL_RETURN(taskExecutor, nullptr);
63 taskExecutor->PostTask(
64 [weakRoot = rootNodeWeak_, overlayNode = selectOverlayNode, animation,
65 isUsingMouse = infoPtr->isUsingMouse, weak = WeakClaim(this), weakCaller = infoPtr->callerFrameNode] {
66 auto selectOverlayManager = weak.Upgrade();
67 CHECK_NULL_VOID(selectOverlayManager);
68 CHECK_NULL_VOID(overlayNode);
69 if (overlayNode != selectOverlayManager->GetSelectOverlayItem()) {
70 return;
71 }
72 auto rootNode = weakRoot.Upgrade();
73 auto container = Container::Current();
74 if (container && container->IsSceneBoardWindow()) {
75 auto root = selectOverlayManager->FindWindowScene(weakCaller.Upgrade());
76 rootNode = DynamicCast<FrameNode>(root);
77 }
78 CHECK_NULL_VOID(rootNode);
79 // get keyboard index to put selet_overlay before keyboard node
80 int32_t slot = DEFAULT_NODE_SLOT;
81 int32_t index = 0;
82 for (const auto& it : rootNode->GetChildren()) {
83 if (it->GetTag() == V2::KEYBOARD_ETS_TAG) {
84 slot = index;
85 break;
86 }
87 index++;
88 }
89
90 overlayNode->MountToParent(rootNode, slot);
91 rootNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
92 if (!isUsingMouse) {
93 auto node = DynamicCast<SelectOverlayNode>(overlayNode);
94 CHECK_NULL_VOID(node);
95 node->ShowSelectOverlay(animation);
96 }
97 },
98 TaskExecutor::TaskType::UI, "ArkUISelectOverlayShow");
99
100 auto proxy = MakeRefPtr<SelectOverlayProxy>(selectOverlayNode->GetId());
101 return proxy;
102 }
103
104 // This function will be used in SceneBoard Thread only.
105 // if need to show the select-overlay component,
106 // it expects to receive the target component bound by the select-overlay component to find the windowScene component.
107 // if need to hide the select-overlay component,
108 // it expects to receive the the select-overlay component to return the parent component.
109 // And the parent component will be the windowScene component exactly.
FindWindowScene(RefPtr<FrameNode> targetNode)110 RefPtr<UINode> SelectOverlayManager::FindWindowScene(RefPtr<FrameNode> targetNode)
111 {
112 auto container = Container::Current();
113 if (!container || !container->IsSceneBoardWindow()) {
114 return rootNodeWeak_.Upgrade();
115 }
116 CHECK_NULL_RETURN(targetNode, nullptr);
117 auto parent = targetNode->GetParent();
118 while (parent && parent->GetTag() != V2::WINDOW_SCENE_ETS_TAG) {
119 parent = parent->GetParent();
120 }
121 CHECK_NULL_RETURN(parent, nullptr);
122 return parent;
123 }
124
DestroySelectOverlay(const RefPtr<SelectOverlayProxy> & proxy,bool animation)125 void SelectOverlayManager::DestroySelectOverlay(const RefPtr<SelectOverlayProxy>& proxy, bool animation)
126 {
127 auto id = proxy->GetSelectOverlayId();
128 DestroySelectOverlay(id, animation);
129 }
130
DestroySelectOverlay(int32_t overlayId,bool animation)131 void SelectOverlayManager::DestroySelectOverlay(int32_t overlayId, bool animation)
132 {
133 auto current = selectOverlayItem_.Upgrade();
134 if (current && (current->GetId() == overlayId)) {
135 DestroyHelper(current, animation);
136 }
137 }
138
DestroySelectOverlay(bool animation)139 bool SelectOverlayManager::DestroySelectOverlay(bool animation)
140 {
141 auto current = selectOverlayItem_.Upgrade();
142 if (current) {
143 DestroyHelper(current, animation);
144 return true;
145 }
146 return false;
147 }
148
ResetSelectionAndDestroySelectOverlay(bool isBackPressed,bool animation)149 bool SelectOverlayManager::ResetSelectionAndDestroySelectOverlay(bool isBackPressed, bool animation)
150 {
151 NotifyOverlayClosed(true);
152 auto isDestroyed = DestroySelectOverlay(animation);
153 CHECK_NULL_RETURN(selectContentManager_, isDestroyed);
154 auto isStopBackPress = selectContentManager_->IsStopBackPress();
155 auto isClosed = selectContentManager_->CloseCurrent(
156 animation, isBackPressed ? CloseReason::CLOSE_REASON_BACK_PRESSED : CloseReason::CLOSE_REASON_NORMAL);
157 auto closeFlag = isDestroyed || (isClosed && isStopBackPress);
158 TAG_LOGI(AceLogTag::ACE_SELECT_OVERLAY, "isDestroyed:%{public}d,isClosed:%{public}d", isDestroyed, isClosed);
159 return closeFlag;
160 }
161
DestroyHelper(const RefPtr<FrameNode> & overlay,bool animation)162 void SelectOverlayManager::DestroyHelper(const RefPtr<FrameNode>& overlay, bool animation)
163 {
164 auto rootNode = rootNodeWeak_.Upgrade();
165 CHECK_NULL_VOID(rootNode);
166 if (animation && !selectOverlayInfo_.isUsingMouse) {
167 selectOverlayItem_.Reset();
168 host_.Reset();
169 touchDownPoints_.clear();
170 selectOverlayInfo_.callerFrameNode.Reset();
171 auto node = DynamicCast<SelectOverlayNode>(overlay);
172 node->HideSelectOverlay([overlayWeak = WeakClaim(RawPtr(overlay)), managerWeak = WeakClaim(this)]() {
173 auto manager = managerWeak.Upgrade();
174 CHECK_NULL_VOID(manager);
175 auto overlay = overlayWeak.Upgrade();
176 CHECK_NULL_VOID(overlay);
177 manager->Destroy(overlay);
178 });
179 } else {
180 Destroy(overlay);
181 selectOverlayItem_.Reset();
182 host_.Reset();
183 touchDownPoints_.clear();
184 selectOverlayInfo_.callerFrameNode.Reset();
185 }
186 }
187
Destroy(const RefPtr<FrameNode> & overlay)188 void SelectOverlayManager::Destroy(const RefPtr<FrameNode>& overlay)
189 {
190 auto rootNode = overlay->GetParent();
191 CHECK_NULL_VOID(rootNode);
192 rootNode->RemoveChild(overlay);
193 rootNode->MarkNeedSyncRenderTree();
194 rootNode->RebuildRenderContextTree();
195 }
196
HasSelectOverlay(int32_t overlayId)197 bool SelectOverlayManager::HasSelectOverlay(int32_t overlayId)
198 {
199 auto current = selectOverlayItem_.Upgrade();
200 CHECK_NULL_RETURN(current, false);
201 return current->GetId() == overlayId;
202 }
203
IsInSelectedOrSelectOverlayArea(const PointF & point)204 bool SelectOverlayManager::IsInSelectedOrSelectOverlayArea(const PointF& point)
205 {
206 auto host = host_.Upgrade();
207 if (host && host->IsTouchTestPointInArea(Offset { point.GetX(), point.GetY() }, IsTouchInCallerArea(point))) {
208 return true;
209 }
210 auto current = selectOverlayItem_.Upgrade();
211 CHECK_NULL_RETURN(current, false);
212 auto selectOverlayNode = DynamicCast<SelectOverlayNode>(current);
213 if (selectOverlayNode) {
214 return selectOverlayNode->IsInSelectedOrSelectOverlayArea(point);
215 }
216 // get the menu rect not the out wrapper
217 const auto& children = current->GetChildren();
218 for (const auto& it : children) {
219 auto child = DynamicCast<FrameNode>(it);
220 if (child == nullptr) {
221 continue;
222 }
223 auto frameRect = child->GetGeometryNode()->GetFrameRect();
224 if (frameRect.IsInRegion(point)) {
225 return true;
226 }
227 }
228 return false;
229 }
230
GetSelectOverlayNode(int32_t overlayId)231 RefPtr<SelectOverlayNode> SelectOverlayManager::GetSelectOverlayNode(int32_t overlayId)
232 {
233 auto current = selectOverlayItem_.Upgrade();
234 if (current && (current->GetId() == overlayId)) {
235 return DynamicCast<SelectOverlayNode>(current);
236 }
237 if (selectContentManager_) {
238 return selectContentManager_->GetSelectOverlayNode();
239 }
240 return nullptr;
241 }
242
IsSameSelectOverlayInfo(const SelectOverlayInfo & info)243 bool SelectOverlayManager::IsSameSelectOverlayInfo(const SelectOverlayInfo& info)
244 {
245 if (selectOverlayInfo_.menuInfo.IsIconChanged(info.menuInfo)) {
246 return false;
247 }
248 if (selectOverlayInfo_.isUsingMouse != info.isUsingMouse) {
249 return false;
250 }
251 if (selectOverlayInfo_.rightClickOffset != info.rightClickOffset) {
252 return false;
253 }
254 return true;
255 }
256
HandleGlobalEvent(const TouchEvent & touchPoint,const NG::OffsetF & rootOffset,bool isMousePressAtSelectedNode)257 void SelectOverlayManager::HandleGlobalEvent(
258 const TouchEvent& touchPoint, const NG::OffsetF& rootOffset, bool isMousePressAtSelectedNode)
259 {
260 if (selectContentManager_) {
261 selectContentManager_->HandleGlobalEvent(touchPoint, rootOffset);
262 }
263 ResetSelection(touchPoint, isMousePressAtSelectedNode);
264 CHECK_NULL_VOID(!selectOverlayItem_.Invalid());
265 NG::PointF point { touchPoint.x - rootOffset.GetX(), touchPoint.y - rootOffset.GetY() };
266 // handle global touch event.
267 if (PreProcessTouchEvent(point, touchPoint)) {
268 return;
269 }
270 bool acceptTouchUp = !touchDownPoints_.empty();
271 if (touchPoint.type == TouchType::UP && touchPoint.sourceType == SourceType::TOUCH && acceptTouchUp) {
272 auto lastTouchDownPoint = touchDownPoints_.back();
273 if (lastTouchDownPoint.id != touchPoint.id) {
274 return;
275 }
276 touchDownPoints_.pop_back();
277 point.SetX(lastTouchDownPoint.x - rootOffset.GetX());
278 point.SetY(lastTouchDownPoint.y - rootOffset.GetY());
279 }
280
281 // handle global mouse event.
282 if ((touchPoint.type != TouchType::DOWN || touchPoint.sourceType != SourceType::MOUSE) && !acceptTouchUp) {
283 return;
284 }
285 if (EventInfoConvertor::MatchCompatibleCondition() &&
286 (touchPoint.type == TouchType::DOWN && touchPoint.sourceType == SourceType::MOUSE) && !acceptTouchUp) {
287 return;
288 }
289
290 if (!IsInSelectedOrSelectOverlayArea(point)) {
291 NotifyOverlayClosed(true);
292 DestroySelectOverlay();
293 }
294 }
295
PreProcessTouchEvent(const NG::PointF & point,const TouchEvent & touchPoint)296 bool SelectOverlayManager::PreProcessTouchEvent(const NG::PointF& point, const TouchEvent& touchPoint)
297 {
298 if (touchPoint.type == TouchType::DOWN && touchPoint.sourceType == SourceType::TOUCH) {
299 if (touchDownPoints_.empty() && !IsTouchInCallerArea(point) && !IsInSelectedOrSelectOverlayArea(point)) {
300 touchDownPoints_.emplace_back(touchPoint);
301 }
302 return true;
303 }
304 if (touchPoint.type == TouchType::MOVE && touchPoint.sourceType == SourceType::TOUCH) {
305 if (touchDownPoints_.empty() || touchDownPoints_.back().id != touchPoint.id) {
306 return true;
307 }
308 auto deltaOffset = touchPoint.GetOffset() - touchDownPoints_.back().GetOffset();
309 auto deltaDistance = deltaOffset.GetDistance();
310 auto context = PipelineBase::GetCurrentContext();
311 auto thresholdDistance = context ? context->NormalizeToPx(Dimension(5, DimensionUnit::VP)) : 5;
312 if (deltaDistance > thresholdDistance) {
313 touchDownPoints_.clear();
314 }
315 return true;
316 }
317 return false;
318 }
319
ResetSelection(const TouchEvent & touchPoint,bool isMousePressAtSelectedNode)320 void SelectOverlayManager::ResetSelection(const TouchEvent& touchPoint, bool isMousePressAtSelectedNode)
321 {
322 if (touchPoint.type == TouchType::DOWN && !isMousePressAtSelectedNode && !selectOverlayItem_.Upgrade()) {
323 CHECK_NULL_VOID(selectedByMouseInfo_.selectedNode.Upgrade());
324 if (selectedByMouseInfo_.onResetSelection) {
325 selectedByMouseInfo_.onResetSelection();
326 }
327 selectedByMouseInfo_.clear();
328 }
329 }
330
IsTouchInCallerArea(const std::optional<NG::PointF> & point) const331 bool SelectOverlayManager::IsTouchInCallerArea(const std::optional<NG::PointF>& point) const
332 {
333 if (point.has_value() && selectOverlayInfo_.checkIsTouchInHostArea &&
334 selectOverlayInfo_.checkIsTouchInHostArea(point.value())) {
335 return true;
336 }
337 if (touchTestResults_.empty()) {
338 return false;
339 }
340 auto frameNode = GetCallerHost();
341 CHECK_NULL_RETURN(frameNode, false);
342 auto id = std::to_string(frameNode->GetId());
343 for (auto testId : touchTestResults_) {
344 if (testId == id) {
345 return true;
346 }
347 }
348 return false;
349 }
350
GetCallerHost() const351 RefPtr<FrameNode> SelectOverlayManager::GetCallerHost() const
352 {
353 auto host = host_.Upgrade();
354 CHECK_NULL_RETURN(host, nullptr);
355 auto pattern = DynamicCast<Pattern>(host);
356 CHECK_NULL_RETURN(pattern, nullptr);
357 return pattern->GetHost();
358 }
359
NotifyOverlayClosed(bool closedByGlobalEvent)360 void SelectOverlayManager::NotifyOverlayClosed(bool closedByGlobalEvent)
361 {
362 auto current = selectOverlayItem_.Upgrade();
363 if (current) {
364 auto selectOverlayNode = DynamicCast<SelectOverlayNode>(current);
365 CHECK_NULL_VOID(selectOverlayNode);
366 selectOverlayNode->SetClosedByGlobalEvent(closedByGlobalEvent);
367 }
368 }
369
MarkDirty(PropertyChangeFlag flag)370 void SelectOverlayManager::MarkDirty(PropertyChangeFlag flag)
371 {
372 auto selectOverlayItem = selectOverlayItem_.Upgrade();
373 if (selectOverlayItem) {
374 selectOverlayItem->MarkDirtyNode(flag);
375 }
376 }
377
NotifyOnScrollCallback(int32_t id,Axis axis,float offset,int32_t source)378 void SelectOverlayManager::NotifyOnScrollCallback(int32_t id, Axis axis, float offset, int32_t source)
379 {
380 if (parentScrollCallbacks_.empty()) {
381 return;
382 }
383 auto it = parentScrollCallbacks_.find(id);
384 if (it == parentScrollCallbacks_.end()) {
385 return;
386 }
387 auto callbackMap = it->second;
388 if (callbackMap.empty()) {
389 parentScrollCallbacks_.erase(id);
390 return;
391 }
392 for (const auto& pair : callbackMap) {
393 pair.second(axis, offset, source);
394 }
395 }
396
RegisterScrollCallback(int32_t scrollableParentId,int32_t callbackId,ScrollableParentCallback && callback)397 void SelectOverlayManager::RegisterScrollCallback(
398 int32_t scrollableParentId, int32_t callbackId, ScrollableParentCallback&& callback)
399 {
400 auto it = parentScrollCallbacks_.find(scrollableParentId);
401 if (it == parentScrollCallbacks_.end()) {
402 std::map<int32_t, ScrollableParentCallback> callbackMap = { { callbackId, std::move(callback) } };
403 parentScrollCallbacks_.insert(std::make_pair(scrollableParentId, callbackMap));
404 } else {
405 it->second.insert(std::make_pair(callbackId, std::move(callback)));
406 }
407 }
408
RemoveScrollCallback(int32_t callbackId)409 void SelectOverlayManager::RemoveScrollCallback(int32_t callbackId)
410 {
411 if (parentScrollCallbacks_.empty()) {
412 return;
413 }
414 for (auto it = parentScrollCallbacks_.begin(); it != parentScrollCallbacks_.end();) {
415 it->second.erase(callbackId);
416 if (it->second.empty()) {
417 it = parentScrollCallbacks_.erase(it);
418 } else {
419 ++it;
420 }
421 }
422 }
423
CloseSelectContentOverlay(int32_t overlayId,CloseReason reason,bool animation)424 void SelectOverlayManager::CloseSelectContentOverlay(int32_t overlayId, CloseReason reason, bool animation)
425 {
426 CHECK_NULL_VOID(selectContentManager_);
427 selectContentManager_->CloseWithOverlayId(overlayId, reason, animation);
428 }
429
GetSelectContentOverlayManager()430 const RefPtr<SelectContentOverlayManager>& SelectOverlayManager::GetSelectContentOverlayManager()
431 {
432 if (!selectContentManager_) {
433 selectContentManager_ = AceType::MakeRefPtr<SelectContentOverlayManager>(rootNodeWeak_.Upgrade());
434 LegacyManagerCallbacks callbacks;
435 callbacks.closeCallback = [weak = WeakClaim(this)](bool animation, bool fireCloseEvent) {
436 auto manager = weak.Upgrade();
437 CHECK_NULL_VOID(manager);
438 if (fireCloseEvent) {
439 manager->NotifyOverlayClosed(fireCloseEvent);
440 }
441 manager->DestroySelectOverlay(animation);
442 };
443 callbacks.selectionResetCallback = [weak = WeakClaim(this)]() {
444 auto manager = weak.Upgrade();
445 CHECK_NULL_VOID(manager);
446 if (manager->selectedByMouseInfo_.onResetSelection) {
447 manager->selectedByMouseInfo_.onResetSelection();
448 }
449 manager->selectedByMouseInfo_.clear();
450 };
451 selectContentManager_->SetLegacyManagerBridge(callbacks);
452 }
453 return selectContentManager_;
454 }
455
OnFontChanged()456 void SelectOverlayManager::OnFontChanged()
457 {
458 auto contentOverlayManager = GetSelectContentOverlayManager();
459 CHECK_NULL_VOID(contentOverlayManager);
460 contentOverlayManager->NotifyUpdateToolBar(true);
461 }
462
~SelectOverlayManager()463 SelectOverlayManager::~SelectOverlayManager()
464 {
465 auto pipeline = PipelineBase::GetCurrentContext();
466 if (pipeline) {
467 auto fontManager = pipeline->GetFontManager();
468 if (fontManager) {
469 fontManager->RemoveFontChangeObserver(WeakClaim(this));
470 }
471 }
472 }
473 } // namespace OHOS::Ace::NG
474