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