1 /*
2  * Copyright (c) 2022-2024 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/indexer/indexer_pattern.h"
17 #if !defined(PREVIEW) && !defined(ACE_UNITTEST) && defined(OHOS_PLATFORM)
18 #include "interfaces/inner_api/ui_session/ui_session_manager.h"
19 #endif
20 
21 #include "base/geometry/dimension.h"
22 #include "base/geometry/ng/size_t.h"
23 #include "base/log/dump_log.h"
24 #include "base/memory/ace_type.h"
25 #include "base/memory/referenced.h"
26 #include "base/utils/utils.h"
27 #include "bridge/common/utils/utils.h"
28 #include "core/animation/animator.h"
29 #include "core/common/container.h"
30 #include "core/common/font_manager.h"
31 #include "core/common/vibrator/vibrator_utils.h"
32 #include "core/components/common/layout/constants.h"
33 #include "core/components/common/properties/color.h"
34 #include "core/components/common/properties/popup_param.h"
35 #include "core/components/common/properties/shadow_config.h"
36 #include "core/components/indexer/indexer_theme.h"
37 #include "core/components/theme/shadow_theme.h"
38 #include "core/components_ng/base/frame_node.h"
39 #include "core/components_ng/pattern/divider/divider_pattern.h"
40 #include "core/components_ng/pattern/indexer/indexer_theme.h"
41 #include "core/components_ng/pattern/linear_layout/linear_layout_pattern.h"
42 #include "core/components_ng/pattern/linear_layout/linear_layout_property.h"
43 #include "core/components_ng/pattern/list/list_event_hub.h"
44 #include "core/components_ng/pattern/list/list_item_layout_property.h"
45 #include "core/components_ng/pattern/list/list_item_pattern.h"
46 #include "core/components_ng/pattern/list/list_layout_property.h"
47 #include "core/components_ng/pattern/list/list_pattern.h"
48 #include "core/components_ng/pattern/stack/stack_pattern.h"
49 #include "core/components_ng/pattern/text/text_layout_property.h"
50 #include "core/components_ng/pattern/text/text_model.h"
51 #include "core/components_ng/pattern/text/text_pattern.h"
52 #include "core/components_ng/property/border_property.h"
53 #include "core/components_ng/property/calc_length.h"
54 #include "core/components_ng/property/measure_property.h"
55 #include "core/components_ng/property/measure_utils.h"
56 #include "core/components_ng/property/property.h"
57 #include "core/components_v2/inspector/inspector_constants.h"
58 #include "core/components_v2/list/list_properties.h"
59 #include "core/event/mouse_event.h"
60 #include "core/pipeline_ng/pipeline_context.h"
61 
62 namespace OHOS::Ace::NG {
63 namespace {
64 constexpr int32_t TOTAL_NUMBER = 1000;
65 constexpr double PERCENT_100 = 100.0;
66 constexpr int32_t MODE_SEVEN = 6; // items is divided into 6 groups in (7 + #) mode
67 constexpr int32_t MODE_FIVE = 4; // items is divided into 4 groups in (5 + #) mode
68 }
OnModifyDone()69 void IndexerPattern::OnModifyDone()
70 {
71     Pattern::OnModifyDone();
72     auto host = GetHost();
73     CHECK_NULL_VOID(host);
74     auto layoutProperty = host->GetLayoutProperty<IndexerLayoutProperty>();
75     CHECK_NULL_VOID(layoutProperty);
76 
77     enableHapticFeedback_ = layoutProperty->GetEnableHapticFeedback().value_or(true);
78     bool autoCollapseModeChanged = true;
79     bool itemCountChanged = false;
80     InitArrayValue(autoCollapseModeChanged, itemCountChanged);
81     BuildArrayValueItems();
82     bool removeBubble = false;
83     auto usePopup = layoutProperty->GetUsingPopup().value_or(false);
84     if (isPopup_ != usePopup) {
85         isPopup_ = usePopup;
86         removeBubble = !isPopup_;
87     }
88     // Remove bubble if auto-collapse mode switched on/off or if items count changed
89     removeBubble = removeBubble || autoCollapseModeChanged || itemCountChanged;
90     if (removeBubble) {
91         RemoveBubble();
92     }
93 
94     isNewHeightCalculated_ = false;
95     auto itemSize =
96         layoutProperty->GetItemSize().value_or(Dimension(INDEXER_ITEM_SIZE, DimensionUnit::VP)).ConvertToPx();
97     auto indexerSizeChanged = (itemCountChanged || !NearEqual(itemSize, lastItemSize_));
98     lastItemSize_ = itemSize;
99     auto needMarkDirty = (layoutProperty->GetPropertyChangeFlag() == PROPERTY_UPDATE_NORMAL);
100     ApplyIndexChanged(needMarkDirty, initialized_ && selectChanged_, false, indexerSizeChanged);
101     auto gesture = host->GetOrCreateGestureEventHub();
102     if (gesture) {
103         InitPanEvent(gesture);
104         InitTouchEvent(gesture);
105     }
106     InitInputEvent();
107     InitOnKeyEvent();
108     SetAccessibilityAction();
109 }
110 
InitArrayValue(bool & autoCollapseModeChanged,bool & itemCountChanged)111 void IndexerPattern::InitArrayValue(bool& autoCollapseModeChanged, bool& itemCountChanged)
112 {
113     auto host = GetHost();
114     CHECK_NULL_VOID(host);
115     auto layoutProperty = host->GetLayoutProperty<IndexerLayoutProperty>();
116     CHECK_NULL_VOID(layoutProperty);
117     auto autoCollapse = layoutProperty->GetAutoCollapse().value_or(true);
118     if (!isNewHeightCalculated_) {
119         autoCollapseModeChanged = autoCollapse != lastAutoCollapse_;
120         lastAutoCollapse_ = autoCollapse;
121         auto newArray = layoutProperty->GetArrayValue().value_or(std::vector<std::string>());
122         bool arrayValueChanged = newArray.size() != fullArrayValue_.size() || newArray != fullArrayValue_;
123         if (arrayValueChanged || autoCollapseModeChanged) {
124             lastCollapsingMode_ = IndexerCollapsingMode::INVALID;
125         }
126         fullArrayValue_ = newArray;
127     }
128     auto propSelect = layoutProperty->GetSelected().value();
129     if (fullArrayValue_.size() > 0) {
130         if (autoCollapse) {
131             sharpItemCount_ = fullArrayValue_.at(0) == StringUtils::Str16ToStr8(INDEXER_STR_SHARP) ? 1 : 0;
132             CollapseArrayValue();
133             if ((lastCollapsingMode_ == IndexerCollapsingMode::SEVEN ||
134                     lastCollapsingMode_ == IndexerCollapsingMode::FIVE) &&
135                 (propSelect > sharpItemCount_)) {
136                 propSelect = GetAutoCollapseIndex(propSelect);
137             }
138         } else {
139             sharpItemCount_ = 0;
140             BuildFullArrayValue();
141         }
142         itemCountChanged = (itemCount_ != static_cast<int32_t>(arrayValue_.size()));
143         itemCount_ = static_cast<int32_t>(arrayValue_.size());
144     } else {
145         sharpItemCount_ = 0;
146         itemCountChanged = (itemCount_ != 0);
147         itemCount_ = 0;
148         arrayValue_.clear();
149     }
150     if (propSelect != selected_) {
151         selected_ = propSelect;
152         selectChanged_ = true;
153         ResetStatus();
154     } else if (!isNewHeightCalculated_) {
155         selectChanged_ = false;
156     }
157 }
158 
InitTouchEvent(const RefPtr<GestureEventHub> & gestureHub)159 void IndexerPattern::InitTouchEvent(const RefPtr<GestureEventHub>& gestureHub)
160 {
161     if (!touchListener_) {
162         CHECK_NULL_VOID(gestureHub);
163         auto touchCallback = [weak = WeakClaim(this)](const TouchEventInfo& info) {
164             auto indexerPattern = weak.Upgrade();
165             CHECK_NULL_VOID(indexerPattern);
166             TouchType touchType = info.GetTouches().front().GetTouchType();
167             if (touchType == TouchType::DOWN) {
168                 indexerPattern->isTouch_ = true;
169                 indexerPattern->OnTouchDown(info);
170             } else if (touchType == TouchType::UP || touchType == TouchType::CANCEL) {
171                 indexerPattern->isTouch_ = false;
172                 indexerPattern->OnTouchUp(info);
173             }
174         };
175         touchListener_ = MakeRefPtr<TouchEventImpl>(std::move(touchCallback));
176         gestureHub->AddTouchEvent(touchListener_);
177     }
178 }
179 
OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper> & dirty,const DirtySwapConfig & config)180 bool IndexerPattern::OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper>& dirty, const DirtySwapConfig& config)
181 {
182     if (config.skipMeasure && config.skipLayout) {
183         initialized_ = true;
184         return false;
185     }
186     auto layoutAlgorithmWrapper = DynamicCast<LayoutAlgorithmWrapper>(dirty->GetLayoutAlgorithm());
187     CHECK_NULL_RETURN(layoutAlgorithmWrapper, false);
188     auto indexerLayoutAlgorithm = DynamicCast<IndexerLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
189     CHECK_NULL_RETURN(indexerLayoutAlgorithm, false);
190     itemHeight_ = indexerLayoutAlgorithm->GetItemHeight();
191     auto height = indexerLayoutAlgorithm->GetMaxContentHeight();
192     if (maxContentHeight_ != height && lastAutoCollapse_) {
193         maxContentHeight_ = height;
194         isNewHeightCalculated_ = true;
195         auto hostNode = dirty->GetHostNode();
196         StartCollapseDelayTask(hostNode, INDEXER_COLLAPSE_WAIT_DURATION);
197     } else {
198         initialized_ = true;
199     }
200     return true;
201 }
202 
BuildArrayValueItems()203 void IndexerPattern::BuildArrayValueItems()
204 {
205     int32_t indexerSize = static_cast<int32_t>(arrayValue_.size());
206     auto host = GetHost();
207     CHECK_NULL_VOID(host);
208     auto layoutProperty = host->GetLayoutProperty<IndexerLayoutProperty>();
209     CHECK_NULL_VOID(layoutProperty);
210     auto children = host->GetChildren();
211     auto lastChildCount = static_cast<int32_t>(children.size());
212     if (layoutProperty->GetIsPopupValue(false)) {
213         lastChildCount -= 1;
214     }
215     if (indexerSize != lastChildCount) {
216         host->Clean();
217         layoutProperty->UpdateIsPopup(false);
218         for (int32_t index = 0; index < indexerSize; index++) {
219             auto indexerChildNode = FrameNode::CreateFrameNode(
220                 V2::TEXT_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<TextPattern>());
221             CHECK_NULL_VOID(indexerChildNode);
222             InitChildInputEvent(indexerChildNode, index);
223             host->AddChild(indexerChildNode);
224         }
225     }
226     std::vector<std::string> arrayValueStrs;
227     for (auto indexerItem : arrayValue_) {
228         arrayValueStrs.push_back(indexerItem.first);
229     }
230     layoutProperty->UpdateActualArrayValue(arrayValueStrs);
231 }
232 
BuildFullArrayValue()233 void IndexerPattern::BuildFullArrayValue()
234 {
235     arrayValue_.clear();
236     autoCollapse_ = false;
237     for (auto indexerLetter : fullArrayValue_) {
238         arrayValue_.push_back(std::pair(indexerLetter, false));
239     }
240 }
241 
CollapseArrayValue()242 void IndexerPattern::CollapseArrayValue()
243 {
244     auto host = GetHost();
245     CHECK_NULL_VOID(host);
246     auto layoutProperty = host->GetLayoutProperty<IndexerLayoutProperty>();
247     CHECK_NULL_VOID(layoutProperty);
248     auto geometryNode = host->GetGeometryNode();
249     CHECK_NULL_VOID(geometryNode);
250     auto itemSize =
251         layoutProperty->GetItemSize().value_or(Dimension(INDEXER_ITEM_SIZE, DimensionUnit::VP)).ConvertToVp();
252     int32_t maxItemsCount = 0;
253     auto height = Dimension(maxContentHeight_, DimensionUnit::PX).ConvertToVp();
254     if (height > 0 && itemSize > 0) {
255         maxItemsCount = static_cast<int32_t>(height / itemSize);
256     }
257     int32_t fullArraySize = static_cast<int32_t>(fullArrayValue_.size());
258     if (NearZero(height) || maxItemsCount >= fullArraySize ||
259         fullArraySize - sharpItemCount_ <= INDEXER_NINE_CHARACTERS_CHECK) {
260         if (lastCollapsingMode_ != IndexerCollapsingMode::NONE) {
261             lastCollapsingMode_ = IndexerCollapsingMode::NONE;
262             BuildFullArrayValue();
263         }
264     } else if (fullArraySize - sharpItemCount_ <= INDEXER_THIRTEEN_CHARACTERS_CHECK) {
265         if (lastCollapsingMode_ != IndexerCollapsingMode::FIVE) {
266             lastCollapsingMode_ = IndexerCollapsingMode::FIVE;
267             ApplyFivePlusOneMode(fullArraySize);
268         }
269     } else {
270         // 13 here is count of visible items in 7 + 1 mode (i.e. 7 characters 6 dots and # item if exists)
271         if (maxItemsCount >= INDEXER_THIRTEEN_CHARACTERS_CHECK + sharpItemCount_) {
272             if (lastCollapsingMode_ != IndexerCollapsingMode::SEVEN) {
273                 lastCollapsingMode_ = IndexerCollapsingMode::SEVEN;
274                 ApplySevenPlusOneMode(fullArraySize);
275             }
276         } else {
277             if (lastCollapsingMode_ != IndexerCollapsingMode::FIVE) {
278                 lastCollapsingMode_ = IndexerCollapsingMode::FIVE;
279                 ApplyFivePlusOneMode(fullArraySize);
280             }
281         }
282     }
283 }
284 
ApplySevenPlusOneMode(int32_t fullArraySize)285 void IndexerPattern::ApplySevenPlusOneMode(int32_t fullArraySize)
286 {
287     // 7 + # mode
288     // minimum items in one group (totally 6 groups) including
289     // visible character in the group and excluding the first always visible item
290     auto cmin = static_cast<int32_t>((fullArraySize - 1 - sharpItemCount_) / 6);
291     auto gmax = (fullArraySize - 1 - sharpItemCount_) - cmin * 6; // number of groups with maximum items count
292     auto cmax = cmin + 1; // maximum items in one group including visible character in the group
293     auto gmin = 6 - gmax; // number of groups with minimum items count
294 
295     arrayValue_.clear();
296     arrayValue_.push_back(std::pair(fullArrayValue_.at(0), false)); // push the first item
297     if (sharpItemCount_ > 0) {
298         arrayValue_.push_back(std::pair(fullArrayValue_.at(1), false)); // push the second item if the first is #
299     }
300 
301     auto lastPushedIndex = sharpItemCount_;
302 
303     for (int32_t groupIndex = 0; groupIndex < gmin; groupIndex++) { // push groups of minimum items count
304         int32_t firstIndex = lastPushedIndex + 1;
305         int32_t lastIndex = firstIndex + cmin - 1;
306         arrayValue_.push_back(std::pair(fullArrayValue_.at(firstIndex), true));
307         arrayValue_.push_back(std::pair(fullArrayValue_.at(lastIndex), false));
308         lastPushedIndex = lastIndex;
309     }
310 
311     for (int32_t groupIndex = 0; groupIndex < gmax; groupIndex++) { // push groups of maximum items count
312         int32_t firstIndex = lastPushedIndex + 1;
313         int32_t lastIndex = firstIndex + cmax - 1;
314         arrayValue_.push_back(std::pair(fullArrayValue_.at(firstIndex), true));
315         arrayValue_.push_back(std::pair(fullArrayValue_.at(lastIndex), false));
316         lastPushedIndex = lastIndex;
317     }
318     autoCollapse_ = true;
319 }
320 
ApplyFivePlusOneMode(int32_t fullArraySize)321 void IndexerPattern::ApplyFivePlusOneMode(int32_t fullArraySize)
322 {
323     // 5 + # mode
324     // minimum items in one group (totally 4 groups) including
325     // visible character in the group and excluding the first always visible item and # item if exists
326     auto cmin = static_cast<int32_t>((fullArraySize - 1 - sharpItemCount_) / 4);
327     auto gmax = (fullArraySize - 1 - sharpItemCount_) - cmin * 4; // number of groups with maximum items count
328     auto cmax = cmin + 1; // maximum items in one group including visible character in the group
329     auto gmin = 4 - gmax; // number of groups with minimum items count
330 
331     arrayValue_.clear();
332     arrayValue_.push_back(std::pair(fullArrayValue_.at(0), false)); // push the first item
333     if (sharpItemCount_ > 0) {
334         arrayValue_.push_back(std::pair(fullArrayValue_.at(1), false)); // push the second item if the first is #
335     }
336 
337     auto lastPushedIndex = sharpItemCount_;
338 
339     for (int32_t groupIndex = 0; groupIndex < gmin; groupIndex++) { // push groups of minimum items count
340         int32_t firstIndex = lastPushedIndex + 1;
341         int32_t lastIndex = firstIndex + cmin - 1;
342         arrayValue_.push_back(std::pair(fullArrayValue_.at(firstIndex), true));
343         arrayValue_.push_back(std::pair(fullArrayValue_.at(lastIndex), false));
344         lastPushedIndex = lastIndex;
345     }
346 
347     for (int32_t groupIndex = 0; groupIndex < gmax; groupIndex++) { // push groups of maximum items count
348         int32_t firstIndex = lastPushedIndex + 1;
349         int32_t lastIndex = firstIndex + cmax - 1;
350         arrayValue_.push_back(std::pair(fullArrayValue_.at(firstIndex), true));
351         arrayValue_.push_back(std::pair(fullArrayValue_.at(lastIndex), false));
352         lastPushedIndex = lastIndex;
353     }
354     autoCollapse_ = true;
355 }
356 
GetAutoCollapseIndex(int32_t propSelect)357 int32_t IndexerPattern::GetAutoCollapseIndex(int32_t propSelect)
358 {
359     int32_t fullArraySize = static_cast<int32_t>(fullArrayValue_.size());
360     int32_t index = sharpItemCount_;
361     int32_t mode = MODE_FIVE;
362     propSelect -= sharpItemCount_;
363     if (lastCollapsingMode_ == IndexerCollapsingMode::SEVEN) {
364         mode = MODE_SEVEN;
365     }
366     // minimum items in one group including
367     // visible character in the group and excluding the first always visible item and # item if exists
368     auto cmin = static_cast<int32_t>((fullArraySize - 1 - sharpItemCount_) / mode);
369     auto gmax = (fullArraySize - 1 - sharpItemCount_) - cmin * mode; // number of groups with maximum items count
370     auto cmax = cmin + 1; // maximum items in one group including visible character in the group
371     auto gmin = mode - gmax; // number of groups with minimum items count
372     if (propSelect > gmin * cmin) {
373         index += gmin * 2; // one group includes two index
374         propSelect -= gmin * cmin;
375         index += propSelect / cmax * 2 + (propSelect % cmax == 0 ? 0 : 1);
376     } else {
377         index += propSelect / cmin * 2 + (propSelect % cmin == 0 ? 0 : 1);
378     }
379     return  index;
380 }
381 
InitPanEvent(const RefPtr<GestureEventHub> & gestureHub)382 void IndexerPattern::InitPanEvent(const RefPtr<GestureEventHub>& gestureHub)
383 {
384     if (panEvent_) {
385         return;
386     }
387     auto onActionStart = [weak = WeakClaim(this)](const GestureEvent& info) {
388         auto pattern = weak.Upgrade();
389         if (pattern) {
390             if (info.GetInputEventType() == InputEventType::AXIS) {
391                 return;
392             }
393             pattern->MoveIndexByOffset(info.GetLocalLocation());
394         }
395     };
396 
397     auto onActionUpdate = [weak = WeakClaim(this)](const GestureEvent& info) {
398         auto pattern = weak.Upgrade();
399         CHECK_NULL_VOID(pattern);
400         if (info.GetInputEventType() == InputEventType::AXIS) {
401             if (GreatNotEqual(info.GetMainDelta(), 0.0)) {
402                 pattern->MoveIndexByStep(-1);
403             } else if (LessNotEqual(info.GetMainDelta(), 0.0)) {
404                 pattern->MoveIndexByStep(1);
405             }
406         } else {
407             pattern->MoveIndexByOffset(info.GetLocalLocation());
408         }
409     };
410 
411     auto onActionEnd = [weak = WeakClaim(this)](const GestureEvent& info) {};
412 
413     auto onActionCancel = [weak = WeakClaim(this)]() {};
414 
415     PanDirection panDirection;
416     panDirection.type = PanDirection::VERTICAL;
417     panEvent_ = MakeRefPtr<PanEvent>(
418         std::move(onActionStart), std::move(onActionUpdate), std::move(onActionEnd), std::move(onActionCancel));
419     gestureHub->AddPanEvent(panEvent_, panDirection, 1, 0.0_vp);
420 }
421 
OnHover(bool isHover)422 void IndexerPattern::OnHover(bool isHover)
423 {
424     if (itemCount_ <= 0) {
425         return;
426     }
427     if (isHover_ == isHover) {
428         return;
429     }
430     isHover_ = isHover;
431     isTouch_ = false;
432     if (isHover_) {
433         IndexerHoverInAnimation();
434     } else {
435         IndexerHoverOutAnimation();
436     }
437     ApplyIndexChanged(true, false);
438 }
439 
OnChildHover(int32_t index,bool isHover)440 void IndexerPattern::OnChildHover(int32_t index, bool isHover)
441 {
442     childHoverIndex_ = isHover ? index : -1;
443     ApplyIndexChanged(true, childHoverIndex_ >= 0 && childHoverIndex_ < itemCount_);
444 }
445 
OnPopupHover(bool isHover)446 void IndexerPattern::OnPopupHover(bool isHover)
447 {
448     isPopupHover_ = isHover;
449     if (isHover) {
450         delayTask_.Cancel();
451         StartBubbleAppearAnimation();
452     } else {
453         StartDelayTask(INDEXER_BUBBLE_ENTER_DURATION + INDEXER_BUBBLE_WAIT_DURATION);
454     }
455 }
456 
InitInputEvent()457 void IndexerPattern::InitInputEvent()
458 {
459     if (isInputEventRegisted_) {
460         return;
461     }
462     isInputEventRegisted_ = true;
463     InitCurrentInputEvent();
464 }
465 
InitCurrentInputEvent()466 void IndexerPattern::InitCurrentInputEvent()
467 {
468     auto host = GetHost();
469     CHECK_NULL_VOID(host);
470     auto hoverCallback = [weak = WeakClaim(this)](bool isHovered) {
471         auto pattern = weak.Upgrade();
472         CHECK_NULL_VOID(pattern);
473         pattern->OnHover(isHovered);
474     };
475     auto hoverEvent = MakeRefPtr<InputEvent>(hoverCallback);
476     auto inputGesture = host->GetOrCreateInputEventHub();
477     inputGesture->AddOnHoverEvent(hoverEvent);
478 }
479 
InitChildInputEvent(RefPtr<FrameNode> & itemNode,int32_t childIndex)480 void IndexerPattern::InitChildInputEvent(RefPtr<FrameNode>& itemNode, int32_t childIndex)
481 {
482     CHECK_NULL_VOID(itemNode);
483     auto childHoverCallback = [weak = WeakClaim(this), index = childIndex](bool isHovered) {
484         auto pattern = weak.Upgrade();
485         CHECK_NULL_VOID(pattern);
486         pattern->OnChildHover(index, isHovered);
487     };
488     auto childOnHoverEvent = MakeRefPtr<InputEvent>(childHoverCallback);
489     auto childInputEventHub = itemNode->GetOrCreateInputEventHub();
490     childInputEventHub->AddOnHoverEvent(childOnHoverEvent);
491 }
492 
InitPopupInputEvent()493 void IndexerPattern::InitPopupInputEvent()
494 {
495     CHECK_NULL_VOID(popupNode_);
496     auto popupHoverCallback = [weak = WeakClaim(this)](bool isHovered) {
497         auto pattern = weak.Upgrade();
498         CHECK_NULL_VOID(pattern);
499         pattern->OnPopupHover(isHovered);
500     };
501     auto popupOnHoverEvent = MakeRefPtr<InputEvent>(popupHoverCallback);
502     auto popupInputEventHub = popupNode_->GetOrCreateInputEventHub();
503     popupInputEventHub->AddOnHoverEvent(popupOnHoverEvent);
504 }
505 
InitPopupPanEvent()506 void IndexerPattern::InitPopupPanEvent()
507 {
508     CHECK_NULL_VOID(popupNode_);
509     auto gestureHub = popupNode_->GetOrCreateGestureEventHub();
510     CHECK_NULL_VOID(gestureHub);
511     PanDirection panDirection;
512     panDirection.type = PanDirection::ALL;
513     auto panEvent = MakeRefPtr<PanEvent>(nullptr, nullptr, nullptr, nullptr);
514     gestureHub->AddPanEvent(panEvent, panDirection, 1, 0.0_vp);
515 }
516 
OnTouchDown(const TouchEventInfo & info)517 void IndexerPattern::OnTouchDown(const TouchEventInfo& info)
518 {
519     TAG_LOGI(AceLogTag::ACE_ALPHABET_INDEXER, "touch down at alphabetIndexer");
520     if (itemCount_ <= 0) {
521         return;
522     }
523     MoveIndexByOffset(info.GetTouches().front().GetLocalLocation());
524 }
525 
OnTouchUp(const TouchEventInfo & info)526 void IndexerPattern::OnTouchUp(const TouchEventInfo& info)
527 {
528     TAG_LOGI(AceLogTag::ACE_ALPHABET_INDEXER, "leave up from alphabetIndexer");
529     if (itemCount_ <= 0) {
530         return;
531     }
532     childPressIndex_ = -1;
533     if (isHover_) {
534         IndexerPressOutAnimation();
535     }
536     ResetStatus();
537     ApplyIndexChanged(true, true, true);
538     OnSelect();
539 }
540 
MoveIndexByOffset(const Offset & offset)541 void IndexerPattern::MoveIndexByOffset(const Offset& offset)
542 {
543     if (itemHeight_ <= 0) {
544         return;
545     }
546     if (itemCount_ <= 0) {
547         return;
548     }
549     auto nextSelectIndex = GetSelectChildIndex(offset);
550     if (nextSelectIndex == childPressIndex_ || nextSelectIndex == -1) {
551         return;
552     }
553     childPressIndex_ = nextSelectIndex;
554     selected_ = nextSelectIndex;
555     selectedChangedForHaptic_ = lastSelected_ != selected_;
556     lastSelected_ = nextSelectIndex;
557     FireOnSelect(selected_, true);
558     if (isHover_ && childPressIndex_ >= 0) {
559         IndexerPressInAnimation();
560     }
561     childFocusIndex_ = -1;
562     childHoverIndex_ = -1;
563     ApplyIndexChanged(true, true);
564 }
565 
GetSelectChildIndex(const Offset & offset)566 int32_t IndexerPattern::GetSelectChildIndex(const Offset& offset)
567 {
568     auto host = GetHost();
569     CHECK_NULL_RETURN(host, -1);
570     auto layoutProperty = host->GetLayoutProperty<IndexerLayoutProperty>();
571     CHECK_NULL_RETURN(layoutProperty, -1);
572     int32_t index = 0;
573     for (auto child : host->GetChildren()) {
574         auto childNode = DynamicCast<FrameNode>(child);
575         CHECK_NULL_RETURN(childNode, -1);
576         auto geometryNode = childNode->GetGeometryNode();
577         CHECK_NULL_RETURN(geometryNode, -1);
578         auto childOffset = geometryNode->GetFrameOffset();
579         if (index == 0 && LessNotEqual(offset.GetY(), childOffset.GetY())) {
580             return -1;
581         }
582         if (GreatOrEqual(offset.GetY(), childOffset.GetY()) &&
583             LessNotEqual(offset.GetY(), childOffset.GetY() + itemHeight_)) {
584             break;
585         }
586         index++;
587     }
588     return index < itemCount_ ? index : -1;
589 }
590 
KeyIndexByStep(int32_t step)591 bool IndexerPattern::KeyIndexByStep(int32_t step)
592 {
593     auto nextSected = GetSkipChildIndex(step);
594     if (childFocusIndex_ == nextSected || nextSected == -1) {
595         return false;
596     }
597     childFocusIndex_ = nextSected;
598     auto refreshBubble = nextSected >= 0 && nextSected < itemCount_;
599     if (refreshBubble) {
600         selected_ = nextSected;
601         selectedChangedForHaptic_ = lastSelected_ != selected_;
602         lastSelected_ = nextSected;
603     }
604     childPressIndex_ = -1;
605     childHoverIndex_ = -1;
606     ApplyIndexChanged(true, refreshBubble);
607     OnSelect();
608     return nextSected >= 0;
609 }
610 
GetSkipChildIndex(int32_t step)611 int32_t IndexerPattern::GetSkipChildIndex(int32_t step)
612 {
613     auto nextSelected = selected_ + step;
614     if (nextSelected < 0 || nextSelected >= itemCount_) {
615         return -1;
616     }
617     return nextSelected;
618 }
619 
MoveIndexByStep(int32_t step)620 bool IndexerPattern::MoveIndexByStep(int32_t step)
621 {
622     auto nextSected = GetSkipChildIndex(step);
623     if (selected_ == nextSected || nextSected == -1) {
624         return false;
625     }
626     selected_ = nextSected;
627     ResetStatus();
628     ApplyIndexChanged(true, true);
629     OnSelect();
630     return nextSected >= 0;
631 }
632 
MoveIndexBySearch(const std::string & searchStr)633 bool IndexerPattern::MoveIndexBySearch(const std::string& searchStr)
634 {
635     auto nextSelectIndex = GetFocusChildIndex(searchStr);
636     if (selected_ == nextSelectIndex || nextSelectIndex == -1) {
637         return false;
638     }
639     selected_ = nextSelectIndex;
640     childFocusIndex_ = nextSelectIndex;
641     childHoverIndex_ = -1;
642     childPressIndex_ = -1;
643     ApplyIndexChanged(true, true);
644     OnSelect();
645     return nextSelectIndex >= 0;
646 }
647 
GetFocusChildIndex(const std::string & searchStr)648 int32_t IndexerPattern::GetFocusChildIndex(const std::string& searchStr)
649 {
650     int32_t nextSelectIndex = -1;
651     for (auto i = selected_ + 1; i < itemCount_; ++i) {
652         const auto& indexValue = arrayValue_.at(i).first;
653         if (searchStr.length() > indexValue.length()) {
654             continue;
655         }
656         if (strcasecmp(indexValue.substr(0, searchStr.length()).c_str(), searchStr.c_str()) == 0) {
657             nextSelectIndex = i;
658             break;
659         }
660     }
661     if (nextSelectIndex >= 0 && nextSelectIndex < itemCount_) {
662         return nextSelectIndex;
663     }
664     for (auto i = 0; i < selected_; ++i) {
665         const auto& indexValue = arrayValue_.at(i).first;
666         if (searchStr.length() > indexValue.length()) {
667             continue;
668         }
669         if (strcasecmp(indexValue.substr(0, searchStr.length()).c_str(), searchStr.c_str()) == 0) {
670             nextSelectIndex = i;
671             break;
672         }
673     }
674     if (nextSelectIndex >= 0 && nextSelectIndex < itemCount_) {
675         return nextSelectIndex;
676     }
677     return -1;
678 }
679 
ResetStatus()680 void IndexerPattern::ResetStatus()
681 {
682     childHoverIndex_ = -1;
683     childFocusIndex_ = -1;
684     childPressIndex_ = -1;
685     popupClickedIndex_ = -1;
686 }
687 
OnSelect()688 void IndexerPattern::OnSelect()
689 {
690     auto host = GetHost();
691     CHECK_NULL_VOID(host);
692     FireOnSelect(selected_, false);
693     animateSelected_ = selected_;
694     if (animateSelected_ >= 0) {
695         auto selectedFrameNode = DynamicCast<FrameNode>(host->GetChildAtIndex(animateSelected_));
696         CHECK_NULL_VOID(selectedFrameNode);
697         ItemSelectedInAnimation(selectedFrameNode);
698     }
699     if (lastSelected_ >= 0 && lastSelected_ != animateSelected_) {
700         auto lastFrameNode = DynamicCast<FrameNode>(host->GetChildAtIndex(lastSelected_));
701         CHECK_NULL_VOID(lastFrameNode);
702         ItemSelectedOutAnimation(lastFrameNode);
703     }
704     selectedChangedForHaptic_ = lastSelected_ != selected_;
705     lastSelected_ = selected_;
706 }
707 
ApplyIndexChanged(bool isTextNodeInTree,bool selectChanged,bool fromTouchUp,bool indexerSizeChanged)708 void IndexerPattern::ApplyIndexChanged(
709     bool isTextNodeInTree, bool selectChanged, bool fromTouchUp, bool indexerSizeChanged)
710 {
711     auto host = GetHost();
712     CHECK_NULL_VOID(host);
713     auto layoutProperty = host->GetLayoutProperty<IndexerLayoutProperty>();
714     CHECK_NULL_VOID(layoutProperty);
715     if (layoutProperty->GetAdaptiveWidthValue(false)) {
716         host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF_AND_PARENT);
717     }
718     auto paintProperty = host->GetPaintProperty<IndexerPaintProperty>();
719     CHECK_NULL_VOID(paintProperty);
720     auto pipeline = PipelineContext::GetCurrentContext();
721     CHECK_NULL_VOID(pipeline);
722     auto indexerTheme = pipeline->GetTheme<IndexerTheme>();
723     CHECK_NULL_VOID(indexerTheme);
724 #ifndef ACE_UNITTEST
725     auto fontManager = pipeline->GetFontManager();
726     CHECK_NULL_VOID(fontManager);
727     const std::vector<std::string> customFonts = Framework::ConvertStrToFontFamilies(fontManager->GetAppCustomFont());
728 #else
729     const std::vector<std::string> customFonts;
730 #endif
731     int32_t index = 0;
732     auto total = host->GetTotalChildCount();
733     auto childrenNode = host->GetChildren();
734     if (layoutProperty->GetIsPopupValue(false)) {
735         total -= 1;
736     }
737     if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
738         auto indexerRenderContext = host->GetRenderContext();
739         CHECK_NULL_VOID(indexerRenderContext);
740         if (paintProperty->GetIndexerBorderRadius().has_value()) {
741             auto indexerRadius = paintProperty->GetIndexerBorderRadiusValue();
742             indexerRenderContext->UpdateBorderRadius({ indexerRadius, indexerRadius, indexerRadius, indexerRadius });
743         } else {
744             auto indexerRadius = Dimension(INDEXER_DEFAULT_RADIUS, DimensionUnit::VP);
745             indexerRenderContext->UpdateBorderRadius({ indexerRadius, indexerRadius, indexerRadius, indexerRadius });
746         }
747     }
748     for (int32_t i = 0; i < total; i++) {
749         auto childNode = host->GetChildByIndex(i)->GetHostNode();
750         UpdateChildBoundary(childNode);
751         auto nodeLayoutProperty = childNode->GetLayoutProperty<TextLayoutProperty>();
752         auto childRenderContext = childNode->GetRenderContext();
753         childRenderContext->SetClipToBounds(true);
754         auto nodeStr = autoCollapse_ && arrayValue_[index].second ?
755             StringUtils::Str16ToStr8(INDEXER_STR_DOT) : arrayValue_[index].first;
756         nodeLayoutProperty->UpdateContent(nodeStr);
757         nodeLayoutProperty->UpdateTextAlign(TextAlign::CENTER);
758         nodeLayoutProperty->UpdateAlignment(Alignment::CENTER);
759         nodeLayoutProperty->UpdateMinFontScale(1.0f);
760         nodeLayoutProperty->UpdateMaxFontScale(1.0f);
761         nodeLayoutProperty->UpdateMaxLines(1);
762         if (index == childHoverIndex_ || index == childPressIndex_) {
763             if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
764                 auto radiusSize = paintProperty->GetItemBorderRadius().has_value()
765                                         ? paintProperty->GetItemBorderRadiusValue()
766                                         : Dimension(INDEXER_ITEM_DEFAULT_RADIUS, DimensionUnit::VP);
767                 childRenderContext->UpdateBorderRadius({ radiusSize, radiusSize, radiusSize, radiusSize });
768                 childRenderContext->UpdateBackgroundColor(index == childHoverIndex_
769                                                             ? indexerTheme->GetHoverBgAreaColor()
770                                                             : indexerTheme->GetPressedBgAreaColor());
771             } else {
772                 auto radiusSize = indexerTheme->GetHoverRadiusSize();
773                 childRenderContext->UpdateBorderRadius({ radiusSize, radiusSize, radiusSize, radiusSize });
774                 childRenderContext->UpdateBackgroundColor(indexerTheme->GetHoverBgAreaColor());
775             }
776             if (selectChanged) {
777                 host->OnAccessibilityEvent(AccessibilityEventType::TEXT_CHANGE);
778             }
779         } else if (index == childFocusIndex_ || index == selected_) {
780             if (index == childFocusIndex_) {
781                 auto borderWidth = indexerTheme->GetFocusBgOutlineSize();
782                 nodeLayoutProperty->UpdateBorderWidth({ borderWidth, borderWidth, borderWidth, borderWidth });
783                 auto borderColor = indexerTheme->GetFocusBgOutlineColor();
784                 childRenderContext->UpdateBorderColor({ borderColor, borderColor, borderColor, borderColor });
785                 childRenderContext->UpdateBackgroundColor(
786                     paintProperty->GetSelectedBackgroundColor().value_or(indexerTheme->GetSeclectedBackgroundColor()));
787             } else {
788                 Dimension borderWidth;
789                 nodeLayoutProperty->UpdateBorderWidth({ borderWidth, borderWidth, borderWidth, borderWidth });
790                 if (!fromTouchUp || animateSelected_ == lastSelected_) {
791                     childRenderContext->UpdateBackgroundColor(paintProperty->GetSelectedBackgroundColor().value_or(
792                         indexerTheme->GetSeclectedBackgroundColor()));
793                 }
794                 childRenderContext->ResetBlendBorderColor();
795             }
796             nodeLayoutProperty->UpdateTextColor(
797                 layoutProperty->GetSelectedColor().value_or(indexerTheme->GetSelectedTextColor()));
798             if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
799                 auto radius = paintProperty->GetItemBorderRadius().has_value()
800                                     ? paintProperty->GetItemBorderRadiusValue()
801                                     : Dimension(INDEXER_ITEM_DEFAULT_RADIUS, DimensionUnit::VP);
802                 childRenderContext->UpdateBorderRadius({ radius, radius, radius, radius });
803             } else {
804                 auto radius = indexerTheme->GetHoverRadiusSize();
805                 childRenderContext->UpdateBorderRadius({ radius, radius, radius, radius });
806             }
807             auto selectedFont = layoutProperty->GetSelectedFont().value_or(indexerTheme->GetSelectTextStyle());
808             if ((!layoutProperty->GetSelectedFont().has_value() ||
809                     layoutProperty->GetSelectedFont().value().GetFontFamilies().empty()) &&
810                 !customFonts.empty()) {
811                 selectedFont.SetFontFamilies(customFonts);
812             }
813             nodeLayoutProperty->UpdateFontSize(selectedFont.GetFontSize());
814             auto fontWeight = selectedFont.GetFontWeight();
815             nodeLayoutProperty->UpdateFontWeight(fontWeight);
816             nodeLayoutProperty->UpdateFontFamily(selectedFont.GetFontFamilies());
817             nodeLayoutProperty->UpdateItalicFontStyle(selectedFont.GetFontStyle());
818             childNode->MarkModifyDone();
819             if (isTextNodeInTree) {
820                 childNode->MarkDirtyNode();
821             }
822             index++;
823             if (selectChanged) {
824                 AccessibilityEventType type = AccessibilityEventType::SELECTED;
825                 host->OnAccessibilityEvent(type);
826                 auto textAccessibilityProperty = childNode->GetAccessibilityProperty<TextAccessibilityProperty>();
827                 CHECK_NULL_VOID(textAccessibilityProperty);
828                 textAccessibilityProperty->SetSelected(true);
829             }
830             continue;
831         } else {
832             if (!fromTouchUp || animateSelected_ == lastSelected_ || index != lastSelected_) {
833                 childRenderContext->UpdateBackgroundColor(Color::TRANSPARENT);
834             }
835             if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
836                 auto radiusDefaultSize = Dimension(INDEXER_ITEM_DEFAULT_RADIUS, DimensionUnit::VP);
837                 childRenderContext->UpdateBorderRadius({ radiusDefaultSize, radiusDefaultSize,
838                     radiusDefaultSize, radiusDefaultSize });
839             } else {
840                 Dimension radiusZeroSize;
841                 childRenderContext->UpdateBorderRadius(
842                     { radiusZeroSize, radiusZeroSize, radiusZeroSize, radiusZeroSize });
843             }
844         }
845         Dimension borderWidth;
846         nodeLayoutProperty->UpdateBorderWidth({ borderWidth, borderWidth, borderWidth, borderWidth });
847         childRenderContext->ResetBlendBorderColor();
848         auto defaultFont = layoutProperty->GetFont().value_or(indexerTheme->GetDefaultTextStyle());
849         if ((!layoutProperty->GetFont().has_value() || layoutProperty->GetFont().value().GetFontFamilies().empty()) &&
850             !customFonts.empty()) {
851             defaultFont.SetFontFamilies(customFonts);
852         }
853         nodeLayoutProperty->UpdateFontSize(defaultFont.GetFontSize());
854         nodeLayoutProperty->UpdateFontFamily(defaultFont.GetFontFamilies());
855         nodeLayoutProperty->UpdateFontWeight(defaultFont.GetFontWeight());
856         nodeLayoutProperty->UpdateItalicFontStyle(defaultFont.GetFontStyle());
857         nodeLayoutProperty->UpdateTextColor(layoutProperty->GetColor().value_or(indexerTheme->GetDefaultTextColor()));
858         index++;
859         auto textAccessibilityProperty = childNode->GetAccessibilityProperty<TextAccessibilityProperty>();
860         if (textAccessibilityProperty) textAccessibilityProperty->SetSelected(false);
861         childNode->MarkModifyDone();
862         if (isTextNodeInTree) childNode->MarkDirtyNode();
863     }
864     if (selectChanged) {
865         ShowBubble(fromTouchUp);
866         if (enableHapticFeedback_ && selectedChangedForHaptic_ && !fromTouchUp) {
867             VibratorUtils::StartVibraFeedback();
868         }
869     }
870 }
871 
ShowBubble(bool fromTouchUp)872 void IndexerPattern::ShowBubble(bool fromTouchUp)
873 {
874     if (!NeedShowBubble() || itemCount_ < 1) {
875         return;
876     }
877     auto host = GetHost();
878     CHECK_NULL_VOID(host);
879     auto layoutProperty = host->GetLayoutProperty<IndexerLayoutProperty>();
880     CHECK_NULL_VOID(layoutProperty);
881     if (!popupNode_) {
882         popupNode_ = CreatePopupNode();
883         AddPopupTouchListener(popupNode_);
884         InitPopupInputEvent();
885         InitPopupPanEvent();
886         UpdatePopupOpacity(0.0f);
887     }
888     if (!layoutProperty->GetIsPopupValue(false)) {
889         popupNode_->MountToParent(host);
890         layoutProperty->UpdateIsPopup(true);
891     }
892     UpdateBubbleView();
893     delayTask_.Cancel();
894     if (!fromTouchUp) {
895         StartBubbleAppearAnimation();
896     }
897     if (!isTouch_) {
898         StartDelayTask(INDEXER_BUBBLE_ENTER_DURATION + INDEXER_BUBBLE_WAIT_DURATION);
899     }
900 }
901 
CreatePopupNode()902 RefPtr<FrameNode> IndexerPattern::CreatePopupNode()
903 {
904     auto columnNode = FrameNode::CreateFrameNode(V2::COLUMN_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(),
905         AceType::MakeRefPtr<LinearLayoutPattern>(true));
906     CHECK_NULL_RETURN(columnNode, nullptr);
907 
908     if (!autoCollapse_) {
909         auto letterNode = FrameNode::CreateFrameNode(
910             V2::TEXT_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<TextPattern>());
911         CHECK_NULL_RETURN(letterNode, nullptr);
912         auto letterStackNode = FrameNode::CreateFrameNode(
913             V2::STACK_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<StackPattern>());
914         CHECK_NULL_RETURN(letterStackNode, nullptr);
915         letterStackNode->AddChild(letterNode);
916         columnNode->AddChild(letterStackNode);
917     }
918     auto listNode = FrameNode::CreateFrameNode(
919         V2::LIST_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<ListPattern>());
920     CHECK_NULL_RETURN(listNode, nullptr);
921     auto listStackNode = FrameNode::CreateFrameNode(
922         V2::STACK_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<StackPattern>());
923     CHECK_NULL_RETURN(listStackNode, nullptr);
924     listStackNode->AddChild(listNode);
925     columnNode->AddChild(listStackNode);
926     return columnNode;
927 }
928 
UpdateBubbleView()929 void IndexerPattern::UpdateBubbleView()
930 {
931     CHECK_NULL_VOID(popupNode_);
932     auto host = GetHost();
933     CHECK_NULL_VOID(host);
934     auto columnLayoutProperty = popupNode_->GetLayoutProperty<LinearLayoutProperty>();
935     CHECK_NULL_VOID(columnLayoutProperty);
936     auto indexerEventHub = host->GetEventHub<IndexerEventHub>();
937     auto popListData = indexerEventHub->GetOnRequestPopupData();
938     auto actualIndex =
939         autoCollapse_ && selected_ > 0 && selected_ < itemCount_
940             ? std::find(fullArrayValue_.begin(), fullArrayValue_.end(), arrayValue_.at(selected_).first) -
941                   fullArrayValue_.begin()
942             : selected_;
943     auto actualChildIndex =
944         autoCollapse_ && childPressIndex_ > 0 && childPressIndex_ < itemCount_
945             ? std::find(fullArrayValue_.begin(), fullArrayValue_.end(), arrayValue_.at(childPressIndex_).first) -
946                   fullArrayValue_.begin()
947             : childPressIndex_;
948     auto currentListData =
949         popListData ? popListData(actualChildIndex >= 0 ? actualChildIndex : actualIndex) : std::vector<std::string>();
950     UpdateBubbleListView(currentListData);
951     UpdateBubbleLetterView(!currentListData.empty(), currentListData);
952     auto columnRenderContext = popupNode_->GetRenderContext();
953     CHECK_NULL_VOID(columnRenderContext);
954     if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
955         auto columnPadding = Dimension(BUBBLE_DIVIDER_SIZE, DimensionUnit::VP).ConvertToPx();
956         columnLayoutProperty->UpdatePadding({ CalcLength(0), CalcLength(0), CalcLength(columnPadding), CalcLength(0) });
957         auto paintProperty = host->GetPaintProperty<IndexerPaintProperty>();
958         CHECK_NULL_VOID(paintProperty);
959         if (paintProperty->GetPopupBorderRadius().has_value()) {
960             auto radius = paintProperty->GetPopupBorderRadiusValue();
961             columnRenderContext->UpdateBorderRadius({ radius, radius, radius, radius });
962         } else {
963             auto radius = Dimension(BUBBLE_RADIUS, DimensionUnit::VP);
964             columnRenderContext->UpdateBorderRadius({ radius, radius, radius, radius });
965         }
966         Shadow shadow = GetPopupShadow();
967         columnRenderContext->UpdateBackShadow(shadow);
968     } else {
969         auto radius = Dimension(BUBBLE_BOX_RADIUS, DimensionUnit::VP);
970         columnRenderContext->UpdateBorderRadius({ radius, radius, radius, radius });
971         columnRenderContext->UpdateBackShadow(Shadow::CreateShadow(ShadowStyle::OuterDefaultMD));
972     }
973     UpdateBubbleBackgroundView();
974     columnRenderContext->SetClipToBounds(true);
975     popupNode_->MarkModifyDone();
976     popupNode_->MarkDirtyNode();
977 }
978 
GetPopupShadow()979 Shadow IndexerPattern::GetPopupShadow()
980 {
981     Shadow shadow;
982     auto colorMode = SystemProperties::GetColorMode();
983     auto pipelineContext = GetContext();
984     CHECK_NULL_RETURN(pipelineContext, shadow);
985     auto shadowTheme = pipelineContext->GetTheme<ShadowTheme>();
986     CHECK_NULL_RETURN(shadowTheme, shadow);
987     shadow = shadowTheme->GetShadow(ShadowStyle::OuterDefaultLG, colorMode);
988     return shadow;
989 }
990 
UpdateBubbleBackgroundView()991 void IndexerPattern::UpdateBubbleBackgroundView()
992 {
993     if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
994         CHECK_NULL_VOID(popupNode_);
995         auto host = GetHost();
996         CHECK_NULL_VOID(host);
997         auto paintProperty = host->GetPaintProperty<IndexerPaintProperty>();
998         CHECK_NULL_VOID(paintProperty);
999         auto pipeline = PipelineContext::GetCurrentContext();
1000         CHECK_NULL_VOID(pipeline);
1001         auto indexerTheme = pipeline->GetTheme<IndexerTheme>();
1002         BlurStyleOption styleOption;
1003         if (paintProperty->GetPopupBackgroundBlurStyle().has_value()) {
1004             styleOption = paintProperty->GetPopupBackgroundBlurStyle().value();
1005         } else {
1006             styleOption.blurStyle = BlurStyle::COMPONENT_REGULAR;
1007         }
1008         auto bubbleRenderContext = popupNode_->GetRenderContext();
1009         CHECK_NULL_VOID(bubbleRenderContext);
1010         bubbleRenderContext->UpdateBackBlurStyle(styleOption);
1011         bubbleRenderContext->UpdateBackgroundColor(
1012             paintProperty->GetPopupBackground().value_or(indexerTheme->GetPopupBackgroundColor()));
1013     }
1014 }
1015 
UpdateBubbleSize()1016 void IndexerPattern::UpdateBubbleSize()
1017 {
1018     CHECK_NULL_VOID(popupNode_);
1019     auto host = GetHost();
1020     CHECK_NULL_VOID(host);
1021     auto columnLayoutProperty = popupNode_->GetLayoutProperty<LinearLayoutProperty>();
1022     CHECK_NULL_VOID(columnLayoutProperty);
1023     auto indexerEventHub = host->GetEventHub<IndexerEventHub>();
1024     auto popListData = indexerEventHub->GetOnRequestPopupData();
1025     auto actualIndex =
1026         autoCollapse_ && selected_ > 0
1027             ? std::find(fullArrayValue_.begin(), fullArrayValue_.end(), arrayValue_.at(selected_).first) -
1028                   fullArrayValue_.begin()
1029             : selected_;
1030     auto actualChildIndex =
1031         autoCollapse_ && childPressIndex_ > 0
1032             ? std::find(fullArrayValue_.begin(), fullArrayValue_.end(), arrayValue_.at(childPressIndex_).first) -
1033                   fullArrayValue_.begin()
1034             : childPressIndex_;
1035     auto currentListData =
1036         popListData ? popListData(actualChildIndex >= 0 ? actualChildIndex : actualIndex) : std::vector<std::string>();
1037     auto popupSize = autoCollapse_ ? currentListData.size() + 1 : currentListData.size();
1038 
1039     auto bubbleSize = Dimension(BUBBLE_BOX_SIZE, DimensionUnit::VP).ConvertToPx();
1040     auto columnCalcOffset = autoCollapse_ ? 0 : 1;
1041     if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
1042         auto maxItemsSize = autoCollapse_ ? INDEXER_BUBBLE_MAXSIZE_COLLAPSED_API_TWELVE : INDEXER_BUBBLE_MAXSIZE;
1043         auto bubbleHeight = Dimension(BUBBLE_ITEM_SIZE, DimensionUnit::VP).ConvertToPx();
1044         auto bubbleDivider = Dimension(BUBBLE_DIVIDER_SIZE, DimensionUnit::VP).ConvertToPx();
1045         auto columnCalcSize = CalcSize();
1046         if (popupSize <= maxItemsSize) {
1047             columnCalcSize = CalcSize(CalcLength(bubbleSize),
1048                 CalcLength((bubbleHeight + bubbleDivider) * (static_cast<int32_t>(popupSize) + columnCalcOffset) +
1049                            bubbleDivider));
1050         } else {
1051             columnCalcSize = CalcSize(CalcLength(bubbleSize),
1052                 CalcLength(Dimension(
1053                     autoCollapse_ ? BUBBLE_COLLAPSE_COLUMN_MAX_SIZE : BUBBLE_COLUMN_MAX_SIZE, DimensionUnit::VP)
1054                                 .ConvertToPx()));
1055         }
1056         columnLayoutProperty->UpdateUserDefinedIdealSize(columnCalcSize);
1057     } else {
1058         auto maxItemsSize = autoCollapse_ ? INDEXER_BUBBLE_MAXSIZE_COLLAPSED : INDEXER_BUBBLE_MAXSIZE;
1059         auto listActualSize = popupSize < maxItemsSize ? popupSize : maxItemsSize;
1060         auto columnCalcSize = CalcSize(
1061             CalcLength(bubbleSize),
1062             CalcLength(bubbleSize * (static_cast<int32_t>(listActualSize) + columnCalcOffset)));
1063         columnLayoutProperty->UpdateUserDefinedIdealSize(columnCalcSize);
1064     }
1065     popupNode_->MarkDirtyNode();
1066 }
1067 
UpdateBubbleLetterView(bool showDivider,std::vector<std::string> & currentListData)1068 void IndexerPattern::UpdateBubbleLetterView(bool showDivider, std::vector<std::string>& currentListData)
1069 {
1070     CHECK_NULL_VOID(popupNode_);
1071     auto host = GetHost();
1072     CHECK_NULL_VOID(host);
1073     auto pipeline = PipelineContext::GetCurrentContext();
1074     CHECK_NULL_VOID(pipeline);
1075     auto indexerTheme = pipeline->GetTheme<IndexerTheme>();
1076     CHECK_NULL_VOID(indexerTheme);
1077     auto paintProperty = host->GetPaintProperty<IndexerPaintProperty>();
1078     CHECK_NULL_VOID(paintProperty);
1079     auto layoutProperty = host->GetLayoutProperty<IndexerLayoutProperty>();
1080     CHECK_NULL_VOID(layoutProperty);
1081     auto letterNode = GetLetterNode();
1082     CHECK_NULL_VOID(letterNode);
1083     UpdateBubbleLetterStackAndLetterTextView();
1084     auto letterLayoutProperty = letterNode->GetLayoutProperty<TextLayoutProperty>();
1085     CHECK_NULL_VOID(letterLayoutProperty);
1086     auto letterNodeRenderContext = letterNode->GetRenderContext();
1087     if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
1088         auto bubbleSize = Dimension(BUBBLE_ITEM_SIZE, DimensionUnit::VP).ConvertToPx();
1089         letterLayoutProperty->UpdateUserDefinedIdealSize(CalcSize(CalcLength(bubbleSize), CalcLength(bubbleSize)));
1090         auto letterContext = letterNode->GetRenderContext();
1091         CHECK_NULL_VOID(letterContext);
1092         auto radius = paintProperty->GetPopupItemBorderRadius().has_value()
1093                             ? paintProperty->GetPopupItemBorderRadiusValue()
1094                             : Dimension(BUBBLE_ITEM_RADIUS, DimensionUnit::VP);
1095         letterContext->UpdateBorderRadius({ radius, radius, radius, radius });
1096         letterNodeRenderContext->UpdateBackgroundColor(paintProperty->GetPopupTitleBackground().value_or(
1097             currentListData.size() > 0 ? indexerTheme->GetPopupTitleBackground() : Color(POPUP_TITLE_BG_COLOR_SINGLE)));
1098     } else {
1099         auto bubbleSize = Dimension(BUBBLE_BOX_SIZE, DimensionUnit::VP).ConvertToPx();
1100         letterLayoutProperty->UpdateUserDefinedIdealSize(CalcSize(CalcLength(bubbleSize), CalcLength(bubbleSize)));
1101         letterNodeRenderContext->UpdateBackgroundColor(
1102             paintProperty->GetPopupBackground().value_or(indexerTheme->GetPopupBackgroundColor()));
1103         auto zeroWidth = Dimension();
1104         if (showDivider) {
1105             letterLayoutProperty->UpdateBorderWidth(
1106                 { zeroWidth, zeroWidth, zeroWidth, Dimension(INDEXER_LIST_DIVIDER) });
1107             auto boderColor = BorderColorProperty();
1108             boderColor.bottomColor = indexerTheme->GetPopupSeparateColor();
1109             letterNodeRenderContext->UpdateBorderColor(boderColor);
1110         } else {
1111             letterLayoutProperty->UpdateBorderWidth({ zeroWidth, zeroWidth, zeroWidth, zeroWidth });
1112         }
1113     }
1114     letterNodeRenderContext->SetClipToBounds(true);
1115     letterNode->MarkModifyDone();
1116     letterNode->MarkDirtyNode();
1117 }
1118 
UpdateBubbleLetterStackAndLetterTextView()1119 void IndexerPattern::UpdateBubbleLetterStackAndLetterTextView()
1120 {
1121     CHECK_NULL_VOID(popupNode_);
1122     auto host = GetHost();
1123     CHECK_NULL_VOID(host);
1124     auto pipeline = PipelineContext::GetCurrentContext();
1125     CHECK_NULL_VOID(pipeline);
1126     auto indexerTheme = pipeline->GetTheme<IndexerTheme>();
1127     CHECK_NULL_VOID(indexerTheme);
1128     auto fontManager = pipeline->GetFontManager();
1129     CHECK_NULL_VOID(fontManager);
1130     auto layoutProperty = host->GetLayoutProperty<IndexerLayoutProperty>();
1131     CHECK_NULL_VOID(layoutProperty);
1132     auto letterNode = GetLetterNode();
1133     CHECK_NULL_VOID(letterNode);
1134     auto letterLayoutProperty = letterNode->GetLayoutProperty<TextLayoutProperty>();
1135     CHECK_NULL_VOID(letterLayoutProperty);
1136     letterLayoutProperty->UpdateContent(arrayValue_[childPressIndex_ >= 0 ? childPressIndex_ : selected_].first);
1137     auto popupTextFont = layoutProperty->GetPopupFont().value_or(indexerTheme->GetPopupTextStyle());
1138     if ((!layoutProperty->GetPopupFont().has_value() ||
1139             layoutProperty->GetPopupFont().value().GetFontFamilies().empty()) &&
1140         fontManager->IsUseAppCustomFont()) {
1141         popupTextFont.SetFontFamilies(Framework::ConvertStrToFontFamilies(fontManager->GetAppCustomFont()));
1142     }
1143     letterLayoutProperty->UpdateMaxLines(1);
1144     letterLayoutProperty->UpdateFontSize(popupTextFont.GetFontSize());
1145     letterLayoutProperty->UpdateFontWeight(popupTextFont.GetFontWeight());
1146     letterLayoutProperty->UpdateFontFamily(popupTextFont.GetFontFamilies());
1147     letterLayoutProperty->UpdateItalicFontStyle(popupTextFont.GetFontStyle());
1148     letterLayoutProperty->UpdateTextColor(layoutProperty->GetPopupColor().value_or(indexerTheme->GetPopupTextColor()));
1149     letterLayoutProperty->UpdateTextAlign(TextAlign::CENTER);
1150     letterLayoutProperty->UpdateAlignment(Alignment::CENTER);
1151     letterLayoutProperty->UpdateMinFontScale(1.0f);
1152     letterLayoutProperty->UpdateMaxFontScale(1.0f);
1153     auto textPadding = Dimension(IndexerTheme::TEXT_PADDING_LEFT, DimensionUnit::VP).ConvertToPx();
1154     letterLayoutProperty->UpdatePadding(
1155         { CalcLength(textPadding), CalcLength(textPadding), CalcLength(0), CalcLength(0) });
1156 
1157     if (!autoCollapse_ && Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
1158         auto letterStackNode = DynamicCast<FrameNode>(popupNode_->GetFirstChild());
1159         CHECK_NULL_VOID(letterStackNode);
1160         auto letterStackLayoutProperty = letterStackNode->GetLayoutProperty<StackLayoutProperty>();
1161         CHECK_NULL_VOID(letterStackLayoutProperty);
1162         auto letterStackWidth = Dimension(BUBBLE_BOX_SIZE, DimensionUnit::VP).ConvertToPx();
1163         auto letterStackHeight = Dimension(BUBBLE_ITEM_SIZE + BUBBLE_DIVIDER_SIZE, DimensionUnit::VP).ConvertToPx();
1164         letterStackLayoutProperty->UpdateUserDefinedIdealSize(
1165             CalcSize(CalcLength(letterStackWidth), CalcLength(letterStackHeight)));
1166         auto letterStackPadding = Dimension(BUBBLE_DIVIDER_SIZE, DimensionUnit::VP).ConvertToPx();
1167         letterStackLayoutProperty->UpdatePadding({ CalcLength(letterStackPadding), CalcLength(letterStackPadding),
1168             CalcLength(0), CalcLength(letterStackPadding) });
1169     }
1170 }
1171 
GetLetterNode()1172 RefPtr<FrameNode> IndexerPattern::GetLetterNode()
1173 {
1174     CHECK_NULL_RETURN(popupNode_, nullptr);
1175     return autoCollapse_ ? GetAutoCollapseLetterNode()
1176                             : DynamicCast<FrameNode>(popupNode_->GetFirstChild()->GetFirstChild());
1177 }
1178 
GetAutoCollapseLetterNode()1179 RefPtr<FrameNode> IndexerPattern::GetAutoCollapseLetterNode()
1180 {
1181     CHECK_NULL_RETURN(popupNode_, nullptr);
1182     return DynamicCast<FrameNode>(popupNode_->GetLastChild()->GetFirstChild()->GetFirstChild()->GetFirstChild());
1183 }
1184 
UpdateBubbleListView(std::vector<std::string> & currentListData)1185 void IndexerPattern::UpdateBubbleListView(std::vector<std::string>& currentListData)
1186 {
1187     CHECK_NULL_VOID(popupNode_);
1188     if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
1189         CreateBubbleListView(currentListData);
1190     }
1191     auto listNode = DynamicCast<FrameNode>(popupNode_->GetLastChild()->GetFirstChild());
1192     CHECK_NULL_VOID(listNode);
1193     auto pipeline = PipelineContext::GetCurrentContext();
1194     CHECK_NULL_VOID(pipeline);
1195     auto indexerTheme = pipeline->GetTheme<IndexerTheme>();
1196     CHECK_NULL_VOID(indexerTheme);
1197     auto listPattern = DynamicCast<ListPattern>(listNode->GetPattern());
1198     listPattern->SetNeedLinked(false);
1199     auto listLayoutProperty = listNode->GetLayoutProperty<ListLayoutProperty>();
1200     CHECK_NULL_VOID(listLayoutProperty);
1201     UpdateBubbleListSize(currentListData);
1202     auto popupSize = autoCollapse_ ? currentListData.size() + 1 : currentListData.size();
1203     if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
1204         auto maxItemsSize = autoCollapse_ ? INDEXER_BUBBLE_MAXSIZE_COLLAPSED_API_TWELVE : INDEXER_BUBBLE_MAXSIZE;
1205         auto listPadding = Dimension(BUBBLE_DIVIDER_SIZE, DimensionUnit::VP).ConvertToPx();
1206         listLayoutProperty->UpdatePadding(
1207             { CalcLength(listPadding), CalcLength(listPadding), CalcLength(0), CalcLength(0) });
1208         UpdatePopupListGradientView(popupSize, maxItemsSize);
1209     }
1210     if (!currentListData.empty() || autoCollapse_) {
1211         UpdateBubbleListItem(currentListData, listNode, indexerTheme);
1212     } else {
1213         listNode->Clean();
1214     }
1215     auto divider = V2::ItemDivider();
1216     if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
1217         divider.strokeWidth = Dimension(BUBBLE_DIVIDER_SIZE, DimensionUnit::VP);
1218     } else {
1219         divider.strokeWidth = Dimension(INDEXER_LIST_DIVIDER, DimensionUnit::PX);
1220         divider.color = indexerTheme->GetPopupSeparateColor();
1221     }
1222     listLayoutProperty->UpdateDivider(divider);
1223     listLayoutProperty->UpdateListDirection(Axis::VERTICAL);
1224     auto listPaintProperty = listNode->GetPaintProperty<ScrollablePaintProperty>();
1225     CHECK_NULL_VOID(listPaintProperty);
1226     listPaintProperty->UpdateScrollBarMode(DisplayMode::OFF);
1227     auto listRenderContext = listNode->GetRenderContext();
1228     CHECK_NULL_VOID(listRenderContext);
1229     listRenderContext->SetClipToBounds(true);
1230     listNode->MarkModifyDone();
1231     listNode->MarkDirtyNode();
1232 }
1233 
UpdateBubbleListSize(std::vector<std::string> & currentListData)1234 void IndexerPattern::UpdateBubbleListSize(std::vector<std::string>& currentListData)
1235 {
1236     CHECK_NULL_VOID(popupNode_);
1237     currentPopupIndex_ = childPressIndex_ >= 0 ? childPressIndex_ : selected_;
1238     auto popupSize = autoCollapse_ ? currentListData.size() + 1 : currentListData.size();
1239     if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
1240         auto maxItemsSize = autoCollapse_ ? INDEXER_BUBBLE_MAXSIZE_COLLAPSED_API_TWELVE : INDEXER_BUBBLE_MAXSIZE;
1241         auto listActualSize = popupSize < maxItemsSize ? popupSize : maxItemsSize;
1242         lastPopupIndex_ = currentPopupIndex_;
1243         lastPopupSize_ = listActualSize;
1244         auto stackNode = DynamicCast<FrameNode>(popupNode_->GetLastChild());
1245         CHECK_NULL_VOID(stackNode);
1246         auto stackLayoutProperty = stackNode->GetLayoutProperty<StackLayoutProperty>();
1247         CHECK_NULL_VOID(stackLayoutProperty);
1248         auto listCalcSize = CalcBubbleListSize(popupSize, maxItemsSize);
1249         stackLayoutProperty->UpdateUserDefinedIdealSize(listCalcSize);
1250         auto listNode =  DynamicCast<FrameNode>(stackNode->GetFirstChild());
1251         CHECK_NULL_VOID(listNode);
1252         auto listLayoutProperty = listNode->GetLayoutProperty<ListLayoutProperty>();
1253         CHECK_NULL_VOID(listLayoutProperty);
1254         listLayoutProperty->UpdateUserDefinedIdealSize(listCalcSize);
1255     } else {
1256         auto maxItemsSize = autoCollapse_ ? INDEXER_BUBBLE_MAXSIZE_COLLAPSED : INDEXER_BUBBLE_MAXSIZE;
1257         auto listActualSize = popupSize < maxItemsSize ? popupSize : maxItemsSize;
1258         if (listActualSize != lastPopupSize_ || lastPopupIndex_ != currentPopupIndex_) {
1259             lastPopupIndex_ = currentPopupIndex_;
1260             CreateBubbleListView(currentListData);
1261             lastPopupSize_ = listActualSize;
1262         }
1263         auto bubbleSize = Dimension(BUBBLE_BOX_SIZE, DimensionUnit::VP).ConvertToPx();
1264         auto listNode = DynamicCast<FrameNode>(popupNode_->GetLastChild()->GetFirstChild());
1265         CHECK_NULL_VOID(listNode);
1266         auto listLayoutProperty = listNode->GetLayoutProperty<ListLayoutProperty>();
1267         CHECK_NULL_VOID(listLayoutProperty);
1268         listLayoutProperty->UpdateUserDefinedIdealSize(
1269             CalcSize(CalcLength(bubbleSize), CalcLength(bubbleSize * listActualSize)));
1270     }
1271 }
1272 
CreateBubbleListView(std::vector<std::string> & currentListData)1273 void IndexerPattern::CreateBubbleListView(std::vector<std::string>& currentListData)
1274 {
1275     CHECK_NULL_VOID(popupNode_);
1276     auto listNode = Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)
1277                         ? FrameNode::CreateFrameNode(V2::LIST_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(),
1278                             AceType::MakeRefPtr<ListPattern>())
1279                         : DynamicCast<FrameNode>(popupNode_->GetLastChild()->GetFirstChild());
1280     CHECK_NULL_VOID(listNode);
1281     if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
1282         auto stackNode = DynamicCast<FrameNode>(popupNode_->GetLastChild());
1283         CHECK_NULL_VOID(stackNode);
1284         stackNode->Clean();
1285     } else {
1286         listNode->Clean();
1287     }
1288 
1289     if (autoCollapse_) {
1290         auto letterNode = FrameNode::CreateFrameNode(
1291             V2::TEXT_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<TextPattern>());
1292         CHECK_NULL_VOID(letterNode);
1293         auto listItemNode =
1294             FrameNode::CreateFrameNode(V2::LIST_ITEM_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(),
1295                 AceType::MakeRefPtr<ListItemPattern>(nullptr, V2::ListItemStyle::NONE));
1296         listItemNode->AddChild(letterNode);
1297         listNode->AddChild(listItemNode);
1298     }
1299 
1300     for (uint32_t i = 0; i < currentListData.size(); i++) {
1301         auto listItemNode =
1302             FrameNode::CreateFrameNode(V2::LIST_ITEM_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(),
1303                 AceType::MakeRefPtr<ListItemPattern>(nullptr, V2::ListItemStyle::NONE));
1304         auto textNode = FrameNode::CreateFrameNode(
1305             V2::TEXT_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<TextPattern>());
1306         listItemNode->AddChild(textNode);
1307         AddListItemClickListener(listItemNode, i);
1308         listNode->AddChild(listItemNode);
1309     }
1310     if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
1311         auto stackNode = DynamicCast<FrameNode>(popupNode_->GetLastChild());
1312         CHECK_NULL_VOID(stackNode);
1313         stackNode->AddChild(listNode);
1314     }
1315 }
1316 
UpdatePopupListGradientView(int32_t popupSize,int32_t maxItemsSize)1317 void IndexerPattern::UpdatePopupListGradientView(int32_t popupSize, int32_t maxItemsSize)
1318 {
1319     CHECK_NULL_VOID(popupNode_);
1320     auto listNode = DynamicCast<FrameNode>(popupNode_->GetLastChild()->GetFirstChild());
1321     CHECK_NULL_VOID(listNode);
1322     if (popupSize > maxItemsSize) {
1323         DrawPopupListGradient(PopupListGradientStatus::BOTTOM);
1324         auto listEventHub = listNode->GetEventHub<ListEventHub>();
1325         CHECK_NULL_VOID(listEventHub);
1326         auto onScroll = [weak = WeakClaim(this)](Dimension offset, ScrollState state) {
1327             auto pattern = weak.Upgrade();
1328             CHECK_NULL_VOID(pattern);
1329             auto popupNode = pattern->popupNode_;
1330             CHECK_NULL_VOID(popupNode);
1331             auto listNode = DynamicCast<FrameNode>(popupNode->GetLastChild()->GetFirstChild());
1332             CHECK_NULL_VOID(listNode);
1333             auto listPattern = listNode->GetPattern<ListPattern>();
1334             CHECK_NULL_VOID(listPattern);
1335             if (listPattern->IsAtTop()) {
1336                 pattern->DrawPopupListGradient(PopupListGradientStatus::BOTTOM);
1337                 return;
1338             } else if (listPattern->IsAtBottom()) {
1339                 pattern->DrawPopupListGradient(PopupListGradientStatus::TOP);
1340                 return;
1341             } else {
1342                 pattern->DrawPopupListGradient(PopupListGradientStatus::BOTH);
1343                 return;
1344             }
1345         };
1346         listEventHub->SetOnScroll(onScroll);
1347     } else {
1348         DrawPopupListGradient(PopupListGradientStatus::NONE);
1349     }
1350 }
1351 
DrawPopupListGradient(PopupListGradientStatus gradientStatus)1352 void IndexerPattern::DrawPopupListGradient(PopupListGradientStatus gradientStatus)
1353 {
1354     CHECK_NULL_VOID(popupNode_);
1355     auto stackNode = DynamicCast<FrameNode>(popupNode_->GetLastChild());
1356     CHECK_NULL_VOID(stackNode);
1357     auto listNode = DynamicCast<FrameNode>(stackNode->GetFirstChild());
1358     auto listRenderContext = listNode->GetRenderContext();
1359     CHECK_NULL_VOID(listRenderContext);
1360     auto stackRenderContext = stackNode->GetRenderContext();
1361     CHECK_NULL_VOID(stackRenderContext);
1362     auto listStackHeight = autoCollapse_ ? BUBBLE_COLLAPSE_COLUMN_MAX_SIZE : BUBBLE_COLUMN_MAX_SIZE;
1363     auto gradientPercent = static_cast<float>(GRADIENT_COVER_HEIGHT / listStackHeight) ;
1364     NG::Gradient coverGradient;
1365     coverGradient.CreateGradientWithType(NG::GradientType::LINEAR);
1366     switch (gradientStatus) {
1367         case PopupListGradientStatus::TOP:
1368             coverGradient.AddColor(CreatePercentGradientColor(0, Color::TRANSPARENT));
1369             coverGradient.AddColor(CreatePercentGradientColor(gradientPercent, Color::WHITE));
1370             coverGradient.AddColor(CreatePercentGradientColor(1, Color::WHITE));
1371             break;
1372         case PopupListGradientStatus::BOTTOM:
1373             coverGradient.AddColor(CreatePercentGradientColor(0, Color::WHITE));
1374             coverGradient.AddColor(CreatePercentGradientColor(1 - gradientPercent, Color::WHITE));
1375             coverGradient.AddColor(CreatePercentGradientColor(1, Color::TRANSPARENT));
1376             break;
1377         case PopupListGradientStatus::BOTH:
1378             coverGradient.AddColor(CreatePercentGradientColor(0, Color::TRANSPARENT));
1379             coverGradient.AddColor(CreatePercentGradientColor(gradientPercent, Color::WHITE));
1380             coverGradient.AddColor(CreatePercentGradientColor(1 - gradientPercent, Color::WHITE));
1381             coverGradient.AddColor(CreatePercentGradientColor(1, Color::TRANSPARENT));
1382             break;
1383         case PopupListGradientStatus::NONE:
1384         default:
1385             coverGradient.AddColor(CreatePercentGradientColor(0, Color::WHITE));
1386             coverGradient.AddColor(CreatePercentGradientColor(1, Color::WHITE));
1387             break;
1388     }
1389     listRenderContext->UpdateBackBlendMode(BlendMode::SRC_IN);
1390     listRenderContext->UpdateBackBlendApplyType(BlendApplyType::OFFSCREEN);
1391     stackRenderContext->UpdateLinearGradient(coverGradient);
1392     stackRenderContext->UpdateBackBlendMode(BlendMode::SRC_OVER);
1393     stackRenderContext->UpdateBackBlendApplyType(BlendApplyType::OFFSCREEN);
1394 }
1395 
CreatePercentGradientColor(float percent,Color color)1396 GradientColor IndexerPattern::CreatePercentGradientColor(float percent, Color color)
1397 {
1398     NG::GradientColor gredient = GradientColor(color);
1399     gredient.SetDimension(CalcDimension(percent * PERCENT_100, DimensionUnit::PERCENT));
1400     return gredient;
1401 }
1402 
CalcBubbleListSize(int32_t popupSize,int32_t maxItemsSize)1403 CalcSize IndexerPattern::CalcBubbleListSize(int32_t popupSize, int32_t maxItemsSize)
1404 {
1405     auto bubbleSize = Dimension(BUBBLE_BOX_SIZE, DimensionUnit::VP).ConvertToPx();
1406     auto bubbleHeight = Dimension(BUBBLE_ITEM_SIZE, DimensionUnit::VP).ConvertToPx();
1407     auto bubbleDivider = Dimension(BUBBLE_DIVIDER_SIZE, DimensionUnit::VP).ConvertToPx();
1408     auto listCalcSize = CalcSize();
1409     if (popupSize <= maxItemsSize) {
1410         listCalcSize = CalcSize(
1411             CalcLength(bubbleSize),
1412             CalcLength((bubbleHeight + bubbleDivider) * static_cast<int32_t>(popupSize) - bubbleDivider));
1413     } else {
1414         if (autoCollapse_) {
1415             listCalcSize = CalcSize(
1416                 CalcLength(bubbleSize),
1417                 CalcLength(Dimension(BUBBLE_COLLAPSE_LIST_MAX_SIZE, DimensionUnit::VP).ConvertToPx()));
1418         } else {
1419             listCalcSize = CalcSize(
1420                 CalcLength(bubbleSize),
1421                 CalcLength(Dimension(BUBBLE_LIST_MAX_SIZE, DimensionUnit::VP).ConvertToPx()));
1422         }
1423     }
1424     return listCalcSize;
1425 }
1426 
UpdateBubbleListItem(std::vector<std::string> & currentListData,const RefPtr<FrameNode> & listNode,RefPtr<IndexerTheme> & indexerTheme)1427 void IndexerPattern::UpdateBubbleListItem(
1428     std::vector<std::string>& currentListData, const RefPtr<FrameNode>& listNode, RefPtr<IndexerTheme>& indexerTheme)
1429 {
1430     CHECK_NULL_VOID(listNode);
1431     CHECK_NULL_VOID(indexerTheme);
1432     auto layoutProperty = GetLayoutProperty<IndexerLayoutProperty>();
1433     CHECK_NULL_VOID(layoutProperty);
1434     auto paintProperty = GetPaintProperty<IndexerPaintProperty>();
1435     CHECK_NULL_VOID(paintProperty);
1436     auto popupSelectedTextColor =
1437         paintProperty->GetPopupSelectedColor().value_or(indexerTheme->GetPopupSelectedTextColor());
1438     auto popupUnselectedTextColor =
1439         paintProperty->GetPopupUnselectedColor().value_or(indexerTheme->GetPopupUnselectedTextColor());
1440     auto popupItemTextFontSize =
1441         layoutProperty->GetFontSize().value_or(indexerTheme->GetPopupTextStyle().GetFontSize());
1442     auto popupItemTextFontWeight =
1443         layoutProperty->GetFontWeight().value_or(indexerTheme->GetPopupTextStyle().GetFontWeight());
1444     auto bubbleSize = Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)
1445                           ? Dimension(BUBBLE_ITEM_SIZE, DimensionUnit::VP).ConvertToPx()
1446                           : Dimension(BUBBLE_BOX_SIZE, DimensionUnit::VP).ConvertToPx();
1447     for (uint32_t i = 0; i < currentListData.size(); i++) {
1448         auto childIndexOffset = autoCollapse_ ? 1 : 0;
1449         auto listItemNode = DynamicCast<FrameNode>(listNode->GetChildAtIndex(i + childIndexOffset));
1450         CHECK_NULL_VOID(listItemNode);
1451         auto listItemProperty = listItemNode->GetLayoutProperty<ListItemLayoutProperty>();
1452         CHECK_NULL_VOID(listItemProperty);
1453         listItemProperty->UpdateUserDefinedIdealSize(CalcSize(CalcLength(bubbleSize), CalcLength(bubbleSize)));
1454         listItemProperty->UpdateAlignment(Alignment::CENTER);
1455         auto listItemContext = listItemNode->GetRenderContext();
1456         CHECK_NULL_VOID(listItemContext);
1457         auto textNode = DynamicCast<FrameNode>(listItemNode->GetFirstChild());
1458         CHECK_NULL_VOID(textNode);
1459         auto textLayoutProperty = textNode->GetLayoutProperty<TextLayoutProperty>();
1460         CHECK_NULL_VOID(textLayoutProperty);
1461         textLayoutProperty->UpdateContent(currentListData.at(i));
1462         textLayoutProperty->UpdateFontSize(popupItemTextFontSize);
1463         textLayoutProperty->UpdateFontWeight(popupItemTextFontWeight);
1464         textLayoutProperty->UpdateMaxLines(1);
1465         textLayoutProperty->UpdateTextColor(
1466             static_cast<int32_t>(i) == popupClickedIndex_ ? popupSelectedTextColor : popupUnselectedTextColor);
1467         textLayoutProperty->UpdateTextAlign(TextAlign::CENTER);
1468         textLayoutProperty->UpdateAlignment(Alignment::CENTER);
1469         textLayoutProperty->UpdateMinFontScale(1.0f);
1470         textLayoutProperty->UpdateMaxFontScale(1.0f);
1471         UpdateBubbleListItemContext(listNode, indexerTheme, i);
1472         UpdateBubbleListItemMarkModify(textNode, listItemNode);
1473     }
1474 }
1475 
UpdateBubbleListItemContext(const RefPtr<FrameNode> & listNode,RefPtr<IndexerTheme> & indexerTheme,uint32_t pos)1476 void IndexerPattern::UpdateBubbleListItemContext(
1477     const RefPtr<FrameNode>& listNode, RefPtr<IndexerTheme>& indexerTheme, uint32_t pos)
1478 {
1479     CHECK_NULL_VOID(listNode);
1480     auto layoutProperty = GetLayoutProperty<IndexerLayoutProperty>();
1481     CHECK_NULL_VOID(layoutProperty);
1482     auto paintProperty = GetPaintProperty<IndexerPaintProperty>();
1483     CHECK_NULL_VOID(paintProperty);
1484     auto childIndexOffset = autoCollapse_ ? 1 : 0;
1485     auto listItemNode = DynamicCast<FrameNode>(listNode->GetChildAtIndex(pos + childIndexOffset));
1486     CHECK_NULL_VOID(listItemNode);
1487     auto listItemContext = listItemNode->GetRenderContext();
1488     CHECK_NULL_VOID(listItemContext);
1489     if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
1490         auto popupItemRadius = paintProperty->GetPopupItemBorderRadius().has_value()
1491                                     ? paintProperty->GetPopupItemBorderRadiusValue()
1492                                     : Dimension(BUBBLE_ITEM_RADIUS, DimensionUnit::VP);
1493         listItemContext->UpdateBorderRadius({ popupItemRadius, popupItemRadius, popupItemRadius, popupItemRadius });
1494         auto popupItemBackground =
1495             paintProperty->GetPopupItemBackground().value_or(indexerTheme->GetPopupUnclickedBgAreaColor());
1496         listItemContext->UpdateBackgroundColor(static_cast<int32_t>(pos) == popupClickedIndex_
1497                                                    ? (indexerTheme->GetPopupClickedBgAreaColor())
1498                                                    : popupItemBackground);
1499     } else {
1500         auto popupItemBackground =
1501             paintProperty->GetPopupItemBackground().value_or(indexerTheme->GetPopupBackgroundColor());
1502         listItemContext->UpdateBackgroundColor(
1503             static_cast<int32_t>(pos) == popupClickedIndex_ ? Color(POPUP_LISTITEM_CLICKED_BG) : popupItemBackground);
1504     }
1505 }
1506 
UpdateBubbleListItemMarkModify(RefPtr<FrameNode> & textNode,RefPtr<FrameNode> & listItemNode)1507 void IndexerPattern::UpdateBubbleListItemMarkModify(RefPtr<FrameNode>& textNode, RefPtr<FrameNode>& listItemNode)
1508 {
1509     textNode->MarkModifyDone();
1510     textNode->MarkDirtyNode();
1511     listItemNode->MarkModifyDone();
1512     listItemNode->MarkDirtyNode();
1513 }
1514 
ChangeListItemsSelectedStyle(int32_t clickIndex)1515 void IndexerPattern::ChangeListItemsSelectedStyle(int32_t clickIndex)
1516 {
1517     popupClickedIndex_ = clickIndex;
1518     auto host = GetHost();
1519     CHECK_NULL_VOID(popupNode_);
1520     auto pipeline = PipelineContext::GetCurrentContext();
1521     CHECK_NULL_VOID(pipeline);
1522     auto indexerTheme = pipeline->GetTheme<IndexerTheme>();
1523     CHECK_NULL_VOID(indexerTheme);
1524     auto paintProperty = host->GetPaintProperty<IndexerPaintProperty>();
1525     CHECK_NULL_VOID(paintProperty);
1526     auto popupSelectedTextColor =
1527         paintProperty->GetPopupSelectedColor().value_or(indexerTheme->GetPopupSelectedTextColor());
1528     auto popupUnselectedTextColor =
1529         paintProperty->GetPopupUnselectedColor().value_or(indexerTheme->GetPopupUnselectedTextColor());
1530     auto popupItemBackground =
1531         Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)
1532             ? paintProperty->GetPopupItemBackground().value_or(indexerTheme->GetPopupUnclickedBgAreaColor())
1533             : paintProperty->GetPopupItemBackground().value_or(indexerTheme->GetPopupBackgroundColor());
1534     auto listNode = popupNode_->GetLastChild()->GetFirstChild();
1535     auto currentIndex = 0;
1536     for (auto child : listNode->GetChildren()) {
1537         if (autoCollapse_ && listNode->GetChildIndex(child) == 0) continue;
1538         auto listItemNode = DynamicCast<FrameNode>(child);
1539         CHECK_NULL_VOID(listItemNode);
1540         auto listItemProperty = listItemNode->GetLayoutProperty<ListItemLayoutProperty>();
1541         CHECK_NULL_VOID(listItemProperty);
1542         auto listItemContext = listItemNode->GetRenderContext();
1543         CHECK_NULL_VOID(listItemContext);
1544         auto textNode = DynamicCast<FrameNode>(listItemNode->GetFirstChild());
1545         CHECK_NULL_VOID(textNode);
1546         auto textLayoutProperty = textNode->GetLayoutProperty<TextLayoutProperty>();
1547         CHECK_NULL_VOID(textLayoutProperty);
1548         if (currentIndex == clickIndex) {
1549             textLayoutProperty->UpdateTextColor(popupSelectedTextColor);
1550             listItemContext->UpdateBackgroundColor(
1551                 Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)
1552                     ? indexerTheme->GetPopupClickedBgAreaColor()
1553                     : Color(POPUP_LISTITEM_CLICKED_BG));
1554         } else {
1555             textLayoutProperty->UpdateTextColor(popupUnselectedTextColor);
1556             listItemContext->UpdateBackgroundColor(popupItemBackground);
1557         }
1558         textNode->MarkModifyDone();
1559         textNode->MarkDirtyNode();
1560         listItemNode->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
1561         currentIndex++;
1562     }
1563 }
1564 
AddPopupTouchListener(RefPtr<FrameNode> popupNode)1565 void IndexerPattern::AddPopupTouchListener(RefPtr<FrameNode> popupNode)
1566 {
1567     CHECK_NULL_VOID(popupNode);
1568     auto gesture = popupNode->GetOrCreateGestureEventHub();
1569     CHECK_NULL_VOID(gesture);
1570     auto touchCallback = [weak = WeakClaim(this)](TouchEventInfo& info) {
1571         auto indexerPattern = weak.Upgrade();
1572         CHECK_NULL_VOID(indexerPattern);
1573         info.SetStopPropagation(true);
1574         auto touchType = info.GetTouches().front().GetTouchType();
1575         if (touchType == TouchType::DOWN) {
1576             indexerPattern->isTouch_ = true;
1577             indexerPattern->OnPopupTouchDown(info);
1578         } else if (touchType == TouchType::UP || touchType == TouchType::CANCEL) {
1579             indexerPattern->isTouch_ = false;
1580             if (!indexerPattern->isPopupHover_) {
1581                 indexerPattern->StartDelayTask();
1582             }
1583         }
1584     };
1585     gesture->AddTouchEvent(MakeRefPtr<TouchEventImpl>(std::move(touchCallback)));
1586 }
1587 
AddListItemClickListener(RefPtr<FrameNode> & listItemNode,int32_t index)1588 void IndexerPattern::AddListItemClickListener(RefPtr<FrameNode>& listItemNode, int32_t index)
1589 {
1590     CHECK_NULL_VOID(listItemNode);
1591     auto gestureHub = listItemNode->GetOrCreateGestureEventHub();
1592     CHECK_NULL_VOID(gestureHub);
1593     auto touchCallback = [weak = WeakClaim(this), index](const TouchEventInfo& info) {
1594         auto indexerPattern = weak.Upgrade();
1595         CHECK_NULL_VOID(indexerPattern);
1596         TouchType touchType = info.GetTouches().front().GetTouchType();
1597         if (touchType == TouchType::DOWN) {
1598             indexerPattern->OnListItemClick(index);
1599         } else if (touchType == TouchType::UP || touchType == TouchType::CANCEL) {
1600             indexerPattern->ClearClickStatus();
1601         }
1602     };
1603     gestureHub->AddTouchEvent(MakeRefPtr<TouchEventImpl>(std::move(touchCallback)));
1604 }
1605 
OnListItemClick(int32_t index)1606 void IndexerPattern::OnListItemClick(int32_t index)
1607 {
1608     auto host = GetHost();
1609     CHECK_NULL_VOID(host);
1610     auto indexerEventHub = host->GetEventHub<IndexerEventHub>();
1611     CHECK_NULL_VOID(indexerEventHub);
1612     auto onPopupSelected = indexerEventHub->GetOnPopupSelected();
1613     if (onPopupSelected) {
1614         onPopupSelected(index);
1615 #if !defined(PREVIEW) && !defined(ACE_UNITTEST) && defined(OHOS_PLATFORM)
1616         UiSessionManager::GetInstance().ReportComponentChangeEvent("event", "onPopupSelected");
1617 #endif
1618     }
1619     ChangeListItemsSelectedStyle(index);
1620 }
1621 
ClearClickStatus()1622 void IndexerPattern::ClearClickStatus()
1623 {
1624     ChangeListItemsSelectedStyle(-1);
1625 }
1626 
OnPopupTouchDown(const TouchEventInfo & info)1627 void IndexerPattern::OnPopupTouchDown(const TouchEventInfo& info)
1628 {
1629     if (NeedShowPopupView()) {
1630         delayTask_.Cancel();
1631         StartBubbleAppearAnimation();
1632     }
1633 }
1634 
NeedShowBubble()1635 bool IndexerPattern::NeedShowBubble()
1636 {
1637     auto host = GetHost();
1638     CHECK_NULL_RETURN(host, false);
1639     auto layoutProperty = host->GetLayoutProperty<IndexerLayoutProperty>();
1640     CHECK_NULL_RETURN(layoutProperty, false);
1641     auto usePopup = layoutProperty->GetUsingPopup().value_or(false);
1642     return usePopup && IfSelectIndexValid();
1643 }
1644 
IfSelectIndexValid()1645 bool IndexerPattern::IfSelectIndexValid()
1646 {
1647     return (selected_ >= 0 && selected_ < static_cast<int32_t>(arrayValue_.size()));
1648 }
1649 
InitOnKeyEvent()1650 void IndexerPattern::InitOnKeyEvent()
1651 {
1652     if (isKeyEventRegisted_) {
1653         return;
1654     }
1655     auto host = GetHost();
1656     CHECK_NULL_VOID(host);
1657     auto focusHub = host->GetFocusHub();
1658     CHECK_NULL_VOID(focusHub);
1659     auto onKeyEvent = [wp = WeakClaim(this)](const KeyEvent& event) -> bool {
1660         auto pattern = wp.Upgrade();
1661         CHECK_NULL_RETURN(pattern, false);
1662         return pattern->OnKeyEvent(event);
1663     };
1664     isKeyEventRegisted_ = true;
1665     focusHub->SetOnKeyEventInternal(std::move(onKeyEvent));
1666 }
1667 
OnKeyEvent(const KeyEvent & event)1668 bool IndexerPattern::OnKeyEvent(const KeyEvent& event)
1669 {
1670     if (event.action != KeyAction::DOWN) {
1671         return false;
1672     }
1673     if (event.code == KeyCode::KEY_DPAD_UP) {
1674         return KeyIndexByStep(-1);
1675     }
1676     if (event.code == KeyCode::KEY_DPAD_DOWN) {
1677         return KeyIndexByStep(1);
1678     }
1679     if (!event.IsCombinationKey() && (event.IsLetterKey() || event.IsNumberKey())) {
1680         return MoveIndexBySearch(event.ConvertCodeToString());
1681     }
1682     OnKeyEventDisapear();
1683     return false;
1684 }
1685 
OnKeyEventDisapear()1686 void IndexerPattern::OnKeyEventDisapear()
1687 {
1688     ResetStatus();
1689     ApplyIndexChanged(true, false);
1690 }
1691 
ItemSelectedInAnimation(RefPtr<FrameNode> & itemNode)1692 void IndexerPattern::ItemSelectedInAnimation(RefPtr<FrameNode>& itemNode)
1693 {
1694     CHECK_NULL_VOID(itemNode);
1695     auto rendercontext = itemNode->GetRenderContext();
1696     CHECK_NULL_VOID(rendercontext);
1697     AnimationOption option;
1698     option.SetDuration(INDEXER_SELECT_DURATION);
1699     option.SetCurve(Curves::LINEAR);
1700     AnimationUtils::Animate(option, [rendercontext, id = Container::CurrentId(), weak = WeakClaim(this)]() {
1701         ContainerScope scope(id);
1702         auto pipeline = PipelineContext::GetCurrentContext();
1703         CHECK_NULL_VOID(pipeline);
1704         auto indexerTheme = pipeline->GetTheme<IndexerTheme>();
1705         CHECK_NULL_VOID(indexerTheme);
1706         auto pattern = weak.Upgrade();
1707         CHECK_NULL_VOID(pattern);
1708         auto host = pattern->GetHost();
1709         CHECK_NULL_VOID(host);
1710         auto paintProperty = host->GetPaintProperty<IndexerPaintProperty>();
1711         CHECK_NULL_VOID(paintProperty);
1712         rendercontext->UpdateBackgroundColor(
1713             paintProperty->GetSelectedBackgroundColor().value_or(indexerTheme->GetSeclectedBackgroundColor()));
1714     });
1715 }
1716 
ItemSelectedOutAnimation(RefPtr<FrameNode> & itemNode)1717 void IndexerPattern::ItemSelectedOutAnimation(RefPtr<FrameNode>& itemNode)
1718 {
1719     CHECK_NULL_VOID(itemNode);
1720     auto rendercontext = itemNode->GetRenderContext();
1721     CHECK_NULL_VOID(rendercontext);
1722     AnimationOption option;
1723     option.SetDuration(INDEXER_SELECT_DURATION);
1724     option.SetCurve(Curves::LINEAR);
1725     AnimationUtils::Animate(option, [rendercontext, id = Container::CurrentId()]() {
1726         ContainerScope scope(id);
1727         auto pipeline = PipelineContext::GetCurrentContext();
1728         CHECK_NULL_VOID(pipeline);
1729         auto indexerTheme = pipeline->GetTheme<IndexerTheme>();
1730         CHECK_NULL_VOID(indexerTheme);
1731         rendercontext->UpdateBackgroundColor(Color::TRANSPARENT);
1732     });
1733 }
1734 
IndexerHoverInAnimation()1735 void IndexerPattern::IndexerHoverInAnimation()
1736 {
1737     auto host = GetHost();
1738     CHECK_NULL_VOID(host);
1739     auto renderContext = host->GetRenderContext();
1740     CHECK_NULL_VOID(renderContext);
1741     AnimationOption option;
1742     option.SetDuration(INDEXER_HOVER_IN_DURATION);
1743     option.SetCurve(Curves::FRICTION);
1744     AnimationUtils::Animate(option, [renderContext, id = Container::CurrentId()]() {
1745         ContainerScope scope(id);
1746         auto pipeline = PipelineContext::GetCurrentContext();
1747         CHECK_NULL_VOID(pipeline);
1748         auto indexerTheme = pipeline->GetTheme<IndexerTheme>();
1749         CHECK_NULL_VOID(indexerTheme);
1750         renderContext->UpdateBackgroundColor(
1751             indexerTheme->GetSlipHoverBackgroundColor());
1752     });
1753 }
1754 
IndexerHoverOutAnimation()1755 void IndexerPattern::IndexerHoverOutAnimation()
1756 {
1757     auto host = GetHost();
1758     CHECK_NULL_VOID(host);
1759     auto renderContext = host->GetRenderContext();
1760     CHECK_NULL_VOID(renderContext);
1761     AnimationOption option;
1762     option.SetDuration(INDEXER_HOVER_OUT_DURATION);
1763     option.SetCurve(Curves::FRICTION);
1764     AnimationUtils::Animate(option, [renderContext, id = Container::CurrentId()]() {
1765         ContainerScope scope(id);
1766         auto pipeline = PipelineContext::GetCurrentContext();
1767         CHECK_NULL_VOID(pipeline);
1768         auto indexerTheme = pipeline->GetTheme<IndexerTheme>();
1769         CHECK_NULL_VOID(indexerTheme);
1770         renderContext->UpdateBackgroundColor(Color::TRANSPARENT);
1771     });
1772 }
1773 
IndexerPressInAnimation()1774 void IndexerPattern::IndexerPressInAnimation()
1775 {
1776     auto host = GetHost();
1777     CHECK_NULL_VOID(host);
1778     auto renderContext = host->GetRenderContext();
1779     CHECK_NULL_VOID(renderContext);
1780     AnimationOption option;
1781     option.SetDuration(INDEXER_PRESS_IN_DURATION);
1782     option.SetCurve(Curves::SHARP);
1783     AnimationUtils::Animate(option, [renderContext, id = Container::CurrentId()]() {
1784         ContainerScope scope(id);
1785         auto pipeline = PipelineContext::GetCurrentContext();
1786         CHECK_NULL_VOID(pipeline);
1787         auto indexerTheme = pipeline->GetTheme<IndexerTheme>();
1788         CHECK_NULL_VOID(indexerTheme);
1789         renderContext->UpdateBackgroundColor(Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)
1790                                                  ? indexerTheme->GetSlipPressedBackgroundColor()
1791                                                  : indexerTheme->GetSlipHoverBackgroundColor());
1792     });
1793 }
1794 
IndexerPressOutAnimation()1795 void IndexerPattern::IndexerPressOutAnimation()
1796 {
1797     auto host = GetHost();
1798     CHECK_NULL_VOID(host);
1799     auto renderContext = host->GetRenderContext();
1800     CHECK_NULL_VOID(renderContext);
1801     AnimationOption option;
1802     option.SetDuration(INDEXER_PRESS_OUT_DURATION);
1803     option.SetCurve(Curves::SHARP);
1804     AnimationUtils::Animate(option, [renderContext, id = Container::CurrentId()]() {
1805         ContainerScope scope(id);
1806         auto pipeline = PipelineContext::GetCurrentContext();
1807         CHECK_NULL_VOID(pipeline);
1808         auto indexerTheme = pipeline->GetTheme<IndexerTheme>();
1809         CHECK_NULL_VOID(indexerTheme);
1810         renderContext->UpdateBackgroundColor(Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)
1811                                                  ? indexerTheme->GetSlipPressedBackgroundColor()
1812                                                  : indexerTheme->GetSlipHoverBackgroundColor());
1813     });
1814 }
1815 
StartBubbleAppearAnimation()1816 void IndexerPattern::StartBubbleAppearAnimation()
1817 {
1818     animationId_ = GenerateAnimationId();
1819     UpdatePopupVisibility(VisibleType::VISIBLE);
1820     AnimationOption option;
1821     option.SetCurve(Curves::SHARP);
1822     option.SetDuration(INDEXER_BUBBLE_ENTER_DURATION);
1823     AnimationUtils::Animate(
1824         option,
1825         [id = Container::CurrentId(), weak = AceType::WeakClaim(this)]() {
1826             ContainerScope scope(id);
1827             auto pattern = weak.Upgrade();
1828             CHECK_NULL_VOID(pattern);
1829             pattern->UpdatePopupOpacity(1.0f);
1830             pattern->UpdateBubbleSize();
1831         });
1832 }
1833 
StartDelayTask(uint32_t duration)1834 void IndexerPattern::StartDelayTask(uint32_t duration)
1835 {
1836     auto host = GetHost();
1837     CHECK_NULL_VOID(host);
1838     auto context = host->GetContext();
1839     CHECK_NULL_VOID(context);
1840     CHECK_NULL_VOID(context->GetTaskExecutor());
1841     delayTask_.Reset([weak = AceType::WeakClaim(this)] {
1842         auto pattern = weak.Upgrade();
1843         CHECK_NULL_VOID(pattern);
1844         pattern->StartBubbleDisappearAnimation();
1845         });
1846     context->GetTaskExecutor()->PostDelayedTask(
1847         delayTask_, TaskExecutor::TaskType::UI, duration, "ArkUIAlphabetIndexerBubbleDisappear");
1848 }
1849 
StartCollapseDelayTask(RefPtr<FrameNode> & hostNode,uint32_t duration)1850 void IndexerPattern::StartCollapseDelayTask(RefPtr<FrameNode>& hostNode, uint32_t duration)
1851 {
1852     auto host = GetHost();
1853     CHECK_NULL_VOID(host);
1854     auto context = host->GetContext();
1855     CHECK_NULL_VOID(context);
1856     CHECK_NULL_VOID(context->GetTaskExecutor());
1857     delayCollapseTask_.Reset([node = WeakClaim(RawPtr(hostNode))]() {
1858         auto hostNode = node.Upgrade();
1859         CHECK_NULL_VOID(hostNode);
1860         hostNode->MarkModifyDone();
1861         hostNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
1862     });
1863     context->GetTaskExecutor()->PostDelayedTask(
1864         delayCollapseTask_, TaskExecutor::TaskType::UI, duration, "ArkUIAlphabetIndexerCollapse");
1865 }
1866 
StartBubbleDisappearAnimation()1867 void IndexerPattern::StartBubbleDisappearAnimation()
1868 {
1869     AnimationOption option;
1870     option.SetCurve(Curves::SHARP);
1871     option.SetDuration(INDEXER_BUBBLE_EXIT_DURATION);
1872     AnimationUtils::Animate(
1873         option,
1874         [id = Container::CurrentId(), weak = AceType::WeakClaim(this)]() {
1875             ContainerScope scope(id);
1876             auto pattern = weak.Upgrade();
1877             CHECK_NULL_VOID(pattern);
1878             pattern->UpdatePopupOpacity(0.0f);
1879         },
1880         [id = Container::CurrentId(), weak = AceType::WeakClaim(this)]() {
1881             ContainerScope scope(id);
1882             auto pattern = weak.Upgrade();
1883             CHECK_NULL_VOID(pattern);
1884             CHECK_NULL_VOID(pattern->popupNode_);
1885             auto rendercontext = pattern->popupNode_->GetRenderContext();
1886             CHECK_NULL_VOID(rendercontext);
1887             if (NearZero(rendercontext->GetOpacityValue(0.0f))) {
1888                 pattern->UpdatePopupVisibility(VisibleType::GONE);
1889             }
1890         });
1891 }
1892 
UpdatePopupOpacity(float ratio)1893 void IndexerPattern::UpdatePopupOpacity(float ratio)
1894 {
1895     CHECK_NULL_VOID(popupNode_);
1896     auto rendercontext = popupNode_->GetRenderContext();
1897     CHECK_NULL_VOID(rendercontext);
1898     rendercontext->UpdateOpacity(ratio);
1899 }
1900 
UpdatePopupVisibility(VisibleType visible)1901 void IndexerPattern::UpdatePopupVisibility(VisibleType visible)
1902 {
1903     CHECK_NULL_VOID(popupNode_);
1904     auto layoutProperty = popupNode_->GetLayoutProperty<LinearLayoutProperty>();
1905     CHECK_NULL_VOID(layoutProperty);
1906     auto currentVisibility = layoutProperty->GetVisibility().value_or(VisibleType::VISIBLE);
1907     if (currentVisibility != visible) {
1908         layoutProperty->UpdateVisibility(visible);
1909         popupNode_->MarkDirtyNode(PROPERTY_UPDATE_LAYOUT);
1910     }
1911 }
1912 
NeedShowPopupView()1913 bool IndexerPattern::NeedShowPopupView()
1914 {
1915     CHECK_NULL_RETURN(popupNode_, false);
1916     auto layoutProperty = popupNode_->GetLayoutProperty<LinearLayoutProperty>();
1917     CHECK_NULL_RETURN(layoutProperty, false);
1918     return layoutProperty->GetVisibility().value_or(VisibleType::VISIBLE) == VisibleType::VISIBLE;
1919 }
1920 
GenerateAnimationId()1921 int32_t IndexerPattern::GenerateAnimationId()
1922 {
1923     return (++animationId_) % TOTAL_NUMBER;
1924 }
1925 
FireOnSelect(int32_t selectIndex,bool fromPress)1926 void IndexerPattern::FireOnSelect(int32_t selectIndex, bool fromPress)
1927 {
1928     auto host = GetHost();
1929     CHECK_NULL_VOID(host);
1930     auto indexerEventHub = host->GetEventHub<IndexerEventHub>();
1931     CHECK_NULL_VOID(indexerEventHub);
1932     int32_t actualIndex = autoCollapse_ ?
1933             selected_ > 0 && selected_ < itemCount_ ?
1934                 std::find(fullArrayValue_.begin(), fullArrayValue_.end(),
1935                     arrayValue_.at(selected_).first) - fullArrayValue_.begin() :
1936                 selected_ :
1937         selectIndex;
1938     if (fromPress || lastIndexFromPress_ == fromPress || lastFireSelectIndex_ != selectIndex) {
1939         auto onChangeEvent = indexerEventHub->GetChangeEvent();
1940         if (onChangeEvent && (selected_ >= 0) && (selected_ < itemCount_)) {
1941             onChangeEvent(actualIndex);
1942         }
1943         auto onCreatChangeEvent = indexerEventHub->GetCreatChangeEvent();
1944         if (onCreatChangeEvent && (selected_ >= 0) && (selected_ < itemCount_)) {
1945             onCreatChangeEvent(actualIndex);
1946         }
1947         auto onSelected = indexerEventHub->GetOnSelected();
1948         if (onSelected && (selectIndex >= 0) && (selectIndex < itemCount_)) {
1949             TAG_LOGD(AceLogTag::ACE_ALPHABET_INDEXER, "item %{public}d is selected", actualIndex);
1950             onSelected(actualIndex); // fire onSelected with an item's index from original array
1951         }
1952     }
1953     selectedChangedForHaptic_ = lastFireSelectIndex_ != selected_;
1954     lastFireSelectIndex_ = selectIndex;
1955     lastIndexFromPress_ = fromPress;
1956 }
1957 
SetAccessibilityAction()1958 void IndexerPattern::SetAccessibilityAction()
1959 {
1960     auto host = GetHost();
1961     CHECK_NULL_VOID(host);
1962     auto childrenNode = host->GetChildren();
1963     for (auto& iter : childrenNode) {
1964         auto textNode = DynamicCast<NG::FrameNode>(iter);
1965         CHECK_NULL_VOID(textNode);
1966         auto accessibilityProperty = textNode->GetAccessibilityProperty<AccessibilityProperty>();
1967         CHECK_NULL_VOID(accessibilityProperty);
1968         SetActionSelect(textNode, accessibilityProperty);
1969         SetActionClearSelection(textNode, accessibilityProperty);
1970     }
1971 }
1972 
SetActionSelect(RefPtr<FrameNode> & textNode,RefPtr<AccessibilityProperty> & accessibilityProperty)1973 void IndexerPattern::SetActionSelect(RefPtr<FrameNode>& textNode, RefPtr<AccessibilityProperty>& accessibilityProperty)
1974 {
1975     CHECK_NULL_VOID(textNode);
1976     CHECK_NULL_VOID(accessibilityProperty);
1977     accessibilityProperty->SetActionSelect(
1978         [weakPtr = WeakClaim(this), node = WeakClaim(RawPtr(textNode))]() {
1979             const auto& indexerPattern = weakPtr.Upgrade();
1980             CHECK_NULL_VOID(indexerPattern);
1981             auto host = indexerPattern->GetHost();
1982             CHECK_NULL_VOID(host);
1983             auto childrenNode = host->GetChildren();
1984             const auto& frameNode = node.Upgrade();
1985             CHECK_NULL_VOID(frameNode);
1986             auto index = 0;
1987             auto nodeId = frameNode->GetAccessibilityId();
1988             for (auto& child : childrenNode) {
1989                 if (child->GetAccessibilityId() == nodeId) {
1990                     break;
1991                 }
1992                 index++;
1993             }
1994             indexerPattern->selected_ = index;
1995             indexerPattern->ResetStatus();
1996             indexerPattern->ApplyIndexChanged(true, true, true);
1997             indexerPattern->OnSelect();
1998         });
1999 }
2000 
SetActionClearSelection(RefPtr<FrameNode> & textNode,RefPtr<AccessibilityProperty> & accessibilityProperty)2001 void IndexerPattern::SetActionClearSelection(
2002     RefPtr<FrameNode>& textNode, RefPtr<AccessibilityProperty>& accessibilityProperty)
2003 {
2004     CHECK_NULL_VOID(textNode);
2005     CHECK_NULL_VOID(accessibilityProperty);
2006     accessibilityProperty->SetActionClearSelection(
2007         [weakPtr = WeakClaim(this), node = WeakClaim(RawPtr(textNode))] {
2008             const auto& indexerPattern = weakPtr.Upgrade();
2009             CHECK_NULL_VOID(indexerPattern);
2010             auto host = indexerPattern->GetHost();
2011             CHECK_NULL_VOID(host);
2012             auto childrenNode = host->GetChildren();
2013             const auto& frameNode = node.Upgrade();
2014             CHECK_NULL_VOID(frameNode);
2015             auto index = 0;
2016             auto nodeId = frameNode->GetAccessibilityId();
2017             for (auto& child : childrenNode) {
2018                 if (child->GetAccessibilityId() == nodeId) {
2019                     break;
2020                 }
2021                 index++;
2022             }
2023             if (indexerPattern->selected_ != index) {
2024                 return;
2025             }
2026             indexerPattern->selected_ = 0;
2027             indexerPattern->ResetStatus();
2028             indexerPattern->ApplyIndexChanged(true, false);
2029             indexerPattern->OnSelect();
2030         });
2031 }
2032 
RemoveBubble()2033 void IndexerPattern::RemoveBubble()
2034 {
2035     auto host = GetHost();
2036     CHECK_NULL_VOID(host);
2037     host->RemoveChild(popupNode_);
2038     auto layoutProperty = host->GetLayoutProperty<IndexerLayoutProperty>();
2039     CHECK_NULL_VOID(layoutProperty);
2040     layoutProperty->UpdateIsPopup(false);
2041     popupNode_ = nullptr;
2042     lastPopupIndex_ = -1;
2043 }
2044 
IsMeasureBoundary() const2045 bool IndexerPattern::IsMeasureBoundary() const
2046 {
2047     auto host = GetHost();
2048     CHECK_NULL_RETURN(host, false);
2049     auto layoutProperty = host->GetLayoutProperty<IndexerLayoutProperty>();
2050     CHECK_NULL_RETURN(layoutProperty, false);
2051     return CheckMeasureSelfFlag(layoutProperty->GetPropertyChangeFlag());
2052 }
2053 
UpdateChildBoundary(RefPtr<FrameNode> & frameNode)2054 void IndexerPattern::UpdateChildBoundary(RefPtr<FrameNode>& frameNode)
2055 {
2056     auto host = GetHost();
2057     CHECK_NULL_VOID(host);
2058     auto layoutProperty = host->GetLayoutProperty<IndexerLayoutProperty>();
2059     CHECK_NULL_VOID(layoutProperty);
2060     CHECK_NULL_VOID(frameNode);
2061     auto pattern = DynamicCast<TextPattern>(frameNode->GetPattern());
2062     CHECK_NULL_VOID(pattern);
2063     auto isMeasureBoundary = layoutProperty->GetPropertyChangeFlag() ==  PROPERTY_UPDATE_NORMAL;
2064     pattern->SetIsMeasureBoundary(isMeasureBoundary);
2065 }
2066 
DumpInfo()2067 void IndexerPattern::DumpInfo()
2068 {
2069     auto layoutProperty = GetLayoutProperty<IndexerLayoutProperty>();
2070     CHECK_NULL_VOID(layoutProperty);
2071     DumpLog::GetInstance().AddDesc(
2072         "AlignStyle: ", static_cast<int32_t>(layoutProperty->GetAlignStyleValue(AlignStyle::RIGHT)));
2073     auto offset = layoutProperty->GetPopupHorizontalSpace();
2074     DumpLog::GetInstance().AddDesc("Offset: ", offset.has_value() ? offset.value().ToString() : "undefined");
2075     DumpLog::GetInstance().AddDesc("PopupPositionX: ",
2076         layoutProperty->GetPopupPositionXValue(Dimension(NG::BUBBLE_POSITION_X, DimensionUnit::VP)).ToString());
2077     DumpLog::GetInstance().AddDesc("PopupPositionY: ",
2078         layoutProperty->GetPopupPositionYValue(Dimension(NG::BUBBLE_POSITION_Y, DimensionUnit::VP)).ToString());
2079     DumpLog::GetInstance().AddDesc("AutoCollapse: ", autoCollapse_ ? "true" : "false");
2080     DumpLog::GetInstance().AddDesc("IsPopup: ", isPopup_ ? "true" : "false");
2081     DumpLog::GetInstance().AddDesc(std::string("EnableHapticFeedback: ").append(std::to_string(enableHapticFeedback_)));
2082     DumpLog::GetInstance().AddDesc("ItemSize: ", lastItemSize_);
2083     DumpLog::GetInstance().AddDesc("ItemHeight: ", itemHeight_);
2084     DumpLog::GetInstance().AddDesc("ActualItemCount: ", itemCount_);
2085     DumpLog::GetInstance().AddDesc("FullItemCount: ", static_cast<int32_t>(fullArrayValue_.size()));
2086     DumpLog::GetInstance().AddDesc("MaxContentHeight: ", maxContentHeight_);
2087 }
2088 } // namespace OHOS::Ace::NG
2089