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