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