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