| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "ui/app_list/views/page_switcher_vertical.h" | 5 #include "ui/app_list/views/page_switcher_vertical.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 | 8 |
| 9 #include "base/macros.h" | 9 #include "base/macros.h" |
| 10 #include "third_party/skia/include/core/SkPath.h" | 10 #include "third_party/skia/include/core/SkPath.h" |
| 11 #include "ui/app_list/app_list_constants.h" | 11 #include "ui/app_list/app_list_constants.h" |
| 12 #include "ui/app_list/pagination_model.h" | 12 #include "ui/app_list/pagination_model.h" |
| 13 #include "ui/gfx/animation/throb_animation.h" | 13 #include "ui/gfx/animation/throb_animation.h" |
| 14 #include "ui/gfx/canvas.h" | 14 #include "ui/gfx/canvas.h" |
| 15 #include "ui/gfx/geometry/insets.h" | 15 #include "ui/gfx/geometry/insets.h" |
| 16 #include "ui/gfx/skia_util.h" | 16 #include "ui/gfx/skia_util.h" |
| 17 #include "ui/views/animation/flood_fill_ink_drop_ripple.h" |
| 18 #include "ui/views/animation/ink_drop_highlight.h" |
| 19 #include "ui/views/animation/ink_drop_impl.h" |
| 20 #include "ui/views/animation/ink_drop_mask.h" |
| 21 #include "ui/views/animation/ink_drop_painted_layer_delegates.h" |
| 17 #include "ui/views/controls/button/custom_button.h" | 22 #include "ui/views/controls/button/custom_button.h" |
| 18 #include "ui/views/layout/box_layout.h" | 23 #include "ui/views/layout/box_layout.h" |
| 19 | 24 |
| 20 namespace app_list { | 25 namespace app_list { |
| 21 | 26 |
| 22 namespace { | 27 namespace { |
| 23 | 28 |
| 24 const int kPreferredWidth = 58; | 29 constexpr int kNormalButtonRadius = 3; |
| 30 constexpr int kSelectedButtonRadius = 4; |
| 31 constexpr int kInkDropRadius = 8; |
| 32 // The padding on top/bottom side of each button. |
| 33 constexpr int kButtonPadding = 12; |
| 34 // The padding of a list of buttons as a button strip. |
| 35 constexpr int kButtonStripPadding = 24; |
| 36 constexpr int kMaxButtonRadius = 8; |
| 37 constexpr int kPreferredButtonStripWidth = |
| 38 kMaxButtonRadius * 2 + kButtonStripPadding * 2; |
| 25 | 39 |
| 26 const int kMaxButtonSpacing = 18; | 40 // The selected button color. |
| 27 const int kMinButtonSpacing = 4; | 41 constexpr SkColor kSelectedButtonColor = SK_ColorWHITE; |
| 28 const int kMaxButtonHeight = 68; | 42 // The normal button color (54% white). |
| 29 const int kMinButtonHeight = 28; | 43 constexpr SkColor kNormalColor = SkColorSetA(SK_ColorWHITE, 138); |
| 30 const int kButtonWidth = 6; | 44 constexpr SkColor kInkDropBaseColor = SK_ColorWHITE; |
| 31 const int kButtonCornerRadius = 2; | 45 constexpr SkColor kInkDropRippleColor = SkColorSetA(kInkDropBaseColor, 20); |
| 32 const int kButtonStripPadding = 20; | 46 constexpr SkColor kInkDropHighlightColor = SkColorSetA(kInkDropBaseColor, 15); |
| 47 |
| 48 constexpr SkScalar kStrokeWidth = SkIntToScalar(1); |
| 33 | 49 |
| 34 class PageSwitcherButton : public views::CustomButton { | 50 class PageSwitcherButton : public views::CustomButton { |
| 35 public: | 51 public: |
| 36 explicit PageSwitcherButton(views::ButtonListener* listener) | 52 explicit PageSwitcherButton(views::ButtonListener* listener) |
| 37 : views::CustomButton(listener), | 53 : views::CustomButton(listener) { |
| 38 button_height_(kMaxButtonHeight), | 54 SetInkDropMode(InkDropMode::ON); |
| 39 selected_range_(0) {} | 55 } |
| 56 |
| 40 ~PageSwitcherButton() override {} | 57 ~PageSwitcherButton() override {} |
| 41 | 58 |
| 42 void SetSelectedRange(double selected_range) { | 59 void SetSelected(bool selected) { |
| 43 if (selected_range_ == selected_range) | 60 if (selected == selected_) |
| 44 return; | 61 return; |
| 45 | 62 |
| 46 selected_range_ = selected_range; | 63 selected_ = selected; |
| 47 SchedulePaint(); | 64 SchedulePaint(); |
| 48 } | 65 } |
| 49 | 66 |
| 50 void set_button_height(int button_height) { button_height_ = button_height; } | |
| 51 | |
| 52 // Overridden from views::View: | 67 // Overridden from views::View: |
| 53 gfx::Size CalculatePreferredSize() const override { | 68 gfx::Size CalculatePreferredSize() const override { |
| 54 return gfx::Size(kButtonWidth, button_height_); | 69 return gfx::Size(kMaxButtonRadius * 2, kMaxButtonRadius * 2); |
| 55 } | 70 } |
| 56 | 71 |
| 57 void PaintButtonContents(gfx::Canvas* canvas) override { | 72 void PaintButtonContents(gfx::Canvas* canvas) override { |
| 58 if (state() == STATE_HOVERED) | 73 PaintButton(canvas, BuildPaintButtonInfo()); |
| 59 PaintButton(canvas, kPagerHoverColor); | |
| 60 else | |
| 61 PaintButton(canvas, kPagerNormalColor); | |
| 62 } | 74 } |
| 63 | 75 |
| 64 void OnGestureEvent(ui::GestureEvent* event) override { | 76 protected: |
| 65 CustomButton::OnGestureEvent(event); | 77 bool IsTriggerableEvent(const ui::Event& event) override { |
| 78 return event.IsMouseEvent() && |
| 79 (triggerable_event_flags() & event.flags()) != 0; |
| 80 } |
| 66 | 81 |
| 67 if (event->type() == ui::ET_GESTURE_TAP_DOWN) | 82 std::unique_ptr<views::InkDrop> CreateInkDrop() override { |
| 68 SetState(views::CustomButton::STATE_HOVERED); | 83 std::unique_ptr<views::InkDropImpl> ink_drop = |
| 69 else if (event->type() == ui::ET_GESTURE_TAP_CANCEL || | 84 CustomButton::CreateDefaultInkDropImpl(); |
| 70 event->type() == ui::ET_GESTURE_TAP) | 85 ink_drop->SetShowHighlightOnHover(false); |
| 71 SetState(views::CustomButton::STATE_NORMAL); | 86 ink_drop->SetAutoHighlightMode( |
| 72 SchedulePaint(); | 87 views::InkDropImpl::AutoHighlightMode::SHOW_ON_RIPPLE); |
| 88 return std::move(ink_drop); |
| 89 } |
| 90 |
| 91 std::unique_ptr<views::InkDropMask> CreateInkDropMask() const override { |
| 92 return base::MakeUnique<views::CircleInkDropMask>( |
| 93 size(), GetLocalBounds().CenterPoint(), kMaxButtonRadius); |
| 94 } |
| 95 |
| 96 std::unique_ptr<views::InkDropRipple> CreateInkDropRipple() const override { |
| 97 gfx::Point center = GetLocalBounds().CenterPoint(); |
| 98 gfx::Rect bounds(center.x() - kMaxButtonRadius, |
| 99 center.y() - kMaxButtonRadius, 2 * kMaxButtonRadius, |
| 100 2 * kMaxButtonRadius); |
| 101 return base::MakeUnique<views::FloodFillInkDropRipple>( |
| 102 size(), GetLocalBounds().InsetsFrom(bounds), |
| 103 GetInkDropCenterBasedOnLastEvent(), kInkDropRippleColor, 1.0f); |
| 104 } |
| 105 |
| 106 std::unique_ptr<views::InkDropHighlight> CreateInkDropHighlight() |
| 107 const override { |
| 108 return base::MakeUnique<views::InkDropHighlight>( |
| 109 gfx::PointF(GetLocalBounds().CenterPoint()), |
| 110 base::MakeUnique<views::CircleLayerDelegate>(kInkDropHighlightColor, |
| 111 kInkDropRadius)); |
| 112 } |
| 113 |
| 114 void NotifyClick(const ui::Event& event) override { |
| 115 CustomButton::NotifyClick(event); |
| 116 GetInkDrop()->AnimateToState(views::InkDropState::ACTION_TRIGGERED); |
| 73 } | 117 } |
| 74 | 118 |
| 75 private: | 119 private: |
| 76 // Paints a button that has two rounded corner at bottom. | 120 // Stores the information of how to paint the button. |
| 77 void PaintButton(gfx::Canvas* canvas, SkColor base_color) { | 121 struct PaintButtonInfo { |
| 122 SkColor color; |
| 123 cc::PaintFlags::Style style; |
| 124 SkScalar radius; |
| 125 SkScalar stroke_width; |
| 126 }; |
| 127 |
| 128 // Returns the information of how to paint selected/normal button. |
| 129 PaintButtonInfo BuildPaintButtonInfo() { |
| 130 PaintButtonInfo info; |
| 131 if (selected_) { |
| 132 info.color = kSelectedButtonColor; |
| 133 info.style = cc::PaintFlags::kFill_Style; |
| 134 info.radius = SkIntToScalar(kSelectedButtonRadius); |
| 135 info.stroke_width = SkIntToScalar(0); |
| 136 } else { |
| 137 info.color = kNormalColor; |
| 138 info.style = cc::PaintFlags::kStroke_Style; |
| 139 info.radius = SkIntToScalar(kNormalButtonRadius); |
| 140 info.stroke_width = kStrokeWidth; |
| 141 } |
| 142 return info; |
| 143 } |
| 144 |
| 145 // Paints a button based on the |info|. |
| 146 void PaintButton(gfx::Canvas* canvas, PaintButtonInfo info) { |
| 78 gfx::Rect rect(GetContentsBounds()); | 147 gfx::Rect rect(GetContentsBounds()); |
| 79 rect.ClampToCenteredSize(gfx::Size(kButtonWidth, button_height_)); | |
| 80 | |
| 81 SkPath path; | 148 SkPath path; |
| 82 path.addRoundRect(gfx::RectToSkRect(rect), | 149 path.addCircle(rect.CenterPoint().x(), rect.CenterPoint().y(), info.radius); |
| 83 SkIntToScalar(kButtonCornerRadius), | |
| 84 SkIntToScalar(kButtonCornerRadius)); | |
| 85 | 150 |
| 86 cc::PaintFlags flags; | 151 cc::PaintFlags flags; |
| 87 flags.setAntiAlias(true); | 152 flags.setAntiAlias(true); |
| 88 flags.setStyle(cc::PaintFlags::kFill_Style); | 153 flags.setStyle(info.style); |
| 89 flags.setColor(base_color); | 154 flags.setColor(info.color); |
| 90 canvas->DrawPath(path, flags); | 155 canvas->DrawPath(path, flags); |
| 91 | |
| 92 int selected_start_y = 0; | |
| 93 int selected_height = 0; | |
| 94 if (selected_range_ > 0) { | |
| 95 selected_height = selected_range_ * rect.height(); | |
| 96 } else if (selected_range_ < 0) { | |
| 97 selected_height = -selected_range_ * rect.height(); | |
| 98 selected_start_y = rect.bottom() - selected_height; | |
| 99 } | |
| 100 | |
| 101 if (selected_height) { | |
| 102 gfx::Rect selected_rect(rect); | |
| 103 selected_rect.set_y(selected_start_y); | |
| 104 selected_rect.set_height(selected_height); | |
| 105 | |
| 106 SkPath selected_path; | |
| 107 selected_path.addRoundRect(gfx::RectToSkRect(selected_rect), | |
| 108 SkIntToScalar(kButtonCornerRadius), | |
| 109 SkIntToScalar(kButtonCornerRadius)); | |
| 110 flags.setColor(kPagerSelectedColor); | |
| 111 canvas->DrawPath(selected_path, flags); | |
| 112 } | |
| 113 } | 156 } |
| 114 | 157 |
| 115 int button_height_; | 158 // If this button is selected, set to true. By default, set to false; |
| 116 | 159 bool selected_ = false; |
| 117 // [-1, 1] range that represents the portion of the button that should be | |
| 118 // painted with kSelectedColor. Positive range starts from top side and | |
| 119 // negative range starts from the bottom side. | |
| 120 double selected_range_; | |
| 121 | 160 |
| 122 DISALLOW_COPY_AND_ASSIGN(PageSwitcherButton); | 161 DISALLOW_COPY_AND_ASSIGN(PageSwitcherButton); |
| 123 }; | 162 }; |
| 124 | 163 |
| 125 // Gets PageSwitcherButton at |index| in |buttons|. | 164 // Gets PageSwitcherButton at |index| in |buttons|. |
| 126 PageSwitcherButton* GetButtonByIndex(views::View* buttons, int index) { | 165 PageSwitcherButton* GetButtonByIndex(views::View* buttons, int index) { |
| 127 return static_cast<PageSwitcherButton*>(buttons->child_at(index)); | 166 return static_cast<PageSwitcherButton*>(buttons->child_at(index)); |
| 128 } | 167 } |
| 129 | 168 |
| 130 } // namespace | 169 } // namespace |
| 131 | 170 |
| 132 PageSwitcherVertical::PageSwitcherVertical(PaginationModel* model) | 171 PageSwitcherVertical::PageSwitcherVertical(PaginationModel* model) |
| 133 : model_(model), buttons_(new views::View) { | 172 : model_(model), buttons_(new views::View) { |
| 173 buttons_->SetLayoutManager(new views::BoxLayout( |
| 174 views::BoxLayout::kVertical, |
| 175 gfx::Insets(kButtonStripPadding, kButtonStripPadding), kButtonPadding)); |
| 176 |
| 134 AddChildView(buttons_); | 177 AddChildView(buttons_); |
| 135 | 178 |
| 136 TotalPagesChanged(); | 179 TotalPagesChanged(); |
| 137 SelectedPageChanged(-1, model->selected_page()); | 180 SelectedPageChanged(-1, model->selected_page()); |
| 138 model_->AddObserver(this); | 181 model_->AddObserver(this); |
| 139 } | 182 } |
| 140 | 183 |
| 141 PageSwitcherVertical::~PageSwitcherVertical() { | 184 PageSwitcherVertical::~PageSwitcherVertical() { |
| 142 model_->RemoveObserver(this); | 185 model_->RemoveObserver(this); |
| 143 } | 186 } |
| (...skipping 28 matching lines...) Expand all Loading... |
| 172 for (int i = 0; i < button_count; ++i) { | 215 for (int i = 0; i < button_count; ++i) { |
| 173 PageSwitcherButton* button = | 216 PageSwitcherButton* button = |
| 174 static_cast<PageSwitcherButton*>(buttons_->child_at(i)); | 217 static_cast<PageSwitcherButton*>(buttons_->child_at(i)); |
| 175 button->SetState(views::CustomButton::STATE_NORMAL); | 218 button->SetState(views::CustomButton::STATE_NORMAL); |
| 176 } | 219 } |
| 177 } | 220 } |
| 178 | 221 |
| 179 gfx::Size PageSwitcherVertical::CalculatePreferredSize() const { | 222 gfx::Size PageSwitcherVertical::CalculatePreferredSize() const { |
| 180 // Always return a size with correct width so that container resize is not | 223 // Always return a size with correct width so that container resize is not |
| 181 // needed when more pages are added. | 224 // needed when more pages are added. |
| 182 return gfx::Size(kPreferredWidth, buttons_->GetPreferredSize().height()); | 225 return gfx::Size(kPreferredButtonStripWidth, |
| 226 buttons_->GetPreferredSize().height()); |
| 183 } | 227 } |
| 184 | 228 |
| 185 void PageSwitcherVertical::Layout() { | 229 void PageSwitcherVertical::Layout() { |
| 186 gfx::Rect rect(GetContentsBounds()); | 230 gfx::Rect rect(GetContentsBounds()); |
| 187 | 231 |
| 188 CalculateButtonHeightAndSpacing(rect.height()); | |
| 189 | |
| 190 // Makes |buttons_| vertically center and horizontally fill. | 232 // Makes |buttons_| vertically center and horizontally fill. |
| 191 gfx::Size buttons_size(buttons_->GetPreferredSize()); | 233 gfx::Size buttons_size(buttons_->GetPreferredSize()); |
| 192 gfx::Rect buttons_bounds(rect.x(), | 234 gfx::Rect buttons_bounds(rect.x(), |
| 193 rect.CenterPoint().y() - buttons_size.height() / 2, | 235 rect.CenterPoint().y() - buttons_size.height() / 2, |
| 194 rect.width(), buttons_size.height()); | 236 rect.width(), buttons_size.height()); |
| 195 buttons_->SetBoundsRect(gfx::IntersectRects(rect, buttons_bounds)); | 237 buttons_->SetBoundsRect(gfx::IntersectRects(rect, buttons_bounds)); |
| 196 } | 238 } |
| 197 | 239 |
| 198 void PageSwitcherVertical::CalculateButtonHeightAndSpacing( | |
| 199 int contents_height) { | |
| 200 const int button_count = buttons_->child_count(); | |
| 201 if (!button_count) | |
| 202 return; | |
| 203 | |
| 204 contents_height -= 2 * kButtonStripPadding; | |
| 205 | |
| 206 int button_height = kMinButtonHeight; | |
| 207 int button_spacing = kMinButtonSpacing; | |
| 208 if (button_count > 1) { | |
| 209 button_spacing = | |
| 210 (contents_height - button_height * button_count) / (button_count - 1); | |
| 211 button_spacing = std::min(kMaxButtonSpacing, | |
| 212 std::max(kMinButtonSpacing, button_spacing)); | |
| 213 } | |
| 214 | |
| 215 button_height = | |
| 216 (contents_height - (button_count - 1) * button_spacing) / button_count; | |
| 217 button_height = | |
| 218 std::min(kMaxButtonHeight, std::max(kMinButtonHeight, button_height)); | |
| 219 | |
| 220 buttons_->SetLayoutManager(new views::BoxLayout( | |
| 221 views::BoxLayout::kVertical, gfx::Insets(kButtonStripPadding, 0), | |
| 222 button_spacing)); | |
| 223 for (int i = 0; i < button_count; ++i) { | |
| 224 PageSwitcherButton* button = | |
| 225 static_cast<PageSwitcherButton*>(buttons_->child_at(i)); | |
| 226 button->set_button_height(button_height); | |
| 227 } | |
| 228 } | |
| 229 | |
| 230 void PageSwitcherVertical::ButtonPressed(views::Button* sender, | 240 void PageSwitcherVertical::ButtonPressed(views::Button* sender, |
| 231 const ui::Event& event) { | 241 const ui::Event& event) { |
| 232 for (int i = 0; i < buttons_->child_count(); ++i) { | 242 for (int i = 0; i < buttons_->child_count(); ++i) { |
| 233 if (sender == static_cast<views::Button*>(buttons_->child_at(i))) { | 243 if (sender == static_cast<views::Button*>(buttons_->child_at(i))) { |
| 234 model_->SelectPage(i, true /* animate */); | 244 model_->SelectPage(i, true /* animate */); |
| 235 break; | 245 break; |
| 236 } | 246 } |
| 237 } | 247 } |
| 238 } | 248 } |
| 239 | 249 |
| 240 void PageSwitcherVertical::TotalPagesChanged() { | 250 void PageSwitcherVertical::TotalPagesChanged() { |
| 241 buttons_->RemoveAllChildViews(true); | 251 buttons_->RemoveAllChildViews(true); |
| 242 for (int i = 0; i < model_->total_pages(); ++i) { | 252 for (int i = 0; i < model_->total_pages(); ++i) { |
| 243 PageSwitcherButton* button = new PageSwitcherButton(this); | 253 PageSwitcherButton* button = new PageSwitcherButton(this); |
| 244 button->SetSelectedRange(i == model_->selected_page() ? 1 : 0); | 254 button->SetSelected(i == model_->selected_page() ? true : false); |
| 245 buttons_->AddChildView(button); | 255 buttons_->AddChildView(button); |
| 246 } | 256 } |
| 247 buttons_->SetVisible(model_->total_pages() > 1); | 257 buttons_->SetVisible(model_->total_pages() > 1); |
| 248 Layout(); | 258 Layout(); |
| 249 } | 259 } |
| 250 | 260 |
| 251 void PageSwitcherVertical::SelectedPageChanged(int old_selected, | 261 void PageSwitcherVertical::SelectedPageChanged(int old_selected, |
| 252 int new_selected) { | 262 int new_selected) { |
| 253 if (old_selected >= 0 && old_selected < buttons_->child_count()) | 263 if (old_selected >= 0 && old_selected < buttons_->child_count()) |
| 254 GetButtonByIndex(buttons_, old_selected)->SetSelectedRange(0); | 264 GetButtonByIndex(buttons_, old_selected)->SetSelected(false); |
| 255 if (new_selected >= 0 && new_selected < buttons_->child_count()) | 265 if (new_selected >= 0 && new_selected < buttons_->child_count()) |
| 256 GetButtonByIndex(buttons_, new_selected)->SetSelectedRange(1); | 266 GetButtonByIndex(buttons_, new_selected)->SetSelected(true); |
| 257 } | 267 } |
| 258 | 268 |
| 259 void PageSwitcherVertical::TransitionStarted() {} | 269 void PageSwitcherVertical::TransitionStarted() {} |
| 260 | 270 |
| 261 void PageSwitcherVertical::TransitionChanged() { | 271 void PageSwitcherVertical::TransitionChanged() {} |
| 262 const int current_page = model_->selected_page(); | |
| 263 const int target_page = model_->transition().target_page; | |
| 264 | |
| 265 double progress = model_->transition().progress; | |
| 266 double remaining = progress - 1; | |
| 267 | |
| 268 if (current_page > target_page) { | |
| 269 remaining = -remaining; | |
| 270 progress = -progress; | |
| 271 } | |
| 272 | |
| 273 GetButtonByIndex(buttons_, current_page)->SetSelectedRange(remaining); | |
| 274 if (model_->is_valid_page(target_page)) | |
| 275 GetButtonByIndex(buttons_, target_page)->SetSelectedRange(progress); | |
| 276 } | |
| 277 | 272 |
| 278 } // namespace app_list | 273 } // namespace app_list |
| OLD | NEW |