Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / ui / app_list / views / page_switcher.cc
blobdc750f9d8c2aeb4c851d1725088fa592aa941c9e
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.
5 #include "ui/app_list/views/page_switcher.h"
7 #include <algorithm>
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/base/ui_base_switches_util.h"
13 #include "ui/gfx/animation/throb_animation.h"
14 #include "ui/gfx/canvas.h"
15 #include "ui/gfx/skia_util.h"
16 #include "ui/views/controls/button/custom_button.h"
17 #include "ui/views/layout/box_layout.h"
19 namespace app_list {
21 namespace {
23 const int kPreferredHeight = 58;
25 const int kMaxButtonSpacing = 18;
26 const int kMinButtonSpacing = 4;
27 const int kMaxButtonWidth = 68;
28 const int kMinButtonWidth = 28;
29 const int kButtonHeight = 6;
30 const int kButtonCornerRadius = 2;
31 const int kButtonStripPadding = 20;
33 class PageSwitcherButton : public views::CustomButton {
34 public:
35 explicit PageSwitcherButton(views::ButtonListener* listener)
36 : views::CustomButton(listener),
37 button_width_(kMaxButtonWidth),
38 selected_range_(0) {
40 ~PageSwitcherButton() override {}
42 void SetSelectedRange(double selected_range) {
43 if (selected_range_ == selected_range)
44 return;
46 selected_range_ = selected_range;
47 SchedulePaint();
50 void set_button_width(int button_width) { button_width_ = button_width; }
52 // Overridden from views::View:
53 gfx::Size GetPreferredSize() const override {
54 return gfx::Size(button_width_, kButtonHeight);
57 void OnPaint(gfx::Canvas* canvas) override {
58 if (state() == STATE_HOVERED)
59 PaintButton(canvas, kPagerHoverColor);
60 else
61 PaintButton(canvas, kPagerNormalColor);
64 private:
65 void OnGestureEvent(ui::GestureEvent* event) override {
66 CustomButton::OnGestureEvent(event);
68 if (!switches::IsTouchFeedbackEnabled())
69 return;
71 if (event->type() == ui::ET_GESTURE_TAP_DOWN)
72 SetState(views::CustomButton::STATE_HOVERED);
73 else if (event->type() == ui::ET_GESTURE_TAP_CANCEL ||
74 event->type() == ui::ET_GESTURE_TAP)
75 SetState(views::CustomButton::STATE_NORMAL);
76 SchedulePaint();
79 // Paints a button that has two rounded corner at bottom.
80 void PaintButton(gfx::Canvas* canvas, SkColor base_color) {
81 gfx::Rect rect(GetContentsBounds());
82 rect.ClampToCenteredSize(gfx::Size(button_width_, kButtonHeight));
84 SkPath path;
85 path.addRoundRect(gfx::RectToSkRect(rect),
86 SkIntToScalar(kButtonCornerRadius),
87 SkIntToScalar(kButtonCornerRadius));
89 SkPaint paint;
90 paint.setAntiAlias(true);
91 paint.setStyle(SkPaint::kFill_Style);
92 paint.setColor(base_color);
93 canvas->DrawPath(path, paint);
95 int selected_start_x = 0;
96 int selected_width = 0;
97 if (selected_range_ > 0) {
98 selected_width = selected_range_ * rect.width();
99 } else if (selected_range_ < 0) {
100 selected_width = -selected_range_ * rect.width();
101 selected_start_x = rect.right() - selected_width;
104 if (selected_width) {
105 gfx::Rect selected_rect(rect);
106 selected_rect.set_x(selected_start_x);
107 selected_rect.set_width(selected_width);
109 SkPath selected_path;
110 selected_path.addRoundRect(gfx::RectToSkRect(selected_rect),
111 SkIntToScalar(kButtonCornerRadius),
112 SkIntToScalar(kButtonCornerRadius));
113 paint.setColor(kPagerSelectedColor);
114 canvas->DrawPath(selected_path, paint);
118 int button_width_;
120 // [-1, 1] range that represents the portion of the button that should be
121 // painted with kSelectedColor. Positive range starts from left side and
122 // negative range starts from the right side.
123 double selected_range_;
125 DISALLOW_COPY_AND_ASSIGN(PageSwitcherButton);
128 // Gets PageSwitcherButton at |index| in |buttons|.
129 PageSwitcherButton* GetButtonByIndex(views::View* buttons, int index) {
130 return static_cast<PageSwitcherButton*>(buttons->child_at(index));
133 } // namespace
135 PageSwitcher::PageSwitcher(PaginationModel* model)
136 : model_(model),
137 buttons_(new views::View) {
138 AddChildView(buttons_);
140 TotalPagesChanged();
141 SelectedPageChanged(-1, model->selected_page());
142 model_->AddObserver(this);
145 PageSwitcher::~PageSwitcher() {
146 model_->RemoveObserver(this);
149 int PageSwitcher::GetPageForPoint(const gfx::Point& point) const {
150 if (!buttons_->bounds().Contains(point))
151 return -1;
153 gfx::Point buttons_point(point);
154 views::View::ConvertPointToTarget(this, buttons_, &buttons_point);
156 for (int i = 0; i < buttons_->child_count(); ++i) {
157 const views::View* button = buttons_->child_at(i);
158 if (button->bounds().Contains(buttons_point))
159 return i;
162 return -1;
165 void PageSwitcher::UpdateUIForDragPoint(const gfx::Point& point) {
166 int page = GetPageForPoint(point);
168 const int button_count = buttons_->child_count();
169 if (page >= 0 && page < button_count) {
170 PageSwitcherButton* button =
171 static_cast<PageSwitcherButton*>(buttons_->child_at(page));
172 button->SetState(views::CustomButton::STATE_HOVERED);
173 return;
176 for (int i = 0; i < button_count; ++i) {
177 PageSwitcherButton* button =
178 static_cast<PageSwitcherButton*>(buttons_->child_at(i));
179 button->SetState(views::CustomButton::STATE_NORMAL);
183 gfx::Size PageSwitcher::GetPreferredSize() const {
184 // Always return a size with correct height so that container resize is not
185 // needed when more pages are added.
186 return gfx::Size(buttons_->GetPreferredSize().width(),
187 kPreferredHeight);
190 void PageSwitcher::Layout() {
191 gfx::Rect rect(GetContentsBounds());
193 CalculateButtonWidthAndSpacing(rect.width());
195 // Makes |buttons_| horizontally center and vertically fill.
196 gfx::Size buttons_size(buttons_->GetPreferredSize());
197 gfx::Rect buttons_bounds(rect.CenterPoint().x() - buttons_size.width() / 2,
198 rect.y(),
199 buttons_size.width(),
200 rect.height());
201 buttons_->SetBoundsRect(gfx::IntersectRects(rect, buttons_bounds));
204 void PageSwitcher::CalculateButtonWidthAndSpacing(int contents_width) {
205 const int button_count = buttons_->child_count();
206 if (!button_count)
207 return;
209 contents_width -= 2 * kButtonStripPadding;
211 int button_width = kMinButtonWidth;
212 int button_spacing = kMinButtonSpacing;
213 if (button_count > 1) {
214 button_spacing = (contents_width - button_width * button_count) /
215 (button_count - 1);
216 button_spacing = std::min(kMaxButtonSpacing,
217 std::max(kMinButtonSpacing, button_spacing));
220 button_width = (contents_width - (button_count - 1) * button_spacing) /
221 button_count;
222 button_width = std::min(kMaxButtonWidth,
223 std::max(kMinButtonWidth, button_width));
225 buttons_->SetLayoutManager(new views::BoxLayout(
226 views::BoxLayout::kHorizontal, kButtonStripPadding, 0, button_spacing));
227 for (int i = 0; i < button_count; ++i) {
228 PageSwitcherButton* button =
229 static_cast<PageSwitcherButton*>(buttons_->child_at(i));
230 button->set_button_width(button_width);
234 void PageSwitcher::ButtonPressed(views::Button* sender,
235 const ui::Event& event) {
236 for (int i = 0; i < buttons_->child_count(); ++i) {
237 if (sender == static_cast<views::Button*>(buttons_->child_at(i))) {
238 model_->SelectPage(i, true /* animate */);
239 break;
244 void PageSwitcher::TotalPagesChanged() {
245 buttons_->RemoveAllChildViews(true);
246 for (int i = 0; i < model_->total_pages(); ++i) {
247 PageSwitcherButton* button = new PageSwitcherButton(this);
248 button->SetSelectedRange(i == model_->selected_page() ? 1 : 0);
249 buttons_->AddChildView(button);
251 buttons_->SetVisible(model_->total_pages() > 1);
252 Layout();
255 void PageSwitcher::SelectedPageChanged(int old_selected, int new_selected) {
256 if (old_selected >= 0 && old_selected < buttons_->child_count())
257 GetButtonByIndex(buttons_, old_selected)->SetSelectedRange(0);
258 if (new_selected >= 0 && new_selected < buttons_->child_count())
259 GetButtonByIndex(buttons_, new_selected)->SetSelectedRange(1);
262 void PageSwitcher::TransitionStarted() {
265 void PageSwitcher::TransitionChanged() {
266 const int current_page = model_->selected_page();
267 const int target_page = model_->transition().target_page;
269 double progress = model_->transition().progress;
270 double remaining = progress - 1;
272 if (current_page > target_page) {
273 remaining = -remaining;
274 progress = -progress;
277 GetButtonByIndex(buttons_, current_page)->SetSelectedRange(remaining);
278 if (model_->is_valid_page(target_page))
279 GetButtonByIndex(buttons_, target_page)->SetSelectedRange(progress);
282 } // namespace app_list