- add sources.
[platform/framework/web/crosswalk.git] / src / ui / app_list / views / page_switcher.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/views/page_switcher.h"
6
7 #include <algorithm>
8
9 #include "third_party/skia/include/core/SkPath.h"
10 #include "ui/app_list/app_list_constants.h"
11 #include "ui/app_list/pagination_model.h"
12 #include "ui/gfx/animation/throb_animation.h"
13 #include "ui/gfx/canvas.h"
14 #include "ui/gfx/skia_util.h"
15 #include "ui/views/controls/button/custom_button.h"
16 #include "ui/views/layout/box_layout.h"
17
18 namespace app_list {
19
20 namespace {
21
22 const int kPreferredHeight = 57;
23
24 const int kMaxButtonSpacing = 18;
25 const int kMinButtonSpacing = 4;
26 const int kMaxButtonWidth = 68;
27 const int kMinButtonWidth = 28;
28 const int kButtonHeight = 6;
29 const int kButtonCornerRadius = 2;
30 const int kButtonStripPadding = 20;
31
32 class PageSwitcherButton : public views::CustomButton {
33  public:
34   explicit PageSwitcherButton(views::ButtonListener* listener)
35       : views::CustomButton(listener),
36         button_width_(kMaxButtonWidth),
37         selected_range_(0) {
38   }
39   virtual ~PageSwitcherButton() {}
40
41   void SetSelectedRange(double selected_range) {
42     if (selected_range_ == selected_range)
43       return;
44
45     selected_range_ = selected_range;
46     SchedulePaint();
47   }
48
49   void set_button_width(int button_width) { button_width_ = button_width; }
50
51   // Overridden from views::View:
52   virtual gfx::Size GetPreferredSize() OVERRIDE {
53     return gfx::Size(button_width_, kButtonHeight);
54   }
55
56   virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE {
57     if (state() == STATE_HOVERED)
58       PaintButton(canvas, kPagerHoverColor);
59     else
60       PaintButton(canvas, kPagerNormalColor);
61   }
62
63  private:
64   // Paints a button that has two rounded corner at bottom.
65   void PaintButton(gfx::Canvas* canvas, SkColor base_color) {
66     gfx::Rect rect(GetContentsBounds());
67     rect.ClampToCenteredSize(gfx::Size(button_width_, kButtonHeight));
68
69     SkPath path;
70     path.addRoundRect(gfx::RectToSkRect(rect),
71                       SkIntToScalar(kButtonCornerRadius),
72                       SkIntToScalar(kButtonCornerRadius));
73
74     SkPaint paint;
75     paint.setAntiAlias(true);
76     paint.setStyle(SkPaint::kFill_Style);
77     paint.setColor(base_color);
78     canvas->DrawPath(path, paint);
79
80     int selected_start_x = 0;
81     int selected_width = 0;
82     if (selected_range_ > 0) {
83       selected_width = selected_range_ * rect.width();
84     } else if (selected_range_ < 0) {
85       selected_width =  -selected_range_ * rect.width();
86       selected_start_x = rect.right() - selected_width;
87     }
88
89     if (selected_width) {
90       gfx::Rect selected_rect(rect);
91       selected_rect.set_x(selected_start_x);
92       selected_rect.set_width(selected_width);
93
94       SkPath selected_path;
95       selected_path.addRoundRect(gfx::RectToSkRect(selected_rect),
96                                  SkIntToScalar(kButtonCornerRadius),
97                                  SkIntToScalar(kButtonCornerRadius));
98       paint.setColor(kPagerSelectedColor);
99       canvas->DrawPath(selected_path, paint);
100     }
101   }
102
103   int button_width_;
104
105   // [-1, 1] range that represents the portion of the button that should be
106   // painted with kSelectedColor. Positive range starts from left side and
107   // negative range starts from the right side.
108   double selected_range_;
109
110   DISALLOW_COPY_AND_ASSIGN(PageSwitcherButton);
111 };
112
113 // Gets PageSwitcherButton at |index| in |buttons|.
114 PageSwitcherButton* GetButtonByIndex(views::View* buttons, int index) {
115   return static_cast<PageSwitcherButton*>(buttons->child_at(index));
116 }
117
118 }  // namespace
119
120 PageSwitcher::PageSwitcher(PaginationModel* model)
121     : model_(model),
122       buttons_(new views::View) {
123   AddChildView(buttons_);
124
125   TotalPagesChanged();
126   SelectedPageChanged(-1, model->selected_page());
127   model_->AddObserver(this);
128 }
129
130 PageSwitcher::~PageSwitcher() {
131   model_->RemoveObserver(this);
132 }
133
134 int PageSwitcher::GetPageForPoint(const gfx::Point& point) const {
135   if (!buttons_->bounds().Contains(point))
136     return -1;
137
138   gfx::Point buttons_point(point);
139   views::View::ConvertPointToTarget(this, buttons_, &buttons_point);
140
141   for (int i = 0; i < buttons_->child_count(); ++i) {
142     const views::View* button = buttons_->child_at(i);
143     if (button->bounds().Contains(buttons_point))
144       return i;
145   }
146
147   return -1;
148 }
149
150 void PageSwitcher::UpdateUIForDragPoint(const gfx::Point& point) {
151   int page = GetPageForPoint(point);
152
153   const int button_count = buttons_->child_count();
154   if (page >= 0 && page < button_count) {
155     PageSwitcherButton* button =
156         static_cast<PageSwitcherButton*>(buttons_->child_at(page));
157     button->SetState(views::CustomButton::STATE_HOVERED);
158     return;
159   }
160
161   for (int i = 0; i < button_count; ++i) {
162     PageSwitcherButton* button =
163         static_cast<PageSwitcherButton*>(buttons_->child_at(i));
164     button->SetState(views::CustomButton::STATE_NORMAL);
165   }
166 }
167
168 gfx::Size PageSwitcher::GetPreferredSize() {
169   // Always return a size with correct height so that container resize is not
170   // needed when more pages are added.
171   return gfx::Size(buttons_->GetPreferredSize().width(),
172                    kPreferredHeight);
173 }
174
175 void PageSwitcher::Layout() {
176   gfx::Rect rect(GetContentsBounds());
177
178   CalculateButtonWidthAndSpacing(rect.width());
179
180   // Makes |buttons_| horizontally center and vertically fill.
181   gfx::Size buttons_size(buttons_->GetPreferredSize());
182   gfx::Rect buttons_bounds(rect.CenterPoint().x() - buttons_size.width() / 2,
183                            rect.y(),
184                            buttons_size.width(),
185                            rect.height());
186   buttons_->SetBoundsRect(gfx::IntersectRects(rect, buttons_bounds));
187 }
188
189 void PageSwitcher::CalculateButtonWidthAndSpacing(int contents_width) {
190   const int button_count = buttons_->child_count();
191   if (!button_count)
192     return;
193
194   contents_width -= 2 * kButtonStripPadding;
195
196   int button_width = kMinButtonWidth;
197   int button_spacing = kMinButtonSpacing;
198   if (button_count > 1) {
199     button_spacing = (contents_width - button_width * button_count) /
200         (button_count - 1);
201     button_spacing = std::min(kMaxButtonSpacing,
202                               std::max(kMinButtonSpacing, button_spacing));
203   }
204
205   button_width = (contents_width - (button_count - 1) * button_spacing) /
206       button_count;
207   button_width = std::min(kMaxButtonWidth,
208                           std::max(kMinButtonWidth, button_width));
209
210   buttons_->SetLayoutManager(new views::BoxLayout(
211       views::BoxLayout::kHorizontal, kButtonStripPadding, 0, button_spacing));
212   for (int i = 0; i < button_count; ++i) {
213     PageSwitcherButton* button =
214         static_cast<PageSwitcherButton*>(buttons_->child_at(i));
215     button->set_button_width(button_width);
216   }
217 }
218
219 void PageSwitcher::ButtonPressed(views::Button* sender,
220                                  const ui::Event& event) {
221   for (int i = 0; i < buttons_->child_count(); ++i) {
222     if (sender == static_cast<views::Button*>(buttons_->child_at(i))) {
223       model_->SelectPage(i, true /* animate */);
224       break;
225     }
226   }
227 }
228
229 void PageSwitcher::TotalPagesChanged() {
230   buttons_->RemoveAllChildViews(true);
231   for (int i = 0; i < model_->total_pages(); ++i) {
232     PageSwitcherButton* button = new PageSwitcherButton(this);
233     button->SetSelectedRange(i == model_->selected_page() ? 1 : 0);
234     buttons_->AddChildView(button);
235   }
236   buttons_->SetVisible(model_->total_pages() > 1);
237   Layout();
238 }
239
240 void PageSwitcher::SelectedPageChanged(int old_selected, int new_selected) {
241   if (old_selected >= 0 && old_selected < buttons_->child_count())
242     GetButtonByIndex(buttons_, old_selected)->SetSelectedRange(0);
243   if (new_selected >= 0 && new_selected < buttons_->child_count())
244     GetButtonByIndex(buttons_, new_selected)->SetSelectedRange(1);
245 }
246
247 void PageSwitcher::TransitionStarted() {
248 }
249
250 void PageSwitcher::TransitionChanged() {
251   const int current_page = model_->selected_page();
252   const int target_page = model_->transition().target_page;
253
254   double progress = model_->transition().progress;
255   double remaining = progress - 1;
256
257   if (current_page > target_page) {
258     remaining = -remaining;
259     progress = -progress;
260   }
261
262   GetButtonByIndex(buttons_, current_page)->SetSelectedRange(remaining);
263   if (model_->is_valid_page(target_page))
264     GetButtonByIndex(buttons_, target_page)->SetSelectedRange(progress);
265 }
266
267 }  // namespace app_list