Skip to content

Commit

Permalink
With this CL animated rotations can be triggered by visiting URLs lik…
Browse files Browse the repository at this point in the history
…e about:rotate?right. The about handler fires orientation events that the TouchBrowserFrameView is listening for. When notified, the TBFV either rotates itself, or the views desktop (if it is active). The animation code lives in views/animation and knows nothing about sensors.

BUG=none
TEST=none

Review URL: http://codereview.chromium.org/7273073

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@100148 0039d316-1c4b-4281-b951-d872f2087c98
  • Loading branch information
vollick@chromium.org committed Sep 8, 2011
1 parent f714d7e commit a8f2115
Show file tree
Hide file tree
Showing 26 changed files with 1,014 additions and 36 deletions.
21 changes: 21 additions & 0 deletions chrome/browser/browser_about_handler.cc
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
#include "content/browser/gpu/gpu_process_host.h"
#include "content/browser/renderer_host/render_process_host.h"
#include "content/browser/renderer_host/render_view_host.h"
#include "content/browser/sensors/sensors_provider.h"
#include "content/common/gpu/gpu_messages.h"
#include "crypto/nss_util.h"
#include "googleurl/src/gurl.h"
Expand Down Expand Up @@ -1491,6 +1492,26 @@ bool WillHandleBrowserAboutURL(GURL* url,
} else if (host == chrome::kChromeUIGpuHangHost) {
GpuProcessHost::SendOnIO(
0, content::CAUSE_FOR_GPU_LAUNCH_ABOUT_GPUHANG, new GpuMsg_Hang());
#if defined(OS_CHROMEOS)
} else if (host == chrome::kChromeUIRotateHost) {
sensors::ScreenOrientation change;
std::string query(url->query());
if (query == "left") {
change.upward = sensors::ScreenOrientation::LEFT;
} else if (query == "right") {
change.upward = sensors::ScreenOrientation::RIGHT;
} else if (query == "top") {
change.upward = sensors::ScreenOrientation::TOP;
} else if (query == "bottom") {
change.upward = sensors::ScreenOrientation::BOTTOM;
} else {
NOTREACHED() << "Unknown orientation";
}
sensors::Provider::GetInstance()->ScreenOrientationChanged(change);
// Nothing to communicate to the user, so show a blank page.
host = chrome::kChromeUIBlankHost;
*url = GURL(chrome::kChromeUIBlankHost);
#endif
}

// Initialize any potentially corresponding AboutSource handler.
Expand Down
234 changes: 234 additions & 0 deletions chrome/browser/ui/touch/animation/screen_rotation.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
// Copyright (c) 2011 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 "chrome/browser/ui/touch/animation/screen_rotation.h"

#include "base/debug/trace_event.h"
#include "base/message_loop.h"
#include "base/task.h"
#include "ui/base/animation/slide_animation.h"
#include "ui/gfx/compositor/layer.h"
#include "ui/gfx/interpolated_transform.h"
#include "ui/gfx/rect.h"
#include "ui/gfx/transform.h"
#include "views/paint_lock.h"
#include "views/view.h"
#include "views/widget/widget.h"

namespace {
const int kDefaultTransitionDurationMs = 350;

} // namespace

////////////////////////////////////////////////////////////////////////////////
// ScreenRotationListener public:
//

ScreenRotationListener::~ScreenRotationListener() {
}

////////////////////////////////////////////////////////////////////////////////
// ScreenRotation public:
//

ScreenRotation::ScreenRotation(views::View* view,
ScreenRotationListener* listener,
float old_degrees,
float new_degrees)
: view_(view),
widget_(view->GetWidget()),
listener_(listener),
old_degrees_(old_degrees),
new_degrees_(new_degrees),
last_t_(0.0),
duration_(kDefaultTransitionDurationMs),
animation_started_(false),
animation_stopped_(false) {
DCHECK(view);
DCHECK(listener);

if (!view->layer() || !widget_) {
Finalize();
} else {
// Screen rotations are trigged as a result of a call to SetTransform which
// causes a paint to be scheduled to occur. At this point, the paint has
// been scheduled, but has not yet been started. We will listen for this
// paint to be completed before we start animating.
view->layer()->compositor()->AddObserver(this);
}
}

ScreenRotation::~ScreenRotation() {
if (view_->layer())
view_->layer()->compositor()->RemoveObserver(this);
}

void ScreenRotation::SetTarget(float degrees) {
if (new_degrees_ == degrees)
return;

new_degrees_ = degrees;
Init();
}

void ScreenRotation::Stop() {
animation_.reset();
if (view_->layer()) {
if (!interpolated_transform_.get()) {
// attempt to initialize.
Init();
}
if (interpolated_transform_.get()) {
view_->layer()->SetTransform(interpolated_transform_->Interpolate(1.0));
view_->GetWidget()->SchedulePaintInRect(
view_->GetWidget()->GetClientAreaScreenBounds());
}
}
Finalize();
}

////////////////////////////////////////////////////////////////////////////////
// ScreenRotation private:
//

void ScreenRotation::AnimationProgressed(const ui::Animation* anim) {
TRACE_EVENT0("ScreenRotation", "step");
last_t_ = static_cast<float>(anim->GetCurrentValue());
view_->layer()->SetTransform(interpolated_transform_->Interpolate(last_t_));
widget_->SchedulePaintInRect(widget_->GetClientAreaScreenBounds());
}

void ScreenRotation::AnimationEnded(const ui::Animation* anim) {
TRACE_EVENT_END0("ScreenRotation", "ScreenRotation");
// TODO(vollick) massage matrix so that entries sufficiently close
// to 0, 1, or -1 are clamped to these values. The idea is to fight
// accumulated numeric error due to successive rotations.
ui::Transform xform = view_->layer()->transform();
gfx::Point origin;
xform.TransformPoint(origin);
ui::Transform translation;
translation.SetTranslate(new_origin_.x() - origin.x(),
new_origin_.y() - origin.y());
xform.ConcatTransform(translation);
view_->layer()->SetTransform(xform);
widget_->SchedulePaintInRect(widget_->GetClientAreaScreenBounds());
animation_stopped_ = true;
}

void ScreenRotation::OnCompositingEnded() {
DoPendingWork();
}

void ScreenRotation::Init() {
TRACE_EVENT0("ScreenRotation", "init");

ui::Transform current_transform = view_->layer()->transform();
int degrees = new_degrees_ - old_degrees_;
degrees = NormalizeAngle(degrees);

// No rotation required.
if (degrees == 0)
return;

gfx::Point old_pivot;
gfx::Point new_pivot;
int width = view_->layer()->bounds().width();
int height = view_->layer()->bounds().height();

switch (degrees) {
case 90:
new_origin_ = new_pivot = gfx::Point(width, 0);
new_size_.SetSize(height, width);
break;
case -90:
new_origin_ = new_pivot = gfx::Point(0, height);
new_size_.SetSize(height, width);
break;
case 180:
duration_ = 550;
new_pivot = old_pivot = gfx::Point(width / 2, height / 2);
new_origin_.SetPoint(width, height);
new_size_.SetSize(width, height);
break;
}

// Convert points to world space.
current_transform.TransformPoint(old_pivot);
current_transform.TransformPoint(new_pivot);
current_transform.TransformPoint(new_origin_);

scoped_ptr<ui::InterpolatedTransform> rotation(
new ui::InterpolatedTransformAboutPivot(
old_pivot,
new ui::InterpolatedRotation(0, degrees)));

scoped_ptr<ui::InterpolatedTransform> translation(
new ui::InterpolatedTranslation(
gfx::Point(0, 0),
gfx::Point(new_pivot.x() - old_pivot.x(),
new_pivot.y() - old_pivot.y())));

float scale_factor = 0.9f;
scoped_ptr<ui::InterpolatedTransform> scale_down(
new ui::InterpolatedScale(1.0f, scale_factor, 0.0f, 0.5f));

scoped_ptr<ui::InterpolatedTransform> scale_up(
new ui::InterpolatedScale(1.0f, 1.0f / scale_factor, 0.5f, 1.0f));

scoped_ptr<ui::InterpolatedTransform> transition(
new ui::InterpolatedConstantTransform(current_transform));

scale_up->SetChild(scale_down.release());
translation->SetChild(scale_up.release());
rotation->SetChild(translation.release());
transition->SetChild(rotation.release());

if (interpolated_transform_.get()) {
// We are in the middle of a transition. In this case, we need to create
// an interpolated transform that gets us from where we are to the target
// transform.
ui::Transform target = transition->Interpolate(1.0);
interpolated_transform_.reset(
new ui::InterpolatedTRSTransform(
current_transform, target, last_t_, 1.0));
} else {
interpolated_transform_.reset(transition.release());
}
}

void ScreenRotation::Start() {
TRACE_EVENT_BEGIN0("ScreenRotation", "ScreenRotation");
Init();
if (interpolated_transform_.get()) {
paint_lock_.reset(new views::PaintLock(view_));
animation_.reset(new ui::SlideAnimation(this));
animation_->SetTweenType(ui::Tween::LINEAR);
animation_->SetSlideDuration(duration_);
animation_->Show();
animation_started_ = true;
} else {
Finalize();
}
}

void ScreenRotation::Finalize() {
ui::Transform final_transform = view_->GetTransform();
gfx::Rect final_bounds(0, 0, new_size_.width(), new_size_.height());
listener_->OnScreenRotationCompleted(final_transform, final_bounds);
}

int ScreenRotation::NormalizeAngle(int degrees) {
while (degrees <= -180) degrees += 360;
while (degrees > 180) degrees -= 360;
return degrees;
}

void ScreenRotation::DoPendingWork() {
if (!animation_started_)
Start();
else if (animation_stopped_) {
view_->layer()->compositor()->RemoveObserver(this);
Finalize();
}
}
Loading

0 comments on commit a8f2115

Please sign in to comment.