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