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/pattern/select_overlay/select_overlay_pattern.h"
17 
18 #include <algorithm>
19 
20 #include "base/geometry/dimension.h"
21 #include "base/geometry/dimension_rect.h"
22 #include "base/geometry/ng/offset_t.h"
23 #include "base/geometry/ng/point_t.h"
24 #include "base/geometry/ng/rect_t.h"
25 #include "base/geometry/offset.h"
26 #include "base/utils/utils.h"
27 #include "core/components/menu/menu_component.h"
28 #include "core/components/text_overlay/text_overlay_theme.h"
29 #include "core/components_ng/base/ui_node.h"
30 #include "core/components_ng/pattern/menu/menu_layout_property.h"
31 #include "core/components_ng/pattern/select_overlay/select_overlay_node.h"
32 #include "core/components_ng/pattern/select_overlay/select_overlay_property.h"
33 #include "core/components_ng/property/property.h"
34 #include "core/components_ng/property/safe_area_insets.h"
35 #include "core/pipeline/base/constants.h"
36 #include "core/pipeline_ng/pipeline_context.h"
37 
38 namespace OHOS::Ace::NG {
39 namespace {
40 constexpr uint32_t HIDDEN_HANDLE_TIMER_MS = 4000; // 4000ms
41 } // namespace
42 
OnAttachToFrameNode()43 void SelectOverlayPattern::OnAttachToFrameNode()
44 {
45     auto host = GetHost();
46     CHECK_NULL_VOID(host);
47     host->GetLayoutProperty()->UpdateMeasureType(MeasureType::MATCH_PARENT);
48     host->GetLayoutProperty()->UpdateAlignment(Alignment::TOP_LEFT);
49 
50     UpdateHandleHotZone();
51     auto gesture = host->GetOrCreateGestureEventHub();
52     if (overlayMode_ == SelectOverlayMode::MENU_ONLY) {
53         gesture->SetHitTestMode(HitTestMode::HTMTRANSPARENT_SELF);
54         return;
55     }
56     gesture->SetHitTestMode(info_->hitTestMode);
57     SetGestureEvent();
58     if (info_->isSingleHandle) {
59         StartHiddenHandleTask();
60     }
61 }
62 
SetGestureEvent()63 void SelectOverlayPattern::SetGestureEvent()
64 {
65     auto host = GetHost();
66     CHECK_NULL_VOID(host);
67     auto gesture = host->GetOrCreateGestureEventHub();
68     clickEvent_ = MakeRefPtr<ClickEvent>([weak = WeakClaim(this)](GestureEvent& info) {
69         auto pattern = weak.Upgrade();
70         CHECK_NULL_VOID(pattern);
71         pattern->HandleOnClick(info);
72     });
73     gesture->AddClickEvent(clickEvent_);
74     auto panStart = [weak = WeakClaim(this)](GestureEvent& info) {
75         auto pattern = weak.Upgrade();
76         CHECK_NULL_VOID(pattern);
77         pattern->HandlePanStart(info);
78     };
79     auto panUpdate = [weak = WeakClaim(this)](GestureEvent& info) {
80         auto pattern = weak.Upgrade();
81         CHECK_NULL_VOID(pattern);
82         pattern->HandlePanMove(info);
83     };
84     auto panEnd = [weak = WeakClaim(this)](GestureEvent& info) {
85         auto pattern = weak.Upgrade();
86         CHECK_NULL_VOID(pattern);
87         pattern->HandlePanEnd(info);
88     };
89     auto panCancel = [weak = WeakClaim(this)]() {
90         auto pattern = weak.Upgrade();
91         CHECK_NULL_VOID(pattern);
92         pattern->HandlePanCancel();
93     };
94     panEvent_ =
95         MakeRefPtr<PanEvent>(std::move(panStart), std::move(panUpdate), std::move(panEnd), std::move(panCancel));
96     gesture->AddPanEvent(panEvent_, { PanDirection::ALL }, 1, DEFAULT_PAN_DISTANCE);
97 
98     auto touchTask = [weak = WeakClaim(this)](const TouchEventInfo& info) {
99         auto pattern = weak.Upgrade();
100         if (pattern) {
101             pattern->HandleTouchEvent(info);
102         }
103     };
104     touchEvent_ = MakeRefPtr<TouchEventImpl>(std::move(touchTask));
105     gesture->AddTouchEvent(touchEvent_);
106     InitMouseEvent();
107 }
108 
InitMouseEvent()109 void SelectOverlayPattern::InitMouseEvent()
110 {
111     auto host = GetHost();
112     CHECK_NULL_VOID(host);
113     auto eventHub = host->GetEventHub<EventHub>();
114     CHECK_NULL_VOID(eventHub);
115     auto inputHub = eventHub->GetOrCreateInputEventHub();
116     CHECK_NULL_VOID(inputHub);
117     auto mouseTask = [weak = WeakClaim(this)](MouseInfo& info) {
118         auto pattern = weak.Upgrade();
119         CHECK_NULL_VOID(pattern);
120         pattern->HandleMouseEvent(info);
121     };
122     auto mouseEvent = MakeRefPtr<InputEvent>(std::move(mouseTask));
123     inputHub->AddOnMouseEvent(mouseEvent);
124 }
125 
OnDetachFromFrameNode(FrameNode *)126 void SelectOverlayPattern::OnDetachFromFrameNode(FrameNode* /*frameNode*/)
127 {
128     CHECK_NULL_VOID(info_);
129     if (info_->onClose) {
130         info_->onClose(closedByGlobalTouchEvent_);
131         closedByGlobalTouchEvent_ = false;
132     }
133 }
134 
AddMenuResponseRegion(std::vector<DimensionRect> & responseRegion)135 void SelectOverlayPattern::AddMenuResponseRegion(std::vector<DimensionRect>& responseRegion)
136 {
137     auto layoutProps = GetLayoutProperty<LayoutProperty>();
138     CHECK_NULL_VOID(layoutProps);
139     float safeAreaInsetsLeft = 0.0f;
140     float safeAreaInsetsTop = 0.0f;
141     auto&& safeAreaInsets = layoutProps->GetSafeAreaInsets();
142     if (safeAreaInsets) {
143         safeAreaInsetsLeft = static_cast<float>(safeAreaInsets->left_.end);
144         safeAreaInsetsTop = static_cast<float>(safeAreaInsets->top_.end);
145     }
146     const auto& children = GetHost()->GetChildren();
147     for (const auto& it : children) {
148         auto child = DynamicCast<FrameNode>(it);
149         if (child == nullptr) {
150             continue;
151         }
152         auto frameRect = child->GetGeometryNode()->GetFrameRect();
153         // rect is relative to window
154         auto rect = Rect(frameRect.GetX() + safeAreaInsetsLeft, frameRect.GetY() + safeAreaInsetsTop, frameRect.Width(),
155             frameRect.Height());
156 
157         DimensionRect region;
158         region.SetSize({ Dimension(rect.GetSize().Width()), Dimension(rect.GetSize().Height()) });
159         region.SetOffset(DimensionOffset(Offset(rect.GetOffset().GetX(), rect.GetOffset().GetY())));
160 
161         responseRegion.emplace_back(region);
162     }
163 }
164 
UpdateHandleHotZone()165 void SelectOverlayPattern::UpdateHandleHotZone()
166 {
167     if (!CheckIfNeedHandle()) {
168         return;
169     }
170     auto host = GetHost();
171     CHECK_NULL_VOID(host);
172     auto pipeline = host->GetContext();
173     CHECK_NULL_VOID(pipeline);
174     auto firstHandle = info_->GetFirstHandlePaintRect();
175     auto secondHandle = info_->GetSecondHandlePaintRect();
176 
177     auto theme = pipeline->GetTheme<TextOverlayTheme>();
178     CHECK_NULL_VOID(theme);
179     auto hotZone = theme->GetHandleHotZoneRadius().ConvertToPx();
180     firstHandleRegion_.SetSize({ hotZone * 2, hotZone * 2 + firstHandle.Height() });
181     auto firstHandleOffsetX = (firstHandle.Left() + firstHandle.Right()) / 2;
182     secondHandleRegion_.SetSize({ hotZone * 2, hotZone * 2 + secondHandle.Height() });
183     auto secondHandleOffsetX = (secondHandle.Left() + secondHandle.Right()) / 2;
184     std::vector<DimensionRect> responseRegion;
185     if (info_->isSingleHandle) {
186         if (!info_->firstHandle.isShow && info_->secondHandle.isShow) {
187             // Use the second handle to make a single handle.
188             auto secondHandleOffsetY = secondHandle.Top();
189             secondHandleRegion_.SetOffset({ secondHandleOffsetX - hotZone, secondHandleOffsetY });
190             DimensionRect secondHandleRegion;
191             secondHandleRegion.SetSize({ Dimension(secondHandleRegion_.GetSize().Width()),
192                 Dimension(secondHandleRegion_.GetSize().Height()) });
193             secondHandleRegion.SetOffset(DimensionOffset(
194                 Offset(secondHandleRegion_.GetOffset().GetX(), secondHandleRegion_.GetOffset().GetY())));
195             responseRegion.emplace_back(secondHandleRegion);
196             host->GetOrCreateGestureEventHub()->SetResponseRegion(responseRegion);
197             firstHandleRegion_.Reset();
198         } else {
199             // Use the first handle to make a single handle.
200             auto firstHandleOffsetY = firstHandle.Top();
201             firstHandleRegion_.SetOffset({ firstHandleOffsetX - hotZone, firstHandleOffsetY });
202             DimensionRect firstHandleRegion;
203             firstHandleRegion.SetSize(
204                 { Dimension(firstHandleRegion_.GetSize().Width()), Dimension(firstHandleRegion_.GetSize().Height()) });
205             firstHandleRegion.SetOffset(
206                 DimensionOffset(Offset(firstHandleRegion_.GetOffset().GetX(), firstHandleRegion_.GetOffset().GetY())));
207             responseRegion.emplace_back(firstHandleRegion);
208             host->GetOrCreateGestureEventHub()->SetResponseRegion(responseRegion);
209             secondHandleRegion_.Reset();
210         }
211         return;
212     }
213     if (info_->handleReverse) {
214         auto firstHandleOffsetY = firstHandle.Top();
215         firstHandleRegion_.SetOffset({ firstHandleOffsetX - hotZone, firstHandleOffsetY });
216         auto secondHandleOffsetY = secondHandle.Top();
217         secondHandleRegion_.SetOffset({ secondHandleOffsetX - hotZone, secondHandleOffsetY - hotZone * 2 });
218     } else {
219         auto firstHandleOffsetY = firstHandle.Top();
220         firstHandleRegion_.SetOffset({ firstHandleOffsetX - hotZone, firstHandleOffsetY - hotZone * 2 });
221         auto secondHandleOffsetY = secondHandle.Top();
222         secondHandleRegion_.SetOffset({ secondHandleOffsetX - hotZone, secondHandleOffsetY });
223     }
224     DimensionRect firstHandleRegion;
225     firstHandleRegion.SetSize(
226         { Dimension(firstHandleRegion_.GetSize().Width()), Dimension(firstHandleRegion_.GetSize().Height()) });
227     firstHandleRegion.SetOffset(
228         DimensionOffset(Offset(firstHandleRegion_.GetOffset().GetX(), firstHandleRegion_.GetOffset().GetY())));
229     responseRegion.emplace_back(firstHandleRegion);
230     DimensionRect secondHandleRegion;
231     secondHandleRegion.SetSize(
232         { Dimension(secondHandleRegion_.GetSize().Width()), Dimension(secondHandleRegion_.GetSize().Height()) });
233     secondHandleRegion.SetOffset(
234         DimensionOffset(Offset(secondHandleRegion_.GetOffset().GetX(), secondHandleRegion_.GetOffset().GetY())));
235     responseRegion.emplace_back(secondHandleRegion);
236     if (IsCustomMenu()) {
237         AddMenuResponseRegion(responseRegion);
238     }
239     host->GetOrCreateGestureEventHub()->SetResponseRegion(responseRegion);
240 }
241 
HandleOnClick(GestureEvent & info)242 void SelectOverlayPattern::HandleOnClick(GestureEvent& info)
243 {
244     if (info_->onClick) {
245         info_->onClick(info, isFirstHandleTouchDown_);
246     }
247     if (!info_->isSingleHandle) {
248         return;
249     }
250     auto host = DynamicCast<SelectOverlayNode>(GetHost());
251     CHECK_NULL_VOID(host);
252     if (!info_->menuInfo.menuDisable) {
253         info_->menuInfo.menuIsShow = !info_->menuInfo.menuIsShow;
254         host->UpdateToolBar(false);
255 
256         StopHiddenHandleTask();
257         StartHiddenHandleTask();
258         info_->menuInfo.singleHandleMenuIsShow = info_->menuInfo.menuIsShow;
259     }
260     if (info_->afterOnClick) {
261         info_->afterOnClick(info, isFirstHandleTouchDown_);
262     }
263 }
264 
HandleTouchEvent(const TouchEventInfo & info)265 void SelectOverlayPattern::HandleTouchEvent(const TouchEventInfo& info)
266 {
267     const auto& changedPoint = info.GetChangedTouches().front();
268     if (changedPoint.GetTouchType() == TouchType::DOWN) {
269         HandleTouchDownEvent(info);
270     } else if (info_->onTouchDown && changedPoint.GetTouchType() == TouchType::UP) {
271         info_->onTouchUp(info);
272     } else if (info_->onTouchMove && changedPoint.GetTouchType() == TouchType::MOVE) {
273         info_->onTouchMove(info);
274     }
275     if (IsCustomMenu()) {
276         MenuWrapperPattern::OnTouchEvent(info);
277     }
278     if (changedPoint.GetTouchType() == TouchType::UP) {
279         SwitchHandleToOverlayMode(false);
280     }
281 }
282 
HandleTouchDownEvent(const TouchEventInfo & info)283 void SelectOverlayPattern::HandleTouchDownEvent(const TouchEventInfo& info)
284 {
285     if (info_->onTouchDown) {
286         info_->onTouchDown(info);
287     }
288     auto touchOffset = info.GetChangedTouches().front().GetLocalLocation();
289     PointF point = { touchOffset.GetX(), touchOffset.GetY() };
290     if (firstHandleRegion_.IsInRegion(point)) {
291         isFirstHandleTouchDown_ = true;
292     } else if (secondHandleRegion_.IsInRegion(point)) {
293         isSecondHandleTouchDown_ = true;
294     }
295 }
296 
HandlePanStart(GestureEvent & info)297 void SelectOverlayPattern::HandlePanStart(GestureEvent& info)
298 {
299     if (info.GetSourceDevice() == SourceType::MOUSE) {
300         return;
301     }
302     if (!isFirstHandleTouchDown_ && !isSecondHandleTouchDown_) {
303         LOGW("no handle is pressed");
304         return;
305     }
306     if (IsFirstHandleMoveStart(info.GetLocalLocation())) {
307         firstHandleDrag_ = true;
308         secondHandleDrag_ = false;
309         if (info_->onHandleMoveStart) {
310             info_->onHandleMoveStart(info, firstHandleDrag_);
311         }
312     } else {
313         firstHandleDrag_ = false;
314         secondHandleDrag_ = true;
315         if (info_->onHandleMoveStart) {
316             info_->onHandleMoveStart(info, firstHandleDrag_);
317         }
318     }
319 
320     auto host = DynamicCast<SelectOverlayNode>(GetHost());
321     CHECK_NULL_VOID(host);
322     orignMenuIsShow_ = info_->menuInfo.menuIsShow;
323     if (info_->menuInfo.menuIsShow) {
324         info_->menuInfo.menuIsShow = false;
325         host->UpdateToolBar(false);
326     }
327     if (info_->isSingleHandle) {
328         StopHiddenHandleTask();
329     }
330     isFirstHandleTouchDown_ = false;
331     isSecondHandleTouchDown_ = false;
332     SwitchHandleToOverlayMode(true);
333 }
334 
HandlePanMove(GestureEvent & info)335 void SelectOverlayPattern::HandlePanMove(GestureEvent& info)
336 {
337     auto host = DynamicCast<SelectOverlayNode>(GetHost());
338     CHECK_NULL_VOID(host);
339     const auto& offset = OffsetF(info.GetDelta().GetX(), info.GetDelta().GetY());
340     if (firstHandleDrag_) {
341         if (info_->onHandlePanMove) {
342             info_->onHandlePanMove(info, true);
343         }
344         UpdateOffsetOnMove(firstHandleRegion_, info_->firstHandle, offset, true);
345     } else if (secondHandleDrag_) {
346         if (info_->onHandlePanMove) {
347             info_->onHandlePanMove(info, false);
348         }
349         UpdateOffsetOnMove(secondHandleRegion_, info_->secondHandle, offset, false);
350     } else {
351         LOGW("the move point is not in drag area");
352     }
353     auto context = host->GetContext();
354     CHECK_NULL_VOID(context);
355     if (host->IsLayoutDirtyMarked()) {
356         context->AddDirtyLayoutNode(host);
357     }
358 }
359 
UpdateOffsetOnMove(RectF & region,SelectHandleInfo & handleInfo,const OffsetF & offset,bool isFirst)360 void SelectOverlayPattern::UpdateOffsetOnMove(
361     RectF& region, SelectHandleInfo& handleInfo, const OffsetF& offset, bool isFirst)
362 {
363     auto host = DynamicCast<SelectOverlayNode>(GetHost());
364     CHECK_NULL_VOID(host);
365     region += offset;
366     handleInfo.paintRect += offset;
367     handleInfo.localPaintRect += offset;
368     auto isOverlayMode = info_->handleLevelMode == HandleLevelMode::OVERLAY;
369     if (!isOverlayMode && info_->getDeltaHandleOffset) {
370         handleInfo.localPaintRect += info_->getDeltaHandleOffset();
371     }
372     auto paintRect = isOverlayMode ? handleInfo.paintRect : handleInfo.localPaintRect;
373     handleInfo.paintInfo = handleInfo.paintInfo + offset;
374     if (isOverlayMode && handleInfo.isPaintHandleWithPoints && handleInfo.paintInfoConverter) {
375         paintRect = handleInfo.paintInfoConverter(handleInfo.paintInfo);
376     }
377     CheckHandleReverse();
378     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
379     if (info_->onHandleMove) {
380         info_->onHandleMove(paintRect, isFirst);
381     }
382 }
383 
HandlePanEnd(GestureEvent & info)384 void SelectOverlayPattern::HandlePanEnd(GestureEvent& info)
385 {
386     auto host = DynamicCast<SelectOverlayNode>(GetHost());
387     CHECK_NULL_VOID(host);
388     if (!info_->menuInfo.menuIsShow) {
389         info_->menuInfo.menuIsShow = orignMenuIsShow_;
390         host->UpdateToolBar(false);
391     }
392     if (firstHandleDrag_) {
393         firstHandleDrag_ = false;
394         if (info_->onHandlePanEnd) {
395             info_->onHandlePanEnd(info, true);
396         }
397         if (info_->onHandleMoveDone) {
398             auto paintRect = GetHandlePaintRect(info_->firstHandle);
399             info_->onHandleMoveDone(paintRect, true);
400         }
401     } else if (secondHandleDrag_) {
402         secondHandleDrag_ = false;
403         if (info_->onHandlePanEnd) {
404             info_->onHandlePanEnd(info, false);
405         }
406         if (info_->onHandleMoveDone) {
407             auto paintRect = GetHandlePaintRect(info_->secondHandle);
408             info_->onHandleMoveDone(paintRect, false);
409         }
410     }
411     if (info_->isSingleHandle) {
412         StartHiddenHandleTask();
413     }
414 }
415 
GetHandlePaintRect(const SelectHandleInfo & handleInfo)416 RectF SelectOverlayPattern::GetHandlePaintRect(const SelectHandleInfo& handleInfo)
417 {
418     auto paintRect = handleInfo.paintRect;
419     if (info_->handleLevelMode == HandleLevelMode::OVERLAY && handleInfo.isPaintHandleWithPoints &&
420         handleInfo.paintInfoConverter) {
421         paintRect = handleInfo.paintInfoConverter(handleInfo.paintInfo);
422     }
423     return paintRect;
424 }
425 
HandlePanCancel()426 void SelectOverlayPattern::HandlePanCancel()
427 {
428     GestureEvent info;
429     HandlePanEnd(info);
430 }
431 
HandleMouseEvent(const MouseInfo & info)432 void SelectOverlayPattern::HandleMouseEvent(const MouseInfo& info)
433 {
434     if (info_->onMouseEvent) {
435         info_->onMouseEvent(info);
436     }
437 }
438 
CheckHandleReverse()439 void SelectOverlayPattern::CheckHandleReverse()
440 {
441     bool handleReverseChanged = false;
442     if (IsHandlesInSameLine()) {
443         if (info_->firstHandle.paintRect.Left() > info_->secondHandle.paintRect.Left()) {
444             if (!info_->handleReverse) {
445                 info_->handleReverse = true;
446                 handleReverseChanged = true;
447             }
448         } else {
449             if (info_->handleReverse) {
450                 info_->handleReverse = false;
451                 handleReverseChanged = true;
452             }
453         }
454     } else if (GreatNotEqual(info_->firstHandle.paintRect.Top(), info_->secondHandle.paintRect.Top())) {
455         if (!info_->handleReverse) {
456             info_->handleReverse = true;
457             handleReverseChanged = true;
458         }
459     } else {
460         if (info_->handleReverse) {
461             info_->handleReverse = false;
462             handleReverseChanged = true;
463         }
464     }
465     if (handleReverseChanged && info_->onHandleReverse) {
466         info_->onHandleReverse(info_->handleReverse);
467     }
468 }
469 
IsHandlesInSameLine()470 bool SelectOverlayPattern::IsHandlesInSameLine()
471 {
472     float lowerHandleTop = 0.0f;
473     RectF heigherHandleRect;
474     if (GreatNotEqual(info_->firstHandle.paintRect.Top(), info_->secondHandle.paintRect.Top())) {
475         lowerHandleTop = info_->firstHandle.paintRect.Top() + 0.5f;
476         heigherHandleRect = info_->secondHandle.paintRect;
477     } else {
478         lowerHandleTop = info_->secondHandle.paintRect.Top() + 0.5f;
479         heigherHandleRect = info_->firstHandle.paintRect;
480     }
481     return GreatNotEqual(lowerHandleTop, heigherHandleRect.Top())
482         && LessNotEqual(lowerHandleTop, heigherHandleRect.Bottom());
483 }
484 
IsFirstHandleMoveStart(const Offset & touchOffset)485 bool SelectOverlayPattern::IsFirstHandleMoveStart(const Offset& touchOffset)
486 {
487     if (isFirstHandleTouchDown_ && isSecondHandleTouchDown_) {
488         auto firstHandleCenter = Offset{ firstHandleRegion_.Center().GetX(), firstHandleRegion_.Center().GetY() };
489         auto secondHandleCenter = Offset{ secondHandleRegion_.Center().GetX(), secondHandleRegion_.Center().GetY() };
490         auto distanceToFirstHandle = (firstHandleCenter - touchOffset).GetDistance();
491         auto distanceToSecondHandle = (secondHandleCenter - touchOffset).GetDistance();
492         return GreatNotEqual(distanceToSecondHandle, distanceToFirstHandle);
493     }
494     return isFirstHandleTouchDown_;
495 }
496 
SetHandleReverse(bool reverse)497 void SelectOverlayPattern::SetHandleReverse(bool reverse)
498 {
499     info_->handleReverse = reverse;
500     UpdateHandleHotZone();
501     auto host = DynamicCast<SelectOverlayNode>(GetHost());
502     CHECK_NULL_VOID(host);
503     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
504 }
505 
SetSelectRegionVisible(bool isSelectRegionVisible)506 void SelectOverlayPattern::SetSelectRegionVisible(bool isSelectRegionVisible)
507 {
508     if (info_->isSelectRegionVisible != isSelectRegionVisible) {
509         info_->isSelectRegionVisible = isSelectRegionVisible;
510         auto host = DynamicCast<SelectOverlayNode>(GetHost());
511         CHECK_NULL_VOID(host);
512         host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
513     }
514 }
515 
UpdateFirstSelectHandleInfo(const SelectHandleInfo & info)516 void SelectOverlayPattern::UpdateFirstSelectHandleInfo(const SelectHandleInfo& info)
517 {
518     if (info_->firstHandle == info) {
519         return;
520     }
521     info_->firstHandle = info;
522     CheckHandleReverse();
523     UpdateHandleHotZone();
524     auto host = DynamicCast<SelectOverlayNode>(GetHost());
525     CHECK_NULL_VOID(host);
526     if (info.needLayout) {
527         host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
528     } else {
529         host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
530     }
531 }
532 
UpdateSecondSelectHandleInfo(const SelectHandleInfo & info)533 void SelectOverlayPattern::UpdateSecondSelectHandleInfo(const SelectHandleInfo& info)
534 {
535     if (info_->secondHandle == info) {
536         return;
537     }
538     info_->secondHandle = info;
539     CheckHandleReverse();
540     UpdateHandleHotZone();
541     auto host = DynamicCast<SelectOverlayNode>(GetHost());
542     CHECK_NULL_VOID(host);
543     if (info.needLayout) {
544         host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
545     } else {
546         host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
547     }
548 }
549 
UpdateFirstAndSecondHandleInfo(const SelectHandleInfo & firstInfo,const SelectHandleInfo & secondInfo)550 void SelectOverlayPattern::UpdateFirstAndSecondHandleInfo(
551     const SelectHandleInfo& firstInfo, const SelectHandleInfo& secondInfo)
552 {
553     if (info_->firstHandle == firstInfo && info_->secondHandle == secondInfo) {
554         return;
555     }
556     if (info_->firstHandle != firstInfo && !firstHandleDrag_) {
557         info_->firstHandle = firstInfo;
558     }
559     if (info_->secondHandle != secondInfo && !secondHandleDrag_) {
560         info_->secondHandle = secondInfo;
561     }
562     CheckHandleReverse();
563     UpdateHandleHotZone();
564     auto host = DynamicCast<SelectOverlayNode>(GetHost());
565     CHECK_NULL_VOID(host);
566     host->UpdateToolBar(false);
567 }
568 
UpdateSelectMenuInfo(const SelectMenuInfo & info)569 void SelectOverlayPattern::UpdateSelectMenuInfo(const SelectMenuInfo& info)
570 {
571     auto host = DynamicCast<SelectOverlayNode>(GetHost());
572     CHECK_NULL_VOID(host);
573     auto itemChanged = info_->menuInfo.IsIconChanged(info);
574     info_->menuInfo = info;
575     host->UpdateToolBar(itemChanged);
576 }
577 
UpdateShowArea(const RectF & area)578 void SelectOverlayPattern::UpdateShowArea(const RectF& area)
579 {
580     if (info_->showArea != area) {
581         info_->showArea = area;
582     }
583     auto host = GetHost();
584     CHECK_NULL_VOID(host);
585     host->MarkDirtyNode(PROPERTY_UPDATE_LAYOUT);
586 }
587 
UpdateSelectMenuInfo(std::function<void (SelectMenuInfo & menuInfo)> updateAction)588 void SelectOverlayPattern::UpdateSelectMenuInfo(std::function<void(SelectMenuInfo& menuInfo)> updateAction)
589 {
590     if (updateAction) {
591         SelectMenuInfo shadowMenuInfo = info_->menuInfo;
592         updateAction(shadowMenuInfo);
593         UpdateSelectMenuInfo(shadowMenuInfo);
594     }
595 }
596 
ShowOrHiddenMenu(bool isHidden,bool noAnimation)597 void SelectOverlayPattern::ShowOrHiddenMenu(bool isHidden, bool noAnimation)
598 {
599     auto host = DynamicCast<SelectOverlayNode>(GetHost());
600     CHECK_NULL_VOID(host);
601     if (info_->menuInfo.menuIsShow && isHidden) {
602         info_->menuInfo.menuIsShow = false;
603         host->UpdateToolBar(false, noAnimation);
604     } else if (!info_->menuInfo.menuIsShow && !isHidden &&
605                (info_->firstHandle.isShow || info_->secondHandle.isShow || info_->isSelectRegionVisible ||
606                (info_->isNewAvoid && !info_->isSingleHandle))) {
607         info_->menuInfo.menuIsShow = true;
608         host->UpdateToolBar(false, noAnimation);
609     }
610 }
611 
DisableMenu(bool isDisabled)612 void SelectOverlayPattern::DisableMenu(bool isDisabled)
613 {
614     info_->menuInfo.menuDisable = isDisabled;
615     auto host = DynamicCast<SelectOverlayNode>(GetHost());
616     CHECK_NULL_VOID(host);
617     host->UpdateToolBar(false);
618 }
619 
OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper> & dirty,const DirtySwapConfig & config)620 bool SelectOverlayPattern::OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper>& dirty, const DirtySwapConfig& config)
621 {
622     UpdateHandleHotZone();
623     if (config.skipMeasure || dirty->SkipMeasureContent()) {
624         return false;
625     }
626     auto layoutAlgorithmWrapper = DynamicCast<LayoutAlgorithmWrapper>(dirty->GetLayoutAlgorithm());
627     CHECK_NULL_RETURN(layoutAlgorithmWrapper, false);
628     auto selectOverlayLayoutAlgorithm =
629         DynamicCast<SelectOverlayLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
630     CHECK_NULL_RETURN(selectOverlayLayoutAlgorithm, false);
631     defaultMenuStartOffset_ = selectOverlayLayoutAlgorithm->GetDefaultMenuStartOffset();
632     defaultMenuEndOffset_ = selectOverlayLayoutAlgorithm->GetDefaultMenuEndOffset();
633     menuWidth_ = selectOverlayLayoutAlgorithm->GetMenuWidth();
634     menuHeight_ = selectOverlayLayoutAlgorithm->GetMenuHeight();
635     hasExtensionMenu_ =
636         selectOverlayLayoutAlgorithm->GetHasExtensionMenu() && !selectOverlayLayoutAlgorithm->GetHideMoreOrBack();
637     if (IsCustomMenu()) {
638         MenuWrapperPattern::CheckAndShowAnimation();
639     }
640     return true;
641 }
642 
IsMenuShow()643 bool SelectOverlayPattern::IsMenuShow()
644 {
645     CHECK_NULL_RETURN(info_, false);
646     return info_->menuInfo.menuIsShow;
647 }
648 
IsSingleHandleMenuShow()649 bool SelectOverlayPattern::IsSingleHandleMenuShow()
650 {
651     CHECK_NULL_RETURN(info_, false);
652     return info_->menuInfo.singleHandleMenuIsShow;
653 }
654 
IsHandleShow()655 bool SelectOverlayPattern::IsHandleShow()
656 {
657     CHECK_NULL_RETURN(info_, false);
658     return info_->firstHandle.isShow || info_->secondHandle.isShow;
659 }
660 
IsSingleHandle()661 bool SelectOverlayPattern::IsSingleHandle()
662 {
663     CHECK_NULL_RETURN(info_, false);
664     return info_->isSingleHandle;
665 }
666 
StartHiddenHandleTask(bool isDelay)667 void SelectOverlayPattern::StartHiddenHandleTask(bool isDelay)
668 {
669     auto host = GetHost();
670     CHECK_NULL_VOID(host);
671     auto context = host->GetContext();
672     CHECK_NULL_VOID(context);
673     auto taskExecutor = context->GetTaskExecutor();
674     CHECK_NULL_VOID(taskExecutor);
675     auto weak = WeakClaim(this);
676     hiddenHandleTask_.Reset([weak] {
677         auto client = weak.Upgrade();
678         CHECK_NULL_VOID(client);
679         client->HiddenHandle();
680     });
681     if (isDelay) {
682         taskExecutor->PostDelayedTask(hiddenHandleTask_, TaskExecutor::TaskType::UI, HIDDEN_HANDLE_TIMER_MS,
683             "ArkUISelectOverlayHiddenHandle");
684     } else {
685         taskExecutor->PostTask(hiddenHandleTask_, TaskExecutor::TaskType::UI, "ArkUISelectOverlayHiddenHandle");
686     }
687 }
688 
HiddenHandle()689 void SelectOverlayPattern::HiddenHandle()
690 {
691     hiddenHandleTask_.Cancel();
692     isHiddenHandle_ = true;
693     if (info_->onHandleIsHidden) {
694         info_->onHandleIsHidden();
695     }
696     auto host = DynamicCast<SelectOverlayNode>(GetHost());
697     CHECK_NULL_VOID(host);
698     firstHandleRegion_.Reset();
699     secondHandleRegion_.Reset();
700     std::vector<DimensionRect> responseRegion;
701     host->GetOrCreateGestureEventHub()->SetResponseRegion(responseRegion);
702     host->GetOrCreateGestureEventHub()->SetHitTestMode(HitTestMode::HTMNONE);
703     host->GetOrCreateGestureEventHub()->RemoveClickEvent(clickEvent_);
704     host->GetOrCreateGestureEventHub()->RemovePanEvent(panEvent_);
705     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
706 }
707 
StopHiddenHandleTask()708 void SelectOverlayPattern::StopHiddenHandleTask()
709 {
710     hiddenHandleTask_.Cancel();
711 }
712 
UpdateSelectArea(const RectF & selectArea)713 void SelectOverlayPattern::UpdateSelectArea(const RectF& selectArea)
714 {
715     info_->selectArea = selectArea;
716 }
717 
SetIsNewAvoid(bool isNewAvoid)718 void SelectOverlayPattern::SetIsNewAvoid(bool isNewAvoid)
719 {
720     info_->isNewAvoid = isNewAvoid;
721 }
722 
SetSelectMenuHeight()723 void SelectOverlayPattern::SetSelectMenuHeight()
724 {
725     auto host = DynamicCast<SelectOverlayNode>(GetHost());
726     CHECK_NULL_VOID(host);
727     auto selectMenu = AceType::DynamicCast<FrameNode>(host->GetFirstChild());
728     CHECK_NULL_VOID(selectMenu);
729     auto geometryNode = selectMenu->GetGeometryNode();
730     CHECK_NULL_VOID(geometryNode);
731     selectMenuHeight_ = geometryNode->GetFrameSize().Height();
732 }
733 
CheckIfNeedMenu()734 bool SelectOverlayPattern::CheckIfNeedMenu()
735 {
736     return (overlayMode_ == SelectOverlayMode::ALL || overlayMode_ == SelectOverlayMode::MENU_ONLY);
737 }
738 
CheckIfNeedHandle()739 bool SelectOverlayPattern::CheckIfNeedHandle()
740 {
741     return (overlayMode_ == SelectOverlayMode::ALL || overlayMode_ == SelectOverlayMode::HANDLE_ONLY);
742 }
743 
GetHandleDiameter()744 float SelectOverlayPattern::GetHandleDiameter()
745 {
746     auto pipleline = PipelineContext::GetCurrentContextSafely();
747     CHECK_NULL_RETURN(pipleline, 0.0f);
748     auto textOverlayTheme = pipleline->GetTheme<TextOverlayTheme>();
749     CHECK_NULL_RETURN(textOverlayTheme, 0.0f);
750     return textOverlayTheme->GetHandleDiameter().ConvertToPx();
751 }
752 
SetContentModifierBounds(const RefPtr<SelectOverlayContentModifier> & modifier)753 void SelectOverlayPattern::SetContentModifierBounds(const RefPtr<SelectOverlayContentModifier>& modifier)
754 {
755     CHECK_NULL_VOID(modifier);
756     auto host = GetHost();
757     CHECK_NULL_VOID(host);
758     auto geometryNode = host->GetGeometryNode();
759     CHECK_NULL_VOID(geometryNode);
760     auto frameRect = geometryNode->GetFrameRect();
761     auto handleDiameter = GetHandleDiameter();
762     RectF boundsRect;
763     boundsRect.SetLeft(frameRect.Left() - handleDiameter * 1.5f);
764     boundsRect.SetTop(frameRect.Top() - handleDiameter * 1.5f);
765     boundsRect.SetWidth(frameRect.Width() + handleDiameter * 3.0f);
766     boundsRect.SetHeight(frameRect.Height() + handleDiameter * 3.0f);
767     modifier->SetBoundsRect(boundsRect);
768 }
769 
OnDpiConfigurationUpdate()770 void SelectOverlayPattern::OnDpiConfigurationUpdate()
771 {
772     auto host = DynamicCast<SelectOverlayNode>(GetHost());
773     CHECK_NULL_VOID(host);
774     host->UpdateToolBar(true, true);
775 }
776 
SwitchHandleToOverlayMode(bool afterRender)777 void SelectOverlayPattern::SwitchHandleToOverlayMode(bool afterRender)
778 {
779     if (!info_->enableHandleLevel || info_->handleLevelMode != HandleLevelMode::EMBED) {
780         return;
781     }
782     auto host = GetHost();
783     CHECK_NULL_VOID(host);
784     auto overlayNode = DynamicCast<SelectOverlayNode>(host);
785     CHECK_NULL_VOID(overlayNode);
786     auto switchTask = [weak = WeakClaim(AceType::RawPtr(overlayNode))]() {
787         auto overlayNode = weak.Upgrade();
788         CHECK_NULL_VOID(overlayNode);
789         if (overlayNode) {
790             overlayNode->SwitchToOverlayMode();
791         }
792     };
793     if (afterRender) {
794         auto pipeline = host->GetContext();
795         CHECK_NULL_VOID(pipeline);
796         pipeline->AddAfterRenderTask(switchTask);
797     } else {
798         switchTask();
799     }
800 }
801 } // namespace OHOS::Ace::NG
802