blob: 2006d98fddb44a0779c850a7be0c32edb12c5d1c [file] [log] [blame]
// Copyright 2013 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 "cc/input/scrollbar_animation_controller_thinning.h"
#include "base/memory/ptr_util.h"
#include "base/time/time.h"
#include "cc/layers/layer_impl.h"
#include "cc/layers/scrollbar_layer_impl_base.h"
#include "cc/trees/layer_tree_impl.h"
namespace {
const float kIdleThicknessScale = 0.4f;
const float kDefaultMouseMoveDistanceToTriggerAnimation = 25.f;
}
namespace cc {
std::unique_ptr<ScrollbarAnimationControllerThinning>
ScrollbarAnimationControllerThinning::Create(
int scroll_layer_id,
ScrollbarAnimationControllerClient* client,
base::TimeDelta delay_before_starting,
base::TimeDelta resize_delay_before_starting,
base::TimeDelta fade_duration,
base::TimeDelta thinning_duration) {
return base::WrapUnique(new ScrollbarAnimationControllerThinning(
scroll_layer_id, client, delay_before_starting,
resize_delay_before_starting, fade_duration, thinning_duration));
}
ScrollbarAnimationControllerThinning::ScrollbarAnimationControllerThinning(
int scroll_layer_id,
ScrollbarAnimationControllerClient* client,
base::TimeDelta delay_before_starting,
base::TimeDelta resize_delay_before_starting,
base::TimeDelta fade_duration,
base::TimeDelta thinning_duration)
: ScrollbarAnimationController(scroll_layer_id,
client,
delay_before_starting,
resize_delay_before_starting),
opacity_(0.0f),
captured_(false),
mouse_is_over_scrollbar_(false),
mouse_is_near_scrollbar_(false),
thickness_change_(NONE),
mouse_move_distance_to_trigger_animation_(
kDefaultMouseMoveDistanceToTriggerAnimation),
fade_duration_(fade_duration),
thinning_duration_(thinning_duration),
current_animating_property_(OPACITY) {
ApplyOpacity(0.f);
ApplyThumbThicknessScale(kIdleThicknessScale);
}
ScrollbarAnimationControllerThinning::~ScrollbarAnimationControllerThinning() {}
void ScrollbarAnimationControllerThinning::RunAnimationFrame(float progress) {
if (captured_)
return;
if (current_animating_property_ == OPACITY)
ApplyOpacity(1.f - progress);
else
ApplyThumbThicknessScale(ThumbThicknessScaleAt(progress));
client_->SetNeedsRedrawForScrollbarAnimation();
if (progress == 1.f) {
StopAnimation();
if (current_animating_property_ == THICKNESS) {
thickness_change_ = NONE;
SetCurrentAnimatingProperty(OPACITY);
PostDelayedAnimationTask(false);
}
}
}
const base::TimeDelta& ScrollbarAnimationControllerThinning::Duration() {
if (current_animating_property_ == OPACITY)
return fade_duration_;
else
return thinning_duration_;
}
void ScrollbarAnimationControllerThinning::DidMouseDown() {
if (!mouse_is_over_scrollbar_ || opacity_ == 0.0f)
return;
StopAnimation();
captured_ = true;
ApplyOpacity(1.f);
ApplyThumbThicknessScale(1.f);
}
void ScrollbarAnimationControllerThinning::DidMouseUp() {
if (!captured_ || opacity_ == 0.0f)
return;
captured_ = false;
StopAnimation();
if (!mouse_is_near_scrollbar_) {
SetCurrentAnimatingProperty(THICKNESS);
thickness_change_ = DECREASE;
StartAnimation();
} else {
SetCurrentAnimatingProperty(OPACITY);
PostDelayedAnimationTask(false);
}
}
void ScrollbarAnimationControllerThinning::DidMouseLeave() {
if (!mouse_is_over_scrollbar_ && !mouse_is_near_scrollbar_)
return;
mouse_is_over_scrollbar_ = false;
mouse_is_near_scrollbar_ = false;
if (captured_ || opacity_ == 0.0f)
return;
thickness_change_ = DECREASE;
SetCurrentAnimatingProperty(THICKNESS);
StartAnimation();
}
void ScrollbarAnimationControllerThinning::DidScrollUpdate(bool on_resize) {
if (captured_)
return;
ScrollbarAnimationController::DidScrollUpdate(on_resize);
ApplyOpacity(1.f);
ApplyThumbThicknessScale(mouse_is_near_scrollbar_ ? 1.f
: kIdleThicknessScale);
SetCurrentAnimatingProperty(OPACITY);
}
void ScrollbarAnimationControllerThinning::DidMouseMoveNear(float distance) {
bool mouse_is_over_scrollbar = distance == 0.0f;
bool mouse_is_near_scrollbar =
distance < mouse_move_distance_to_trigger_animation_;
if (captured_ || opacity_ == 0.0f) {
mouse_is_near_scrollbar_ = mouse_is_near_scrollbar;
mouse_is_over_scrollbar_ = mouse_is_over_scrollbar;
return;
}
if (mouse_is_over_scrollbar == mouse_is_over_scrollbar_ &&
mouse_is_near_scrollbar == mouse_is_near_scrollbar_)
return;
if (mouse_is_over_scrollbar_ != mouse_is_over_scrollbar)
mouse_is_over_scrollbar_ = mouse_is_over_scrollbar;
if (mouse_is_near_scrollbar_ != mouse_is_near_scrollbar) {
mouse_is_near_scrollbar_ = mouse_is_near_scrollbar;
thickness_change_ = mouse_is_near_scrollbar_ ? INCREASE : DECREASE;
}
SetCurrentAnimatingProperty(THICKNESS);
StartAnimation();
}
bool ScrollbarAnimationControllerThinning::ScrollbarsHidden() const {
return opacity_ == 0.0f;
}
float ScrollbarAnimationControllerThinning::ThumbThicknessScaleAt(
float progress) {
if (thickness_change_ == NONE)
return mouse_is_near_scrollbar_ ? 1.f : kIdleThicknessScale;
float factor = thickness_change_ == INCREASE ? progress : (1.f - progress);
return ((1.f - kIdleThicknessScale) * factor) + kIdleThicknessScale;
}
float ScrollbarAnimationControllerThinning::AdjustScale(
float new_value,
float current_value,
AnimationChange animation_change,
float min_value,
float max_value) {
float result;
if (animation_change == INCREASE && current_value > new_value)
result = current_value;
else if (animation_change == DECREASE && current_value < new_value)
result = current_value;
else
result = new_value;
if (result > max_value)
return max_value;
if (result < min_value)
return min_value;
return result;
}
void ScrollbarAnimationControllerThinning::ApplyOpacity(float opacity) {
for (ScrollbarLayerImplBase* scrollbar : Scrollbars()) {
if (!scrollbar->is_overlay_scrollbar())
continue;
float effective_opacity = scrollbar->CanScrollOrientation() ? opacity : 0;
PropertyTrees* property_trees =
scrollbar->layer_tree_impl()->property_trees();
// If this method is called during LayerImpl::PushPropertiesTo, we may not
// yet have valid effect_id_to_index_map entries as property trees are
// pushed after layers during activation. We can skip updating opacity in
// that case as we are only registering a scrollbar and because opacity will
// be overwritten anyway when property trees are pushed.
if (property_trees->IsInIdToIndexMap(PropertyTrees::TreeType::EFFECT,
scrollbar->id())) {
property_trees->effect_tree.OnOpacityAnimated(
effective_opacity,
property_trees->effect_id_to_index_map[scrollbar->id()],
scrollbar->layer_tree_impl());
}
}
bool previouslyVisible = opacity_ > 0.0f;
bool currentlyVisible = opacity > 0.0f;
opacity_ = opacity;
if (previouslyVisible != currentlyVisible)
client_->DidChangeScrollbarVisibility();
}
void ScrollbarAnimationControllerThinning::ApplyThumbThicknessScale(
float thumb_thickness_scale) {
for (ScrollbarLayerImplBase* scrollbar : Scrollbars()) {
if (!scrollbar->is_overlay_scrollbar())
continue;
scrollbar->SetThumbThicknessScaleFactor(AdjustScale(
thumb_thickness_scale, scrollbar->thumb_thickness_scale_factor(),
thickness_change_, kIdleThicknessScale, 1));
}
}
void ScrollbarAnimationControllerThinning::SetCurrentAnimatingProperty(
AnimatingProperty property) {
if (current_animating_property_ == property)
return;
StopAnimation();
current_animating_property_ = property;
if (current_animating_property_ == THICKNESS)
ApplyOpacity(1.f);
}
} // namespace cc