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