1 /*
2 * Copyright (c) 2021-2022 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_item.h"
17
18 #include "base/log/event_report.h"
19 #include "core/components/focus_animation/focus_animation_theme.h"
20 #include "frameworks/bridge/common/dom/dom_list.h"
21 #include "frameworks/bridge/common/dom/dom_list_item_group.h"
22 #include "frameworks/bridge/common/utils/utils.h"
23
24 namespace OHOS::Ace::Framework {
25
DOMListItem(NodeId nodeId,const std::string & nodeName,int32_t itemIndex)26 DOMListItem::DOMListItem(NodeId nodeId, const std::string& nodeName, int32_t itemIndex) : DOMNode(nodeId, nodeName)
27 {
28 itemIndex_ = itemIndex;
29 listItemComponent_ = AceType::MakeRefPtr<ListItemComponent>(type_, RefPtr<Component>());
30 }
31
SetSpecializedAttr(const std::pair<std::string,std::string> & attr)32 bool DOMListItem::SetSpecializedAttr(const std::pair<std::string, std::string>& attr)
33 {
34 auto parent = AceType::DynamicCast<DOMList>(parentNode_.Upgrade());
35 if (!parent) {
36 auto group = AceType::DynamicCast<DOMListItemGroup>(parentNode_.Upgrade());
37 if (!group) {
38 EventReport::SendComponentException(ComponentExcepType::LIST_ITEM_ERR);
39 return false;
40 }
41 }
42
43 if (attr.first == DOM_LISTITEM_TYPE) {
44 type_ = attr.second;
45 return true;
46 } else if (attr.first == DOM_LISTITEM_CARD_TYPE) {
47 isCard_ = StringToBool(attr.second);
48 return true;
49 } else if (attr.first == DOM_LISTITEM_CARD_BLUR) {
50 isCardBlur_ = StringToBool(attr.second);
51 return true;
52 } else if (attr.first == DOM_LISTITEM_STICKY) {
53 sticky_ = StringToBool(attr.second);
54 if (attr.second == "normal" || attr.second == "true") {
55 sticky_ = true;
56 stickyMode_ = StickyMode::NORMAL;
57 } else if (attr.second == "opacity") {
58 sticky_ = true;
59 stickyMode_ = StickyMode::OPACITY;
60 } else {
61 sticky_ = false;
62 stickyMode_ = StickyMode::NONE;
63 }
64 return true;
65 } else if (attr.first == DOM_LISTITEM_IS_TITLE) {
66 isTitle_ = StringToBool(attr.second);
67 return true;
68 } else if (attr.first == DOM_LISTITEM_CLICK_EFFECT) {
69 clickEffect_ = StringToBool(attr.second);
70 return true;
71 } else if (attr.first == DOM_LISTITEM_STICKY_RADIUS) {
72 stickyRadius_ = ParseDimension(attr.second);
73 } else if (attr.first == DOM_LISTITEM_INDEX_KEY) {
74 indexKey_ = attr.second;
75 return true;
76 } else if (attr.first == DOM_LISTITEM_PRIMARY) {
77 primary_ = StringToBool(attr.second);
78 return true;
79 } else if (attr.first == DOM_LISTITEM_ACTIVE) {
80 isActive_ = StringToBool(attr.second);
81 return true;
82 } else if (attr.first == DOM_LISTITEM_KEY) {
83 key_ = StringUtils::StringToInt(attr.second);
84 return true;
85 }
86 return false;
87 }
88
SetCardThemeAttrs()89 void DOMListItem::SetCardThemeAttrs()
90 {
91 cardTheme_ = GetTheme<CardTheme>();
92 if (!cardTheme_) {
93 EventReport::SendComponentException(ComponentExcepType::GET_THEME_ERR);
94 return;
95 }
96 if (!boxComponent_) {
97 return;
98 }
99
100 if (!isCard_) {
101 return;
102 }
103 RefPtr<Decoration> backDecoration = boxComponent_->GetBackDecoration();
104 if (!backDecoration) {
105 RefPtr<Decoration> decoration = AceType::MakeRefPtr<Decoration>();
106 decoration->SetBackgroundColor(cardTheme_->GetBackgroundColor());
107 decoration->SetBorderRadius(Radius(cardTheme_->GetBorderRadius()));
108 boxComponent_->SetBackDecoration(decoration);
109 }
110 if (backDecoration && !backDecoration->GetBorder().HasRadius()) {
111 backDecoration->SetBorderRadius(Radius(cardTheme_->GetBorderRadius()));
112 }
113 if (backDecoration && (backDecoration->GetBackgroundColor() == Color::TRANSPARENT)) {
114 backDecoration->SetBackgroundColor(cardTheme_->GetBackgroundColor());
115 }
116 if (isCardBlur_) {
117 RefPtr<Decoration> frontDecoration = boxComponent_->GetFrontDecoration();
118 if (!frontDecoration) {
119 RefPtr<Decoration> frontDecoration = AceType::MakeRefPtr<Decoration>();
120 frontDecoration->SetBlurRadius(cardTheme_->GetBlurRadius());
121 boxComponent_->SetFrontDecoration(frontDecoration);
122 }
123 if (frontDecoration && !frontDecoration->GetBlurRadius().IsValid()) {
124 frontDecoration->SetBlurRadius(cardTheme_->GetBlurRadius());
125 }
126 } else {
127 RefPtr<Decoration> frontDecoration = boxComponent_->GetFrontDecoration();
128 if (frontDecoration && frontDecoration->GetBlurRadius().IsValid()) {
129 frontDecoration->SetBlurRadius(Dimension());
130 }
131 }
132 SetCardTransitionEffect();
133 }
134
SetCardTransitionEffect()135 void DOMListItem::SetCardTransitionEffect()
136 {
137 // set default style for card if user did not set clickSpringEffect
138 if (!transformComponent_) {
139 transformComponent_ = AceType::MakeRefPtr<TransformComponent>();
140 }
141 if (!declaration_) {
142 return;
143 }
144 if (!declaration_->HasClickEffect()) {
145 transformComponent_->SetClickSpringEffectType(ClickSpringEffectType::MEDIUM);
146 }
147 if (!declaration_->HasTransitionAnimation()) {
148 transformComponent_->SetTransitionEffect(TransitionEffect::UNFOLD);
149 if (listItemComponent_) {
150 listItemComponent_->SetTransitionEffect(TransitionEffect::UNFOLD);
151 }
152 } else {
153 listItemComponent_->SetTransitionEffect(transformComponent_->GetTransitionEffect());
154 }
155 }
156
SetSpecializedStyle(const std::pair<std::string,std::string> & style)157 bool DOMListItem::SetSpecializedStyle(const std::pair<std::string, std::string>& style)
158 {
159 static const LinearMapNode<void (*)(const std::string&, DOMListItem&)> listItemStyleOperators[] = {
160 { DOM_ALIGN_ITEMS, [](const std::string& val,
161 DOMListItem& listItem) { listItem.flexCrossAlign_ = ConvertStrToFlexAlign(val); } },
162 { DOM_ALIGN_SELF, [](const std::string& val,
163 DOMListItem& listItem) { listItem.alignSelf_ = ConvertStrToFlexAlign(val); } },
164 { DOM_LISTITEM_CLICK_COLOR,
165 [](const std::string& val, DOMListItem& listItem) { listItem.clickColor_ = listItem.ParseColor(val); } },
166 { DOM_FLEX_DIRECTION, [](const std::string& val,
167 DOMListItem& listItem) { listItem.flexDirection_ = ConvertStrToFlexDirection(val); } },
168 { DOM_JUSTIFY_CONTENT, [](const std::string& val,
169 DOMListItem& listItem) { listItem.flexMainAlign_ = ConvertStrToFlexAlign(val); } },
170 };
171 auto operatorIter = BinarySearchFindIndex(listItemStyleOperators,
172 ArraySize(listItemStyleOperators), style.first.c_str());
173 if (operatorIter != -1) {
174 listItemStyleOperators[operatorIter].value(style.second, *this);
175 return true;
176 }
177 static const LinearMapNode<void (*)(const std::string&, DOMListItem&)> listItemRadiusStyleOperators[] = {
178 // Set border radius
179 { DOM_BORDER_BOTTOM_LEFT_RADIUS,
180 [](const std::string& val, DOMListItem& listItem) {
181 listItem.bottomLeftRadius_ = Radius(listItem.ParseDimension(val));
182 } },
183 { DOM_BORDER_BOTTOM_RIGHT_RADIUS,
184 [](const std::string& val, DOMListItem& listItem) {
185 listItem.bottomRightRadius_ = Radius(listItem.ParseDimension(val));
186 } },
187 { DOM_BORDER_RADIUS,
188 [](const std::string& val, DOMListItem& listItem) {
189 listItem.topLeftRadius_ = Radius(listItem.ParseDimension(val));
190 listItem.topRightRadius_ = Radius(listItem.ParseDimension(val));
191 listItem.bottomLeftRadius_ = Radius(listItem.ParseDimension(val));
192 listItem.bottomRightRadius_ = Radius(listItem.ParseDimension(val));
193 } },
194 { DOM_BORDER_TOP_LEFT_RADIUS,
195 [](const std::string& val, DOMListItem& listItem) {
196 listItem.topLeftRadius_ = Radius(listItem.ParseDimension(val));
197 } },
198 { DOM_BORDER_TOP_RIGHT_RADIUS,
199 [](const std::string& val, DOMListItem& listItem) {
200 listItem.topRightRadius_ = Radius(listItem.ParseDimension(val));
201 } },
202 };
203 // The radius still needs to be set to the radius of the box component, so return false.
204 auto radiusIter = BinarySearchFindIndex(listItemRadiusStyleOperators,
205 ArraySize(listItemRadiusStyleOperators), style.first.c_str());
206 if (radiusIter != -1) {
207 listItemRadiusStyleOperators[radiusIter].value(style.second, *this);
208 return false;
209 }
210
211 if (style.first == DOM_LISTITEM_COLUMN_SPAN) {
212 auto columnSpan = StringUtils::StringToInt(style.second);
213 if (columnSpan <= 0) {
214 columnSpan = DEFAULT_COLUMN_SPAN;
215 }
216 columnSpan_ = columnSpan;
217 return true;
218 }
219 return false;
220 }
221
AddSpecializedEvent(int32_t pageId,const std::string & event)222 bool DOMListItem::AddSpecializedEvent(int32_t pageId, const std::string& event)
223 {
224 if (event == DOM_LIST_ITEM_EVENT_STICKY) {
225 stickyEventId_ = EventMarker(GetNodeIdForEvent(), event, pageId);
226 listItemComponent_->SetStickyEventId(stickyEventId_);
227 return true;
228 }
229
230 if (event == DOM_CLICK || event == DOM_CATCH_BUBBLE_CLICK) {
231 EventMarker eventMarker(GetNodeIdForEvent(), event, pageId);
232 listItemComponent_->SetClickEventId(eventMarker);
233 return true;
234 }
235 return false;
236 }
237
AddListItem(const RefPtr<DOMNode> & node,int32_t slot)238 void DOMListItem::AddListItem(const RefPtr<DOMNode>& node, int32_t slot)
239 {
240 const auto& childComponent = node->GetRootComponent();
241 if (!flexComponent_) {
242 flexComponent_ = AceType::MakeRefPtr<FlexComponent>(
243 flexDirection_, flexMainAlign_, flexCrossAlign_, std::list<RefPtr<Component>>());
244 }
245 flexComponent_->InsertChild(slot, childComponent);
246 }
247
RemoveListItem(const RefPtr<DOMNode> & node)248 void DOMListItem::RemoveListItem(const RefPtr<DOMNode>& node)
249 {
250 const auto& childComponent = node->GetRootComponent();
251 if (flexComponent_) {
252 flexComponent_->RemoveChild(childComponent);
253 }
254 }
255
OnChildNodeAdded(const RefPtr<DOMNode> & child,int32_t slot)256 void DOMListItem::OnChildNodeAdded(const RefPtr<DOMNode>& child, int32_t slot)
257 {
258 if (child) {
259 AddListItem(child, slot);
260 }
261 }
262
OnChildNodeRemoved(const RefPtr<DOMNode> & child)263 void DOMListItem::OnChildNodeRemoved(const RefPtr<DOMNode>& child)
264 {
265 if (child) {
266 RemoveListItem(child);
267 }
268 }
269
OnMounted(const RefPtr<DOMNode> & parentNode)270 void DOMListItem::OnMounted(const RefPtr<DOMNode>& parentNode)
271 {
272 if (!parentNode) {
273 return;
274 }
275 if (parentNode->GetTag() == DOM_NODE_TAG_LIST) {
276 const auto& parentNodeTmp = AceType::DynamicCast<DOMList>(parentNode);
277 if (listItemComponent_ && parentNodeTmp) {
278 listItemComponent_->SetSupportScale(parentNodeTmp->GetItemScale());
279 listItemComponent_->SetSupportOpacity(parentNodeTmp->GetItemOpacity());
280 // NeedVibrate means scroll and rotation all trigger vibrate.
281 // RotationVibrate means only rotation trigger vibrate.
282 listItemComponent_->MarkNeedVibrate(parentNodeTmp->NeedVibrate());
283 listItemComponent_->MarkNeedRotationVibrate(parentNodeTmp->NeedRotationVibrate());
284 SetDividerStyle(parentNode);
285 }
286 } else if (parentNode->GetTag() == DOM_NODE_TAG_LIST_ITEM_GROUP) {
287 const auto& parentNodeTmp = AceType::DynamicCast<DOMListItemGroup>(parentNode);
288 if (listItemComponent_ && parentNodeTmp) {
289 listItemComponent_->SetSupportScale(false);
290 // Divider style is set in DOMList
291 SetDividerStyle(parentNode->GetParentNode());
292 }
293 } else {
294 LOGW("list item parent is invalid type.");
295 }
296 }
297
SetDividerStyle(const RefPtr<DOMNode> & parentNode)298 void DOMListItem::SetDividerStyle(const RefPtr<DOMNode>& parentNode)
299 {
300 auto parentList = AceType::DynamicCast<DOMList>(parentNode);
301 if (parentList) {
302 listItemComponent_->MarkNeedDivider(parentList->NeedDivider());
303 listItemComponent_->SetDividerLength(parentList->GetDividerLength());
304 listItemComponent_->SetDividerOrigin(parentList->GetDividerOrigin());
305 listItemComponent_->SetDividerHeight(parentList->GetDividerHeight());
306 auto dividerColor = parentList->GetDividerColor();
307 RefPtr<ListTheme> listTheme = GetTheme<ListTheme>();
308 if (dividerColor == Color::TRANSPARENT && listTheme) {
309 dividerColor = listTheme->GetDividerColor();
310 }
311 listItemComponent_->SetDividerColor(dividerColor);
312 }
313 }
314
ResetInitializedStyle()315 void DOMListItem::ResetInitializedStyle()
316 {
317 if (!listItemComponent_) {
318 return;
319 }
320 RefPtr<FocusAnimationTheme> theme = GetTheme<FocusAnimationTheme>();
321 if (theme) {
322 listItemComponent_->SetFocusAnimationColor(theme->GetColor());
323 }
324 if (isCard_ || (isCard_ && isCardBlur_)) {
325 SetCardThemeAttrs();
326 }
327
328 if (SystemProperties::GetDeviceType() == DeviceType::TV && boxComponent_) {
329 RefPtr<ListItemTheme> itemTheme = GetTheme<ListItemTheme>();
330 if (itemTheme && declaration_) {
331 Edge padding;
332 auto& style = static_cast<CommonPaddingStyle&>(declaration_->GetStyle(StyleTag::COMMON_PADDING_STYLE));
333 if (style.IsValid() && style.padding.IsEffective()) {
334 return;
335 }
336 // Add theme padding to item when not set customized padding.
337 double additionalPadding = itemTheme->GetItemPaddingInPercent();
338 boxComponent_->SetPadding(Edge(), Edge(Dimension(additionalPadding, DimensionUnit::PERCENT)));
339 }
340 }
341 }
342
PrepareSpecializedComponent()343 void DOMListItem::PrepareSpecializedComponent()
344 {
345 if (!listItemComponent_) {
346 listItemComponent_ = AceType::MakeRefPtr<ListItemComponent>(type_, RefPtr<Component>());
347 }
348
349 ResetInitializedStyle();
350 listItemComponent_->SetType(type_.empty() ? "default" : type_);
351 listItemComponent_->SetSticky(sticky_);
352 listItemComponent_->SetStickyMode(stickyMode_);
353 listItemComponent_->SetStickyRadius(stickyRadius_);
354 listItemComponent_->SetPrimary(primary_);
355 listItemComponent_->SetSupportClick(clickEffect_);
356 listItemComponent_->MarkTitle(isTitle_);
357 listItemComponent_->SetFlags(LIST_ITEM_FLAG_FROM_CHILD);
358 listItemComponent_->SetColumnSpan(columnSpan_);
359 listItemComponent_->SetTopLeftRadius(topLeftRadius_);
360 listItemComponent_->SetTopRightRadius(topRightRadius_);
361 listItemComponent_->SetBottomLeftRadius(bottomLeftRadius_);
362 listItemComponent_->SetBottomRightRadius(bottomRightRadius_);
363 if (clickColor_ != Color::TRANSPARENT) {
364 listItemComponent_->SetClickColor(clickColor_);
365 }
366 listItemComponent_->SetAlignSelf(alignSelf_);
367 if (!indexKey_.empty()) {
368 listItemComponent_->SetIndexKey(indexKey_);
369 }
370 if (key_ != -1) {
371 listItemComponent_->SetKey(key_);
372 }
373 if (flexComponent_) {
374 flexComponent_->SetDirection(flexDirection_);
375 flexComponent_->SetMainAxisAlign(flexMainAlign_);
376 flexComponent_->SetCrossAxisAlign(flexCrossAlign_);
377 } else {
378 flexComponent_ = AceType::MakeRefPtr<FlexComponent>(
379 flexDirection_, flexMainAlign_, flexCrossAlign_, std::list<RefPtr<Component>>());
380 flexComponent_->SetTextDirection(IsRightToLeft() ? TextDirection::RTL : TextDirection::LTR);
381 }
382
383 if (displayComponent_) {
384 displayComponent_->SetVisible(VisibleType::VISIBLE);
385 displayComponent_->SetOpacity(1.0);
386 } else {
387 displayComponent_ = AceType::MakeRefPtr<DisplayComponent>();
388 }
389
390 if (boxComponent_) {
391 boxComponent_->SetMouseAnimationType(HoverAnimationType::OPACITY);
392 auto& sizeStyle = static_cast<CommonSizeStyle&>(declaration_->GetStyle(StyleTag::COMMON_SIZE_STYLE));
393 if (sizeStyle.IsValid()) {
394 boxComponent_->SetAspectRatio(sizeStyle.aspectRatio);
395 boxComponent_->SetMinWidth(sizeStyle.minWidth);
396 boxComponent_->SetMinHeight(sizeStyle.minHeight);
397 boxComponent_->SetMaxWidth(sizeStyle.maxWidth);
398 boxComponent_->SetMaxHeight(sizeStyle.maxHeight);
399 boxComponent_->SetBoxSizing(sizeStyle.boxSizing);
400 }
401 }
402 }
403
GetSpecializedComponent()404 RefPtr<Component> DOMListItem::GetSpecializedComponent()
405 {
406 SetCardThemeAttrs();
407 return listItemComponent_;
408 }
409
CompositeSpecializedComponent(const std::vector<RefPtr<SingleChild>> & components)410 RefPtr<Component> DOMListItem::CompositeSpecializedComponent(const std::vector<RefPtr<SingleChild>>& components)
411 {
412 RefPtr<Component> component;
413 if (sticky_ && stickyMode_ == StickyMode::OPACITY) {
414 RefPtr<DisplayComponent> opacity = AceType::MakeRefPtr<DisplayComponent>();
415 opacity->SetVisible(VisibleType::VISIBLE);
416 opacity->SetOpacity(1.0);
417 RefPtr<TransformComponent> scale = AceType::MakeRefPtr<TransformComponent>();
418 opacity->SetChild(scale);
419 scale->SetChild(flexComponent_);
420 component = opacity;
421 } else {
422 component = flexComponent_;
423 }
424 if (!components.empty()) {
425 const auto& parent = components.back();
426 if (parent) {
427 parent->SetChild(component);
428 }
429 component = AceType::DynamicCast<Component>(components.front());
430 }
431 if (listItemComponent_) {
432 if (displayComponent_) {
433 displayComponent_->SetChild(component);
434 listItemComponent_->SetChild(displayComponent_);
435 } else {
436 listItemComponent_->SetChild(component);
437 }
438 }
439 if (declaration_) {
440 declaration_->SetHasPositionProcessed(true);
441 }
442 return listItemComponent_;
443 }
444
GetDirtyNodeId() const445 NodeId DOMListItem::GetDirtyNodeId() const
446 {
447 if (key_ == -1) {
448 return DOMNode::GetNodeId();
449 }
450 return GetParentId();
451 }
452
453 } // namespace OHOS::Ace::Framework
454