- add sources.
[platform/framework/web/crosswalk.git] / src / ui / app_list / pagination_model.cc
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "ui/app_list/pagination_model.h"
6
7 #include <algorithm>
8
9 #include "ui/app_list/pagination_model_observer.h"
10 #include "ui/gfx/animation/slide_animation.h"
11
12 namespace app_list {
13
14 PaginationModel::PaginationModel()
15     : total_pages_(-1),
16       selected_page_(-1),
17       transition_(-1, 0),
18       pending_selected_page_(-1),
19       transition_duration_ms_(0),
20       overscroll_transition_duration_ms_(0),
21       last_overscroll_target_page_(0) {
22 }
23
24 PaginationModel::~PaginationModel() {
25 }
26
27 void PaginationModel::SetTotalPages(int total_pages) {
28   if (total_pages == total_pages_)
29     return;
30
31   total_pages_ = total_pages;
32   if (selected_page_ < 0)
33     SelectPage(0, false /* animate */);
34   if (selected_page_ >= total_pages_)
35     SelectPage(std::max(total_pages_ - 1, 0), false /* animate */);
36   FOR_EACH_OBSERVER(PaginationModelObserver, observers_, TotalPagesChanged());
37 }
38
39 void PaginationModel::SelectPage(int page, bool animate) {
40   if (animate) {
41     // -1 and |total_pages_| are valid target page for animation.
42     DCHECK(page >= -1 && page <= total_pages_);
43
44     if (!transition_animation_) {
45       if (page == selected_page_)
46         return;
47
48       // Suppress over scroll animation if the same one happens too fast.
49       if (!is_valid_page(page)) {
50         const base::TimeTicks now = base::TimeTicks::Now();
51
52         if (page == last_overscroll_target_page_) {
53           const int kMinOverScrollTimeGapInMs = 500;
54           const base::TimeDelta time_elapsed =
55                now - last_overscroll_animation_start_time_;
56           if (time_elapsed.InMilliseconds() < kMinOverScrollTimeGapInMs)
57             return;
58         }
59
60         last_overscroll_target_page_ = page;
61         last_overscroll_animation_start_time_ = now;
62       }
63
64       // Creates an animation if there is not one.
65       StartTransitionAnimation(Transition(page, 0));
66       return;
67     } else {
68       const bool showing = transition_animation_->IsShowing();
69       const int from_page = showing ? selected_page_ : transition_.target_page;
70       const int to_page = showing ? transition_.target_page : selected_page_;
71
72       if (from_page == page) {
73         if (showing)
74           transition_animation_->Hide();
75         else
76           transition_animation_->Show();
77         pending_selected_page_ = -1;
78       } else if (to_page != page) {
79         pending_selected_page_ = page;
80       } else {
81         pending_selected_page_ = -1;
82       }
83     }
84   } else {
85     DCHECK(total_pages_ == 0 || (page >= 0 && page < total_pages_));
86
87     if (page == selected_page_)
88       return;
89
90     ResetTransitionAnimation();
91
92     int old_selected = selected_page_;
93     selected_page_ = page;
94     NotifySelectedPageChanged(old_selected, selected_page_);
95   }
96 }
97
98 void PaginationModel::SelectPageRelative(int delta, bool animate) {
99   SelectPage(CalculateTargetPage(delta), animate);
100 }
101
102 void PaginationModel::SetTransition(const Transition& transition) {
103   // -1 and |total_pages_| is a valid target page, which means user is at
104   // the end and there is no target page for this scroll.
105   DCHECK(transition.target_page >= -1 &&
106          transition.target_page <= total_pages_);
107   DCHECK(transition.progress >= 0 && transition.progress <= 1);
108
109   if (transition_.Equals(transition))
110     return;
111
112   transition_ = transition;
113   NotifyTransitionChanged();
114 }
115
116 void PaginationModel::SetTransitionDurations(int duration_ms,
117                                              int overscroll_duration_ms) {
118   transition_duration_ms_ = duration_ms;
119   overscroll_transition_duration_ms_ = overscroll_duration_ms;
120 }
121
122 void PaginationModel::StartScroll() {
123   // Cancels current transition animation (if any).
124   transition_animation_.reset();
125 }
126
127 void PaginationModel::UpdateScroll(double delta) {
128   // Translates scroll delta to desired page change direction.
129   int page_change_dir = delta > 0 ? -1 : 1;
130
131   // Initializes a transition if there is none.
132   if (!has_transition())
133     transition_.target_page = CalculateTargetPage(page_change_dir);
134
135   // Updates transition progress.
136   int transition_dir = transition_.target_page > selected_page_ ? 1 : -1;
137   double progress = transition_.progress +
138       fabs(delta) * page_change_dir * transition_dir;
139
140   if (progress < 0) {
141     if (transition_.progress) {
142       transition_.progress = 0;
143       NotifyTransitionChanged();
144     }
145     clear_transition();
146   } else if (progress > 1) {
147     if (is_valid_page(transition_.target_page)) {
148       SelectPage(transition_.target_page, false);
149       clear_transition();
150     }
151   } else {
152     transition_.progress = progress;
153     NotifyTransitionChanged();
154   }
155 }
156
157 void PaginationModel::EndScroll(bool cancel) {
158   if (!has_transition())
159     return;
160
161   StartTransitionAnimation(transition_);
162
163   if (cancel)
164     transition_animation_->Hide();
165 }
166
167 bool PaginationModel::IsRevertingCurrentTransition() const {
168   // Use !IsShowing() so that we return true at the end of hide animation.
169   return transition_animation_ && !transition_animation_->IsShowing();
170 }
171
172 void PaginationModel::AddObserver(PaginationModelObserver* observer) {
173   observers_.AddObserver(observer);
174 }
175
176 void PaginationModel::RemoveObserver(PaginationModelObserver* observer) {
177   observers_.RemoveObserver(observer);
178 }
179
180 void PaginationModel::NotifySelectedPageChanged(int old_selected,
181                                                 int new_selected) {
182   FOR_EACH_OBSERVER(PaginationModelObserver,
183                     observers_,
184                     SelectedPageChanged(old_selected, new_selected));
185 }
186
187 void PaginationModel::NotifyTransitionStarted() {
188   FOR_EACH_OBSERVER(PaginationModelObserver, observers_, TransitionStarted());
189 }
190
191 void PaginationModel::NotifyTransitionChanged() {
192   FOR_EACH_OBSERVER(PaginationModelObserver, observers_, TransitionChanged());
193 }
194
195 int PaginationModel::CalculateTargetPage(int delta) const {
196   DCHECK_GT(total_pages_, 0);
197
198   int current_page = selected_page_;
199   if (transition_animation_ && transition_animation_->IsShowing()) {
200     current_page = pending_selected_page_ >= 0 ?
201         pending_selected_page_ : transition_.target_page;
202   }
203
204   const int target_page = current_page + delta;
205
206   int start_page = 0;
207   int end_page = total_pages_ - 1;
208
209   // Use invalid page when |selected_page_| is at ends.
210   if (target_page < start_page && selected_page_ == start_page)
211     start_page = -1;
212   else if (target_page > end_page && selected_page_ == end_page)
213     end_page = total_pages_;
214
215   return std::max(start_page, std::min(end_page, target_page));
216 }
217
218 void PaginationModel::StartTransitionAnimation(const Transition& transition) {
219   DCHECK(selected_page_ != transition.target_page);
220
221   NotifyTransitionStarted();
222   SetTransition(transition);
223
224   transition_animation_.reset(new gfx::SlideAnimation(this));
225   transition_animation_->SetTweenType(gfx::Tween::LINEAR);
226   transition_animation_->Reset(transition_.progress);
227
228   const int duration = is_valid_page(transition_.target_page) ?
229       transition_duration_ms_ : overscroll_transition_duration_ms_;
230   if (duration)
231     transition_animation_->SetSlideDuration(duration);
232
233   transition_animation_->Show();
234 }
235
236 void PaginationModel::ResetTransitionAnimation() {
237   transition_animation_.reset();
238   transition_.target_page = -1;
239   transition_.progress = 0;
240   pending_selected_page_ = -1;
241 }
242
243 void PaginationModel::AnimationProgressed(const gfx::Animation* animation) {
244   transition_.progress = transition_animation_->GetCurrentValue();
245   NotifyTransitionChanged();
246 }
247
248 void PaginationModel::AnimationEnded(const gfx::Animation* animation) {
249   // Save |pending_selected_page_| because SelectPage resets it.
250   int next_target = pending_selected_page_;
251
252   if (transition_animation_->GetCurrentValue() == 1) {
253     // Showing animation ends.
254     if (!is_valid_page(transition_.target_page)) {
255       // If target page is not in valid range, reverse the animation.
256       transition_animation_->Hide();
257       return;
258     }
259
260     // Otherwise, change page and finish the transition.
261     DCHECK(selected_page_ != transition_.target_page);
262     SelectPage(transition_.target_page, false /* animate */);
263   } else if (transition_animation_->GetCurrentValue() == 0) {
264     // Hiding animation ends. No page change should happen.
265     ResetTransitionAnimation();
266   }
267
268   if (next_target >= 0)
269     SelectPage(next_target, true);
270 }
271
272 }  // namespace app_list