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