• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021 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 "frameworks/bridge/common/dom/dom_list.h"
17 
18 #include "base/log/event_report.h"
19 #include "base/utils/linear_map.h"
20 #include "base/utils/utils.h"
21 #include "core/components/list/list_theme.h"
22 #include "core/components/scroll/scroll_bar_theme.h"
23 #include "core/components/scroll/scroll_fade_effect.h"
24 #include "core/components/scroll/scroll_spring_effect.h"
25 #include "core/components/theme/theme_manager.h"
26 #include "frameworks/bridge/common/dom/dom_list_item.h"
27 #include "frameworks/bridge/common/dom/dom_list_item_group.h"
28 #include "frameworks/bridge/common/utils/utils.h"
29 
30 namespace OHOS::Ace::Framework {
31 namespace {
32 
33 constexpr int32_t DEFAULT_NODE_INDEX = -1;
34 constexpr char INDEXER_ALPHABET_DIV = 10; // newline character
35 
36 } // namespace
37 
DOMList(NodeId nodeId,const std::string & nodeName)38 DOMList::DOMList(NodeId nodeId, const std::string& nodeName) : DOMNode(nodeId, nodeName) {}
39 
SetSpecializedAttr(const std::pair<std::string,std::string> & attr)40 bool DOMList::SetSpecializedAttr(const std::pair<std::string, std::string>& attr)
41 {
42     // static linear map must be sorted by key.
43     static const LinearMapNode<void (*)(DOMList&, const std::string&)> attrOperators[] = {
44         {
45             DOM_LIST_ACCESSIBILITY_DISABLED,
46             [](DOMList& list, const std::string& val) { list.accessibilityDisabled_ = StringToBool(val); },
47         },
48         {
49             LIST_BEGIN_INDEX,
50             [](DOMList& list, const std::string& val) { list.beginIndex_ = StringUtils::StringToInt(val); },
51         },
52         {
53             LIST_CACHED_COUNT,
54             [](DOMList& list, const std::string& val) { list.cachedCount_ = StringUtils::StringToInt(val); },
55         },
56         {
57             DOM_LIST_CENTER_LAYOUT,
58             [](DOMList& list, const std::string& val) { list.centerLayout_ = StringToBool(val); },
59         },
60         {
61             DOM_LIST_CHAIN_ANIMATION,
62             [](DOMList& list, const std::string& val) { list.chainAnimation_ = StringToBool(val); },
63         },
64         {
65             DOM_LIST_DIVIDER,
66             [](DOMList& list, const std::string& val) { list.needDivider_ = StringToBool(val); },
67         },
68         {
69             LIST_END_INDEX,
70             [](DOMList& list, const std::string& val) { list.endIndex_ = StringUtils::StringToInt(val); },
71         },
72         {
73             DOM_LIST_INDEXER,
74             [](DOMList& list, const std::string& val) { list.ParseIndexer(val); },
75         },
76         {
77             DOM_LIST_INDEXER_BUBBLE,
78             [](DOMList& list, const std::string& val) {
79                 list.bubble_.first = StringToBool(val);
80                 list.bubble_.second = true;
81             },
82         },
83         {
84             DOM_LIST_INDEXER_MODE,
85             [](DOMList& list, const std::string& val) {
86                 list.circleMode_.first = StringToBool(val);
87                 list.circleMode_.second = true;
88             },
89         },
90         {
91             DOM_LIST_INDEXER_MULTI,
92             [](DOMList& list, const std::string& val) {
93               list.multiLanguage_.first = StringToBool(val);
94               list.multiLanguage_.second = true;
95             },
96         },
97         {
98             LIST_INDEX_OFFSET,
99             [](DOMList& list, const std::string& val) { list.indexOffset_ = StringUtils::StringToInt(val); },
100         },
101         {
102             DOM_LIST_INITIAL_INDEX,
103             [](DOMList& list, const std::string& val) { list.initialIndex_ = StringToInt(val); },
104         },
105         {
106             DOM_LIST_INITIAL_OFFSET,
107             [](DOMList& list, const std::string& val) { list.initialOffset_ = StringToDouble(val); },
108         },
109         {
110             DOM_LIST_ITEM_CENTER,
111             [](DOMList& list, const std::string& val) { list.itemCenter_ = StringToBool(val); },
112         },
113         {
114             DOM_LIST_ITEM_OPACITY,
115             [](DOMList& list, const std::string& val) { list.itemOpacity_ = StringToBool(val); },
116         },
117         {
118             DOM_LIST_ITEM_SCALE,
119             [](DOMList& list, const std::string& val) { list.itemScale_ = StringToBool(val); },
120         },
121         {
122             LIST_REPEATED_LENGTH,
123             [](DOMList& list, const std::string& val) { list.repeatedLength_ = StringUtils::StringToInt(val); },
124         },
125         {
126             DOM_LIST_ROTATION_VIBRATE,
127             [](DOMList& list, const std::string& val) {
128 #ifdef WEARABLE_PRODUCT
129                 list.rotationVibrate_ = StringToBool(val);
130                 list.scrollVibrate_ = false;
131 #endif
132             },
133         },
134         {
135             DOM_SCROLL_SCROLLBAR,
136             [](DOMList& list, const std::string& val) {
137                 if (val == DOM_SCROLL_SCROLLBAR_ON) {
138                     list.displayMode_ = DisplayMode::ON;
139                 } else if (val == DOM_SCROLL_SCROLLBAR_AUTO) {
140                     list.displayMode_ = DisplayMode::AUTO;
141                 } else {
142                     list.displayMode_ = DisplayMode::OFF;
143                 }
144             },
145         },
146         {
147             DOM_SCROLL_EFFECT,
148             [](DOMList& list, const std::string& val) {
149                 if (val == DOM_SCROLL_EFFECT_SPRING) {
150                     list.edgeEffect_ = EdgeEffect::SPRING;
151                 } else if (val == DOM_SCROLL_EFFECT_FADE) {
152                     list.edgeEffect_ = EdgeEffect::FADE;
153                 } else {
154                     list.edgeEffect_ = EdgeEffect::NONE;
155                 }
156             },
157         },
158         {
159             DOM_LIST_SCROLLPAGE,
160             [](DOMList& list, const std::string& val) { list.scrollPage_ = StringToBool(val); },
161         },
162         {
163             DOM_LIST_SCROLL_VIBRATE,
164             [](DOMList& list, const std::string& val) { list.scrollVibrate_ = StringToBool(val); },
165         },
166         {
167             DOM_LIST_ATTR_SELECTED,
168             [](DOMList& list, const std::string& val) { list.selectedItem_ = val; },
169         },
170         {
171             DOM_SCROLL_SHAPE_MODE,
172             [](DOMList& list, const std::string& val) {
173                 if (val == DOM_SCROLL_SHAPE_MODE_RECT) {
174                     list.shapeMode_ = ShapeMode::RECT;
175                 } else if (val == DOM_SCROLL_SHAPE_MODE_ROUND) {
176                     list.shapeMode_ = ShapeMode::ROUND;
177                 } else {
178                     list.shapeMode_ = ShapeMode::DEFAULT;
179                 }
180             },
181         },
182         {
183             DOM_LIST_UPDATE_EFFECT,
184             [](DOMList& list, const std::string& val) { list.updateEffect_ = StringToBool(val); },
185         },
186     };
187     auto operatorIter = BinarySearchFindIndex(attrOperators, ArraySize(attrOperators), attr.first.c_str());
188     if (operatorIter != -1) {
189         attrOperators[operatorIter].value(*this, attr.second);
190         return true;
191     }
192     return false;
193 }
194 
ParseIndexer(const std::string & indexerAlphabet)195 void DOMList::ParseIndexer(const std::string& indexerAlphabet)
196 {
197     indexerAlphabet_.clear();
198     indexer_ = false;
199 
200     auto context = GetPipelineContext().Upgrade();
201     if (context && context->IsJsCard()) {
202         return;
203     }
204 
205     if (indexerAlphabet.empty() || indexerAlphabet.find("false") != std::string::npos) {
206         return;
207     }
208 
209     if (indexerAlphabet.find("true") != std::string::npos) {
210         indexer_ = true;
211         return;
212     }
213 
214     StringUtils::StringSplitter(indexerAlphabet, INDEXER_ALPHABET_DIV, indexerAlphabet_);
215     int32_t alphabetCount = static_cast<int32_t>(indexerAlphabet_.size());
216     indexer_ = alphabetCount > 0;
217     LOGI("IndexerFlag:%{public}d, AlphabetCount:%{public}d.", indexer_, alphabetCount);
218 }
219 
SupportChainAnimation() const220 bool DOMList::SupportChainAnimation() const
221 {
222     return chainAnimation_ && !indexer_;
223 }
224 
AddSpecializedEvent(int32_t pageId,const std::string & event)225 bool DOMList::AddSpecializedEvent(int32_t pageId, const std::string& event)
226 {
227     // static linear map must be sorted by key.
228     static const LinearMapNode<void (*)(int32_t, DOMList&)> eventOperators[] = {
229         {
230             DOM_LIST_EVENT_INDEXER_CHANGE,
231             [](int32_t pageId, DOMList& list) {
232                 list.onIndexerChange_ = EventMarker(list.GetNodeIdForEvent(), DOM_LIST_EVENT_INDEXER_CHANGE, pageId);
233             },
234         },
235         {
236             LIST_EVENT_REQUEST_ITEM,
237             [](int32_t pageId, DOMList& list) {
238                 list.onRequestItem_ = EventMarker(list.GetNodeIdForEvent(), LIST_EVENT_REQUEST_ITEM, pageId);
239             },
240         },
241         {
242             DOM_LIST_EVENT_SCROLL,
243             [](int32_t pageId, DOMList& list) {
244                 list.onScroll_ = EventMarker(list.GetNodeIdForEvent(), DOM_LIST_EVENT_SCROLL, pageId);
245             },
246         },
247         {
248             DOM_LIST_EVENT_SCROLL_BOTTOM,
249             [](int32_t pageId, DOMList& list) {
250                 list.onScrollBottom_ = EventMarker(list.GetNodeIdForEvent(), DOM_LIST_EVENT_SCROLL_BOTTOM, pageId);
251             },
252         },
253         {
254             DOM_LIST_EVENT_SCROLL_END,
255             [](int32_t pageId, DOMList& list) {
256                 list.onScrollEnd_ = EventMarker(list.GetNodeIdForEvent(), DOM_LIST_EVENT_SCROLL_END, pageId);
257             },
258         },
259         {
260             DOM_LIST_EVENT_SCROLL_TOP,
261             [](int32_t pageId, DOMList& list) {
262                 list.onScrollTop_ = EventMarker(list.GetNodeIdForEvent(), DOM_LIST_EVENT_SCROLL_TOP, pageId);
263             },
264         },
265         {
266             DOM_LIST_EVENT_SCROLL_TOUCH_UP,
267             [](int32_t pageId, DOMList& list) {
268                 list.onScrollTouchUp_ = EventMarker(list.GetNodeIdForEvent(), DOM_LIST_EVENT_SCROLL_TOUCH_UP, pageId);
269             },
270         },
271     };
272     auto iter = BinarySearchFindIndex(eventOperators, ArraySize(eventOperators), event.c_str());
273     if (iter != -1) {
274         eventOperators[iter].value(pageId, *this);
275         return true;
276     }
277     return false;
278 }
279 
ResetInitializedStyle()280 void DOMList::ResetInitializedStyle()
281 {
282     if (!listComponent_) {
283         LOGE("list component is null, reset style failed.");
284         EventReport::SendComponentException(ComponentExcepType::LIST_COMPONENT_ERR);
285         return;
286     }
287     // list theme
288     RefPtr<ListTheme> listTheme = GetTheme<ListTheme>();
289     if (listTheme) {
290         listComponent_->InitStyle(listTheme);
291         if (declaration_) {
292             auto& backgroundStyle =
293                 static_cast<CommonBackgroundStyle&>(declaration_->GetStyle(StyleTag::COMMON_BACKGROUND_STYLE));
294             if (declaration_->HasBackGroundColor() && backgroundStyle.IsValid()) {
295                 listComponent_->SetBackgroundColor(backgroundStyle.backgroundColor);
296             }
297         }
298         scrollDistance_ = listTheme->GetScrollDistance();
299     }
300 
301     if (!scrollBar_ || displayMode_ == DisplayMode::OFF) {
302         return;
303     }
304     // scrollBar_ theme
305     RefPtr<ScrollBarTheme> scrollBarTheme = GetTheme<ScrollBarTheme>();
306     if (scrollBarTheme) {
307         if (scrollBar_->GetShapeMode() == ShapeMode::DEFAULT) {
308             scrollBar_->SetShapeMode(scrollBarTheme->GetShapeMode());
309         }
310         scrollBar_->SetInactiveWidth(scrollBarTheme->GetNormalWidth());
311         scrollBar_->SetNormalWidth(scrollBarTheme->GetNormalWidth());
312         scrollBar_->SetActiveWidth(scrollBarTheme->GetActiveWidth());
313         scrollBar_->SetMinHeight(scrollBarTheme->GetMinHeight());
314         scrollBar_->SetMinDynamicHeight(scrollBarTheme->GetMinDynamicHeight());
315         if (scrollbarPositionY_.first == false) {
316             scrollBar_->SetReservedHeight(scrollBarTheme->GetReservedHeight());
317         } else {
318             scrollBar_->SetReservedHeight(scrollbarPositionY_.second);
319         }
320         scrollBar_->SetTouchWidth(scrollBarTheme->GetTouchWidth());
321         scrollBar_->SetBackgroundColor(scrollBarTheme->GetBackgroundColor());
322         scrollBar_->SetForegroundColor(scrollBarTheme->GetForegroundColor());
323         scrollBar_->SetPadding(scrollBarTheme->GetPadding());
324     }
325 }
326 
CreateOrUpdateList()327 void DOMList::CreateOrUpdateList()
328 {
329     if (!listComponent_) {
330         listComponent_ = AceType::MakeRefPtr<ListComponent>();
331     }
332 
333     listComponent_->SetScrollVibrate(scrollVibrate_);
334     listComponent_->MarkNeedRotationVibrate(rotationVibrate_);
335 
336     listComponent_->SetDirection(flexDirection_);
337     if (flexDirection_ == FlexDirection::COLUMN || flexDirection_ == FlexDirection::COLUMN_REVERSE) {
338         listComponent_->SetScrollPage(scrollPage_);
339         boxComponent_->SetScrollPage(scrollPage_);
340     } else {
341         listComponent_->SetScrollPage(false);
342         boxComponent_->SetScrollPage(false);
343     }
344     listComponent_->SetChainProperty(chainProperty_);
345     listComponent_->SetChainAnimation(SupportChainAnimation());
346     if (useDefaultOverSpringProperty_) {
347         if (SupportChainAnimation()) {
348             listComponent_->SetOverSpringProperty(SpringChainProperty::GetDefaultOverSpringProperty());
349         } else {
350             listComponent_->SetOverSpringProperty(Scrollable::GetDefaultOverSpringProperty());
351         }
352     } else {
353         listComponent_->SetOverSpringProperty(1.0, overStiffness_, overDamping_);
354     }
355 
356     listComponent_->SetFlexAlign(crossAxisAlign_);
357     listComponent_->SetRightToLeft(IsRightToLeft());
358     listComponent_->SetOnRequestItem(onRequestItem_);
359     listComponent_->SetOnScroll(onScroll_);
360     listComponent_->SetOnScrollBottom(onScrollBottom_);
361     listComponent_->SetOnScrollTop(onScrollTop_);
362     listComponent_->SetOnScrollEnd(onScrollEnd_);
363     listComponent_->SetOnScrollTouchUp(onScrollTouchUp_);
364     if (cachedCount_ > 0) {
365         listComponent_->SetCachedCount(cachedCount_);
366     }
367     listComponent_->SetBeginIndex(beginIndex_);
368     listComponent_->SetEndIndex(endIndex_);
369     listComponent_->SetRepeatedLength(repeatedLength_);
370     listComponent_->SetIndexOffset(indexOffset_);
371     listComponent_->SetColumnCount(listColumns_);
372     listComponent_->SetItemExtent(itemExtent_);
373     listComponent_->SetUpdateEffect(updateEffect_);
374     listComponent_->SetAccessibilityDisabled(accessibilityDisabled_);
375     if (listColumns_ > 1) {
376         // itemScale is not supported in 'columns > 1' case.
377         itemScale_ = false;
378     }
379     listComponent_->SetItemScale(itemScale_);
380     listComponent_->MarkCenterLayout(centerLayout_);
381     listComponent_->SetSupportItemCenter(itemCenter_);
382     if (edgeEffect_ == EdgeEffect::SPRING) {
383         listComponent_->SetScrollEffect(AceType::MakeRefPtr<ScrollSpringEffect>());
384     } else if (edgeEffect_ == EdgeEffect::FADE) {
385         listComponent_->SetScrollEffect(AceType::MakeRefPtr<ScrollFadeEffect>(fadeColor_));
386     } else {
387         listComponent_->SetScrollEffect(AceType::MakeRefPtr<ScrollEdgeEffect>(EdgeEffect::NONE));
388     }
389 
390     // keep list state during update.
391     if (!listComponent_->GetPositionController()) {
392         listComponent_->SetPositionController(AceType::MakeRefPtr<ScrollPositionController>());
393     }
394     if (declaration_) {
395         declaration_->SetPositionController(listComponent_->GetPositionController());
396     }
397     listComponent_->GetPositionController()->SetInitialIndex(initialIndex_);
398     listComponent_->GetPositionController()->SetInitialOffset(initialOffset_);
399 
400     if (declaration_ && !GetRotateId().IsEmpty()) {
401         listComponent_->SetOnRotateId(GetRotateId());
402     }
403 
404     SetScrollBar();
405     ResetInitializedStyle();
406     InitScrollBarWithSpecializedStyle();
407     SetChildActive();
408 }
409 
CreateOrUpdateIndexer()410 void DOMList::CreateOrUpdateIndexer()
411 {
412     CreateOrUpdateList();
413     bool isCircle = circleMode_.second ? circleMode_.first : SystemProperties::GetDeviceType() == DeviceType::WATCH;
414     bool bubble = bubble_.second ? bubble_.first : true;
415     bool multiLanguage = multiLanguage_.second ? multiLanguage_.first : false;
416     if (!indexerComponent_) {
417         if (indexerAlphabet_.empty()) {
418             indexerComponent_ = AceType::MakeRefPtr<IndexerListComponent>(
419                 listComponent_, isCircle, IsRightToLeft(), bubble, multiLanguage);
420         } else {
421             indexerComponent_ = AceType::MakeRefPtr<IndexerListComponent>(
422                 listComponent_, indexerAlphabet_, isCircle, IsRightToLeft(), bubble, multiLanguage);
423         }
424         if (isCircle && !onIndexerChange_.IsEmpty()) {
425             indexerComponent_->SetIndexerChangeEvent(onIndexerChange_);
426         }
427     }
428     indexerComponent_->SetBubbleEnabled(bubble_.first);
429 }
430 
SetScrollBar()431 void DOMList::SetScrollBar()
432 {
433     if (displayMode_ == DisplayMode::ON || displayMode_ == DisplayMode::AUTO) {
434         if (shapeMode_ == ShapeMode::ROUND || shapeMode_ == ShapeMode::RECT) {
435             scrollBar_ = AceType::MakeRefPtr<ScrollBar>(displayMode_, shapeMode_);
436         } else {
437             scrollBar_ = AceType::MakeRefPtr<ScrollBar>(displayMode_, ShapeMode::DEFAULT);
438         }
439         scrollBar_->SetPositionMode(IsRightToLeft() ? PositionMode::LEFT : PositionMode::RIGHT);
440     } else {
441         scrollBar_ = AceType::MakeRefPtr<ScrollBar>(DisplayMode::OFF, ShapeMode::DEFAULT);
442     }
443     listComponent_->SetScrollBar(scrollBar_);
444 }
445 
InitScrollBarWithSpecializedStyle()446 void DOMList::InitScrollBarWithSpecializedStyle()
447 {
448     if (!scrollBar_) {
449         return;
450     }
451 
452     if (scrollbarColor_.first) {
453         scrollBar_->SetForegroundColor(scrollbarColor_.second);
454     }
455     if (scrollbarWidth_.first) {
456         scrollBar_->SetInactiveWidth(scrollbarWidth_.second);
457         scrollBar_->SetNormalWidth(scrollbarWidth_.second);
458         scrollBar_->SetActiveWidth(scrollbarWidth_.second);
459         scrollBar_->SetTouchWidth(scrollbarWidth_.second);
460     }
461     if (scrollbarPositionX_.first) {
462         scrollBar_->SetPosition(scrollbarPositionX_.second);
463     }
464 }
465 
SetChildActive()466 void DOMList::SetChildActive()
467 {
468     if (selectedItem_.empty()) {
469         return;
470     }
471     for (const auto& child : GetChildList()) {
472         auto childItem = AceType::DynamicCast<DOMListItem>(child);
473         if (!childItem) {
474             continue;
475         }
476         auto listItem = AceType::DynamicCast<ListItemComponent>(childItem->GetSpecializedComponent());
477         if (!listItem) {
478             continue;
479         }
480         auto itemKey = childItem->GetItemKey();
481         bool isItemActive = !itemKey.empty() && selectedItem_ == itemKey;
482         if (listItem->IsActive() != isItemActive) {
483             listItem->SetIsActive(isItemActive);
484             childItem->MarkNeedUpdate();
485         }
486     }
487 }
488 
SetSpecializedStyle(const std::pair<std::string,std::string> & style)489 bool DOMList::SetSpecializedStyle(const std::pair<std::string, std::string>& style)
490 {
491     static const LinearMapNode<bool (*)(const std::string& val, DOMList& list)> styleOperators[] = {
492         { DOM_ALIGN_ITEMS,
493             [](const std::string& val, DOMList& list) {
494               list.crossAxisAlign_ = ConvertStrToFlexAlign(val);
495               return true;
496             } },
497         { DOM_BACKGROUND_COLOR,
498             [](const std::string& val, DOMList& list) {
499               // The list component uses backgroundColor as the foreground color.
500               // The backgroundColor still needs to be set to the background color of the box component, so return
501               // false.
502               list.backgroundColor_ = list.ParseColor(val);
503               return false;
504             } },
505         { DOM_LIST_COLUMNS,
506             [](const std::string& val, DOMList& list) {
507               list.listColumns_ = StringUtils::StringToInt(val);
508               return true;
509             } },
510         { DOM_LIST_DIVIDER_COLOR,
511             [](const std::string& val, DOMList& list) {
512               list.dividerColor_ = list.ParseColor(val);
513               return true;
514             } },
515         { DOM_LIST_DIVIDER_HEIGHT,
516             [](const std::string& val, DOMList& list) {
517               list.dividerHeight_ = list.ParseDimension(val);
518               return true;
519             } },
520         { DOM_LIST_DIVIDER_LENGTH,
521             [](const std::string& val, DOMList& list) {
522               list.dividerLength_ = list.ParseDimension(val);
523               return true;
524             } },
525         { DOM_LIST_DIVIDER_ORIGIN,
526             [](const std::string& val, DOMList& list) {
527               list.dividerOrigin_ = list.ParseDimension(val);
528               return true;
529             } },
530         { DOM_FADE_COLOR,
531             [](const std::string& val, DOMList& list) {
532               list.fadeColor_ = list.ParseColor(val);
533               return true;
534             } },
535         { DOM_FLEX_DIRECTION,
536             [](const std::string& val, DOMList& list) {
537                 LOGD("DOMList::SetSpecializedStyle, val:%{public}s ", val.c_str());
538                 if (val == DOM_FLEX_ROW) {
539                     list.flexDirection_ = FlexDirection::ROW;
540                 } else if (val == DOM_FLEX_ROW_REVERSE) {
541                     LOGD("DOMList::SetSpecializedStyle, val:%{public}s ,rv:%{public}s ", val.c_str(),
542                         DOM_FLEX_ROW_REVERSE);
543                     list.flexDirection_ = FlexDirection::ROW_REVERSE;
544                 } else if (val == DOM_FLEX_COLUMN_REVERSE) {
545                     LOGD("DOMList::SetSpecializedStyle, val:%{public}s ,cv:%{public}s ", val.c_str(),
546                         DOM_FLEX_COLUMN_REVERSE);
547                     list.flexDirection_ = FlexDirection::COLUMN_REVERSE;
548                 } else {
549                     list.flexDirection_ = FlexDirection::COLUMN;
550                 }
551                 return true;
552             } },
553         { DOM_LIST_ITEM_EXTENT,
554             [](const std::string& val, DOMList& list) {
555                 list.itemExtent_ = list.ParseDimension(val);
556                 return true;
557             } },
558         { DOM_SCROLL_OVER_SCROLL_EFFECT,
559             [](const std::string& val, DOMList& list) {
560                 if (val == DOM_SCROLL_EFFECT_SPRING) {
561                     list.edgeEffect_ = EdgeEffect::SPRING;
562                 } else if (val == DOM_SCROLL_EFFECT_FADE) {
563                     list.edgeEffect_ = EdgeEffect::FADE;
564                 } else {
565                     list.edgeEffect_ = EdgeEffect::NONE;
566                 }
567                 return true;
568             } },
569         { DOM_SCROLL_SCROLLBAR_COLOR,
570             [](const std::string& val, DOMList& list) {
571                 list.scrollbarColor_.first = true;
572                 list.scrollbarColor_.second = list.ParseColor(val);
573                 return true;
574             } },
575         { DOM_SCROLL_SCROLLBAR_OFFSET,
576             [](const std::string& val, DOMList& list) {
577                 std::vector<std::string> offset;
578                 OHOS::Ace::StringUtils::StringSplitter(val, ',', offset);
579                 list.scrollbarPositionX_.first = true;
580                 auto position = list.ParseDimension(offset[0]);
581                 list.scrollbarPositionX_.second = position.IsValid() ? position : Dimension();
582                 if (offset.size() > 1) {
583                     list.scrollbarPositionY_.first = true;
584                     auto positionY = list.ParseDimension(offset[1]);
585                     list.scrollbarPositionY_.second = positionY.IsValid() ? positionY : Dimension();
586                 }
587                 return true;
588             } },
589         { DOM_SCROLL_SCROLLBAR_WIDTH,
590             [](const std::string& val, DOMList& list) {
591                 list.scrollbarWidth_.first = true;
592                 auto width = list.ParseDimension(val);
593                 list.scrollbarWidth_.second = width.IsValid() ? width : Dimension();
594                 return true;
595             } },
596         { DOM_SCROLL_SCROLLBAR_POSITION,
597             [](const std::string& val, DOMList& list) {
598                 list.scrollbarPositionX_.first = true;
599                 auto position = list.ParseDimension(val);
600                 list.scrollbarPositionX_.second = position.IsValid() ? position : Dimension();
601                 return true;
602             } },
603     };
604     auto operatorIter = BinarySearchFindIndex(styleOperators, ArraySize(styleOperators), style.first.c_str());
605     if (operatorIter != -1) {
606         return styleOperators[operatorIter].value(style.second, *this);
607     }
608     return false;
609 }
610 
OnChildNodeAdded(const RefPtr<DOMNode> & child,int32_t slot)611 void DOMList::OnChildNodeAdded(const RefPtr<DOMNode>& child, int32_t slot)
612 {
613     auto childListItem = AceType::DynamicCast<DOMListItem>(child);
614     if (!childListItem) {
615         LOGE("child is not list item, add to list failed.");
616         return;
617     }
618 
619     // childIndex is generated by js framework, just the position of this new list item should be added into the list.
620     auto childIndex = childListItem->GetItemIndex();
621     LOGD("DOMList AddChild %{public}s, childIndex: %{public}d", child->GetTag().c_str(), childIndex);
622     if (childIndex != DEFAULT_NODE_INDEX) {
623         if (indexer_) {
624             needUpdateIds_ = true;
625             indexerComponent_->InsertChild(childIndex, child->GetRootComponent());
626         } else {
627             listComponent_->InsertChild(childIndex, child->GetRootComponent());
628         }
629     } else {
630         if (indexer_) {
631             needUpdateIds_ = true;
632             indexerComponent_->AppendChild(child->GetRootComponent());
633         } else {
634             listComponent_->AppendChild(child->GetRootComponent());
635         }
636     }
637     auto item = AceType::DynamicCast<ListItemComponent>(childListItem->GetSpecializedComponent());
638     if (!item) {
639         return;
640     }
641     if (!selectedItem_.empty() && !childListItem->GetItemKey().empty() &&
642         (selectedItem_ == childListItem->GetItemKey())) {
643         item->SetIsActive(true);
644     } else {
645         item->SetIsActive(false);
646     }
647 }
648 
CallSpecializedMethod(const std::string & method,const std::string & args)649 void DOMList::CallSpecializedMethod(const std::string& method, const std::string& args)
650 {
651     if (method == DOM_LIST_METHOD_SCROLL_TO) {
652         std::unique_ptr<JsonValue> argsValue = JsonUtil::ParseJsonString(args);
653         if (!argsValue || !argsValue->IsArray() || argsValue->GetArraySize() != 1) {
654             LOGE("parse args error");
655             return;
656         }
657         std::unique_ptr<JsonValue> indexValue = argsValue->GetArrayItem(0)->GetValue("index");
658         if (!indexValue || !indexValue->IsNumber()) {
659             LOGE("get index failed");
660             return;
661         }
662         int32_t index = indexValue->GetInt();
663         ScrollToMethod(index);
664     } else if (method == DOM_LIST_METHOD_SCROLL_ARROW) {
665         std::unique_ptr<JsonValue> argsValue = JsonUtil::ParseJsonString(args);
666         if (!argsValue || !argsValue->IsArray() || argsValue->GetArraySize() != 1) {
667             LOGE("parse args error");
668             return;
669         }
670         std::unique_ptr<JsonValue> scrollArrowParams = argsValue->GetArrayItem(0);
671         bool reverse = scrollArrowParams->GetBool("reverse", false);
672         bool isSmooth = scrollArrowParams->GetBool("smooth", false);
673         ScrollArrowMethod(reverse, isSmooth);
674     } else if (method == DOM_LIST_METHOD_SCROLL_TOP || method == DOM_LIST_METHOD_SCROLL_BOTTOM) {
675         ScrollToEdgeMethod(method, args);
676     } else if (method == DOM_LIST_METHOD_SCROLL_PAGE) {
677         ScrollPageMethod(method, args);
678     } else if (method == DOM_LIST_METHOD_EXPAND_GROUP || method == DOM_LIST_METHOD_COLLAPSE_GROUP) {
679         std::unique_ptr<JsonValue> argsValue = JsonUtil::ParseJsonString(args);
680         std::string groupId;
681         if (argsValue && argsValue->IsArray() && argsValue->GetArraySize() == 1) {
682             std::unique_ptr<JsonValue> expandParams = argsValue->GetArrayItem(0);
683             std::unique_ptr<JsonValue> value = expandParams->GetValue("groupid");
684             if (value && value->IsString()) {
685                 groupId = value->GetString();
686             }
687         }
688         if (method == DOM_LIST_METHOD_EXPAND_GROUP) {
689             ExpandGroup(groupId, true);
690         } else {
691             ExpandGroup(groupId, false);
692         }
693     } else if (method == DOM_ROTATION) {
694         auto controller = listComponent_->GetRotationController();
695         if (controller) {
696             LOGD("Rotation focus list request");
697             controller->RequestRotation(true);
698         }
699     } else {
700         LOGE("CallSpecializedMethod: undefined method :%{private}s", method.c_str());
701         EventReport::SendComponentException(ComponentExcepType::LIST_COMPONENT_ERR);
702     }
703 }
704 
OnScrollBy(double dx,double dy,bool isSmooth)705 void DOMList::OnScrollBy(double dx, double dy, bool isSmooth)
706 {
707     ScrollByMethod(dx, dy, isSmooth);
708 }
709 
ExpandGroup(const std::string & groupId,bool expand)710 void DOMList::ExpandGroup(const std::string& groupId, bool expand)
711 {
712     if (!listComponent_) {
713         LOGE("DOMList ExpandGroup listComponent_ is null");
714         EventReport::SendComponentException(ComponentExcepType::LIST_COMPONENT_ERR);
715         return;
716     }
717     if (groupId.empty()) {
718         listComponent_->SetGroupState(INDEX_EXPAND_ALL, expand);
719     }
720     auto children = GetChildList();
721     for (const auto& child : children) {
722         auto itemGroup = AceType::DynamicCast<DOMListItemGroup>(child);
723         if (!itemGroup) {
724             continue;
725         }
726         if (groupId.empty() || groupId == itemGroup->GetGroupId()) {
727             if (!groupId.empty()) {
728                 listComponent_->SetGroupState(itemGroup->GetItemIndex(), expand);
729                 itemGroup->Update();
730                 break;
731             }
732             itemGroup->Update();
733         }
734     }
735 }
736 
ScrollToMethod(int32_t index)737 void DOMList::ScrollToMethod(int32_t index)
738 {
739     if (!listComponent_) {
740         LOGE("DOMList ScrollToMethod listComponent_ is null");
741         EventReport::SendComponentException(ComponentExcepType::LIST_COMPONENT_ERR);
742         return;
743     }
744     auto controller = listComponent_->GetPositionController();
745     if (!controller) {
746         LOGE("get controller failed");
747         EventReport::SendComponentException(ComponentExcepType::LIST_COMPONENT_ERR);
748         return;
749     }
750     controller->JumpTo(index);
751 }
752 
ScrollByMethod(double x,double y,bool isSmooth)753 void DOMList::ScrollByMethod(double x, double y, bool isSmooth)
754 {
755     if (!listComponent_) {
756         LOGE("DOMList ScrollByMethod listComponent_ is null");
757         EventReport::SendComponentException(ComponentExcepType::LIST_COMPONENT_ERR);
758         return;
759     }
760     auto controller = listComponent_->GetPositionController();
761     if (!controller) {
762         LOGE("get controller failed");
763         EventReport::SendComponentException(ComponentExcepType::LIST_COMPONENT_ERR);
764         return;
765     }
766     controller->ScrollBy(x, y, isSmooth);
767 }
768 
ScrollArrowMethod(bool reverse,bool isSmooth)769 void DOMList::ScrollArrowMethod(bool reverse, bool isSmooth)
770 {
771     if (!listComponent_) {
772         LOGE("DOMList ScrollArrowMethod listComponent_ is null");
773         EventReport::SendComponentException(ComponentExcepType::LIST_COMPONENT_ERR);
774         return;
775     }
776     auto controller = listComponent_->GetPositionController();
777     if (!controller) {
778         LOGE("get controller failed");
779         EventReport::SendComponentException(ComponentExcepType::LIST_COMPONENT_ERR);
780         return;
781     }
782     controller->ScrollArrow(scrollDistance_, reverse, isSmooth);
783 }
784 
ScrollToEdgeMethod(const std::string & method,const std::string & args)785 void DOMList::ScrollToEdgeMethod(const std::string& method, const std::string& args)
786 {
787     if (!listComponent_) {
788         LOGE("DOMList ScrollToEdgeMethod listComponent_ is null");
789         EventReport::SendComponentException(ComponentExcepType::LIST_COMPONENT_ERR);
790         return;
791     }
792     auto controller = listComponent_->GetPositionController();
793     if (!controller) {
794         LOGE("get controller failed");
795         EventReport::SendComponentException(ComponentExcepType::LIST_COMPONENT_ERR);
796         return;
797     }
798 
799     std::unique_ptr<JsonValue> argsValue = JsonUtil::ParseJsonString(args);
800     if (!argsValue || !argsValue->IsArray() || argsValue->GetArraySize() != 1) {
801         LOGE("parse args error");
802         return;
803     }
804     std::unique_ptr<JsonValue> params = argsValue->GetArrayItem(0);
805     bool isSmooth = params->GetBool("smooth", false);
806     if (method == DOM_LIST_METHOD_SCROLL_TOP) {
807         controller->ScrollToEdge(ScrollEdgeType::SCROLL_TOP, isSmooth);
808     } else {
809         controller->ScrollToEdge(ScrollEdgeType::SCROLL_BOTTOM, isSmooth);
810     }
811 }
812 
ScrollPageMethod(const std::string & method,const std::string & args)813 void DOMList::ScrollPageMethod(const std::string& method, const std::string& args)
814 {
815     if (!listComponent_) {
816         LOGE("DOMList ScrollPageMethod listComponent_ is null");
817         EventReport::SendComponentException(ComponentExcepType::LIST_COMPONENT_ERR);
818         return;
819     }
820     auto controller = listComponent_->GetPositionController();
821     if (!controller) {
822         LOGE("get controller failed");
823         EventReport::SendComponentException(ComponentExcepType::LIST_COMPONENT_ERR);
824         return;
825     }
826 
827     std::unique_ptr<JsonValue> argsValue = JsonUtil::ParseJsonString(args);
828     if (!argsValue || !argsValue->IsArray() || argsValue->GetArraySize() != 1) {
829         LOGE("parse args error");
830         return;
831     }
832     std::unique_ptr<JsonValue> params = argsValue->GetArrayItem(0);
833     bool reverse = params->GetBool("reverse", false);
834     bool isSmooth = params->GetBool("smooth", false);
835     controller->ScrollPage(reverse, isSmooth);
836 }
837 
GetCurrentOffset() const838 Offset DOMList::GetCurrentOffset() const
839 {
840     if (!listComponent_) {
841         LOGE("DOMList GetCurrentOffset listComponent_ is null");
842         EventReport::SendComponentException(ComponentExcepType::LIST_COMPONENT_ERR);
843         return Offset::Zero();
844     }
845     auto controller = listComponent_->GetPositionController();
846     if (!controller) {
847         LOGE("get controller failed");
848         EventReport::SendComponentException(ComponentExcepType::LIST_COMPONENT_ERR);
849         return Offset::Zero();
850     }
851     return controller->GetCurrentOffset();
852 }
853 
OnChildNodeRemoved(const RefPtr<DOMNode> & child)854 void DOMList::OnChildNodeRemoved(const RefPtr<DOMNode>& child)
855 {
856     if (!listComponent_ || !child) {
857         LOGE("DOMList OnChildNodeRemoved listComponent_ is null");
858         return;
859     }
860     LOGD("DOMList %{public}s, remove child %{public}s", GetTag().c_str(), child->GetTag().c_str());
861     listComponent_->RemoveChild(child->GetRootComponent());
862 }
863 
PrepareSpecializedComponent()864 void DOMList::PrepareSpecializedComponent()
865 {
866     if (indexer_) {
867         CreateOrUpdateIndexer();
868     } else {
869         CreateOrUpdateList();
870     }
871 }
872 
OnMounted(const RefPtr<DOMNode> & parentNode)873 void DOMList::OnMounted(const RefPtr<DOMNode>& parentNode)
874 {
875     auto parent = parentNode;
876     while (parent) {
877         if (parent->GetTag() == DOM_NODE_TAG_REFRESH) {
878             listComponent_->SetInRefresh(true);
879             break;
880         }
881         parent = parent->GetParentNode();
882     }
883 }
884 
GetAccessibilityNode()885 RefPtr<AccessibilityNode> DOMList::GetAccessibilityNode()
886 {
887     auto pipelineContext = pipelineContext_.Upgrade();
888     if (!pipelineContext) {
889         return RefPtr<AccessibilityNode>();
890     }
891     auto accessibilityManager = pipelineContext->GetAccessibilityManager();
892     if (!accessibilityManager) {
893         return RefPtr<AccessibilityNode>();
894     }
895     auto accessibilityNode = accessibilityManager->GetAccessibilityNodeById(GetNodeId());
896     if (!accessibilityNode) {
897         return RefPtr<AccessibilityNode>();
898     }
899 
900     return accessibilityNode;
901 }
902 
UpdateAccessibilityOrder()903 void DOMList::UpdateAccessibilityOrder()
904 {
905     auto accessibilityNode = GetAccessibilityNode();
906     if (!accessibilityNode) {
907         return;
908     }
909 
910     if (listComponent_) {
911         listComponent_->UpdateListItemIndex();
912     }
913 
914     children_.sort([](const RefPtr<DOMNode>& node1, const RefPtr<DOMNode>& node2) {
915         RefPtr<ListItemComponent> item1, item2;
916         auto itemNode1 = DOMListItem::GetDOMListItem(node1);
917         auto itemNode2 = DOMListItem::GetDOMListItem(node2);
918         if (itemNode1) {
919             item1 = itemNode1->GetListItemComponent();
920         }
921         if (itemNode2) {
922             item2 = itemNode2->GetListItemComponent();
923         }
924         if (item1 && item2) {
925             return item1->GetIndex() < item2->GetIndex();
926         }
927         return false;
928     });
929 
930     std::list<RefPtr<AccessibilityNode>> children;
931     auto nodes = accessibilityNode->GetChildList();
932     for (const auto& child : children_) {
933         auto item = DOMListItem::GetDOMListItem(child);
934         if (item) {
935             auto it = std::find_if(
936                 nodes.begin(), nodes.end(), [nodeId = item->GetNodeId()](const RefPtr<AccessibilityNode>& node) {
937                     return node->GetNodeId() == nodeId;
938                 });
939             if (it != nodes.end()) {
940                 children.emplace_back(*it);
941             }
942         }
943     }
944     if (!children.empty()) {
945         accessibilityNode->ResetChildList(children);
946     } else {
947         LOGW("Update accessibility node order failed.");
948     }
949 }
950 
UpdateAccessibilityByVisible()951 void DOMList::UpdateAccessibilityByVisible()
952 {
953     auto pipelineContext = pipelineContext_.Upgrade();
954     if (!pipelineContext) {
955         return;
956     }
957     auto accessibilityManager = pipelineContext->GetAccessibilityManager();
958     if (!accessibilityManager) {
959         return;
960     }
961     auto accessibilityNode = accessibilityManager->GetAccessibilityNodeById(GetNodeId());
962     if (!accessibilityNode) {
963         return;
964     }
965 
966     bool visibleRange = false;
967     std::list<RefPtr<AccessibilityNode>> children;
968     for (auto& child : children_) {
969         auto childAccessibilityNode = accessibilityManager->GetAccessibilityNodeById(child->GetNodeId());
970         if (childAccessibilityNode &&
971             childAccessibilityNode->GetWidth() != 0 && childAccessibilityNode->GetHeight() != 0) {
972             children.emplace_back(childAccessibilityNode);
973             visibleRange = true;
974         } else {
975             if (visibleRange) {
976                 break; // Just load the visible range item.
977             }
978         }
979     }
980     if (!children.empty()) {
981         accessibilityNode->ResetChildList(children);
982     }
983 }
984 
OnPageLoadFinish()985 void DOMList::OnPageLoadFinish()
986 {
987     if (listComponent_) {
988         listComponent_->SetPageReady(true);
989     }
990 
991     auto accessibilityNode = GetAccessibilityNode();
992     if (!accessibilityNode) {
993         return;
994     }
995 
996     accessibilityNode->SetActionUpdateIdsImpl([weakList = AceType::WeakClaim(this), indexer = this->indexer_]() {
997         auto list = weakList.Upgrade();
998         if (list) {
999             if (indexer && list->needUpdateIds_) {
1000                 list->UpdateAccessibilityOrder();
1001                 list->needUpdateIds_ = false;
1002             }
1003 
1004             list->UpdateAccessibilityByVisible();
1005         }
1006     });
1007 }
1008 
AdjustSpecialParamInLiteMode()1009 void DOMList::AdjustSpecialParamInLiteMode()
1010 {
1011     itemScale_ = false;
1012 }
1013 
1014 } // namespace OHOS::Ace::Framework
1015