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