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