diff options
author | varkha <varkha@chromium.org> | 2015-11-27 08:54:00 -0800 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-11-27 16:54:43 +0000 |
commit | bcd9c28cd2858c07ef2b0b5107e1c5523cf84645 (patch) | |
tree | dbb8620eb9f37f489a3881f004f943d65e89ed35 | |
parent | e20defea3ea7b641bb91fce301cc000b44c0f92c (diff) | |
download | chromium_src-bcd9c28cd2858c07ef2b0b5107e1c5523cf84645.zip chromium_src-bcd9c28cd2858c07ef2b0b5107e1c5523cf84645.tar.gz chromium_src-bcd9c28cd2858c07ef2b0b5107e1c5523cf84645.tar.bz2 |
Refactor ink drop animations to allow adding them to any View
and not just Button descendants.
This CL is upstreamed to https://codereview.chromium.org/1409183003/
Looking for a way to allow organizing event handling necessary for
ink drop animations. See goals description here:
https://docs.google.com/document/d/1U4AdR4wZVsZ8g0y95EblY5YiYVbVXcBTJ59XJBF147M/edit#heading=h.ai99f1d1eumr
BUG=537202
Review URL: https://codereview.chromium.org/1411833006
Cr-Commit-Position: refs/heads/master@{#362013}
27 files changed, 577 insertions, 80 deletions
diff --git a/chrome/browser/ui/views/bar_control_button.cc b/chrome/browser/ui/views/bar_control_button.cc index e8c727c..6eb966b 100644 --- a/chrome/browser/ui/views/bar_control_button.cc +++ b/chrome/browser/ui/views/bar_control_button.cc @@ -66,8 +66,7 @@ void BarControlButton::OnNativeThemeChanged(const ui::NativeTheme* theme) { void BarControlButton::Layout() { ImageButton::Layout(); - ink_drop_animation_controller_->SetInkDropCenter( - GetLocalBounds().CenterPoint()); + ink_drop_animation_controller_->SetInkDropCenter(CalculateInkDropCenter()); } void BarControlButton::AddInkDropLayer(ui::Layer* ink_drop_layer) { @@ -85,6 +84,10 @@ void BarControlButton::RemoveInkDropLayer(ui::Layer* ink_drop_layer) { SetPaintToLayer(false); } +gfx::Point BarControlButton::CalculateInkDropCenter() const { + return GetLocalBounds().CenterPoint(); +} + bool BarControlButton::OnMousePressed(const ui::MouseEvent& event) { if (IsTriggerableEvent(event)) { ink_drop_animation_controller_->AnimateToState( diff --git a/chrome/browser/ui/views/bar_control_button.h b/chrome/browser/ui/views/bar_control_button.h index 7a997cc..8bc3917 100644 --- a/chrome/browser/ui/views/bar_control_button.h +++ b/chrome/browser/ui/views/bar_control_button.h @@ -43,6 +43,7 @@ class BarControlButton : public views::ImageButton, public views::InkDropHost { // views::InkDropHost: void AddInkDropLayer(ui::Layer* ink_drop_layer) override; void RemoveInkDropLayer(ui::Layer* ink_drop_layer) override; + gfx::Point CalculateInkDropCenter() const override; gfx::VectorIconId id_; base::Callback<SkColor(void)> get_text_color_callback_; diff --git a/chrome/browser/ui/views/toolbar/app_menu_button.cc b/chrome/browser/ui/views/toolbar/app_menu_button.cc index bca918d..6a1e488 100644 --- a/chrome/browser/ui/views/toolbar/app_menu_button.cc +++ b/chrome/browser/ui/views/toolbar/app_menu_button.cc @@ -211,6 +211,17 @@ void AppMenuButton::RemoveInkDropLayer(ui::Layer* ink_drop_layer) { SetPaintToLayer(false); } +gfx::Point AppMenuButton::CalculateInkDropCenter() const { + // ToolbarView extends the bounds of the app button to the right in maximized + // mode. So instead of using the center point of local bounds, we use the + // center point (adjusted for RTL layouts) of the preferred size, which + // doesn't change in maximized mode. + const int visible_width = GetPreferredSize().width(); + return gfx::Point( + (GetMirroredXWithWidthInView(0, visible_width) + visible_width) / 2, + height() / 2); +} + const char* AppMenuButton::GetClassName() const { return "AppMenuButton"; } @@ -259,15 +270,7 @@ bool AppMenuButton::CanDrop(const ui::OSExchangeData& data) { void AppMenuButton::Layout() { MenuButton::Layout(); - - // ToolbarView extends the bounds of the app button to the right in maximized - // mode. So instead of using the center point of local bounds, we use the - // center point (adjusted for RTL layouts) of preferred size which doesn't - // change in maximized mode. - const int visible_width = GetPreferredSize().width(); - ink_drop_animation_controller_->SetInkDropCenter(gfx::Point( - (GetMirroredXWithWidthInView(0, visible_width) + visible_width) / 2, - height() / 2)); + ink_drop_animation_controller_->SetInkDropCenter(CalculateInkDropCenter()); } void AppMenuButton::OnDragEntered(const ui::DropTargetEvent& event) { diff --git a/chrome/browser/ui/views/toolbar/app_menu_button.h b/chrome/browser/ui/views/toolbar/app_menu_button.h index bb677f7..2536f543 100644 --- a/chrome/browser/ui/views/toolbar/app_menu_button.h +++ b/chrome/browser/ui/views/toolbar/app_menu_button.h @@ -74,6 +74,7 @@ class AppMenuButton : public views::InkDropHost, // views::InkDropHost: void AddInkDropLayer(ui::Layer* ink_drop_layer) override; void RemoveInkDropLayer(ui::Layer* ink_drop_layer) override; + gfx::Point CalculateInkDropCenter() const override; // views::MenuButton: const char* GetClassName() const override; diff --git a/chrome/browser/ui/views/toolbar/toolbar_action_view.cc b/chrome/browser/ui/views/toolbar/toolbar_action_view.cc index 2843f70..524fb95 100644 --- a/chrome/browser/ui/views/toolbar/toolbar_action_view.cc +++ b/chrome/browser/ui/views/toolbar/toolbar_action_view.cc @@ -26,6 +26,7 @@ #include "ui/gfx/image/image_skia_operations.h" #include "ui/gfx/image/image_skia_source.h" #include "ui/resources/grit/ui_resources.h" +#include "ui/views/animation/button_ink_drop_delegate.h" #include "ui/views/controls/button/label_button_border.h" #include "ui/views/controls/menu/menu_controller.h" #include "ui/views/controls/menu/menu_model_adapter.h" @@ -67,6 +68,9 @@ ToolbarActionView::ToolbarActionView( wants_to_run_(false), menu_(nullptr), weak_factory_(this) { + scoped_ptr<views::InkDropDelegate> new_ink_drop_delegate( + new views::ButtonInkDropDelegate(this, this)); + SetInkDropDelegate(new_ink_drop_delegate.Pass()); set_id(VIEW_ID_BROWSER_ACTION); view_controller_->SetDelegate(this); SetHorizontalAlignment(gfx::ALIGN_CENTER); @@ -74,6 +78,14 @@ ToolbarActionView::ToolbarActionView( set_context_menu_controller(this); + const int kInkDropLargeSize = 32; + const int kInkDropLargeCornerRadius = 5; + const int kInkDropSmallSize = 24; + const int kInkDropSmallCornerRadius = 2; + ink_drop_delegate()->SetInkDropSize( + kInkDropLargeSize, kInkDropLargeCornerRadius, kInkDropSmallSize, + kInkDropSmallCornerRadius); + // We also listen for browser theme changes on linux because a switch from or // to GTK requires that we regrab our browser action images. registrar_.Add( @@ -99,42 +111,31 @@ ToolbarActionView::~ToolbarActionView() { view_controller_->SetDelegate(nullptr); } -gfx::Size ToolbarActionView::GetPreferredSize() const { - return gfx::Size(ToolbarActionsBar::IconWidth(false), - ToolbarActionsBar::IconHeight()); +void ToolbarActionView::GetAccessibleState(ui::AXViewState* state) { + views::MenuButton::GetAccessibleState(state); + state->role = ui::AX_ROLE_BUTTON; } -void ToolbarActionView::OnDragDone() { - views::MenuButton::OnDragDone(); - delegate_->OnToolbarActionViewDragDone(); +scoped_ptr<LabelButtonBorder> ToolbarActionView::CreateDefaultBorder() const { + scoped_ptr<LabelButtonBorder> border = LabelButton::CreateDefaultBorder(); + border->set_insets(gfx::Insets(kBorderInset, kBorderInset, + kBorderInset, kBorderInset)); + return border.Pass(); } -void ToolbarActionView::ViewHierarchyChanged( - const ViewHierarchyChangedDetails& details) { - if (details.is_add && !called_register_command_ && GetFocusManager()) { - view_controller_->RegisterCommand(); - called_register_command_ = true; - } - - MenuButton::ViewHierarchyChanged(details); +void ToolbarActionView::OnMouseEntered(const ui::MouseEvent& event) { + delegate_->OnMouseEnteredToolbarActionView(); + views::MenuButton::OnMouseEntered(event); } -void ToolbarActionView::GetAccessibleState(ui::AXViewState* state) { - views::MenuButton::GetAccessibleState(state); - state->role = ui::AX_ROLE_BUTTON; +bool ToolbarActionView::ShouldEnterPushedState(const ui::Event& event) { + return views::MenuButton::ShouldEnterPushedState(event) && + (base::TimeTicks::Now() - popup_closed_time_).InMilliseconds() > + views::kMinimumMsBetweenButtonClicks; } -void ToolbarActionView::OnMenuButtonClicked(views::View* sender, - const gfx::Point& point) { - if (!view_controller_->IsEnabled(GetCurrentWebContents())) { - // We should only get a button pressed event with a non-enabled action if - // the left-click behavior should open the menu. - DCHECK(view_controller_->DisabledClickOpensMenu()); - context_menu_controller()->ShowContextMenuForView(this, point, - ui::MENU_SOURCE_NONE); - } else { - view_controller_->ExecuteAction(true); - } +content::WebContents* ToolbarActionView::GetCurrentWebContents() const { + return delegate_->GetCurrentWebContents(); } void ToolbarActionView::UpdateState() { @@ -170,6 +171,19 @@ void ToolbarActionView::UpdateState() { SchedulePaint(); } +void ToolbarActionView::OnMenuButtonClicked(views::View* sender, + const gfx::Point& point) { + if (!view_controller_->IsEnabled(GetCurrentWebContents())) { + // We should only get a button pressed event with a non-enabled action if + // the left-click behavior should open the menu. + DCHECK(view_controller_->DisabledClickOpensMenu()); + context_menu_controller()->ShowContextMenuForView(this, point, + ui::MENU_SOURCE_NONE); + } else { + view_controller_->ExecuteAction(true); + } +} + void ToolbarActionView::Observe(int type, const content::NotificationSource& source, const content::NotificationDetails& details) { @@ -177,22 +191,22 @@ void ToolbarActionView::Observe(int type, UpdateState(); } -void ToolbarActionView::OnMouseEntered(const ui::MouseEvent& event) { - delegate_->OnMouseEnteredToolbarActionView(); - views::MenuButton::OnMouseEntered(event); -} +void ToolbarActionView::AddInkDropLayer(ui::Layer* ink_drop_layer) { + SetPaintToLayer(true); + SetFillsBoundsOpaquely(false); + image()->SetPaintToLayer(true); + image()->SetFillsBoundsOpaquely(false); -bool ToolbarActionView::ShouldEnterPushedState(const ui::Event& event) { - return views::MenuButton::ShouldEnterPushedState(event) && - (base::TimeTicks::Now() - popup_closed_time_).InMilliseconds() > - views::kMinimumMsBetweenButtonClicks; + layer()->Add(ink_drop_layer); + layer()->StackAtBottom(ink_drop_layer); } -scoped_ptr<LabelButtonBorder> ToolbarActionView::CreateDefaultBorder() const { - scoped_ptr<LabelButtonBorder> border = LabelButton::CreateDefaultBorder(); - border->set_insets(gfx::Insets(kBorderInset, kBorderInset, - kBorderInset, kBorderInset)); - return border.Pass(); +void ToolbarActionView::RemoveInkDropLayer(ui::Layer* ink_drop_layer) { + layer()->Remove(ink_drop_layer); + + image()->SetFillsBoundsOpaquely(true); + image()->SetPaintToLayer(false); + SetPaintToLayer(false); } gfx::ImageSkia ToolbarActionView::GetIconForTest() { @@ -204,6 +218,41 @@ void ToolbarActionView::set_context_menu_callback_for_testing( context_menu_callback = callback; } +gfx::Size ToolbarActionView::GetPreferredSize() const { + return gfx::Size(ToolbarActionsBar::IconWidth(false), + ToolbarActionsBar::IconHeight()); +} + +bool ToolbarActionView::OnMousePressed(const ui::MouseEvent& event) { + // views::MenuButton actions are only triggered by left mouse clicks. + if (event.IsOnlyLeftMouseButton()) + ink_drop_delegate()->OnAction(views::InkDropState::ACTION_PENDING); + return MenuButton::OnMousePressed(event); +} + +void ToolbarActionView::OnGestureEvent(ui::GestureEvent* event) { + // While the dropdown menu is showing, the button should not handle gestures. + if (menu_) + event->StopPropagation(); + else + MenuButton::OnGestureEvent(event); +} + +void ToolbarActionView::OnDragDone() { + views::MenuButton::OnDragDone(); + delegate_->OnToolbarActionViewDragDone(); +} + +void ToolbarActionView::ViewHierarchyChanged( + const ViewHierarchyChangedDetails& details) { + if (details.is_add && !called_register_command_ && GetFocusManager()) { + view_controller_->RegisterCommand(); + called_register_command_ = true; + } + + MenuButton::ViewHierarchyChanged(details); +} + views::View* ToolbarActionView::GetAsView() { return this; } @@ -223,10 +272,6 @@ bool ToolbarActionView::IsMenuRunning() const { return menu_ != nullptr; } -content::WebContents* ToolbarActionView::GetCurrentWebContents() const { - return delegate_->GetCurrentWebContents(); -} - void ToolbarActionView::OnPopupShown(bool by_user) { // If this was through direct user action, we press the menu button. if (by_user) { @@ -271,6 +316,10 @@ void ToolbarActionView::ShowContextMenuForView( DoShowContextMenu(source_type); } +gfx::Point ToolbarActionView::CalculateInkDropCenter() const { + return GetLocalBounds().CenterPoint(); +} + void ToolbarActionView::DoShowContextMenu( ui::MenuSourceType source_type) { ui::MenuModel* context_menu_model = view_controller_->GetContextMenu(); @@ -295,6 +344,8 @@ void ToolbarActionView::DoShowContextMenu( delegate_->GetOverflowReferenceView()->GetWidget() : GetWidget(); + ink_drop_delegate()->OnAction(views::InkDropState::ACTIVATED); + views::MenuModelAdapter adapter(context_menu_model); menu_ = adapter.CreateMenu(); menu_runner_.reset(new views::MenuRunner(menu_, run_types)); @@ -306,6 +357,7 @@ void ToolbarActionView::DoShowContextMenu( source_type) == views::MenuRunner::MENU_DELETED) { return; } + ink_drop_delegate()->OnAction(views::InkDropState::DEACTIVATED); menu_runner_.reset(); menu_ = nullptr; diff --git a/chrome/browser/ui/views/toolbar/toolbar_action_view.h b/chrome/browser/ui/views/toolbar/toolbar_action_view.h index 73e607e..7df2efe 100644 --- a/chrome/browser/ui/views/toolbar/toolbar_action_view.h +++ b/chrome/browser/ui/views/toolbar/toolbar_action_view.h @@ -9,6 +9,7 @@ #include "chrome/browser/ui/views/toolbar/toolbar_action_view_delegate_views.h" #include "content/public/browser/notification_observer.h" #include "content/public/browser/notification_registrar.h" +#include "ui/views/animation/ink_drop_host.h" #include "ui/views/context_menu_controller.h" #include "ui/views/controls/button/menu_button.h" #include "ui/views/controls/button/menu_button_listener.h" @@ -39,7 +40,8 @@ class ToolbarActionView : public views::MenuButton, public ToolbarActionViewDelegateViews, public views::MenuButtonListener, public views::ContextMenuController, - public content::NotificationObserver { + public content::NotificationObserver, + public views::InkDropHost { public: // Need DragController here because ToolbarActionView could be // dragged/dropped. @@ -65,6 +67,9 @@ class ToolbarActionView : public views::MenuButton, ~Delegate() override {} }; + // Callback type used for testing. + using ContextMenuCallback = base::Callback<void(ToolbarActionView*)>; + ToolbarActionView(ToolbarActionViewController* view_controller, Profile* profile, Delegate* delegate); @@ -72,6 +77,13 @@ class ToolbarActionView : public views::MenuButton, // views::MenuButton: void GetAccessibleState(ui::AXViewState* state) override; + scoped_ptr<views::LabelButtonBorder> CreateDefaultBorder() const override; + void OnMouseEntered(const ui::MouseEvent& event) override; + bool ShouldEnterPushedState(const ui::Event& event) override; + + // ToolbarActionViewDelegateViews: + content::WebContents* GetCurrentWebContents() const override; + void UpdateState() override; // views::MenuButtonListener: void OnMenuButtonClicked(views::View* sender, @@ -82,14 +94,9 @@ class ToolbarActionView : public views::MenuButton, const content::NotificationSource& source, const content::NotificationDetails& details) override; - // views::MenuButton: - scoped_ptr<views::LabelButtonBorder> CreateDefaultBorder() const override; - void OnMouseEntered(const ui::MouseEvent& event) override; - bool ShouldEnterPushedState(const ui::Event& event) override; - - // ToolbarActionViewDelegate: (public because called by others). - void UpdateState() override; - content::WebContents* GetCurrentWebContents() const override; + // views::InkDropHost: + void AddInkDropLayer(ui::Layer* ink_drop_layer) override; + void RemoveInkDropLayer(ui::Layer* ink_drop_layer) override; ToolbarActionViewController* view_controller() { return view_controller_; @@ -100,7 +107,6 @@ class ToolbarActionView : public views::MenuButton, bool wants_to_run_for_testing() const { return wants_to_run_; } - using ContextMenuCallback = base::Callback<void(ToolbarActionView*)>; // Set a callback to be called directly before the context menu is shown. // The toolbar action opening the menu will be passed in. static void set_context_menu_callback_for_testing( @@ -111,6 +117,8 @@ class ToolbarActionView : public views::MenuButton, private: // views::MenuButton: gfx::Size GetPreferredSize() const override; + bool OnMousePressed(const ui::MouseEvent& event) override; + void OnGestureEvent(ui::GestureEvent* event) override; void OnDragDone() override; void ViewHierarchyChanged( const ViewHierarchyChangedDetails& details) override; @@ -128,6 +136,9 @@ class ToolbarActionView : public views::MenuButton, const gfx::Point& point, ui::MenuSourceType source_type) override; + // views::InkDropHost: + gfx::Point CalculateInkDropCenter() const override; + // Shows the context menu (if one exists) for the toolbar action. void DoShowContextMenu(ui::MenuSourceType source_type); diff --git a/chrome/browser/ui/views/toolbar/toolbar_button.cc b/chrome/browser/ui/views/toolbar/toolbar_button.cc index f4c889b..f73fa3c 100644 --- a/chrome/browser/ui/views/toolbar/toolbar_button.cc +++ b/chrome/browser/ui/views/toolbar/toolbar_button.cc @@ -347,10 +347,10 @@ void ToolbarButton::ShowDropDownMenu(ui::MenuSourceType source_type) { SetState(STATE_NORMAL); } -gfx::Point ToolbarButton::CalculateInkDropCenter() const { - return GetLocalBounds().CenterPoint(); -} - const char* ToolbarButton::GetClassName() const { return "ToolbarButton"; } + +gfx::Point ToolbarButton::CalculateInkDropCenter() const { + return GetLocalBounds().CenterPoint(); +} diff --git a/chrome/browser/ui/views/toolbar/toolbar_button.h b/chrome/browser/ui/views/toolbar/toolbar_button.h index f607a44..a9d7b84 100644 --- a/chrome/browser/ui/views/toolbar/toolbar_button.h +++ b/chrome/browser/ui/views/toolbar/toolbar_button.h @@ -73,9 +73,6 @@ class ToolbarButton : public views::LabelButton, // Function to show the dropdown menu. virtual void ShowDropDownMenu(ui::MenuSourceType source_type); - // Returns the Point where the ink drop should be centered. - virtual gfx::Point CalculateInkDropCenter() const; - views::InkDropAnimationController* ink_drop_animation_controller() { return ink_drop_animation_controller_.get(); } @@ -84,6 +81,9 @@ class ToolbarButton : public views::LabelButton, // views::LabelButton: const char* GetClassName() const override; + // views::InkDropHost: + gfx::Point CalculateInkDropCenter() const override; + // The model that populates the attached menu. scoped_ptr<ui::MenuModel> model_; diff --git a/ui/aura/window.cc b/ui/aura/window.cc index c97941d..2634eba 100644 --- a/ui/aura/window.cc +++ b/ui/aura/window.cc @@ -93,7 +93,7 @@ Window::Window(WindowDelegate* delegate) // problems for code that adds an observer as part of an observer // notification (such as the workspace code). observers_(base::ObserverList<WindowObserver>::NOTIFY_EXISTING_ONLY) { - set_target_handler(delegate_); + SetTargetHandler(delegate_); } Window::~Window() { @@ -109,7 +109,7 @@ Window::~Window() { // While we are being destroyed, our target handler may also be in the // process of destruction or already destroyed, so do not forward any // input events at the ui::EP_TARGET phase. - set_target_handler(nullptr); + SetTargetHandler(nullptr); // TODO(beng): See comment in window_event_dispatcher.h. This shouldn't be // necessary but unfortunately is right now due to ordering diff --git a/ui/events/event_processor_unittest.cc b/ui/events/event_processor_unittest.cc index a0ab5b6..c563c17 100644 --- a/ui/events/event_processor_unittest.cc +++ b/ui/events/event_processor_unittest.cc @@ -4,6 +4,7 @@ #include <vector> +#include "base/macros.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/events/event.h" #include "ui/events/event_target_iterator.h" @@ -139,7 +140,7 @@ TEST_F(EventProcessorTest, NestedEventProcessing) { // first event processor should be handled by |target_handler| instead. scoped_ptr<TestEventHandler> target_handler( new ReDispatchEventHandler(second_processor.get(), root()->child_at(0))); - root()->child_at(0)->set_target_handler(target_handler.get()); + ignore_result(root()->child_at(0)->SetTargetHandler(target_handler.get())); // Dispatch a mouse event to the tree of event targets owned by the first // event processor, checking in ReDispatchEventHandler that the phase and diff --git a/ui/events/event_target.cc b/ui/events/event_target.cc index 9b44f9a..2e07219 100644 --- a/ui/events/event_target.cc +++ b/ui/events/event_target.cc @@ -56,6 +56,12 @@ bool EventTarget::IsPreTargetListEmpty() const { return pre_target_list_.empty(); } +EventHandler* EventTarget::SetTargetHandler(EventHandler* target_handler) { + EventHandler* original_target_handler = target_handler_; + target_handler_ = target_handler; + return original_target_handler; +} + void EventTarget::OnEvent(Event* event) { CHECK_EQ(this, event->target()); if (target_handler_) diff --git a/ui/events/event_target.h b/ui/events/event_target.h index e8b14a6..21a84a4 100644 --- a/ui/events/event_target.h +++ b/ui/events/event_target.h @@ -77,9 +77,8 @@ class EVENTS_EXPORT EventTarget : public EventHandler { // Returns true if the event pre target list is empty. bool IsPreTargetListEmpty() const; - void set_target_handler(EventHandler* handler) { - target_handler_ = handler; - } + // Sets |target_handler| as |target_handler_| and returns the old handler. + EventHandler* SetTargetHandler(EventHandler* target_handler); protected: EventHandler* target_handler() { return target_handler_; } diff --git a/ui/views/animation/button_ink_drop_delegate.cc b/ui/views/animation/button_ink_drop_delegate.cc new file mode 100644 index 0000000..5d3b9eb --- /dev/null +++ b/ui/views/animation/button_ink_drop_delegate.cc @@ -0,0 +1,87 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/views/animation/button_ink_drop_delegate.h" + +#include "ui/events/event.h" +#include "ui/views/animation/ink_drop_animation_controller.h" +#include "ui/views/animation/ink_drop_animation_controller_factory.h" +#include "ui/views/animation/ink_drop_host.h" +#include "ui/views/animation/ink_drop_state.h" +#include "ui/views/scoped_target_handler.h" +#include "ui/views/view.h" + +namespace views { + +ButtonInkDropDelegate::ButtonInkDropDelegate(InkDropHost* ink_drop_host, + View* view) + : target_handler_(new views::ScopedTargetHandler(view, this)), + ink_drop_host_(ink_drop_host), + ink_drop_animation_controller_( + InkDropAnimationControllerFactory::CreateInkDropAnimationController( + ink_drop_host_)) {} + +ButtonInkDropDelegate::~ButtonInkDropDelegate() { +} + +void ButtonInkDropDelegate::SetInkDropSize(int large_size, + int large_corner_radius, + int small_size, + int small_corner_radius) { + ink_drop_animation_controller_->SetInkDropSize( + gfx::Size(large_size, large_size), large_corner_radius, + gfx::Size(small_size, small_size), small_corner_radius); +} + +void ButtonInkDropDelegate::OnLayout() { + ink_drop_animation_controller_->SetInkDropCenter( + ink_drop_host_->CalculateInkDropCenter()); +} + +void ButtonInkDropDelegate::OnAction(InkDropState state) { + ink_drop_animation_controller_->AnimateToState(state); +} + +//////////////////////////////////////////////////////////////////////////////// +// ui::EventHandler: + +void ButtonInkDropDelegate::OnGestureEvent(ui::GestureEvent* event) { + InkDropState ink_drop_state = InkDropState::HIDDEN; + switch (event->type()) { + case ui::ET_GESTURE_TAP_DOWN: + ink_drop_state = InkDropState::ACTION_PENDING; + // The ui::ET_GESTURE_TAP_DOWN event needs to be marked as handled so that + // subsequent events for the gesture are sent to |this|. + event->SetHandled(); + break; + case ui::ET_GESTURE_LONG_PRESS: + ink_drop_state = InkDropState::SLOW_ACTION_PENDING; + break; + case ui::ET_GESTURE_LONG_TAP: + ink_drop_state = InkDropState::SLOW_ACTION; + break; + case ui::ET_GESTURE_SCROLL_BEGIN: + case ui::ET_GESTURE_END: + ink_drop_state = InkDropState::HIDDEN; + break; + default: + return; + } + + InkDropState current_ink_drop_state = + ink_drop_animation_controller_->GetInkDropState(); + + if (ink_drop_state == InkDropState::HIDDEN && + (current_ink_drop_state == InkDropState::QUICK_ACTION || + current_ink_drop_state == InkDropState::SLOW_ACTION || + current_ink_drop_state == InkDropState::DEACTIVATED)) { + // These InkDropStates automatically transition to the HIDDEN state so we + // don't make an explicit call. Explicitly animating to HIDDEN in this case + // would prematurely pre-empt these animations. + return; + } + ink_drop_animation_controller_->AnimateToState(ink_drop_state); +} + +} // namespace views diff --git a/ui/views/animation/button_ink_drop_delegate.h b/ui/views/animation/button_ink_drop_delegate.h new file mode 100644 index 0000000..1deb51c --- /dev/null +++ b/ui/views/animation/button_ink_drop_delegate.h @@ -0,0 +1,54 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_VIEWS_ANIMATION_BUTTON_INK_DROP_DELEGATE_H_ +#define UI_VIEWS_ANIMATION_BUTTON_INK_DROP_DELEGATE_H_ + +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" +#include "ui/events/event_handler.h" +#include "ui/views/animation/ink_drop_delegate.h" +#include "ui/views/views_export.h" + +namespace views { + +class InkDropAnimationController; +class InkDropHost; +class ScopedTargetHandler; +class View; + +// An InkDropDelegate that handles animations for toolbar buttons. +class VIEWS_EXPORT ButtonInkDropDelegate : public InkDropDelegate, + public ui::EventHandler { + public: + ButtonInkDropDelegate(InkDropHost* ink_drop_host, View* view); + ~ButtonInkDropDelegate() override; + + // InkDropDelegate: + void SetInkDropSize(int large_size, + int large_corner_radius, + int small_size, + int small_corner_radius) override; + void OnLayout() override; + void OnAction(InkDropState state) override; + + // ui::EventHandler: + void OnGestureEvent(ui::GestureEvent* event) override; + + private: + // An instance of ScopedTargetHandler allowing |this| to handling events. + scoped_ptr<views::ScopedTargetHandler> target_handler_; + + // Parent InkDropHost (typically a View) that hosts the ink ripple animations. + InkDropHost* ink_drop_host_; + + // Animation controller for the ink drop ripple effect. + scoped_ptr<InkDropAnimationController> ink_drop_animation_controller_; + + DISALLOW_COPY_AND_ASSIGN(ButtonInkDropDelegate); +}; + +} // namespace views + +#endif // UI_VIEWS_ANIMATION_BUTTON_INK_DROP_DELEGATE_H_ diff --git a/ui/views/animation/ink_drop_delegate.h b/ui/views/animation/ink_drop_delegate.h new file mode 100644 index 0000000..f8f7861 --- /dev/null +++ b/ui/views/animation/ink_drop_delegate.h @@ -0,0 +1,48 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_VIEWS_ANIMATION_INK_DROP_DELEGATE_H_ +#define UI_VIEWS_ANIMATION_INK_DROP_DELEGATE_H_ + +#include "base/macros.h" +#include "ui/views/animation/ink_drop_state.h" +#include "ui/views/views_export.h" + +namespace ui { +class GestureEvent; +class Event; +} // namespace ui + +namespace views { + +// Ink ripple animation delegate that starts and stops animations based on +// View states and events. +class VIEWS_EXPORT InkDropDelegate { + public: + InkDropDelegate() {} + virtual ~InkDropDelegate() {} + + // Sets sizes for the animation layers that are squares with |large_size| and + // |small_size| being the length of each side. When painting rounded squares + // |large_corner_radius| and |small_corner_radius| are specifying the + // corner radius. + virtual void SetInkDropSize(int large_size, + int large_corner_radius, + int small_size, + int small_corner_radius) = 0; + + // Called when the bounds or layout of the View changes necessitating change + // in positioning of ink ripple layers. + virtual void OnLayout() = 0; + + // Called when ink ripple state changes. + virtual void OnAction(InkDropState state) = 0; + + private: + DISALLOW_COPY_AND_ASSIGN(InkDropDelegate); +}; + +} // namespace views + +#endif // UI_VIEWS_ANIMATION_INK_DROP_DELEGATE_H_ diff --git a/ui/views/animation/ink_drop_host.h b/ui/views/animation/ink_drop_host.h index 4045e09..0830a41 100644 --- a/ui/views/animation/ink_drop_host.h +++ b/ui/views/animation/ink_drop_host.h @@ -6,6 +6,7 @@ #define UI_VIEWS_ANIMATION_INK_DROP_HOST_H_ #include "base/macros.h" +#include "ui/gfx/geometry/point.h" #include "ui/views/views_export.h" namespace ui { @@ -32,6 +33,10 @@ class VIEWS_EXPORT InkDropHost { // Removes |ink_drop_layer| from the layer tree. virtual void RemoveInkDropLayer(ui::Layer* ink_drop_layer) = 0; + // Returns the Point where the ink drop should be centered. + // TODO(varkha): This should be moved to InkDropConsumer. + virtual gfx::Point CalculateInkDropCenter() const = 0; + private: DISALLOW_COPY_AND_ASSIGN(InkDropHost); }; diff --git a/ui/views/animation/test/test_ink_drop_host.cc b/ui/views/animation/test/test_ink_drop_host.cc index 4a30ff6..b853820 100644 --- a/ui/views/animation/test/test_ink_drop_host.cc +++ b/ui/views/animation/test/test_ink_drop_host.cc @@ -14,4 +14,8 @@ void TestInkDropHost::AddInkDropLayer(ui::Layer* ink_drop_layer) {} void TestInkDropHost::RemoveInkDropLayer(ui::Layer* ink_drop_layer) {} +gfx::Point TestInkDropHost::CalculateInkDropCenter() const { + return gfx::Point(); +} + } // namespace views diff --git a/ui/views/animation/test/test_ink_drop_host.h b/ui/views/animation/test/test_ink_drop_host.h index e94839d..b952b77 100644 --- a/ui/views/animation/test/test_ink_drop_host.h +++ b/ui/views/animation/test/test_ink_drop_host.h @@ -20,6 +20,7 @@ class TestInkDropHost : public InkDropHost { // TestInkDropHost: void AddInkDropLayer(ui::Layer* ink_drop_layer) override; void RemoveInkDropLayer(ui::Layer* ink_drop_layer) override; + gfx::Point CalculateInkDropCenter() const override; private: DISALLOW_COPY_AND_ASSIGN(TestInkDropHost); diff --git a/ui/views/controls/button/custom_button.cc b/ui/views/controls/button/custom_button.cc index aafbf2d..66d9b2c 100644 --- a/ui/views/controls/button/custom_button.cc +++ b/ui/views/controls/button/custom_button.cc @@ -10,6 +10,7 @@ #include "ui/events/keycodes/keyboard_codes.h" #include "ui/gfx/animation/throb_animation.h" #include "ui/gfx/screen.h" +#include "ui/views/animation/ink_drop_delegate.h" #include "ui/views/controls/button/blue_button.h" #include "ui/views/controls/button/checkbox.h" #include "ui/views/controls/button/image_button.h" @@ -116,6 +117,11 @@ bool CustomButton::IsHotTracked() const { //////////////////////////////////////////////////////////////////////////////// // CustomButton, View overrides: +void CustomButton::Layout() { + if (ink_drop_delegate_) + ink_drop_delegate_->OnLayout(); +} + void CustomButton::OnEnabledChanged() { if (enabled() ? (state_ != STATE_DISABLED) : (state_ == STATE_DISABLED)) return; @@ -284,6 +290,8 @@ void CustomButton::OnDragDone() { // (since disabled buttons may still be able to be dragged). if (state_ != STATE_DISABLED) SetState(STATE_NORMAL); + if (ink_drop_delegate_) + ink_drop_delegate_->OnAction(InkDropState::HIDDEN); } void CustomButton::GetAccessibleState(ui::AXViewState* state) { @@ -371,9 +379,19 @@ bool CustomButton::ShouldEnterHoveredState() { return check_mouse_position && IsMouseHovered(); } +void CustomButton::SetInkDropDelegate( + scoped_ptr<InkDropDelegate> ink_drop_delegate) { + ink_drop_delegate_ = ink_drop_delegate.Pass(); +} + //////////////////////////////////////////////////////////////////////////////// // CustomButton, View overrides (protected): +void CustomButton::OnBoundsChanged(const gfx::Rect& previous_bounds) { + if (ink_drop_delegate_) + ink_drop_delegate_->OnLayout(); +} + void CustomButton::ViewHierarchyChanged( const ViewHierarchyChangedDetails& details) { if (!details.is_add && state_ != STATE_DISABLED) diff --git a/ui/views/controls/button/custom_button.h b/ui/views/controls/button/custom_button.h index 869bcd7..ef250c0 100644 --- a/ui/views/controls/button/custom_button.h +++ b/ui/views/controls/button/custom_button.h @@ -16,6 +16,8 @@ class ThrobAnimation; namespace views { +class InkDropDelegate; + // A button with custom rendering. The base of ImageButton and LabelButton. // Note that this type of button is not focusable by default and will not be // part of the focus chain. Call SetFocusable(true) to make it part of the @@ -76,6 +78,7 @@ class VIEWS_EXPORT CustomButton : public Button, bool IsHotTracked() const; // Overridden from View: + void Layout() override; void OnEnabledChanged() override; const char* GetClassName() const override; bool OnMousePressed(const ui::MouseEvent& event) override; @@ -123,7 +126,13 @@ class VIEWS_EXPORT CustomButton : public Button, // state). This does not take into account enabled state. bool ShouldEnterHoveredState(); + void SetInkDropDelegate(scoped_ptr<InkDropDelegate> ink_drop_delegate); + InkDropDelegate* ink_drop_delegate() const { + return ink_drop_delegate_.get(); + } + // Overridden from View: + void OnBoundsChanged(const gfx::Rect& previous_bounds) override; void ViewHierarchyChanged( const ViewHierarchyChangedDetails& details) override; void OnBlur() override; @@ -152,6 +161,9 @@ class VIEWS_EXPORT CustomButton : public Button, // See description above setter. bool request_focus_on_press_; + // Animation delegate for the ink drop ripple effect. + scoped_ptr<InkDropDelegate> ink_drop_delegate_; + // The event on which the button should notify its listener. NotifyAction notify_action_; diff --git a/ui/views/controls/button/label_button.cc b/ui/views/controls/button/label_button.cc index abd2c3f..2bb88c3 100644 --- a/ui/views/controls/button/label_button.cc +++ b/ui/views/controls/button/label_button.cc @@ -335,6 +335,7 @@ void LabelButton::Layout() { image_->SetBoundsRect(gfx::Rect(image_origin, image_size)); label_->SetBoundsRect(gfx::Rect(label_origin, label_size)); + CustomButton::Layout(); } const char* LabelButton::GetClassName() const { diff --git a/ui/views/controls/button/menu_button.cc b/ui/views/controls/button/menu_button.cc index 5603548..22a79ae 100644 --- a/ui/views/controls/button/menu_button.cc +++ b/ui/views/controls/button/menu_button.cc @@ -18,6 +18,7 @@ #include "ui/gfx/text_constants.h" #include "ui/resources/grit/ui_resources.h" #include "ui/strings/grit/ui_strings.h" +#include "ui/views/animation/ink_drop_delegate.h" #include "ui/views/controls/button/button.h" #include "ui/views/controls/button/menu_button_listener.h" #include "ui/views/mouse_constants.h" @@ -126,6 +127,8 @@ bool MenuButton::Activate() { // We don't set our state here. It's handled in the MenuController code or // by our click listener. + if (ink_drop_delegate()) + ink_drop_delegate()->OnAction(InkDropState::QUICK_ACTION); listener_->OnMenuButtonClicked(this, menu_position); if (destroyed) { @@ -189,6 +192,8 @@ void MenuButton::OnMouseReleased(const ui::MouseEvent& event) { HitTestPoint(event.location()) && !InDrag()) { Activate(); } else { + if (ink_drop_delegate()) + ink_drop_delegate()->OnAction(InkDropState::HIDDEN); LabelButton::OnMouseReleased(event); } } diff --git a/ui/views/controls/button/menu_button.h b/ui/views/controls/button/menu_button.h index 1881fd2..b7a1a8b 100644 --- a/ui/views/controls/button/menu_button.h +++ b/ui/views/controls/button/menu_button.h @@ -121,7 +121,7 @@ class VIEWS_EXPORT MenuButton : public LabelButton { // The down arrow used to differentiate the menu button from normal buttons. const gfx::ImageSkia* menu_marker_; - // If non-null the destuctor sets this to true. This is set while the menu is + // If non-null the destructor sets this to true. This is set while the menu is // showing and used to detect if the menu was deleted while running. bool* destroyed_flag_; diff --git a/ui/views/scoped_target_handler.cc b/ui/views/scoped_target_handler.cc new file mode 100644 index 0000000..180a8a8 --- /dev/null +++ b/ui/views/scoped_target_handler.cc @@ -0,0 +1,67 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/views/scoped_target_handler.h" + +#include "ui/events/event.h" +#include "ui/events/event_handler.h" +#include "ui/events/event_target.h" +#include "ui/views/view.h" + +namespace views { + +ScopedTargetHandler::ScopedTargetHandler(View* view, + ui::EventHandler* handler) + : destroyed_flag_(NULL), view_(view), new_handler_(handler){ + original_handler_ = view_->SetTargetHandler(this); +} + +ScopedTargetHandler::~ScopedTargetHandler() { + EventHandler* handler = view_->SetTargetHandler(original_handler_); + DCHECK_EQ(this, handler); + if (destroyed_flag_) + *destroyed_flag_ = true; +} + +void ScopedTargetHandler::OnEvent(ui::Event* event) { + bool destroyed = false; + destroyed_flag_ = &destroyed; + + if (original_handler_) + original_handler_->OnEvent(event); + else + EventHandler::OnEvent(event); + + if (destroyed) + return; + destroyed_flag_ = NULL; + + new_handler_->OnEvent(event); +} + +void ScopedTargetHandler::OnKeyEvent(ui::KeyEvent* event) { + static_cast<EventHandler*>(view_)->OnKeyEvent(event); +} + +void ScopedTargetHandler::OnMouseEvent(ui::MouseEvent* event) { + static_cast<EventHandler*>(view_)->OnMouseEvent(event); +} + +void ScopedTargetHandler::OnScrollEvent(ui::ScrollEvent* event) { + static_cast<EventHandler*>(view_)->OnScrollEvent(event); +} + +void ScopedTargetHandler::OnTouchEvent(ui::TouchEvent* event) { + static_cast<EventHandler*>(view_)->OnTouchEvent(event); +} + +void ScopedTargetHandler::OnGestureEvent(ui::GestureEvent* event) { + static_cast<EventHandler*>(view_)->OnGestureEvent(event); +} + +void ScopedTargetHandler::OnCancelMode(ui::CancelModeEvent* event) { + static_cast<EventHandler*>(view_)->OnCancelMode(event); +} + +} // namespace views diff --git a/ui/views/scoped_target_handler.h b/ui/views/scoped_target_handler.h new file mode 100644 index 0000000..75b4f90 --- /dev/null +++ b/ui/views/scoped_target_handler.h @@ -0,0 +1,55 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_VIEWS_SCOPED_TARGET_HANDLER_H_ +#define UI_VIEWS_SCOPED_TARGET_HANDLER_H_ + +#include "base/macros.h" +#include "ui/events/event_handler.h" +#include "ui/views/views_export.h" + +namespace views { + +class View; + +// An EventHandler that replaces an View's target handler with itself to pass +// events first to the original handlers and second to an additional new +// EventHandler. The new handler gets called after the original handlers even +// if they call SetHandled() or StopPropagation() on the event. +class VIEWS_EXPORT ScopedTargetHandler : public ui::EventHandler { + public: + ScopedTargetHandler(View* view, ui::EventHandler* new_handler); + ~ScopedTargetHandler() override; + + // ui::EventHandler: + void OnEvent(ui::Event* event) override; + void OnKeyEvent(ui::KeyEvent* event) override; + void OnMouseEvent(ui::MouseEvent* event) override; + void OnScrollEvent(ui::ScrollEvent* event) override; + void OnTouchEvent(ui::TouchEvent* event) override; + void OnGestureEvent(ui::GestureEvent* event) override; + void OnCancelMode(ui::CancelModeEvent* event) override; + + private: + // If non-null the destructor sets this to true. This is set while handling + // an event and used to detect if |this| has been deleted. + bool* destroyed_flag_; + + // An View that has its target handler replaced with |this| for a life time of + // |this|. + View* view_; + + // An EventHandler that gets restored on |view_| when |this| is destroyed. + ui::EventHandler* original_handler_; + + // A new handler that gets events in addition to the |original_handler_| or + // |view_|. + ui::EventHandler* new_handler_; + + DISALLOW_COPY_AND_ASSIGN(ScopedTargetHandler); +}; + +} // namespace views + +#endif // UI_VIEWS_SCOPED_TARGET_HANDLER_H_ diff --git a/ui/views/view_unittest.cc b/ui/views/view_unittest.cc index cabf5d4..3e2180b 100644 --- a/ui/views/view_unittest.cc +++ b/ui/views/view_unittest.cc @@ -31,6 +31,7 @@ #include "ui/views/controls/scroll_view.h" #include "ui/views/controls/textfield/textfield.h" #include "ui/views/focus/view_storage.h" +#include "ui/views/scoped_target_handler.h" #include "ui/views/test/views_test_base.h" #include "ui/views/view.h" #include "ui/views/widget/native_widget.h" @@ -4296,4 +4297,61 @@ TEST_F(ViewTest, OnNativeThemeChanged) { widget->CloseNow(); } +class TestEventHandler : public ui::EventHandler { + public: + TestEventHandler(TestView* view) : view_(view), had_mouse_event_(false) {} + ~TestEventHandler() override {} + + void OnMouseEvent(ui::MouseEvent* event) override { + // The |view_| should have received the event first. + EXPECT_EQ(ui::ET_MOUSE_PRESSED, view_->last_mouse_event_type_); + had_mouse_event_ = true; + } + + TestView* view_; + bool had_mouse_event_; +}; + +TEST_F(ViewTest, ScopedTargetHandlerReceivesEvents) { + TestView* v = new TestView(); + v->SetBoundsRect(gfx::Rect(0, 0, 300, 300)); + + scoped_ptr<Widget> widget(new Widget); + Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); + params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + params.bounds = gfx::Rect(50, 50, 350, 350); + widget->Init(params); + internal::RootView* root = + static_cast<internal::RootView*>(widget->GetRootView()); + root->AddChildView(v); + v->Reset(); + TestEventHandler handler(v); + { + ScopedTargetHandler scoped_target_handler(v, &handler); + // View's target EventHandler should be set to the |scoped_target_handler|. + EXPECT_EQ(&scoped_target_handler, + v->SetTargetHandler(&scoped_target_handler)); + + EXPECT_EQ(ui::ET_UNKNOWN, v->last_mouse_event_type_); + gfx::Point p(10, 120); + ui::MouseEvent pressed(ui::ET_MOUSE_PRESSED, p, p, ui::EventTimeForNow(), + ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); + root->OnMousePressed(pressed); + + // Both the View |v| and the |handler| should have received the event. + EXPECT_EQ(ui::ET_MOUSE_PRESSED, v->last_mouse_event_type_); + EXPECT_TRUE(handler.had_mouse_event_); + } + + // The View should no longer have a target EventHandler. + EXPECT_EQ(nullptr, v->SetTargetHandler(nullptr)); + + // The View should continue receiving events after the |handler| is deleted. + v->Reset(); + ui::MouseEvent released(ui::ET_MOUSE_RELEASED, gfx::Point(), gfx::Point(), + ui::EventTimeForNow(), 0, 0); + root->OnMouseReleased(released); + EXPECT_EQ(ui::ET_MOUSE_RELEASED, v->last_mouse_event_type_); +} + } // namespace views diff --git a/ui/views/views.gyp b/ui/views/views.gyp index 536b738..43423bf 100644 --- a/ui/views/views.gyp +++ b/ui/views/views.gyp @@ -14,6 +14,8 @@ 'accessible_pane_view.h', 'animation/bounds_animator.cc', 'animation/bounds_animator.h', + 'animation/button_ink_drop_delegate.cc', + 'animation/button_ink_drop_delegate.h', 'animation/ink_drop_animation.cc', 'animation/ink_drop_animation.h', 'animation/ink_drop_animation_controller.h', @@ -23,6 +25,7 @@ 'animation/ink_drop_animation_controller_impl.h', 'animation/ink_drop_animation_observer.cc', 'animation/ink_drop_animation_observer.h', + 'animation/ink_drop_delegate.h', 'animation/ink_drop_host.h', 'animation/ink_drop_painted_layer_delegates.cc', 'animation/ink_drop_painted_layer_delegates.h', @@ -269,6 +272,8 @@ 'repeat_controller.h', 'round_rect_painter.cc', 'round_rect_painter.h', + 'scoped_target_handler.cc', + 'scoped_target_handler.h', 'shadow_border.cc', 'shadow_border.h', 'style/mac/dialog_button_border_mac.cc', |