blob: ab1490099a3bcef524a7dfe6238bb6d80890a447 [file] [log] [blame]
// Copyright 2016 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/ink_drop_host_view.h"
#include "ui/events/event.h"
#include "ui/events/scoped_target_handler.h"
#include "ui/gfx/color_palette.h"
#include "ui/gfx/geometry/size_conversions.h"
#include "ui/views/animation/ink_drop.h"
#include "ui/views/animation/ink_drop_highlight.h"
#include "ui/views/animation/ink_drop_impl.h"
#include "ui/views/animation/ink_drop_stub.h"
#include "ui/views/animation/square_ink_drop_ripple.h"
namespace views {
namespace {
// Default sizes for ink drop effects.
const int kInkDropSize = 24;
const int kInkDropLargeCornerRadius = 4;
// The scale factor to compute the large ink drop size.
const float kLargeInkDropScale = 1.333f;
// Default opacity of the ink drop when it is visible.
const float kInkDropVisibleOpacity = 0.175f;
gfx::Size CalculateLargeInkDropSize(const gfx::Size small_size) {
return gfx::ScaleToCeiledSize(gfx::Size(small_size), kLargeInkDropScale);
}
} // namespace
// static
const int InkDropHostView::kInkDropSmallCornerRadius = 2;
// An EventHandler that is guaranteed to be invoked and is not prone to
// InkDropHostView descendents who do not call
// InkDropHostView::OnGestureEvent(). Only one instance of this class can exist
// at any given time for each ink drop host view.
//
// TODO(bruthig): Consider getting rid of this class.
class InkDropHostView::InkDropGestureHandler : public ui::EventHandler {
public:
InkDropGestureHandler(InkDropHostView* host_view, InkDrop* ink_drop)
: target_handler_(new ui::ScopedTargetHandler(host_view, this)),
host_view_(host_view),
ink_drop_(ink_drop) {}
~InkDropGestureHandler() override {}
void SetInkDrop(InkDrop* ink_drop) { ink_drop_ = ink_drop; }
// ui::EventHandler:
void OnGestureEvent(ui::GestureEvent* event) override {
InkDropState current_ink_drop_state = ink_drop_->GetTargetInkDropState();
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::ALTERNATE_ACTION_PENDING;
break;
case ui::ET_GESTURE_LONG_TAP:
ink_drop_state = InkDropState::ALTERNATE_ACTION_TRIGGERED;
break;
case ui::ET_GESTURE_END:
if (current_ink_drop_state == InkDropState::ACTIVATED)
return;
// Fall through to ui::ET_GESTURE_SCROLL_BEGIN case.
case ui::ET_GESTURE_SCROLL_BEGIN:
ink_drop_state = InkDropState::HIDDEN;
break;
default:
return;
}
if (ink_drop_state == InkDropState::HIDDEN &&
(current_ink_drop_state == InkDropState::ACTION_TRIGGERED ||
current_ink_drop_state == InkDropState::ALTERNATE_ACTION_TRIGGERED ||
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;
}
host_view_->AnimateInkDrop(ink_drop_state, event);
}
private:
// Allows |this| to handle all GestureEvents on |host_view_|.
std::unique_ptr<ui::ScopedTargetHandler> target_handler_;
// The host view to cache ui::Events to when animating the ink drop.
InkDropHostView* host_view_;
// Animation controller for the ink drop ripple effect.
InkDrop* ink_drop_;
DISALLOW_COPY_AND_ASSIGN(InkDropGestureHandler);
};
InkDropHostView::InkDropHostView()
: ink_drop_(new InkDropStub()),
ink_drop_size_(kInkDropSize, kInkDropSize),
ink_drop_visible_opacity_(kInkDropVisibleOpacity),
old_paint_to_layer_(false),
destroying_(false) {}
InkDropHostView::~InkDropHostView() {
// TODO(bruthig): Improve InkDropImpl to be safer about calling back to
// potentially destroyed InkDropHosts and remove |destroying_|.
destroying_ = true;
}
void InkDropHostView::AddInkDropLayer(ui::Layer* ink_drop_layer) {
old_paint_to_layer_ = layer() != nullptr;
SetPaintToLayer(true);
layer()->SetFillsBoundsOpaquely(false);
layer()->Add(ink_drop_layer);
layer()->StackAtBottom(ink_drop_layer);
}
void InkDropHostView::RemoveInkDropLayer(ui::Layer* ink_drop_layer) {
// No need to do anything when called during shutdown, and if a derived
// class has overridden Add/RemoveInkDropLayer, running this implementation
// would be wrong.
if (destroying_)
return;
layer()->Remove(ink_drop_layer);
SetPaintToLayer(old_paint_to_layer_);
}
std::unique_ptr<InkDropRipple> InkDropHostView::CreateInkDropRipple() const {
return CreateDefaultInkDropRipple(GetLocalBounds().CenterPoint());
}
std::unique_ptr<InkDropHighlight> InkDropHostView::CreateInkDropHighlight()
const {
return CreateDefaultInkDropHighlight(
gfx::RectF(GetLocalBounds()).CenterPoint());
}
std::unique_ptr<InkDropRipple> InkDropHostView::CreateDefaultInkDropRipple(
const gfx::Point& center_point) const {
std::unique_ptr<InkDropRipple> ripple(new SquareInkDropRipple(
CalculateLargeInkDropSize(ink_drop_size_), kInkDropLargeCornerRadius,
ink_drop_size_, kInkDropSmallCornerRadius, center_point,
GetInkDropBaseColor(), ink_drop_visible_opacity()));
return ripple;
}
std::unique_ptr<InkDropHighlight>
InkDropHostView::CreateDefaultInkDropHighlight(
const gfx::PointF& center_point) const {
std::unique_ptr<InkDropHighlight> highlight(
new InkDropHighlight(ink_drop_size_, kInkDropSmallCornerRadius,
center_point, GetInkDropBaseColor()));
highlight->set_explode_size(CalculateLargeInkDropSize(ink_drop_size_));
return highlight;
}
gfx::Point InkDropHostView::GetInkDropCenterBasedOnLastEvent() const {
return last_ripple_triggering_event_
? last_ripple_triggering_event_->location()
: GetLocalBounds().CenterPoint();
}
void InkDropHostView::AnimateInkDrop(InkDropState state,
const ui::LocatedEvent* event) {
#if defined(OS_WIN)
// On Windows, don't initiate ink-drops for touch/gesture events.
// Additionally, certain event states should dismiss existing ink-drop
// animations. If the state is already other than HIDDEN, presumably from
// a mouse or keyboard event, then the state should be allowed. Conversely,
// if the requested state is ACTIVATED, then it should always be allowed.
if (event && (event->IsTouchEvent() || event->IsGestureEvent()) &&
ink_drop_->GetTargetInkDropState() == InkDropState::HIDDEN &&
state != InkDropState::ACTIVATED)
return;
#endif
last_ripple_triggering_event_.reset(
event ? ui::Event::Clone(*event).release()->AsLocatedEvent() : nullptr);
ink_drop_->AnimateToState(state);
}
void InkDropHostView::VisibilityChanged(View* starting_from, bool is_visible) {
View::VisibilityChanged(starting_from, is_visible);
if (GetWidget() && !is_visible) {
ink_drop()->AnimateToState(InkDropState::HIDDEN);
ink_drop()->SetHovered(false);
}
}
void InkDropHostView::OnFocus() {
views::View::OnFocus();
if (ShouldShowInkDropForFocus())
ink_drop()->SetFocused(true);
}
void InkDropHostView::OnBlur() {
views::View::OnBlur();
if (ShouldShowInkDropForFocus())
ink_drop()->SetFocused(false);
}
void InkDropHostView::OnMouseEvent(ui::MouseEvent* event) {
switch (event->type()) {
case ui::ET_MOUSE_ENTERED:
ink_drop_->SetHovered(true);
break;
case ui::ET_MOUSE_EXITED:
ink_drop_->SetHovered(false);
break;
default:
break;
}
View::OnMouseEvent(event);
}
SkColor InkDropHostView::GetInkDropBaseColor() const {
NOTREACHED();
return gfx::kPlaceholderColor;
}
bool InkDropHostView::ShouldShowInkDropForFocus() const {
return false;
}
void InkDropHostView::SetInkDropMode(InkDropMode ink_drop_mode) {
if (ink_drop_mode == InkDropMode::OFF)
ink_drop_.reset(new InkDropStub());
else
ink_drop_.reset(new InkDropImpl(this));
if (ink_drop_mode == InkDropMode::ON) {
if (gesture_handler_)
gesture_handler_->SetInkDrop(ink_drop_.get());
else
gesture_handler_.reset(new InkDropGestureHandler(this, ink_drop_.get()));
} else {
gesture_handler_.reset();
}
}
} // namespace views