1/*
2 * Copyright (c) 2020-2021 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#include "components/ui_scroll_view.h"
17
18#include "components/ui_abstract_scroll_bar.h"
19#include "dock/focus_manager.h"
20#include "dock/vibrator_manager.h"
21#include "draw/draw_rect.h"
22#include "gfx_utils/graphic_log.h"
23
24namespace OHOS {
25UIScrollView::UIScrollView() : scrollListener_(nullptr)
26{
27#if defined(ENABLE_ROTATE_INPUT) && ENABLE_ROTATE_INPUT
28    rotateFactor_ = DEFAULT_SCROLL_VIEW_ROTATE_FACTOR;
29    rotateThrowthreshold_ = SCROLLVIEW_ROTATE_THROW_THRESHOLD;
30    rotateAccCoefficient_ = SCROLLVIEW_ROTATE_DISTANCE_COEFF;
31#endif
32#if defined(ENABLE_VIBRATOR) && ENABLE_VIBRATOR
33    totalRotateLen_ = 0;
34    lastVibratorRotateLen_ = 0;
35#endif
36#if defined(ENABLE_FOCUS_MANAGER) && ENABLE_FOCUS_MANAGER
37    focusable_ = true;
38#endif
39    direction_ = HORIZONTAL_AND_VERTICAL;
40}
41
42bool UIScrollView::OnDragEvent(const DragEvent& event)
43{
44    if (scrollAnimator_.GetState() != Animator::STOP) {
45        UIAbstractScroll::StopAnimator();
46    }
47    Drag(event);
48    return UIView::OnDragEvent(event);
49}
50
51bool UIScrollView::OnDragEndEvent(const DragEvent& event)
52{
53    Point last = event.GetPreLastPoint();
54    Point current = event.GetLastPoint();
55    if ((last.x == current.x) && (last.y == current.y)) {
56        last = current;
57        current = event.GetCurrentPos();
58    }
59
60    if (!DragThrowAnimator(current, last, event.GetDragDirection())) {
61        if (scrollListener_ && (scrollListener_->GetScrollState() == OnScrollListener::SCROLL_STATE_MOVE)) {
62            scrollListener_->OnScrollEnd();
63            scrollListener_->SetScrollState(OnScrollListener::SCROLL_STATE_STOP);
64        }
65    }
66    return UIView::OnDragEndEvent(event);
67}
68
69void UIScrollView::Drag(const DragEvent& event)
70{
71    int16_t xDistance = event.GetDeltaX();
72    int16_t yDistance = event.GetDeltaY();
73
74    if ((direction_ == HORIZONTAL || direction_ == HORIZONTAL_AND_VERTICAL) && xDistance != 0) {
75        DragXInner(xDistance);
76    }
77    if ((direction_ == VERTICAL || direction_ == HORIZONTAL_AND_VERTICAL) && yDistance != 0) {
78        RefreshDelta(yDistance);
79        DragYInner(yDistance);
80    }
81}
82
83bool UIScrollView::OnPressEvent(const PressEvent& event)
84{
85    StopAnimator();
86    return UIView::OnPressEvent(event);
87}
88
89#if defined(ENABLE_ROTATE_INPUT) && ENABLE_ROTATE_INPUT
90bool UIScrollView::OnRotateEvent(const RotateEvent& event)
91{
92    if (direction_ == HORIZONTAL_NOR_VERTICAL) {
93        return UIView::OnRotateEvent(event);
94    }
95    int16_t rotateLen = static_cast<int16_t>(event.GetRotate() * rotateFactor_);
96#if defined(ENABLE_VIBRATOR) && ENABLE_VIBRATOR
97    bool lastIsEdge = false;
98    Rect childRect = GetAllChildRelativeRect();
99    SetIsEdge(lastIsEdge, childRect);
100#endif
101    RefreshRotate(rotateLen);
102    if (direction_ == HORIZONTAL) {
103        DragXInner(rotateLen);
104    } else {
105        DragYInner(rotateLen);
106    }
107#if defined(ENABLE_VIBRATOR) && ENABLE_VIBRATOR
108    totalRotateLen_ += rotateLen;
109    childRect = GetAllChildRelativeRect();
110    bool isEdge = false;
111    if (direction_ == HORIZONTAL) {
112        if (childRect.GetLeft() - scrollBlankSize_ >= 0 || childRect.GetRight() + scrollBlankSize_ <= GetWidth()) {
113            isEdge = true;
114        }
115    } else {
116        if (childRect.GetTop() - scrollBlankSize_ >= 0 || childRect.GetBottom() + scrollBlankSize_ <= GetHeight()) {
117            isEdge = true;
118        }
119    }
120    VibratorFunc vibratorFunc = VibratorManager::GetInstance()->GetVibratorFunc();
121    if (vibratorFunc != nullptr && !isEdge) {
122        rotateLen = MATH_ABS(totalRotateLen_ - lastVibratorRotateLen_);
123        if (rotateLen > DEFAULT_SCROLL_VIEW_VIBRATION_LEN) {
124            uint16_t vibrationCnt = rotateLen / DEFAULT_SCROLL_VIEW_VIBRATION_LEN;
125            for (uint16_t i = 0; i < vibrationCnt; i++) {
126                GRAPHIC_LOGI("UIScrollView::OnRotateEvent calls TYPE_ONE vibrator");
127                vibratorFunc(VibratorType::VIBRATOR_TYPE_ONE);
128            }
129            lastVibratorRotateLen_ = totalRotateLen_;
130        }
131    }
132    if (vibratorFunc != nullptr && (!lastIsEdge && isEdge)) {
133        GRAPHIC_LOGI("UIScrollView::OnRotateEvent calls TYPE_THREE vibrator");
134        vibratorFunc(VibratorType::VIBRATOR_TYPE_THREE);
135    }
136#endif
137    return UIView::OnRotateEvent(event);
138}
139
140bool UIScrollView::OnRotateEndEvent(const RotateEvent& event)
141{
142    if (direction_ == HORIZONTAL_NOR_VERTICAL) {
143        return UIView::OnRotateEvent(event);
144    }
145    return UIAbstractScroll::OnRotateEndEvent(event);
146}
147
148#if defined(ENABLE_VIBRATOR) && ENABLE_VIBRATOR
149void UIScrollView::SetIsEdge(bool& lastIsEdge, Rect childRect)
150{
151    if (direction_ == HORIZONTAL) {
152        if (childRect.GetLeft() - scrollBlankSize_ >= 0 || childRect.GetRight() + scrollBlankSize_ <= GetWidth()) {
153            lastIsEdge = true;
154        }
155    } else {
156        if (childRect.GetTop() - scrollBlankSize_ >= 0 || childRect.GetBottom() + scrollBlankSize_ <= GetHeight()) {
157            lastIsEdge = true;
158        }
159    }
160}
161#endif
162#endif
163
164void UIScrollView::ScrollBy(int16_t xDistance, int16_t yDistance)
165{
166    if ((direction_ == HORIZONTAL || direction_ == HORIZONTAL_AND_VERTICAL) && xDistance != 0) {
167        DragXInner(xDistance);
168    }
169    if ((direction_ == VERTICAL || direction_ == HORIZONTAL_AND_VERTICAL) && yDistance != 0) {
170        DragYInner(yDistance);
171    }
172    if ((scrollListener_ != nullptr) && (scrollListener_->GetScrollState() == OnScrollListener::SCROLL_STATE_MOVE)) {
173        scrollListener_->OnScrollEnd();
174        scrollListener_->SetScrollState(OnScrollListener::SCROLL_STATE_STOP);
175    }
176}
177
178bool UIScrollView::DragXInner(int16_t distance)
179{
180    Rect childRect = GetAllChildRelativeRect();
181    int16_t reboundSize = reboundSize_;
182    if (scrollAnimator_.GetState() != Animator::STOP) {
183        reboundSize = 0;
184    }
185
186    if (childRect.GetWidth() <= (GetWidth() - (scrollBlankSize_ << 1)) ||
187        !(direction_ == HORIZONTAL || direction_ == HORIZONTAL_AND_VERTICAL)) {
188        return false;
189    }
190
191    if (distance > 0) {
192        if (childRect.GetLeft() > scrollBlankSize_ + reboundSize) {
193            distance = 0;
194        } else if ((childRect.GetLeft() + distance) > scrollBlankSize_ + reboundSize) {
195            distance = scrollBlankSize_ - childRect.GetLeft() + reboundSize;
196        }
197    } else {
198        int16_t childRight = childRect.GetRight();
199        int16_t scrollWidth = GetWidth();
200        if (childRight < scrollWidth - (scrollBlankSize_ + reboundSize)) {
201            distance = 0;
202        } else if (childRight + distance < scrollWidth - (scrollBlankSize_ + reboundSize)) {
203            distance = scrollWidth - (scrollBlankSize_ + reboundSize) - childRight - 1;
204        }
205    }
206
207    return MoveOffset(distance, 0);
208}
209
210bool UIScrollView::DragYInner(int16_t distance)
211{
212    Rect childRect = GetAllChildRelativeRect();
213    int16_t reboundSize = reboundSize_;
214    if (scrollAnimator_.GetState() != Animator::STOP) {
215        reboundSize = 0;
216    }
217
218    if (childRect.GetHeight() <= (GetHeight() - (scrollBlankSize_ << 1)) ||
219        !(direction_ == VERTICAL || direction_ == HORIZONTAL_AND_VERTICAL)) {
220        return false;
221    }
222
223    if (distance > 0) {
224        if (childRect.GetTop() > scrollBlankSize_ + reboundSize) {
225            distance = 0;
226        } else if ((childRect.GetTop() + distance) > scrollBlankSize_ + reboundSize) {
227            distance = scrollBlankSize_ - childRect.GetTop() + reboundSize;
228        }
229    } else {
230        int16_t childBottom = childRect.GetBottom();
231        int16_t scrollHeight = GetHeight();
232        if (childBottom < scrollHeight - (scrollBlankSize_ + reboundSize)) {
233            distance = 0;
234        } else if (childBottom + distance < scrollHeight - (scrollBlankSize_ + reboundSize)) {
235            distance = scrollHeight - (scrollBlankSize_ + reboundSize) - childBottom - 1;
236        }
237    }
238
239    return MoveOffset(0, distance);
240}
241
242bool UIScrollView::MoveOffset(int16_t offsetX, int16_t offsetY)
243{
244    if ((offsetX != 0) || (offsetY != 0)) {
245        if ((scrollListener_ != nullptr) &&
246            (scrollListener_->GetScrollState() == OnScrollListener::SCROLL_STATE_STOP)) {
247            scrollListener_->OnScrollStart();
248            scrollListener_->SetScrollState(OnScrollListener::SCROLL_STATE_MOVE);
249        }
250        UIAbstractScroll::MoveChildByOffset(offsetX, offsetY);
251        if (xScrollBarVisible_ || yScrollBarVisible_) {
252            RefreshScrollBar();
253        }
254        Invalidate();
255        return true;
256    }
257    return false;
258}
259
260void UIScrollView::RefreshScrollBar()
261{
262    Rect childrenRect = GetAllChildRelativeRect();
263    /* calculate scrollBar's the proportion of foreground */
264    int16_t totalLen = childrenRect.GetHeight() + 2 * scrollBlankSize_; // 2: two blank space on both sizes
265    int16_t len = GetHeight();
266    if (yScrollBarVisible_) {
267        yScrollBar_->SetForegroundProportion(static_cast<float>(len) / totalLen);
268        /* calculate scrolling progress */
269        yScrollBar_->SetScrollProgress(static_cast<float>(scrollBlankSize_ - childrenRect.GetTop()) / (totalLen - len));
270    }
271    if (xScrollBarVisible_) {
272        /* so do x-bar */
273        totalLen = childrenRect.GetWidth() + 2 * scrollBlankSize_; // 2: two blank space on both sizes
274        len = GetWidth();
275        xScrollBar_->SetForegroundProportion(static_cast<float>(len) / totalLen);
276        xScrollBar_->SetScrollProgress(static_cast<float>(scrollBlankSize_ - childrenRect.GetLeft()) /
277                                       (totalLen - len));
278    }
279    RefreshAnimator();
280}
281
282void UIScrollView::CalculateReboundDistance(int16_t& dragDistanceX, int16_t& dragDistanceY)
283{
284    Rect rect = GetAllChildRelativeRect();
285    int16_t top = rect.GetTop();
286    int16_t bottom = rect.GetBottom();
287    int16_t scrollHeight = GetHeight();
288    int16_t left = rect.GetLeft();
289    int16_t right = rect.GetRight();
290    int16_t scrollWidth = GetWidth();
291    if (scrollBlankSize_ < top) {
292        dragDistanceY = scrollBlankSize_ - top;
293    } else if (bottom < scrollHeight - 1) {
294        dragDistanceY = scrollHeight - scrollBlankSize_ - bottom - 1;
295    }
296
297    if (scrollBlankSize_ < left) {
298        dragDistanceX = scrollBlankSize_ - left;
299    } else if (right < scrollWidth - 1) {
300        dragDistanceX = scrollWidth - scrollBlankSize_ - right - 1;
301    }
302}
303
304void UIScrollView::StopAnimator()
305{
306    if ((scrollListener_ != nullptr) && (scrollListener_->GetScrollState() == OnScrollListener::SCROLL_STATE_MOVE)) {
307        scrollListener_->OnScrollEnd();
308        scrollListener_->SetScrollState(OnScrollListener::SCROLL_STATE_STOP);
309    }
310    UIAbstractScroll::StopAnimator();
311}
312} // namespace OHOS
313