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