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 LOGI("IndexerFlag:%{public}d, AlphabetCount:%{public}d.", indexer_, alphabetCount);
218 }
219
SupportChainAnimation() const220 bool DOMList::SupportChainAnimation() const
221 {
222 return chainAnimation_ && !indexer_;
223 }
224
AddSpecializedEvent(int32_t pageId,const std::string & event)225 bool DOMList::AddSpecializedEvent(int32_t pageId, const std::string& event)
226 {
227 // static linear map must be sorted by key.
228 static const LinearMapNode<void (*)(int32_t, DOMList&)> eventOperators[] = {
229 {
230 DOM_LIST_EVENT_INDEXER_CHANGE,
231 [](int32_t pageId, DOMList& list) {
232 list.onIndexerChange_ = EventMarker(list.GetNodeIdForEvent(), DOM_LIST_EVENT_INDEXER_CHANGE, pageId);
233 },
234 },
235 {
236 LIST_EVENT_REQUEST_ITEM,
237 [](int32_t pageId, DOMList& list) {
238 list.onRequestItem_ = EventMarker(list.GetNodeIdForEvent(), LIST_EVENT_REQUEST_ITEM, pageId);
239 },
240 },
241 {
242 DOM_LIST_EVENT_SCROLL,
243 [](int32_t pageId, DOMList& list) {
244 list.onScroll_ = EventMarker(list.GetNodeIdForEvent(), DOM_LIST_EVENT_SCROLL, pageId);
245 },
246 },
247 {
248 DOM_LIST_EVENT_SCROLL_BOTTOM,
249 [](int32_t pageId, DOMList& list) {
250 list.onScrollBottom_ = EventMarker(list.GetNodeIdForEvent(), DOM_LIST_EVENT_SCROLL_BOTTOM, pageId);
251 },
252 },
253 {
254 DOM_LIST_EVENT_SCROLL_END,
255 [](int32_t pageId, DOMList& list) {
256 list.onScrollEnd_ = EventMarker(list.GetNodeIdForEvent(), DOM_LIST_EVENT_SCROLL_END, pageId);
257 },
258 },
259 {
260 DOM_LIST_EVENT_SCROLL_TOP,
261 [](int32_t pageId, DOMList& list) {
262 list.onScrollTop_ = EventMarker(list.GetNodeIdForEvent(), DOM_LIST_EVENT_SCROLL_TOP, pageId);
263 },
264 },
265 {
266 DOM_LIST_EVENT_SCROLL_TOUCH_UP,
267 [](int32_t pageId, DOMList& list) {
268 list.onScrollTouchUp_ = EventMarker(list.GetNodeIdForEvent(), DOM_LIST_EVENT_SCROLL_TOUCH_UP, pageId);
269 },
270 },
271 };
272 auto iter = BinarySearchFindIndex(eventOperators, ArraySize(eventOperators), event.c_str());
273 if (iter != -1) {
274 eventOperators[iter].value(pageId, *this);
275 return true;
276 }
277 return false;
278 }
279
ResetInitializedStyle()280 void DOMList::ResetInitializedStyle()
281 {
282 if (!listComponent_) {
283 LOGE("list component is null, reset style failed.");
284 EventReport::SendComponentException(ComponentExcepType::LIST_COMPONENT_ERR);
285 return;
286 }
287 // list theme
288 RefPtr<ListTheme> listTheme = GetTheme<ListTheme>();
289 if (listTheme) {
290 listComponent_->InitStyle(listTheme);
291 if (declaration_) {
292 auto& backgroundStyle =
293 static_cast<CommonBackgroundStyle&>(declaration_->GetStyle(StyleTag::COMMON_BACKGROUND_STYLE));
294 if (declaration_->HasBackGroundColor() && backgroundStyle.IsValid()) {
295 listComponent_->SetBackgroundColor(backgroundStyle.backgroundColor);
296 }
297 }
298 scrollDistance_ = listTheme->GetScrollDistance();
299 }
300
301 if (!scrollBar_ || displayMode_ == DisplayMode::OFF) {
302 return;
303 }
304 // scrollBar_ theme
305 RefPtr<ScrollBarTheme> scrollBarTheme = GetTheme<ScrollBarTheme>();
306 if (scrollBarTheme) {
307 if (scrollBar_->GetShapeMode() == ShapeMode::DEFAULT) {
308 scrollBar_->SetShapeMode(scrollBarTheme->GetShapeMode());
309 }
310 scrollBar_->SetInactiveWidth(scrollBarTheme->GetNormalWidth());
311 scrollBar_->SetNormalWidth(scrollBarTheme->GetNormalWidth());
312 scrollBar_->SetActiveWidth(scrollBarTheme->GetActiveWidth());
313 scrollBar_->SetMinHeight(scrollBarTheme->GetMinHeight());
314 scrollBar_->SetMinDynamicHeight(scrollBarTheme->GetMinDynamicHeight());
315 if (scrollbarPositionY_.first == false) {
316 scrollBar_->SetReservedHeight(scrollBarTheme->GetReservedHeight());
317 } else {
318 scrollBar_->SetReservedHeight(scrollbarPositionY_.second);
319 }
320 scrollBar_->SetTouchWidth(scrollBarTheme->GetTouchWidth());
321 scrollBar_->SetBackgroundColor(scrollBarTheme->GetBackgroundColor());
322 scrollBar_->SetForegroundColor(scrollBarTheme->GetForegroundColor());
323 scrollBar_->SetPadding(scrollBarTheme->GetPadding());
324 }
325 }
326
CreateOrUpdateList()327 void DOMList::CreateOrUpdateList()
328 {
329 if (!listComponent_) {
330 listComponent_ = AceType::MakeRefPtr<ListComponent>();
331 }
332
333 listComponent_->SetScrollVibrate(scrollVibrate_);
334 listComponent_->MarkNeedRotationVibrate(rotationVibrate_);
335
336 listComponent_->SetDirection(flexDirection_);
337 if (flexDirection_ == FlexDirection::COLUMN || flexDirection_ == FlexDirection::COLUMN_REVERSE) {
338 listComponent_->SetScrollPage(scrollPage_);
339 boxComponent_->SetScrollPage(scrollPage_);
340 } else {
341 listComponent_->SetScrollPage(false);
342 boxComponent_->SetScrollPage(false);
343 }
344 listComponent_->SetChainProperty(chainProperty_);
345 listComponent_->SetChainAnimation(SupportChainAnimation());
346 if (useDefaultOverSpringProperty_) {
347 if (SupportChainAnimation()) {
348 listComponent_->SetOverSpringProperty(SpringChainProperty::GetDefaultOverSpringProperty());
349 } else {
350 listComponent_->SetOverSpringProperty(Scrollable::GetDefaultOverSpringProperty());
351 }
352 } else {
353 listComponent_->SetOverSpringProperty(1.0, overStiffness_, overDamping_);
354 }
355
356 listComponent_->SetFlexAlign(crossAxisAlign_);
357 listComponent_->SetRightToLeft(IsRightToLeft());
358 listComponent_->SetOnRequestItem(onRequestItem_);
359 listComponent_->SetOnScroll(onScroll_);
360 listComponent_->SetOnScrollBottom(onScrollBottom_);
361 listComponent_->SetOnScrollTop(onScrollTop_);
362 listComponent_->SetOnScrollEnd(onScrollEnd_);
363 listComponent_->SetOnScrollTouchUp(onScrollTouchUp_);
364 if (cachedCount_ > 0) {
365 listComponent_->SetCachedCount(cachedCount_);
366 }
367 listComponent_->SetBeginIndex(beginIndex_);
368 listComponent_->SetEndIndex(endIndex_);
369 listComponent_->SetRepeatedLength(repeatedLength_);
370 listComponent_->SetIndexOffset(indexOffset_);
371 listComponent_->SetColumnCount(listColumns_);
372 listComponent_->SetItemExtent(itemExtent_);
373 listComponent_->SetUpdateEffect(updateEffect_);
374 listComponent_->SetAccessibilityDisabled(accessibilityDisabled_);
375 if (listColumns_ > 1) {
376 // itemScale is not supported in 'columns > 1' case.
377 itemScale_ = false;
378 }
379 listComponent_->SetItemScale(itemScale_);
380 listComponent_->MarkCenterLayout(centerLayout_);
381 listComponent_->SetSupportItemCenter(itemCenter_);
382 if (edgeEffect_ == EdgeEffect::SPRING) {
383 listComponent_->SetScrollEffect(AceType::MakeRefPtr<ScrollSpringEffect>());
384 } else if (edgeEffect_ == EdgeEffect::FADE) {
385 listComponent_->SetScrollEffect(AceType::MakeRefPtr<ScrollFadeEffect>(fadeColor_));
386 } else {
387 listComponent_->SetScrollEffect(AceType::MakeRefPtr<ScrollEdgeEffect>(EdgeEffect::NONE));
388 }
389
390 // keep list state during update.
391 if (!listComponent_->GetPositionController()) {
392 listComponent_->SetPositionController(AceType::MakeRefPtr<ScrollPositionController>());
393 }
394 if (declaration_) {
395 declaration_->SetPositionController(listComponent_->GetPositionController());
396 }
397 listComponent_->GetPositionController()->SetInitialIndex(initialIndex_);
398 listComponent_->GetPositionController()->SetInitialOffset(initialOffset_);
399
400 if (declaration_ && !GetRotateId().IsEmpty()) {
401 listComponent_->SetOnRotateId(GetRotateId());
402 }
403
404 SetScrollBar();
405 ResetInitializedStyle();
406 InitScrollBarWithSpecializedStyle();
407 SetChildActive();
408 }
409
CreateOrUpdateIndexer()410 void DOMList::CreateOrUpdateIndexer()
411 {
412 CreateOrUpdateList();
413 bool isCircle = circleMode_.second ? circleMode_.first : SystemProperties::GetDeviceType() == DeviceType::WATCH;
414 bool bubble = bubble_.second ? bubble_.first : true;
415 bool multiLanguage = multiLanguage_.second ? multiLanguage_.first : false;
416 if (!indexerComponent_) {
417 if (indexerAlphabet_.empty()) {
418 indexerComponent_ = AceType::MakeRefPtr<IndexerListComponent>(
419 listComponent_, isCircle, IsRightToLeft(), bubble, multiLanguage);
420 } else {
421 indexerComponent_ = AceType::MakeRefPtr<IndexerListComponent>(
422 listComponent_, indexerAlphabet_, isCircle, IsRightToLeft(), bubble, multiLanguage);
423 }
424 if (isCircle && !onIndexerChange_.IsEmpty()) {
425 indexerComponent_->SetIndexerChangeEvent(onIndexerChange_);
426 }
427 }
428 indexerComponent_->SetBubbleEnabled(bubble_.first);
429 }
430
SetScrollBar()431 void DOMList::SetScrollBar()
432 {
433 if (displayMode_ == DisplayMode::ON || displayMode_ == DisplayMode::AUTO) {
434 if (shapeMode_ == ShapeMode::ROUND || shapeMode_ == ShapeMode::RECT) {
435 scrollBar_ = AceType::MakeRefPtr<ScrollBar>(displayMode_, shapeMode_);
436 } else {
437 scrollBar_ = AceType::MakeRefPtr<ScrollBar>(displayMode_, ShapeMode::DEFAULT);
438 }
439 scrollBar_->SetPositionMode(IsRightToLeft() ? PositionMode::LEFT : PositionMode::RIGHT);
440 } else {
441 scrollBar_ = AceType::MakeRefPtr<ScrollBar>(DisplayMode::OFF, ShapeMode::DEFAULT);
442 }
443 listComponent_->SetScrollBar(scrollBar_);
444 }
445
InitScrollBarWithSpecializedStyle()446 void DOMList::InitScrollBarWithSpecializedStyle()
447 {
448 if (!scrollBar_) {
449 return;
450 }
451
452 if (scrollbarColor_.first) {
453 scrollBar_->SetForegroundColor(scrollbarColor_.second);
454 }
455 if (scrollbarWidth_.first) {
456 scrollBar_->SetInactiveWidth(scrollbarWidth_.second);
457 scrollBar_->SetNormalWidth(scrollbarWidth_.second);
458 scrollBar_->SetActiveWidth(scrollbarWidth_.second);
459 scrollBar_->SetTouchWidth(scrollbarWidth_.second);
460 }
461 if (scrollbarPositionX_.first) {
462 scrollBar_->SetPosition(scrollbarPositionX_.second);
463 }
464 }
465
SetChildActive()466 void DOMList::SetChildActive()
467 {
468 if (selectedItem_.empty()) {
469 return;
470 }
471 for (const auto& child : GetChildList()) {
472 auto childItem = AceType::DynamicCast<DOMListItem>(child);
473 if (!childItem) {
474 continue;
475 }
476 auto listItem = AceType::DynamicCast<ListItemComponent>(childItem->GetSpecializedComponent());
477 if (!listItem) {
478 continue;
479 }
480 auto itemKey = childItem->GetItemKey();
481 bool isItemActive = !itemKey.empty() && selectedItem_ == itemKey;
482 if (listItem->IsActive() != isItemActive) {
483 listItem->SetIsActive(isItemActive);
484 childItem->MarkNeedUpdate();
485 }
486 }
487 }
488
SetSpecializedStyle(const std::pair<std::string,std::string> & style)489 bool DOMList::SetSpecializedStyle(const std::pair<std::string, std::string>& style)
490 {
491 static const LinearMapNode<bool (*)(const std::string& val, DOMList& list)> styleOperators[] = {
492 { DOM_ALIGN_ITEMS,
493 [](const std::string& val, DOMList& list) {
494 list.crossAxisAlign_ = ConvertStrToFlexAlign(val);
495 return true;
496 } },
497 { DOM_BACKGROUND_COLOR,
498 [](const std::string& val, DOMList& list) {
499 // The list component uses backgroundColor as the foreground color.
500 // The backgroundColor still needs to be set to the background color of the box component, so return
501 // false.
502 list.backgroundColor_ = list.ParseColor(val);
503 return false;
504 } },
505 { DOM_LIST_COLUMNS,
506 [](const std::string& val, DOMList& list) {
507 list.listColumns_ = StringUtils::StringToInt(val);
508 return true;
509 } },
510 { DOM_LIST_DIVIDER_COLOR,
511 [](const std::string& val, DOMList& list) {
512 list.dividerColor_ = list.ParseColor(val);
513 return true;
514 } },
515 { DOM_LIST_DIVIDER_HEIGHT,
516 [](const std::string& val, DOMList& list) {
517 list.dividerHeight_ = list.ParseDimension(val);
518 return true;
519 } },
520 { DOM_LIST_DIVIDER_LENGTH,
521 [](const std::string& val, DOMList& list) {
522 list.dividerLength_ = list.ParseDimension(val);
523 return true;
524 } },
525 { DOM_LIST_DIVIDER_ORIGIN,
526 [](const std::string& val, DOMList& list) {
527 list.dividerOrigin_ = list.ParseDimension(val);
528 return true;
529 } },
530 { DOM_FADE_COLOR,
531 [](const std::string& val, DOMList& list) {
532 list.fadeColor_ = list.ParseColor(val);
533 return true;
534 } },
535 { DOM_FLEX_DIRECTION,
536 [](const std::string& val, DOMList& list) {
537 LOGD("DOMList::SetSpecializedStyle, val:%{public}s ", val.c_str());
538 if (val == DOM_FLEX_ROW) {
539 list.flexDirection_ = FlexDirection::ROW;
540 } else if (val == DOM_FLEX_ROW_REVERSE) {
541 LOGD("DOMList::SetSpecializedStyle, val:%{public}s ,rv:%{public}s ", val.c_str(),
542 DOM_FLEX_ROW_REVERSE);
543 list.flexDirection_ = FlexDirection::ROW_REVERSE;
544 } else if (val == DOM_FLEX_COLUMN_REVERSE) {
545 LOGD("DOMList::SetSpecializedStyle, val:%{public}s ,cv:%{public}s ", val.c_str(),
546 DOM_FLEX_COLUMN_REVERSE);
547 list.flexDirection_ = FlexDirection::COLUMN_REVERSE;
548 } else {
549 list.flexDirection_ = FlexDirection::COLUMN;
550 }
551 return true;
552 } },
553 { DOM_LIST_ITEM_EXTENT,
554 [](const std::string& val, DOMList& list) {
555 list.itemExtent_ = list.ParseDimension(val);
556 return true;
557 } },
558 { DOM_SCROLL_OVER_SCROLL_EFFECT,
559 [](const std::string& val, DOMList& list) {
560 if (val == DOM_SCROLL_EFFECT_SPRING) {
561 list.edgeEffect_ = EdgeEffect::SPRING;
562 } else if (val == DOM_SCROLL_EFFECT_FADE) {
563 list.edgeEffect_ = EdgeEffect::FADE;
564 } else {
565 list.edgeEffect_ = EdgeEffect::NONE;
566 }
567 return true;
568 } },
569 { DOM_SCROLL_SCROLLBAR_COLOR,
570 [](const std::string& val, DOMList& list) {
571 list.scrollbarColor_.first = true;
572 list.scrollbarColor_.second = list.ParseColor(val);
573 return true;
574 } },
575 { DOM_SCROLL_SCROLLBAR_OFFSET,
576 [](const std::string& val, DOMList& list) {
577 std::vector<std::string> offset;
578 OHOS::Ace::StringUtils::StringSplitter(val, ',', offset);
579 list.scrollbarPositionX_.first = true;
580 auto position = list.ParseDimension(offset[0]);
581 list.scrollbarPositionX_.second = position.IsValid() ? position : Dimension();
582 if (offset.size() > 1) {
583 list.scrollbarPositionY_.first = true;
584 auto positionY = list.ParseDimension(offset[1]);
585 list.scrollbarPositionY_.second = positionY.IsValid() ? positionY : Dimension();
586 }
587 return true;
588 } },
589 { DOM_SCROLL_SCROLLBAR_WIDTH,
590 [](const std::string& val, DOMList& list) {
591 list.scrollbarWidth_.first = true;
592 auto width = list.ParseDimension(val);
593 list.scrollbarWidth_.second = width.IsValid() ? width : Dimension();
594 return true;
595 } },
596 { DOM_SCROLL_SCROLLBAR_POSITION,
597 [](const std::string& val, DOMList& list) {
598 list.scrollbarPositionX_.first = true;
599 auto position = list.ParseDimension(val);
600 list.scrollbarPositionX_.second = position.IsValid() ? position : Dimension();
601 return true;
602 } },
603 };
604 auto operatorIter = BinarySearchFindIndex(styleOperators, ArraySize(styleOperators), style.first.c_str());
605 if (operatorIter != -1) {
606 return styleOperators[operatorIter].value(style.second, *this);
607 }
608 return false;
609 }
610
OnChildNodeAdded(const RefPtr<DOMNode> & child,int32_t slot)611 void DOMList::OnChildNodeAdded(const RefPtr<DOMNode>& child, int32_t slot)
612 {
613 auto childListItem = AceType::DynamicCast<DOMListItem>(child);
614 if (!childListItem) {
615 LOGE("child is not list item, add to list failed.");
616 return;
617 }
618
619 // childIndex is generated by js framework, just the position of this new list item should be added into the list.
620 auto childIndex = childListItem->GetItemIndex();
621 LOGD("DOMList AddChild %{public}s, childIndex: %{public}d", child->GetTag().c_str(), childIndex);
622 if (childIndex != DEFAULT_NODE_INDEX) {
623 if (indexer_) {
624 needUpdateIds_ = true;
625 indexerComponent_->InsertChild(childIndex, child->GetRootComponent());
626 } else {
627 listComponent_->InsertChild(childIndex, child->GetRootComponent());
628 }
629 } else {
630 if (indexer_) {
631 needUpdateIds_ = true;
632 indexerComponent_->AppendChild(child->GetRootComponent());
633 } else {
634 listComponent_->AppendChild(child->GetRootComponent());
635 }
636 }
637 auto item = AceType::DynamicCast<ListItemComponent>(childListItem->GetSpecializedComponent());
638 if (!item) {
639 return;
640 }
641 if (!selectedItem_.empty() && !childListItem->GetItemKey().empty() &&
642 (selectedItem_ == childListItem->GetItemKey())) {
643 item->SetIsActive(true);
644 } else {
645 item->SetIsActive(false);
646 }
647 }
648
CallSpecializedMethod(const std::string & method,const std::string & args)649 void DOMList::CallSpecializedMethod(const std::string& method, const std::string& args)
650 {
651 if (method == DOM_LIST_METHOD_SCROLL_TO) {
652 std::unique_ptr<JsonValue> argsValue = JsonUtil::ParseJsonString(args);
653 if (!argsValue || !argsValue->IsArray() || argsValue->GetArraySize() != 1) {
654 LOGE("parse args error");
655 return;
656 }
657 std::unique_ptr<JsonValue> indexValue = argsValue->GetArrayItem(0)->GetValue("index");
658 if (!indexValue || !indexValue->IsNumber()) {
659 LOGE("get index failed");
660 return;
661 }
662 int32_t index = indexValue->GetInt();
663 ScrollToMethod(index);
664 } else if (method == DOM_LIST_METHOD_SCROLL_ARROW) {
665 std::unique_ptr<JsonValue> argsValue = JsonUtil::ParseJsonString(args);
666 if (!argsValue || !argsValue->IsArray() || argsValue->GetArraySize() != 1) {
667 LOGE("parse args error");
668 return;
669 }
670 std::unique_ptr<JsonValue> scrollArrowParams = argsValue->GetArrayItem(0);
671 bool reverse = scrollArrowParams->GetBool("reverse", false);
672 bool isSmooth = scrollArrowParams->GetBool("smooth", false);
673 ScrollArrowMethod(reverse, isSmooth);
674 } else if (method == DOM_LIST_METHOD_SCROLL_TOP || method == DOM_LIST_METHOD_SCROLL_BOTTOM) {
675 ScrollToEdgeMethod(method, args);
676 } else if (method == DOM_LIST_METHOD_SCROLL_PAGE) {
677 ScrollPageMethod(method, args);
678 } else if (method == DOM_LIST_METHOD_EXPAND_GROUP || method == DOM_LIST_METHOD_COLLAPSE_GROUP) {
679 std::unique_ptr<JsonValue> argsValue = JsonUtil::ParseJsonString(args);
680 std::string groupId;
681 if (argsValue && argsValue->IsArray() && argsValue->GetArraySize() == 1) {
682 std::unique_ptr<JsonValue> expandParams = argsValue->GetArrayItem(0);
683 std::unique_ptr<JsonValue> value = expandParams->GetValue("groupid");
684 if (value && value->IsString()) {
685 groupId = value->GetString();
686 }
687 }
688 if (method == DOM_LIST_METHOD_EXPAND_GROUP) {
689 ExpandGroup(groupId, true);
690 } else {
691 ExpandGroup(groupId, false);
692 }
693 } else if (method == DOM_ROTATION) {
694 auto controller = listComponent_->GetRotationController();
695 if (controller) {
696 LOGD("Rotation focus list request");
697 controller->RequestRotation(true);
698 }
699 } else {
700 LOGE("CallSpecializedMethod: undefined method :%{private}s", method.c_str());
701 EventReport::SendComponentException(ComponentExcepType::LIST_COMPONENT_ERR);
702 }
703 }
704
OnScrollBy(double dx,double dy,bool isSmooth)705 void DOMList::OnScrollBy(double dx, double dy, bool isSmooth)
706 {
707 ScrollByMethod(dx, dy, isSmooth);
708 }
709
ExpandGroup(const std::string & groupId,bool expand)710 void DOMList::ExpandGroup(const std::string& groupId, bool expand)
711 {
712 if (!listComponent_) {
713 LOGE("DOMList ExpandGroup listComponent_ is null");
714 EventReport::SendComponentException(ComponentExcepType::LIST_COMPONENT_ERR);
715 return;
716 }
717 if (groupId.empty()) {
718 listComponent_->SetGroupState(INDEX_EXPAND_ALL, expand);
719 }
720 auto children = GetChildList();
721 for (const auto& child : children) {
722 auto itemGroup = AceType::DynamicCast<DOMListItemGroup>(child);
723 if (!itemGroup) {
724 continue;
725 }
726 if (groupId.empty() || groupId == itemGroup->GetGroupId()) {
727 if (!groupId.empty()) {
728 listComponent_->SetGroupState(itemGroup->GetItemIndex(), expand);
729 itemGroup->Update();
730 break;
731 }
732 itemGroup->Update();
733 }
734 }
735 }
736
ScrollToMethod(int32_t index)737 void DOMList::ScrollToMethod(int32_t index)
738 {
739 if (!listComponent_) {
740 LOGE("DOMList ScrollToMethod listComponent_ is null");
741 EventReport::SendComponentException(ComponentExcepType::LIST_COMPONENT_ERR);
742 return;
743 }
744 auto controller = listComponent_->GetPositionController();
745 if (!controller) {
746 LOGE("get controller failed");
747 EventReport::SendComponentException(ComponentExcepType::LIST_COMPONENT_ERR);
748 return;
749 }
750 controller->JumpTo(index);
751 }
752
ScrollByMethod(double x,double y,bool isSmooth)753 void DOMList::ScrollByMethod(double x, double y, bool isSmooth)
754 {
755 if (!listComponent_) {
756 LOGE("DOMList ScrollByMethod listComponent_ is null");
757 EventReport::SendComponentException(ComponentExcepType::LIST_COMPONENT_ERR);
758 return;
759 }
760 auto controller = listComponent_->GetPositionController();
761 if (!controller) {
762 LOGE("get controller failed");
763 EventReport::SendComponentException(ComponentExcepType::LIST_COMPONENT_ERR);
764 return;
765 }
766 controller->ScrollBy(x, y, isSmooth);
767 }
768
ScrollArrowMethod(bool reverse,bool isSmooth)769 void DOMList::ScrollArrowMethod(bool reverse, bool isSmooth)
770 {
771 if (!listComponent_) {
772 LOGE("DOMList ScrollArrowMethod listComponent_ is null");
773 EventReport::SendComponentException(ComponentExcepType::LIST_COMPONENT_ERR);
774 return;
775 }
776 auto controller = listComponent_->GetPositionController();
777 if (!controller) {
778 LOGE("get controller failed");
779 EventReport::SendComponentException(ComponentExcepType::LIST_COMPONENT_ERR);
780 return;
781 }
782 controller->ScrollArrow(scrollDistance_, reverse, isSmooth);
783 }
784
ScrollToEdgeMethod(const std::string & method,const std::string & args)785 void DOMList::ScrollToEdgeMethod(const std::string& method, const std::string& args)
786 {
787 if (!listComponent_) {
788 LOGE("DOMList ScrollToEdgeMethod listComponent_ is null");
789 EventReport::SendComponentException(ComponentExcepType::LIST_COMPONENT_ERR);
790 return;
791 }
792 auto controller = listComponent_->GetPositionController();
793 if (!controller) {
794 LOGE("get controller failed");
795 EventReport::SendComponentException(ComponentExcepType::LIST_COMPONENT_ERR);
796 return;
797 }
798
799 std::unique_ptr<JsonValue> argsValue = JsonUtil::ParseJsonString(args);
800 if (!argsValue || !argsValue->IsArray() || argsValue->GetArraySize() != 1) {
801 LOGE("parse args error");
802 return;
803 }
804 std::unique_ptr<JsonValue> params = argsValue->GetArrayItem(0);
805 bool isSmooth = params->GetBool("smooth", false);
806 if (method == DOM_LIST_METHOD_SCROLL_TOP) {
807 controller->ScrollToEdge(ScrollEdgeType::SCROLL_TOP, isSmooth);
808 } else {
809 controller->ScrollToEdge(ScrollEdgeType::SCROLL_BOTTOM, isSmooth);
810 }
811 }
812
ScrollPageMethod(const std::string & method,const std::string & args)813 void DOMList::ScrollPageMethod(const std::string& method, const std::string& args)
814 {
815 if (!listComponent_) {
816 LOGE("DOMList ScrollPageMethod listComponent_ is null");
817 EventReport::SendComponentException(ComponentExcepType::LIST_COMPONENT_ERR);
818 return;
819 }
820 auto controller = listComponent_->GetPositionController();
821 if (!controller) {
822 LOGE("get controller failed");
823 EventReport::SendComponentException(ComponentExcepType::LIST_COMPONENT_ERR);
824 return;
825 }
826
827 std::unique_ptr<JsonValue> argsValue = JsonUtil::ParseJsonString(args);
828 if (!argsValue || !argsValue->IsArray() || argsValue->GetArraySize() != 1) {
829 LOGE("parse args error");
830 return;
831 }
832 std::unique_ptr<JsonValue> params = argsValue->GetArrayItem(0);
833 bool reverse = params->GetBool("reverse", false);
834 bool isSmooth = params->GetBool("smooth", false);
835 controller->ScrollPage(reverse, isSmooth);
836 }
837
GetCurrentOffset() const838 Offset DOMList::GetCurrentOffset() const
839 {
840 if (!listComponent_) {
841 LOGE("DOMList GetCurrentOffset listComponent_ is null");
842 EventReport::SendComponentException(ComponentExcepType::LIST_COMPONENT_ERR);
843 return Offset::Zero();
844 }
845 auto controller = listComponent_->GetPositionController();
846 if (!controller) {
847 LOGE("get controller failed");
848 EventReport::SendComponentException(ComponentExcepType::LIST_COMPONENT_ERR);
849 return Offset::Zero();
850 }
851 return controller->GetCurrentOffset();
852 }
853
OnChildNodeRemoved(const RefPtr<DOMNode> & child)854 void DOMList::OnChildNodeRemoved(const RefPtr<DOMNode>& child)
855 {
856 if (!listComponent_ || !child) {
857 LOGE("DOMList OnChildNodeRemoved listComponent_ is null");
858 return;
859 }
860 LOGD("DOMList %{public}s, remove child %{public}s", GetTag().c_str(), child->GetTag().c_str());
861 listComponent_->RemoveChild(child->GetRootComponent());
862 }
863
PrepareSpecializedComponent()864 void DOMList::PrepareSpecializedComponent()
865 {
866 if (indexer_) {
867 CreateOrUpdateIndexer();
868 } else {
869 CreateOrUpdateList();
870 }
871 }
872
OnMounted(const RefPtr<DOMNode> & parentNode)873 void DOMList::OnMounted(const RefPtr<DOMNode>& parentNode)
874 {
875 auto parent = parentNode;
876 while (parent) {
877 if (parent->GetTag() == DOM_NODE_TAG_REFRESH) {
878 listComponent_->SetInRefresh(true);
879 break;
880 }
881 parent = parent->GetParentNode();
882 }
883 }
884
GetAccessibilityNode()885 RefPtr<AccessibilityNode> DOMList::GetAccessibilityNode()
886 {
887 auto pipelineContext = pipelineContext_.Upgrade();
888 if (!pipelineContext) {
889 return RefPtr<AccessibilityNode>();
890 }
891 auto accessibilityManager = pipelineContext->GetAccessibilityManager();
892 if (!accessibilityManager) {
893 return RefPtr<AccessibilityNode>();
894 }
895 auto accessibilityNode = accessibilityManager->GetAccessibilityNodeById(GetNodeId());
896 if (!accessibilityNode) {
897 return RefPtr<AccessibilityNode>();
898 }
899
900 return accessibilityNode;
901 }
902
UpdateAccessibilityOrder()903 void DOMList::UpdateAccessibilityOrder()
904 {
905 auto accessibilityNode = GetAccessibilityNode();
906 if (!accessibilityNode) {
907 return;
908 }
909
910 if (listComponent_) {
911 listComponent_->UpdateListItemIndex();
912 }
913
914 children_.sort([](const RefPtr<DOMNode>& node1, const RefPtr<DOMNode>& node2) {
915 RefPtr<ListItemComponent> item1, item2;
916 auto itemNode1 = DOMListItem::GetDOMListItem(node1);
917 auto itemNode2 = DOMListItem::GetDOMListItem(node2);
918 if (itemNode1) {
919 item1 = itemNode1->GetListItemComponent();
920 }
921 if (itemNode2) {
922 item2 = itemNode2->GetListItemComponent();
923 }
924 if (item1 && item2) {
925 return item1->GetIndex() < item2->GetIndex();
926 }
927 return false;
928 });
929
930 std::list<RefPtr<AccessibilityNode>> children;
931 auto nodes = accessibilityNode->GetChildList();
932 for (const auto& child : children_) {
933 auto item = DOMListItem::GetDOMListItem(child);
934 if (item) {
935 auto it = std::find_if(
936 nodes.begin(), nodes.end(), [nodeId = item->GetNodeId()](const RefPtr<AccessibilityNode>& node) {
937 return node->GetNodeId() == nodeId;
938 });
939 if (it != nodes.end()) {
940 children.emplace_back(*it);
941 }
942 }
943 }
944 if (!children.empty()) {
945 accessibilityNode->ResetChildList(children);
946 } else {
947 LOGW("Update accessibility node order failed.");
948 }
949 }
950
UpdateAccessibilityByVisible()951 void DOMList::UpdateAccessibilityByVisible()
952 {
953 auto pipelineContext = pipelineContext_.Upgrade();
954 if (!pipelineContext) {
955 return;
956 }
957 auto accessibilityManager = pipelineContext->GetAccessibilityManager();
958 if (!accessibilityManager) {
959 return;
960 }
961 auto accessibilityNode = accessibilityManager->GetAccessibilityNodeById(GetNodeId());
962 if (!accessibilityNode) {
963 return;
964 }
965
966 bool visibleRange = false;
967 std::list<RefPtr<AccessibilityNode>> children;
968 for (auto& child : children_) {
969 auto childAccessibilityNode = accessibilityManager->GetAccessibilityNodeById(child->GetNodeId());
970 if (childAccessibilityNode &&
971 childAccessibilityNode->GetWidth() != 0 && childAccessibilityNode->GetHeight() != 0) {
972 children.emplace_back(childAccessibilityNode);
973 visibleRange = true;
974 } else {
975 if (visibleRange) {
976 break; // Just load the visible range item.
977 }
978 }
979 }
980 if (!children.empty()) {
981 accessibilityNode->ResetChildList(children);
982 }
983 }
984
OnPageLoadFinish()985 void DOMList::OnPageLoadFinish()
986 {
987 if (listComponent_) {
988 listComponent_->SetPageReady(true);
989 }
990
991 auto accessibilityNode = GetAccessibilityNode();
992 if (!accessibilityNode) {
993 return;
994 }
995
996 accessibilityNode->SetActionUpdateIdsImpl([weakList = AceType::WeakClaim(this), indexer = this->indexer_]() {
997 auto list = weakList.Upgrade();
998 if (list) {
999 if (indexer && list->needUpdateIds_) {
1000 list->UpdateAccessibilityOrder();
1001 list->needUpdateIds_ = false;
1002 }
1003
1004 list->UpdateAccessibilityByVisible();
1005 }
1006 });
1007 }
1008
AdjustSpecialParamInLiteMode()1009 void DOMList::AdjustSpecialParamInLiteMode()
1010 {
1011 itemScale_ = false;
1012 }
1013
1014 } // namespace OHOS::Ace::Framework
1015