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