1a3e0fd82Sopenharmony_ci/*
2a3e0fd82Sopenharmony_ci * Copyright (c) 2020-2021 Huawei Device Co., Ltd.
3a3e0fd82Sopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License");
4a3e0fd82Sopenharmony_ci * you may not use this file except in compliance with the License.
5a3e0fd82Sopenharmony_ci * You may obtain a copy of the License at
6a3e0fd82Sopenharmony_ci *
7a3e0fd82Sopenharmony_ci *     http://www.apache.org/licenses/LICENSE-2.0
8a3e0fd82Sopenharmony_ci *
9a3e0fd82Sopenharmony_ci * Unless required by applicable law or agreed to in writing, software
10a3e0fd82Sopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS,
11a3e0fd82Sopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12a3e0fd82Sopenharmony_ci * See the License for the specific language governing permissions and
13a3e0fd82Sopenharmony_ci * limitations under the License.
14a3e0fd82Sopenharmony_ci */
15a3e0fd82Sopenharmony_ci
16a3e0fd82Sopenharmony_ci#include "dock/focus_manager.h"
17a3e0fd82Sopenharmony_ci#if ENABLE_FOCUS_MANAGER
18a3e0fd82Sopenharmony_ci#include "components/root_view.h"
19a3e0fd82Sopenharmony_ci#include "gfx_utils/graphic_math.h"
20a3e0fd82Sopenharmony_ci
21a3e0fd82Sopenharmony_ci#include "common/input_method_manager.h"
22a3e0fd82Sopenharmony_ci
23a3e0fd82Sopenharmony_cinamespace OHOS {
24a3e0fd82Sopenharmony_ciFocusManager* FocusManager::GetInstance()
25a3e0fd82Sopenharmony_ci{
26a3e0fd82Sopenharmony_ci    static FocusManager instance;
27a3e0fd82Sopenharmony_ci    return &instance;
28a3e0fd82Sopenharmony_ci}
29a3e0fd82Sopenharmony_ci
30a3e0fd82Sopenharmony_cibool FocusManager::RequestFocus(UIView* view)
31a3e0fd82Sopenharmony_ci{
32a3e0fd82Sopenharmony_ci    if (view == nullptr || view == focusView_ || !view->IsFocusable() ||
33a3e0fd82Sopenharmony_ci        !view->IsVisible()) {
34a3e0fd82Sopenharmony_ci        return false;
35a3e0fd82Sopenharmony_ci    }
36a3e0fd82Sopenharmony_ci    lastFocusView_ = focusView_;
37a3e0fd82Sopenharmony_ci    focusView_ = view;
38a3e0fd82Sopenharmony_ci
39a3e0fd82Sopenharmony_ci    UIViewGroup* rootView = RootView::GetInstance();
40a3e0fd82Sopenharmony_ci    if (RootView::FindSubView(*rootView, lastFocusView_)) {
41a3e0fd82Sopenharmony_ci        lastFocusView_->Blur();
42a3e0fd82Sopenharmony_ci    }
43a3e0fd82Sopenharmony_ci    focusView_->Focus();
44a3e0fd82Sopenharmony_ci
45a3e0fd82Sopenharmony_ci    UIViewType viewType = focusView_->GetViewType();
46a3e0fd82Sopenharmony_ci    // show keyboard if the view is edittable
47a3e0fd82Sopenharmony_ci    if (viewType == UI_EDIT_TEXT) {
48a3e0fd82Sopenharmony_ci        InputMethodManager::GetInstance().ShowInputMethod(focusView_);
49a3e0fd82Sopenharmony_ci    }
50a3e0fd82Sopenharmony_ci
51a3e0fd82Sopenharmony_ci    return true;
52a3e0fd82Sopenharmony_ci}
53a3e0fd82Sopenharmony_ci
54a3e0fd82Sopenharmony_cibool FocusManager::ClearFocus()
55a3e0fd82Sopenharmony_ci{
56a3e0fd82Sopenharmony_ci    if (focusView_ == nullptr) {
57a3e0fd82Sopenharmony_ci        return false;
58a3e0fd82Sopenharmony_ci    }
59a3e0fd82Sopenharmony_ci    lastFocusView_ = focusView_;
60a3e0fd82Sopenharmony_ci    focusView_->Blur();
61a3e0fd82Sopenharmony_ci    focusView_ = nullptr;
62a3e0fd82Sopenharmony_ci    return true;
63a3e0fd82Sopenharmony_ci}
64a3e0fd82Sopenharmony_ci
65a3e0fd82Sopenharmony_cibool FocusManager::RequestFocusByDirection(uint8_t direction)
66a3e0fd82Sopenharmony_ci{
67a3e0fd82Sopenharmony_ci    if (focusView_ != nullptr) {
68a3e0fd82Sopenharmony_ci        UIView* candidate = nullptr;
69a3e0fd82Sopenharmony_ci        if (GetNextFocus(focusView_, candidate, direction)) {
70a3e0fd82Sopenharmony_ci            return RequestFocus(candidate);
71a3e0fd82Sopenharmony_ci        }
72a3e0fd82Sopenharmony_ci    }
73a3e0fd82Sopenharmony_ci    return false;
74a3e0fd82Sopenharmony_ci}
75a3e0fd82Sopenharmony_ci
76a3e0fd82Sopenharmony_cibool FocusManager::CompareCandidates(UIView* focusedView, UIView*& candidate, UIView* current, uint8_t direction)
77a3e0fd82Sopenharmony_ci{
78a3e0fd82Sopenharmony_ci    if (current == focusedView) {
79a3e0fd82Sopenharmony_ci        return false;
80a3e0fd82Sopenharmony_ci    }
81a3e0fd82Sopenharmony_ci    bool res = false;
82a3e0fd82Sopenharmony_ci    switch (direction) {
83a3e0fd82Sopenharmony_ci        case FOCUS_DIRECTION_UP: {
84a3e0fd82Sopenharmony_ci            res = CompareCandidatesByUp(focusedView, candidate, current);
85a3e0fd82Sopenharmony_ci            break;
86a3e0fd82Sopenharmony_ci        }
87a3e0fd82Sopenharmony_ci        case FOCUS_DIRECTION_DOWN: {
88a3e0fd82Sopenharmony_ci            res = CompareCandidatesByDown(focusedView, candidate, current);
89a3e0fd82Sopenharmony_ci            break;
90a3e0fd82Sopenharmony_ci        }
91a3e0fd82Sopenharmony_ci        case FOCUS_DIRECTION_LEFT: {
92a3e0fd82Sopenharmony_ci            res = CompareCandidatesByLeft(focusedView, candidate, current);
93a3e0fd82Sopenharmony_ci            break;
94a3e0fd82Sopenharmony_ci        }
95a3e0fd82Sopenharmony_ci        case FOCUS_DIRECTION_RIGHT: {
96a3e0fd82Sopenharmony_ci            res = CompareCandidatesByRight(focusedView, candidate, current);
97a3e0fd82Sopenharmony_ci            break;
98a3e0fd82Sopenharmony_ci        }
99a3e0fd82Sopenharmony_ci        default:
100a3e0fd82Sopenharmony_ci            return false;
101a3e0fd82Sopenharmony_ci    }
102a3e0fd82Sopenharmony_ci    return res;
103a3e0fd82Sopenharmony_ci}
104a3e0fd82Sopenharmony_ci
105a3e0fd82Sopenharmony_cibool FocusManager::IsAtSameCol(const Rect& rect1, const Rect& rect2)
106a3e0fd82Sopenharmony_ci{
107a3e0fd82Sopenharmony_ci    return ((rect1.GetLeft() < rect2.GetRight()) && (rect1.GetRight() > rect2.GetLeft()));
108a3e0fd82Sopenharmony_ci}
109a3e0fd82Sopenharmony_ci
110a3e0fd82Sopenharmony_cibool FocusManager::IsAtSameRow(const Rect& rect1, const Rect& rect2)
111a3e0fd82Sopenharmony_ci{
112a3e0fd82Sopenharmony_ci    return ((rect1.GetBottom() < rect2.GetTop()) && (rect1.GetTop() > rect2.GetBottom()));
113a3e0fd82Sopenharmony_ci}
114a3e0fd82Sopenharmony_ci
115a3e0fd82Sopenharmony_cibool FocusManager::CompareCandidatesByUp(UIView* focusedView, UIView*& candidate, UIView* current)
116a3e0fd82Sopenharmony_ci{
117a3e0fd82Sopenharmony_ci    Rect focusedViewRect = focusedView->GetRect();
118a3e0fd82Sopenharmony_ci    Rect currentRect = current->GetRect();
119a3e0fd82Sopenharmony_ci    if (currentRect.GetBottom() >= focusedViewRect.GetBottom()) {
120a3e0fd82Sopenharmony_ci        return false;
121a3e0fd82Sopenharmony_ci    }
122a3e0fd82Sopenharmony_ci    if (candidate != nullptr) {
123a3e0fd82Sopenharmony_ci        Rect candidateRect = candidate->GetRect();
124a3e0fd82Sopenharmony_ci        if (IsAtSameCol(focusedViewRect, currentRect) && IsAtSameCol(focusedViewRect, candidateRect)) {
125a3e0fd82Sopenharmony_ci            return currentRect.GetBottom() > candidateRect.GetBottom();
126a3e0fd82Sopenharmony_ci        }
127a3e0fd82Sopenharmony_ci        return CompareCandidatesDistance(focusedViewRect, candidateRect, currentRect);
128a3e0fd82Sopenharmony_ci    }
129a3e0fd82Sopenharmony_ci    return true;
130a3e0fd82Sopenharmony_ci}
131a3e0fd82Sopenharmony_ci
132a3e0fd82Sopenharmony_cibool FocusManager::CompareCandidatesByDown(UIView* focusedView, UIView*& candidate, UIView* current)
133a3e0fd82Sopenharmony_ci{
134a3e0fd82Sopenharmony_ci    Rect focusedViewRect = focusedView->GetRect();
135a3e0fd82Sopenharmony_ci    Rect currentRect = current->GetRect();
136a3e0fd82Sopenharmony_ci    if (currentRect.GetTop() <= focusedViewRect.GetTop()) {
137a3e0fd82Sopenharmony_ci        return false;
138a3e0fd82Sopenharmony_ci    }
139a3e0fd82Sopenharmony_ci    if (candidate != nullptr) {
140a3e0fd82Sopenharmony_ci        Rect candidateRect = candidate->GetRect();
141a3e0fd82Sopenharmony_ci        if (IsAtSameCol(focusedViewRect, currentRect) && IsAtSameCol(focusedViewRect, candidateRect)) {
142a3e0fd82Sopenharmony_ci            return currentRect.GetTop() < candidateRect.GetTop();
143a3e0fd82Sopenharmony_ci        }
144a3e0fd82Sopenharmony_ci        return CompareCandidatesDistance(focusedViewRect, candidateRect, currentRect);
145a3e0fd82Sopenharmony_ci    }
146a3e0fd82Sopenharmony_ci    return true;
147a3e0fd82Sopenharmony_ci}
148a3e0fd82Sopenharmony_ci
149a3e0fd82Sopenharmony_cibool FocusManager::CompareCandidatesByLeft(UIView* focusedView, UIView*& candidate, UIView* current)
150a3e0fd82Sopenharmony_ci{
151a3e0fd82Sopenharmony_ci    Rect focusedViewRect = focusedView->GetRect();
152a3e0fd82Sopenharmony_ci    Rect currentRect = current->GetRect();
153a3e0fd82Sopenharmony_ci    if (currentRect.GetRight() >= focusedViewRect.GetRight()) {
154a3e0fd82Sopenharmony_ci        return false;
155a3e0fd82Sopenharmony_ci    }
156a3e0fd82Sopenharmony_ci    if (candidate != nullptr) {
157a3e0fd82Sopenharmony_ci        Rect candidateRect = candidate->GetRect();
158a3e0fd82Sopenharmony_ci        if (IsAtSameRow(focusedViewRect, currentRect) && IsAtSameRow(focusedViewRect, candidateRect)) {
159a3e0fd82Sopenharmony_ci            return currentRect.GetRight() > candidateRect.GetRight();
160a3e0fd82Sopenharmony_ci        }
161a3e0fd82Sopenharmony_ci        return CompareCandidatesDistance(focusedViewRect, candidateRect, currentRect);
162a3e0fd82Sopenharmony_ci    }
163a3e0fd82Sopenharmony_ci    return true;
164a3e0fd82Sopenharmony_ci}
165a3e0fd82Sopenharmony_ci
166a3e0fd82Sopenharmony_cibool FocusManager::CompareCandidatesByRight(UIView* focusedView, UIView*& candidate, UIView* current)
167a3e0fd82Sopenharmony_ci{
168a3e0fd82Sopenharmony_ci    Rect focusedViewRect = focusedView->GetRect();
169a3e0fd82Sopenharmony_ci    Rect currentRect = current->GetRect();
170a3e0fd82Sopenharmony_ci    if (currentRect.GetLeft() <= focusedViewRect.GetLeft()) {
171a3e0fd82Sopenharmony_ci        return false;
172a3e0fd82Sopenharmony_ci    }
173a3e0fd82Sopenharmony_ci    if (candidate != nullptr) {
174a3e0fd82Sopenharmony_ci        Rect candidateRect = candidate->GetRect();
175a3e0fd82Sopenharmony_ci        if (IsAtSameRow(focusedViewRect, currentRect) && IsAtSameRow(focusedViewRect, candidateRect)) {
176a3e0fd82Sopenharmony_ci            return currentRect.GetLeft() < candidateRect.GetLeft();
177a3e0fd82Sopenharmony_ci        }
178a3e0fd82Sopenharmony_ci        return CompareCandidatesDistance(focusedViewRect, candidateRect, currentRect);
179a3e0fd82Sopenharmony_ci    }
180a3e0fd82Sopenharmony_ci    return true;
181a3e0fd82Sopenharmony_ci}
182a3e0fd82Sopenharmony_ci
183a3e0fd82Sopenharmony_cibool FocusManager::CompareCandidatesDistance(const Rect& focused, const Rect& candidate, const Rect& current)
184a3e0fd82Sopenharmony_ci{
185a3e0fd82Sopenharmony_ci    uint64_t xDiff1 = focused.GetX() - candidate.GetX();
186a3e0fd82Sopenharmony_ci    uint64_t yDiff1 = focused.GetY() - candidate.GetY();
187a3e0fd82Sopenharmony_ci    uint64_t xDiff2 = focused.GetX() - current.GetX();
188a3e0fd82Sopenharmony_ci    uint64_t yDiff2 = focused.GetY() - current.GetY();
189a3e0fd82Sopenharmony_ci    uint64_t distance1 = xDiff1 * xDiff1 + yDiff1 * yDiff1;
190a3e0fd82Sopenharmony_ci    uint64_t distance2 = xDiff2 * xDiff2 + yDiff2 * yDiff2;
191a3e0fd82Sopenharmony_ci    return distance2 < distance1;
192a3e0fd82Sopenharmony_ci}
193a3e0fd82Sopenharmony_ci
194a3e0fd82Sopenharmony_cibool FocusManager::GetNextFocus(UIView* focusedView, UIView*& candidate, uint8_t direction)
195a3e0fd82Sopenharmony_ci{
196a3e0fd82Sopenharmony_ci    UIView* parent = focusedView->GetParent();
197a3e0fd82Sopenharmony_ci    if (parent == nullptr) {
198a3e0fd82Sopenharmony_ci        return false;
199a3e0fd82Sopenharmony_ci    }
200a3e0fd82Sopenharmony_ci    UIView* child = nullptr;
201a3e0fd82Sopenharmony_ci    bool isFoundBestCandidate = false;
202a3e0fd82Sopenharmony_ci    UIView* current = focusedView;
203a3e0fd82Sopenharmony_ci    while (parent != nullptr) {
204a3e0fd82Sopenharmony_ci        if (isFoundBestCandidate) {
205a3e0fd82Sopenharmony_ci            return true;
206a3e0fd82Sopenharmony_ci        }
207a3e0fd82Sopenharmony_ci        child = static_cast<UIViewGroup*>(parent)->GetChildrenHead();
208a3e0fd82Sopenharmony_ci        while (child != nullptr) {
209a3e0fd82Sopenharmony_ci            if (child == current) {
210a3e0fd82Sopenharmony_ci                child = child->GetNextSibling();
211a3e0fd82Sopenharmony_ci                continue;
212a3e0fd82Sopenharmony_ci            }
213a3e0fd82Sopenharmony_ci            if (child->IsViewGroup()) {
214a3e0fd82Sopenharmony_ci                if (GetNextFocus(focusedView, candidate, static_cast<UIViewGroup*>(child), direction)) {
215a3e0fd82Sopenharmony_ci                    isFoundBestCandidate = true;
216a3e0fd82Sopenharmony_ci                }
217a3e0fd82Sopenharmony_ci            } else {
218a3e0fd82Sopenharmony_ci                if (GetNextFocus(focusedView, candidate, child, direction)) {
219a3e0fd82Sopenharmony_ci                    isFoundBestCandidate = true;
220a3e0fd82Sopenharmony_ci                }
221a3e0fd82Sopenharmony_ci            }
222a3e0fd82Sopenharmony_ci            child = child->GetNextSibling();
223a3e0fd82Sopenharmony_ci        }
224a3e0fd82Sopenharmony_ci        if (isFoundBestCandidate) {
225a3e0fd82Sopenharmony_ci            return true;
226a3e0fd82Sopenharmony_ci        } else {
227a3e0fd82Sopenharmony_ci            current = parent;
228a3e0fd82Sopenharmony_ci        }
229a3e0fd82Sopenharmony_ci        parent = parent->GetParent();
230a3e0fd82Sopenharmony_ci    }
231a3e0fd82Sopenharmony_ci    return false;
232a3e0fd82Sopenharmony_ci}
233a3e0fd82Sopenharmony_ci
234a3e0fd82Sopenharmony_cibool FocusManager::GetNextFocus(UIView* focusedView, UIView*& candidate, UIView* view, uint8_t direction)
235a3e0fd82Sopenharmony_ci{
236a3e0fd82Sopenharmony_ci    UIView* current = view;
237a3e0fd82Sopenharmony_ci    if (!current->IsVisible() || !current->IsFocusable()) {
238a3e0fd82Sopenharmony_ci        return false;
239a3e0fd82Sopenharmony_ci    }
240a3e0fd82Sopenharmony_ci    if (CompareCandidates(focusedView, candidate, current, direction)) {
241a3e0fd82Sopenharmony_ci        candidate = current;
242a3e0fd82Sopenharmony_ci        return true;
243a3e0fd82Sopenharmony_ci    }
244a3e0fd82Sopenharmony_ci    return false;
245a3e0fd82Sopenharmony_ci}
246a3e0fd82Sopenharmony_ci
247a3e0fd82Sopenharmony_cibool FocusManager::GetNextFocus(UIView* focusedView, UIView*& candidate, UIViewGroup* viewGroup, uint8_t direction)
248a3e0fd82Sopenharmony_ci{
249a3e0fd82Sopenharmony_ci    UIViewGroup* current = viewGroup;
250a3e0fd82Sopenharmony_ci    if (!current->IsVisible() || !current->IsFocusable()) {
251a3e0fd82Sopenharmony_ci        return false;
252a3e0fd82Sopenharmony_ci    }
253a3e0fd82Sopenharmony_ci    if (current->IsInterceptFocus()) {
254a3e0fd82Sopenharmony_ci        return GetNextFocus(focusedView, candidate, static_cast<UIView*>(current), direction);
255a3e0fd82Sopenharmony_ci    }
256a3e0fd82Sopenharmony_ci
257a3e0fd82Sopenharmony_ci    UIView* child = current->GetChildrenHead();
258a3e0fd82Sopenharmony_ci    bool childFocusable = false;
259a3e0fd82Sopenharmony_ci    while (child != nullptr) {
260a3e0fd82Sopenharmony_ci        if (child == focusedView) {
261a3e0fd82Sopenharmony_ci            child = child->GetNextSibling();
262a3e0fd82Sopenharmony_ci            continue;
263a3e0fd82Sopenharmony_ci        }
264a3e0fd82Sopenharmony_ci        if (child->IsViewGroup()) {
265a3e0fd82Sopenharmony_ci            if (GetNextFocus(focusedView, candidate, static_cast<UIViewGroup*>(child), direction)) {
266a3e0fd82Sopenharmony_ci                childFocusable = true;
267a3e0fd82Sopenharmony_ci            }
268a3e0fd82Sopenharmony_ci        } else if (GetNextFocus(focusedView, candidate, child, direction)) {
269a3e0fd82Sopenharmony_ci            childFocusable = true;
270a3e0fd82Sopenharmony_ci        }
271a3e0fd82Sopenharmony_ci        child = child->GetNextSibling();
272a3e0fd82Sopenharmony_ci    }
273a3e0fd82Sopenharmony_ci    return childFocusable;
274a3e0fd82Sopenharmony_ci}
275a3e0fd82Sopenharmony_ci}
276a3e0fd82Sopenharmony_ci#endif
277