| 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 |