• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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