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