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