Chromium Code Reviews| 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), selected_(false) { |
| 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() && | |
|
xiyuan
2017/06/14 18:21:49
We stripped out gesture events here. Would this af
weidongg
2017/06/14 18:40:13
Yes, the page switch button is un-usable for tap a
| |
| 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 std::unique_ptr<views::BasePaintedLayerDelegate>( | |
| 111 new views::CircleLayerDelegate(kInkDropHighlightColor, | |
|
xiyuan
2017/06/14 18:21:50
nit: base::MakeUnique() ?
weidongg
2017/06/14 18:40:12
Done.
| |
| 112 kInkDropRadius))); | |
| 113 } | |
| 114 | |
| 115 void NotifyClick(const ui::Event& event) override { | |
| 116 CustomButton::NotifyClick(event); | |
| 117 GetInkDrop()->AnimateToState(views::InkDropState::ACTION_TRIGGERED); | |
| 73 } | 118 } |
| 74 | 119 |
| 75 private: | 120 private: |
| 76 // Paints a button that has two rounded corner at bottom. | 121 // Stores the information of how to paint the button. |
| 77 void PaintButton(gfx::Canvas* canvas, SkColor base_color) { | 122 struct PaintButtonInfo { |
| 123 SkColor color; | |
| 124 cc::PaintFlags::Style style; | |
| 125 SkScalar radius; | |
| 126 SkScalar stroke_width; | |
| 127 }; | |
| 128 | |
| 129 // Returns the information of how to paint selected/normal button. | |
| 130 PaintButtonInfo BuildPaintButtonInfo() { | |
| 131 PaintButtonInfo info; | |
| 132 if (selected_) { | |
| 133 info.color = kSelectedButtonColor; | |
| 134 info.style = cc::PaintFlags::kFill_Style; | |
| 135 info.radius = SkIntToScalar(kSelectedButtonRadius); | |
| 136 info.stroke_width = SkIntToScalar(0); | |
| 137 } else { | |
| 138 info.color = kNormalColor; | |
| 139 info.style = cc::PaintFlags::kStroke_Style; | |
| 140 info.radius = SkIntToScalar(kNormalButtonRadius); | |
| 141 info.stroke_width = kStrokeWidth; | |
| 142 } | |
| 143 return info; | |
| 144 } | |
| 145 | |
| 146 // Paints a button based on the |info|. | |
| 147 void PaintButton(gfx::Canvas* canvas, PaintButtonInfo info) { | |
| 78 gfx::Rect rect(GetContentsBounds()); | 148 gfx::Rect rect(GetContentsBounds()); |
| 79 rect.ClampToCenteredSize(gfx::Size(kButtonWidth, button_height_)); | |
| 80 | |
| 81 SkPath path; | 149 SkPath path; |
| 82 path.addRoundRect(gfx::RectToSkRect(rect), | 150 path.addCircle(rect.CenterPoint().x(), rect.CenterPoint().y(), info.radius); |
| 83 SkIntToScalar(kButtonCornerRadius), | |
| 84 SkIntToScalar(kButtonCornerRadius)); | |
| 85 | 151 |
| 86 cc::PaintFlags flags; | 152 cc::PaintFlags flags; |
| 87 flags.setAntiAlias(true); | 153 flags.setAntiAlias(true); |
| 88 flags.setStyle(cc::PaintFlags::kFill_Style); | 154 flags.setStyle(info.style); |
| 89 flags.setColor(base_color); | 155 flags.setColor(info.color); |
| 90 canvas->DrawPath(path, flags); | 156 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 } | 157 } |
| 114 | 158 |
| 115 int button_height_; | 159 // If this button is selected, set to true. |
| 116 | 160 bool selected_; |
|
xiyuan
2017/06/14 18:21:50
nit: initalize member in-place
i.e.
bool selec
weidongg
2017/06/14 18:40:13
Done.
| |
| 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 | 161 |
| 122 DISALLOW_COPY_AND_ASSIGN(PageSwitcherButton); | 162 DISALLOW_COPY_AND_ASSIGN(PageSwitcherButton); |
| 123 }; | 163 }; |
| 124 | 164 |
| 125 // Gets PageSwitcherButton at |index| in |buttons|. | 165 // Gets PageSwitcherButton at |index| in |buttons|. |
| 126 PageSwitcherButton* GetButtonByIndex(views::View* buttons, int index) { | 166 PageSwitcherButton* GetButtonByIndex(views::View* buttons, int index) { |
| 127 return static_cast<PageSwitcherButton*>(buttons->child_at(index)); | 167 return static_cast<PageSwitcherButton*>(buttons->child_at(index)); |
| 128 } | 168 } |
| 129 | 169 |
| 130 } // namespace | 170 } // namespace |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 172 for (int i = 0; i < button_count; ++i) { | 212 for (int i = 0; i < button_count; ++i) { |
| 173 PageSwitcherButton* button = | 213 PageSwitcherButton* button = |
| 174 static_cast<PageSwitcherButton*>(buttons_->child_at(i)); | 214 static_cast<PageSwitcherButton*>(buttons_->child_at(i)); |
| 175 button->SetState(views::CustomButton::STATE_NORMAL); | 215 button->SetState(views::CustomButton::STATE_NORMAL); |
| 176 } | 216 } |
| 177 } | 217 } |
| 178 | 218 |
| 179 gfx::Size PageSwitcherVertical::CalculatePreferredSize() const { | 219 gfx::Size PageSwitcherVertical::CalculatePreferredSize() const { |
| 180 // Always return a size with correct width so that container resize is not | 220 // Always return a size with correct width so that container resize is not |
| 181 // needed when more pages are added. | 221 // needed when more pages are added. |
| 182 return gfx::Size(kPreferredWidth, buttons_->GetPreferredSize().height()); | 222 return gfx::Size(kPreferredButtonStripWidth, |
| 223 buttons_->GetPreferredSize().height()); | |
| 183 } | 224 } |
| 184 | 225 |
| 185 void PageSwitcherVertical::Layout() { | 226 void PageSwitcherVertical::Layout() { |
| 186 gfx::Rect rect(GetContentsBounds()); | 227 gfx::Rect rect(GetContentsBounds()); |
| 187 | 228 |
| 188 CalculateButtonHeightAndSpacing(rect.height()); | 229 buttons_->SetLayoutManager(new views::BoxLayout( |
|
xiyuan
2017/06/14 18:21:49
Move this to where |buttons_| is created. Resettin
weidongg
2017/06/14 18:40:13
Good to know, thanks. Done.
| |
| 230 views::BoxLayout::kVertical, | |
| 231 gfx::Insets(kButtonStripPadding, kButtonStripPadding), kButtonPadding)); | |
| 189 | 232 |
| 190 // Makes |buttons_| vertically center and horizontally fill. | 233 // Makes |buttons_| vertically center and horizontally fill. |
| 191 gfx::Size buttons_size(buttons_->GetPreferredSize()); | 234 gfx::Size buttons_size(buttons_->GetPreferredSize()); |
| 192 gfx::Rect buttons_bounds(rect.x(), | 235 gfx::Rect buttons_bounds(rect.x(), |
| 193 rect.CenterPoint().y() - buttons_size.height() / 2, | 236 rect.CenterPoint().y() - buttons_size.height() / 2, |
| 194 rect.width(), buttons_size.height()); | 237 rect.width(), buttons_size.height()); |
| 195 buttons_->SetBoundsRect(gfx::IntersectRects(rect, buttons_bounds)); | 238 buttons_->SetBoundsRect(gfx::IntersectRects(rect, buttons_bounds)); |
| 196 } | 239 } |
| 197 | 240 |
| 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, | 241 void PageSwitcherVertical::ButtonPressed(views::Button* sender, |
| 231 const ui::Event& event) { | 242 const ui::Event& event) { |
| 232 for (int i = 0; i < buttons_->child_count(); ++i) { | 243 for (int i = 0; i < buttons_->child_count(); ++i) { |
| 233 if (sender == static_cast<views::Button*>(buttons_->child_at(i))) { | 244 if (sender == static_cast<views::Button*>(buttons_->child_at(i))) { |
| 234 model_->SelectPage(i, true /* animate */); | 245 model_->SelectPage(i, true /* animate */); |
| 235 break; | 246 break; |
| 236 } | 247 } |
| 237 } | 248 } |
| 238 } | 249 } |
| 239 | 250 |
| 240 void PageSwitcherVertical::TotalPagesChanged() { | 251 void PageSwitcherVertical::TotalPagesChanged() { |
| 241 buttons_->RemoveAllChildViews(true); | 252 buttons_->RemoveAllChildViews(true); |
| 242 for (int i = 0; i < model_->total_pages(); ++i) { | 253 for (int i = 0; i < model_->total_pages(); ++i) { |
| 243 PageSwitcherButton* button = new PageSwitcherButton(this); | 254 PageSwitcherButton* button = new PageSwitcherButton(this); |
| 244 button->SetSelectedRange(i == model_->selected_page() ? 1 : 0); | 255 button->SetSelected(i == model_->selected_page() ? true : false); |
| 245 buttons_->AddChildView(button); | 256 buttons_->AddChildView(button); |
| 246 } | 257 } |
| 247 buttons_->SetVisible(model_->total_pages() > 1); | 258 buttons_->SetVisible(model_->total_pages() > 1); |
| 248 Layout(); | 259 Layout(); |
| 249 } | 260 } |
| 250 | 261 |
| 251 void PageSwitcherVertical::SelectedPageChanged(int old_selected, | 262 void PageSwitcherVertical::SelectedPageChanged(int old_selected, |
| 252 int new_selected) { | 263 int new_selected) { |
| 253 if (old_selected >= 0 && old_selected < buttons_->child_count()) | 264 if (old_selected >= 0 && old_selected < buttons_->child_count()) |
| 254 GetButtonByIndex(buttons_, old_selected)->SetSelectedRange(0); | 265 GetButtonByIndex(buttons_, old_selected)->SetSelected(false); |
| 255 if (new_selected >= 0 && new_selected < buttons_->child_count()) | 266 if (new_selected >= 0 && new_selected < buttons_->child_count()) |
| 256 GetButtonByIndex(buttons_, new_selected)->SetSelectedRange(1); | 267 GetButtonByIndex(buttons_, new_selected)->SetSelected(true); |
| 257 } | 268 } |
| 258 | 269 |
| 259 void PageSwitcherVertical::TransitionStarted() {} | 270 void PageSwitcherVertical::TransitionStarted() {} |
| 260 | 271 |
| 261 void PageSwitcherVertical::TransitionChanged() { | 272 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 | 273 |
| 278 } // namespace app_list | 274 } // namespace app_list |
| OLD | NEW |