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"
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"
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
{
35 explicit PageSwitcherButton(views::ButtonListener
* listener
)
36 : views::CustomButton(listener
),
37 button_width_(kMaxButtonWidth
),
40 ~PageSwitcherButton() override
{}
42 void SetSelectedRange(double selected_range
) {
43 if (selected_range_
== selected_range
)
46 selected_range_
= selected_range
;
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
);
61 PaintButton(canvas
, kPagerNormalColor
);
65 void OnGestureEvent(ui::GestureEvent
* event
) override
{
66 CustomButton::OnGestureEvent(event
);
68 if (!switches::IsTouchFeedbackEnabled())
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
);
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
));
85 path
.addRoundRect(gfx::RectToSkRect(rect
),
86 SkIntToScalar(kButtonCornerRadius
),
87 SkIntToScalar(kButtonCornerRadius
));
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
);
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
));
135 PageSwitcher::PageSwitcher(PaginationModel
* model
)
137 buttons_(new views::View
) {
138 AddChildView(buttons_
);
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
))
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
))
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
);
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(),
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,
199 buttons_size
.width(),
201 buttons_
->SetBoundsRect(gfx::IntersectRects(rect
, buttons_bounds
));
204 void PageSwitcher::CalculateButtonWidthAndSpacing(int contents_width
) {
205 const int button_count
= buttons_
->child_count();
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
) /
216 button_spacing
= std::min(kMaxButtonSpacing
,
217 std::max(kMinButtonSpacing
, button_spacing
));
220 button_width
= (contents_width
- (button_count
- 1) * button_spacing
) /
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 */);
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);
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