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