diff options
-rw-r--r-- | ui/gfx/canvas.h | 9 | ||||
-rw-r--r-- | ui/gfx/canvas_direct2d.cc | 6 | ||||
-rw-r--r-- | ui/gfx/canvas_direct2d.h | 3 | ||||
-rw-r--r-- | ui/gfx/canvas_skia.cc | 7 | ||||
-rw-r--r-- | ui/gfx/canvas_skia.h | 3 | ||||
-rw-r--r-- | ui/gfx/gfx.gyp | 3 | ||||
-rw-r--r-- | ui/gfx/transform.h | 66 | ||||
-rw-r--r-- | ui/gfx/transform_skia.cc | 102 | ||||
-rw-r--r-- | ui/gfx/transform_skia.h | 54 | ||||
-rw-r--r-- | views/events/event.cc | 10 | ||||
-rw-r--r-- | views/events/event.h | 26 | ||||
-rw-r--r-- | views/touchui/gesture_manager.cc | 16 | ||||
-rw-r--r-- | views/view.cc | 180 | ||||
-rw-r--r-- | views/view.h | 65 | ||||
-rw-r--r-- | views/view_unittest.cc | 127 | ||||
-rw-r--r-- | views/widget/root_view.cc | 20 | ||||
-rw-r--r-- | views/widget/root_view.h | 8 |
17 files changed, 606 insertions, 99 deletions
diff --git a/ui/gfx/canvas.h b/ui/gfx/canvas.h index 8c89d59..0149486 100644 --- a/ui/gfx/canvas.h +++ b/ui/gfx/canvas.h @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// 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. @@ -13,6 +13,10 @@ #include "skia/ext/platform_canvas.h" #include "ui/gfx/native_widget_types.h" +namespace ui { +class Transform; +} + namespace gfx { class Brush; @@ -208,6 +212,9 @@ class Canvas { // returned by BeginPlatformPaint(). virtual void EndPlatformPaint() = 0; + // Apply transformation on the canvas. + virtual void Transform(const ui::Transform& transform) = 0; + // TODO(beng): remove this once we don't need to use any skia-specific methods // through this interface. // A quick and dirty way to obtain the underlying SkCanvas. diff --git a/ui/gfx/canvas_direct2d.cc b/ui/gfx/canvas_direct2d.cc index 61d3403..63e9bb9 100644 --- a/ui/gfx/canvas_direct2d.cc +++ b/ui/gfx/canvas_direct2d.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// 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. @@ -341,6 +341,10 @@ void CanvasDirect2D::EndPlatformPaint() { interop_rt_.release(); } +void CanvasDirect2D::Transform(const ui::Transform& transform) { + NOTIMPLEMENTED(); +} + CanvasSkia* CanvasDirect2D::AsCanvasSkia() { return NULL; } diff --git a/ui/gfx/canvas_direct2d.h b/ui/gfx/canvas_direct2d.h index d87fdf3..45452f5 100644 --- a/ui/gfx/canvas_direct2d.h +++ b/ui/gfx/canvas_direct2d.h @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// 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. @@ -77,6 +77,7 @@ class CanvasDirect2D : public Canvas { int dest_x, int dest_y, int w, int h); virtual gfx::NativeDrawingContext BeginPlatformPaint(); virtual void EndPlatformPaint(); + virtual void Transform(const ui::Transform& transform); virtual CanvasSkia* AsCanvasSkia(); virtual const CanvasSkia* AsCanvasSkia() const; diff --git a/ui/gfx/canvas_skia.cc b/ui/gfx/canvas_skia.cc index 99174d1..e61f0f2 100644 --- a/ui/gfx/canvas_skia.cc +++ b/ui/gfx/canvas_skia.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// 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. @@ -12,6 +12,7 @@ #include "ui/gfx/brush.h" #include "ui/gfx/font.h" #include "ui/gfx/rect.h" +#include "ui/gfx/transform_skia.h" #if defined(OS_WIN) #include "ui/gfx/canvas_skia_paint.h" @@ -330,6 +331,10 @@ void CanvasSkia::EndPlatformPaint() { endPlatformPaint(); } +void CanvasSkia::Transform(const ui::Transform& transform) { + concat(*reinterpret_cast<const ui::TransformSkia&>(transform).matrix_.get()); +} + CanvasSkia* CanvasSkia::AsCanvasSkia() { return this; } diff --git a/ui/gfx/canvas_skia.h b/ui/gfx/canvas_skia.h index be44687..854ce84 100644 --- a/ui/gfx/canvas_skia.h +++ b/ui/gfx/canvas_skia.h @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// 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. @@ -139,6 +139,7 @@ class CanvasSkia : public skia::PlatformCanvas, int dest_x, int dest_y, int w, int h); virtual gfx::NativeDrawingContext BeginPlatformPaint(); virtual void EndPlatformPaint(); + virtual void Transform(const ui::Transform& transform); virtual CanvasSkia* AsCanvasSkia(); virtual const CanvasSkia* AsCanvasSkia() const; diff --git a/ui/gfx/gfx.gyp b/ui/gfx/gfx.gyp index 12d368d..d72e24b 100644 --- a/ui/gfx/gfx.gyp +++ b/ui/gfx/gfx.gyp @@ -147,6 +147,9 @@ 'skia_util.h', 'skia_utils_gtk.cc', 'skia_utils_gtk.h', + 'transform.h', + 'transform_skia.cc', + 'transform_skia.h', ], 'conditions': [ ['OS=="win"', { diff --git a/ui/gfx/transform.h b/ui/gfx/transform.h new file mode 100644 index 0000000..d8f6899 --- /dev/null +++ b/ui/gfx/transform.h @@ -0,0 +1,66 @@ +// 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. + +#ifndef UI_GFX_TRANSFORM_H_ +#define UI_GFX_TRANSFORM_H_ +#pragma once + +namespace gfx { +class Point; +class Rect; +} + +namespace ui { + +// Transformation interface. +// Classes implement this interface to apply transformations (e.g. rotation, +// scaling etc.) on UI components. +class Transform { + public: + // Create an object that implements this interface (e.g. using skia + // transformation matrices). + static Transform* Create(); + + // Set the rotation of the transformation. + virtual void SetRotate(float degree) = 0; + + // Set the scaling parameters. + virtual void SetScaleX(float x) = 0; + virtual void SetScaleY(float y) = 0; + virtual void SetScale(float x, float y) = 0; + + // Set the translation parameters. + virtual void SetTranslateX(float x) = 0; + virtual void SetTranslateY(float y) = 0; + virtual void SetTranslate(float x, float y) = 0; + + // Apply rotation on the current transformation. + virtual void ConcatRotate(float degree) = 0; + + // Apply scaling on current transform. + virtual void ConcatScale(float x, float y) = 0; + + // Apply translation on current transform. + virtual void ConcatTranslate(float x, float y) = 0; + + // Apply a transformation on the current transformation + // (i.e. 'this = this * transform;') + virtual bool ConcatTransform(const Transform& transform) = 0; + + // Does the transformation change anything? + virtual bool HasChange() const = 0; + + // Apply the transformation on the point. + virtual bool TransformPoint(gfx::Point* point) = 0; + + // Apply the reverse transformation on the point. + virtual bool TransformPointReverse(gfx::Point* point) = 0; + + // Apply transformatino on the rectangle. + virtual bool TransformRect(gfx::Rect* rect) = 0; +}; + +} // namespace ui + +#endif // UI_GFX_TRANSFORM_H_ diff --git a/ui/gfx/transform_skia.cc b/ui/gfx/transform_skia.cc new file mode 100644 index 0000000..50a7dcd --- /dev/null +++ b/ui/gfx/transform_skia.cc @@ -0,0 +1,102 @@ +// 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 "ui/gfx/transform_skia.h" + +#include "third_party/skia/include/core/SkMatrix.h" +#include "ui/gfx/canvas_skia.h" +#include "ui/gfx/point.h" +#include "ui/gfx/rect.h" +#include "ui/gfx/skia_util.h" + +namespace ui { + +// static +Transform* Transform::Create() { + return new TransformSkia(); +} + +TransformSkia::TransformSkia() { + matrix_.reset(new SkMatrix); + matrix_->reset(); +} + +void TransformSkia::SetRotate(float degree) { + matrix_->setRotate(SkFloatToScalar(degree)); +} + +void TransformSkia::SetScaleX(float x) { + matrix_->setScaleX(SkFloatToScalar(x)); +} + +void TransformSkia::SetScaleY(float y) { + matrix_->setScaleY(SkFloatToScalar(y)); +} + +void TransformSkia::SetScale(float x, float y) { + matrix_->setScale(SkFloatToScalar(x), SkFloatToScalar(y)); +} + +void TransformSkia::SetTranslateX(float x) { + matrix_->setTranslateX(SkFloatToScalar(x)); +} + +void TransformSkia::SetTranslateY(float y) { + matrix_->setTranslateY(SkFloatToScalar(y)); +} + +void TransformSkia::SetTranslate(float x, float y) { + matrix_->setTranslate(SkFloatToScalar(x), SkFloatToScalar(y)); +} + +void TransformSkia::ConcatRotate(float degree) { + matrix_->postRotate(SkFloatToScalar(degree)); +} + +void TransformSkia::ConcatScale(float x, float y) { + matrix_->postScale(SkFloatToScalar(x), SkFloatToScalar(y)); +} + +void TransformSkia::ConcatTranslate(float x, float y) { + matrix_->postTranslate(SkFloatToScalar(x), SkFloatToScalar(y)); +} + +bool TransformSkia::ConcatTransform(const Transform& transform) { + return matrix_->setConcat(*reinterpret_cast<const TransformSkia&> + (transform).matrix_.get(), *matrix_.get()); +} + +bool TransformSkia::HasChange() const { + return !matrix_->isIdentity(); +} + +bool TransformSkia::TransformPoint(gfx::Point* point) { + SkPoint skp; + matrix_->mapXY(SkIntToScalar(point->x()), SkIntToScalar(point->y()), &skp); + point->SetPoint(static_cast<int>(skp.fX), static_cast<int>(skp.fY)); + return true; +} + +bool TransformSkia::TransformPointReverse(gfx::Point* point) { + SkMatrix inverse; + // TODO(sad): Try to avoid trying to invert the matrix. + if (matrix_->invert(&inverse)) { + SkPoint skp; + inverse.mapXY(SkIntToScalar(point->x()), SkIntToScalar(point->y()), &skp); + point->SetPoint(static_cast<int>(skp.fX), static_cast<int>(skp.fY)); + return true; + } + return false; +} + +bool TransformSkia::TransformRect(gfx::Rect* rect) { + SkRect src = gfx::RectToSkRect(*rect); + if (!matrix_->mapRect(&src)) + return false; + gfx::Rect xrect = gfx::SkRectToRect(src); + rect->SetRect(xrect.x(), xrect.y(), xrect.width(), xrect.height()); + return true; +} + +} // namespace ui diff --git a/ui/gfx/transform_skia.h b/ui/gfx/transform_skia.h new file mode 100644 index 0000000..1bd8188 --- /dev/null +++ b/ui/gfx/transform_skia.h @@ -0,0 +1,54 @@ +// 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. + +#ifndef UI_GFX_TRANSFORM_SKIA_H_ +#define UI_GFX_TRANSFORM_SKIA_H_ +#pragma once + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "base/scoped_ptr.h" +#include "ui/gfx/transform.h" + +class SkMatrix; + +namespace gfx { +class CanvasSkia; +} + +namespace ui { + +// Transformation using skia transformation matrices. +class TransformSkia : public Transform { + public: + TransformSkia(); + virtual ~TransformSkia() {} + + // Overridden from ui::Transform + virtual void SetRotate(float degree) OVERRIDE; + virtual void SetScaleX(float x) OVERRIDE; + virtual void SetScaleY(float y) OVERRIDE; + virtual void SetScale(float x, float y) OVERRIDE; + virtual void SetTranslateX(float x) OVERRIDE; + virtual void SetTranslateY(float y) OVERRIDE; + virtual void SetTranslate(float x, float y) OVERRIDE; + virtual void ConcatRotate(float degree) OVERRIDE; + virtual void ConcatScale(float x, float y) OVERRIDE; + virtual void ConcatTranslate(float x, float y) OVERRIDE; + virtual bool ConcatTransform(const Transform& transform) OVERRIDE; + virtual bool HasChange() const OVERRIDE; + virtual bool TransformPoint(gfx::Point* point) OVERRIDE; + virtual bool TransformPointReverse(gfx::Point* point) OVERRIDE; + virtual bool TransformRect(gfx::Rect* rect) OVERRIDE; + + private: + friend class gfx::CanvasSkia; + scoped_ptr<SkMatrix> matrix_; + + DISALLOW_COPY_AND_ASSIGN(TransformSkia); +}; + +} // namespace ui + +#endif // UI_GFX_TRANSFORM_SKIA_H_ diff --git a/views/events/event.cc b/views/events/event.cc index 2a814bd..49fe29b 100644 --- a/views/events/event.cc +++ b/views/events/event.cc @@ -5,6 +5,7 @@ #include "views/events/event.h" #include "views/view.h" +#include "views/widget/root_view.h" namespace views { @@ -45,6 +46,15 @@ LocatedEvent::LocatedEvent(const LocatedEvent& model, View* from, View* to) } //////////////////////////////////////////////////////////////////////////////// +// LocatedEvent, private: + +LocatedEvent::LocatedEvent(const LocatedEvent& model, RootView* root) + : Event(model), + location_(model.location_) { + View::ConvertPointFromWidget(root, &location_); +} + +//////////////////////////////////////////////////////////////////////////////// // KeyEvent, public: KeyEvent::KeyEvent(ui::EventType type, ui::KeyboardCode key_code, diff --git a/views/events/event.h b/views/events/event.h index 5382573..83f8bbf 100644 --- a/views/events/event.h +++ b/views/events/event.h @@ -24,6 +24,7 @@ using ui::OSExchangeData; namespace views { +class RootView; class View; //////////////////////////////////////////////////////////////////////////////// @@ -145,6 +146,11 @@ class LocatedEvent : public Event { int y() const { return location_.y(); } const gfx::Point& location() const { return location_; } + protected: + // This constructor is to allow converting the location of an event from the + // widget's coordinate system to the RootView's coordinate system. + LocatedEvent(const LocatedEvent& model, RootView* root); + private: gfx::Point location_; }; @@ -211,6 +217,12 @@ class MouseEvent : public LocatedEvent { } private: + friend class RootView; + + MouseEvent(const MouseEvent& model, RootView* root) + : LocatedEvent(model, root) { + } + DISALLOW_COPY_AND_ASSIGN(MouseEvent); }; @@ -251,6 +263,13 @@ class TouchEvent : public LocatedEvent { bool identity() const { return touch_id_; } private: + friend class RootView; + + TouchEvent(const TouchEvent& model, RootView* root) + : LocatedEvent(model, root), + touch_id_(model.touch_id_) { + } + // The identity (typically finger) of the touch starting at 0 and incrementing // for each separable additional touch that the hardware can detect. const int touch_id_; @@ -308,6 +327,13 @@ class MouseWheelEvent : public LocatedEvent { int offset() const { return offset_; } private: + friend class RootView; + + MouseWheelEvent(const MouseWheelEvent& model, RootView* root) + : LocatedEvent(model, root), + offset_(model.offset_) { + } + int offset_; DISALLOW_COPY_AND_ASSIGN(MouseWheelEvent); diff --git a/views/touchui/gesture_manager.cc b/views/touchui/gesture_manager.cc index 2d6c7d0..2442469 100644 --- a/views/touchui/gesture_manager.cc +++ b/views/touchui/gesture_manager.cc @@ -30,9 +30,19 @@ bool GestureManager::ProcessTouchEventForGesture(const TouchEvent& event, // appear in a subsequent CL. This interim version permits verifying that the // event distribution code works by turning all touch inputs into // mouse approximations. + + // TODO(sad): Clean this up. + // This is currently only called where |source| is a RootView. Now, RootView + // expects the mouse-events in the widget's coordinate system, and not in the + // RV's coordinate system. But |event| is in the RV's coordinate system. So it + // is necessary to construct the synthetic event in the widget's coordinate + // system. + gfx::Point location = event.location(); + View::ConvertPointToWidget(source, &location); + if (event.type() == ui::ET_TOUCH_PRESSED) { DVLOG(1) << "GestureManager::ProcessTouchEventForGesture: TouchPressed"; - MouseEvent mouse_event(ui::ET_MOUSE_PRESSED, event.x(), event.y(), + MouseEvent mouse_event(ui::ET_MOUSE_PRESSED, location.x(), location.y(), event.flags()); source->OnMousePressed(mouse_event); return true; @@ -40,7 +50,7 @@ bool GestureManager::ProcessTouchEventForGesture(const TouchEvent& event, if (event.type() == ui::ET_TOUCH_RELEASED) { DVLOG(1) << "GestureManager::ProcessTouchEventForGesture: TouchReleased"; - MouseEvent mouse_event(ui::ET_MOUSE_RELEASED, event.x(), event.y(), + MouseEvent mouse_event(ui::ET_MOUSE_RELEASED, location.x(), location.y(), event.flags()); source->OnMouseReleased(mouse_event, false); return true; @@ -48,7 +58,7 @@ bool GestureManager::ProcessTouchEventForGesture(const TouchEvent& event, if (event.type() == ui::ET_TOUCH_MOVED) { DVLOG(1) << "GestureManager::ProcessTouchEventForGesture: TouchMotion"; - MouseEvent mouse_event(ui::ET_MOUSE_DRAGGED, event.x(), event.y(), + MouseEvent mouse_event(ui::ET_MOUSE_DRAGGED, location.x(), location.y(), event.flags()); source->OnMouseDragged(mouse_event); return true; diff --git a/views/view.cc b/views/view.cc index 8ffb77e..b36a3a4 100644 --- a/views/view.cc +++ b/views/view.cc @@ -10,12 +10,11 @@ #include "base/message_loop.h" #include "base/scoped_ptr.h" #include "base/utf_string_conversions.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 "ui/gfx/transform.h" #include "views/background.h" #include "views/layout/layout_manager.h" #include "views/views_delegate.h" @@ -61,8 +60,8 @@ View::View() parent_(NULL), is_visible_(true), registered_for_visible_bounds_notification_(false), - clip_x_(0), - clip_y_(0), + clip_x_(0.0), + clip_y_(0.0), needs_layout_(true), flip_canvas_on_paint_for_rtl_ui_(false), accelerator_registration_delayed_(false), @@ -357,45 +356,66 @@ bool View::IsEnabled() const { // Transformations ------------------------------------------------------------- -void View::SetRotation(double degree) { +const ui::Transform& View::GetTransform() const { + static const ui::Transform* no_op = ui::Transform::Create(); + if (transform_.get()) + return *transform_.get(); + return *no_op; +} + +void View::SetRotation(float degree) { + InitTransform(); + transform_->SetRotate(degree); +} + +void View::SetScaleX(float x) { InitTransform(); - transform_->setRotate(SkDoubleToScalar(degree), - SkIntToScalar(0), SkIntToScalar(0)); + transform_->SetScaleX(x); } -void View::SetScaleX(double x) { +void View::SetScaleY(float y) { InitTransform(); - transform_->setScaleX(SkDoubleToScalar(x)); + transform_->SetScaleY(y); } -void View::SetScaleY(double y) { +void View::SetScale(float x, float y) { InitTransform(); - transform_->setScaleY(SkDoubleToScalar(y)); + transform_->SetScale(x, y); } -void View::SetScale(double x, double y) { +void View::SetTranslateX(float x) { InitTransform(); - transform_->setScale(SkDoubleToScalar(x), SkDoubleToScalar(y)); + transform_->SetTranslateX(x); } -void View::SetTranslateX(int x) { +void View::SetTranslateY(float y) { InitTransform(); - transform_->setTranslateX(SkIntToScalar(x)); + transform_->SetTranslateY(y); } -void View::SetTranslateY(int y) { +void View::SetTranslate(float x, float y) { InitTransform(); - transform_->setTranslateY(SkIntToScalar(y)); + transform_->SetTranslate(x, y); } -void View::SetTranslate(int x, int y) { +void View::ConcatRotation(float degree) { InitTransform(); - transform_->setTranslate(SkIntToScalar(x), SkIntToScalar(y)); + transform_->ConcatRotate(degree); +} + +void View::ConcatScale(float x, float y) { + InitTransform(); + transform_->ConcatScale(x, y); +} + +void View::ConcatTranslate(float x, float y) { + InitTransform(); + transform_->ConcatTranslate(x, y); } void View::ResetTransform() { transform_.reset(NULL); - clip_x_ = clip_y_ = 0; + clip_x_ = clip_y_ = 0.0; } @@ -554,19 +574,15 @@ void View::ConvertPointToWidget(const View* src, gfx::Point* p) { DCHECK(src); DCHECK(p); - gfx::Point offset; - for (const View* v = src; v; v = v->parent()) { - offset.set_x(offset.x() + v->GetMirroredX()); - offset.set_y(offset.y() + v->y()); - } - p->SetPoint(p->x() + offset.x(), p->y() + offset.y()); + src->ConvertPointForAncestor(NULL, p); } // static void View::ConvertPointFromWidget(const View* dest, gfx::Point* p) { - gfx::Point t; - ConvertPointToWidget(dest, &t); - p->SetPoint(p->x() - t.x(), p->y() - t.y()); + DCHECK(dest); + DCHECK(p); + + dest->ConvertPointFromAncestor(NULL, p); } // static @@ -585,12 +601,55 @@ 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)) + if (!transform_.get() || !transform_->HasChange()) return rect; - return gfx::SkRectToRect(src); + gfx::Rect x_rect = rect; + transform_->TransformRect(&x_rect); + return x_rect; +} + +bool View::ConvertPointForAncestor(const View* ancestor, + gfx::Point* point) const { + scoped_ptr<ui::Transform> trans(ui::Transform::Create()); + + // TODO(sad): Have some way of caching the transformation results. + + const View* v = this; + for (; v && v != ancestor; v = v->parent()) { + if (v->GetTransform().HasChange()) { + if (!trans->ConcatTransform(v->GetTransform())) + return false; + } + trans->ConcatTranslate(static_cast<float>(v->GetMirroredX()), + static_cast<float>(v->y())); + } + + if (trans->HasChange()) { + trans->TransformPoint(point); + } + + return v == ancestor; +} + +bool View::ConvertPointFromAncestor(const View* ancestor, + gfx::Point* point) const { + scoped_ptr<ui::Transform> trans(ui::Transform::Create()); + + const View* v = this; + for (; v && v != ancestor; v = v->parent()) { + if (v->GetTransform().HasChange()) { + if (!trans->ConcatTransform(v->GetTransform())) + return false; + } + trans->ConcatTranslate(static_cast<float>(v->GetMirroredX()), + static_cast<float>(v->y())); + } + + if (trans->HasChange()) { + trans->TransformPointReverse(point); + } + + return v == ancestor; } // Painting -------------------------------------------------------------------- @@ -627,13 +686,14 @@ void View::Paint(gfx::Canvas* canvas) { // 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() - clip_x_, height() - clip_y_)) { + width() - static_cast<int>(clip_x_), + height() - static_cast<int>(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 (transform_.get() && transform_->HasChange()) + canvas->Transform(*transform_.get()); // If the View we are about to paint requested the canvas to be flipped, we // should change the transform appropriately. @@ -675,6 +735,11 @@ ThemeProvider* View::GetThemeProvider() const { // Input ----------------------------------------------------------------------- View* View::GetViewForPoint(const gfx::Point& point) { + return GetEventHandlerForPoint(point, NULL); +} + +View* View::GetEventHandlerForPoint(const gfx::Point& point, + gfx::Point* xpoint) { // Walk the child Views recursively looking for the View that most // tightly encloses the specified point. for (int i = child_count() - 1; i >= 0; --i) { @@ -685,8 +750,10 @@ View* View::GetViewForPoint(const gfx::Point& point) { gfx::Point point_in_child_coords(point); View::ConvertPointToView(this, child, &point_in_child_coords); if (child->HitTest(point_in_child_coords)) - return child->GetViewForPoint(point_in_child_coords); + return child->GetEventHandlerForPoint(point_in_child_coords, xpoint); } + if (xpoint) + xpoint->SetPoint(point.x(), point.y()); return this; } @@ -1323,10 +1390,8 @@ void View::RemoveDescendantToNotify(View* view) { // Transformations ------------------------------------------------------------- void View::InitTransform() { - if (!transform_.get()) { - transform_.reset(new SkMatrix); - transform_->reset(); - } + if (!transform_.get()) + transform_.reset(ui::Transform::Create()); } // Coordinate conversion ------------------------------------------------------- @@ -1340,27 +1405,9 @@ void View::ConvertPointToView(const View* src, DCHECK(dst); DCHECK(point); - const View* v; - gfx::Point offset; - - for (v = dst; v && v != src; v = v->parent()) - offset.SetPoint(offset.x() + v->GetMirroredX(), offset.y() + v->y()); - - // The source was not found. The caller wants a conversion - // from a view to a transitive parent. - if (src && v == NULL && try_other_direction) { - gfx::Point p; - // note: try_other_direction is force to FALSE so we don't - // end up in an infinite recursion should both src and dst - // are not parented. - ConvertPointToView(dst, src, &p, false); - // since the src and dst are inverted, p should also be negated - point->SetPoint(point->x() - p.x(), point->y() - p.y()); - } else { - point->SetPoint(point->x() - offset.x(), point->y() - offset.y()); - - // If src is NULL, sp is in the screen coordinate system - if (src == NULL) { + if (src == NULL || src->Contains(dst)) { + dst->ConvertPointFromAncestor(src, point); + if (!src) { const Widget* widget = dst->GetWidget(); if (widget) { gfx::Rect b; @@ -1368,6 +1415,13 @@ void View::ConvertPointToView(const View* src, point->SetPoint(point->x() - b.x(), point->y() - b.y()); } } + } else if (src && try_other_direction) { + if (!src->ConvertPointForAncestor(dst, point)) { + // |src| is not an ancestor of |dst|, and |dst| is not an ancestor of + // |src| either. At this stage, |point| is in the widget's coordinate + // system. So convert from the widget's to |dst|'s coordiante system now. + ConvertPointFromWidget(dst, point); + } } } diff --git a/views/view.h b/views/view.h index 490662d..eb7ff06 100644 --- a/views/view.h +++ b/views/view.h @@ -26,8 +26,6 @@ using ui::OSExchangeData; -// TODO(sad): Use platform independent wrapper for transform matrix. -class SkMatrix; class ViewAccessibility; namespace gfx { @@ -38,6 +36,7 @@ class Path; namespace ui { class ThemeProvider; +class Transform; } using ui::ThemeProvider; @@ -331,25 +330,31 @@ class View : public AcceleratorTarget { // Methods for setting transformations for a view (e.g. rotation, scaling). - const SkMatrix* transform() { return transform_.get(); } + const ui::Transform& GetTransform() const; // 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 set_clip_x(float x) { clip_x_ = x; } + void set_clip_y(float y) { clip_y_ = y; } + void set_clip(float x, float y) { clip_x_ = x; clip_y_ = y; } - void SetRotation(double degree); + void SetRotation(float degree); - void SetScaleX(double x); - void SetScaleY(double y); - void SetScale(double x, double y); + void SetScaleX(float x); + void SetScaleY(float y); + void SetScale(float x, float y); - void SetTranslateX(int x); - void SetTranslateY(int y); - void SetTranslate(int x, int y); + void SetTranslateX(float x); + void SetTranslateY(float y); + void SetTranslate(float x, float y); + + // The following functions apply the transformations on top of the existing + // transform. + void ConcatRotation(float degree); + void ConcatScale(float x, float y); + void ConcatTranslate(float x, float y); // Reset the transformation matrix. void ResetTransform(); @@ -476,9 +481,9 @@ class View : public AcceleratorTarget { // Convert a point from source coordinate system to dst coordinate system. // - // source is a parent or a child of dst, directly or transitively. - // If source and dst are not in the same View hierarchy, the result is - // undefined. + // |src| and |dst| needs to be in the same widget, but doesn't need to be in + // the same view hierarchy. + // If |src| and |dst| are not in the same widget, the result is undefined. // Source can be NULL in which case it means the screen coordinate system static void ConvertPointToView(const View* src, const View* dst, @@ -560,10 +565,18 @@ class View : public AcceleratorTarget { } // Input --------------------------------------------------------------------- + // The points (and mouse locations) in the following functions are in the + // view's coordinates, except for a RootView. + // TODO(sad): Remove // Returns the deepest descendant that contains the specified point. virtual View* GetViewForPoint(const gfx::Point& point); + // Returns the deepest descendant that contains the specified point, and the + // point in the returned view's coordinates. + virtual View* GetEventHandlerForPoint(const gfx::Point& point, + gfx::Point* xpoint); + // Return the cursor that should be used for this view or NULL if // the default cursor should be used. The provided point is in the // receiver's coordinate system. The caller is responsible for managing the @@ -1227,16 +1240,28 @@ class View : public AcceleratorTarget { // Initialize the transform matrix when necessary. void InitTransform(); - // Coordinate conersion ------------------------------------------------------ + // Coordinate conversion ----------------------------------------------------- // This is the actual implementation for ConvertPointToView() // Attempts a parent -> child conversion and then a // child -> parent conversion if try_other_direction is true + // Applies necessary transformations during the conversion. static void ConvertPointToView(const View* src, const View* dst, gfx::Point* point, bool try_other_direction); + // Convert a point in the view's coordinate to an ancestor view's coordinate + // system using necessary transformations. Returns whether the point was + // successfully converted to the ancestor's coordinate system. + bool ConvertPointForAncestor(const View* ancestor, gfx::Point* point) const; + + // Convert a point in the ancestor's coordinate system to the view's + // coordinate system using necessary transformations. Returns whether the + // point was successfully from the ancestor's coordinate system to the view's + // coordinate system. + bool ConvertPointFromAncestor(const View* ancestor, gfx::Point* point) const; + // Input --------------------------------------------------------------------- // RootView invokes these. These in turn invoke the appropriate OnMouseXXX @@ -1328,12 +1353,12 @@ class View : public AcceleratorTarget { // Transformations ----------------------------------------------------------- // The transformation matrix (rotation, translate, scale). - scoped_ptr<SkMatrix> transform_; + scoped_ptr<ui::Transform> transform_; // Clipping parameters. skia transformation matrix does not give us clipping. // So we do it ourselves. - int clip_x_; - int clip_y_; + float clip_x_; + float clip_y_; // Layout -------------------------------------------------------------------- diff --git a/views/view_unittest.cc b/views/view_unittest.cc index 0d30b6c..74aadd5 100644 --- a/views/view_unittest.cc +++ b/views/view_unittest.cc @@ -1587,6 +1587,133 @@ TEST_F(ViewTest, TransformPaint) { widget->CloseNow(); } +TEST_F(ViewTest, TransformEvent) { + 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|. + + // Rotate |v1| counter-clockwise. + v1->SetRotation(-90.0); + v1->SetTranslateY(500); + + // |v2| now occupies (100, 200) to (200, 400) in |root|. + v1->Reset(); + v2->Reset(); + + MouseEvent pressed(ui::ET_MOUSE_PRESSED, + 110, 210, + ui::EF_LEFT_BUTTON_DOWN); + root->OnMousePressed(pressed); + EXPECT_EQ(0, v1->last_mouse_event_type_); + EXPECT_EQ(ui::ET_MOUSE_PRESSED, v2->last_mouse_event_type_); + EXPECT_EQ(190, v2->location_.x()); + EXPECT_EQ(10, v2->location_.y()); + + MouseEvent released(ui::ET_MOUSE_RELEASED, 0, 0, 0); + root->OnMouseReleased(released, false); + + // Now rotate |v2| inside |v1| clockwise. + v2->SetRotation(90.0); + v2->SetTranslateX(100); + + // Now, |v2| occupies (100, 100) to (200, 300) in |v1|, and (100, 300) to + // (300, 400) in |root|. + + v1->Reset(); + v2->Reset(); + + MouseEvent p2(ui::ET_MOUSE_PRESSED, + 110, 320, + ui::EF_LEFT_BUTTON_DOWN); + root->OnMousePressed(p2); + EXPECT_EQ(0, v1->last_mouse_event_type_); + EXPECT_EQ(ui::ET_MOUSE_PRESSED, v2->last_mouse_event_type_); + EXPECT_EQ(10, v2->location_.x()); + EXPECT_EQ(20, v2->location_.y()); + + root->OnMouseReleased(released, false); + + v1->ResetTransform(); + v2->ResetTransform(); + + TestView* v3 = new TestView(); + v3->SetBounds(10, 10, 20, 30); + v2->AddChildView(v3); + + // Rotate |v3| clockwise with respect to |v2|. + v3->SetRotation(90.0); + v3->SetTranslateX(30); + + // Scale |v2| with respect to |v1| along both axis. + v2->SetScale(0.8, 0.5); + + // |v3| occupies (108, 105) to (132, 115) in |root|. + + v1->Reset(); + v2->Reset(); + v3->Reset(); + + MouseEvent p3(ui::ET_MOUSE_PRESSED, + 112, 110, + ui::EF_LEFT_BUTTON_DOWN); + root->OnMousePressed(p3); + + EXPECT_EQ(ui::ET_MOUSE_PRESSED, v3->last_mouse_event_type_); + EXPECT_EQ(10, v3->location_.x()); + EXPECT_EQ(25, v3->location_.y()); + + root->OnMouseReleased(released, false); + + v1->ResetTransform(); + v2->ResetTransform(); + v3->ResetTransform(); + + v1->Reset(); + v2->Reset(); + v3->Reset(); + + // Rotate |v3| clockwise with respect to |v2|, and scale it along both axis. + v3->SetRotation(90.0); + v3->SetTranslateX(30); + // Rotation sets some scaling transformation. Using SetScale would overwrite + // that and pollute the rotation. So combine the scaling with the existing + // transforamtion. + v3->ConcatScale(0.8, 0.5); + + // Translate |v2| with respect to |v1|. + v2->SetTranslate(10, 10); + + // |v3| now occupies (120, 120) to (144, 130) in |root|. + + MouseEvent p4(ui::ET_MOUSE_PRESSED, + 124, 125, + ui::EF_LEFT_BUTTON_DOWN); + root->OnMousePressed(p4); + + EXPECT_EQ(ui::ET_MOUSE_PRESSED, v3->last_mouse_event_type_); + EXPECT_EQ(10, v3->location_.x()); + EXPECT_EQ(25, v3->location_.y()); + + root->OnMouseReleased(released, false); + + widget->CloseNow(); +} + //////////////////////////////////////////////////////////////////////////////// // OnVisibleBoundsChanged() diff --git a/views/widget/root_view.cc b/views/widget/root_view.cc index d7b9007..4919bf4 100644 --- a/views/widget/root_view.cc +++ b/views/widget/root_view.cc @@ -116,7 +116,8 @@ bool RootView::ProcessKeyEvent(const KeyEvent& event) { return consumed; } -bool RootView::ProcessMouseWheelEvent(const MouseWheelEvent& e) { +bool RootView::ProcessMouseWheelEvent(const MouseWheelEvent& event) { + MouseWheelEvent e(event, this); View* v; bool consumed = false; View* focused_view = GetFocusManager()->GetFocusedView(); @@ -181,7 +182,9 @@ Widget* RootView::GetWidget() { return const_cast<Widget*>(const_cast<const RootView*>(this)->GetWidget()); } -bool RootView::OnMousePressed(const MouseEvent& e) { +bool RootView::OnMousePressed(const MouseEvent& event) { + MouseEvent e(event, this); + // This function does not normally handle non-client messages except for // non-client double-clicks. Actually, all double-clicks are special as the // are formed from a single-click followed by a double-click event. When the @@ -265,7 +268,8 @@ bool RootView::OnMousePressed(const MouseEvent& e) { return hit_disabled_view; } -bool RootView::OnMouseDragged(const MouseEvent& e) { +bool RootView::OnMouseDragged(const MouseEvent& event) { + MouseEvent e(event, this); UpdateCursor(e); if (mouse_pressed_handler_) { @@ -279,7 +283,8 @@ bool RootView::OnMouseDragged(const MouseEvent& e) { return false; } -void RootView::OnMouseReleased(const MouseEvent& e, bool canceled) { +void RootView::OnMouseReleased(const MouseEvent& event, bool canceled) { + MouseEvent e(event, this); UpdateCursor(e); if (mouse_pressed_handler_) { @@ -296,7 +301,8 @@ void RootView::OnMouseReleased(const MouseEvent& e, bool canceled) { } } -void RootView::OnMouseMoved(const MouseEvent& e) { +void RootView::OnMouseMoved(const MouseEvent& event) { + MouseEvent e(event, this); View* v = GetViewForPoint(e.location()); // Find the first enabled view, or the existing move handler, whichever comes // first. The check for the existing handler is because if a view becomes @@ -344,7 +350,9 @@ void RootView::SetMouseHandler(View *new_mh) { } #if defined(TOUCH_UI) -View::TouchStatus RootView::OnTouchEvent(const TouchEvent& e) { +View::TouchStatus RootView::OnTouchEvent(const TouchEvent& event) { + TouchEvent e(event, this); + // If touch_pressed_handler_ is non null, we are currently processing // a touch down on the screen situation. In that case we send the // event to touch_pressed_handler_ diff --git a/views/widget/root_view.h b/views/widget/root_view.h index df74ede..e38c686 100644 --- a/views/widget/root_view.h +++ b/views/widget/root_view.h @@ -149,10 +149,14 @@ class RootView : public View, // Update the cursor given a mouse event. This is called by non mouse_move // event handlers to honor the cursor desired by views located under the - // cursor during drag operations. + // cursor during drag operations. The location of the mouse should be in the + // current coordinate system (i.e. any necessary transformation should be + // applied to the point prior to calling this). void UpdateCursor(const MouseEvent& e); - // Updates the last_mouse_* fields from e. + // Updates the last_mouse_* fields from e. The location of the mouse should be + // in the current coordinate system (i.e. any necessary transformation should + // be applied to the point prior to calling this). void SetMouseLocationAndFlags(const MouseEvent& e); ////////////////////////////////////////////////////////////////////////////// |