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