#include "base/memory/scoped_ptr.h"
#include "base/rand_util.h"
#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
-#include "grit/ui_strings.h"
-#include "ui/aura/window.h"
-#include "ui/aura/window_event_dispatcher.h"
#include "ui/base/accelerators/accelerator.h"
#include "ui/base/clipboard/clipboard.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/compositor/compositor.h"
#include "ui/compositor/layer.h"
#include "ui/compositor/layer_animator.h"
-#include "ui/compositor/layer_tree_owner.h"
#include "ui/compositor/test/draw_waiter_for_test.h"
-#include "ui/compositor/test/test_layers.h"
#include "ui/events/event.h"
-#include "ui/events/gestures/gesture_recognizer.h"
#include "ui/events/keycodes/keyboard_codes.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/path.h"
#include "ui/gfx/transform.h"
+#include "ui/strings/grit/ui_strings.h"
#include "ui/views/background.h"
#include "ui/views/controls/native/native_view_host.h"
#include "ui/views/controls/scroll_view.h"
#include "ui/views/focus/view_storage.h"
#include "ui/views/test/views_test_base.h"
#include "ui/views/view.h"
-#include "ui/views/view_constants_aura.h"
#include "ui/views/views_delegate.h"
#include "ui/views/widget/native_widget.h"
#include "ui/views/widget/root_view.h"
#include "ui/views/window/dialog_client_view.h"
#include "ui/views/window/dialog_delegate.h"
-#include "ui/wm/core/window_util.h"
using base::ASCIIToUTF16;
view->SetVisible(!view->visible());
}
-// Convenience to make constructing a GestureEvent simpler.
-class GestureEventForTest : public ui::GestureEvent {
- public:
- GestureEventForTest(ui::EventType type, int x, int y, int flags)
- : GestureEvent(type, x, y, flags, base::TimeDelta(),
- ui::GestureEventDetails(type, 0.0f, 0.0f), 0) {
- }
-
- private:
- DISALLOW_COPY_AND_ASSIGN(GestureEventForTest);
-};
-
} // namespace
namespace views {
// A derived class for testing purpose.
class TestView : public View {
public:
- TestView() : View(), delete_on_pressed_(false), in_touch_sequence_(false) {}
+ TestView()
+ : View(),
+ delete_on_pressed_(false),
+ native_theme_(NULL),
+ can_process_events_within_subtree_(true) {}
virtual ~TestView() {}
// Reset all test state
location_.SetPoint(0, 0);
received_mouse_enter_ = false;
received_mouse_exit_ = false;
- last_touch_event_type_ = 0;
- last_touch_event_was_handled_ = false;
- last_gesture_event_type_ = 0;
- last_gesture_event_was_handled_ = false;
last_clip_.setEmpty();
accelerator_count_map_.clear();
+ can_process_events_within_subtree_ = true;
}
// Exposed as public for testing.
bool focusable() const { return View::focusable(); }
+ void set_can_process_events_within_subtree(bool can_process) {
+ can_process_events_within_subtree_ = can_process;
+ }
+
+ virtual bool CanProcessEventsWithinSubtree() const OVERRIDE {
+ return can_process_events_within_subtree_;
+ }
+
virtual void OnBoundsChanged(const gfx::Rect& previous_bounds) OVERRIDE;
virtual bool OnMousePressed(const ui::MouseEvent& event) OVERRIDE;
virtual bool OnMouseDragged(const ui::MouseEvent& event) OVERRIDE;
virtual void OnMouseEntered(const ui::MouseEvent& event) OVERRIDE;
virtual void OnMouseExited(const ui::MouseEvent& event) OVERRIDE;
- virtual void OnTouchEvent(ui::TouchEvent* event) OVERRIDE;
- // Ignores GestureEvent by default.
- virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE;
-
- virtual void Paint(gfx::Canvas* canvas) OVERRIDE;
+ virtual void Paint(gfx::Canvas* canvas, const CullSet& cull_set) OVERRIDE;
virtual void SchedulePaintInRect(const gfx::Rect& rect) OVERRIDE;
virtual bool AcceleratorPressed(const ui::Accelerator& accelerator) OVERRIDE;
+ virtual void OnNativeThemeChanged(const ui::NativeTheme* native_theme)
+ OVERRIDE;
+
// OnBoundsChanged.
bool did_change_bounds_;
gfx::Rect new_bounds_;
// Painting.
std::vector<gfx::Rect> scheduled_paint_rects_;
- // GestureEvent
- int last_gesture_event_type_;
- bool last_gesture_event_was_handled_;
-
- // TouchEvent.
- int last_touch_event_type_;
- bool last_touch_event_was_handled_;
- bool in_touch_sequence_;
-
// Painting.
SkRect last_clip_;
// Accelerators.
std::map<ui::Accelerator, int> accelerator_count_map_;
-};
-
-// A view subclass that ignores all touch events for testing purposes.
-class TestViewIgnoreTouch : public TestView {
- public:
- TestViewIgnoreTouch() : TestView() {}
- virtual ~TestViewIgnoreTouch() {}
-
- private:
- virtual void OnTouchEvent(ui::TouchEvent* event) OVERRIDE;
-};
-
-// A view subclass that consumes all Gesture events for testing purposes.
-class TestViewConsumeGesture : public TestView {
- public:
- TestViewConsumeGesture() : TestView() {}
- virtual ~TestViewConsumeGesture() {}
-
- protected:
- virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE {
- last_gesture_event_type_ = event->type();
- location_.SetPoint(event->x(), event->y());
- event->StopPropagation();
- }
-
- private:
- DISALLOW_COPY_AND_ASSIGN(TestViewConsumeGesture);
-};
-
-// A view subclass that ignores all Gesture events.
-class TestViewIgnoreGesture: public TestView {
- public:
- TestViewIgnoreGesture() : TestView() {}
- virtual ~TestViewIgnoreGesture() {}
- private:
- virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE {
- }
+ // Native theme.
+ const ui::NativeTheme* native_theme_;
- DISALLOW_COPY_AND_ASSIGN(TestViewIgnoreGesture);
-};
-
-// A view subclass that ignores all scroll-gesture events, but consume all other
-// gesture events.
-class TestViewIgnoreScrollGestures : public TestViewConsumeGesture {
- public:
- TestViewIgnoreScrollGestures() {}
- virtual ~TestViewIgnoreScrollGestures() {}
-
- private:
- virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE {
- if (event->IsScrollGestureEvent())
- return;
- TestViewConsumeGesture::OnGestureEvent(event);
- }
-
- DISALLOW_COPY_AND_ASSIGN(TestViewIgnoreScrollGestures);
+ // Value to return from CanProcessEventsWithinSubtree().
+ bool can_process_events_within_subtree_;
};
////////////////////////////////////////////////////////////////////////////////
}
////////////////////////////////////////////////////////////////////////////////
-// TouchEvent
-////////////////////////////////////////////////////////////////////////////////
-void TestView::OnTouchEvent(ui::TouchEvent* event) {
- last_touch_event_type_ = event->type();
- location_.SetPoint(event->x(), event->y());
- if (!in_touch_sequence_) {
- if (event->type() == ui::ET_TOUCH_PRESSED) {
- in_touch_sequence_ = true;
- event->StopPropagation();
- return;
- }
- } else {
- if (event->type() == ui::ET_TOUCH_RELEASED) {
- in_touch_sequence_ = false;
- event->SetHandled();
- return;
- }
- event->StopPropagation();
- return;
- }
-
- if (last_touch_event_was_handled_)
- event->StopPropagation();
-}
-
-void TestViewIgnoreTouch::OnTouchEvent(ui::TouchEvent* event) {
-}
-
-TEST_F(ViewTest, TouchEvent) {
- TestView* v1 = new TestView();
- v1->SetBoundsRect(gfx::Rect(0, 0, 300, 300));
-
- TestView* v2 = new TestView();
- v2->SetBoundsRect(gfx::Rect(100, 100, 100, 100));
-
- TestView* v3 = new TestViewIgnoreTouch();
- v3->SetBoundsRect(gfx::Rect(0, 0, 100, 100));
-
- scoped_ptr<Widget> widget(new Widget());
- Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP);
- params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
- params.bounds = gfx::Rect(50, 50, 650, 650);
- widget->Init(params);
- internal::RootView* root =
- static_cast<internal::RootView*>(widget->GetRootView());
- ui::EventDispatchDetails details;
-
- root->AddChildView(v1);
- v1->AddChildView(v2);
- v2->AddChildView(v3);
-
- // |v3| completely obscures |v2|, but all the touch events on |v3| should
- // reach |v2| because |v3| doesn't process any touch events.
-
- // Make sure if none of the views handle the touch event, the gesture manager
- // does.
- v1->Reset();
- v2->Reset();
-
- ui::TouchEvent unhandled(ui::ET_TOUCH_MOVED,
- gfx::Point(400, 400),
- 0, /* no flags */
- 0, /* first finger touch */
- base::TimeDelta(),
- 1.0, 0.0, 1.0, 0.0);
- details = root->OnEventFromSource(&unhandled);
- EXPECT_FALSE(details.dispatcher_destroyed);
- EXPECT_FALSE(details.target_destroyed);
-
- EXPECT_EQ(v1->last_touch_event_type_, 0);
- EXPECT_EQ(v2->last_touch_event_type_, 0);
-
- // Test press, drag, release touch sequence.
- v1->Reset();
- v2->Reset();
-
- ui::TouchEvent pressed(ui::ET_TOUCH_PRESSED,
- gfx::Point(110, 120),
- 0, /* no flags */
- 0, /* first finger touch */
- base::TimeDelta(),
- 1.0, 0.0, 1.0, 0.0);
- v2->last_touch_event_was_handled_ = true;
- details = root->OnEventFromSource(&pressed);
- EXPECT_FALSE(details.dispatcher_destroyed);
- EXPECT_FALSE(details.target_destroyed);
-
- EXPECT_EQ(v2->last_touch_event_type_, ui::ET_TOUCH_PRESSED);
- EXPECT_EQ(v2->location_.x(), 10);
- EXPECT_EQ(v2->location_.y(), 20);
- // Make sure v1 did not receive the event
- EXPECT_EQ(v1->last_touch_event_type_, 0);
-
- // Drag event out of bounds. Should still go to v2
- v1->Reset();
- v2->Reset();
- ui::TouchEvent dragged(ui::ET_TOUCH_MOVED,
- gfx::Point(50, 40),
- 0, /* no flags */
- 0, /* first finger touch */
- base::TimeDelta(),
- 1.0, 0.0, 1.0, 0.0);
-
- details = root->OnEventFromSource(&dragged);
- EXPECT_FALSE(details.dispatcher_destroyed);
- EXPECT_FALSE(details.target_destroyed);
-
- EXPECT_EQ(v2->last_touch_event_type_, ui::ET_TOUCH_MOVED);
- EXPECT_EQ(v2->location_.x(), -50);
- EXPECT_EQ(v2->location_.y(), -60);
- // Make sure v1 did not receive the event
- EXPECT_EQ(v1->last_touch_event_type_, 0);
-
- // Released event out of bounds. Should still go to v2
- v1->Reset();
- v2->Reset();
- ui::TouchEvent released(ui::ET_TOUCH_RELEASED, gfx::Point(),
- 0, /* no flags */
- 0, /* first finger */
- base::TimeDelta(),
- 1.0, 0.0, 1.0, 0.0);
- v2->last_touch_event_was_handled_ = true;
-
- details = root->OnEventFromSource(&released);
- EXPECT_FALSE(details.dispatcher_destroyed);
- EXPECT_FALSE(details.target_destroyed);
-
- EXPECT_EQ(v2->last_touch_event_type_, ui::ET_TOUCH_RELEASED);
- EXPECT_EQ(v2->location_.x(), -100);
- EXPECT_EQ(v2->location_.y(), -100);
- // Make sure v1 did not receive the event
- EXPECT_EQ(v1->last_touch_event_type_, 0);
-
- widget->CloseNow();
-}
-
-void TestView::OnGestureEvent(ui::GestureEvent* event) {
-}
-
-TEST_F(ViewTest, GestureEvent) {
- // Views hierarchy for non delivery of GestureEvent.
- TestView* v1 = new TestViewConsumeGesture();
- v1->SetBoundsRect(gfx::Rect(0, 0, 300, 300));
-
- TestView* v2 = new TestViewConsumeGesture();
- v2->SetBoundsRect(gfx::Rect(100, 100, 100, 100));
-
- TestView* v3 = new TestViewIgnoreGesture();
- v3->SetBoundsRect(gfx::Rect(0, 0, 100, 100));
-
- scoped_ptr<Widget> widget(new Widget());
- Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP);
- params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
- params.bounds = gfx::Rect(50, 50, 650, 650);
- widget->Init(params);
- internal::RootView* root =
- static_cast<internal::RootView*>(widget->GetRootView());
- ui::EventDispatchDetails details;
-
- root->AddChildView(v1);
- v1->AddChildView(v2);
- v2->AddChildView(v3);
-
- // |v3| completely obscures |v2|, but all the gesture events on |v3| should
- // reach |v2| because |v3| doesn't process any gesture events. However, since
- // |v2| does process gesture events, gesture events on |v3| or |v2| should not
- // reach |v1|.
-
- v1->Reset();
- v2->Reset();
- v3->Reset();
-
- // Gesture on |v3|
- GestureEventForTest g1(ui::ET_GESTURE_TAP, 110, 110, 0);
- details = root->OnEventFromSource(&g1);
- EXPECT_FALSE(details.dispatcher_destroyed);
- EXPECT_FALSE(details.target_destroyed);
-
- EXPECT_EQ(ui::ET_GESTURE_TAP, v2->last_gesture_event_type_);
- EXPECT_EQ(gfx::Point(10, 10), v2->location_);
- EXPECT_EQ(ui::ET_UNKNOWN, v1->last_gesture_event_type_);
-
- // Simulate an up so that RootView is no longer targetting |v3|.
- GestureEventForTest g1_up(ui::ET_GESTURE_END, 110, 110, 0);
- details = root->OnEventFromSource(&g1_up);
- EXPECT_FALSE(details.dispatcher_destroyed);
- EXPECT_FALSE(details.target_destroyed);
-
- v1->Reset();
- v2->Reset();
- v3->Reset();
-
- // Gesture on |v1|
- GestureEventForTest g2(ui::ET_GESTURE_TAP, 80, 80, 0);
- details = root->OnEventFromSource(&g2);
- EXPECT_FALSE(details.dispatcher_destroyed);
- EXPECT_FALSE(details.target_destroyed);
-
- EXPECT_EQ(ui::ET_GESTURE_TAP, v1->last_gesture_event_type_);
- EXPECT_EQ(gfx::Point(80, 80), v1->location_);
- EXPECT_EQ(ui::ET_UNKNOWN, v2->last_gesture_event_type_);
-
- // Send event |g1| again. Even though the coordinates target |v3| it should go
- // to |v1| as that is the view the touch was initially down on.
- v1->last_gesture_event_type_ = ui::ET_UNKNOWN;
- v3->last_gesture_event_type_ = ui::ET_UNKNOWN;
- details = root->OnEventFromSource(&g1);
- EXPECT_FALSE(details.dispatcher_destroyed);
- EXPECT_FALSE(details.target_destroyed);
-
- EXPECT_EQ(ui::ET_GESTURE_TAP, v1->last_gesture_event_type_);
- EXPECT_EQ(ui::ET_UNKNOWN, v3->last_gesture_event_type_);
- EXPECT_EQ("110,110", v1->location_.ToString());
-
- widget->CloseNow();
-}
-
-TEST_F(ViewTest, ScrollGestureEvent) {
- // Views hierarchy for non delivery of GestureEvent.
- TestView* v1 = new TestViewConsumeGesture();
- v1->SetBoundsRect(gfx::Rect(0, 0, 300, 300));
-
- TestView* v2 = new TestViewIgnoreScrollGestures();
- v2->SetBoundsRect(gfx::Rect(100, 100, 100, 100));
-
- TestView* v3 = new TestViewIgnoreGesture();
- v3->SetBoundsRect(gfx::Rect(0, 0, 100, 100));
-
- scoped_ptr<Widget> widget(new Widget());
- Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP);
- params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
- params.bounds = gfx::Rect(50, 50, 650, 650);
- widget->Init(params);
- internal::RootView* root =
- static_cast<internal::RootView*>(widget->GetRootView());
- ui::EventDispatchDetails details;
-
- root->AddChildView(v1);
- v1->AddChildView(v2);
- v2->AddChildView(v3);
-
- // |v3| completely obscures |v2|, but all the gesture events on |v3| should
- // reach |v2| because |v3| doesn't process any gesture events. However, since
- // |v2| does process gesture events, gesture events on |v3| or |v2| should not
- // reach |v1|.
-
- v1->Reset();
- v2->Reset();
- v3->Reset();
-
- // Gesture on |v3|
- GestureEventForTest g1(ui::ET_GESTURE_TAP, 110, 110, 0);
- details = root->OnEventFromSource(&g1);
- EXPECT_FALSE(details.dispatcher_destroyed);
- EXPECT_FALSE(details.target_destroyed);
-
- EXPECT_EQ(ui::ET_GESTURE_TAP, v2->last_gesture_event_type_);
- EXPECT_EQ(gfx::Point(10, 10), v2->location_);
- EXPECT_EQ(ui::ET_UNKNOWN, v1->last_gesture_event_type_);
-
- v2->Reset();
-
- // Send scroll gestures on |v3|. The gesture should reach |v2|, however,
- // since it does not process scroll-gesture events, these events should reach
- // |v1|.
- GestureEventForTest gscroll_begin(ui::ET_GESTURE_SCROLL_BEGIN, 115, 115, 0);
- details = root->OnEventFromSource(&gscroll_begin);
- EXPECT_FALSE(details.dispatcher_destroyed);
- EXPECT_FALSE(details.target_destroyed);
-
- EXPECT_EQ(ui::ET_UNKNOWN, v2->last_gesture_event_type_);
- EXPECT_EQ(ui::ET_GESTURE_SCROLL_BEGIN, v1->last_gesture_event_type_);
- v1->Reset();
-
- // Send a second tap on |v1|. The event should reach |v2| since it is the
- // default gesture handler, and not |v1| (even though it is the view under the
- // point, and is the scroll event handler).
- GestureEventForTest second_tap(ui::ET_GESTURE_TAP, 70, 70, 0);
- details = root->OnEventFromSource(&second_tap);
- EXPECT_FALSE(details.dispatcher_destroyed);
- EXPECT_FALSE(details.target_destroyed);
-
- EXPECT_EQ(ui::ET_GESTURE_TAP, v2->last_gesture_event_type_);
- EXPECT_EQ(ui::ET_UNKNOWN, v1->last_gesture_event_type_);
- v2->Reset();
-
- GestureEventForTest gscroll_end(ui::ET_GESTURE_SCROLL_END, 50, 50, 0);
- details = root->OnEventFromSource(&gscroll_end);
- EXPECT_FALSE(details.dispatcher_destroyed);
- EXPECT_FALSE(details.target_destroyed);
-
- EXPECT_EQ(ui::ET_GESTURE_SCROLL_END, v1->last_gesture_event_type_);
- v1->Reset();
-
- // Simulate an up so that RootView is no longer targetting |v3|.
- GestureEventForTest g1_up(ui::ET_GESTURE_END, 110, 110, 0);
- details = root->OnEventFromSource(&g1_up);
- EXPECT_FALSE(details.dispatcher_destroyed);
- EXPECT_FALSE(details.target_destroyed);
-
- EXPECT_EQ(ui::ET_GESTURE_END, v2->last_gesture_event_type_);
-
- v1->Reset();
- v2->Reset();
- v3->Reset();
-
- // Gesture on |v1|
- GestureEventForTest g2(ui::ET_GESTURE_TAP, 80, 80, 0);
- details = root->OnEventFromSource(&g2);
- EXPECT_FALSE(details.dispatcher_destroyed);
- EXPECT_FALSE(details.target_destroyed);
-
- EXPECT_EQ(ui::ET_GESTURE_TAP, v1->last_gesture_event_type_);
- EXPECT_EQ(gfx::Point(80, 80), v1->location_);
- EXPECT_EQ(ui::ET_UNKNOWN, v2->last_gesture_event_type_);
-
- // Send event |g1| again. Even though the coordinates target |v3| it should go
- // to |v1| as that is the view the touch was initially down on.
- v1->last_gesture_event_type_ = ui::ET_UNKNOWN;
- v3->last_gesture_event_type_ = ui::ET_UNKNOWN;
- details = root->OnEventFromSource(&g1);
- EXPECT_FALSE(details.dispatcher_destroyed);
- EXPECT_FALSE(details.target_destroyed);
-
- EXPECT_EQ(ui::ET_GESTURE_TAP, v1->last_gesture_event_type_);
- EXPECT_EQ(ui::ET_UNKNOWN, v3->last_gesture_event_type_);
- EXPECT_EQ("110,110", v1->location_.ToString());
-
- widget->CloseNow();
-}
-
-////////////////////////////////////////////////////////////////////////////////
// Painting
////////////////////////////////////////////////////////////////////////////////
-void TestView::Paint(gfx::Canvas* canvas) {
+void TestView::Paint(gfx::Canvas* canvas, const CullSet& cull_set) {
canvas->sk_canvas()->getClipBounds(&last_clip_);
}
}
namespace {
-class HitTestView : public View {
- public:
- explicit HitTestView(bool has_hittest_mask)
- : has_hittest_mask_(has_hittest_mask) {
- }
- virtual ~HitTestView() {}
-
- protected:
- // Overridden from View:
- virtual bool HasHitTestMask() const OVERRIDE {
- return has_hittest_mask_;
- }
- virtual void GetHitTestMask(HitTestSource source,
- gfx::Path* mask) const OVERRIDE {
- DCHECK(has_hittest_mask_);
- DCHECK(mask);
-
- SkScalar w = SkIntToScalar(width());
- SkScalar h = SkIntToScalar(height());
-
- // Create a triangular mask within the bounds of this View.
- mask->moveTo(w / 2, 0);
- mask->lineTo(w, h);
- mask->lineTo(0, h);
- mask->close();
- }
-
- private:
- bool has_hittest_mask_;
-
- DISALLOW_COPY_AND_ASSIGN(HitTestView);
-};
-
-gfx::Point ConvertPointToView(View* view, const gfx::Point& p) {
- gfx::Point tmp(p);
- View::ConvertPointToTarget(view->GetWidget()->GetRootView(), view, &tmp);
- return tmp;
-}
-
-gfx::Rect ConvertRectToView(View* view, const gfx::Rect& r) {
- gfx::Rect tmp(r);
- tmp.set_origin(ConvertPointToView(view, r.origin()));
- return tmp;
-}
void RotateCounterclockwise(gfx::Transform* transform) {
transform->matrix().set3x3(0, -1, 0,
} // namespace
-TEST_F(ViewTest, HitTestMasks) {
- Widget* widget = new Widget;
- Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP);
- widget->Init(params);
- View* root_view = widget->GetRootView();
- root_view->SetBoundsRect(gfx::Rect(0, 0, 500, 500));
-
- gfx::Rect v1_bounds = gfx::Rect(0, 0, 100, 100);
- HitTestView* v1 = new HitTestView(false);
- v1->SetBoundsRect(v1_bounds);
- root_view->AddChildView(v1);
-
- gfx::Rect v2_bounds = gfx::Rect(105, 0, 100, 100);
- HitTestView* v2 = new HitTestView(true);
- v2->SetBoundsRect(v2_bounds);
- root_view->AddChildView(v2);
-
- gfx::Point v1_centerpoint = v1_bounds.CenterPoint();
- gfx::Point v2_centerpoint = v2_bounds.CenterPoint();
- gfx::Point v1_origin = v1_bounds.origin();
- gfx::Point v2_origin = v2_bounds.origin();
-
- gfx::Rect r1(10, 10, 110, 15);
- gfx::Rect r2(106, 1, 98, 98);
- gfx::Rect r3(0, 0, 300, 300);
- gfx::Rect r4(115, 342, 200, 10);
-
- // Test HitTestPoint
- EXPECT_TRUE(v1->HitTestPoint(ConvertPointToView(v1, v1_centerpoint)));
- EXPECT_TRUE(v2->HitTestPoint(ConvertPointToView(v2, v2_centerpoint)));
-
- EXPECT_TRUE(v1->HitTestPoint(ConvertPointToView(v1, v1_origin)));
- EXPECT_FALSE(v2->HitTestPoint(ConvertPointToView(v2, v2_origin)));
-
- // Test HitTestRect
- EXPECT_TRUE(v1->HitTestRect(ConvertRectToView(v1, r1)));
- EXPECT_FALSE(v2->HitTestRect(ConvertRectToView(v2, r1)));
-
- EXPECT_FALSE(v1->HitTestRect(ConvertRectToView(v1, r2)));
- EXPECT_TRUE(v2->HitTestRect(ConvertRectToView(v2, r2)));
-
- EXPECT_TRUE(v1->HitTestRect(ConvertRectToView(v1, r3)));
- EXPECT_TRUE(v2->HitTestRect(ConvertRectToView(v2, r3)));
-
- EXPECT_FALSE(v1->HitTestRect(ConvertRectToView(v1, r4)));
- EXPECT_FALSE(v2->HitTestRect(ConvertRectToView(v2, r4)));
-
- // Test GetEventHandlerForPoint
- EXPECT_EQ(v1, root_view->GetEventHandlerForPoint(v1_centerpoint));
- EXPECT_EQ(v2, root_view->GetEventHandlerForPoint(v2_centerpoint));
-
- EXPECT_EQ(v1, root_view->GetEventHandlerForPoint(v1_origin));
- EXPECT_EQ(root_view, root_view->GetEventHandlerForPoint(v2_origin));
-
- // Test GetTooltipHandlerForPoint
- EXPECT_EQ(v1, root_view->GetTooltipHandlerForPoint(v1_centerpoint));
- EXPECT_EQ(v2, root_view->GetTooltipHandlerForPoint(v2_centerpoint));
-
- EXPECT_EQ(v1, root_view->GetTooltipHandlerForPoint(v1_origin));
- EXPECT_EQ(root_view, root_view->GetTooltipHandlerForPoint(v2_origin));
-
- EXPECT_FALSE(v1->GetTooltipHandlerForPoint(v2_origin));
-
- widget->CloseNow();
-}
-
// Tests the correctness of the rect-based targeting algorithm implemented in
// View::GetEventHandlerForRect(). See http://goo.gl/3Jp2BD for a description
// of rect-based targeting.
widget->CloseNow();
}
+// Tests that GetEventHandlerForRect() and GetTooltipHandlerForPoint() behave
+// as expected when different views in the view hierarchy return false
+// when CanProcessEventsWithinSubtree() is called.
+TEST_F(ViewTest, CanProcessEventsWithinSubtree) {
+ Widget* widget = new Widget;
+ Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP);
+ widget->Init(params);
+ View* root_view = widget->GetRootView();
+ root_view->SetBoundsRect(gfx::Rect(0, 0, 500, 500));
+
+ // Have this hierarchy of views (the coords here are in the coordinate
+ // space of the root view):
+ // v (0, 0, 100, 100)
+ // - v_child (0, 0, 20, 30)
+ // - v_grandchild (5, 5, 5, 15)
+
+ TestView* v = new TestView;
+ v->SetBounds(0, 0, 100, 100);
+ root_view->AddChildView(v);
+ v->set_notify_enter_exit_on_child(true);
+
+ TestView* v_child = new TestView;
+ v_child->SetBounds(0, 0, 20, 30);
+ v->AddChildView(v_child);
+
+ TestView* v_grandchild = new TestView;
+ v_grandchild->SetBounds(5, 5, 5, 15);
+ v_child->AddChildView(v_grandchild);
+
+ v->Reset();
+ v_child->Reset();
+ v_grandchild->Reset();
+
+ // Define rects and points within the views in the hierarchy.
+ gfx::Rect rect_in_v_grandchild(7, 7, 3, 3);
+ gfx::Point point_in_v_grandchild(rect_in_v_grandchild.origin());
+ gfx::Rect rect_in_v_child(12, 3, 5, 5);
+ gfx::Point point_in_v_child(rect_in_v_child.origin());
+ gfx::Rect rect_in_v(50, 50, 25, 30);
+ gfx::Point point_in_v(rect_in_v.origin());
+
+ // When all three views return true when CanProcessEventsWithinSubtree()
+ // is called, targeting should behave as expected.
+
+ View* result_view = root_view->GetEventHandlerForRect(rect_in_v_grandchild);
+ EXPECT_EQ(v_grandchild, result_view);
+ result_view = NULL;
+ result_view = root_view->GetTooltipHandlerForPoint(point_in_v_grandchild);
+ EXPECT_EQ(v_grandchild, result_view);
+ result_view = NULL;
+
+ result_view = root_view->GetEventHandlerForRect(rect_in_v_child);
+ EXPECT_EQ(v_child, result_view);
+ result_view = NULL;
+ result_view = root_view->GetTooltipHandlerForPoint(point_in_v_child);
+ EXPECT_EQ(v_child, result_view);
+ result_view = NULL;
+
+ result_view = root_view->GetEventHandlerForRect(rect_in_v);
+ EXPECT_EQ(v, result_view);
+ result_view = NULL;
+ result_view = root_view->GetTooltipHandlerForPoint(point_in_v);
+ EXPECT_EQ(v, result_view);
+ result_view = NULL;
+
+ // When |v_grandchild| returns false when CanProcessEventsWithinSubtree()
+ // is called, then |v_grandchild| cannot be returned as a target.
+
+ v_grandchild->set_can_process_events_within_subtree(false);
+
+ result_view = root_view->GetEventHandlerForRect(rect_in_v_grandchild);
+ EXPECT_EQ(v_child, result_view);
+ result_view = NULL;
+ result_view = root_view->GetTooltipHandlerForPoint(point_in_v_grandchild);
+ EXPECT_EQ(v_child, result_view);
+ result_view = NULL;
+
+ result_view = root_view->GetEventHandlerForRect(rect_in_v_child);
+ EXPECT_EQ(v_child, result_view);
+ result_view = NULL;
+ result_view = root_view->GetTooltipHandlerForPoint(point_in_v_child);
+ EXPECT_EQ(v_child, result_view);
+ result_view = NULL;
+
+ result_view = root_view->GetEventHandlerForRect(rect_in_v);
+ EXPECT_EQ(v, result_view);
+ result_view = NULL;
+ result_view = root_view->GetTooltipHandlerForPoint(point_in_v);
+ EXPECT_EQ(v, result_view);
+
+ // When |v_grandchild| returns false when CanProcessEventsWithinSubtree()
+ // is called, then NULL should be returned as a target if we call
+ // GetTooltipHandlerForPoint() with |v_grandchild| as the root of the
+ // views tree. Note that the location must be in the coordinate space
+ // of the root view (|v_grandchild| in this case), so use (1, 1).
+
+ result_view = v_grandchild;
+ result_view = v_grandchild->GetTooltipHandlerForPoint(gfx::Point(1, 1));
+ EXPECT_EQ(NULL, result_view);
+ result_view = NULL;
+
+ // When |v_child| returns false when CanProcessEventsWithinSubtree()
+ // is called, then neither |v_child| nor |v_grandchild| can be returned
+ // as a target (|v| should be returned as the target for each case).
+
+ v_grandchild->Reset();
+ v_child->set_can_process_events_within_subtree(false);
+
+ result_view = root_view->GetEventHandlerForRect(rect_in_v_grandchild);
+ EXPECT_EQ(v, result_view);
+ result_view = NULL;
+ result_view = root_view->GetTooltipHandlerForPoint(point_in_v_grandchild);
+ EXPECT_EQ(v, result_view);
+ result_view = NULL;
+
+ result_view = root_view->GetEventHandlerForRect(rect_in_v_child);
+ EXPECT_EQ(v, result_view);
+ result_view = NULL;
+ result_view = root_view->GetTooltipHandlerForPoint(point_in_v_child);
+ EXPECT_EQ(v, result_view);
+ result_view = NULL;
+
+ result_view = root_view->GetEventHandlerForRect(rect_in_v);
+ EXPECT_EQ(v, result_view);
+ result_view = NULL;
+ result_view = root_view->GetTooltipHandlerForPoint(point_in_v);
+ EXPECT_EQ(v, result_view);
+ result_view = NULL;
+
+ // When |v| returns false when CanProcessEventsWithinSubtree()
+ // is called, then none of |v|, |v_child|, and |v_grandchild| can be returned
+ // as a target (|root_view| should be returned as the target for each case).
+
+ v_child->Reset();
+ v->set_can_process_events_within_subtree(false);
+
+ result_view = root_view->GetEventHandlerForRect(rect_in_v_grandchild);
+ EXPECT_EQ(root_view, result_view);
+ result_view = NULL;
+ result_view = root_view->GetTooltipHandlerForPoint(point_in_v_grandchild);
+ EXPECT_EQ(root_view, result_view);
+ result_view = NULL;
+
+ result_view = root_view->GetEventHandlerForRect(rect_in_v_child);
+ EXPECT_EQ(root_view, result_view);
+ result_view = NULL;
+ result_view = root_view->GetTooltipHandlerForPoint(point_in_v_child);
+ EXPECT_EQ(root_view, result_view);
+ result_view = NULL;
+
+ result_view = root_view->GetEventHandlerForRect(rect_in_v);
+ EXPECT_EQ(root_view, result_view);
+ result_view = NULL;
+ result_view = root_view->GetTooltipHandlerForPoint(point_in_v);
+ EXPECT_EQ(root_view, result_view);
+}
+
TEST_F(ViewTest, NotifyEnterExitOnChild) {
Widget* widget = new Widget;
Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP);
const base::string16 kText = ASCIIToUTF16(
"Reality is that which, when you stop believing it, doesn't go away.");
const base::string16 kExtraText = ASCIIToUTF16("Pretty deep, Philip!");
- const base::string16 kEmptyString;
Widget* widget = new Widget;
Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP);
textfield->AppendText(kExtraText);
EXPECT_EQ(kText + kExtraText, textfield->text());
textfield->SetText(base::string16());
- EXPECT_EQ(kEmptyString, textfield->text());
+ EXPECT_TRUE(textfield->text().empty());
// Test selection related methods.
textfield->SetText(kText);
- EXPECT_EQ(kEmptyString, textfield->GetSelectedText());
+ EXPECT_TRUE(textfield->GetSelectedText().empty());
textfield->SelectAll(false);
EXPECT_EQ(kText, textfield->text());
textfield->ClearSelection();
- EXPECT_EQ(kEmptyString, textfield->GetSelectedText());
+ EXPECT_TRUE(textfield->GetSelectedText().empty());
widget->CloseNow();
}
// TODO: these tests were initially commented out when getting aura to
// run. Figure out if still valuable and either nuke or fix.
-#if defined(false)
+#if 0
TEST_F(ViewTest, ActivateAccelerator) {
// Register a keyboard accelerator before the view is added to a window.
ui::Accelerator return_accelerator(ui::VKEY_RETURN, ui::EF_NONE);
window1->CloseNow();
window2->CloseNow();
}
-#endif // false
+#endif // 0
////////////////////////////////////////////////////////////////////////////////
// Native view hierachy
private:
// Overridden from View:
- virtual bool NeedsNotificationWhenVisibleBoundsChange() const OVERRIDE {
+ virtual bool GetNeedsNotificationWhenVisibleBoundsChange() const OVERRIDE {
return true;
}
virtual void OnVisibleBoundsChanged() OVERRIDE {
child->SetTransform(t);
gfx::Point point_in_screen(100, 90);
- gfx::Point point_in_child(80,60);
+ gfx::Point point_in_child(80, 60);
gfx::Point point = point_in_screen;
View::ConvertPointFromScreen(child, &point);
}
////////////////////////////////////////////////////////////////////////////////
+// FocusManager
+////////////////////////////////////////////////////////////////////////////////
+
+// A widget that always claims to be active, regardless of its real activation
+// status.
+class ActiveWidget : public Widget {
+ public:
+ ActiveWidget() {}
+ virtual ~ActiveWidget() {}
+
+ virtual bool IsActive() const OVERRIDE {
+ return true;
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ActiveWidget);
+};
+
+TEST_F(ViewTest, AdvanceFocusIfNecessaryForUnfocusableView) {
+ // Create a widget with two views and give the first one focus.
+ ActiveWidget widget;
+ Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP);
+ params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
+ widget.Init(params);
+
+ View* view1 = new View();
+ view1->SetFocusable(true);
+ widget.GetRootView()->AddChildView(view1);
+ View* view2 = new View();
+ view2->SetFocusable(true);
+ widget.GetRootView()->AddChildView(view2);
+
+ FocusManager* focus_manager = widget.GetFocusManager();
+ ASSERT_TRUE(focus_manager);
+
+ focus_manager->SetFocusedView(view1);
+ EXPECT_EQ(view1, focus_manager->GetFocusedView());
+
+ // Disable the focused view and check if the next view gets focused.
+ view1->SetEnabled(false);
+ EXPECT_EQ(view2, focus_manager->GetFocusedView());
+
+ // Re-enable and re-focus.
+ view1->SetEnabled(true);
+ focus_manager->SetFocusedView(view1);
+ EXPECT_EQ(view1, focus_manager->GetFocusedView());
+
+ // Hide the focused view and check it the next view gets focused.
+ view1->SetVisible(false);
+ EXPECT_EQ(view2, focus_manager->GetFocusedView());
+
+ // Re-show and re-focus.
+ view1->SetVisible(true);
+ focus_manager->SetFocusedView(view1);
+ EXPECT_EQ(view1, focus_manager->GetFocusedView());
+
+ // Set the focused view as not focusable and check if the next view gets
+ // focused.
+ view1->SetFocusable(false);
+ EXPECT_EQ(view2, focus_manager->GetFocusedView());
+}
+
+////////////////////////////////////////////////////////////////////////////////
// Layers
////////////////////////////////////////////////////////////////////////////////
c1.reset();
}
-// Verify that new layer scales content only if the old layer does.
-TEST_F(ViewLayerTest, RecreateLayerScaling) {
- scoped_ptr<View> v(new View());
- v->SetPaintToLayer(true);
- // Set to non default value.
- v->layer()->set_scale_content(false);
- scoped_ptr<ui::Layer> old_layer(v->RecreateLayer());
- ui::Layer* new_layer = v->layer();
- EXPECT_FALSE(new_layer->scale_content());
-}
-
// Verify the z-order of the layers as a result of calling RecreateLayer().
TEST_F(ViewLayerTest, RecreateLayerZOrder) {
scoped_ptr<View> v(new View());
EXPECT_EQ(v.layer()->children()[1], child.layer());
}
+class BoundsTreeTestView : public View {
+ public:
+ BoundsTreeTestView() {}
+
+ virtual void PaintChildren(gfx::Canvas* canvas,
+ const CullSet& cull_set) OVERRIDE {
+ // Save out a copy of the cull_set before calling the base implementation.
+ last_cull_set_.clear();
+ if (cull_set.cull_set_) {
+ for (base::hash_set<intptr_t>::iterator it = cull_set.cull_set_->begin();
+ it != cull_set.cull_set_->end();
+ ++it) {
+ last_cull_set_.insert(reinterpret_cast<View*>(*it));
+ }
+ }
+ View::PaintChildren(canvas, cull_set);
+ }
+
+ std::set<View*> last_cull_set_;
+};
+
+TEST_F(ViewLayerTest, BoundsTreePaintUpdatesCullSet) {
+ BoundsTreeTestView* test_view = new BoundsTreeTestView;
+ widget()->SetContentsView(test_view);
+
+ View* v1 = new View();
+ v1->SetBoundsRect(gfx::Rect(10, 15, 150, 151));
+ test_view->AddChildView(v1);
+
+ View* v2 = new View();
+ v2->SetBoundsRect(gfx::Rect(20, 33, 40, 50));
+ v1->AddChildView(v2);
+
+ // Schedule a full-view paint to get everyone's rectangles updated.
+ test_view->SchedulePaintInRect(test_view->bounds());
+ GetRootLayer()->GetCompositor()->ScheduleDraw();
+ ui::DrawWaiterForTest::Wait(GetRootLayer()->GetCompositor());
+
+ // Now we have test_view - v1 - v2. Damage to only test_view should only
+ // return root_view and test_view.
+ test_view->SchedulePaintInRect(gfx::Rect(0, 0, 1, 1));
+ GetRootLayer()->GetCompositor()->ScheduleDraw();
+ ui::DrawWaiterForTest::Wait(GetRootLayer()->GetCompositor());
+ EXPECT_EQ(2U, test_view->last_cull_set_.size());
+ EXPECT_EQ(1U, test_view->last_cull_set_.count(widget()->GetRootView()));
+ EXPECT_EQ(1U, test_view->last_cull_set_.count(test_view));
+
+ // Damage to v1 only should only return root_view, test_view, and v1.
+ test_view->SchedulePaintInRect(gfx::Rect(11, 16, 1, 1));
+ GetRootLayer()->GetCompositor()->ScheduleDraw();
+ ui::DrawWaiterForTest::Wait(GetRootLayer()->GetCompositor());
+ EXPECT_EQ(3U, test_view->last_cull_set_.size());
+ EXPECT_EQ(1U, test_view->last_cull_set_.count(widget()->GetRootView()));
+ EXPECT_EQ(1U, test_view->last_cull_set_.count(test_view));
+ EXPECT_EQ(1U, test_view->last_cull_set_.count(v1));
+
+ // A Damage rect inside v2 should get all 3 views back in the |last_cull_set_|
+ // on call to TestView::Paint(), along with the widget root view.
+ test_view->SchedulePaintInRect(gfx::Rect(31, 49, 1, 1));
+ GetRootLayer()->GetCompositor()->ScheduleDraw();
+ ui::DrawWaiterForTest::Wait(GetRootLayer()->GetCompositor());
+ EXPECT_EQ(4U, test_view->last_cull_set_.size());
+ EXPECT_EQ(1U, test_view->last_cull_set_.count(widget()->GetRootView()));
+ EXPECT_EQ(1U, test_view->last_cull_set_.count(test_view));
+ EXPECT_EQ(1U, test_view->last_cull_set_.count(v1));
+ EXPECT_EQ(1U, test_view->last_cull_set_.count(v2));
+}
+
+TEST_F(ViewLayerTest, BoundsTreeWithRTL) {
+ std::string locale = l10n_util::GetApplicationLocale(std::string());
+ base::i18n::SetICUDefaultLocale("ar");
+
+ BoundsTreeTestView* test_view = new BoundsTreeTestView;
+ widget()->SetContentsView(test_view);
+
+ // Add child views, which should be in RTL coordinate space of parent view.
+ View* v1 = new View;
+ v1->SetBoundsRect(gfx::Rect(10, 12, 25, 26));
+ test_view->AddChildView(v1);
+
+ View* v2 = new View;
+ v2->SetBoundsRect(gfx::Rect(5, 6, 7, 8));
+ v1->AddChildView(v2);
+
+ // Schedule a full-view paint to get everyone's rectangles updated.
+ test_view->SchedulePaintInRect(test_view->bounds());
+ GetRootLayer()->GetCompositor()->ScheduleDraw();
+ ui::DrawWaiterForTest::Wait(GetRootLayer()->GetCompositor());
+
+ // Damage to the right side of the parent view should touch both child views.
+ gfx::Rect rtl_damage(test_view->bounds().width() - 16, 18, 1, 1);
+ test_view->SchedulePaintInRect(rtl_damage);
+ GetRootLayer()->GetCompositor()->ScheduleDraw();
+ ui::DrawWaiterForTest::Wait(GetRootLayer()->GetCompositor());
+ EXPECT_EQ(4U, test_view->last_cull_set_.size());
+ EXPECT_EQ(1U, test_view->last_cull_set_.count(widget()->GetRootView()));
+ EXPECT_EQ(1U, test_view->last_cull_set_.count(test_view));
+ EXPECT_EQ(1U, test_view->last_cull_set_.count(v1));
+ EXPECT_EQ(1U, test_view->last_cull_set_.count(v2));
+
+ // Damage to the left side of the parent view should only touch the
+ // container views.
+ gfx::Rect ltr_damage(16, 18, 1, 1);
+ test_view->SchedulePaintInRect(ltr_damage);
+ GetRootLayer()->GetCompositor()->ScheduleDraw();
+ ui::DrawWaiterForTest::Wait(GetRootLayer()->GetCompositor());
+ EXPECT_EQ(2U, test_view->last_cull_set_.size());
+ EXPECT_EQ(1U, test_view->last_cull_set_.count(widget()->GetRootView()));
+ EXPECT_EQ(1U, test_view->last_cull_set_.count(test_view));
+
+ // Reset locale.
+ base::i18n::SetICUDefaultLocale(locale);
+}
+
+TEST_F(ViewLayerTest, BoundsTreeSetBoundsChangesCullSet) {
+ BoundsTreeTestView* test_view = new BoundsTreeTestView;
+ widget()->SetContentsView(test_view);
+
+ View* v1 = new View;
+ v1->SetBoundsRect(gfx::Rect(5, 6, 100, 101));
+ test_view->AddChildView(v1);
+
+ View* v2 = new View;
+ v2->SetBoundsRect(gfx::Rect(20, 33, 40, 50));
+ v1->AddChildView(v2);
+
+ // Schedule a full-view paint to get everyone's rectangles updated.
+ test_view->SchedulePaintInRect(test_view->bounds());
+ GetRootLayer()->GetCompositor()->ScheduleDraw();
+ ui::DrawWaiterForTest::Wait(GetRootLayer()->GetCompositor());
+
+ // Move v1 to a new origin out of the way of our next query.
+ v1->SetBoundsRect(gfx::Rect(50, 60, 100, 101));
+ // The move will force a repaint.
+ GetRootLayer()->GetCompositor()->ScheduleDraw();
+ ui::DrawWaiterForTest::Wait(GetRootLayer()->GetCompositor());
+
+ // Schedule a paint with damage rect where v1 used to be.
+ test_view->SchedulePaintInRect(gfx::Rect(5, 6, 10, 11));
+ GetRootLayer()->GetCompositor()->ScheduleDraw();
+ ui::DrawWaiterForTest::Wait(GetRootLayer()->GetCompositor());
+
+ // Should only have picked up root_view and test_view.
+ EXPECT_EQ(2U, test_view->last_cull_set_.size());
+ EXPECT_EQ(1U, test_view->last_cull_set_.count(widget()->GetRootView()));
+ EXPECT_EQ(1U, test_view->last_cull_set_.count(test_view));
+}
+
+TEST_F(ViewLayerTest, BoundsTreeLayerChangeMakesNewTree) {
+ BoundsTreeTestView* test_view = new BoundsTreeTestView;
+ widget()->SetContentsView(test_view);
+
+ View* v1 = new View;
+ v1->SetBoundsRect(gfx::Rect(5, 10, 15, 20));
+ test_view->AddChildView(v1);
+
+ View* v2 = new View;
+ v2->SetBoundsRect(gfx::Rect(1, 2, 3, 4));
+ v1->AddChildView(v2);
+
+ // Schedule a full-view paint to get everyone's rectangles updated.
+ test_view->SchedulePaintInRect(test_view->bounds());
+ GetRootLayer()->GetCompositor()->ScheduleDraw();
+ ui::DrawWaiterForTest::Wait(GetRootLayer()->GetCompositor());
+
+ // Set v1 to paint to its own layer, it should remove itself from the
+ // test_view heiarchy and no longer intersect with damage rects in that cull
+ // set.
+ v1->SetPaintToLayer(true);
+
+ // Schedule another full-view paint.
+ test_view->SchedulePaintInRect(test_view->bounds());
+ GetRootLayer()->GetCompositor()->ScheduleDraw();
+ ui::DrawWaiterForTest::Wait(GetRootLayer()->GetCompositor());
+ // v1 and v2 should no longer be present in the test_view cull_set.
+ EXPECT_EQ(2U, test_view->last_cull_set_.size());
+ EXPECT_EQ(0U, test_view->last_cull_set_.count(v1));
+ EXPECT_EQ(0U, test_view->last_cull_set_.count(v2));
+
+ // Now set v1 back to not painting to a layer.
+ v1->SetPaintToLayer(false);
+ // Schedule another full-view paint.
+ test_view->SchedulePaintInRect(test_view->bounds());
+ GetRootLayer()->GetCompositor()->ScheduleDraw();
+ ui::DrawWaiterForTest::Wait(GetRootLayer()->GetCompositor());
+ // We should be back to the full cull set including v1 and v2.
+ EXPECT_EQ(4U, test_view->last_cull_set_.size());
+ EXPECT_EQ(1U, test_view->last_cull_set_.count(widget()->GetRootView()));
+ EXPECT_EQ(1U, test_view->last_cull_set_.count(test_view));
+ EXPECT_EQ(1U, test_view->last_cull_set_.count(v1));
+ EXPECT_EQ(1U, test_view->last_cull_set_.count(v2));
+}
+
+TEST_F(ViewLayerTest, BoundsTreeRemoveChildRemovesBounds) {
+ BoundsTreeTestView* test_view = new BoundsTreeTestView;
+ widget()->SetContentsView(test_view);
+
+ View* v1 = new View;
+ v1->SetBoundsRect(gfx::Rect(5, 10, 15, 20));
+ test_view->AddChildView(v1);
+
+ View* v2 = new View;
+ v2->SetBoundsRect(gfx::Rect(1, 2, 3, 4));
+ v1->AddChildView(v2);
+
+ // Schedule a full-view paint to get everyone's rectangles updated.
+ test_view->SchedulePaintInRect(test_view->bounds());
+ GetRootLayer()->GetCompositor()->ScheduleDraw();
+ ui::DrawWaiterForTest::Wait(GetRootLayer()->GetCompositor());
+
+ // Now remove v1 from the root view.
+ test_view->RemoveChildView(v1);
+
+ // Schedule another full-view paint.
+ test_view->SchedulePaintInRect(test_view->bounds());
+ GetRootLayer()->GetCompositor()->ScheduleDraw();
+ ui::DrawWaiterForTest::Wait(GetRootLayer()->GetCompositor());
+ // v1 and v2 should no longer be present in the test_view cull_set.
+ EXPECT_EQ(2U, test_view->last_cull_set_.size());
+ EXPECT_EQ(0U, test_view->last_cull_set_.count(v1));
+ EXPECT_EQ(0U, test_view->last_cull_set_.count(v2));
+
+ // View v1 and v2 are no longer part of view hierarchy and therefore won't be
+ // deleted with that hierarchy.
+ delete v1;
+}
+
+TEST_F(ViewLayerTest, BoundsTreeMoveViewMovesBounds) {
+ BoundsTreeTestView* test_view = new BoundsTreeTestView;
+ widget()->SetContentsView(test_view);
+
+ // Build hierarchy v1 - v2 - v3.
+ View* v1 = new View;
+ v1->SetBoundsRect(gfx::Rect(20, 30, 150, 160));
+ test_view->AddChildView(v1);
+
+ View* v2 = new View;
+ v2->SetBoundsRect(gfx::Rect(5, 10, 40, 50));
+ v1->AddChildView(v2);
+
+ View* v3 = new View;
+ v3->SetBoundsRect(gfx::Rect(1, 2, 3, 4));
+ v2->AddChildView(v3);
+
+ // Schedule a full-view paint and ensure all views are present in the cull.
+ test_view->SchedulePaintInRect(test_view->bounds());
+ GetRootLayer()->GetCompositor()->ScheduleDraw();
+ ui::DrawWaiterForTest::Wait(GetRootLayer()->GetCompositor());
+ EXPECT_EQ(5U, test_view->last_cull_set_.size());
+ EXPECT_EQ(1U, test_view->last_cull_set_.count(widget()->GetRootView()));
+ EXPECT_EQ(1U, test_view->last_cull_set_.count(test_view));
+ EXPECT_EQ(1U, test_view->last_cull_set_.count(v1));
+ EXPECT_EQ(1U, test_view->last_cull_set_.count(v2));
+ EXPECT_EQ(1U, test_view->last_cull_set_.count(v3));
+
+ // Build an unrelated view hierarchy and move v2 in to it.
+ scoped_ptr<Widget> test_widget(new Widget);
+ Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP);
+ params.bounds = gfx::Rect(10, 10, 500, 500);
+ params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
+ test_widget->Init(params);
+ test_widget->Show();
+ BoundsTreeTestView* widget_view = new BoundsTreeTestView;
+ test_widget->SetContentsView(widget_view);
+ widget_view->AddChildView(v2);
+
+ // Now schedule full-view paints in both widgets.
+ test_view->SchedulePaintInRect(test_view->bounds());
+ widget_view->SchedulePaintInRect(widget_view->bounds());
+ GetRootLayer()->GetCompositor()->ScheduleDraw();
+ ui::DrawWaiterForTest::Wait(GetRootLayer()->GetCompositor());
+
+ // Only v1 should be present in the first cull set.
+ EXPECT_EQ(3U, test_view->last_cull_set_.size());
+ EXPECT_EQ(1U, test_view->last_cull_set_.count(widget()->GetRootView()));
+ EXPECT_EQ(1U, test_view->last_cull_set_.count(test_view));
+ EXPECT_EQ(1U, test_view->last_cull_set_.count(v1));
+
+ // We should find v2 and v3 in the widget_view cull_set.
+ EXPECT_EQ(4U, widget_view->last_cull_set_.size());
+ EXPECT_EQ(1U, widget_view->last_cull_set_.count(test_widget->GetRootView()));
+ EXPECT_EQ(1U, widget_view->last_cull_set_.count(widget_view));
+ EXPECT_EQ(1U, widget_view->last_cull_set_.count(v2));
+ EXPECT_EQ(1U, widget_view->last_cull_set_.count(v3));
+}
+
+namespace {
+
+std::string ToString(const gfx::Vector2dF& vector) {
+ return base::StringPrintf("%.2f %0.2f", vector.x(), vector.y());
+}
+
+} // namespace
+
+TEST_F(ViewLayerTest, SnapLayerToPixel) {
+ View* v1 = new View;
+
+ View* v11 = new View;
+ v1->AddChildView(v11);
+
+ widget()->SetContentsView(v1);
+
+ const gfx::Size& size = GetRootLayer()->GetCompositor()->size();
+ GetRootLayer()->GetCompositor()->SetScaleAndSize(1.25f, size);
+
+ v11->SetBoundsRect(gfx::Rect(1, 1, 10, 10));
+ v1->SetBoundsRect(gfx::Rect(1, 1, 10, 10));
+ v11->SetPaintToLayer(true);
+
+ EXPECT_EQ("0.40 0.40", ToString(v11->layer()->subpixel_position_offset()));
+
+ // Creating a layer in parent should update the child view's layer offset.
+ v1->SetPaintToLayer(true);
+ EXPECT_EQ("-0.20 -0.20", ToString(v1->layer()->subpixel_position_offset()));
+ EXPECT_EQ("-0.20 -0.20", ToString(v11->layer()->subpixel_position_offset()));
+
+ // DSF change should get propagated and update offsets.
+ GetRootLayer()->GetCompositor()->SetScaleAndSize(1.5f, size);
+ EXPECT_EQ("0.33 0.33", ToString(v1->layer()->subpixel_position_offset()));
+ EXPECT_EQ("0.33 0.33", ToString(v11->layer()->subpixel_position_offset()));
+
+ // Deleting parent's layer should update the child view's layer's offset.
+ v1->SetPaintToLayer(false);
+ EXPECT_EQ("0.00 0.00", ToString(v11->layer()->subpixel_position_offset()));
+
+ // Setting parent view should update the child view's layer's offset.
+ v1->SetBoundsRect(gfx::Rect(2, 2, 10, 10));
+ EXPECT_EQ("0.33 0.33", ToString(v11->layer()->subpixel_position_offset()));
+
+ // Setting integral DSF should reset the offset.
+ GetRootLayer()->GetCompositor()->SetScaleAndSize(2.0f, size);
+ EXPECT_EQ("0.00 0.00", ToString(v11->layer()->subpixel_position_offset()));
+}
+
TEST_F(ViewTest, FocusableAssertions) {
// View subclasses may change insets based on whether they are focusable,
// which effects the preferred size. To avoid preferred size changing around
EXPECT_FALSE(view.focusable());
}
-// Creates a widget of TYPE_CONTROL.
-// The caller takes ownership of the returned widget.
-Widget* CreateControlWidget(aura::Window* parent, const gfx::Rect& bounds) {
- Widget::InitParams params(Widget::InitParams::TYPE_CONTROL);
- params.parent = parent;
- params.bounds = bounds;
- Widget* widget = new Widget();
- widget->Init(params);
- return widget;
-}
-
-// Returns a view with a layer with the passed in |bounds| and |layer_name|.
-// The caller takes ownership of the returned view.
-View* CreateViewWithLayer(const gfx::Rect& bounds,
- const char* layer_name) {
- View* view = new View();
- view->SetBoundsRect(bounds);
- view->SetPaintToLayer(true);
- view->layer()->set_name(layer_name);
- return view;
-}
-
-// Test that RecreateWindowLayers() recreates the layers for all child windows
-// and all child views and that the z-order of the recreated layers matches that
-// of the original layers.
-// Test hierarchy:
-// w1
-// +-- v1
-// +-- v2 (no layer)
-// +-- v3 (no layer)
-// +-- v4
-// +-- w2
-// +-- v5
-// +-- v6
-// +-- v7
-// +-- v8
-// +-- v9
-TEST_F(ViewTest, RecreateLayers) {
- Widget* w1 = CreateControlWidget(GetContext(), gfx::Rect(0, 0, 100, 100));
- w1->GetNativeView()->layer()->set_name("w1");
-
- View* v2 = new View();
- v2->SetBounds(0, 1, 100, 101);
- View* v3 = new View();
- v3->SetBounds(0, 2, 100, 102);
- View* w2_host_view = new View();
-
- View* v1 = CreateViewWithLayer(gfx::Rect(0, 3, 100, 103), "v1");
- ui::Layer* v1_layer = v1->layer();
- w1->GetRootView()->AddChildView(v1);
- w1->GetRootView()->AddChildView(v2);
- v2->AddChildView(v3);
- View* v4 = CreateViewWithLayer(gfx::Rect(0, 4, 100, 104), "v4");
- ui::Layer* v4_layer = v4->layer();
- v2->AddChildView(v4);
-
- w1->GetRootView()->AddChildView(w2_host_view);
- View* v7 = CreateViewWithLayer(gfx::Rect(0, 4, 100, 104), "v7");
- ui::Layer* v7_layer = v7->layer();
- w1->GetRootView()->AddChildView(v7);
-
- View* v8 = CreateViewWithLayer(gfx::Rect(0, 4, 100, 104), "v8");
- ui::Layer* v8_layer = v8->layer();
- v7->AddChildView(v8);
-
- View* v9 = CreateViewWithLayer(gfx::Rect(0, 4, 100, 104), "v9");
- ui::Layer* v9_layer = v9->layer();
- v7->AddChildView(v9);
-
- Widget* w2 = CreateControlWidget(w1->GetNativeView(),
- gfx::Rect(0, 5, 100, 105));
- w2->GetNativeView()->layer()->set_name("w2");
- w2->GetNativeView()->SetProperty(kHostViewKey, w2_host_view);
-
- View* v5 = CreateViewWithLayer(gfx::Rect(0, 6, 100, 106), "v5");
- w2->GetRootView()->AddChildView(v5);
- View* v6 = CreateViewWithLayer(gfx::Rect(0, 7, 100, 107), "v6");
- ui::Layer* v6_layer = v6->layer();
- v5->AddChildView(v6);
-
- // Test the initial order of the layers.
- ui::Layer* w1_layer = w1->GetNativeView()->layer();
- ASSERT_EQ("w1", w1_layer->name());
- ASSERT_EQ("v1 v4 w2 v7", ui::test::ChildLayerNamesAsString(*w1_layer));
- ui::Layer* w2_layer = w1_layer->children()[2];
- ASSERT_EQ("v5", ui::test::ChildLayerNamesAsString(*w2_layer));
- ui::Layer* v5_layer = w2_layer->children()[0];
- ASSERT_EQ("v6", ui::test::ChildLayerNamesAsString(*v5_layer));
-
- {
- scoped_ptr<ui::LayerTreeOwner> cloned_owner(
- wm::RecreateLayers(w1->GetNativeView()));
- EXPECT_EQ(w1_layer, cloned_owner->root());
- EXPECT_NE(w1_layer, w1->GetNativeView()->layer());
-
- // The old layers should still exist and have the same hierarchy.
- ASSERT_EQ("w1", w1_layer->name());
- ASSERT_EQ("v1 v4 w2 v7", ui::test::ChildLayerNamesAsString(*w1_layer));
- ASSERT_EQ("v5", ui::test::ChildLayerNamesAsString(*w2_layer));
- ASSERT_EQ("v6", ui::test::ChildLayerNamesAsString(*v5_layer));
- EXPECT_EQ("v8 v9", ui::test::ChildLayerNamesAsString(*v7_layer));
-
- ASSERT_EQ(4u, w1_layer->children().size());
- EXPECT_EQ(v1_layer, w1_layer->children()[0]);
- EXPECT_EQ(v4_layer, w1_layer->children()[1]);
- EXPECT_EQ(w2_layer, w1_layer->children()[2]);
- EXPECT_EQ(v7_layer, w1_layer->children()[3]);
-
- ASSERT_EQ(1u, w2_layer->children().size());
- EXPECT_EQ(v5_layer, w2_layer->children()[0]);
-
- ASSERT_EQ(1u, v5_layer->children().size());
- EXPECT_EQ(v6_layer, v5_layer->children()[0]);
-
- ASSERT_EQ(0u, v6_layer->children().size());
-
- EXPECT_EQ(2u, v7_layer->children().size());
- EXPECT_EQ(v8_layer, v7_layer->children()[0]);
- EXPECT_EQ(v9_layer, v7_layer->children()[1]);
-
- // The cloned layers should have the same hierarchy as old.
- ui::Layer* w1_new_layer = w1->GetNativeView()->layer();
- EXPECT_EQ("w1", w1_new_layer->name());
- ASSERT_EQ("v1 v4 w2 v7", ui::test::ChildLayerNamesAsString(*w1_new_layer));
- ui::Layer* w2_new_layer = w1_new_layer->children()[2];
- ASSERT_EQ("v5", ui::test::ChildLayerNamesAsString(*w2_new_layer));
- ui::Layer* v5_new_layer = w2_new_layer->children()[0];
- ASSERT_EQ("v6", ui::test::ChildLayerNamesAsString(*v5_new_layer));
- ui::Layer* v7_new_layer = w1_new_layer->children()[3];
- ASSERT_EQ("v8 v9", ui::test::ChildLayerNamesAsString(*v7_new_layer));
- }
- // The views and the widgets are destroyed when AuraTestHelper::TearDown()
- // destroys root_window().
-}
-
// Verifies when a view is deleted it is removed from ViewStorage.
TEST_F(ViewTest, UpdateViewStorageOnDelete) {
ViewStorage* view_storage = ViewStorage::GetInstance();
EXPECT_TRUE(view_storage->RetrieveView(storage_id) == NULL);
}
+////////////////////////////////////////////////////////////////////////////////
+// NativeTheme
+////////////////////////////////////////////////////////////////////////////////
+
+void TestView::OnNativeThemeChanged(const ui::NativeTheme* native_theme) {
+ native_theme_ = native_theme;
+}
+
+TEST_F(ViewTest, OnNativeThemeChanged) {
+ TestView* test_view = new TestView();
+ EXPECT_FALSE(test_view->native_theme_);
+ TestView* test_view_child = new TestView();
+ EXPECT_FALSE(test_view_child->native_theme_);
+
+ // Child view added before the widget hierarchy exists should get the
+ // new native theme notification.
+ test_view->AddChildView(test_view_child);
+
+ scoped_ptr<Widget> widget(new Widget);
+ Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW);
+ params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
+ widget->Init(params);
+
+ widget->GetRootView()->AddChildView(test_view);
+ EXPECT_TRUE(test_view->native_theme_);
+ EXPECT_EQ(widget->GetNativeTheme(), test_view->native_theme_);
+ EXPECT_TRUE(test_view_child->native_theme_);
+ EXPECT_EQ(widget->GetNativeTheme(), test_view_child->native_theme_);
+
+ // Child view added after the widget hierarchy exists should also get the
+ // notification.
+ TestView* test_view_child_2 = new TestView();
+ test_view->AddChildView(test_view_child_2);
+ EXPECT_TRUE(test_view_child_2->native_theme_);
+ EXPECT_EQ(widget->GetNativeTheme(), test_view_child_2->native_theme_);
+
+ widget->CloseNow();
+}
+
} // namespace views