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 "core/components/indexer/indexer_component.h"
17
18 #include "core/components/common/properties/shadow_config.h"
19 #include "core/components/indexer/indexer_element.h"
20 #include "core/components/indexer/render_indexer_circle.h"
21
22 namespace OHOS::Ace {
23
CreateElement()24 RefPtr<Element> Ace::IndexerComponent::CreateElement()
25 {
26 LOGI("[indexer] CreateElement ");
27 return AceType::MakeRefPtr<IndexerElement>();
28 }
29
CreateRenderNode()30 RefPtr<RenderNode> IndexerComponent::CreateRenderNode()
31 {
32 LOGI("[indexer] CreateRenderNode ");
33 if (circleMode_) {
34 return AceType::MakeRefPtr<RenderIndexerCircle>();
35 } else {
36 return AceType::MakeRefPtr<RenderIndexer>();
37 }
38 }
39
FormatLabelAlphabet()40 void IndexerComponent::FormatLabelAlphabet()
41 {
42 for (auto& item : indexerLabel_) {
43 if (item == INDEXER_STR_DOT_EX) {
44 item = INDEXER_STR_DOT;
45 }
46 }
47 }
48
BuildDefaultAlphabet()49 void IndexerComponent::BuildDefaultAlphabet()
50 {
51 defaultAlphaLocal_ = GetU16StrVector(alphabetIndexer_);
52 }
53
BuildIndexerAlphabet()54 void IndexerComponent::BuildIndexerAlphabet()
55 {
56 BuildDefaultAlphabet();
57 std::vector<std::u16string> alphabet = Localization::GetInstance()->GetIndexAlphabet(); // get alphabet
58 if (alphabet.empty()) {
59 LOGE("fail to build indexer alphabet due to alphabet is empty");
60 return;
61 }
62 FormatLabelAlphabet();
63 // alphabet must begin with "#", and items "." can not be neighbors
64 if (alphabet[0] != INDEXER_STR_SHARP || indexerLabel_[0] != INDEXER_STR_SHARP) {
65 LOGE("fail to build indexer alphabet due to alphabet is wrong");
66 return;
67 }
68
69 sectionsLocal_.clear();
70 labelLocal_.clear();
71
72 std::u16string strItem;
73 size_t countAlphabet = 0;
74 for (size_t i = 0; i < indexerLabel_.size(); ++i) {
75 if (indexerLabel_[i] != INDEXER_STR_DOT) {
76 sectionsLocal_.emplace_back(indexerLabel_[i]);
77 labelLocal_.emplace_back(indexerLabel_[i]);
78 ++countAlphabet;
79 } else {
80 if (i == indexerLabel_.size() - 1) { // while this "." is the last item
81 while (countAlphabet < alphabet.size()) {
82 strItem += alphabet[countAlphabet];
83 strItem += INDEXER_STR_SPACE;
84 ++countAlphabet;
85 }
86 sectionsLocal_.emplace_back(strItem);
87 labelLocal_.emplace_back(indexerLabel_[i]);
88 } else if (indexerLabel_[i + 1] != INDEXER_STR_DOT) { // while the next item is not "."
89 while ((countAlphabet < alphabet.size()) && (indexerLabel_[i + 1] != alphabet[countAlphabet])) {
90 strItem += alphabet[countAlphabet];
91 strItem += INDEXER_STR_SPACE;
92 ++countAlphabet;
93 }
94 sectionsLocal_.emplace_back(strItem);
95 labelLocal_.emplace_back(indexerLabel_[i]);
96 } else {
97 // Do nothing while the next item is "." too
98 }
99 }
100 strItem.clear();
101 }
102 }
103
InitIndexerItemStyle()104 void IndexerComponent::InitIndexerItemStyle()
105 {
106 if (GetCircleMode()) {
107 // Indexer item style when binding list item is not current.
108 normalStyle_.SetFontSize(Dimension(INDEXER_CIRCLE_ITEM_TEXT_SIZE, DimensionUnit::FP));
109 normalStyle_.SetFontWeight(FontWeight::W400);
110 normalStyle_.SetTextColor(Color::WHITE);
111
112 // Indexer item style when binding list item is current.
113 activeStyle_ = normalStyle_;
114 } else {
115 // Indexer item style when binding list item is not current.
116 normalStyle_.SetFontSize(Dimension(INDEXER_ITEM_TEXT_SIZE, DimensionUnit::FP));
117 normalStyle_.SetFontWeight(FontWeight::W400);
118 normalStyle_.SetTextColor(Color(INDEXER_LIST_COLOR));
119
120 // Indexer item style when binding list item is current.
121 activeStyle_.SetFontSize(Dimension(INDEXER_LIST_ITEM_TEXT_SIZE, DimensionUnit::FP));
122 activeStyle_.SetFontWeight(FontWeight::W500);
123 activeStyle_.SetTextColor(Color(INDEXER_LIST_ACTIVE_COLOR));
124 }
125 }
126
BuildArcItem()127 void IndexerComponent::BuildArcItem()
128 {
129 RefPtr<ArcComponent> arcItem = AceType::MakeRefPtr<ArcComponent>();
130 arcItem->SetWidth(itemSize_);
131 arcItem->SetStartAngle(startPosition_);
132 arcItem->SetSweepAngle(arcLength_);
133 arcItem->SetColor(Color(INDEXER_CIRCLE_ARC_COLOR));
134 arcItem->SetShadowWidth(Dimension(INDEXER_CIRCLE_ITEM_SHADOW_RADIUS, DimensionUnit::VP));
135 AppendChild(arcItem);
136 nonItemCount_++;
137 }
138
BuildBubbleBox()139 void IndexerComponent::BuildBubbleBox()
140 {
141 if (!bubbleEnabled_) {
142 return;
143 }
144 RefPtr<BoxComponent> bubble = AceType::MakeRefPtr<BoxComponent>();
145 bubble->SetFlex(BoxFlex::FLEX_NO);
146 bubble->SetAlignment(Alignment::CENTER);
147 Radius radius = Radius(Dimension(BUBBLE_BOX_SIZE_CIRCLE, DimensionUnit::VP) * HALF);
148 RefPtr<Decoration> back = AceType::MakeRefPtr<Decoration>();
149 if (circleMode_) {
150 bubble->SetWidth(BUBBLE_BOX_SIZE_CIRCLE, DimensionUnit::VP);
151 bubble->SetHeight(BUBBLE_BOX_SIZE_CIRCLE, DimensionUnit::VP);
152 back->SetBackgroundColor(Color(BUBBLE_BG_COLOR_CIRCLE).BlendOpacity(NINETY_OPACITY_IN_PERCENT));
153 } else {
154 // for shadow blur region
155 bubble->SetMargin(Edge(200));
156 bubble->SetWidth(BUBBLE_BOX_SIZE, DimensionUnit::VP);
157 bubble->SetHeight(BUBBLE_BOX_SIZE, DimensionUnit::VP);
158 radius = Radius(Dimension(BUBBLE_BOX_RADIUS, DimensionUnit::VP));
159 back->SetBackgroundColor(Color(BUBBLE_BG_COLOR).BlendOpacity(NINETY_OPACITY_IN_PERCENT));
160 back->AddShadow(ShadowConfig::DefaultShadowL);
161 }
162 back->SetBorderRadius(radius);
163 bubble->SetBackDecoration(back);
164 bubbleText_ = AceType::MakeRefPtr<TextComponent>(StringUtils::Str16ToStr8(INDEXER_STR_SHARP));
165 TextStyle textStyle;
166 textStyle.SetTextAlign(TextAlign::CENTER);
167 if (circleMode_) {
168 textStyle.SetFontSize(Dimension(BUBBLE_FONT_SIZE_CIRCLE, DimensionUnit::FP));
169 textStyle.SetTextColor(Color::WHITE);
170 } else {
171 textStyle.SetFontSize(Dimension(BUBBLE_FONT_SIZE, DimensionUnit::VP));
172 textStyle.SetTextColor(Color(BUBBLE_FONT_COLOR));
173 }
174 bubbleText_->SetTextStyle(textStyle);
175 bubble->SetChild(bubbleText_);
176 RefPtr<DisplayComponent> displayComponent = AceType::MakeRefPtr<DisplayComponent>(bubble);
177 displayComponent->SetOpacity(ZERO_OPACITY);
178 AppendChild(displayComponent);
179 nonItemCount_++;
180 }
181
BuildIndicatorBox()182 void IndexerComponent::BuildIndicatorBox()
183 {
184 RefPtr<BoxComponent> indicator = AceType::MakeRefPtr<BoxComponent>();
185 indicator->SetWidth(INDEXER_CIRCLE_ITEM_SIZE, DimensionUnit::VP);
186 indicator->SetHeight(INDEXER_CIRCLE_ITEM_SIZE, DimensionUnit::VP);
187 indicator->SetFlex(BoxFlex::FLEX_NO);
188 indicator->SetAlignment(Alignment::CENTER);
189 RefPtr<Decoration> front = AceType::MakeRefPtr<Decoration>();
190 Radius radius = Radius(itemSize_ * HALF);
191 front->SetBorderRadius(radius);
192 front->SetBackgroundColor(Color::TRANSPARENT);
193 indicator->SetFrontDecoration(front);
194 AppendChild(indicator);
195 nonItemCount_++;
196 }
197
BuildTextItem(const std::u16string & strSection,const std::u16string & strLabel,int32_t itemType)198 void IndexerComponent::BuildTextItem(const std::u16string& strSection, const std::u16string& strLabel, int32_t itemType)
199 {
200 RefPtr<IndexerItemComponent> textItem =
201 AceType::MakeRefPtr<IndexerItemComponent>(strSection, strLabel, itemSize_, circleMode_, false);
202 textItem->SetNormalTextStyle(normalStyle_);
203 textItem->SetActiveTextStyle(activeStyle_);
204 textItem->SetTextStyle(false);
205 textItem->SetItemType(itemType);
206 if (isFirstItem_ || strSection == INDEXER_STR_SHARP || strSection == INDEXER_STR_COLLECT) {
207 textItem->MarkItemPrimary();
208 if (!userDefineList_ || strSection != INDEXER_STR_SHARP) {
209 isFirstItem_ = false;
210 }
211 }
212 listItem_.emplace_back(textItem);
213 AppendChild(textItem);
214 ++itemCount_;
215 }
216
BuildCollapseItem()217 void IndexerComponent::BuildCollapseItem()
218 {
219 RefPtr<IndexerItemComponent> collapseItem = AceType::MakeRefPtr<IndexerItemComponent>(
220 INDEXER_STR_COLLAPSE, INDEXER_STR_COLLAPSE, itemSize_, circleMode_, true);
221 collapseItem->SetNormalTextStyle(normalStyle_);
222 collapseItem->SetActiveTextStyle(activeStyle_);
223 collapseItem->SetTextStyle(false);
224 collapseItem->MarkItemPrimary();
225 listItem_.emplace_back(collapseItem);
226 AppendChild(collapseItem);
227 ++itemCount_;
228 hasCollapseItem_ = true;
229 }
230
BuildIndexerItems()231 void IndexerComponent::BuildIndexerItems()
232 {
233 uint32_t length = static_cast<uint32_t>(labelLocal_.size());
234 if (length == 0) {
235 LOGE("[indexer] invalid section string");
236 return;
237 }
238
239 // add arc first while circle mode
240 if (GetCircleMode()) {
241 BuildArcItem();
242 BuildIndicatorBox();
243 }
244 BuildBubbleBox();
245 itemCount_ = 0;
246
247 // add “*” item while circle mode
248 if (GetCircleMode() && !userDefineList_) {
249 BuildTextItem(INDEXER_STR_COLLECT, INDEXER_STR_COLLECT);
250 }
251 // add "#" first
252 if (!userDefineList_) {
253 BuildTextItem(INDEXER_STR_SHARP, INDEXER_STR_SHARP);
254 }
255 // add indexer items except '#'
256 isFirstItem_ = true;
257 for (uint32_t i = 0; i < length; ++i) {
258 std::u16string strItem = labelLocal_[i];
259 if (!userDefineList_ && strItem == INDEXER_STR_SHARP) {
260 continue;
261 }
262 BuildTextItem(sectionsLocal_[i], strItem);
263 }
264
265 if (circleMode_ && multiLanguageEnabled_) {
266 isFirstItem_ = true;
267 // add default alphabet indexer
268 uint32_t count = defaultAlphaLocal_.size();
269 for (uint32_t i = 0; i < count; ++i) {
270 std::u16string strItem = defaultAlphaLocal_[i];
271 BuildTextItem(strItem, strItem, 1);
272 }
273 ++maxShowCount_;
274 if (userDefineList_ && indexerLabel_[0] != INDEXER_STR_COLLECT) {
275 --maxShowCount_;
276 }
277 }
278 isFirstItem_ = false;
279
280 // add collapse item while circle mode
281 if (GetCircleMode() && itemCount_ > INDEXER_COLLAPSE_ITEM_COUNT) {
282 BuildCollapseItem();
283 }
284 if (GetCircleMode()) {
285 RefPtr<ArcComponent> arc =
286 AceType::DynamicCast<ArcComponent>(GetChildren().empty() ? nullptr : GetChildren().front());
287 if (arc) {
288 double arcLen = itemCount_ < maxShowCount_ ?
289 INDEXER_ARC_LENGTH / maxShowCount_ * (itemCount_ - 1) : INDEXER_ARC_LENGTH;
290 arc->SetSweepAngle(arcLen);
291 }
292 }
293 LOGI("[indexer] BuildIndexerItems circleMode:%{public}d, itemCount_:%{public}d", circleMode_, itemCount_);
294 }
295
GetIndexerItem(const std::string & indexKey)296 RefPtr<IndexerItemComponent> IndexerComponent::GetIndexerItem(const std::string& indexKey)
297 {
298 RefPtr<IndexerItemComponent> ret = nullptr;
299 if (indexKey.empty()) {
300 LOGE("[indexer] indexKey is NULL");
301 return ret;
302 }
303
304 LOGI("[indexer] GetIndexerItem section: %{public}s", indexKey.c_str());
305 for (const auto& item : listItem_) {
306 if (item->IsCorrectItem(indexKey)) {
307 LOGI("[indexer] section: %{public}s", StringUtils::Str16ToStr8(item->GetSectionStr()).c_str());
308 ret = item;
309 break;
310 }
311 }
312 return ret;
313 }
314
AddItemIndexKey(const std::string & indexKey,const std::string & headStyle)315 int32_t IndexerComponent::AddItemIndexKey(const std::string& indexKey, const std::string& headStyle)
316 {
317 int32_t itemIndex = INDEXER_INVALID_INDEX;
318 if (!list_) {
319 LOGE("[indexer] AddItemIndexKey List ptr is NULL");
320 return itemIndex;
321 }
322
323 if (indexKey.empty()) {
324 LOGI("[indexer] indexKey is NULL, add to Sharp area.");
325 // add to sharp section, while indexkey is Null
326 return AddItemToSharp(indexKey, headStyle);
327 }
328
329 for (const auto& item : listItem_) {
330 if (item && (item->IsCorrectItem(indexKey))) {
331 // add section head
332 if (item->GetKeyCount() == 0 && !GetCircleMode()) {
333 AddSectionHead(item, headStyle);
334 }
335 // add index key
336 itemIndex = static_cast<int32_t>(item->AddIndexKey(indexKey));
337 break;
338 }
339 }
340
341 // add to sharp section, while indexkey is number
342 if (itemIndex == INVALID_INDEX) {
343 itemIndex = AddItemToSharp(indexKey, headStyle);
344 } else {
345 UpdateSectionIndex();
346 }
347
348 return itemIndex;
349 }
350
AddItemToSharp(const std::string & indexKey,const std::string & headStyle)351 int32_t IndexerComponent::AddItemToSharp(const std::string& indexKey, const std::string& headStyle)
352 {
353 // find the # section
354 RefPtr<IndexerItemComponent> itemPtr = nullptr;
355 if (!listItem_.empty()) {
356 for (const auto& item : listItem_) {
357 if (item && (item->GetSectionStr() == INDEXER_STR_SHARP)) {
358 itemPtr = item;
359 break;
360 }
361 }
362 }
363 if (!itemPtr) {
364 LOGE("[indexer] AddItemToSharp get # section failed");
365 return 0;
366 }
367
368 // add section head
369 if (itemPtr->GetKeyCount() == 0 && !GetCircleMode()) {
370 AddSectionHead(itemPtr, headStyle);
371 }
372 // add index key
373 int32_t itemIndex = static_cast<int32_t>(itemPtr->AddIndexKey(indexKey));
374 // update section head index
375 UpdateSectionIndex();
376
377 return itemIndex;
378 }
379
PrintItemInfo() const380 void IndexerComponent::PrintItemInfo() const
381 {
382 for (const auto& item : listItem_) {
383 item->PrintIndexerItem();
384 }
385 }
386
AddSectionHead(const RefPtr<IndexerItemComponent> & indexerItem,const std::string & headStyleStr)387 int32_t IndexerComponent::AddSectionHead(
388 const RefPtr<IndexerItemComponent>& indexerItem, const std::string& headStyleStr)
389 {
390 int32_t sectionHeadIndex = INDEXER_INVALID_INDEX;
391 if (GetCircleMode()) {
392 LOGI("[indexer] Circle Mode, cancel section head adding");
393 return sectionHeadIndex;
394 }
395
396 if (!list_) {
397 LOGE("[indexer] List ptr is NULL");
398 return sectionHeadIndex;
399 }
400
401 if (!indexerItem) {
402 LOGE("[indexer] Invalid indexerItem ");
403 return sectionHeadIndex;
404 }
405
406 std::string strSectionHeadLabel = StringUtils::Str16ToStr8(indexerItem->GetLabelStr());
407 if (strSectionHeadLabel.empty()) {
408 LOGE("[indexer] Invalid Section head, strSectionHead:%{public}s", strSectionHeadLabel.c_str());
409 return sectionHeadIndex;
410 }
411
412 // [indexer] Section head style need check
413 RefPtr<TextComponent> text = AceType::MakeRefPtr<TextComponent>(strSectionHeadLabel);
414 TextStyle headStyle;
415 if (circleMode_) {
416 headStyle.SetFontSize(Dimension(INDEXER_CIRCLE_ITEM_TEXT_SIZE, DimensionUnit::FP));
417 } else {
418 headStyle.SetFontSize(Dimension(INDEXER_ITEM_TEXT_SIZE, DimensionUnit::FP));
419 }
420 headStyle.SetFontWeight(FontWeight::W500);
421 headStyle.SetTextColor(Color::BLACK);
422 text->SetTextStyle(headStyle);
423 RefPtr<BoxComponent> box = AceType::MakeRefPtr<BoxComponent>();
424 box->SetFlex(BoxFlex::FLEX_X);
425 box->SetDeliverMinToChild(false);
426 box->SetAlignment(Alignment::CENTER);
427 box->SetColor(Color::WHITE);
428 box->SetChild(text);
429 RefPtr<ListItemComponent> listItem = AceType::MakeRefPtr<ListItemComponent>(headStyleStr, box);
430 listItem->SetSticky(true);
431 listItem->SetIndexKey(strSectionHeadLabel);
432 RefPtr<ComposedComponent> composedItem =
433 AceType::MakeRefPtr<ComposedComponent>("section", "sectionComposed", listItem);
434 sectionHeadIndex = indexerItem->GetSectionIndex();
435 list_->InsertChild(sectionHeadIndex, composedItem);
436 LOGI("[indexer] strSectionHeadLabel:%{public}s, headStyle:%{public}s index:%{public}d",
437 strSectionHeadLabel.c_str(), headStyleStr.c_str(), sectionHeadIndex);
438 return sectionHeadIndex;
439 }
440
RemoveSectionHead(int32_t index)441 bool IndexerComponent::RemoveSectionHead(int32_t index)
442 {
443 bool ret = false;
444 if (GetCircleMode()) {
445 return ret;
446 }
447
448 if (!list_) {
449 LOGE("[indexer] RemoveSectionHead List ptr is NULL");
450 return ret;
451 }
452
453 for (const auto& item : list_->GetChildren()) {
454 RefPtr<ListItemComponent> listItem = ListItemComponent::GetListItem(item);
455 if (listItem && listItem->GetIndex() == index) {
456 list_->RemoveChild(item);
457 ret = true;
458 break;
459 }
460 }
461
462 return ret;
463 }
464
RemoveItemIndexKey(const std::string & indexKey)465 bool IndexerComponent::RemoveItemIndexKey(const std::string& indexKey)
466 {
467 bool ret = false;
468 if (!list_) {
469 LOGE("[indexer] RemoveItemIndexKey List ptr is NULL");
470 return ret;
471 }
472
473 if (indexKey.empty()) {
474 LOGI("[indexer] RemoveItemIndexKey indexKey is NULL");
475 // remove item while the index is null
476 return RemoveItemFromSharp(indexKey);
477 }
478
479 for (const auto& item : listItem_) {
480 if (item && (item->IsCorrectItem(indexKey))) {
481 int32_t sectionHeadIndex = item->GetSectionIndex();
482 // remove index key
483 ret = item->RemoveIndexKey(indexKey);
484 // remove section head
485 if (item->GetKeyCount() == 0 && !GetCircleMode()) {
486 RemoveSectionHead(sectionHeadIndex);
487 }
488 break;
489 }
490 }
491
492 if (ret) {
493 // update section head index
494 UpdateSectionIndex();
495 } else {
496 // remove item while indexkey is number, low probable case, put it last
497 ret = RemoveItemFromSharp(indexKey);
498 }
499
500 return ret;
501 }
502
RemoveItemFromSharp(const std::string & indexKey)503 bool IndexerComponent::RemoveItemFromSharp(const std::string& indexKey)
504 {
505 // remove item from "#" section
506 bool ret = false;
507 if (!list_) {
508 LOGE("[indexer] RemoveItemFromSharp List ptr is NULL");
509 return ret;
510 }
511
512 // find section "#"
513 RefPtr<IndexerItemComponent> itemPtr = nullptr;
514 if (!listItem_.empty()) {
515 for (const auto& item : listItem_) {
516 if (item && (item->GetSectionStr() == INDEXER_STR_SHARP)) {
517 itemPtr = item;
518 break;
519 }
520 }
521 }
522 if (!itemPtr) {
523 LOGE("[indexer] RemoveItemFromSharp get # section failed");
524 return ret;
525 }
526
527 ret = itemPtr->RemoveIndexKey(indexKey);
528 if (!ret) {
529 LOGE("[indexer] RemoveItemFromSharp remove IndexKey failed");
530 return ret;
531 }
532
533 // update section head index
534 UpdateSectionIndex();
535
536 return ret;
537 }
538
GetSectionIndexInList(const std::string & indexKey)539 int32_t IndexerComponent::GetSectionIndexInList(const std::string& indexKey)
540 {
541 int32_t index = INVALID_INDEX;
542 RefPtr<IndexerItemComponent> item = GetIndexerItem(indexKey);
543 if (item) {
544 index = item->GetSectionIndex();
545 }
546 return index;
547 }
548
GetSectionIndexInIndexer(int32_t index)549 int32_t IndexerComponent::GetSectionIndexInIndexer(int32_t index)
550 {
551 int32_t count = 0;
552 if (index <= 0) {
553 return count;
554 }
555 for (const auto& item : listItem_) {
556 if (item) {
557 if (index < item->GetSectionIndex()) {
558 break;
559 }
560 }
561 count++;
562 }
563 return count - 1;
564 }
565
UpdateSectionIndex()566 void IndexerComponent::UpdateSectionIndex()
567 {
568 // update section head index
569 if (listItem_.empty()) {
570 LOGE("[indexer] UpdateSectionIndex listItem_ is empty");
571 return;
572 }
573
574 int32_t sectionIndex = listItem_.front()->GetSectionIndex();
575 for (const auto& item : listItem_) {
576 if (!item) {
577 continue;
578 }
579 item->SetSectionIndex(sectionIndex);
580 if (item->GetKeyCount() != 0) {
581 sectionIndex = sectionIndex + static_cast<int32_t>(item->GetKeyCount()) + 1;
582 if (GetCircleMode()) {
583 --sectionIndex;
584 }
585 }
586 }
587 }
588
589 } // namespace OHOS::Ace
590