blob: 28c5043570bd40bb79703e6d10d5fa507bc9f87b [file] [log] [blame]
// Copyright (c) 2012 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/message_center/views/slide_out_controller.h"
#include "ui/compositor/layer.h"
#include "ui/compositor/scoped_layer_animation_settings.h"
#include "ui/gfx/transform.h"
#include "ui/message_center/public/cpp/message_center_constants.h"
namespace message_center {
SlideOutController::SlideOutController(ui::EventTarget* target,
Delegate* delegate)
: target_handling_(target, this), delegate_(delegate) {}
SlideOutController::~SlideOutController() {}
void SlideOutController::CaptureControlOpenState() {
if (!has_swipe_control_)
return;
if (mode_ == SlideMode::FULL &&
fabs(gesture_amount_) >= swipe_control_width_) {
control_open_state_ = gesture_amount_ < 0
? SwipeControlOpenState::OPEN_ON_RIGHT
: SwipeControlOpenState::OPEN_ON_LEFT;
} else {
control_open_state_ = SwipeControlOpenState::CLOSED;
}
}
void SlideOutController::OnGestureEvent(ui::GestureEvent* event) {
ui::Layer* layer = delegate_->GetSlideOutLayer();
int width = layer->bounds().width();
float scroll_amount_for_closing_notification =
has_swipe_control_ ? swipe_control_width_ + kSwipeCloseMargin
: width * 0.5;
if (event->type() == ui::ET_SCROLL_FLING_START) {
// The threshold for the fling velocity is computed empirically.
// The unit is in pixels/second.
const float kFlingThresholdForClose = 800.f;
if (mode_ == SlideMode::FULL &&
fabsf(event->details().velocity_x()) > kFlingThresholdForClose) {
SlideOutAndClose(event->details().velocity_x());
delegate_->OnSlideChanged(false);
event->StopPropagation();
return;
}
CaptureControlOpenState();
RestoreVisualState();
delegate_->OnSlideChanged(false);
return;
}
if (!event->IsScrollGestureEvent())
return;
if (event->type() == ui::ET_GESTURE_SCROLL_BEGIN) {
switch (control_open_state_) {
case SwipeControlOpenState::CLOSED:
gesture_amount_ = 0.f;
break;
case SwipeControlOpenState::OPEN_ON_RIGHT:
gesture_amount_ = -swipe_control_width_;
break;
case SwipeControlOpenState::OPEN_ON_LEFT:
gesture_amount_ = swipe_control_width_;
break;
default:
NOTREACHED();
}
delegate_->OnSlideStarted();
} else if (event->type() == ui::ET_GESTURE_SCROLL_UPDATE) {
// The scroll-update events include the incremental scroll amount.
gesture_amount_ += event->details().scroll_x();
float scroll_amount;
float opacity;
switch (mode_) {
case SlideMode::FULL:
scroll_amount = gesture_amount_;
opacity = 1.f - std::min(fabsf(scroll_amount) / width, 1.f);
break;
case SlideMode::NO_SLIDE:
scroll_amount = 0.f;
opacity = 1.f;
break;
case SlideMode::PARTIALLY:
if (gesture_amount_ >= 0) {
scroll_amount = std::min(0.5f * gesture_amount_,
scroll_amount_for_closing_notification);
} else {
scroll_amount =
std::max(0.5f * gesture_amount_,
-1.f * scroll_amount_for_closing_notification);
}
opacity = 1.f;
break;
}
SetOpacityIfNecessary(opacity);
gfx::Transform transform;
transform.Translate(scroll_amount, 0.0);
layer->SetTransform(transform);
delegate_->OnSlideChanged(true);
} else if (event->type() == ui::ET_GESTURE_SCROLL_END) {
float scrolled_ratio = fabsf(gesture_amount_) / width;
if (mode_ == SlideMode::FULL &&
scrolled_ratio >= scroll_amount_for_closing_notification / width) {
SlideOutAndClose(gesture_amount_);
delegate_->OnSlideChanged(false);
event->StopPropagation();
return;
}
CaptureControlOpenState();
RestoreVisualState();
delegate_->OnSlideChanged(false);
}
event->SetHandled();
}
void SlideOutController::RestoreVisualState() {
ui::Layer* layer = delegate_->GetSlideOutLayer();
// Restore the layer state.
const int kSwipeRestoreDurationMS = 150;
ui::ScopedLayerAnimationSettings settings(layer->GetAnimator());
settings.SetTransitionDuration(
base::TimeDelta::FromMilliseconds(kSwipeRestoreDurationMS));
settings.AddObserver(this);
gfx::Transform transform;
switch (control_open_state_) {
case SwipeControlOpenState::CLOSED:
gesture_amount_ = 0.f;
break;
case SwipeControlOpenState::OPEN_ON_RIGHT:
transform.Translate(-swipe_control_width_, 0);
break;
case SwipeControlOpenState::OPEN_ON_LEFT:
transform.Translate(swipe_control_width_, 0);
break;
}
if (layer->transform() == transform && opacity_ == 1.f) {
// Nothing are changed and no animation starts.
return;
}
// In this case, animation starts. OnImplicitAnimationsCompleted will be
// called just after the animation finishes.
layer->SetTransform(transform);
SetOpacityIfNecessary(1.f);
delegate_->OnSlideChanged(true);
}
void SlideOutController::SlideOutAndClose(int direction) {
ui::Layer* layer = delegate_->GetSlideOutLayer();
const int kSwipeOutTotalDurationMS = 150;
int swipe_out_duration = kSwipeOutTotalDurationMS * opacity_;
ui::ScopedLayerAnimationSettings settings(layer->GetAnimator());
settings.SetTransitionDuration(
base::TimeDelta::FromMilliseconds(swipe_out_duration));
settings.AddObserver(this);
gfx::Transform transform;
int width = layer->bounds().width();
transform.Translate(direction < 0 ? -width : width, 0.0);
// An animation starts. OnImplicitAnimationsCompleted will be called just
// after the animation finishes.
layer->SetTransform(transform);
SetOpacityIfNecessary(0.f);
delegate_->OnSlideChanged(true);
}
void SlideOutController::SetOpacityIfNecessary(float opacity) {
if (update_opacity_)
delegate_->GetSlideOutLayer()->SetOpacity(opacity);
opacity_ = opacity;
}
void SlideOutController::OnImplicitAnimationsCompleted() {
if (opacity_ > 0)
return;
// Call Delegate::OnSlideOut() if this animation came from SlideOutAndClose().
// OnImplicitAnimationsCompleted is called from BeginMainFrame, so we should
// delay operation that might result in deletion of LayerTreeHost.
// https://crbug.com/895883
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(&Delegate::OnSlideOut, base::Unretained(delegate_)));
}
void SlideOutController::SetSwipeControlWidth(int swipe_control_width) {
swipe_control_width_ = swipe_control_width;
has_swipe_control_ = (swipe_control_width != 0);
}
void SlideOutController::CloseSwipeControl() {
if (!has_swipe_control_)
return;
gesture_amount_ = 0;
CaptureControlOpenState();
RestoreVisualState();
}
} // namespace message_center