1 /*
2 * Copyright (c) 2024 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_ng/pattern/grid/grid_focus.h"
17
18 #include "core/components_ng/base/frame_node.h"
19 #include "core/components_ng/pattern/grid/grid_item_pattern.h"
20
21 namespace OHOS::Ace::NG {
IsFirstOrLastFocusableChild(int32_t curMainIndex,int32_t curCrossIndex)22 std::pair<bool, bool> GridFocus::IsFirstOrLastFocusableChild(int32_t curMainIndex, int32_t curCrossIndex)
23 {
24 std::unordered_set<int32_t> crossIndexSet;
25 size_t maxSize = 0;
26 for (int32_t index = curMainIndex - curFocusIndexInfo_.mainSpan + 1; index <= curMainIndex; index++) {
27 auto tempIndexSet = GetFocusableChildCrossIndexesAt(index);
28 if (tempIndexSet.size() > maxSize) {
29 maxSize = tempIndexSet.size();
30 crossIndexSet = tempIndexSet;
31 }
32 }
33 auto findLesser = std::find_if(crossIndexSet.begin(), crossIndexSet.end(),
34 [curCrossIndex](int32_t crossIndex) { return curCrossIndex > crossIndex; });
35 auto findGreater = std::find_if(crossIndexSet.begin(), crossIndexSet.end(),
36 [curCrossIndex](int32_t crossIndex) { return curCrossIndex < crossIndex; });
37 return { curCrossIndex == 0 || findLesser == crossIndexSet.end(),
38 curCrossIndex == info_.crossCount_ - 1 || findGreater == crossIndexSet.end() };
39 }
40
GetFocusSteps(int32_t curMainIndex,int32_t curCrossIndex,FocusStep step)41 std::pair<FocusStep, FocusStep> GridFocus::GetFocusSteps(int32_t curMainIndex, int32_t curCrossIndex, FocusStep step)
42 {
43 auto firstStep = FocusStep::NONE;
44 auto secondStep = FocusStep::NONE;
45 auto isFirstOrLastFocusable = IsFirstOrLastFocusableChild(curMainIndex, curCrossIndex);
46 auto isFirstFocusable = isFirstOrLastFocusable.first;
47 auto isLastFocusable = isFirstOrLastFocusable.second;
48 if (info_.axis_ == Axis::VERTICAL) {
49 if (isFirstFocusable && step == FocusStep::SHIFT_TAB) {
50 firstStep = FocusStep::UP;
51 secondStep = FocusStep::RIGHT_END;
52 } else if (isLastFocusable && step == FocusStep::TAB) {
53 firstStep = FocusStep::DOWN;
54 secondStep = FocusStep::LEFT_END;
55 }
56 } else if (info_.axis_ == Axis::HORIZONTAL) {
57 if (isFirstFocusable && step == FocusStep::SHIFT_TAB) {
58 firstStep = FocusStep::LEFT;
59 secondStep = FocusStep::DOWN_END;
60 } else if (isLastFocusable && step == FocusStep::TAB) {
61 firstStep = FocusStep::RIGHT;
62 secondStep = FocusStep::UP_END;
63 }
64 }
65 TAG_LOGI(AceLogTag::ACE_GRID, "Get focus steps. First step is %{public}d. Second step is %{public}d", firstStep,
66 secondStep);
67 return { firstStep, secondStep };
68 }
69
GetNextFocusSimplified(FocusStep step,const RefPtr<FocusHub> & current)70 WeakPtr<FocusHub> GridFocus::GetNextFocusSimplified(FocusStep step, const RefPtr<FocusHub>& current)
71 {
72 CHECK_NULL_RETURN(current, nullptr);
73 auto host = grid_.GetHost();
74 CHECK_NULL_RETURN(host, nullptr);
75 auto* ctx = grid_.GetContext();
76 CHECK_NULL_RETURN(ctx, nullptr);
77
78 int32_t diff = 0;
79 if (step == FocusStep::TAB) {
80 diff = 1;
81 } else if (step == FocusStep::SHIFT_TAB) {
82 diff = -1;
83 } else {
84 // not yet implemented
85 return nullptr;
86 }
87
88 int32_t idx = host->GetChildTrueIndex(current->GetFrameNode()) + diff;
89 while (idx >= 0 && idx < info_.childrenCount_) {
90 if (idx < info_.startIndex_ || idx > info_.endIndex_) {
91 grid_.ScrollToIndex(idx);
92 ctx->FlushUITaskWithSingleDirtyNode(host);
93 }
94 auto next = host->GetChildByIndex(idx);
95 CHECK_NULL_BREAK(next);
96 auto nextFocus = next->GetHostNode()->GetFocusHub();
97 if (nextFocus && nextFocus->IsFocusable()) {
98 return nextFocus;
99 }
100 idx += diff;
101 }
102 return nullptr;
103 }
104
GetNextFocusNode(FocusStep step,const WeakPtr<FocusHub> & currentFocusNode)105 WeakPtr<FocusHub> GridFocus::GetNextFocusNode(FocusStep step, const WeakPtr<FocusHub>& currentFocusNode)
106 {
107 auto curFocus = currentFocusNode.Upgrade();
108 CHECK_NULL_RETURN(curFocus, nullptr);
109 auto curFrame = curFocus->GetFrameNode();
110 CHECK_NULL_RETURN(curFrame, nullptr);
111 auto curPattern = curFrame->GetPattern();
112 CHECK_NULL_RETURN(curPattern, nullptr);
113 auto curItemPattern = AceType::DynamicCast<GridItemPattern>(curPattern);
114 CHECK_NULL_RETURN(curItemPattern, nullptr);
115 auto curItemProperty = curItemPattern->GetLayoutProperty<GridItemLayoutProperty>();
116 CHECK_NULL_RETURN(curItemProperty, nullptr);
117 auto irregularInfo = curItemPattern->GetIrregularItemInfo();
118 bool hasIrregularItemInfo = irregularInfo.has_value();
119
120 auto curMainIndex = curItemProperty->GetMainIndex().value_or(-1);
121 auto curCrossIndex = curItemProperty->GetCrossIndex().value_or(-1);
122 auto curMainSpan =
123 hasIrregularItemInfo ? irregularInfo.value().mainSpan : curItemProperty->GetMainSpan(info_.axis_);
124 auto curCrossSpan =
125 hasIrregularItemInfo ? irregularInfo.value().crossSpan : curItemProperty->GetCrossSpan(info_.axis_);
126 auto curMainStart =
127 hasIrregularItemInfo ? irregularInfo.value().mainStart : curItemProperty->GetMainStart(info_.axis_);
128 auto curCrossStart =
129 hasIrregularItemInfo ? irregularInfo.value().crossStart : curItemProperty->GetCrossStart(info_.axis_);
130 auto curMainEnd = hasIrregularItemInfo ? irregularInfo.value().mainEnd : curItemProperty->GetMainEnd(info_.axis_);
131 auto curCrossEnd =
132 hasIrregularItemInfo ? irregularInfo.value().crossEnd : curItemProperty->GetCrossEnd(info_.axis_);
133
134 curFocusIndexInfo_.mainIndex = curMainIndex;
135 curFocusIndexInfo_.crossIndex = curCrossIndex;
136 curFocusIndexInfo_.mainSpan = curMainSpan;
137 curFocusIndexInfo_.crossSpan = curCrossSpan;
138 curFocusIndexInfo_.mainStart = curMainStart;
139 curFocusIndexInfo_.mainEnd = curMainEnd;
140 curFocusIndexInfo_.crossStart = curCrossStart;
141 curFocusIndexInfo_.crossEnd = curCrossEnd;
142
143 if (curMainIndex < 0 || curCrossIndex < 0) {
144 TAG_LOGW(AceLogTag::ACE_GRID, "can't find focused child.");
145 return nullptr;
146 }
147 if (info_.gridMatrix_.find(curMainIndex) == info_.gridMatrix_.end()) {
148 TAG_LOGW(AceLogTag::ACE_GRID, "Can not find current main index: %{public}d", curMainIndex);
149 return nullptr;
150 }
151 TAG_LOGI(AceLogTag::ACE_GRID,
152 "GetNextFocusNode: Current:(%{public}d,%{public}d)-[%{public}d,%{public}d]. Focus: %{public}d", curMainIndex,
153 curCrossIndex, curMainSpan, curCrossSpan, step);
154 auto focusSteps = GetFocusSteps(curMainIndex, curCrossIndex, step);
155 if (focusSteps.first != FocusStep::NONE && focusSteps.second != FocusStep::NONE) {
156 auto firstStepRes = GetNextFocusNode(focusSteps.first, currentFocusNode);
157 if (!firstStepRes.Upgrade()) {
158 return nullptr;
159 }
160 auto secondStepRes = GetNextFocusNode(focusSteps.second, firstStepRes);
161 if (!secondStepRes.Upgrade()) {
162 return firstStepRes;
163 }
164 return secondStepRes;
165 }
166 auto indexes = GetNextIndexByStep(curMainIndex, curCrossIndex, curMainSpan, curCrossSpan, step);
167 auto nextMainIndex = indexes.first;
168 auto nextCrossIndex = indexes.second;
169 while (nextMainIndex >= 0 && nextCrossIndex >= 0) {
170 if (info_.gridMatrix_.find(nextMainIndex) == info_.gridMatrix_.end()) {
171 TAG_LOGW(AceLogTag::ACE_GRID, "Can not find next main index: %{public}d", nextMainIndex);
172 return nullptr;
173 }
174 auto nextMaxCrossCount = info_.crossCount_;
175 auto flag = (step == FocusStep::LEFT_END) || (step == FocusStep::RIGHT_END);
176 auto weakChild = info_.hasBigItem_ ? SearchIrregularFocusableChild(nextMainIndex, nextCrossIndex)
177 : SearchFocusableChildInCross(nextMainIndex, nextCrossIndex,
178 nextMaxCrossCount, flag ? -1 : curMainIndex, curCrossIndex);
179 auto child = weakChild.Upgrade();
180 if (child && child->IsFocusable()) {
181 ScrollToFocusNode(weakChild);
182 return weakChild;
183 }
184 auto indexes = GetNextIndexByStep(nextMainIndex, nextCrossIndex, 1, 1, step);
185 nextMainIndex = indexes.first;
186 nextCrossIndex = indexes.second;
187 }
188 return nullptr;
189 }
190
GetIndexByFocusHub(const WeakPtr<FocusHub> & focusNode)191 int32_t GridFocus::GetIndexByFocusHub(const WeakPtr<FocusHub>& focusNode)
192 {
193 auto focusHub = focusNode.Upgrade();
194 CHECK_NULL_RETURN(focusHub, -1);
195 auto node = focusHub->GetFrameNode();
196 CHECK_NULL_RETURN(node, -1);
197 auto property = AceType::DynamicCast<GridItemLayoutProperty>(node->GetLayoutProperty());
198 CHECK_NULL_RETURN(property, -1);
199 int32_t crossIndex = property->GetCrossIndex().value_or(-1);
200 int32_t mainIndex = property->GetMainIndex().value_or(-1);
201 return info_.FindInMatrixByMainIndexAndCrossIndex(mainIndex, crossIndex);
202 }
203
GetNextIndexByStep(int32_t curMainIndex,int32_t curCrossIndex,int32_t curMainSpan,int32_t curCrossSpan,FocusStep step)204 std::pair<int32_t, int32_t> GridFocus::GetNextIndexByStep(
205 int32_t curMainIndex, int32_t curCrossIndex, int32_t curMainSpan, int32_t curCrossSpan, FocusStep step)
206 {
207 auto curMainStart = info_.startMainLineIndex_;
208 auto curMainEnd = info_.endMainLineIndex_;
209 auto curChildStartIndex = info_.startIndex_;
210 auto curChildEndIndex = info_.endIndex_;
211 auto childrenCount = info_.childrenCount_;
212 auto hasIrregularItems = info_.hasBigItem_;
213 if (info_.gridMatrix_.find(curMainIndex) == info_.gridMatrix_.end()) {
214 TAG_LOGW(AceLogTag::ACE_GRID, "Can not find current main index: %{public}d", curMainIndex);
215 return { -1, -1 };
216 }
217 TAG_LOGI(AceLogTag::ACE_GRID,
218 "Current: (%{public}d,%{public}d)-[%{public}d,%{public}d]. axis: %{public}d, step: %{public}d", curMainIndex,
219 curCrossIndex, curMainSpan, curCrossSpan, info_.axis_, step);
220 auto curMaxCrossCount = info_.crossCount_;
221 auto nextMainIndex = curMainIndex;
222 auto nextCrossIndex = curCrossIndex;
223 if ((step == FocusStep::UP_END && info_.axis_ == Axis::HORIZONTAL) ||
224 (step == FocusStep::LEFT_END && info_.axis_ == Axis::VERTICAL)) {
225 nextMainIndex = curMainIndex;
226 nextCrossIndex = 0;
227 isLeftEndStep_ = hasIrregularItems ? true : false;
228 } else if ((step == FocusStep::DOWN_END && info_.axis_ == Axis::HORIZONTAL) ||
229 (step == FocusStep::RIGHT_END && info_.axis_ == Axis::VERTICAL)) {
230 nextMainIndex = curMainIndex;
231 nextCrossIndex = curMaxCrossCount - 1;
232 isRightEndStep_ = hasIrregularItems ? true : false;
233 } else if (((step == FocusStep::UP || step == FocusStep::SHIFT_TAB) && info_.axis_ == Axis::HORIZONTAL) ||
234 ((step == FocusStep::LEFT || step == FocusStep::SHIFT_TAB) && info_.axis_ == Axis::VERTICAL)) {
235 nextMainIndex = curMainIndex;
236 nextCrossIndex = curCrossIndex - 1;
237 isLeftStep_ = hasIrregularItems ? true : false;
238 } else if ((step == FocusStep::UP && info_.axis_ == Axis::VERTICAL) ||
239 (step == FocusStep::LEFT && info_.axis_ == Axis::HORIZONTAL)) {
240 nextMainIndex = hasIrregularItems ? curMainIndex - curMainSpan : curMainIndex - 1;
241 nextCrossIndex = curCrossIndex + static_cast<int32_t>((curCrossSpan - 1) / 2);
242 isUpStep_ = hasIrregularItems ? true : false;
243 } else if (((step == FocusStep::DOWN || step == FocusStep::TAB) && info_.axis_ == Axis::HORIZONTAL) ||
244 ((step == FocusStep::RIGHT || step == FocusStep::TAB) && info_.axis_ == Axis::VERTICAL)) {
245 nextMainIndex = curMainIndex;
246 nextCrossIndex = curCrossIndex + curCrossSpan;
247 isRightStep_ = hasIrregularItems ? true : false;
248 } else if ((step == FocusStep::DOWN && info_.axis_ == Axis::VERTICAL) ||
249 (step == FocusStep::RIGHT && info_.axis_ == Axis::HORIZONTAL)) {
250 nextMainIndex = hasIrregularItems ? curMainIndex + 1 : curMainIndex + curMainSpan;
251 nextCrossIndex = curCrossIndex + static_cast<int32_t>((curCrossSpan - 1) / 2);
252 isDownStep_ = hasIrregularItems ? true : false;
253 } else {
254 TAG_LOGW(
255 AceLogTag::ACE_GRID, "Next index return: Invalid step: %{public}d and axis: %{public}d", step, info_.axis_);
256 return { -1, -1 };
257 }
258 if (curChildStartIndex == 0 && curMainIndex == 0 && nextMainIndex < curMainIndex) {
259 nextMainIndex = curMainIndex;
260 }
261 if (curChildEndIndex == childrenCount - 1 && curMainIndex == curMainEnd && nextMainIndex > curMainIndex) {
262 nextMainIndex = curMainIndex;
263 }
264 if (nextMainIndex == curMainIndex && nextCrossIndex == curCrossIndex) {
265 TAG_LOGI(AceLogTag::ACE_GRID,
266 "Next index return: Move stoped. Next index: (%{public}d,%{public}d) is same as current.", nextMainIndex,
267 nextCrossIndex);
268 ResetAllDirectionsStep();
269 return { -1, -1 };
270 }
271 if (curChildStartIndex != 0 && curMainIndex == curMainStart && nextMainIndex < curMainIndex) {
272 // Scroll item up.
273 grid_.UpdateStartIndex(curChildStartIndex - 1);
274 auto* pipeline = grid_.GetContext();
275 if (pipeline) {
276 pipeline->FlushUITasks();
277 }
278 } else if (curChildEndIndex != childrenCount - 1 && curMainIndex == curMainEnd && nextMainIndex > curMainIndex) {
279 // Scroll item down.
280 grid_.UpdateStartIndex(curChildEndIndex + 1);
281 auto* pipeline = grid_.GetContext();
282 if (pipeline) {
283 pipeline->FlushUITasks();
284 }
285 }
286 curMainStart = info_.startMainLineIndex_;
287 curMainEnd = info_.endMainLineIndex_;
288 if (nextMainIndex < curMainStart || nextMainIndex > curMainEnd) {
289 ResetAllDirectionsStep();
290 return { -1, -1 };
291 }
292 if (nextCrossIndex < 0) {
293 ResetAllDirectionsStep();
294 return { -1, -1 };
295 }
296 if (info_.gridMatrix_.find(nextMainIndex) == info_.gridMatrix_.end()) {
297 ResetAllDirectionsStep();
298 return { -1, -1 };
299 }
300 auto nextMaxCrossCount = info_.crossCount_;
301 if (nextCrossIndex >= nextMaxCrossCount) {
302 TAG_LOGI(AceLogTag::ACE_GRID,
303 "Next index: { %{public}d,%{public}d }. Next cross index is greater than max cross count: %{public}d.",
304 nextMainIndex, nextCrossIndex, nextMaxCrossCount - 1);
305 if (nextMaxCrossCount - 1 != (curCrossIndex + curCrossSpan - 1)) {
306 TAG_LOGI(AceLogTag::ACE_GRID,
307 "Current cross index: %{public}d is not the tail item. Return to the tail: { %{public}d,%{public}d }",
308 curCrossIndex, nextMainIndex, nextMaxCrossCount - 1);
309 return { nextMainIndex, nextMaxCrossCount - 1 };
310 }
311 ResetAllDirectionsStep();
312 TAG_LOGI(AceLogTag::ACE_GRID, "Current cross index: %{public}d is the tail item. No next item can be found!",
313 curCrossIndex);
314 return { -1, -1 };
315 }
316 TAG_LOGI(AceLogTag::ACE_GRID, "Next index return: { %{public}d,%{public}d }.", nextMainIndex, nextCrossIndex);
317 return { nextMainIndex, nextCrossIndex };
318 }
319
SearchFocusableChildInCross(int32_t tarMainIndex,int32_t tarCrossIndex,int32_t maxCrossCount,int32_t curMainIndex,int32_t curCrossIndex)320 WeakPtr<FocusHub> GridFocus::SearchFocusableChildInCross(
321 int32_t tarMainIndex, int32_t tarCrossIndex, int32_t maxCrossCount, int32_t curMainIndex, int32_t curCrossIndex)
322 {
323 bool isDirectionLeft = true;
324 auto indexLeft = tarCrossIndex;
325 auto indexRight = tarCrossIndex;
326 if (curMainIndex == tarMainIndex) {
327 // Search on the same main index. Do not need search on both left and right side.
328 if (tarCrossIndex > curCrossIndex) {
329 // Only search on the right side.
330 indexLeft = -1;
331 } else if (tarCrossIndex < curCrossIndex) {
332 // Only search on the left side.
333 indexRight = maxCrossCount;
334 } else {
335 TAG_LOGW(AceLogTag::ACE_GRID, "Invalid search index: (%{public}d,%{public}d). It's same as current.",
336 tarMainIndex, tarCrossIndex);
337 return nullptr;
338 }
339 }
340 while (indexLeft >= 0 || indexRight < maxCrossCount) {
341 int32_t curIndex = indexLeft;
342 if (indexLeft < 0) {
343 curIndex = indexRight++;
344 } else if (indexRight >= maxCrossCount) {
345 curIndex = indexLeft--;
346 } else {
347 curIndex = isDirectionLeft ? indexLeft-- : indexRight++;
348 isDirectionLeft = !isDirectionLeft;
349 }
350 auto weakChild = GetChildFocusNodeByIndex(tarMainIndex, curIndex);
351 auto child = weakChild.Upgrade();
352 if (child && child->IsFocusable()) {
353 TAG_LOGI(AceLogTag::ACE_GRID, "Found child. Index: %{public}d,%{public}d", tarMainIndex, curIndex);
354 return weakChild;
355 }
356 }
357 return nullptr;
358 }
359
SearchIrregularFocusableChild(int32_t tarMainIndex,int32_t tarCrossIndex)360 WeakPtr<FocusHub> GridFocus::SearchIrregularFocusableChild(int32_t tarMainIndex, int32_t tarCrossIndex)
361 {
362 double minDistance = std::numeric_limits<double>::max();
363 int32_t minMainIndex = std::numeric_limits<int32_t>::max();
364 int32_t minCrossIndex = std::numeric_limits<int32_t>::max();
365 int32_t maxAreaInMainShadow = -1;
366 int32_t maxAreaInCrossShadow = -1;
367 WeakPtr<FocusHub> targetFocusHubWeak;
368
369 auto gridFrame = grid_.GetHost();
370 CHECK_NULL_RETURN(gridFrame, nullptr);
371 auto gridFocus = gridFrame->GetFocusHub();
372 CHECK_NULL_RETURN(gridFocus, nullptr);
373 std::list<RefPtr<FocusHub>> childFocusList;
374 gridFocus->FlushChildrenFocusHub(childFocusList);
375 for (const auto& childFocus : childFocusList) {
376 if (!childFocus->IsFocusable()) {
377 continue;
378 }
379 auto childFrame = childFocus->GetFrameNode();
380 if (!childFrame) {
381 continue;
382 }
383 auto childPattern = childFrame->GetPattern<GridItemPattern>();
384 if (!childPattern) {
385 continue;
386 }
387 auto childItemProperty = childFrame->GetLayoutProperty<GridItemLayoutProperty>();
388 if (!childItemProperty) {
389 continue;
390 }
391 auto irregularInfo = childPattern->GetIrregularItemInfo();
392 bool hasIrregularItemInfo = irregularInfo.has_value();
393
394 auto childMainIndex = childItemProperty->GetMainIndex().value_or(-1);
395 auto childCrossIndex = childItemProperty->GetCrossIndex().value_or(-1);
396 auto childMainStart =
397 hasIrregularItemInfo ? irregularInfo.value().mainStart : childItemProperty->GetMainStart(info_.axis_);
398 auto childMainEnd =
399 hasIrregularItemInfo ? irregularInfo.value().mainEnd : childItemProperty->GetMainEnd(info_.axis_);
400 auto chidCrossStart =
401 hasIrregularItemInfo ? irregularInfo.value().crossStart : childItemProperty->GetCrossStart(info_.axis_);
402 auto chidCrossEnd =
403 hasIrregularItemInfo ? irregularInfo.value().crossEnd : childItemProperty->GetCrossEnd(info_.axis_);
404 auto childCrossSpan =
405 hasIrregularItemInfo ? irregularInfo.value().crossSpan : childItemProperty->GetCrossSpan(info_.axis_);
406 auto childMainSpan =
407 hasIrregularItemInfo ? irregularInfo.value().mainSpan : childItemProperty->GetMainSpan(info_.axis_);
408
409 GridItemIndexInfo childInfo;
410 childInfo.mainIndex = childMainIndex;
411 childInfo.crossIndex = childCrossIndex;
412 childInfo.mainStart = childMainStart;
413 childInfo.mainEnd = childMainEnd;
414 childInfo.crossStart = chidCrossStart;
415 childInfo.crossEnd = chidCrossEnd;
416
417 if (childMainIndex < 0 || childCrossIndex < 0) {
418 continue;
419 }
420
421 if ((isLeftStep_ && ((childCrossIndex == tarCrossIndex && childCrossSpan == 1) ||
422 (chidCrossEnd >= 0 && chidCrossEnd == tarCrossIndex))) ||
423 (isRightStep_ && childCrossIndex == tarCrossIndex)) {
424 double nearestDistance = GetNearestDistanceFromChildToCurFocusItemInMainAxis(tarCrossIndex, childInfo);
425 int32_t intersectAreaSize = CalcIntersectAreaInTargetDirectionShadow(childInfo, true);
426 if (LessNotEqual(nearestDistance, minDistance) ||
427 (NearEqual(nearestDistance, minDistance) && intersectAreaSize > maxAreaInCrossShadow) ||
428 (NearEqual(nearestDistance, minDistance) && intersectAreaSize == maxAreaInCrossShadow &&
429 childMainIndex < minMainIndex)) {
430 minDistance = nearestDistance;
431 maxAreaInCrossShadow = intersectAreaSize;
432 minMainIndex = childMainIndex;
433 targetFocusHubWeak = AceType::WeakClaim(AceType::RawPtr(childFocus));
434 }
435 } else if ((isUpStep_ && childMainIndex == tarMainIndex) ||
436 (isDownStep_ && ((childMainIndex == tarMainIndex && childMainSpan == 1) ||
437 (childMainStart >= 0 && childMainStart == tarMainIndex)))) {
438 double nearestDistance = GetNearestDistanceFromChildToCurFocusItemInCrossAxis(tarMainIndex, childInfo);
439 int32_t intersectAreaSize = CalcIntersectAreaInTargetDirectionShadow(childInfo, false);
440 if (LessNotEqual(nearestDistance, minDistance) ||
441 (NearEqual(nearestDistance, minDistance) && intersectAreaSize > maxAreaInMainShadow) ||
442 (NearEqual(nearestDistance, minDistance) && intersectAreaSize == maxAreaInMainShadow &&
443 childCrossIndex < minCrossIndex)) {
444 minDistance = nearestDistance;
445 minCrossIndex = childCrossIndex;
446 maxAreaInMainShadow = intersectAreaSize;
447 targetFocusHubWeak = AceType::WeakClaim(AceType::RawPtr(childFocus));
448 }
449 } else if ((isLeftEndStep_ || isRightEndStep_) &&
450 ((tarMainIndex == childMainIndex && tarCrossIndex == childCrossIndex) ||
451 (childMainStart >= 0 && childMainStart <= tarMainIndex && tarMainIndex <= childMainIndex &&
452 tarCrossIndex == childCrossIndex))) {
453 targetFocusHubWeak = AceType::WeakClaim(AceType::RawPtr(childFocus));
454 }
455 }
456 ResetAllDirectionsStep();
457 return targetFocusHubWeak;
458 }
459
CalcIntersectAreaInTargetDirectionShadow(GridItemIndexInfo itemIndexInfo,bool isFindInMainAxis)460 int32_t GridFocus::CalcIntersectAreaInTargetDirectionShadow(GridItemIndexInfo itemIndexInfo, bool isFindInMainAxis)
461 {
462 int32_t curFocusLeftTopX = -1;
463 int32_t curFocusLeftTopY = -1;
464 int32_t curFocusRightBottonX = -1;
465 int32_t curFocusRightBottonY = -1;
466
467 if (isFindInMainAxis) {
468 curFocusLeftTopX =
469 curFocusIndexInfo_.mainStart == -1 ? curFocusIndexInfo_.mainIndex : curFocusIndexInfo_.mainStart;
470 curFocusLeftTopY = 0;
471 curFocusRightBottonX =
472 curFocusIndexInfo_.mainEnd == -1 ? curFocusIndexInfo_.mainIndex : curFocusIndexInfo_.mainEnd;
473 curFocusRightBottonY = info_.crossCount_;
474 } else {
475 curFocusLeftTopX = info_.startMainLineIndex_;
476 curFocusLeftTopY =
477 curFocusIndexInfo_.crossStart == -1 ? curFocusIndexInfo_.crossIndex : curFocusIndexInfo_.crossStart;
478 curFocusRightBottonX = info_.endMainLineIndex_;
479 curFocusRightBottonY =
480 curFocusIndexInfo_.crossEnd == -1 ? curFocusIndexInfo_.crossIndex : curFocusIndexInfo_.crossEnd;
481 }
482 int32_t childLeftTopX = itemIndexInfo.mainStart == -1 ? itemIndexInfo.mainIndex : itemIndexInfo.mainStart;
483 int32_t childLeftTopY = itemIndexInfo.crossStart == -1 ? itemIndexInfo.crossIndex : itemIndexInfo.crossStart;
484 int32_t childRightBottonX = itemIndexInfo.mainEnd == -1 ? itemIndexInfo.mainIndex : itemIndexInfo.mainEnd;
485 int32_t childRightBottonY = itemIndexInfo.crossEnd == -1 ? itemIndexInfo.crossIndex : itemIndexInfo.crossEnd;
486
487 int32_t intersectAreaLeftTopX = std::max(curFocusLeftTopX, childLeftTopX);
488 int32_t intersectAreaLeftTopY = std::max(curFocusLeftTopY, childLeftTopY);
489 int32_t intersectAreaRightBottonX = std::min(curFocusRightBottonX, childRightBottonX);
490 int32_t intersectAreaRightBottonY = std::min(curFocusRightBottonY, childRightBottonY);
491
492 int32_t intersectWidth = intersectAreaRightBottonX - intersectAreaLeftTopX + 1;
493 int32_t intersectHeight = intersectAreaRightBottonY - intersectAreaLeftTopY + 1;
494
495 return (intersectWidth < 0 || intersectHeight < 0) ? -1 : intersectWidth * intersectHeight;
496 }
497
498 namespace {
CalcCoordinatesDistance(double curFocusMain,double curFocusCross,double childMain,double childCross)499 double CalcCoordinatesDistance(double curFocusMain, double curFocusCross, double childMain, double childCross)
500 {
501 return std::sqrt(std::pow((curFocusMain - childMain), 2) + std::pow((curFocusCross - childCross), 2));
502 }
503 } // namespace
504
GetNearestDistanceFromChildToCurFocusItemInMainAxis(int32_t targetIndex,GridItemIndexInfo itemIndexInfo)505 double GridFocus::GetNearestDistanceFromChildToCurFocusItemInMainAxis(
506 int32_t targetIndex, GridItemIndexInfo itemIndexInfo)
507 {
508 double minDistance = std::numeric_limits<double>::max();
509 auto mainAxisIndex =
510 curFocusIndexInfo_.mainStart == -1 ? curFocusIndexInfo_.mainIndex : curFocusIndexInfo_.mainStart;
511 auto mainAxisEndIndex =
512 curFocusIndexInfo_.mainEnd == -1 ? curFocusIndexInfo_.mainIndex : curFocusIndexInfo_.mainEnd;
513 for (int32_t i = mainAxisIndex; i <= mainAxisEndIndex; i++) {
514 double childMainIndexDistance =
515 CalcCoordinatesDistance(i, curFocusIndexInfo_.crossIndex, itemIndexInfo.mainIndex, targetIndex);
516 double childMainStartDistance =
517 itemIndexInfo.mainStart == -1
518 ? std::numeric_limits<double>::max()
519 : CalcCoordinatesDistance(i, curFocusIndexInfo_.crossIndex, itemIndexInfo.mainStart, targetIndex);
520 double distance = std::min(childMainIndexDistance, childMainStartDistance);
521 if (LessNotEqual(distance, minDistance)) {
522 minDistance = distance;
523 }
524 }
525 return minDistance;
526 }
527
GetNearestDistanceFromChildToCurFocusItemInCrossAxis(int32_t targetIndex,GridItemIndexInfo itemIndexInfo)528 double GridFocus::GetNearestDistanceFromChildToCurFocusItemInCrossAxis(
529 int32_t targetIndex, GridItemIndexInfo itemIndexInfo)
530 {
531 double minDistance = std::numeric_limits<double>::max();
532 auto crossAxisIndex =
533 curFocusIndexInfo_.crossStart == -1 ? curFocusIndexInfo_.crossIndex : curFocusIndexInfo_.crossStart;
534 auto crossAxisEndIndex =
535 curFocusIndexInfo_.crossEnd == -1 ? curFocusIndexInfo_.crossIndex : curFocusIndexInfo_.crossEnd;
536 for (int32_t i = crossAxisIndex; i <= crossAxisEndIndex; i++) {
537 double childCrossIndexDistance =
538 CalcCoordinatesDistance(curFocusIndexInfo_.mainIndex, i, targetIndex, itemIndexInfo.crossIndex);
539 double childCrossEndDistance =
540 itemIndexInfo.crossEnd == -1
541 ? std::numeric_limits<double>::max()
542 : CalcCoordinatesDistance(curFocusIndexInfo_.mainIndex, i, targetIndex, itemIndexInfo.crossEnd);
543 double distance = std::min(childCrossIndexDistance, childCrossEndDistance);
544 if (LessNotEqual(distance, minDistance)) {
545 minDistance = distance;
546 }
547 }
548 return minDistance;
549 }
550
ResetAllDirectionsStep()551 void GridFocus::ResetAllDirectionsStep()
552 {
553 isLeftStep_ = false;
554 isRightStep_ = false;
555 isUpStep_ = false;
556 isDownStep_ = false;
557 isLeftEndStep_ = false;
558 isRightEndStep_ = false;
559 }
560
GetChildFocusNodeByIndex(int32_t tarMainIndex,int32_t tarCrossIndex,int32_t tarIndex)561 WeakPtr<FocusHub> GridFocus::GetChildFocusNodeByIndex(int32_t tarMainIndex, int32_t tarCrossIndex, int32_t tarIndex)
562 {
563 auto gridFrame = grid_.GetHost();
564 CHECK_NULL_RETURN(gridFrame, nullptr);
565 auto gridFocus = gridFrame->GetFocusHub();
566 CHECK_NULL_RETURN(gridFocus, nullptr);
567 std::list<RefPtr<FocusHub>> childFocusList;
568 gridFocus->FlushChildrenFocusHub(childFocusList);
569 for (const auto& childFocus : childFocusList) {
570 auto childFrame = childFocus->GetFrameNode();
571 if (!childFrame) {
572 continue;
573 }
574 auto childPattern = childFrame->GetPattern();
575 if (!childPattern) {
576 continue;
577 }
578 auto childItemPattern = AceType::DynamicCast<GridItemPattern>(childPattern);
579 if (!childItemPattern) {
580 continue;
581 }
582 auto childItemProperty = childItemPattern->GetLayoutProperty<GridItemLayoutProperty>();
583 if (!childItemProperty) {
584 continue;
585 }
586 auto curMainIndex = childItemProperty->GetMainIndex().value_or(-1);
587 auto curCrossIndex = childItemProperty->GetCrossIndex().value_or(-1);
588 if (tarIndex < 0) {
589 auto curMainSpan = childItemProperty->GetMainSpan(info_.axis_);
590 auto curCrossSpan = childItemProperty->GetCrossSpan(info_.axis_);
591 if (curMainIndex <= tarMainIndex && curMainIndex + curMainSpan > tarMainIndex &&
592 curCrossIndex <= tarCrossIndex && curCrossIndex + curCrossSpan > tarCrossIndex) {
593 return AceType::WeakClaim(AceType::RawPtr(childFocus));
594 }
595 } else {
596 auto row = info_.gridMatrix_.find(curMainIndex);
597 if (row == info_.gridMatrix_.end()) {
598 TAG_LOGW(AceLogTag::ACE_GRID, "Can not find target main index: %{public}d", curMainIndex);
599 continue;
600 }
601 auto cell = row->second.find(curCrossIndex);
602 if (cell == row->second.end()) {
603 TAG_LOGW(AceLogTag::ACE_GRID, "Can not find target cross index: %{public}d", curCrossIndex);
604 continue;
605 }
606 if (cell->second == tarIndex) {
607 return AceType::WeakClaim(AceType::RawPtr(childFocus));
608 }
609 }
610 }
611 return nullptr;
612 }
613
GetFocusableChildCrossIndexesAt(int32_t tarMainIndex)614 std::unordered_set<int32_t> GridFocus::GetFocusableChildCrossIndexesAt(int32_t tarMainIndex)
615 {
616 std::unordered_set<int32_t> result;
617 auto gridFrame = grid_.GetHost();
618 CHECK_NULL_RETURN(gridFrame, result);
619 auto gridFocus = gridFrame->GetFocusHub();
620 CHECK_NULL_RETURN(gridFocus, result);
621 std::list<RefPtr<FocusHub>> childFocusList;
622 gridFocus->FlushChildrenFocusHub(childFocusList);
623 for (const auto& childFocus : childFocusList) {
624 if (!childFocus->IsFocusable()) {
625 continue;
626 }
627 auto childFrame = childFocus->GetFrameNode();
628 if (!childFrame) {
629 continue;
630 }
631 auto childPattern = childFrame->GetPattern();
632 if (!childPattern) {
633 continue;
634 }
635 auto childItemPattern = AceType::DynamicCast<GridItemPattern>(childPattern);
636 if (!childItemPattern) {
637 continue;
638 }
639 auto childItemProperty = childItemPattern->GetLayoutProperty<GridItemLayoutProperty>();
640 if (!childItemProperty) {
641 continue;
642 }
643 auto irregularInfo = childItemPattern->GetIrregularItemInfo();
644 bool hasIrregularItemInfo = irregularInfo.has_value();
645 auto curMainIndex = childItemProperty->GetMainIndex().value_or(-1);
646 auto curCrossIndex = childItemProperty->GetCrossIndex().value_or(-1);
647 auto curMainStart =
648 hasIrregularItemInfo ? irregularInfo.value().mainStart : childItemProperty->GetMainStart(info_.axis_);
649 auto curMainEnd =
650 hasIrregularItemInfo ? irregularInfo.value().mainEnd : childItemProperty->GetMainEnd(info_.axis_);
651 if ((curMainIndex == tarMainIndex) ||
652 (curMainStart >= 0 && curMainStart <= tarMainIndex && tarMainIndex <= curMainEnd)) {
653 result.emplace(curCrossIndex);
654 }
655 }
656 std::string output;
657 for (const auto& index : result) {
658 output += std::to_string(index);
659 }
660 return result;
661 }
662
GetFocusNodeIndex(const RefPtr<FocusHub> & focusNode)663 int32_t GridFocus::GetFocusNodeIndex(const RefPtr<FocusHub>& focusNode)
664 {
665 auto tarFrame = focusNode->GetFrameNode();
666 CHECK_NULL_RETURN(tarFrame, -1);
667 auto tarPattern = tarFrame->GetPattern();
668 CHECK_NULL_RETURN(tarPattern, -1);
669 auto tarItemPattern = AceType::DynamicCast<GridItemPattern>(tarPattern);
670 CHECK_NULL_RETURN(tarItemPattern, -1);
671 auto tarItemProperty = tarItemPattern->GetLayoutProperty<GridItemLayoutProperty>();
672 CHECK_NULL_RETURN(tarItemProperty, -1);
673 auto tarMainIndex = tarItemProperty->GetMainIndex().value_or(-1);
674 auto tarCrossIndex = tarItemProperty->GetCrossIndex().value_or(-1);
675 auto it = info_.gridMatrix_.find(tarMainIndex);
676 if (it == info_.gridMatrix_.end()) {
677 TAG_LOGW(AceLogTag::ACE_GRID, "Can not find target main index: %{public}d", tarMainIndex);
678 if (tarMainIndex == 0) {
679 return 0;
680 }
681 return info_.childrenCount_ - 1;
682 }
683 auto cell = it->second.find(tarCrossIndex);
684 if (cell == it->second.end()) {
685 TAG_LOGW(AceLogTag::ACE_GRID, "Can not find target cross index: %{public}d", tarCrossIndex);
686 if (tarMainIndex == 0) {
687 return 0;
688 }
689 return info_.childrenCount_ - 1;
690 }
691 return cell->second;
692 }
693
ProcessFocusEvent(const KeyEvent & event,bool indexChanged)694 void GridFocus::ProcessFocusEvent(const KeyEvent& event, bool indexChanged)
695 {
696 auto host = grid_.GetHost();
697 CHECK_NULL_VOID(host);
698 auto focusHub = host->GetFocusHub();
699 CHECK_NULL_VOID(focusHub);
700 CHECK_NULL_VOID(focusHub->IsCurrentFocus());
701 if (needTriggerFocus_) {
702 if (triggerFocus_) {
703 needTriggerFocus_ = false;
704 triggerFocus_ = false;
705 focusHub->GetNextFocusByStep(event);
706 } else {
707 if (!focusIndex_.has_value()) {
708 needTriggerFocus_ = false;
709 return;
710 }
711 triggerFocus_ = true;
712 auto child = host->GetOrCreateChildByIndex(focusIndex_.value());
713 CHECK_NULL_VOID(child);
714 auto childNode = child->GetHostNode();
715 auto childFocusHub = childNode->GetFocusHub();
716 if (childFocusHub && !childFocusHub->IsCurrentFocus()) {
717 childFocusHub->RequestFocusImmediately();
718 }
719 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
720 }
721 return;
722 }
723 if (indexChanged && focusIndex_.has_value()) {
724 FireFocus();
725 }
726 }
727
FireFocus()728 void GridFocus::FireFocus()
729 {
730 auto host = grid_.GetHost();
731 CHECK_NULL_VOID(host);
732 auto* pipeline = grid_.GetContext();
733 CHECK_NULL_VOID(pipeline);
734 auto focusHub = host->GetFocusHub();
735 CHECK_NULL_VOID(focusHub);
736 CHECK_NULL_VOID(focusHub->IsCurrentFocus());
737 CHECK_NULL_VOID(focusIndex_.has_value());
738 if (IsInViewport(focusIndex_.value(), true)) {
739 auto child = host->GetChildByIndex(focusIndex_.value());
740 CHECK_NULL_VOID(child);
741 auto childNode = child->GetHostNode();
742 CHECK_NULL_VOID(childNode);
743 auto childFocusHub = childNode->GetFocusHub();
744 CHECK_NULL_VOID(childFocusHub);
745 if (!childFocusHub->IsCurrentFocus()) {
746 focusHub->SetFocusDependence(FocusDependence::AUTO);
747 childFocusHub->RequestFocusImmediately();
748 TAG_LOGI(
749 AceLogTag::ACE_GRID, "GridItem [%{public}d] scroll into viewport, Requests focus", focusIndex_.value());
750 }
751 } else {
752 auto childFocusHub = focusHub->GetLastWeakFocusNode().Upgrade();
753 CHECK_NULL_VOID(childFocusHub);
754 if (childFocusHub->IsCurrentFocus()) {
755 focusHub->LostChildFocusToSelf();
756 }
757 }
758 }
759
ScrollToLastFocusIndex(KeyCode keyCode)760 bool GridFocus::ScrollToLastFocusIndex(KeyCode keyCode)
761 {
762 auto* pipeline = grid_.GetContext();
763 CHECK_NULL_RETURN(pipeline, false);
764 CHECK_NULL_RETURN(pipeline->GetIsFocusActive(), false);
765 auto host = grid_.GetHost();
766 CHECK_NULL_RETURN(host, false);
767 auto focusHub = host->GetFocusHub();
768 CHECK_NULL_RETURN(focusHub, false);
769 CHECK_NULL_RETURN(focusHub->IsCurrentFocus(), false);
770 CHECK_NULL_RETURN(focusIndex_.has_value(), false);
771 auto focusIndex = focusIndex_.value();
772 if (!IsInViewport(focusIndex, false)) {
773 grid_.StopAnimate();
774 needTriggerFocus_ = true;
775
776 // If focused item is above viewport and the current keyCode type is UP, scroll forward one more line
777 if (focusIndex < info_.startIndex_ && keyCode == KeyCode::KEY_DPAD_UP &&
778 focusIndex - info_.crossCount_ >= 0) {
779 grid_.UpdateStartIndex(focusIndex - info_.crossCount_);
780 // If focused item is below viewport and the current keyCode type is DOWN, scroll backward one more line
781 } else if (focusIndex > info_.endIndex_ && keyCode == KeyCode::KEY_DPAD_DOWN &&
782 focusIndex + info_.crossCount_ < info_.childrenCount_) {
783 grid_.UpdateStartIndex(focusIndex + info_.crossCount_);
784 } else {
785 grid_.UpdateStartIndex(focusIndex);
786 }
787 return true;
788 }
789 return false;
790 }
791
ScrollToFocusNode(const WeakPtr<FocusHub> & focusNode)792 void GridFocus::ScrollToFocusNode(const WeakPtr<FocusHub>& focusNode)
793 {
794 grid_.StopAnimate();
795 auto nextFocus = focusNode.Upgrade();
796 CHECK_NULL_VOID(nextFocus);
797 grid_.UpdateStartIndex(GetFocusNodeIndex(nextFocus));
798 }
799
IsInViewport(int32_t index,bool needCheckCache) const800 bool GridFocus::IsInViewport(int32_t index, bool needCheckCache) const
801 {
802 auto host = grid_.GetHost();
803 CHECK_NULL_RETURN(host, true);
804 auto gridLayoutProperty = host->GetLayoutProperty<GridLayoutProperty>();
805 CHECK_NULL_RETURN(gridLayoutProperty, true);
806 int32_t cacheCount = gridLayoutProperty->GetCachedCountValue(info_.defCachedCount_) * info_.crossCount_;
807 bool showCachedItems = gridLayoutProperty->GetShowCachedItemsValue(false);
808 if (needCheckCache && showCachedItems) {
809 return index >= info_.startIndex_ - cacheCount && index <= info_.endIndex_ + cacheCount;
810 }
811 return index >= info_.startIndex_ && index <= info_.endIndex_;
812 }
813 } // namespace OHOS::Ace::NG