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