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 
24 namespace OHOS {
UIScrollView()25 UIScrollView::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 
OnDragEvent(const DragEvent & event)42 bool 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 
OnDragEndEvent(const DragEvent & event)51 bool 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 
Drag(const DragEvent & event)69 void 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 
OnPressEvent(const PressEvent & event)83 bool UIScrollView::OnPressEvent(const PressEvent& event)
84 {
85     StopAnimator();
86     return UIView::OnPressEvent(event);
87 }
88 
89 #if defined(ENABLE_ROTATE_INPUT) && ENABLE_ROTATE_INPUT
OnRotateEvent(const RotateEvent & event)90 bool 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 
OnRotateEndEvent(const RotateEvent & event)140 bool 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
SetIsEdge(bool & lastIsEdge,Rect childRect)149 void 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 
ScrollBy(int16_t xDistance,int16_t yDistance)164 void 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 
DragXInner(int16_t distance)178 bool 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 
DragYInner(int16_t distance)210 bool 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 
MoveOffset(int16_t offsetX,int16_t offsetY)242 bool 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 
RefreshScrollBar()260 void 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 
CalculateReboundDistance(int16_t & dragDistanceX,int16_t & dragDistanceY)282 void 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 
StopAnimator()304 void 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