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 isWatchOrWearable = SystemProperties::GetDeviceType() == DeviceType::WATCH ||
404 SystemProperties::GetDeviceType() == DeviceType::WEARABLE;
405 bool isCircle = circleMode_.second ? circleMode_.first : isWatchOrWearable;
406 bool bubble = bubble_.second ? bubble_.first : true;
407 bool multiLanguage = multiLanguage_.second ? multiLanguage_.first : false;
408 if (!indexerComponent_) {
409 if (indexerAlphabet_.empty()) {
410 indexerComponent_ = AceType::MakeRefPtr<IndexerListComponent>(
411 listComponent_, isCircle, IsRightToLeft(), bubble, multiLanguage);
412 } else {
413 indexerComponent_ = AceType::MakeRefPtr<IndexerListComponent>(
414 listComponent_, indexerAlphabet_, isCircle, IsRightToLeft(), bubble, multiLanguage);
415 }
416 if (isCircle && !onIndexerChange_.IsEmpty()) {
417 indexerComponent_->SetIndexerChangeEvent(onIndexerChange_);
418 }
419 }
420 indexerComponent_->SetBubbleEnabled(bubble_.first);
421 }
422
SetScrollBar()423 void DOMList::SetScrollBar()
424 {
425 if (displayMode_ == DisplayMode::ON || displayMode_ == DisplayMode::AUTO) {
426 if (shapeMode_ == ShapeMode::ROUND || shapeMode_ == ShapeMode::RECT) {
427 scrollBar_ = AceType::MakeRefPtr<ScrollBar>(displayMode_, shapeMode_);
428 } else {
429 scrollBar_ = AceType::MakeRefPtr<ScrollBar>(displayMode_, ShapeMode::DEFAULT);
430 }
431 scrollBar_->SetPositionMode(IsRightToLeft() ? PositionMode::LEFT : PositionMode::RIGHT);
432 } else {
433 scrollBar_ = AceType::MakeRefPtr<ScrollBar>(DisplayMode::OFF, ShapeMode::DEFAULT);
434 }
435 listComponent_->SetScrollBar(scrollBar_);
436 }
437
InitScrollBarWithSpecializedStyle()438 void DOMList::InitScrollBarWithSpecializedStyle()
439 {
440 if (!scrollBar_) {
441 return;
442 }
443
444 if (scrollbarColor_.first) {
445 scrollBar_->SetForegroundColor(scrollbarColor_.second);
446 }
447 if (scrollbarWidth_.first) {
448 scrollBar_->SetInactiveWidth(scrollbarWidth_.second);
449 scrollBar_->SetNormalWidth(scrollbarWidth_.second);
450 scrollBar_->SetActiveWidth(scrollbarWidth_.second);
451 scrollBar_->SetTouchWidth(scrollbarWidth_.second);
452 }
453 if (scrollbarPositionX_.first) {
454 scrollBar_->SetPosition(scrollbarPositionX_.second);
455 }
456 }
457
SetChildActive()458 void DOMList::SetChildActive()
459 {
460 if (selectedItem_.empty()) {
461 return;
462 }
463 for (const auto& child : GetChildList()) {
464 auto childItem = AceType::DynamicCast<DOMListItem>(child);
465 if (!childItem) {
466 continue;
467 }
468 auto listItem = AceType::DynamicCast<ListItemComponent>(childItem->GetSpecializedComponent());
469 if (!listItem) {
470 continue;
471 }
472 auto itemKey = childItem->GetItemKey();
473 bool isItemActive = !itemKey.empty() && selectedItem_ == itemKey;
474 if (listItem->IsActive() != isItemActive) {
475 listItem->SetIsActive(isItemActive);
476 childItem->MarkNeedUpdate();
477 }
478 }
479 }
480
SetSpecializedStyle(const std::pair<std::string,std::string> & style)481 bool DOMList::SetSpecializedStyle(const std::pair<std::string, std::string>& style)
482 {
483 static const LinearMapNode<bool (*)(const std::string& val, DOMList& list)> styleOperators[] = {
484 { DOM_ALIGN_ITEMS,
485 [](const std::string& val, DOMList& list) {
486 list.crossAxisAlign_ = ConvertStrToFlexAlign(val);
487 return true;
488 } },
489 { DOM_BACKGROUND_COLOR,
490 [](const std::string& val, DOMList& list) {
491 // The list component uses backgroundColor as the foreground color.
492 // The backgroundColor still needs to be set to the background color of the box component, so return
493 // false.
494 list.backgroundColor_ = list.ParseColor(val);
495 return false;
496 } },
497 { DOM_LIST_COLUMNS,
498 [](const std::string& val, DOMList& list) {
499 list.listColumns_ = StringUtils::StringToInt(val);
500 return true;
501 } },
502 { DOM_LIST_DIVIDER_COLOR,
503 [](const std::string& val, DOMList& list) {
504 list.dividerColor_ = list.ParseColor(val);
505 return true;
506 } },
507 { DOM_LIST_DIVIDER_HEIGHT,
508 [](const std::string& val, DOMList& list) {
509 list.dividerHeight_ = list.ParseDimension(val);
510 return true;
511 } },
512 { DOM_LIST_DIVIDER_LENGTH,
513 [](const std::string& val, DOMList& list) {
514 list.dividerLength_ = list.ParseDimension(val);
515 return true;
516 } },
517 { DOM_LIST_DIVIDER_ORIGIN,
518 [](const std::string& val, DOMList& list) {
519 list.dividerOrigin_ = list.ParseDimension(val);
520 return true;
521 } },
522 { DOM_FADE_COLOR,
523 [](const std::string& val, DOMList& list) {
524 list.fadeColor_ = list.ParseColor(val);
525 return true;
526 } },
527 { DOM_FLEX_DIRECTION,
528 [](const std::string& val, DOMList& list) {
529 if (val == DOM_FLEX_ROW) {
530 list.flexDirection_ = FlexDirection::ROW;
531 } else if (val == DOM_FLEX_ROW_REVERSE) {
532 list.flexDirection_ = FlexDirection::ROW_REVERSE;
533 } else if (val == DOM_FLEX_COLUMN_REVERSE) {
534 list.flexDirection_ = FlexDirection::COLUMN_REVERSE;
535 } else {
536 list.flexDirection_ = FlexDirection::COLUMN;
537 }
538 return true;
539 } },
540 { DOM_LIST_ITEM_EXTENT,
541 [](const std::string& val, DOMList& list) {
542 list.itemExtent_ = list.ParseDimension(val);
543 return true;
544 } },
545 { DOM_SCROLL_OVER_SCROLL_EFFECT,
546 [](const std::string& val, DOMList& list) {
547 if (val == DOM_SCROLL_EFFECT_SPRING) {
548 list.edgeEffect_ = EdgeEffect::SPRING;
549 } else if (val == DOM_SCROLL_EFFECT_FADE) {
550 list.edgeEffect_ = EdgeEffect::FADE;
551 } else {
552 list.edgeEffect_ = EdgeEffect::NONE;
553 }
554 return true;
555 } },
556 { DOM_SCROLL_SCROLLBAR_COLOR,
557 [](const std::string& val, DOMList& list) {
558 list.scrollbarColor_.first = true;
559 list.scrollbarColor_.second = list.ParseColor(val);
560 return true;
561 } },
562 { DOM_SCROLL_SCROLLBAR_OFFSET,
563 [](const std::string& val, DOMList& list) {
564 std::vector<std::string> offset;
565 OHOS::Ace::StringUtils::StringSplitter(val, ',', offset);
566 list.scrollbarPositionX_.first = true;
567 auto position = list.ParseDimension(offset[0]);
568 list.scrollbarPositionX_.second = position.IsValid() ? position : Dimension();
569 if (offset.size() > 1) {
570 list.scrollbarPositionY_.first = true;
571 auto positionY = list.ParseDimension(offset[1]);
572 list.scrollbarPositionY_.second = positionY.IsValid() ? positionY : Dimension();
573 }
574 return true;
575 } },
576 { DOM_SCROLL_SCROLLBAR_WIDTH,
577 [](const std::string& val, DOMList& list) {
578 list.scrollbarWidth_.first = true;
579 auto width = list.ParseDimension(val);
580 list.scrollbarWidth_.second = width.IsValid() ? width : Dimension();
581 return true;
582 } },
583 { DOM_SCROLL_SCROLLBAR_POSITION,
584 [](const std::string& val, DOMList& list) {
585 list.scrollbarPositionX_.first = true;
586 auto position = list.ParseDimension(val);
587 list.scrollbarPositionX_.second = position.IsValid() ? position : Dimension();
588 return true;
589 } },
590 };
591 auto operatorIter = BinarySearchFindIndex(styleOperators, ArraySize(styleOperators), style.first.c_str());
592 if (operatorIter != -1) {
593 return styleOperators[operatorIter].value(style.second, *this);
594 }
595 return false;
596 }
597
OnChildNodeAdded(const RefPtr<DOMNode> & child,int32_t slot)598 void DOMList::OnChildNodeAdded(const RefPtr<DOMNode>& child, int32_t slot)
599 {
600 auto childListItem = AceType::DynamicCast<DOMListItem>(child);
601 if (!childListItem) {
602 return;
603 }
604
605 // childIndex is generated by js framework, just the position of this new list item should be added into the list.
606 auto childIndex = childListItem->GetItemIndex();
607 if (childIndex != DEFAULT_NODE_INDEX) {
608 if (indexer_) {
609 needUpdateIds_ = true;
610 indexerComponent_->InsertChild(childIndex, child->GetRootComponent());
611 } else {
612 listComponent_->InsertChild(childIndex, child->GetRootComponent());
613 }
614 } else {
615 if (indexer_) {
616 needUpdateIds_ = true;
617 indexerComponent_->AppendChild(child->GetRootComponent());
618 } else {
619 listComponent_->AppendChild(child->GetRootComponent());
620 }
621 }
622 auto item = AceType::DynamicCast<ListItemComponent>(childListItem->GetSpecializedComponent());
623 if (!item) {
624 return;
625 }
626 if (!selectedItem_.empty() && !childListItem->GetItemKey().empty() &&
627 (selectedItem_ == childListItem->GetItemKey())) {
628 item->SetIsActive(true);
629 } else {
630 item->SetIsActive(false);
631 }
632 }
633
CallSpecializedMethod(const std::string & method,const std::string & args)634 void DOMList::CallSpecializedMethod(const std::string& method, const std::string& args)
635 {
636 if (method == DOM_LIST_METHOD_SCROLL_TO) {
637 std::unique_ptr<JsonValue> argsValue = JsonUtil::ParseJsonString(args);
638 if (!argsValue || !argsValue->IsArray() || argsValue->GetArraySize() != 1) {
639 LOGW("list parse args error");
640 return;
641 }
642 std::unique_ptr<JsonValue> indexValue = argsValue->GetArrayItem(0)->GetValue("index");
643 if (!indexValue || !indexValue->IsNumber()) {
644 return;
645 }
646 int32_t index = indexValue->GetInt();
647 ScrollToMethod(index);
648 } else if (method == DOM_LIST_METHOD_SCROLL_ARROW) {
649 std::unique_ptr<JsonValue> argsValue = JsonUtil::ParseJsonString(args);
650 if (!argsValue || !argsValue->IsArray() || argsValue->GetArraySize() != 1) {
651 LOGW("list parse args error");
652 return;
653 }
654 std::unique_ptr<JsonValue> scrollArrowParams = argsValue->GetArrayItem(0);
655 bool reverse = scrollArrowParams->GetBool("reverse", false);
656 bool isSmooth = scrollArrowParams->GetBool("smooth", false);
657 ScrollArrowMethod(reverse, isSmooth);
658 } else if (method == DOM_LIST_METHOD_SCROLL_TOP || method == DOM_LIST_METHOD_SCROLL_BOTTOM) {
659 ScrollToEdgeMethod(method, args);
660 } else if (method == DOM_LIST_METHOD_SCROLL_PAGE) {
661 ScrollPageMethod(method, args);
662 } else if (method == DOM_LIST_METHOD_EXPAND_GROUP || method == DOM_LIST_METHOD_COLLAPSE_GROUP) {
663 std::unique_ptr<JsonValue> argsValue = JsonUtil::ParseJsonString(args);
664 std::string groupId;
665 if (argsValue && argsValue->IsArray() && argsValue->GetArraySize() == 1) {
666 std::unique_ptr<JsonValue> expandParams = argsValue->GetArrayItem(0);
667 std::unique_ptr<JsonValue> value = expandParams->GetValue("groupid");
668 if (value && value->IsString()) {
669 groupId = value->GetString();
670 }
671 }
672 if (method == DOM_LIST_METHOD_EXPAND_GROUP) {
673 ExpandGroup(groupId, true);
674 } else {
675 ExpandGroup(groupId, false);
676 }
677 } else if (method == DOM_ROTATION) {
678 auto controller = listComponent_->GetRotationController();
679 if (controller) {
680 controller->RequestRotation(true);
681 }
682 } else {
683 EventReport::SendComponentException(ComponentExcepType::LIST_COMPONENT_ERR);
684 }
685 }
686
OnScrollBy(double dx,double dy,bool isSmooth)687 void DOMList::OnScrollBy(double dx, double dy, bool isSmooth)
688 {
689 ScrollByMethod(dx, dy, isSmooth);
690 }
691
ExpandGroup(const std::string & groupId,bool expand)692 void DOMList::ExpandGroup(const std::string& groupId, bool expand)
693 {
694 if (!listComponent_) {
695 EventReport::SendComponentException(ComponentExcepType::LIST_COMPONENT_ERR);
696 return;
697 }
698 if (groupId.empty()) {
699 listComponent_->SetGroupState(INDEX_EXPAND_ALL, expand);
700 }
701 auto children = GetChildList();
702 for (const auto& child : children) {
703 auto itemGroup = AceType::DynamicCast<DOMListItemGroup>(child);
704 if (!itemGroup) {
705 continue;
706 }
707 if (groupId.empty() || groupId == itemGroup->GetGroupId()) {
708 if (!groupId.empty()) {
709 listComponent_->SetGroupState(itemGroup->GetItemIndex(), expand);
710 itemGroup->Update();
711 break;
712 }
713 itemGroup->Update();
714 }
715 }
716 }
717
ScrollToMethod(int32_t index)718 void DOMList::ScrollToMethod(int32_t index)
719 {
720 if (!listComponent_) {
721 EventReport::SendComponentException(ComponentExcepType::LIST_COMPONENT_ERR);
722 return;
723 }
724 auto controller = listComponent_->GetPositionController();
725 if (!controller) {
726 EventReport::SendComponentException(ComponentExcepType::LIST_COMPONENT_ERR);
727 return;
728 }
729 controller->JumpTo(index);
730 }
731
ScrollByMethod(double x,double y,bool isSmooth)732 void DOMList::ScrollByMethod(double x, double y, bool isSmooth)
733 {
734 if (!listComponent_) {
735 EventReport::SendComponentException(ComponentExcepType::LIST_COMPONENT_ERR);
736 return;
737 }
738 auto controller = listComponent_->GetPositionController();
739 if (!controller) {
740 EventReport::SendComponentException(ComponentExcepType::LIST_COMPONENT_ERR);
741 return;
742 }
743 controller->ScrollBy(x, y, isSmooth);
744 }
745
ScrollArrowMethod(bool reverse,bool isSmooth)746 void DOMList::ScrollArrowMethod(bool reverse, bool isSmooth)
747 {
748 if (!listComponent_) {
749 EventReport::SendComponentException(ComponentExcepType::LIST_COMPONENT_ERR);
750 return;
751 }
752 auto controller = listComponent_->GetPositionController();
753 if (!controller) {
754 EventReport::SendComponentException(ComponentExcepType::LIST_COMPONENT_ERR);
755 return;
756 }
757 controller->ScrollArrow(scrollDistance_, reverse, isSmooth);
758 }
759
ScrollToEdgeMethod(const std::string & method,const std::string & args)760 void DOMList::ScrollToEdgeMethod(const std::string& method, const std::string& args)
761 {
762 if (!listComponent_) {
763 EventReport::SendComponentException(ComponentExcepType::LIST_COMPONENT_ERR);
764 return;
765 }
766 auto controller = listComponent_->GetPositionController();
767 if (!controller) {
768 EventReport::SendComponentException(ComponentExcepType::LIST_COMPONENT_ERR);
769 return;
770 }
771
772 std::unique_ptr<JsonValue> argsValue = JsonUtil::ParseJsonString(args);
773 if (!argsValue || !argsValue->IsArray() || argsValue->GetArraySize() != 1) {
774 LOGW("list parse args error");
775 return;
776 }
777 std::unique_ptr<JsonValue> params = argsValue->GetArrayItem(0);
778 bool isSmooth = params->GetBool("smooth", false);
779 if (method == DOM_LIST_METHOD_SCROLL_TOP) {
780 controller->ScrollToEdge(ScrollEdgeType::SCROLL_TOP, isSmooth);
781 } else {
782 controller->ScrollToEdge(ScrollEdgeType::SCROLL_BOTTOM, isSmooth);
783 }
784 }
785
ScrollPageMethod(const std::string & method,const std::string & args)786 void DOMList::ScrollPageMethod(const std::string& method, const std::string& args)
787 {
788 if (!listComponent_) {
789 EventReport::SendComponentException(ComponentExcepType::LIST_COMPONENT_ERR);
790 return;
791 }
792 auto controller = listComponent_->GetPositionController();
793 if (!controller) {
794 EventReport::SendComponentException(ComponentExcepType::LIST_COMPONENT_ERR);
795 return;
796 }
797
798 std::unique_ptr<JsonValue> argsValue = JsonUtil::ParseJsonString(args);
799 if (!argsValue || !argsValue->IsArray() || argsValue->GetArraySize() != 1) {
800 LOGW("list parse args error");
801 return;
802 }
803 std::unique_ptr<JsonValue> params = argsValue->GetArrayItem(0);
804 bool reverse = params->GetBool("reverse", false);
805 bool isSmooth = params->GetBool("smooth", false);
806 controller->ScrollPage(reverse, isSmooth);
807 }
808
GetCurrentOffset() const809 Offset DOMList::GetCurrentOffset() const
810 {
811 if (!listComponent_) {
812 EventReport::SendComponentException(ComponentExcepType::LIST_COMPONENT_ERR);
813 return Offset::Zero();
814 }
815 auto controller = listComponent_->GetPositionController();
816 if (!controller) {
817 EventReport::SendComponentException(ComponentExcepType::LIST_COMPONENT_ERR);
818 return Offset::Zero();
819 }
820 return controller->GetCurrentOffset();
821 }
822
OnChildNodeRemoved(const RefPtr<DOMNode> & child)823 void DOMList::OnChildNodeRemoved(const RefPtr<DOMNode>& child)
824 {
825 if (!listComponent_ || !child) {
826 return;
827 }
828 listComponent_->RemoveChild(child->GetRootComponent());
829 }
830
PrepareSpecializedComponent()831 void DOMList::PrepareSpecializedComponent()
832 {
833 if (indexer_) {
834 CreateOrUpdateIndexer();
835 } else {
836 CreateOrUpdateList();
837 }
838 }
839
OnMounted(const RefPtr<DOMNode> & parentNode)840 void DOMList::OnMounted(const RefPtr<DOMNode>& parentNode)
841 {
842 auto parent = parentNode;
843 while (parent) {
844 if (parent->GetTag() == DOM_NODE_TAG_REFRESH) {
845 listComponent_->SetInRefresh(true);
846 break;
847 }
848 parent = parent->GetParentNode();
849 }
850 }
851
GetAccessibilityNode()852 RefPtr<AccessibilityNode> DOMList::GetAccessibilityNode()
853 {
854 auto pipelineContext = pipelineContext_.Upgrade();
855 if (!pipelineContext) {
856 return RefPtr<AccessibilityNode>();
857 }
858 auto accessibilityManager = pipelineContext->GetAccessibilityManager();
859 if (!accessibilityManager) {
860 return RefPtr<AccessibilityNode>();
861 }
862 auto accessibilityNode = accessibilityManager->GetAccessibilityNodeById(GetNodeId());
863 if (!accessibilityNode) {
864 return RefPtr<AccessibilityNode>();
865 }
866
867 return accessibilityNode;
868 }
869
UpdateAccessibilityOrder()870 void DOMList::UpdateAccessibilityOrder()
871 {
872 auto accessibilityNode = GetAccessibilityNode();
873 if (!accessibilityNode) {
874 return;
875 }
876
877 if (listComponent_) {
878 listComponent_->UpdateListItemIndex();
879 }
880
881 children_.sort([](const RefPtr<DOMNode>& node1, const RefPtr<DOMNode>& node2) {
882 RefPtr<ListItemComponent> item1, item2;
883 auto itemNode1 = DOMListItem::GetDOMListItem(node1);
884 auto itemNode2 = DOMListItem::GetDOMListItem(node2);
885 if (itemNode1) {
886 item1 = itemNode1->GetListItemComponent();
887 }
888 if (itemNode2) {
889 item2 = itemNode2->GetListItemComponent();
890 }
891 if (item1 && item2) {
892 return item1->GetIndex() < item2->GetIndex();
893 }
894 return false;
895 });
896
897 std::list<RefPtr<AccessibilityNode>> children;
898 auto nodes = accessibilityNode->GetChildList();
899 for (const auto& child : children_) {
900 auto item = DOMListItem::GetDOMListItem(child);
901 if (item) {
902 auto it = std::find_if(
903 nodes.begin(), nodes.end(), [nodeId = item->GetNodeId()](const RefPtr<AccessibilityNode>& node) {
904 return node->GetNodeId() == nodeId;
905 });
906 if (it != nodes.end()) {
907 children.emplace_back(*it);
908 }
909 }
910 }
911 if (!children.empty()) {
912 accessibilityNode->ResetChildList(children);
913 }
914 }
915
UpdateAccessibilityByVisible()916 void DOMList::UpdateAccessibilityByVisible()
917 {
918 auto pipelineContext = pipelineContext_.Upgrade();
919 if (!pipelineContext) {
920 return;
921 }
922 auto accessibilityManager = pipelineContext->GetAccessibilityManager();
923 if (!accessibilityManager) {
924 return;
925 }
926 auto accessibilityNode = accessibilityManager->GetAccessibilityNodeById(GetNodeId());
927 if (!accessibilityNode) {
928 return;
929 }
930
931 bool visibleRange = false;
932 std::list<RefPtr<AccessibilityNode>> children;
933 for (auto& child : children_) {
934 auto childAccessibilityNode = accessibilityManager->GetAccessibilityNodeById(child->GetNodeId());
935 if (childAccessibilityNode &&
936 childAccessibilityNode->GetWidth() != 0 && childAccessibilityNode->GetHeight() != 0) {
937 children.emplace_back(childAccessibilityNode);
938 visibleRange = true;
939 } else {
940 if (visibleRange) {
941 break; // Just load the visible range item.
942 }
943 }
944 }
945 if (!children.empty()) {
946 accessibilityNode->ResetChildList(children);
947 }
948 }
949
OnPageLoadFinish()950 void DOMList::OnPageLoadFinish()
951 {
952 if (listComponent_) {
953 listComponent_->SetPageReady(true);
954 }
955
956 auto accessibilityNode = GetAccessibilityNode();
957 if (!accessibilityNode) {
958 return;
959 }
960
961 accessibilityNode->SetActionUpdateIdsImpl([weakList = AceType::WeakClaim(this), indexer = this->indexer_]() {
962 auto list = weakList.Upgrade();
963 if (list) {
964 if (indexer && list->needUpdateIds_) {
965 list->UpdateAccessibilityOrder();
966 list->needUpdateIds_ = false;
967 }
968
969 list->UpdateAccessibilityByVisible();
970 }
971 });
972 }
973
AdjustSpecialParamInLiteMode()974 void DOMList::AdjustSpecialParamInLiteMode()
975 {
976 itemScale_ = false;
977 }
978
979 } // namespace OHOS::Ace::Framework
980