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/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"
22 const int kPreferredHeight
= 57;
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;
32 class PageSwitcherButton
: public views::CustomButton
{
34 explicit PageSwitcherButton(views::ButtonListener
* listener
)
35 : views::CustomButton(listener
),
36 button_width_(kMaxButtonWidth
),
39 virtual ~PageSwitcherButton() {}
41 void SetSelectedRange(double selected_range
) {
42 if (selected_range_
== selected_range
)
45 selected_range_
= selected_range
;
49 void set_button_width(int button_width
) { button_width_
= button_width
; }
51 // Overridden from views::View:
52 virtual gfx::Size
GetPreferredSize() OVERRIDE
{
53 return gfx::Size(button_width_
, kButtonHeight
);
56 virtual void OnPaint(gfx::Canvas
* canvas
) OVERRIDE
{
57 if (state() == STATE_HOVERED
)
58 PaintButton(canvas
, kPagerHoverColor
);
60 PaintButton(canvas
, kPagerNormalColor
);
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
));
70 path
.addRoundRect(gfx::RectToSkRect(rect
),
71 SkIntToScalar(kButtonCornerRadius
),
72 SkIntToScalar(kButtonCornerRadius
));
75 paint
.setAntiAlias(true);
76 paint
.setStyle(SkPaint::kFill_Style
);
77 paint
.setColor(base_color
);
78 canvas
->DrawPath(path
, paint
);
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
;
90 gfx::Rect
selected_rect(rect
);
91 selected_rect
.set_x(selected_start_x
);
92 selected_rect
.set_width(selected_width
);
95 selected_path
.addRoundRect(gfx::RectToSkRect(selected_rect
),
96 SkIntToScalar(kButtonCornerRadius
),
97 SkIntToScalar(kButtonCornerRadius
));
98 paint
.setColor(kPagerSelectedColor
);
99 canvas
->DrawPath(selected_path
, paint
);
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_
;
110 DISALLOW_COPY_AND_ASSIGN(PageSwitcherButton
);
113 // Gets PageSwitcherButton at |index| in |buttons|.
114 PageSwitcherButton
* GetButtonByIndex(views::View
* buttons
, int index
) {
115 return static_cast<PageSwitcherButton
*>(buttons
->child_at(index
));
120 PageSwitcher::PageSwitcher(PaginationModel
* model
)
122 buttons_(new views::View
) {
123 AddChildView(buttons_
);
126 SelectedPageChanged(-1, model
->selected_page());
127 model_
->AddObserver(this);
130 PageSwitcher::~PageSwitcher() {
131 model_
->RemoveObserver(this);
134 int PageSwitcher::GetPageForPoint(const gfx::Point
& point
) const {
135 if (!buttons_
->bounds().Contains(point
))
138 gfx::Point
buttons_point(point
);
139 views::View::ConvertPointToTarget(this, buttons_
, &buttons_point
);
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
))
150 void PageSwitcher::UpdateUIForDragPoint(const gfx::Point
& point
) {
151 int page
= GetPageForPoint(point
);
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
);
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
);
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(),
175 void PageSwitcher::Layout() {
176 gfx::Rect
rect(GetContentsBounds());
178 CalculateButtonWidthAndSpacing(rect
.width());
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,
184 buttons_size
.width(),
186 buttons_
->SetBoundsRect(gfx::IntersectRects(rect
, buttons_bounds
));
189 void PageSwitcher::CalculateButtonWidthAndSpacing(int contents_width
) {
190 const int button_count
= buttons_
->child_count();
194 contents_width
-= 2 * kButtonStripPadding
;
196 int button_width
= kMinButtonWidth
;
197 int button_spacing
= kMinButtonSpacing
;
198 if (button_count
> 1) {
199 button_spacing
= (contents_width
- button_width
* button_count
) /
201 button_spacing
= std::min(kMaxButtonSpacing
,
202 std::max(kMinButtonSpacing
, button_spacing
));
205 button_width
= (contents_width
- (button_count
- 1) * button_spacing
) /
207 button_width
= std::min(kMaxButtonWidth
,
208 std::max(kMinButtonWidth
, button_width
));
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
);
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 */);
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
);
236 buttons_
->SetVisible(model_
->total_pages() > 1);
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);
247 void PageSwitcher::TransitionStarted() {
250 void PageSwitcher::TransitionChanged() {
251 const int current_page
= model_
->selected_page();
252 const int target_page
= model_
->transition().target_page
;
254 double progress
= model_
->transition().progress
;
255 double remaining
= progress
- 1;
257 if (current_page
> target_page
) {
258 remaining
= -remaining
;
259 progress
= -progress
;
262 GetButtonByIndex(buttons_
, current_page
)->SetSelectedRange(remaining
);
263 if (model_
->is_valid_page(target_page
))
264 GetButtonByIndex(buttons_
, target_page
)->SetSelectedRange(progress
);
267 } // namespace app_list