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