• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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