• 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/pipeline/base/element_register.h"
25 #include "core/pipeline_ng/pipeline_context.h"
26 namespace OHOS::Ace::NG {
CreateAndShowSelectOverlay(const SelectOverlayInfo & info,const WeakPtr<SelectionHost> & host,bool animation)27 RefPtr<SelectOverlayProxy> SelectOverlayManager::CreateAndShowSelectOverlay(
28     const SelectOverlayInfo& info, const WeakPtr<SelectionHost>& host, bool animation)
29 {
30     host_ = host;
31     auto current = selectOverlayItem_.Upgrade();
32     if (selectedByMouseInfo_.selectedNode.Upgrade() && info.callerFrameNode.Upgrade() &&
33         selectedByMouseInfo_.selectedNode.Upgrade() != info.callerFrameNode.Upgrade()) {
34         if (selectedByMouseInfo_.onResetSelection) {
35             selectedByMouseInfo_.onResetSelection();
36         }
37         selectedByMouseInfo_.clear();
38     }
39     if (current) {
40         if (info.isUsingMouse && IsSameSelectOverlayInfo(info)) {
41             auto proxy = MakeRefPtr<SelectOverlayProxy>(current->GetId());
42             return proxy;
43         }
44         auto frameNode = GetCallerHost();
45         CHECK_NULL_RETURN(frameNode, nullptr);
46         if (frameNode->GetTag() != V2::RICH_EDITOR_ETS_TAG) {
47             NotifyOverlayClosed(true);
48         }
49         DestroySelectOverlay(current->GetId());
50     }
51     selectOverlayInfo_ = info;
52     SelectOverlayInfo selectInfo = info;
53     if (selectInfo.callerFrameNode.Invalid()) {
54         selectInfo.callerFrameNode = GetCallerHost();
55     }
56     auto infoPtr = std::make_shared<SelectOverlayInfo>(selectInfo);
57     auto selectOverlayNode = SelectOverlayNode::CreateSelectOverlayNode(infoPtr);
58     selectOverlayItem_ = selectOverlayNode;
59 
60     auto taskExecutor = Container::CurrentTaskExecutor();
61     taskExecutor->PostTask(
62         [weakRoot = rootNodeWeak_, overlayNode = selectOverlayNode, animation,
63             isUsingMouse = infoPtr->isUsingMouse, weak = WeakClaim(this), weakCaller = infoPtr->callerFrameNode] {
64             auto selectOverlayManager = weak.Upgrade();
65             CHECK_NULL_VOID(selectOverlayManager);
66             CHECK_NULL_VOID(overlayNode);
67             if (overlayNode != selectOverlayManager->GetSelectOverlayItem()) {
68                 return;
69             }
70             auto rootNode = weakRoot.Upgrade();
71             auto container = Container::Current();
72             if (container && container->IsScenceBoardWindow()) {
73                 auto root = selectOverlayManager->FindWindowScene(weakCaller.Upgrade());
74                 rootNode = DynamicCast<FrameNode>(root);
75             }
76             CHECK_NULL_VOID(rootNode);
77             // get keyboard index to put selet_overlay before keyboard node
78             int32_t slot = DEFAULT_NODE_SLOT;
79             int32_t index = 0;
80             for (const auto& it : rootNode->GetChildren()) {
81                 if (it->GetTag() == V2::KEYBOARD_ETS_TAG) {
82                     slot = index;
83                     break;
84                 }
85                 index++;
86             }
87 
88             overlayNode->MountToParent(rootNode, slot);
89             rootNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
90             if (!isUsingMouse) {
91                 auto node = DynamicCast<SelectOverlayNode>(overlayNode);
92                 CHECK_NULL_VOID(node);
93                 node->ShowSelectOverlay(animation);
94             }
95             auto context = PipelineContext::GetCurrentContext();
96             CHECK_NULL_VOID(context);
97             context->AddAfterLayoutTask([weakNode = WeakPtr<FrameNode>(rootNode)]() {
98                 auto hostNode = weakNode.Upgrade();
99                 CHECK_NULL_VOID(hostNode);
100                 hostNode->OnAccessibilityEvent(AccessibilityEventType::PAGE_CHANGE);
101             });
102         },
103         TaskExecutor::TaskType::UI);
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 animation)154 bool SelectOverlayManager::ResetSelectionAndDestroySelectOverlay(bool animation)
155 {
156     NotifyOverlayClosed(true);
157     return DestroySelectOverlay(animation);
158 }
159 
DestroyHelper(const RefPtr<FrameNode> & overlay,bool animation)160 void SelectOverlayManager::DestroyHelper(const RefPtr<FrameNode>& overlay, bool animation)
161 {
162     auto rootNode = rootNodeWeak_.Upgrade();
163     CHECK_NULL_VOID(rootNode);
164     if (animation && !selectOverlayInfo_.isUsingMouse) {
165         selectOverlayItem_.Reset();
166         host_.Reset();
167         touchDownPoints_.clear();
168         selectOverlayInfo_.callerFrameNode.Reset();
169         auto node = DynamicCast<SelectOverlayNode>(overlay);
170         node->HideSelectOverlay([overlayWeak = WeakClaim(RawPtr(overlay)), managerWeak = WeakClaim(this)]() {
171             auto manager = managerWeak.Upgrade();
172             CHECK_NULL_VOID(manager);
173             auto overlay = overlayWeak.Upgrade();
174             CHECK_NULL_VOID(overlay);
175             manager->Destroy(overlay);
176         });
177     } else {
178         Destroy(overlay);
179         selectOverlayItem_.Reset();
180         host_.Reset();
181         touchDownPoints_.clear();
182         selectOverlayInfo_.callerFrameNode.Reset();
183     }
184 }
185 
Destroy(const RefPtr<FrameNode> & overlay)186 void SelectOverlayManager::Destroy(const RefPtr<FrameNode>& overlay)
187 {
188     auto rootNode = overlay->GetParent();
189     CHECK_NULL_VOID(rootNode);
190     rootNode->RemoveChild(overlay);
191     rootNode->MarkNeedSyncRenderTree();
192     rootNode->RebuildRenderContextTree();
193     auto context = PipelineContext::GetCurrentContext();
194     CHECK_NULL_VOID(context);
195     context->AddAfterRenderTask([weakNode = WeakPtr<UINode>(rootNode)]() {
196         auto hostNode = weakNode.Upgrade();
197         CHECK_NULL_VOID(hostNode);
198         if (AceType::InstanceOf<FrameNode>(hostNode)) {
199             auto frameNode = AceType::DynamicCast<FrameNode>(hostNode);
200             CHECK_NULL_VOID(frameNode);
201             frameNode->OnAccessibilityEvent(AccessibilityEventType::PAGE_CHANGE);
202         }
203     });
204 }
205 
HasSelectOverlay(int32_t overlayId)206 bool SelectOverlayManager::HasSelectOverlay(int32_t overlayId)
207 {
208     auto current = selectOverlayItem_.Upgrade();
209     CHECK_NULL_RETURN(current, false);
210     return current->GetId() == overlayId;
211 }
212 
IsInSelectedOrSelectOverlayArea(const PointF & point)213 bool SelectOverlayManager::IsInSelectedOrSelectOverlayArea(const PointF& point)
214 {
215     auto host = host_.Upgrade();
216     if (host && host->IsTouchTestPointInArea(Offset { point.GetX(), point.GetY() }, IsTouchInCallerArea(point))) {
217         return true;
218     }
219     auto current = selectOverlayItem_.Upgrade();
220     CHECK_NULL_RETURN(current, false);
221     auto selectOverlayNode = DynamicCast<SelectOverlayNode>(current);
222     if (selectOverlayNode) {
223         return selectOverlayNode->IsInSelectedOrSelectOverlayArea(point);
224     }
225     // get the menu rect not the out wrapper
226     const auto& children = current->GetChildren();
227     for (const auto& it : children) {
228         auto child = DynamicCast<FrameNode>(it);
229         if (child == nullptr) {
230             continue;
231         }
232         auto frameRect = child->GetGeometryNode()->GetFrameRect();
233         if (frameRect.IsInRegion(point)) {
234             return true;
235         }
236     }
237     return false;
238 }
239 
GetSelectOverlayNode(int32_t overlayId)240 RefPtr<SelectOverlayNode> SelectOverlayManager::GetSelectOverlayNode(int32_t overlayId)
241 {
242     auto current = selectOverlayItem_.Upgrade();
243     if (current && (current->GetId() == overlayId)) {
244         return DynamicCast<SelectOverlayNode>(current);
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     ResetSelection(touchPoint, isMousePressAtSelectedNode);
267     CHECK_NULL_VOID(!selectOverlayItem_.Invalid());
268     NG::PointF point { touchPoint.x - rootOffset.GetX(), touchPoint.y - rootOffset.GetY() };
269     // handle global touch event.
270     if (touchPoint.type == TouchType::DOWN && touchPoint.sourceType == SourceType::TOUCH) {
271         if (touchDownPoints_.empty() && !IsTouchInCallerArea(point) && !IsInSelectedOrSelectOverlayArea(point)) {
272             touchDownPoints_.emplace_back(touchPoint);
273         }
274         return;
275     }
276     if (touchPoint.type == TouchType::MOVE && touchPoint.sourceType == SourceType::TOUCH) {
277         if (touchDownPoints_.empty()) {
278             return;
279         }
280         auto lastTouchDownPoint = touchDownPoints_.back();
281         if (lastTouchDownPoint.id != touchPoint.id) {
282             return;
283         }
284         auto deltaOffset = touchPoint.GetOffset() - lastTouchDownPoint.GetOffset();
285         auto deltaDistance = deltaOffset.GetDistance();
286         auto context = PipelineBase::GetCurrentContext();
287         auto thresholdDistance = context ? context->NormalizeToPx(Dimension(5, DimensionUnit::VP)) : 5;
288         if (deltaDistance > thresholdDistance) {
289             touchDownPoints_.clear();
290         }
291         return;
292     }
293     bool acceptTouchUp = !touchDownPoints_.empty();
294     if (touchPoint.type == TouchType::UP && touchPoint.sourceType == SourceType::TOUCH && acceptTouchUp) {
295         auto lastTouchDownPoint = touchDownPoints_.back();
296         if (lastTouchDownPoint.id != touchPoint.id) {
297             return;
298         }
299         touchDownPoints_.pop_back();
300         point.SetX(lastTouchDownPoint.x - rootOffset.GetX());
301         point.SetY(lastTouchDownPoint.y - rootOffset.GetY());
302     }
303 
304     // handle global mouse event.
305     if ((touchPoint.type != TouchType::DOWN || touchPoint.sourceType != SourceType::MOUSE) && !acceptTouchUp) {
306         return;
307     }
308     if (!IsInSelectedOrSelectOverlayArea(point)) {
309         NotifyOverlayClosed(true);
310         DestroySelectOverlay();
311     }
312 }
313 
ResetSelection(const TouchEvent & touchPoint,bool isMousePressAtSelectedNode)314 void SelectOverlayManager::ResetSelection(const TouchEvent& touchPoint, bool isMousePressAtSelectedNode)
315 {
316     if (touchPoint.type == TouchType::DOWN && !isMousePressAtSelectedNode && !selectOverlayItem_.Upgrade()) {
317         CHECK_NULL_VOID(selectedByMouseInfo_.selectedNode.Upgrade());
318         if (selectedByMouseInfo_.onResetSelection) {
319             selectedByMouseInfo_.onResetSelection();
320         }
321         selectedByMouseInfo_.clear();
322     }
323 }
324 
IsTouchInCallerArea(const std::optional<NG::PointF> & point) const325 bool SelectOverlayManager::IsTouchInCallerArea(const std::optional<NG::PointF>& point) const
326 {
327     if (point.has_value() && selectOverlayInfo_.checkIsTouchInHostArea &&
328         selectOverlayInfo_.checkIsTouchInHostArea(point.value())) {
329         return true;
330     }
331     if (touchTestResults_.empty()) {
332         return false;
333     }
334     auto frameNode = GetCallerHost();
335     CHECK_NULL_RETURN(frameNode, false);
336     auto id = std::to_string(frameNode->GetId());
337     for (auto testId : touchTestResults_) {
338         if (testId == id) {
339             return true;
340         }
341     }
342     return false;
343 }
344 
GetCallerHost() const345 RefPtr<FrameNode> SelectOverlayManager::GetCallerHost() const
346 {
347     auto host = host_.Upgrade();
348     CHECK_NULL_RETURN(host, nullptr);
349     auto pattern = DynamicCast<Pattern>(host);
350     CHECK_NULL_RETURN(pattern, nullptr);
351     return pattern->GetHost();
352 }
353 
NotifyOverlayClosed(bool closedByGlobalEvent)354 void SelectOverlayManager::NotifyOverlayClosed(bool closedByGlobalEvent)
355 {
356     auto current = selectOverlayItem_.Upgrade();
357     if (current) {
358         auto selectOverlayNode = DynamicCast<SelectOverlayNode>(current);
359         CHECK_NULL_VOID(selectOverlayNode);
360         selectOverlayNode->SetClosedByGlobalEvent(closedByGlobalEvent);
361     }
362 }
363 
MarkDirty(PropertyChangeFlag flag)364 void SelectOverlayManager::MarkDirty(PropertyChangeFlag flag)
365 {
366     auto selectOverlayItem = selectOverlayItem_.Upgrade();
367     if (selectOverlayItem) {
368         selectOverlayItem->MarkDirtyNode(flag);
369     }
370 }
371 
NotifyOnScrollCallback(int32_t id,Axis axis,float offset,int32_t source)372 void SelectOverlayManager::NotifyOnScrollCallback(int32_t id, Axis axis, float offset, int32_t source)
373 {
374     if (parentScrollCallbacks_.empty()) {
375         return;
376     }
377     auto it = parentScrollCallbacks_.find(id);
378     if (it == parentScrollCallbacks_.end()) {
379         return;
380     }
381     auto callbackMap = it->second;
382     if (callbackMap.empty()) {
383         parentScrollCallbacks_.erase(id);
384         return;
385     }
386     for (const auto& pair : callbackMap) {
387         pair.second(axis, offset, source);
388     }
389 }
390 
RegisterScrollCallback(int32_t scrollableParentId,int32_t callbackId,ScrollableParentCallback && callback)391 void SelectOverlayManager::RegisterScrollCallback(
392     int32_t scrollableParentId, int32_t callbackId, ScrollableParentCallback&& callback)
393 {
394     auto it = parentScrollCallbacks_.find(scrollableParentId);
395     if (it == parentScrollCallbacks_.end()) {
396         std::map<int32_t, ScrollableParentCallback> callbackMap = { { callbackId, std::move(callback) } };
397         parentScrollCallbacks_.insert(std::make_pair(scrollableParentId, callbackMap));
398     } else {
399         it->second.insert(std::make_pair(callbackId, std::move(callback)));
400     }
401 }
402 
RemoveScrollCallback(int32_t callbackId)403 void SelectOverlayManager::RemoveScrollCallback(int32_t callbackId)
404 {
405     if (parentScrollCallbacks_.empty()) {
406         return;
407     }
408     for (auto it = parentScrollCallbacks_.begin(); it != parentScrollCallbacks_.end();) {
409         it->second.erase(callbackId);
410         if (it->second.empty()) {
411             it = parentScrollCallbacks_.erase(it);
412         } else {
413             ++it;
414         }
415     }
416 }
417 } // namespace OHOS::Ace::NG
418