diff options
author | sadrul@chromium.org <sadrul@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-02-15 22:53:19 +0000 |
---|---|---|
committer | sadrul@chromium.org <sadrul@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-02-15 22:53:19 +0000 |
commit | eb93b619bebd38510f21d3f04d8f2641bf71af35 (patch) | |
tree | 8ddd55e7baddb7588074308b6ad6fc7504e7b25f /views | |
parent | 85c6e92ecdd8a882377a460f8f2cac1b4ef69c51 (diff) | |
download | chromium_src-eb93b619bebd38510f21d3f04d8f2641bf71af35.zip chromium_src-eb93b619bebd38510f21d3f04d8f2641bf71af35.tar.gz chromium_src-eb93b619bebd38510f21d3f04d8f2641bf71af35.tar.bz2 |
views transformation: First cut.
This is a first cut at the transformable views. Skia transformation matrices are used to apply transformation (rotate, scale, translation). Support for clipping is also added (but not through the matrix). Currently, the transformation is applied only for painting. A future CL will apply the transformation for various events.
BUG=none
TEST=ViewTest.TransformPaint
Review URL: http://codereview.chromium.org/6500008
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@75023 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'views')
-rw-r--r-- | views/view.cc | 80 | ||||
-rw-r--r-- | views/view.h | 57 | ||||
-rw-r--r-- | views/view_unittest.cc | 43 | ||||
-rw-r--r-- | views/widget/root_view.cc | 5 |
4 files changed, 179 insertions, 6 deletions
diff --git a/views/view.cc b/views/view.cc index 94ce088..6ab44bf 100644 --- a/views/view.cc +++ b/views/view.cc @@ -10,10 +10,12 @@ #include "base/message_loop.h" #include "base/scoped_ptr.h" #include "base/utf_string_conversions.h" -#include "third_party/skia/include/core/SkShader.h" +#include "third_party/skia/include/core/SkMatrix.h" +#include "third_party/skia/include/core/SkRect.h" #include "ui/base/dragdrop/drag_drop_types.h" #include "ui/gfx/canvas_skia.h" #include "ui/gfx/path.h" +#include "ui/gfx/skia_util.h" #include "views/background.h" #include "views/layout/layout_manager.h" #include "views/views_delegate.h" @@ -63,6 +65,8 @@ View::View() is_visible_(true), notify_when_visible_bounds_in_root_changes_(false), registered_for_visible_bounds_notification_(false), + clip_x_(0), + clip_y_(0), needs_layout_(true), flip_canvas_on_paint_for_rtl_ui_(false), accelerator_registration_delayed_(false), @@ -364,6 +368,50 @@ bool View::IsEnabled() const { return enabled_; } +// Transformations ------------------------------------------------------------- + +void View::SetRotation(double degree) { + InitTransform(); + transform_->setRotate(SkDoubleToScalar(degree), + SkIntToScalar(0), SkIntToScalar(0)); +} + +void View::SetScaleX(double x) { + InitTransform(); + transform_->setScaleX(SkDoubleToScalar(x)); +} + +void View::SetScaleY(double y) { + InitTransform(); + transform_->setScaleY(SkDoubleToScalar(y)); +} + +void View::SetScale(double x, double y) { + InitTransform(); + transform_->setScale(SkDoubleToScalar(x), SkDoubleToScalar(y)); +} + +void View::SetTranslateX(int x) { + InitTransform(); + transform_->setTranslateX(SkIntToScalar(x)); +} + +void View::SetTranslateY(int y) { + InitTransform(); + transform_->setTranslateY(SkIntToScalar(y)); +} + +void View::SetTranslate(int x, int y) { + InitTransform(); + transform_->setTranslate(SkIntToScalar(x), SkIntToScalar(y)); +} + +void View::ResetTransform() { + transform_.reset(NULL); + clip_x_ = clip_y_ = 0; +} + + // RTL positioning ------------------------------------------------------------- gfx::Rect View::GetMirroredBounds() const { @@ -549,6 +597,19 @@ void View::ConvertPointToScreen(const View* src, gfx::Point* p) { } } +gfx::Rect View::ConvertRectToParent(const gfx::Rect& rect) const { + if (!transform_.get() || transform_->isIdentity()) + return rect; + SkRect src = gfx::RectToSkRect(rect); + if (!transform_->mapRect(&src)) + return rect; + gfx::Rect ret(static_cast<int>(SkScalarToDouble(src.fLeft)), + static_cast<int>(SkScalarToDouble(src.fTop)), + static_cast<int>(SkScalarToDouble(src.width())), + static_cast<int>(SkScalarToDouble(src.height()))); + return ret; +} + // Painting -------------------------------------------------------------------- void View::SchedulePaint() { @@ -562,7 +623,7 @@ void View::SchedulePaintInRect(const gfx::Rect& r, bool urgent) { if (parent_) { // Translate the requested paint rect to the parent's coordinate system // then pass this notification up to the parent. - gfx::Rect paint_rect = r; + gfx::Rect paint_rect = ConvertRectToParent(r); paint_rect.Offset(GetMirroredPosition()); parent_->SchedulePaintInRect(paint_rect, urgent); } @@ -611,11 +672,15 @@ void View::ProcessPaint(gfx::Canvas* canvas) { // Note that the X (or left) position we pass to ClipRectInt takes into // consideration whether or not the view uses a right-to-left layout so that // we paint our view in its mirrored position if need be. - if (canvas->ClipRectInt(GetMirroredX(), y(), width(), height())) { + if (canvas->ClipRectInt(GetMirroredX(), y(), + width() - clip_x_, height() - clip_y_)) { // Non-empty clip, translate the graphics such that 0,0 corresponds to // where this view is located (related to its parent). canvas->TranslateInt(GetMirroredX(), y()); + if (transform_.get()) + canvas->AsCanvasSkia()->concat(*transform_.get()); + // If the View we are about to paint requested the canvas to be flipped, we // should change the transform appropriately. canvas->Save(); @@ -1263,6 +1328,15 @@ void View::RemoveDescendantToNotify(View* view) { descendants_to_notify_.reset(); } +// Transformations ------------------------------------------------------------- + +void View::InitTransform() { + if (!transform_.get()) { + transform_.reset(new SkMatrix); + transform_->reset(); + } +} + // Coordinate conversion ------------------------------------------------------- // static diff --git a/views/view.h b/views/view.h index 69c1d2c..a9155b5 100644 --- a/views/view.h +++ b/views/view.h @@ -26,6 +26,8 @@ using ui::OSExchangeData; +// TODO(sad): Use platform independent wrapper for transform matrix. +class SkMatrix; class ViewAccessibility; namespace gfx { @@ -248,6 +250,10 @@ class View : public AcceleratorTarget { // Position is in the coordinate system of the view's parent. // Position is NOT flipped for RTL. See "RTL positioning" for RTL-sensitive // position accessors. + // Transformations are not applied on the size/position. For example, if + // bounds is (0, 0, 100, 100) and it is scaled by 0.5 along the X axis, the + // width will still be 100 (although when painted, it will be 50x50, painted + // at location (0, 0)). void SetBounds(int x, int y, int width, int height); void SetBoundsRect(const gfx::Rect& bounds); @@ -259,6 +265,7 @@ class View : public AcceleratorTarget { // Override to be notified when the bounds of the view have changed. virtual void OnBoundsChanged(); + // No transformation is applied on the size or the locations. const gfx::Rect& bounds() const { return bounds_; } int x() const { return bounds_.x(); } int y() const { return bounds_.y(); } @@ -329,6 +336,33 @@ class View : public AcceleratorTarget { // Returns whether the view is enabled. virtual bool IsEnabled() const; + // Transformations ----------------------------------------------------------- + + // Methods for setting transformations for a view (e.g. rotation, scaling). + + const SkMatrix* transform() { return transform_.get(); } + + // Clipping parameters. Clipping happens from the right and/or bottom. The + // clipping amount is in parent's coordinate system, as in, if the view is + // rotated, then the clipping will be applied after the rotation (and other + // transformations, if any). + void set_clip_x(int x) { clip_x_ = x; } + void set_clip_y(int y) { clip_y_ = y; } + void set_clip(int x, int y) { clip_x_ = x; clip_y_ = y; } + + void SetRotation(double degree); + + void SetScaleX(double x); + void SetScaleY(double y); + void SetScale(double x, double y); + + void SetTranslateX(int x); + void SetTranslateY(int y); + void SetTranslate(int x, int y); + + // Reset the transformation matrix. + void ResetTransform(); + // RTL positioning ----------------------------------------------------------- // Methods for accessing the bounds and position of the view, relative to its @@ -472,13 +506,19 @@ class View : public AcceleratorTarget { // screen. This is useful for example when placing popup windows. static void ConvertPointToScreen(const View* src, gfx::Point* point); + // Applies transformation on the rectangle, which is in the view's coordinate + // system, to convert it into the parent's coordinate system. + gfx::Rect ConvertRectToParent(const gfx::Rect& rect) const; + // Painting ------------------------------------------------------------------ // Mark all or part of the View's bounds as dirty (needing repaint). // |r| is in the View's coordinates. // When |urgent| is true, the view will be repainted when the current event // processing is done. Otherwise, painting will take place as soon as - // possible. + // possible. Rectangle |r| should be in the view's coordinate system. The + // transformations are applied to it to convert it into the parent coordinate + // system before propagating SchedulePaint up the view hierarchy. virtual void SchedulePaint(); virtual void SchedulePaintInRect(const gfx::Rect& r, bool urgent); @@ -1212,6 +1252,11 @@ class View : public AcceleratorTarget { void AddDescendantToNotify(View* view); void RemoveDescendantToNotify(View* view); + // Transformations ----------------------------------------------------------- + + // Initialize the transform matrix when necessary. + void InitTransform(); + // Coordinate conersion ------------------------------------------------------ // This is the actual implementation for ConvertPointToView() @@ -1318,6 +1363,16 @@ class View : public AcceleratorTarget { // List of descendants wanting notification when their visible bounds change. scoped_ptr<ViewVector> descendants_to_notify_; + // Transformations ----------------------------------------------------------- + + // The transformation matrix (rotation, translate, scale). + scoped_ptr<SkMatrix> transform_; + + // Clipping parameters. skia transformation matrix does not give us clipping. + // So we do it ourselves. + int clip_x_; + int clip_y_; + // Layout -------------------------------------------------------------------- // Whether the view needs to be laid out. diff --git a/views/view_unittest.cc b/views/view_unittest.cc index 8733ffe..a8ee7b6 100644 --- a/views/view_unittest.cc +++ b/views/view_unittest.cc @@ -1514,3 +1514,46 @@ TEST_F(ViewTest, ChangeNativeViewHierarchyChangeHierarchy) { test.CheckChangingHierarhy(); #endif } + +//////////////////////////////////////////////////////////////////////////////// +// Transformations +//////////////////////////////////////////////////////////////////////////////// +TEST_F(ViewTest, TransformPaint) { + TestView* v1 = new TestView(); + v1->SetBounds(0, 0, 500, 300); + + TestView* v2 = new TestView(); + v2->SetBounds(100, 100, 200, 100); + + Widget* widget = CreateWidget(); +#if defined(OS_WIN) + WidgetWin* window_win = static_cast<WidgetWin*>(widget); + window_win->set_window_style(WS_OVERLAPPEDWINDOW); + window_win->Init(NULL, gfx::Rect(50, 50, 650, 650)); +#endif + RootView* root = widget->GetRootView(); + + root->AddChildView(v1); + v1->AddChildView(v2); + + // At this moment, |v2| occupies (100, 100) to (300, 200) in |root|. + root->ClearPaintRect(); + v2->SchedulePaint(); + + gfx::Rect rect = root->GetScheduledPaintRect(); + EXPECT_EQ(gfx::Rect(100, 100, 200, 100), rect); + + // Rotate |v1| counter-clockwise. + v1->SetRotation(-90.0); + v1->SetTranslateY(500); + + // |v2| now occupies (100, 200) to (200, 400) in |root|. + + root->ClearPaintRect(); + v2->SchedulePaint(); + + rect = root->GetScheduledPaintRect(); + EXPECT_EQ(gfx::Rect(100, 200, 100, 200), rect); + + widget->CloseNow(); +} diff --git a/views/widget/root_view.cc b/views/widget/root_view.cc index b03338e..25ede0a 100644 --- a/views/widget/root_view.cc +++ b/views/widget/root_view.cc @@ -338,13 +338,14 @@ View* RootView::GetFocusTraversableParentView() { // RootView, View overrides: void RootView::SchedulePaintInRect(const gfx::Rect& r, bool urgent) { + gfx::Rect xrect = ConvertRectToParent(r); // If there is an existing invalid rect, add the union of the scheduled // rect with the invalid rect. This could be optimized further if // necessary. if (invalid_rect_.IsEmpty()) - invalid_rect_ = r; + invalid_rect_ = xrect; else - invalid_rect_ = invalid_rect_.Union(r); + invalid_rect_ = invalid_rect_.Union(xrect); if (urgent || invalid_rect_urgent_) { invalid_rect_urgent_ = true; |