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/list/list_item_group_element.h"
17 
18 #include "core/components/list/list_element.h"
19 #include "core/components/list/list_item_group_component.h"
20 #include "core/components/proxy/render_item_proxy.h"
21 
22 namespace OHOS::Ace {
23 
CreateRenderNode()24 RefPtr<RenderNode> ListItemGroupElement::CreateRenderNode()
25 {
26     SetOnFocusCallback([weak = WeakClaim(this)](void) {
27         auto element = weak.Upgrade();
28         if (element) {
29             element->HandleOnFocus();
30         }
31     });
32     SetOnBlurCallback([weak = WeakClaim(this)](void) {
33         auto element = weak.Upgrade();
34         if (element) {
35             element->HandleOnBlur();
36         }
37     });
38     RefPtr<RenderNode> node = SoleChildElement::CreateRenderNode();
39     renderItemGroup_ = AceType::DynamicCast<RenderListItemGroup>(node);
40     if (renderItemGroup_) {
41         renderItemGroup_->RegisterRebuildCallback(
42             [weakElement = AceType::WeakClaim(this)]() {
43                 auto groupElement = weakElement.Upgrade();
44                 if (groupElement) {
45                     groupElement->MarkNeedRebuild();
46                     groupElement->Rebuild();
47                 }
48             });
49     }
50     return node;
51 }
52 
PerformBuild()53 void ListItemGroupElement::PerformBuild()
54 {
55     auto groupComponent = AceType::DynamicCast<ListItemGroupComponent>(component_);
56     if (!groupComponent) {
57         LOGE("component is not list item group component");
58         return;
59     }
60 
61     auto childElementIter = children_.begin();
62     auto listItems = groupComponent->GetChildren();
63     auto listItemIter = listItems.begin();
64 
65     auto primary = GetPrimary();
66     int32_t itemIndex = 0;
67     while (listItemIter != listItems.end() && childElementIter != children_.end()) {
68         auto element = UpdateChild(*childElementIter++, *listItemIter);
69         auto itemElement = ListItemElement::GetListItem(element);
70         if (itemElement) {
71             primary != *listItemIter ? itemElement->SetIndex(++itemIndex) : itemElement->SetIndex(0);
72         }
73         listItemIter++;
74     }
75 
76     while (listItemIter != listItems.end()) {
77         auto element = UpdateChild(nullptr, *listItemIter);
78         auto itemElement = ListItemElement::GetListItem(element);
79         if (itemElement) {
80             primary != *listItemIter ? itemElement->SetIndex(++itemIndex) : itemElement->SetIndex(0);
81         }
82         listItemIter++;
83     }
84 
85     while (childElementIter != children_.end()) {
86         auto current = childElementIter++;
87         Element::RemoveChild(*current);
88     }
89 }
90 
ApplyRenderChild(const RefPtr<RenderElement> & renderChild)91 void ListItemGroupElement::ApplyRenderChild(const RefPtr<RenderElement>& renderChild)
92 {
93     if (!renderChild) {
94         LOGE("Element child is null");
95         return;
96     }
97 
98     if (!renderNode_) {
99         LOGE("RenderElement don't have a render node");
100         return;
101     }
102 
103     RefPtr<RenderNode> proxy;
104     auto listItemElement = ListItemElement::GetListItem(renderChild);
105     if (listItemElement) {
106         proxy = listItemElement->GetProxyRenderNode();
107     }
108     if (!proxy) {
109         proxy = RenderItemProxy::Create();
110     }
111 
112     proxy->AddChild(renderChild->GetRenderNode());
113     proxy->Attach(context_);
114     renderNode_->AddChild(proxy);
115 }
116 
RequestNextFocus(bool vertical,bool reverse,const Rect & rect)117 bool ListItemGroupElement::RequestNextFocus(bool vertical, bool reverse, const Rect& rect)
118 {
119     bool ret = false;
120     if (!renderItemGroup_->GetExpand()) {
121         AddItemGroupFocusIndex(0);
122         return ret;
123     }
124     int32_t nextFocusIndex = GetNextFocusIndex(vertical, reverse);
125     if (nextFocusIndex < 0) {
126         return ret;
127     }
128     if (reverse) {
129         while (nextFocusIndex >= 0) {
130             ret = TraverseFocusNode(nextFocusIndex, true);
131             if (ret) {
132                 AddItemGroupFocusIndex(nextFocusIndex);
133                 return ret;
134             }
135             --nextFocusIndex;
136         }
137     } else {
138         int32_t maxSize = static_cast<int32_t>(children_.size());
139         while (nextFocusIndex < maxSize) {
140             ret = TraverseFocusNode(nextFocusIndex, false);
141             if (ret) {
142                 AddItemGroupFocusIndex(nextFocusIndex);
143                 return ret;
144             }
145             ++nextFocusIndex;
146         }
147     }
148     return ret;
149 }
150 
GetNextFocusIndex(bool vertical,bool reverse)151 int32_t ListItemGroupElement::GetNextFocusIndex(bool vertical, bool reverse)
152 {
153     int32_t nextFocusIndex = renderItemGroup_->GetNextFocusIndex(GetItemGroupFocusIndex(), vertical, reverse);
154     int32_t childrenSize = static_cast<int32_t>(children_.size());
155     if (nextFocusIndex < 0 || nextFocusIndex >= childrenSize) {
156         return -1;
157     } else {
158         return nextFocusIndex;
159     }
160 }
161 
TraverseFocusNode(int32_t currentFocusIndex,bool reverse)162 bool ListItemGroupElement::TraverseFocusNode(int32_t currentFocusIndex, bool reverse)
163 {
164     for (const auto& element: children_) {
165         auto listItem = ListItemElement::GetListItem(element);
166         if (listItem && listItem->IsFocusable() && currentFocusIndex == listItem->GetIndex()) {
167             bool ret = listItem->RequestFocusImmediately();
168             MoveItemToViewPort(listItem, reverse);
169             return ret;
170         }
171     }
172     return false;
173 }
174 
MoveItemToViewPort(const RefPtr<Element> & listItem,bool reverse)175 void ListItemGroupElement::MoveItemToViewPort(const RefPtr<Element>& listItem, bool reverse)
176 {
177     auto parentElement = FocusGroup::GetParent().Upgrade();
178     auto listElement = AceType::DynamicCast<ListElement>(parentElement);
179     if (listElement) {
180         if (GetItemGroupFocusIndex() == 0) {
181             listElement->MoveItemToViewPort(renderItemGroup_->GetPositionInList());
182         } else {
183             auto renderListItem = RenderListItem::GetRenderListItem(listItem->GetRenderNode());
184             if (renderListItem) {
185                 double itemSize = renderItemGroup_->GetMainSize(renderListItem->GetLayoutSize());
186                 size_ = reverse ? size_ - itemSize : size_ + itemSize;
187             }
188             listElement->MoveItemGroupToViewPort(renderItemGroup_->GetPositionInList(), size_);
189         }
190     }
191 }
192 
OnKeyEvent(const KeyEvent & keyEvent)193 bool ListItemGroupElement::OnKeyEvent(const KeyEvent& keyEvent)
194 {
195     if (keyEvent.action == KeyAction::CLICK && keyEvent.code == KeyCode::TV_CONTROL_ENTER
196         && GetItemGroupFocusIndex() == 0) {
197         renderItemGroup_->HandleClicked();
198         return true;
199     } else {
200         RefPtr<FocusGroup> parentElement = FocusGroup::GetParent().Upgrade();
201         RefPtr<ListElement> listElement = AceType::DynamicCast<ListElement>(parentElement);
202         if (listElement) {
203             listElement->NeedMoveFocusItem(true);
204         }
205         return FocusGroup::OnKeyEvent(keyEvent);
206     }
207 }
208 
HandleOnFocus()209 void ListItemGroupElement::HandleOnFocus()
210 {
211     // Find the parent until parent is not listItemElement.
212     RefPtr<FocusGroup> parentElement = FocusGroup::GetParent().Upgrade();
213     RefPtr<ListElement> listElement = AceType::DynamicCast<ListElement>(parentElement);
214     if (!listElement) {
215         auto listItemElement = ListItemElement::GetListItem(listElement);
216         if (!listItemElement) {
217             LOGE("[ListFocus]HandleOnFocus, Get Parent list/item failed.");
218             return;
219         }
220         listItemElement->HandleOnFocus();
221         return;
222     }
223     renderItemGroup_->SetFocused(true);
224     if (!renderItemGroup_->GetExpand()) {
225         AddItemGroupFocusIndex(0);
226     }
227     listElement->MoveItemToViewPort(renderItemGroup_->GetPositionInList());
228 }
229 
HandleOnBlur()230 void ListItemGroupElement::HandleOnBlur()
231 {
232     RefPtr<RenderListItem> renderListItem = RenderListItem::GetRenderListItem(GetRenderNode());
233     if (!renderListItem) {
234         LOGE("[ListItemGroup]HandleOnBlur, Get RenderListItem failed, Dep:%{public}d.", GetDepth());
235         return;
236     }
237     for (auto child : GetChildren()) {
238         auto listItem = ListItemElement::GetListItem(child);
239         if (listItem && listItem->IsCurrentFocus()) {
240             listItem->LostFocus();
241         }
242     }
243     renderListItem->SetFocused(false);
244 }
245 
GetPrimary()246 RefPtr<Component> ListItemGroupElement::GetPrimary()
247 {
248     auto groupComponent = AceType::DynamicCast<ListItemGroupComponent>(component_);
249     if (!groupComponent) {
250         LOGE("component is not list item group component");
251         return nullptr;
252     }
253 
254     auto listItems = groupComponent->GetChildren();
255     if (listItems.empty()) {
256         LOGE("no child of group");
257         return nullptr;
258     }
259 
260     auto primary = listItems.front();
261     // Find primary item.
262     for (const auto& child : listItems) {
263         auto listItem = ListItemComponent::GetListItem(child);
264         if (listItem && listItem->GetPrimary()) {
265             primary = child;
266             break;
267         }
268     }
269     return primary;
270 }
271 
OnFocus()272 void ListItemGroupElement::OnFocus()
273 {
274     if (focusNodes_.empty()) {
275         return;
276     }
277     auto itFocusNode = itLastFocusNode_;
278     do {
279         if (itLastFocusNode_ == focusNodes_.end()) {
280             itLastFocusNode_ = focusNodes_.begin();
281             if (itLastFocusNode_ == itFocusNode) {
282                 break;
283             }
284         }
285         auto element = AceType::DynamicCast<Element>(*itLastFocusNode_);
286         auto itemElement = ListItemElement::GetListItem(element);
287         if (itemElement->GetIndex() != GetItemGroupFocusIndex()) {
288             continue;
289         }
290         if ((*itLastFocusNode_)->RequestFocusImmediately()) {
291             FocusNode::OnFocus();
292             return;
293         }
294     } while ((++itLastFocusNode_) != itFocusNode);
295     // Not found any focusable node, clear focus.
296     itLastFocusNode_ = focusNodes_.end();
297 }
298 
AddItemGroupFocusIndex(int32_t groupFocusIndex)299 void ListItemGroupElement::AddItemGroupFocusIndex(int32_t groupFocusIndex)
300 {
301     auto parentElement = FocusGroup::GetParent().Upgrade();
302     auto listElement = AceType::DynamicCast<ListElement>(parentElement);
303     if (listElement) {
304         listElement->AddItemGroupFocusIndex(GetIndex(), groupFocusIndex);
305     }
306 }
307 
GetItemGroupFocusIndex()308 int32_t ListItemGroupElement::GetItemGroupFocusIndex()
309 {
310     auto parentElement = FocusGroup::GetParent().Upgrade();
311     auto listElement = AceType::DynamicCast<ListElement>(parentElement);
312     if (listElement) {
313         int32_t focusIndex = listElement->GetItemGroupFocusIndex(GetIndex());
314         return focusIndex;
315     }
316     return 0;
317 }
318 
ItemFocus(int32_t itemIndex)319 void ListItemGroupElement::ItemFocus(int32_t itemIndex)
320 {
321     int32_t focusIndex = GetItemGroupFocusIndex();
322     if (focusIndex == itemIndex) {
323         return;
324     }
325     for (auto child : GetChildren()) {
326         auto listItem = ListItemElement::GetListItem(child);
327         if (listItem && listItem->GetIndex() == focusIndex) {
328             listItem->LostFocus();
329         }
330     }
331     AddItemGroupFocusIndex(itemIndex);
332 }
333 
334 } // namespace OHOS::Ace
335