view_unittest.cc revision 90dce4d38c5ff5333bea97d859d4e484e27edf0c
1// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include <map> 6 7#include "base/memory/scoped_ptr.h" 8#include "base/rand_util.h" 9#include "base/string_util.h" 10#include "base/utf_string_conversions.h" 11#include "grit/ui_strings.h" 12#include "testing/gmock/include/gmock/gmock.h" 13#include "ui/base/accelerators/accelerator.h" 14#include "ui/base/clipboard/clipboard.h" 15#include "ui/base/events/event.h" 16#include "ui/base/keycodes/keyboard_codes.h" 17#include "ui/base/l10n/l10n_util.h" 18#include "ui/base/models/simple_menu_model.h" 19#include "ui/compositor/compositor.h" 20#include "ui/compositor/layer.h" 21#include "ui/compositor/layer_animator.h" 22#include "ui/gfx/canvas.h" 23#include "ui/gfx/path.h" 24#include "ui/gfx/transform.h" 25#include "ui/views/background.h" 26#include "ui/views/controls/button/button_dropdown.h" 27#include "ui/views/controls/button/checkbox.h" 28#include "ui/views/controls/button/label_button.h" 29#include "ui/views/controls/native/native_view_host.h" 30#include "ui/views/controls/scroll_view.h" 31#include "ui/views/controls/textfield/textfield.h" 32#include "ui/views/focus/accelerator_handler.h" 33#include "ui/views/focus/view_storage.h" 34#include "ui/views/test/views_test_base.h" 35#include "ui/views/view.h" 36#include "ui/views/views_delegate.h" 37#include "ui/views/widget/native_widget.h" 38#include "ui/views/widget/root_view.h" 39#include "ui/views/window/dialog_client_view.h" 40#include "ui/views/window/dialog_delegate.h" 41 42#if defined(OS_WIN) 43#include "ui/views/test/test_views_delegate.h" 44#endif 45#if defined(USE_AURA) 46#include "ui/aura/root_window.h" 47#include "ui/base/gestures/gesture_recognizer.h" 48#endif 49 50using ::testing::_; 51 52namespace { 53 54// Returns true if |ancestor| is an ancestor of |layer|. 55bool LayerIsAncestor(const ui::Layer* ancestor, const ui::Layer* layer) { 56 while (layer && layer != ancestor) 57 layer = layer->parent(); 58 return layer == ancestor; 59} 60 61// Convenience functions for walking a View tree. 62const views::View* FirstView(const views::View* view) { 63 const views::View* v = view; 64 while (v->has_children()) 65 v = v->child_at(0); 66 return v; 67} 68 69const views::View* NextView(const views::View* view) { 70 const views::View* v = view; 71 const views::View* parent = v->parent(); 72 if (!parent) 73 return NULL; 74 int next = parent->GetIndexOf(v) + 1; 75 if (next != parent->child_count()) 76 return FirstView(parent->child_at(next)); 77 return parent; 78} 79 80// Convenience functions for walking a Layer tree. 81const ui::Layer* FirstLayer(const ui::Layer* layer) { 82 const ui::Layer* l = layer; 83 while (l->children().size() > 0) 84 l = l->children()[0]; 85 return l; 86} 87 88const ui::Layer* NextLayer(const ui::Layer* layer) { 89 const ui::Layer* parent = layer->parent(); 90 if (!parent) 91 return NULL; 92 const std::vector<ui::Layer*> children = parent->children(); 93 size_t index; 94 for (index = 0; index < children.size(); index++) { 95 if (children[index] == layer) 96 break; 97 } 98 size_t next = index + 1; 99 if (next < children.size()) 100 return FirstLayer(children[next]); 101 return parent; 102} 103 104// Given the root nodes of a View tree and a Layer tree, makes sure the two 105// trees are in sync. 106bool ViewAndLayerTreeAreConsistent(const views::View* view, 107 const ui::Layer* layer) { 108 const views::View* v = FirstView(view); 109 const ui::Layer* l = FirstLayer(layer); 110 while (v && l) { 111 // Find the view with a layer. 112 while (v && !v->layer()) 113 v = NextView(v); 114 EXPECT_TRUE(v); 115 if (!v) 116 return false; 117 118 // Check if the View tree and the Layer tree are in sync. 119 EXPECT_EQ(l, v->layer()); 120 if (v->layer() != l) 121 return false; 122 123 // Check if the visibility states of the View and the Layer are in sync. 124 EXPECT_EQ(l->IsDrawn(), v->IsDrawn()); 125 if (v->IsDrawn() != l->IsDrawn()) { 126 for (const views::View* vv = v; vv; vv = vv->parent()) 127 LOG(ERROR) << "V: " << vv << " " << vv->visible() << " " 128 << vv->IsDrawn() << " " << vv->layer(); 129 for (const ui::Layer* ll = l; ll; ll = ll->parent()) 130 LOG(ERROR) << "L: " << ll << " " << ll->IsDrawn(); 131 return false; 132 } 133 134 // Check if the size of the View and the Layer are in sync. 135 EXPECT_EQ(l->bounds(), v->bounds()); 136 if (v->bounds() != l->bounds()) 137 return false; 138 139 if (v == view || l == layer) 140 return v == view && l == layer; 141 142 v = NextView(v); 143 l = NextLayer(l); 144 } 145 146 return false; 147} 148 149// Constructs a View tree with the specified depth. 150void ConstructTree(views::View* view, int depth) { 151 if (depth == 0) 152 return; 153 int count = base::RandInt(1, 5); 154 for (int i = 0; i < count; i++) { 155 views::View* v = new views::View; 156 view->AddChildView(v); 157 if (base::RandDouble() > 0.5) 158 v->SetPaintToLayer(true); 159 if (base::RandDouble() < 0.2) 160 v->SetVisible(false); 161 162 ConstructTree(v, depth - 1); 163 } 164} 165 166void ScrambleTree(views::View* view) { 167 int count = view->child_count(); 168 if (count == 0) 169 return; 170 for (int i = 0; i < count; i++) { 171 ScrambleTree(view->child_at(i)); 172 } 173 174 if (count > 1) { 175 int a = base::RandInt(0, count - 1); 176 int b = base::RandInt(0, count - 1); 177 178 views::View* view_a = view->child_at(a); 179 views::View* view_b = view->child_at(b); 180 view->ReorderChildView(view_a, b); 181 view->ReorderChildView(view_b, a); 182 } 183 184 if (!view->layer() && base::RandDouble() < 0.1) 185 view->SetPaintToLayer(true); 186 187 if (base::RandDouble() < 0.1) 188 view->SetVisible(!view->visible()); 189} 190 191// Convenience to make constructing a GestureEvent simpler. 192class GestureEventForTest : public ui::GestureEvent { 193 public: 194 GestureEventForTest(ui::EventType type, int x, int y, int flags) 195 : GestureEvent(type, x, y, flags, base::TimeDelta(), 196 ui::GestureEventDetails(type, 0.0f, 0.0f), 0) { 197 } 198 199 private: 200 DISALLOW_COPY_AND_ASSIGN(GestureEventForTest); 201}; 202 203} // namespace 204 205namespace views { 206 207typedef ViewsTestBase ViewTest; 208 209// A derived class for testing purpose. 210class TestView : public View { 211 public: 212 TestView() : View(), delete_on_pressed_(false), in_touch_sequence_(false) {} 213 virtual ~TestView() {} 214 215 // Reset all test state 216 void Reset() { 217 did_change_bounds_ = false; 218 last_mouse_event_type_ = 0; 219 location_.SetPoint(0, 0); 220 received_mouse_enter_ = false; 221 received_mouse_exit_ = false; 222 last_touch_event_type_ = 0; 223 last_touch_event_was_handled_ = false; 224 last_gesture_event_type_ = 0; 225 last_gesture_event_was_handled_ = false; 226 last_clip_.setEmpty(); 227 accelerator_count_map_.clear(); 228 } 229 230 virtual void OnBoundsChanged(const gfx::Rect& previous_bounds) OVERRIDE; 231 virtual bool OnMousePressed(const ui::MouseEvent& event) OVERRIDE; 232 virtual bool OnMouseDragged(const ui::MouseEvent& event) OVERRIDE; 233 virtual void OnMouseReleased(const ui::MouseEvent& event) OVERRIDE; 234 virtual void OnMouseEntered(const ui::MouseEvent& event) OVERRIDE; 235 virtual void OnMouseExited(const ui::MouseEvent& event) OVERRIDE; 236 237 virtual void OnTouchEvent(ui::TouchEvent* event) OVERRIDE; 238 // Ignores GestureEvent by default. 239 virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE; 240 241 virtual void Paint(gfx::Canvas* canvas) OVERRIDE; 242 virtual void SchedulePaintInRect(const gfx::Rect& rect) OVERRIDE; 243 virtual bool AcceleratorPressed(const ui::Accelerator& accelerator) OVERRIDE; 244 245 // OnBoundsChanged. 246 bool did_change_bounds_; 247 gfx::Rect new_bounds_; 248 249 // MouseEvent. 250 int last_mouse_event_type_; 251 gfx::Point location_; 252 bool received_mouse_enter_; 253 bool received_mouse_exit_; 254 bool delete_on_pressed_; 255 256 // Painting. 257 std::vector<gfx::Rect> scheduled_paint_rects_; 258 259 // GestureEvent 260 int last_gesture_event_type_; 261 bool last_gesture_event_was_handled_; 262 263 // TouchEvent. 264 int last_touch_event_type_; 265 bool last_touch_event_was_handled_; 266 bool in_touch_sequence_; 267 268 // Painting. 269 SkRect last_clip_; 270 271 // Accelerators. 272 std::map<ui::Accelerator, int> accelerator_count_map_; 273}; 274 275// A view subclass that ignores all touch events for testing purposes. 276class TestViewIgnoreTouch : public TestView { 277 public: 278 TestViewIgnoreTouch() : TestView() {} 279 virtual ~TestViewIgnoreTouch() {} 280 281 private: 282 virtual void OnTouchEvent(ui::TouchEvent* event) OVERRIDE; 283}; 284 285// A view subclass that consumes all Gesture events for testing purposes. 286class TestViewConsumeGesture : public TestView { 287 public: 288 TestViewConsumeGesture() : TestView() {} 289 virtual ~TestViewConsumeGesture() {} 290 291 protected: 292 virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE { 293 last_gesture_event_type_ = event->type(); 294 location_.SetPoint(event->x(), event->y()); 295 event->StopPropagation(); 296 } 297 298 private: 299 DISALLOW_COPY_AND_ASSIGN(TestViewConsumeGesture); 300}; 301 302// A view subclass that ignores all Gesture events. 303class TestViewIgnoreGesture: public TestView { 304 public: 305 TestViewIgnoreGesture() : TestView() {} 306 virtual ~TestViewIgnoreGesture() {} 307 308 private: 309 virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE { 310 } 311 312 DISALLOW_COPY_AND_ASSIGN(TestViewIgnoreGesture); 313}; 314 315// A view subclass that ignores all scroll-gesture events, but consume all other 316// gesture events. 317class TestViewIgnoreScrollGestures : public TestViewConsumeGesture { 318 public: 319 TestViewIgnoreScrollGestures() {} 320 virtual ~TestViewIgnoreScrollGestures() {} 321 322 private: 323 virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE { 324 if (event->IsScrollGestureEvent()) 325 return; 326 TestViewConsumeGesture::OnGestureEvent(event); 327 } 328 329 DISALLOW_COPY_AND_ASSIGN(TestViewIgnoreScrollGestures); 330}; 331 332//////////////////////////////////////////////////////////////////////////////// 333// OnBoundsChanged 334//////////////////////////////////////////////////////////////////////////////// 335 336void TestView::OnBoundsChanged(const gfx::Rect& previous_bounds) { 337 did_change_bounds_ = true; 338 new_bounds_ = bounds(); 339} 340 341TEST_F(ViewTest, OnBoundsChanged) { 342 TestView v; 343 344 gfx::Rect prev_rect(0, 0, 200, 200); 345 gfx::Rect new_rect(100, 100, 250, 250); 346 347 v.SetBoundsRect(prev_rect); 348 v.Reset(); 349 v.SetBoundsRect(new_rect); 350 351 EXPECT_TRUE(v.did_change_bounds_); 352 EXPECT_EQ(v.new_bounds_, new_rect); 353 EXPECT_EQ(v.bounds(), new_rect); 354} 355 356//////////////////////////////////////////////////////////////////////////////// 357// MouseEvent 358//////////////////////////////////////////////////////////////////////////////// 359 360bool TestView::OnMousePressed(const ui::MouseEvent& event) { 361 last_mouse_event_type_ = event.type(); 362 location_.SetPoint(event.x(), event.y()); 363 if (delete_on_pressed_) 364 delete this; 365 return true; 366} 367 368bool TestView::OnMouseDragged(const ui::MouseEvent& event) { 369 last_mouse_event_type_ = event.type(); 370 location_.SetPoint(event.x(), event.y()); 371 return true; 372} 373 374void TestView::OnMouseReleased(const ui::MouseEvent& event) { 375 last_mouse_event_type_ = event.type(); 376 location_.SetPoint(event.x(), event.y()); 377} 378 379void TestView::OnMouseEntered(const ui::MouseEvent& event) { 380 received_mouse_enter_ = true; 381} 382 383void TestView::OnMouseExited(const ui::MouseEvent& event) { 384 received_mouse_exit_ = true; 385} 386 387TEST_F(ViewTest, MouseEvent) { 388 TestView* v1 = new TestView(); 389 v1->SetBoundsRect(gfx::Rect(0, 0, 300, 300)); 390 391 TestView* v2 = new TestView(); 392 v2->SetBoundsRect(gfx::Rect(100, 100, 100, 100)); 393 394 scoped_ptr<Widget> widget(new Widget); 395 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); 396 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 397 params.bounds = gfx::Rect(50, 50, 650, 650); 398 widget->Init(params); 399 internal::RootView* root = 400 static_cast<internal::RootView*>(widget->GetRootView()); 401 402 root->AddChildView(v1); 403 v1->AddChildView(v2); 404 405 v1->Reset(); 406 v2->Reset(); 407 408 gfx::Point p1(110, 120); 409 ui::MouseEvent pressed(ui::ET_MOUSE_PRESSED, p1, p1, 410 ui::EF_LEFT_MOUSE_BUTTON); 411 root->OnMousePressed(pressed); 412 EXPECT_EQ(v2->last_mouse_event_type_, ui::ET_MOUSE_PRESSED); 413 EXPECT_EQ(v2->location_.x(), 10); 414 EXPECT_EQ(v2->location_.y(), 20); 415 // Make sure v1 did not receive the event 416 EXPECT_EQ(v1->last_mouse_event_type_, 0); 417 418 // Drag event out of bounds. Should still go to v2 419 v1->Reset(); 420 v2->Reset(); 421 gfx::Point p2(50, 40); 422 ui::MouseEvent dragged(ui::ET_MOUSE_DRAGGED, p2, p2, 423 ui::EF_LEFT_MOUSE_BUTTON); 424 root->OnMouseDragged(dragged); 425 EXPECT_EQ(v2->last_mouse_event_type_, ui::ET_MOUSE_DRAGGED); 426 EXPECT_EQ(v2->location_.x(), -50); 427 EXPECT_EQ(v2->location_.y(), -60); 428 // Make sure v1 did not receive the event 429 EXPECT_EQ(v1->last_mouse_event_type_, 0); 430 431 // Releasted event out of bounds. Should still go to v2 432 v1->Reset(); 433 v2->Reset(); 434 ui::MouseEvent released(ui::ET_MOUSE_RELEASED, gfx::Point(), gfx::Point(), 0); 435 root->OnMouseDragged(released); 436 EXPECT_EQ(v2->last_mouse_event_type_, ui::ET_MOUSE_RELEASED); 437 EXPECT_EQ(v2->location_.x(), -100); 438 EXPECT_EQ(v2->location_.y(), -100); 439 // Make sure v1 did not receive the event 440 EXPECT_EQ(v1->last_mouse_event_type_, 0); 441 442 widget->CloseNow(); 443} 444 445// Confirm that a view can be deleted as part of processing a mouse press. 446TEST_F(ViewTest, DeleteOnPressed) { 447 TestView* v1 = new TestView(); 448 v1->SetBoundsRect(gfx::Rect(0, 0, 300, 300)); 449 450 TestView* v2 = new TestView(); 451 v2->SetBoundsRect(gfx::Rect(100, 100, 100, 100)); 452 453 v1->Reset(); 454 v2->Reset(); 455 456 scoped_ptr<Widget> widget(new Widget); 457 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); 458 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 459 params.bounds = gfx::Rect(50, 50, 650, 650); 460 widget->Init(params); 461 View* root = widget->GetRootView(); 462 463 root->AddChildView(v1); 464 v1->AddChildView(v2); 465 466 v2->delete_on_pressed_ = true; 467 gfx::Point point(110, 120); 468 ui::MouseEvent pressed(ui::ET_MOUSE_PRESSED, point, point, 469 ui::EF_LEFT_MOUSE_BUTTON); 470 root->OnMousePressed(pressed); 471 EXPECT_EQ(0, v1->child_count()); 472 473 widget->CloseNow(); 474} 475 476//////////////////////////////////////////////////////////////////////////////// 477// TouchEvent 478//////////////////////////////////////////////////////////////////////////////// 479void TestView::OnTouchEvent(ui::TouchEvent* event) { 480 last_touch_event_type_ = event->type(); 481 location_.SetPoint(event->x(), event->y()); 482 if (!in_touch_sequence_) { 483 if (event->type() == ui::ET_TOUCH_PRESSED) { 484 in_touch_sequence_ = true; 485 event->StopPropagation(); 486 return; 487 } 488 } else { 489 if (event->type() == ui::ET_TOUCH_RELEASED) { 490 in_touch_sequence_ = false; 491 event->SetHandled(); 492 return; 493 } 494 event->StopPropagation(); 495 return; 496 } 497 498 if (last_touch_event_was_handled_) 499 event->StopPropagation(); 500} 501 502void TestViewIgnoreTouch::OnTouchEvent(ui::TouchEvent* event) { 503} 504 505TEST_F(ViewTest, TouchEvent) { 506 TestView* v1 = new TestView(); 507 v1->SetBoundsRect(gfx::Rect(0, 0, 300, 300)); 508 509 TestView* v2 = new TestView(); 510 v2->SetBoundsRect(gfx::Rect(100, 100, 100, 100)); 511 512 TestView* v3 = new TestViewIgnoreTouch(); 513 v3->SetBoundsRect(gfx::Rect(0, 0, 100, 100)); 514 515 scoped_ptr<Widget> widget(new Widget()); 516 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); 517 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 518 params.bounds = gfx::Rect(50, 50, 650, 650); 519 widget->Init(params); 520 internal::RootView* root = 521 static_cast<internal::RootView*>(widget->GetRootView()); 522 523 root->AddChildView(v1); 524 v1->AddChildView(v2); 525 v2->AddChildView(v3); 526 527 // |v3| completely obscures |v2|, but all the touch events on |v3| should 528 // reach |v2| because |v3| doesn't process any touch events. 529 530 // Make sure if none of the views handle the touch event, the gesture manager 531 // does. 532 v1->Reset(); 533 v2->Reset(); 534 535 ui::TouchEvent unhandled(ui::ET_TOUCH_MOVED, 536 gfx::Point(400, 400), 537 0, /* no flags */ 538 0, /* first finger touch */ 539 base::TimeDelta(), 540 1.0, 0.0, 1.0, 0.0); 541 root->DispatchTouchEvent(&unhandled); 542 543 EXPECT_EQ(v1->last_touch_event_type_, 0); 544 EXPECT_EQ(v2->last_touch_event_type_, 0); 545 546 // Test press, drag, release touch sequence. 547 v1->Reset(); 548 v2->Reset(); 549 550 ui::TouchEvent pressed(ui::ET_TOUCH_PRESSED, 551 gfx::Point(110, 120), 552 0, /* no flags */ 553 0, /* first finger touch */ 554 base::TimeDelta(), 555 1.0, 0.0, 1.0, 0.0); 556 v2->last_touch_event_was_handled_ = true; 557 root->DispatchTouchEvent(&pressed); 558 559 EXPECT_EQ(v2->last_touch_event_type_, ui::ET_TOUCH_PRESSED); 560 EXPECT_EQ(v2->location_.x(), 10); 561 EXPECT_EQ(v2->location_.y(), 20); 562 // Make sure v1 did not receive the event 563 EXPECT_EQ(v1->last_touch_event_type_, 0); 564 565 // Drag event out of bounds. Should still go to v2 566 v1->Reset(); 567 v2->Reset(); 568 ui::TouchEvent dragged(ui::ET_TOUCH_MOVED, 569 gfx::Point(50, 40), 570 0, /* no flags */ 571 0, /* first finger touch */ 572 base::TimeDelta(), 573 1.0, 0.0, 1.0, 0.0); 574 575 root->DispatchTouchEvent(&dragged); 576 EXPECT_EQ(v2->last_touch_event_type_, ui::ET_TOUCH_MOVED); 577 EXPECT_EQ(v2->location_.x(), -50); 578 EXPECT_EQ(v2->location_.y(), -60); 579 // Make sure v1 did not receive the event 580 EXPECT_EQ(v1->last_touch_event_type_, 0); 581 582 // Released event out of bounds. Should still go to v2 583 v1->Reset(); 584 v2->Reset(); 585 ui::TouchEvent released(ui::ET_TOUCH_RELEASED, gfx::Point(), 586 0, /* no flags */ 587 0, /* first finger */ 588 base::TimeDelta(), 589 1.0, 0.0, 1.0, 0.0); 590 v2->last_touch_event_was_handled_ = true; 591 root->DispatchTouchEvent(&released); 592 EXPECT_EQ(v2->last_touch_event_type_, ui::ET_TOUCH_RELEASED); 593 EXPECT_EQ(v2->location_.x(), -100); 594 EXPECT_EQ(v2->location_.y(), -100); 595 // Make sure v1 did not receive the event 596 EXPECT_EQ(v1->last_touch_event_type_, 0); 597 598 widget->CloseNow(); 599} 600 601void TestView::OnGestureEvent(ui::GestureEvent* event) { 602} 603 604TEST_F(ViewTest, GestureEvent) { 605 // Views hierarchy for non delivery of GestureEvent. 606 TestView* v1 = new TestViewConsumeGesture(); 607 v1->SetBoundsRect(gfx::Rect(0, 0, 300, 300)); 608 609 TestView* v2 = new TestViewConsumeGesture(); 610 v2->SetBoundsRect(gfx::Rect(100, 100, 100, 100)); 611 612 TestView* v3 = new TestViewIgnoreGesture(); 613 v3->SetBoundsRect(gfx::Rect(0, 0, 100, 100)); 614 615 scoped_ptr<Widget> widget(new Widget()); 616 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); 617 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 618 params.bounds = gfx::Rect(50, 50, 650, 650); 619 widget->Init(params); 620 internal::RootView* root = 621 static_cast<internal::RootView*>(widget->GetRootView()); 622 623 root->AddChildView(v1); 624 v1->AddChildView(v2); 625 v2->AddChildView(v3); 626 627 // |v3| completely obscures |v2|, but all the gesture events on |v3| should 628 // reach |v2| because |v3| doesn't process any gesture events. However, since 629 // |v2| does process gesture events, gesture events on |v3| or |v2| should not 630 // reach |v1|. 631 632 v1->Reset(); 633 v2->Reset(); 634 v3->Reset(); 635 636 // Gesture on |v3| 637 GestureEventForTest g1(ui::ET_GESTURE_TAP, 110, 110, 0); 638 root->DispatchGestureEvent(&g1); 639 EXPECT_EQ(ui::ET_GESTURE_TAP, v2->last_gesture_event_type_); 640 EXPECT_EQ(gfx::Point(10, 10), v2->location_); 641 EXPECT_EQ(ui::ET_UNKNOWN, v1->last_gesture_event_type_); 642 643 // Simulate an up so that RootView is no longer targetting |v3|. 644 GestureEventForTest g1_up(ui::ET_GESTURE_END, 110, 110, 0); 645 root->DispatchGestureEvent(&g1_up); 646 647 v1->Reset(); 648 v2->Reset(); 649 v3->Reset(); 650 651 // Gesture on |v1| 652 GestureEventForTest g2(ui::ET_GESTURE_TAP, 80, 80, 0); 653 root->DispatchGestureEvent(&g2); 654 EXPECT_EQ(ui::ET_GESTURE_TAP, v1->last_gesture_event_type_); 655 EXPECT_EQ(gfx::Point(80, 80), v1->location_); 656 EXPECT_EQ(ui::ET_UNKNOWN, v2->last_gesture_event_type_); 657 658 // Send event |g1| again. Even though the coordinates target |v3| it should go 659 // to |v1| as that is the view the touch was initially down on. 660 v1->last_gesture_event_type_ = ui::ET_UNKNOWN; 661 v3->last_gesture_event_type_ = ui::ET_UNKNOWN; 662 root->DispatchGestureEvent(&g1); 663 EXPECT_EQ(ui::ET_GESTURE_TAP, v1->last_gesture_event_type_); 664 EXPECT_EQ(ui::ET_UNKNOWN, v3->last_gesture_event_type_); 665 EXPECT_EQ("110,110", v1->location_.ToString()); 666 667 widget->CloseNow(); 668} 669 670TEST_F(ViewTest, ScrollGestureEvent) { 671 // Views hierarchy for non delivery of GestureEvent. 672 TestView* v1 = new TestViewConsumeGesture(); 673 v1->SetBoundsRect(gfx::Rect(0, 0, 300, 300)); 674 675 TestView* v2 = new TestViewIgnoreScrollGestures(); 676 v2->SetBoundsRect(gfx::Rect(100, 100, 100, 100)); 677 678 TestView* v3 = new TestViewIgnoreGesture(); 679 v3->SetBoundsRect(gfx::Rect(0, 0, 100, 100)); 680 681 scoped_ptr<Widget> widget(new Widget()); 682 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); 683 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 684 params.bounds = gfx::Rect(50, 50, 650, 650); 685 widget->Init(params); 686 internal::RootView* root = 687 static_cast<internal::RootView*>(widget->GetRootView()); 688 689 root->AddChildView(v1); 690 v1->AddChildView(v2); 691 v2->AddChildView(v3); 692 693 // |v3| completely obscures |v2|, but all the gesture events on |v3| should 694 // reach |v2| because |v3| doesn't process any gesture events. However, since 695 // |v2| does process gesture events, gesture events on |v3| or |v2| should not 696 // reach |v1|. 697 698 v1->Reset(); 699 v2->Reset(); 700 v3->Reset(); 701 702 // Gesture on |v3| 703 GestureEventForTest g1(ui::ET_GESTURE_TAP, 110, 110, 0); 704 root->DispatchGestureEvent(&g1); 705 EXPECT_EQ(ui::ET_GESTURE_TAP, v2->last_gesture_event_type_); 706 EXPECT_EQ(gfx::Point(10, 10), v2->location_); 707 EXPECT_EQ(ui::ET_UNKNOWN, v1->last_gesture_event_type_); 708 709 v2->Reset(); 710 711 // Send scroll gestures on |v3|. The gesture should reach |v2|, however, 712 // since it does not process scroll-gesture events, these events should reach 713 // |v1|. 714 GestureEventForTest gscroll_begin(ui::ET_GESTURE_SCROLL_BEGIN, 115, 115, 0); 715 root->DispatchGestureEvent(&gscroll_begin); 716 EXPECT_EQ(ui::ET_UNKNOWN, v2->last_gesture_event_type_); 717 EXPECT_EQ(ui::ET_GESTURE_SCROLL_BEGIN, v1->last_gesture_event_type_); 718 v1->Reset(); 719 720 // Send a second tap on |v1|. The event should reach |v2| since it is the 721 // default gesture handler, and not |v1| (even though it is the view under the 722 // point, and is the scroll event handler). 723 GestureEventForTest second_tap(ui::ET_GESTURE_TAP, 70, 70, 0); 724 root->DispatchGestureEvent(&second_tap); 725 EXPECT_EQ(ui::ET_GESTURE_TAP, v2->last_gesture_event_type_); 726 EXPECT_EQ(ui::ET_UNKNOWN, v1->last_gesture_event_type_); 727 v2->Reset(); 728 729 GestureEventForTest gscroll_end(ui::ET_GESTURE_SCROLL_END, 50, 50, 0); 730 root->DispatchGestureEvent(&gscroll_end); 731 EXPECT_EQ(ui::ET_GESTURE_SCROLL_END, v1->last_gesture_event_type_); 732 v1->Reset(); 733 734 // Simulate an up so that RootView is no longer targetting |v3|. 735 GestureEventForTest g1_up(ui::ET_GESTURE_END, 110, 110, 0); 736 root->DispatchGestureEvent(&g1_up); 737 EXPECT_EQ(ui::ET_GESTURE_END, v2->last_gesture_event_type_); 738 739 v1->Reset(); 740 v2->Reset(); 741 v3->Reset(); 742 743 // Gesture on |v1| 744 GestureEventForTest g2(ui::ET_GESTURE_TAP, 80, 80, 0); 745 root->DispatchGestureEvent(&g2); 746 EXPECT_EQ(ui::ET_GESTURE_TAP, v1->last_gesture_event_type_); 747 EXPECT_EQ(gfx::Point(80, 80), v1->location_); 748 EXPECT_EQ(ui::ET_UNKNOWN, v2->last_gesture_event_type_); 749 750 // Send event |g1| again. Even though the coordinates target |v3| it should go 751 // to |v1| as that is the view the touch was initially down on. 752 v1->last_gesture_event_type_ = ui::ET_UNKNOWN; 753 v3->last_gesture_event_type_ = ui::ET_UNKNOWN; 754 root->DispatchGestureEvent(&g1); 755 EXPECT_EQ(ui::ET_GESTURE_TAP, v1->last_gesture_event_type_); 756 EXPECT_EQ(ui::ET_UNKNOWN, v3->last_gesture_event_type_); 757 EXPECT_EQ("110,110", v1->location_.ToString()); 758 759 widget->CloseNow(); 760} 761 762//////////////////////////////////////////////////////////////////////////////// 763// Painting 764//////////////////////////////////////////////////////////////////////////////// 765 766void TestView::Paint(gfx::Canvas* canvas) { 767 canvas->sk_canvas()->getClipBounds(&last_clip_); 768} 769 770void TestView::SchedulePaintInRect(const gfx::Rect& rect) { 771 scheduled_paint_rects_.push_back(rect); 772 View::SchedulePaintInRect(rect); 773} 774 775void CheckRect(const SkRect& check_rect, const SkRect& target_rect) { 776 EXPECT_EQ(target_rect.fLeft, check_rect.fLeft); 777 EXPECT_EQ(target_rect.fRight, check_rect.fRight); 778 EXPECT_EQ(target_rect.fTop, check_rect.fTop); 779 EXPECT_EQ(target_rect.fBottom, check_rect.fBottom); 780} 781 782/* This test is disabled because it is flakey on some systems. 783TEST_F(ViewTest, DISABLED_Painting) { 784 // Determine if InvalidateRect generates an empty paint rectangle. 785 EmptyWindow paint_window(CRect(50, 50, 650, 650)); 786 paint_window.RedrawWindow(CRect(0, 0, 600, 600), NULL, 787 RDW_UPDATENOW | RDW_INVALIDATE | RDW_ALLCHILDREN); 788 bool empty_paint = paint_window.empty_paint(); 789 790 NativeWidgetWin window; 791 window.set_delete_on_destroy(false); 792 window.set_window_style(WS_OVERLAPPEDWINDOW); 793 window.Init(NULL, gfx::Rect(50, 50, 650, 650), NULL); 794 View* root = window.GetRootView(); 795 796 TestView* v1 = new TestView(); 797 v1->SetBoundsRect(gfx::Rect(0, 0, 650, 650)); 798 root->AddChildView(v1); 799 800 TestView* v2 = new TestView(); 801 v2->SetBoundsRect(gfx::Rect(10, 10, 80, 80)); 802 v1->AddChildView(v2); 803 804 TestView* v3 = new TestView(); 805 v3->SetBoundsRect(gfx::Rect(10, 10, 60, 60)); 806 v2->AddChildView(v3); 807 808 TestView* v4 = new TestView(); 809 v4->SetBoundsRect(gfx::Rect(10, 200, 100, 100)); 810 v1->AddChildView(v4); 811 812 // Make sure to paint current rects 813 PaintRootView(root, empty_paint); 814 815 816 v1->Reset(); 817 v2->Reset(); 818 v3->Reset(); 819 v4->Reset(); 820 v3->SchedulePaintInRect(gfx::Rect(10, 10, 10, 10)); 821 PaintRootView(root, empty_paint); 822 823 SkRect tmp_rect; 824 825 tmp_rect.iset(10, 10, 20, 20); 826 CheckRect(v3->last_clip_, tmp_rect); 827 828 tmp_rect.iset(20, 20, 30, 30); 829 CheckRect(v2->last_clip_, tmp_rect); 830 831 tmp_rect.iset(30, 30, 40, 40); 832 CheckRect(v1->last_clip_, tmp_rect); 833 834 // Make sure v4 was not painted 835 tmp_rect.setEmpty(); 836 CheckRect(v4->last_clip_, tmp_rect); 837 838 window.DestroyWindow(); 839} 840*/ 841 842TEST_F(ViewTest, RemoveNotification) { 843 ViewStorage* vs = ViewStorage::GetInstance(); 844 Widget* widget = new Widget; 845 widget->Init(CreateParams(Widget::InitParams::TYPE_POPUP)); 846 View* root_view = widget->GetRootView(); 847 848 View* v1 = new View; 849 int s1 = vs->CreateStorageID(); 850 vs->StoreView(s1, v1); 851 root_view->AddChildView(v1); 852 View* v11 = new View; 853 int s11 = vs->CreateStorageID(); 854 vs->StoreView(s11, v11); 855 v1->AddChildView(v11); 856 View* v111 = new View; 857 int s111 = vs->CreateStorageID(); 858 vs->StoreView(s111, v111); 859 v11->AddChildView(v111); 860 View* v112 = new View; 861 int s112 = vs->CreateStorageID(); 862 vs->StoreView(s112, v112); 863 v11->AddChildView(v112); 864 View* v113 = new View; 865 int s113 = vs->CreateStorageID(); 866 vs->StoreView(s113, v113); 867 v11->AddChildView(v113); 868 View* v1131 = new View; 869 int s1131 = vs->CreateStorageID(); 870 vs->StoreView(s1131, v1131); 871 v113->AddChildView(v1131); 872 View* v12 = new View; 873 int s12 = vs->CreateStorageID(); 874 vs->StoreView(s12, v12); 875 v1->AddChildView(v12); 876 877 View* v2 = new View; 878 int s2 = vs->CreateStorageID(); 879 vs->StoreView(s2, v2); 880 root_view->AddChildView(v2); 881 View* v21 = new View; 882 int s21 = vs->CreateStorageID(); 883 vs->StoreView(s21, v21); 884 v2->AddChildView(v21); 885 View* v211 = new View; 886 int s211 = vs->CreateStorageID(); 887 vs->StoreView(s211, v211); 888 v21->AddChildView(v211); 889 890 size_t stored_views = vs->view_count(); 891 892 // Try removing a leaf view. 893 v21->RemoveChildView(v211); 894 EXPECT_EQ(stored_views - 1, vs->view_count()); 895 EXPECT_EQ(NULL, vs->RetrieveView(s211)); 896 delete v211; // We won't use this one anymore. 897 898 // Now try removing a view with a hierarchy of depth 1. 899 v11->RemoveChildView(v113); 900 EXPECT_EQ(stored_views - 3, vs->view_count()); 901 EXPECT_EQ(NULL, vs->RetrieveView(s113)); 902 EXPECT_EQ(NULL, vs->RetrieveView(s1131)); 903 delete v113; // We won't use this one anymore. 904 905 // Now remove even more. 906 root_view->RemoveChildView(v1); 907 EXPECT_EQ(NULL, vs->RetrieveView(s1)); 908 EXPECT_EQ(NULL, vs->RetrieveView(s11)); 909 EXPECT_EQ(NULL, vs->RetrieveView(s12)); 910 EXPECT_EQ(NULL, vs->RetrieveView(s111)); 911 EXPECT_EQ(NULL, vs->RetrieveView(s112)); 912 913 // Put v1 back for more tests. 914 root_view->AddChildView(v1); 915 vs->StoreView(s1, v1); 916 917 // Synchronously closing the window deletes the view hierarchy, which should 918 // remove all its views from ViewStorage. 919 widget->CloseNow(); 920 EXPECT_EQ(stored_views - 10, vs->view_count()); 921 EXPECT_EQ(NULL, vs->RetrieveView(s1)); 922 EXPECT_EQ(NULL, vs->RetrieveView(s12)); 923 EXPECT_EQ(NULL, vs->RetrieveView(s11)); 924 EXPECT_EQ(NULL, vs->RetrieveView(s12)); 925 EXPECT_EQ(NULL, vs->RetrieveView(s21)); 926 EXPECT_EQ(NULL, vs->RetrieveView(s111)); 927 EXPECT_EQ(NULL, vs->RetrieveView(s112)); 928} 929 930namespace { 931class HitTestView : public View { 932 public: 933 explicit HitTestView(bool has_hittest_mask) 934 : has_hittest_mask_(has_hittest_mask) { 935 } 936 virtual ~HitTestView() {} 937 938 protected: 939 // Overridden from View: 940 virtual bool HasHitTestMask() const OVERRIDE { 941 return has_hittest_mask_; 942 } 943 virtual void GetHitTestMask(gfx::Path* mask) const OVERRIDE { 944 DCHECK(has_hittest_mask_); 945 DCHECK(mask); 946 947 SkScalar w = SkIntToScalar(width()); 948 SkScalar h = SkIntToScalar(height()); 949 950 // Create a triangular mask within the bounds of this View. 951 mask->moveTo(w / 2, 0); 952 mask->lineTo(w, h); 953 mask->lineTo(0, h); 954 mask->close(); 955 } 956 957 private: 958 bool has_hittest_mask_; 959 960 DISALLOW_COPY_AND_ASSIGN(HitTestView); 961}; 962 963gfx::Point ConvertPointToView(View* view, const gfx::Point& p) { 964 gfx::Point tmp(p); 965 View::ConvertPointToTarget(view->GetWidget()->GetRootView(), view, &tmp); 966 return tmp; 967} 968 969gfx::Rect ConvertRectToView(View* view, const gfx::Rect& r) { 970 gfx::Rect tmp(r); 971 tmp.set_origin(ConvertPointToView(view, r.origin())); 972 return tmp; 973} 974 975void RotateCounterclockwise(gfx::Transform* transform) { 976 transform->matrix().set3x3(0, -1, 0, 977 1, 0, 0, 978 0, 0, 1); 979} 980 981void RotateClockwise(gfx::Transform* transform) { 982 transform->matrix().set3x3( 0, 1, 0, 983 -1, 0, 0, 984 0, 0, 1); 985} 986 987} // namespace 988 989TEST_F(ViewTest, HitTestMasks) { 990 Widget* widget = new Widget; 991 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); 992 widget->Init(params); 993 View* root_view = widget->GetRootView(); 994 root_view->SetBoundsRect(gfx::Rect(0, 0, 500, 500)); 995 996 gfx::Rect v1_bounds = gfx::Rect(0, 0, 100, 100); 997 HitTestView* v1 = new HitTestView(false); 998 v1->SetBoundsRect(v1_bounds); 999 root_view->AddChildView(v1); 1000 1001 gfx::Rect v2_bounds = gfx::Rect(105, 0, 100, 100); 1002 HitTestView* v2 = new HitTestView(true); 1003 v2->SetBoundsRect(v2_bounds); 1004 root_view->AddChildView(v2); 1005 1006 gfx::Point v1_centerpoint = v1_bounds.CenterPoint(); 1007 gfx::Point v2_centerpoint = v2_bounds.CenterPoint(); 1008 gfx::Point v1_origin = v1_bounds.origin(); 1009 gfx::Point v2_origin = v2_bounds.origin(); 1010 1011 gfx::Rect r1(10, 10, 110, 15); 1012 gfx::Rect r2(106, 1, 98, 98); 1013 gfx::Rect r3(0, 0, 300, 300); 1014 gfx::Rect r4(115, 342, 200, 10); 1015 1016 // Test HitTestPoint 1017 EXPECT_TRUE(v1->HitTestPoint(ConvertPointToView(v1, v1_centerpoint))); 1018 EXPECT_TRUE(v2->HitTestPoint(ConvertPointToView(v2, v2_centerpoint))); 1019 1020 EXPECT_TRUE(v1->HitTestPoint(ConvertPointToView(v1, v1_origin))); 1021 EXPECT_FALSE(v2->HitTestPoint(ConvertPointToView(v2, v2_origin))); 1022 1023 // Test HitTestRect 1024 EXPECT_TRUE(v1->HitTestRect(ConvertRectToView(v1, r1))); 1025 EXPECT_FALSE(v2->HitTestRect(ConvertRectToView(v2, r1))); 1026 1027 EXPECT_FALSE(v1->HitTestRect(ConvertRectToView(v1, r2))); 1028 EXPECT_TRUE(v2->HitTestRect(ConvertRectToView(v2, r2))); 1029 1030 EXPECT_TRUE(v1->HitTestRect(ConvertRectToView(v1, r3))); 1031 EXPECT_TRUE(v2->HitTestRect(ConvertRectToView(v2, r3))); 1032 1033 EXPECT_FALSE(v1->HitTestRect(ConvertRectToView(v1, r4))); 1034 EXPECT_FALSE(v2->HitTestRect(ConvertRectToView(v2, r4))); 1035 1036 // Test GetEventHandlerForPoint 1037 EXPECT_EQ(v1, root_view->GetEventHandlerForPoint(v1_centerpoint)); 1038 EXPECT_EQ(v2, root_view->GetEventHandlerForPoint(v2_centerpoint)); 1039 1040 EXPECT_EQ(v1, root_view->GetEventHandlerForPoint(v1_origin)); 1041 EXPECT_EQ(root_view, root_view->GetEventHandlerForPoint(v2_origin)); 1042 1043 // Test GetTooltipHandlerForPoint 1044 EXPECT_EQ(v1, root_view->GetTooltipHandlerForPoint(v1_centerpoint)); 1045 EXPECT_EQ(v2, root_view->GetTooltipHandlerForPoint(v2_centerpoint)); 1046 1047 EXPECT_EQ(v1, root_view->GetTooltipHandlerForPoint(v1_origin)); 1048 EXPECT_EQ(root_view, root_view->GetTooltipHandlerForPoint(v2_origin)); 1049 1050 EXPECT_FALSE(v1->GetTooltipHandlerForPoint(v2_origin)); 1051 1052 widget->CloseNow(); 1053} 1054 1055TEST_F(ViewTest, NotifyEnterExitOnChild) { 1056 Widget* widget = new Widget; 1057 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); 1058 widget->Init(params); 1059 View* root_view = widget->GetRootView(); 1060 root_view->SetBoundsRect(gfx::Rect(0, 0, 500, 500)); 1061 1062 // Have this hierarchy of views (the coords here are in root coord): 1063 // v1 (0, 0, 100, 100) 1064 // - v11 (0, 0, 20, 30) 1065 // - v111 (5, 5, 5, 15) 1066 // - v12 (50, 10, 30, 90) 1067 // - v121 (60, 20, 10, 10) 1068 // v2 (105, 0, 100, 100) 1069 // - v21 (120, 10, 50, 20) 1070 1071 TestView* v1 = new TestView; 1072 v1->SetBounds(0, 0, 100, 100); 1073 root_view->AddChildView(v1); 1074 v1->set_notify_enter_exit_on_child(true); 1075 1076 TestView* v11 = new TestView; 1077 v11->SetBounds(0, 0, 20, 30); 1078 v1->AddChildView(v11); 1079 1080 TestView* v111 = new TestView; 1081 v111->SetBounds(5, 5, 5, 15); 1082 v11->AddChildView(v111); 1083 1084 TestView* v12 = new TestView; 1085 v12->SetBounds(50, 10, 30, 90); 1086 v1->AddChildView(v12); 1087 1088 TestView* v121 = new TestView; 1089 v121->SetBounds(10, 10, 10, 10); 1090 v12->AddChildView(v121); 1091 1092 TestView* v2 = new TestView; 1093 v2->SetBounds(105, 0, 100, 100); 1094 root_view->AddChildView(v2); 1095 1096 TestView* v21 = new TestView; 1097 v21->SetBounds(15, 10, 50, 20); 1098 v2->AddChildView(v21); 1099 1100 v1->Reset(); 1101 v11->Reset(); 1102 v111->Reset(); 1103 v12->Reset(); 1104 v121->Reset(); 1105 v2->Reset(); 1106 v21->Reset(); 1107 1108 // Move the mouse in v111. 1109 gfx::Point p1(6, 6); 1110 ui::MouseEvent move1(ui::ET_MOUSE_MOVED, p1, p1, 0); 1111 root_view->OnMouseMoved(move1); 1112 EXPECT_TRUE(v111->received_mouse_enter_); 1113 EXPECT_FALSE(v11->last_mouse_event_type_); 1114 EXPECT_TRUE(v1->received_mouse_enter_); 1115 1116 v111->Reset(); 1117 v1->Reset(); 1118 1119 // Now, move into v121. 1120 gfx::Point p2(65, 21); 1121 ui::MouseEvent move2(ui::ET_MOUSE_MOVED, p2, p2, 0); 1122 root_view->OnMouseMoved(move2); 1123 EXPECT_TRUE(v111->received_mouse_exit_); 1124 EXPECT_TRUE(v121->received_mouse_enter_); 1125 EXPECT_FALSE(v1->last_mouse_event_type_); 1126 1127 v111->Reset(); 1128 v121->Reset(); 1129 1130 // Now, move into v11. 1131 gfx::Point p3(1, 1); 1132 ui::MouseEvent move3(ui::ET_MOUSE_MOVED, p3, p3, 0); 1133 root_view->OnMouseMoved(move3); 1134 EXPECT_TRUE(v121->received_mouse_exit_); 1135 EXPECT_TRUE(v11->received_mouse_enter_); 1136 EXPECT_FALSE(v1->last_mouse_event_type_); 1137 1138 v121->Reset(); 1139 v11->Reset(); 1140 1141 // Move to v21. 1142 gfx::Point p4(121, 15); 1143 ui::MouseEvent move4(ui::ET_MOUSE_MOVED, p4, p4, 0); 1144 root_view->OnMouseMoved(move4); 1145 EXPECT_TRUE(v21->received_mouse_enter_); 1146 EXPECT_FALSE(v2->last_mouse_event_type_); 1147 EXPECT_TRUE(v11->received_mouse_exit_); 1148 EXPECT_TRUE(v1->received_mouse_exit_); 1149 1150 v21->Reset(); 1151 v11->Reset(); 1152 v1->Reset(); 1153 1154 // Move to v1. 1155 gfx::Point p5(21, 0); 1156 ui::MouseEvent move5(ui::ET_MOUSE_MOVED, p5, p5, 0); 1157 root_view->OnMouseMoved(move5); 1158 EXPECT_TRUE(v21->received_mouse_exit_); 1159 EXPECT_TRUE(v1->received_mouse_enter_); 1160 1161 v21->Reset(); 1162 v1->Reset(); 1163 1164 // Now, move into v11. 1165 gfx::Point p6(15, 15); 1166 ui::MouseEvent mouse6(ui::ET_MOUSE_MOVED, p6, p6, 0); 1167 root_view->OnMouseMoved(mouse6); 1168 EXPECT_TRUE(v11->received_mouse_enter_); 1169 EXPECT_FALSE(v1->last_mouse_event_type_); 1170 1171 v11->Reset(); 1172 v1->Reset(); 1173 1174 // Move back into v1. Although |v1| had already received an ENTER for mouse6, 1175 // and the mouse remains inside |v1| the whole time, it receives another ENTER 1176 // when the mouse leaves v11. 1177 gfx::Point p7(21, 0); 1178 ui::MouseEvent mouse7(ui::ET_MOUSE_MOVED, p7, p7, 0); 1179 root_view->OnMouseMoved(mouse7); 1180 EXPECT_TRUE(v11->received_mouse_exit_); 1181 EXPECT_FALSE(v1->received_mouse_enter_); 1182 1183 widget->CloseNow(); 1184} 1185 1186TEST_F(ViewTest, Textfield) { 1187 const string16 kText = ASCIIToUTF16("Reality is that which, when you stop " 1188 "believing it, doesn't go away."); 1189 const string16 kExtraText = ASCIIToUTF16("Pretty deep, Philip!"); 1190 const string16 kEmptyString; 1191 1192 Widget* widget = new Widget; 1193 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); 1194 params.bounds = gfx::Rect(0, 0, 100, 100); 1195 widget->Init(params); 1196 View* root_view = widget->GetRootView(); 1197 1198 Textfield* textfield = new Textfield(); 1199 root_view->AddChildView(textfield); 1200 1201 // Test setting, appending text. 1202 textfield->SetText(kText); 1203 EXPECT_EQ(kText, textfield->text()); 1204 textfield->AppendText(kExtraText); 1205 EXPECT_EQ(kText + kExtraText, textfield->text()); 1206 textfield->SetText(string16()); 1207 EXPECT_EQ(kEmptyString, textfield->text()); 1208 1209 // Test selection related methods. 1210 textfield->SetText(kText); 1211 EXPECT_EQ(kEmptyString, textfield->GetSelectedText()); 1212 textfield->SelectAll(false); 1213 EXPECT_EQ(kText, textfield->text()); 1214 textfield->ClearSelection(); 1215 EXPECT_EQ(kEmptyString, textfield->GetSelectedText()); 1216 1217 widget->CloseNow(); 1218} 1219 1220// Tests that the Textfield view respond appropiately to cut/copy/paste. 1221TEST_F(ViewTest, TextfieldCutCopyPaste) { 1222 const string16 kNormalText = ASCIIToUTF16("Normal"); 1223 const string16 kReadOnlyText = ASCIIToUTF16("Read only"); 1224 const string16 kPasswordText = ASCIIToUTF16("Password! ** Secret stuff **"); 1225 1226 ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread(); 1227 1228 Widget* widget = new Widget; 1229 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); 1230 params.bounds = gfx::Rect(0, 0, 100, 100); 1231 widget->Init(params); 1232 View* root_view = widget->GetRootView(); 1233 1234 Textfield* normal = new Textfield(); 1235 Textfield* read_only = new Textfield(); 1236 read_only->SetReadOnly(true); 1237 Textfield* password = new Textfield(Textfield::STYLE_OBSCURED); 1238 1239 root_view->AddChildView(normal); 1240 root_view->AddChildView(read_only); 1241 root_view->AddChildView(password); 1242 1243 normal->SetText(kNormalText); 1244 read_only->SetText(kReadOnlyText); 1245 password->SetText(kPasswordText); 1246 1247 // 1248 // Test cut. 1249 // 1250 1251 normal->SelectAll(false); 1252 normal->ExecuteCommand(IDS_APP_CUT); 1253 string16 result; 1254 clipboard->ReadText(ui::Clipboard::BUFFER_STANDARD, &result); 1255 EXPECT_EQ(kNormalText, result); 1256 normal->SetText(kNormalText); // Let's revert to the original content. 1257 1258 read_only->SelectAll(false); 1259 read_only->ExecuteCommand(IDS_APP_CUT); 1260 result.clear(); 1261 clipboard->ReadText(ui::Clipboard::BUFFER_STANDARD, &result); 1262 // Cut should have failed, so the clipboard content should not have changed. 1263 EXPECT_EQ(kNormalText, result); 1264 1265 password->SelectAll(false); 1266 password->ExecuteCommand(IDS_APP_CUT); 1267 result.clear(); 1268 clipboard->ReadText(ui::Clipboard::BUFFER_STANDARD, &result); 1269 // Cut should have failed, so the clipboard content should not have changed. 1270 EXPECT_EQ(kNormalText, result); 1271 1272 // 1273 // Test copy. 1274 // 1275 1276 // Start with |read_only| to observe a change in clipboard text. 1277 read_only->SelectAll(false); 1278 read_only->ExecuteCommand(IDS_APP_COPY); 1279 result.clear(); 1280 clipboard->ReadText(ui::Clipboard::BUFFER_STANDARD, &result); 1281 EXPECT_EQ(kReadOnlyText, result); 1282 1283 normal->SelectAll(false); 1284 normal->ExecuteCommand(IDS_APP_COPY); 1285 result.clear(); 1286 clipboard->ReadText(ui::Clipboard::BUFFER_STANDARD, &result); 1287 EXPECT_EQ(kNormalText, result); 1288 1289 password->SelectAll(false); 1290 password->ExecuteCommand(IDS_APP_COPY); 1291 result.clear(); 1292 clipboard->ReadText(ui::Clipboard::BUFFER_STANDARD, &result); 1293 // Text cannot be copied from an obscured field; the clipboard won't change. 1294 EXPECT_EQ(kNormalText, result); 1295 1296 // 1297 // Test paste. 1298 // 1299 1300 // Attempting to paste kNormalText in a read-only text-field should fail. 1301 read_only->SelectAll(false); 1302 read_only->ExecuteCommand(IDS_APP_PASTE); 1303 EXPECT_EQ(kReadOnlyText, read_only->text()); 1304 1305 password->SelectAll(false); 1306 password->ExecuteCommand(IDS_APP_PASTE); 1307 EXPECT_EQ(kNormalText, password->text()); 1308 1309 // Copy from |read_only| to observe a change in the normal textfield text. 1310 read_only->SelectAll(false); 1311 read_only->ExecuteCommand(IDS_APP_COPY); 1312 normal->SelectAll(false); 1313 normal->ExecuteCommand(IDS_APP_PASTE); 1314 EXPECT_EQ(kReadOnlyText, normal->text()); 1315 widget->CloseNow(); 1316} 1317 1318//////////////////////////////////////////////////////////////////////////////// 1319// Accelerators 1320//////////////////////////////////////////////////////////////////////////////// 1321bool TestView::AcceleratorPressed(const ui::Accelerator& accelerator) { 1322 accelerator_count_map_[accelerator]++; 1323 return true; 1324} 1325 1326#if defined(OS_WIN) && !defined(USE_AURA) 1327TEST_F(ViewTest, ActivateAccelerator) { 1328 // Register a keyboard accelerator before the view is added to a window. 1329 ui::Accelerator return_accelerator(ui::VKEY_RETURN, ui::EF_NONE); 1330 TestView* view = new TestView(); 1331 view->Reset(); 1332 view->AddAccelerator(return_accelerator); 1333 EXPECT_EQ(view->accelerator_count_map_[return_accelerator], 0); 1334 1335 // Create a window and add the view as its child. 1336 scoped_ptr<Widget> widget(new Widget); 1337 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); 1338 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 1339 params.bounds = gfx::Rect(0, 0, 100, 100); 1340 widget->Init(params); 1341 View* root = widget->GetRootView(); 1342 root->AddChildView(view); 1343 widget->Show(); 1344 1345 // Get the focus manager. 1346 FocusManager* focus_manager = widget->GetFocusManager(); 1347 ASSERT_TRUE(focus_manager); 1348 1349 // Hit the return key and see if it takes effect. 1350 EXPECT_TRUE(focus_manager->ProcessAccelerator(return_accelerator)); 1351 EXPECT_EQ(view->accelerator_count_map_[return_accelerator], 1); 1352 1353 // Hit the escape key. Nothing should happen. 1354 ui::Accelerator escape_accelerator(ui::VKEY_ESCAPE, ui::EF_NONE); 1355 EXPECT_FALSE(focus_manager->ProcessAccelerator(escape_accelerator)); 1356 EXPECT_EQ(view->accelerator_count_map_[return_accelerator], 1); 1357 EXPECT_EQ(view->accelerator_count_map_[escape_accelerator], 0); 1358 1359 // Now register the escape key and hit it again. 1360 view->AddAccelerator(escape_accelerator); 1361 EXPECT_TRUE(focus_manager->ProcessAccelerator(escape_accelerator)); 1362 EXPECT_EQ(view->accelerator_count_map_[return_accelerator], 1); 1363 EXPECT_EQ(view->accelerator_count_map_[escape_accelerator], 1); 1364 1365 // Remove the return key accelerator. 1366 view->RemoveAccelerator(return_accelerator); 1367 EXPECT_FALSE(focus_manager->ProcessAccelerator(return_accelerator)); 1368 EXPECT_EQ(view->accelerator_count_map_[return_accelerator], 1); 1369 EXPECT_EQ(view->accelerator_count_map_[escape_accelerator], 1); 1370 1371 // Add it again. Hit the return key and the escape key. 1372 view->AddAccelerator(return_accelerator); 1373 EXPECT_TRUE(focus_manager->ProcessAccelerator(return_accelerator)); 1374 EXPECT_EQ(view->accelerator_count_map_[return_accelerator], 2); 1375 EXPECT_EQ(view->accelerator_count_map_[escape_accelerator], 1); 1376 EXPECT_TRUE(focus_manager->ProcessAccelerator(escape_accelerator)); 1377 EXPECT_EQ(view->accelerator_count_map_[return_accelerator], 2); 1378 EXPECT_EQ(view->accelerator_count_map_[escape_accelerator], 2); 1379 1380 // Remove all the accelerators. 1381 view->ResetAccelerators(); 1382 EXPECT_FALSE(focus_manager->ProcessAccelerator(return_accelerator)); 1383 EXPECT_EQ(view->accelerator_count_map_[return_accelerator], 2); 1384 EXPECT_EQ(view->accelerator_count_map_[escape_accelerator], 2); 1385 EXPECT_FALSE(focus_manager->ProcessAccelerator(escape_accelerator)); 1386 EXPECT_EQ(view->accelerator_count_map_[return_accelerator], 2); 1387 EXPECT_EQ(view->accelerator_count_map_[escape_accelerator], 2); 1388 1389 widget->CloseNow(); 1390} 1391#endif 1392 1393#if defined(OS_WIN) && !defined(USE_AURA) 1394TEST_F(ViewTest, HiddenViewWithAccelerator) { 1395 ui::Accelerator return_accelerator(ui::VKEY_RETURN, ui::EF_NONE); 1396 TestView* view = new TestView(); 1397 view->Reset(); 1398 view->AddAccelerator(return_accelerator); 1399 EXPECT_EQ(view->accelerator_count_map_[return_accelerator], 0); 1400 1401 scoped_ptr<Widget> widget(new Widget); 1402 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); 1403 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 1404 params.bounds = gfx::Rect(0, 0, 100, 100); 1405 widget->Init(params); 1406 View* root = widget->GetRootView(); 1407 root->AddChildView(view); 1408 widget->Show(); 1409 1410 FocusManager* focus_manager = widget->GetFocusManager(); 1411 ASSERT_TRUE(focus_manager); 1412 1413 view->SetVisible(false); 1414 EXPECT_FALSE(focus_manager->ProcessAccelerator(return_accelerator)); 1415 1416 view->SetVisible(true); 1417 EXPECT_TRUE(focus_manager->ProcessAccelerator(return_accelerator)); 1418 1419 widget->CloseNow(); 1420} 1421#endif 1422 1423#if defined(OS_WIN) && !defined(USE_AURA) 1424TEST_F(ViewTest, ViewInHiddenWidgetWithAccelerator) { 1425 ui::Accelerator return_accelerator(ui::VKEY_RETURN, ui::EF_NONE); 1426 TestView* view = new TestView(); 1427 view->Reset(); 1428 view->AddAccelerator(return_accelerator); 1429 EXPECT_EQ(view->accelerator_count_map_[return_accelerator], 0); 1430 1431 scoped_ptr<Widget> widget(new Widget); 1432 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); 1433 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 1434 params.bounds = gfx::Rect(0, 0, 100, 100); 1435 widget->Init(params); 1436 View* root = widget->GetRootView(); 1437 root->AddChildView(view); 1438 1439 FocusManager* focus_manager = widget->GetFocusManager(); 1440 ASSERT_TRUE(focus_manager); 1441 1442 EXPECT_FALSE(focus_manager->ProcessAccelerator(return_accelerator)); 1443 EXPECT_EQ(0, view->accelerator_count_map_[return_accelerator]); 1444 1445 widget->Show(); 1446 EXPECT_TRUE(focus_manager->ProcessAccelerator(return_accelerator)); 1447 EXPECT_EQ(1, view->accelerator_count_map_[return_accelerator]); 1448 1449 widget->Hide(); 1450 EXPECT_FALSE(focus_manager->ProcessAccelerator(return_accelerator)); 1451 EXPECT_EQ(1, view->accelerator_count_map_[return_accelerator]); 1452 1453 widget->CloseNow(); 1454} 1455#endif 1456 1457#if defined(OS_WIN) && !defined(USE_AURA) 1458//////////////////////////////////////////////////////////////////////////////// 1459// Mouse-wheel message rerouting 1460//////////////////////////////////////////////////////////////////////////////// 1461class ScrollableTestView : public View { 1462 public: 1463 ScrollableTestView() { } 1464 1465 virtual gfx::Size GetPreferredSize() { 1466 return gfx::Size(100, 10000); 1467 } 1468 1469 virtual void Layout() { 1470 SizeToPreferredSize(); 1471 } 1472}; 1473 1474class TestViewWithControls : public View { 1475 public: 1476 TestViewWithControls() { 1477 text_field_ = new Textfield(); 1478 AddChildView(text_field_); 1479 } 1480 1481 Textfield* text_field_; 1482}; 1483 1484class SimpleWidgetDelegate : public WidgetDelegate { 1485 public: 1486 explicit SimpleWidgetDelegate(View* contents) : contents_(contents) { } 1487 1488 virtual void DeleteDelegate() { delete this; } 1489 1490 virtual View* GetContentsView() { return contents_; } 1491 1492 virtual Widget* GetWidget() { return contents_->GetWidget(); } 1493 virtual const Widget* GetWidget() const { return contents_->GetWidget(); } 1494 1495 private: 1496 View* contents_; 1497}; 1498 1499// Tests that the mouse-wheel messages are correctly rerouted to the window 1500// under the mouse. 1501// TODO(jcampan): http://crbug.com/10572 Disabled as it fails on the Vista build 1502// bot. 1503// Note that this fails for a variety of reasons: 1504// - focused view is apparently reset across window activations and never 1505// properly restored 1506// - this test depends on you not having any other window visible open under the 1507// area that it opens the test windows. --beng 1508TEST_F(ViewTest, DISABLED_RerouteMouseWheelTest) { 1509 TestViewWithControls* view_with_controls = new TestViewWithControls(); 1510 Widget* window1 = Widget::CreateWindowWithBounds( 1511 new SimpleWidgetDelegate(view_with_controls), 1512 gfx::Rect(0, 0, 100, 100)); 1513 window1->Show(); 1514 ScrollView* scroll_view = new ScrollView(); 1515 scroll_view->SetContents(new ScrollableTestView()); 1516 Widget* window2 = Widget::CreateWindowWithBounds( 1517 new SimpleWidgetDelegate(scroll_view), 1518 gfx::Rect(200, 200, 100, 100)); 1519 window2->Show(); 1520 EXPECT_EQ(0, scroll_view->GetVisibleRect().y()); 1521 1522 // Make the window1 active, as this is what it would be in real-world. 1523 window1->Activate(); 1524 1525 // Let's send a mouse-wheel message to the different controls and check that 1526 // it is rerouted to the window under the mouse (effectively scrolling the 1527 // scroll-view). 1528 1529 // First to the Window's HWND. 1530 ::SendMessage(view_with_controls->GetWidget()->GetNativeView(), 1531 WM_MOUSEWHEEL, MAKEWPARAM(0, -20), MAKELPARAM(250, 250)); 1532 EXPECT_EQ(20, scroll_view->GetVisibleRect().y()); 1533 1534 // Then the text-field. 1535 ::SendMessage(view_with_controls->text_field_->GetTestingHandle(), 1536 WM_MOUSEWHEEL, MAKEWPARAM(0, -20), MAKELPARAM(250, 250)); 1537 EXPECT_EQ(80, scroll_view->GetVisibleRect().y()); 1538 1539 // Ensure we don't scroll when the mouse is not over that window. 1540 ::SendMessage(view_with_controls->text_field_->GetTestingHandle(), 1541 WM_MOUSEWHEEL, MAKEWPARAM(0, -20), MAKELPARAM(50, 50)); 1542 EXPECT_EQ(80, scroll_view->GetVisibleRect().y()); 1543 1544 window1->CloseNow(); 1545 window2->CloseNow(); 1546} 1547#endif 1548 1549//////////////////////////////////////////////////////////////////////////////// 1550// Dialogs' default button 1551//////////////////////////////////////////////////////////////////////////////// 1552 1553class MockMenuModel : public ui::MenuModel { 1554 public: 1555 MOCK_CONST_METHOD0(HasIcons, bool()); 1556 MOCK_CONST_METHOD0(GetItemCount, int()); 1557 MOCK_CONST_METHOD1(GetTypeAt, ItemType(int index)); 1558 MOCK_CONST_METHOD1(GetSeparatorTypeAt, ui::MenuSeparatorType(int index)); 1559 MOCK_CONST_METHOD1(GetCommandIdAt, int(int index)); 1560 MOCK_CONST_METHOD1(GetLabelAt, string16(int index)); 1561 MOCK_CONST_METHOD1(IsItemDynamicAt, bool(int index)); 1562 MOCK_CONST_METHOD1(GetLabelFontAt, const gfx::Font* (int index)); 1563 MOCK_CONST_METHOD2(GetAcceleratorAt, bool(int index, 1564 ui::Accelerator* accelerator)); 1565 MOCK_CONST_METHOD1(IsItemCheckedAt, bool(int index)); 1566 MOCK_CONST_METHOD1(GetGroupIdAt, int(int index)); 1567 MOCK_METHOD2(GetIconAt, bool(int index, gfx::Image* icon)); 1568 MOCK_CONST_METHOD1(GetButtonMenuItemAt, ui::ButtonMenuItemModel*(int index)); 1569 MOCK_CONST_METHOD1(IsEnabledAt, bool(int index)); 1570 MOCK_CONST_METHOD1(IsVisibleAt, bool(int index)); 1571 MOCK_CONST_METHOD1(GetSubmenuModelAt, MenuModel*(int index)); 1572 MOCK_METHOD1(HighlightChangedTo, void(int index)); 1573 MOCK_METHOD1(ActivatedAt, void(int index)); 1574 MOCK_METHOD2(ActivatedAt, void(int index, int disposition)); 1575 MOCK_METHOD0(MenuWillShow, void()); 1576 MOCK_METHOD0(MenuClosed, void()); 1577 MOCK_METHOD1(SetMenuModelDelegate, void(ui::MenuModelDelegate* delegate)); 1578 MOCK_CONST_METHOD0(GetMenuModelDelegate, ui::MenuModelDelegate*()); 1579 MOCK_METHOD3(GetModelAndIndexForCommandId, bool(int command_id, 1580 MenuModel** model, int* index)); 1581}; 1582 1583class TestDialog : public DialogDelegateView, public ButtonListener { 1584 public: 1585 explicit TestDialog(MockMenuModel* mock_menu_model) 1586 : button1_(NULL), 1587 button2_(NULL), 1588 checkbox_(NULL), 1589 button_drop_(NULL), 1590 last_pressed_button_(NULL), 1591 mock_menu_model_(mock_menu_model), 1592 canceled_(false), 1593 oked_(false), 1594 closeable_(false) { 1595 button1_ = new LabelButton(this, ASCIIToUTF16("Button1")); 1596 button2_ = new LabelButton(this, ASCIIToUTF16("Button2")); 1597 checkbox_ = new Checkbox(ASCIIToUTF16("My checkbox")); 1598 button_drop_ = new ButtonDropDown(this, mock_menu_model_); 1599 AddChildView(button1_); 1600 AddChildView(button2_); 1601 AddChildView(checkbox_); 1602 AddChildView(button_drop_); 1603 } 1604 1605 virtual ~TestDialog() {} 1606 1607 void TearDown() { 1608 // Now we can close safely. 1609 closeable_ = true; 1610 GetWidget()->Close(); 1611 } 1612 1613 // Prevent the dialog from really closing (so we can click the OK/Cancel 1614 // buttons to our heart's content). 1615 virtual bool Cancel() OVERRIDE { 1616 canceled_ = true; 1617 return closeable_; 1618 } 1619 virtual bool Accept() OVERRIDE { 1620 oked_ = true; 1621 return closeable_; 1622 } 1623 1624 // ButtonListener implementation. 1625 virtual void ButtonPressed(Button* sender, const ui::Event& event) OVERRIDE { 1626 last_pressed_button_ = sender; 1627 } 1628 1629 void ResetStates() { 1630 oked_ = false; 1631 canceled_ = false; 1632 last_pressed_button_ = NULL; 1633 } 1634 1635 // Set up expectations for methods that are called when an (empty) menu is 1636 // shown from a drop down button. 1637 void ExpectShowDropMenu() { 1638 if (mock_menu_model_) { 1639 EXPECT_CALL(*mock_menu_model_, HasIcons()); 1640 EXPECT_CALL(*mock_menu_model_, GetItemCount()); 1641 EXPECT_CALL(*mock_menu_model_, MenuClosed()); 1642 } 1643 } 1644 1645 LabelButton* button1_; 1646 LabelButton* button2_; 1647 Checkbox* checkbox_; 1648 ButtonDropDown* button_drop_; 1649 Button* last_pressed_button_; 1650 MockMenuModel* mock_menu_model_; 1651 1652 bool canceled_; 1653 bool oked_; 1654 bool closeable_; 1655 1656 private: 1657 DISALLOW_COPY_AND_ASSIGN(TestDialog); 1658}; 1659 1660class DefaultButtonTest : public ViewTest { 1661 public: 1662 enum ButtonID { OK, CANCEL, BUTTON1, BUTTON2, }; 1663 1664 DefaultButtonTest() : dialog_(NULL) {} 1665 virtual ~DefaultButtonTest() {} 1666 1667 virtual void SetUp() OVERRIDE { 1668 ViewTest::SetUp(); 1669 dialog_ = new TestDialog(NULL); 1670 DialogDelegate::CreateDialogWidget(dialog_, GetContext(), NULL)->Show(); 1671 } 1672 1673 virtual void TearDown() OVERRIDE { 1674 dialog_->TearDown(); 1675 ViewTest::TearDown(); 1676 } 1677 1678 protected: 1679 void SimulatePressingEnterAndCheckDefaultButton(ButtonID button_id) { 1680 ui::KeyEvent event(ui::ET_KEY_PRESSED, ui::VKEY_RETURN, 0, false); 1681 dialog_->GetFocusManager()->OnKeyEvent(event); 1682 switch (button_id) { 1683 case OK: 1684 EXPECT_TRUE(dialog_->oked_); 1685 EXPECT_FALSE(dialog_->canceled_); 1686 EXPECT_FALSE(dialog_->last_pressed_button_); 1687 break; 1688 case CANCEL: 1689 EXPECT_FALSE(dialog_->oked_); 1690 EXPECT_TRUE(dialog_->canceled_); 1691 EXPECT_FALSE(dialog_->last_pressed_button_); 1692 break; 1693 case BUTTON1: 1694 EXPECT_FALSE(dialog_->oked_); 1695 EXPECT_FALSE(dialog_->canceled_); 1696 EXPECT_TRUE(dialog_->last_pressed_button_ == dialog_->button1_); 1697 break; 1698 case BUTTON2: 1699 EXPECT_FALSE(dialog_->oked_); 1700 EXPECT_FALSE(dialog_->canceled_); 1701 EXPECT_TRUE(dialog_->last_pressed_button_ == dialog_->button2_); 1702 break; 1703 } 1704 dialog_->ResetStates(); 1705 } 1706 1707 TestDialog* dialog_; 1708 1709 private: 1710 DISALLOW_COPY_AND_ASSIGN(DefaultButtonTest); 1711}; 1712 1713TEST_F(DefaultButtonTest, DialogDefaultButtonTest) { 1714 DialogClientView* client_view = dialog_->GetDialogClientView(); 1715 LabelButton* ok_button = client_view->ok_button(); 1716 LabelButton* cancel_button = client_view->cancel_button(); 1717 1718 // Window has just been shown, we expect the default button specified in the 1719 // DialogDelegate. 1720 EXPECT_TRUE(ok_button->is_default()); 1721 1722 // Simulate pressing enter, that should trigger the OK button. 1723 SimulatePressingEnterAndCheckDefaultButton(OK); 1724 1725 // Simulate focusing another button, it should become the default button. 1726 client_view->OnWillChangeFocus(ok_button, dialog_->button1_); 1727 EXPECT_FALSE(ok_button->is_default()); 1728 EXPECT_TRUE(dialog_->button1_->is_default()); 1729 // Simulate pressing enter, that should trigger button1. 1730 SimulatePressingEnterAndCheckDefaultButton(BUTTON1); 1731 1732 // Now select something that is not a button, the OK should become the default 1733 // button again. 1734 client_view->OnWillChangeFocus(dialog_->button1_, dialog_->checkbox_); 1735 EXPECT_TRUE(ok_button->is_default()); 1736 EXPECT_FALSE(dialog_->button1_->is_default()); 1737 SimulatePressingEnterAndCheckDefaultButton(OK); 1738 1739 // Select yet another button. 1740 client_view->OnWillChangeFocus(dialog_->checkbox_, dialog_->button2_); 1741 EXPECT_FALSE(ok_button->is_default()); 1742 EXPECT_FALSE(dialog_->button1_->is_default()); 1743 EXPECT_TRUE(dialog_->button2_->is_default()); 1744 SimulatePressingEnterAndCheckDefaultButton(BUTTON2); 1745 1746 // Focus nothing. 1747 client_view->OnWillChangeFocus(dialog_->button2_, NULL); 1748 EXPECT_TRUE(ok_button->is_default()); 1749 EXPECT_FALSE(dialog_->button1_->is_default()); 1750 EXPECT_FALSE(dialog_->button2_->is_default()); 1751 SimulatePressingEnterAndCheckDefaultButton(OK); 1752 1753 // Focus the cancel button. 1754 client_view->OnWillChangeFocus(NULL, cancel_button); 1755 EXPECT_FALSE(ok_button->is_default()); 1756 EXPECT_TRUE(cancel_button->is_default()); 1757 EXPECT_FALSE(dialog_->button1_->is_default()); 1758 EXPECT_FALSE(dialog_->button2_->is_default()); 1759 SimulatePressingEnterAndCheckDefaultButton(CANCEL); 1760} 1761 1762class ButtonDropDownTest : public ViewTest { 1763 public: 1764 ButtonDropDownTest() : dialog_(NULL), button_as_view_(NULL) {} 1765 virtual ~ButtonDropDownTest() {} 1766 1767 virtual void SetUp() OVERRIDE { 1768 ViewTest::SetUp(); 1769 dialog_ = new TestDialog(new MockMenuModel()); 1770 DialogDelegate::CreateDialogWidget(dialog_, GetContext(), NULL)->Show(); 1771 dialog_->button_drop_->SetBoundsRect(gfx::Rect(0, 0, 100, 100)); 1772 // We have to cast the button back into a View in order to invoke it's 1773 // OnMouseReleased method. 1774 button_as_view_ = static_cast<View*>(dialog_->button_drop_); 1775 } 1776 1777 virtual void TearDown() OVERRIDE { 1778 dialog_->TearDown(); 1779 ViewTest::TearDown(); 1780 } 1781 1782 TestDialog* dialog_; 1783 // This is owned by dialog_. 1784 View* button_as_view_; 1785 1786 private: 1787 DISALLOW_COPY_AND_ASSIGN(ButtonDropDownTest); 1788}; 1789 1790// Ensure that regular clicks on the drop down button still work. (i.e. - the 1791// click events are processed and the listener gets the click) 1792TEST_F(ButtonDropDownTest, RegularClickTest) { 1793 gfx::Point point(1, 1); 1794 ui::MouseEvent press_event(ui::ET_MOUSE_PRESSED, point, point, 1795 ui::EF_LEFT_MOUSE_BUTTON); 1796 ui::MouseEvent release_event(ui::ET_MOUSE_RELEASED, point, point, 1797 ui::EF_LEFT_MOUSE_BUTTON); 1798 button_as_view_->OnMousePressed(press_event); 1799 button_as_view_->OnMouseReleased(release_event); 1800 EXPECT_EQ(dialog_->last_pressed_button_, dialog_->button_drop_); 1801} 1802 1803//////////////////////////////////////////////////////////////////////////////// 1804// Native view hierachy 1805//////////////////////////////////////////////////////////////////////////////// 1806class TestNativeViewHierarchy : public View { 1807 public: 1808 TestNativeViewHierarchy() { 1809 } 1810 1811 virtual void NativeViewHierarchyChanged( 1812 bool attached, 1813 gfx::NativeView native_view, 1814 internal::RootView* root_view) OVERRIDE { 1815 NotificationInfo info; 1816 info.attached = attached; 1817 info.native_view = native_view; 1818 info.root_view = root_view; 1819 notifications_.push_back(info); 1820 }; 1821 struct NotificationInfo { 1822 bool attached; 1823 gfx::NativeView native_view; 1824 internal::RootView* root_view; 1825 }; 1826 static const size_t kTotalViews = 2; 1827 std::vector<NotificationInfo> notifications_; 1828}; 1829 1830class TestChangeNativeViewHierarchy { 1831 public: 1832 explicit TestChangeNativeViewHierarchy(ViewTest *view_test) { 1833 view_test_ = view_test; 1834 native_host_ = new NativeViewHost(); 1835 host_ = new Widget; 1836 Widget::InitParams params = 1837 view_test->CreateParams(Widget::InitParams::TYPE_POPUP); 1838 params.bounds = gfx::Rect(0, 0, 500, 300); 1839 host_->Init(params); 1840 host_->GetRootView()->AddChildView(native_host_); 1841 for (size_t i = 0; i < TestNativeViewHierarchy::kTotalViews; ++i) { 1842 windows_[i] = new Widget; 1843 Widget::InitParams params(Widget::InitParams::TYPE_CONTROL); 1844 params.parent = host_->GetNativeView(); 1845 params.bounds = gfx::Rect(0, 0, 500, 300); 1846 windows_[i]->Init(params); 1847 root_views_[i] = windows_[i]->GetRootView(); 1848 test_views_[i] = new TestNativeViewHierarchy; 1849 root_views_[i]->AddChildView(test_views_[i]); 1850 } 1851 } 1852 1853 ~TestChangeNativeViewHierarchy() { 1854 for (size_t i = 0; i < TestNativeViewHierarchy::kTotalViews; ++i) { 1855 windows_[i]->Close(); 1856 } 1857 host_->Close(); 1858 // Will close and self-delete widgets - no need to manually delete them. 1859 view_test_->RunPendingMessages(); 1860 } 1861 1862 void CheckEnumeratingNativeWidgets() { 1863 if (!host_->GetTopLevelWidget()) 1864 return; 1865 Widget::Widgets widgets; 1866 Widget::GetAllChildWidgets(host_->GetNativeView(), &widgets); 1867 EXPECT_EQ(TestNativeViewHierarchy::kTotalViews + 1, widgets.size()); 1868 // Unfortunately there is no guarantee the sequence of views here so always 1869 // go through all of them. 1870 for (Widget::Widgets::iterator i = widgets.begin(); 1871 i != widgets.end(); ++i) { 1872 View* root_view = (*i)->GetRootView(); 1873 if (host_->GetRootView() == root_view) 1874 continue; 1875 size_t j; 1876 for (j = 0; j < TestNativeViewHierarchy::kTotalViews; ++j) 1877 if (root_views_[j] == root_view) 1878 break; 1879 // EXPECT_LT/GT/GE() fails to compile with class-defined constants 1880 // with gcc, with error 1881 // "error: undefined reference to 'TestNativeViewHierarchy::kTotalViews'" 1882 // so I forced to use EXPECT_TRUE() instead. 1883 EXPECT_TRUE(TestNativeViewHierarchy::kTotalViews > j); 1884 } 1885 } 1886 1887 void CheckChangingHierarhy() { 1888 size_t i; 1889 for (i = 0; i < TestNativeViewHierarchy::kTotalViews; ++i) { 1890 // TODO(georgey): use actual hierarchy changes to send notifications. 1891 static_cast<internal::RootView*>(root_views_[i])-> 1892 NotifyNativeViewHierarchyChanged(false, host_->GetNativeView()); 1893 static_cast<internal::RootView*>(root_views_[i])-> 1894 NotifyNativeViewHierarchyChanged(true, host_->GetNativeView()); 1895 } 1896 for (i = 0; i < TestNativeViewHierarchy::kTotalViews; ++i) { 1897 ASSERT_EQ(static_cast<size_t>(2), test_views_[i]->notifications_.size()); 1898 EXPECT_FALSE(test_views_[i]->notifications_[0].attached); 1899 EXPECT_EQ(host_->GetNativeView(), 1900 test_views_[i]->notifications_[0].native_view); 1901 EXPECT_EQ(root_views_[i], test_views_[i]->notifications_[0].root_view); 1902 EXPECT_TRUE(test_views_[i]->notifications_[1].attached); 1903 EXPECT_EQ(host_->GetNativeView(), 1904 test_views_[i]->notifications_[1].native_view); 1905 EXPECT_EQ(root_views_[i], test_views_[i]->notifications_[1].root_view); 1906 } 1907 } 1908 1909 NativeViewHost* native_host_; 1910 Widget* host_; 1911 Widget* windows_[TestNativeViewHierarchy::kTotalViews]; 1912 View* root_views_[TestNativeViewHierarchy::kTotalViews]; 1913 TestNativeViewHierarchy* test_views_[TestNativeViewHierarchy::kTotalViews]; 1914 ViewTest* view_test_; 1915}; 1916 1917TEST_F(ViewTest, ChangeNativeViewHierarchyFindRoots) { 1918 // TODO(georgey): Fix the test for Linux 1919#if defined(OS_WIN) 1920 TestChangeNativeViewHierarchy test(this); 1921 test.CheckEnumeratingNativeWidgets(); 1922#endif 1923} 1924 1925TEST_F(ViewTest, ChangeNativeViewHierarchyChangeHierarchy) { 1926 // TODO(georgey): Fix the test for Linux 1927#if defined(OS_WIN) 1928 TestChangeNativeViewHierarchy test(this); 1929 test.CheckChangingHierarhy(); 1930#endif 1931} 1932 1933//////////////////////////////////////////////////////////////////////////////// 1934// Transformations 1935//////////////////////////////////////////////////////////////////////////////// 1936 1937class TransformPaintView : public TestView { 1938 public: 1939 TransformPaintView() {} 1940 virtual ~TransformPaintView() {} 1941 1942 void ClearScheduledPaintRect() { 1943 scheduled_paint_rect_ = gfx::Rect(); 1944 } 1945 1946 gfx::Rect scheduled_paint_rect() const { return scheduled_paint_rect_; } 1947 1948 // Overridden from View: 1949 virtual void SchedulePaintInRect(const gfx::Rect& rect) OVERRIDE { 1950 gfx::Rect xrect = ConvertRectToParent(rect); 1951 scheduled_paint_rect_.Union(xrect); 1952 } 1953 1954 private: 1955 gfx::Rect scheduled_paint_rect_; 1956 1957 DISALLOW_COPY_AND_ASSIGN(TransformPaintView); 1958}; 1959 1960TEST_F(ViewTest, TransformPaint) { 1961 TransformPaintView* v1 = new TransformPaintView(); 1962 v1->SetBoundsRect(gfx::Rect(0, 0, 500, 300)); 1963 1964 TestView* v2 = new TestView(); 1965 v2->SetBoundsRect(gfx::Rect(100, 100, 200, 100)); 1966 1967 Widget* widget = new Widget; 1968 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); 1969 params.bounds = gfx::Rect(50, 50, 650, 650); 1970 widget->Init(params); 1971 widget->Show(); 1972 View* root = widget->GetRootView(); 1973 1974 root->AddChildView(v1); 1975 v1->AddChildView(v2); 1976 1977 // At this moment, |v2| occupies (100, 100) to (300, 200) in |root|. 1978 v1->ClearScheduledPaintRect(); 1979 v2->SchedulePaint(); 1980 1981 EXPECT_EQ(gfx::Rect(100, 100, 200, 100), v1->scheduled_paint_rect()); 1982 1983 // Rotate |v1| counter-clockwise. 1984 gfx::Transform transform; 1985 RotateCounterclockwise(&transform); 1986 transform.matrix().set(1, 3, 500.0); 1987 v1->SetTransform(transform); 1988 1989 // |v2| now occupies (100, 200) to (200, 400) in |root|. 1990 1991 v1->ClearScheduledPaintRect(); 1992 v2->SchedulePaint(); 1993 1994 EXPECT_EQ(gfx::Rect(100, 200, 100, 200), v1->scheduled_paint_rect()); 1995 1996 widget->CloseNow(); 1997} 1998 1999TEST_F(ViewTest, TransformEvent) { 2000 TestView* v1 = new TestView(); 2001 v1->SetBoundsRect(gfx::Rect(0, 0, 500, 300)); 2002 2003 TestView* v2 = new TestView(); 2004 v2->SetBoundsRect(gfx::Rect(100, 100, 200, 100)); 2005 2006 Widget* widget = new Widget; 2007 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); 2008 params.bounds = gfx::Rect(50, 50, 650, 650); 2009 widget->Init(params); 2010 View* root = widget->GetRootView(); 2011 2012 root->AddChildView(v1); 2013 v1->AddChildView(v2); 2014 2015 // At this moment, |v2| occupies (100, 100) to (300, 200) in |root|. 2016 2017 // Rotate |v1| counter-clockwise. 2018 gfx::Transform transform(v1->GetTransform()); 2019 RotateCounterclockwise(&transform); 2020 transform.matrix().set(1, 3, 500.0); 2021 v1->SetTransform(transform); 2022 2023 // |v2| now occupies (100, 200) to (200, 400) in |root|. 2024 v1->Reset(); 2025 v2->Reset(); 2026 2027 gfx::Point p1(110, 210); 2028 ui::MouseEvent pressed(ui::ET_MOUSE_PRESSED, p1, p1, 2029 ui::EF_LEFT_MOUSE_BUTTON); 2030 root->OnMousePressed(pressed); 2031 EXPECT_EQ(0, v1->last_mouse_event_type_); 2032 EXPECT_EQ(ui::ET_MOUSE_PRESSED, v2->last_mouse_event_type_); 2033 EXPECT_EQ(190, v2->location_.x()); 2034 EXPECT_EQ(10, v2->location_.y()); 2035 2036 ui::MouseEvent released(ui::ET_MOUSE_RELEASED, gfx::Point(), gfx::Point(), 0); 2037 root->OnMouseReleased(released); 2038 2039 // Now rotate |v2| inside |v1| clockwise. 2040 transform = v2->GetTransform(); 2041 RotateClockwise(&transform); 2042 transform.matrix().setDouble(0, 3, 100.0); 2043 v2->SetTransform(transform); 2044 2045 // Now, |v2| occupies (100, 100) to (200, 300) in |v1|, and (100, 300) to 2046 // (300, 400) in |root|. 2047 2048 v1->Reset(); 2049 v2->Reset(); 2050 2051 gfx::Point point2(110, 320); 2052 ui::MouseEvent p2(ui::ET_MOUSE_PRESSED, point2, point2, 2053 ui::EF_LEFT_MOUSE_BUTTON); 2054 root->OnMousePressed(p2); 2055 EXPECT_EQ(0, v1->last_mouse_event_type_); 2056 EXPECT_EQ(ui::ET_MOUSE_PRESSED, v2->last_mouse_event_type_); 2057 EXPECT_EQ(10, v2->location_.x()); 2058 EXPECT_EQ(20, v2->location_.y()); 2059 2060 root->OnMouseReleased(released); 2061 2062 v1->SetTransform(gfx::Transform()); 2063 v2->SetTransform(gfx::Transform()); 2064 2065 TestView* v3 = new TestView(); 2066 v3->SetBoundsRect(gfx::Rect(10, 10, 20, 30)); 2067 v2->AddChildView(v3); 2068 2069 // Rotate |v3| clockwise with respect to |v2|. 2070 transform = v1->GetTransform(); 2071 RotateClockwise(&transform); 2072 transform.matrix().setDouble(0, 3, 30.0); 2073 v3->SetTransform(transform); 2074 2075 // Scale |v2| with respect to |v1| along both axis. 2076 transform = v2->GetTransform(); 2077 transform.matrix().setDouble(0, 0, 0.8); 2078 transform.matrix().setDouble(1, 1, 0.5); 2079 v2->SetTransform(transform); 2080 2081 // |v3| occupies (108, 105) to (132, 115) in |root|. 2082 2083 v1->Reset(); 2084 v2->Reset(); 2085 v3->Reset(); 2086 2087 gfx::Point point(112, 110); 2088 ui::MouseEvent p3(ui::ET_MOUSE_PRESSED, point, point, 2089 ui::EF_LEFT_MOUSE_BUTTON); 2090 root->OnMousePressed(p3); 2091 2092 EXPECT_EQ(ui::ET_MOUSE_PRESSED, v3->last_mouse_event_type_); 2093 EXPECT_EQ(10, v3->location_.x()); 2094 EXPECT_EQ(25, v3->location_.y()); 2095 2096 root->OnMouseReleased(released); 2097 2098 v1->SetTransform(gfx::Transform()); 2099 v2->SetTransform(gfx::Transform()); 2100 v3->SetTransform(gfx::Transform()); 2101 2102 v1->Reset(); 2103 v2->Reset(); 2104 v3->Reset(); 2105 2106 // Rotate |v3| clockwise with respect to |v2|, and scale it along both axis. 2107 transform = v3->GetTransform(); 2108 RotateClockwise(&transform); 2109 transform.matrix().setDouble(0, 3, 30.0); 2110 // Rotation sets some scaling transformation. Using SetScale would overwrite 2111 // that and pollute the rotation. So combine the scaling with the existing 2112 // transforamtion. 2113 gfx::Transform scale; 2114 scale.Scale(0.8, 0.5); 2115 transform.ConcatTransform(scale); 2116 v3->SetTransform(transform); 2117 2118 // Translate |v2| with respect to |v1|. 2119 transform = v2->GetTransform(); 2120 transform.matrix().setDouble(0, 3, 10.0); 2121 transform.matrix().setDouble(1, 3, 10.0); 2122 v2->SetTransform(transform); 2123 2124 // |v3| now occupies (120, 120) to (144, 130) in |root|. 2125 2126 gfx::Point point3(124, 125); 2127 ui::MouseEvent p4(ui::ET_MOUSE_PRESSED, point3, point3, 2128 ui::EF_LEFT_MOUSE_BUTTON); 2129 root->OnMousePressed(p4); 2130 2131 EXPECT_EQ(ui::ET_MOUSE_PRESSED, v3->last_mouse_event_type_); 2132 EXPECT_EQ(10, v3->location_.x()); 2133 EXPECT_EQ(25, v3->location_.y()); 2134 2135 root->OnMouseReleased(released); 2136 2137 widget->CloseNow(); 2138} 2139 2140TEST_F(ViewTest, TransformVisibleBound) { 2141 gfx::Rect viewport_bounds(0, 0, 100, 100); 2142 2143 scoped_ptr<Widget> widget(new Widget); 2144 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); 2145 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 2146 params.bounds = viewport_bounds; 2147 widget->Init(params); 2148 widget->GetRootView()->SetBoundsRect(viewport_bounds); 2149 2150 View* viewport = new View; 2151 widget->SetContentsView(viewport); 2152 View* contents = new View; 2153 viewport->AddChildView(contents); 2154 viewport->SetBoundsRect(viewport_bounds); 2155 contents->SetBoundsRect(gfx::Rect(0, 0, 100, 200)); 2156 2157 View* child = new View; 2158 contents->AddChildView(child); 2159 child->SetBoundsRect(gfx::Rect(10, 90, 50, 50)); 2160 EXPECT_EQ(gfx::Rect(0, 0, 50, 10), child->GetVisibleBounds()); 2161 2162 // Rotate |child| counter-clockwise 2163 gfx::Transform transform; 2164 RotateCounterclockwise(&transform); 2165 transform.matrix().setDouble(1, 3, 50.0); 2166 child->SetTransform(transform); 2167 EXPECT_EQ(gfx::Rect(40, 0, 10, 50), child->GetVisibleBounds()); 2168 2169 widget->CloseNow(); 2170} 2171 2172//////////////////////////////////////////////////////////////////////////////// 2173// OnVisibleBoundsChanged() 2174 2175class VisibleBoundsView : public View { 2176 public: 2177 VisibleBoundsView() : received_notification_(false) {} 2178 virtual ~VisibleBoundsView() {} 2179 2180 bool received_notification() const { return received_notification_; } 2181 void set_received_notification(bool received) { 2182 received_notification_ = received; 2183 } 2184 2185 private: 2186 // Overridden from View: 2187 virtual bool NeedsNotificationWhenVisibleBoundsChange() const OVERRIDE { 2188 return true; 2189 } 2190 virtual void OnVisibleBoundsChanged() OVERRIDE { 2191 received_notification_ = true; 2192 } 2193 2194 bool received_notification_; 2195 2196 DISALLOW_COPY_AND_ASSIGN(VisibleBoundsView); 2197}; 2198 2199TEST_F(ViewTest, OnVisibleBoundsChanged) { 2200 gfx::Rect viewport_bounds(0, 0, 100, 100); 2201 2202 scoped_ptr<Widget> widget(new Widget); 2203 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); 2204 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 2205 params.bounds = viewport_bounds; 2206 widget->Init(params); 2207 widget->GetRootView()->SetBoundsRect(viewport_bounds); 2208 2209 View* viewport = new View; 2210 widget->SetContentsView(viewport); 2211 View* contents = new View; 2212 viewport->AddChildView(contents); 2213 viewport->SetBoundsRect(viewport_bounds); 2214 contents->SetBoundsRect(gfx::Rect(0, 0, 100, 200)); 2215 2216 // Create a view that cares about visible bounds notifications, and position 2217 // it just outside the visible bounds of the viewport. 2218 VisibleBoundsView* child = new VisibleBoundsView; 2219 contents->AddChildView(child); 2220 child->SetBoundsRect(gfx::Rect(10, 110, 50, 50)); 2221 2222 // The child bound should be fully clipped. 2223 EXPECT_TRUE(child->GetVisibleBounds().IsEmpty()); 2224 2225 // Now scroll the contents, but not enough to make the child visible. 2226 contents->SetY(contents->y() - 1); 2227 2228 // We should have received the notification since the visible bounds may have 2229 // changed (even though they didn't). 2230 EXPECT_TRUE(child->received_notification()); 2231 EXPECT_TRUE(child->GetVisibleBounds().IsEmpty()); 2232 child->set_received_notification(false); 2233 2234 // Now scroll the contents, this time by enough to make the child visible by 2235 // one pixel. 2236 contents->SetY(contents->y() - 10); 2237 EXPECT_TRUE(child->received_notification()); 2238 EXPECT_EQ(1, child->GetVisibleBounds().height()); 2239 child->set_received_notification(false); 2240 2241 widget->CloseNow(); 2242} 2243 2244//////////////////////////////////////////////////////////////////////////////// 2245// BoundsChanged() 2246 2247TEST_F(ViewTest, SetBoundsPaint) { 2248 TestView top_view; 2249 TestView* child_view = new TestView; 2250 2251 top_view.SetBoundsRect(gfx::Rect(0, 0, 100, 100)); 2252 top_view.scheduled_paint_rects_.clear(); 2253 child_view->SetBoundsRect(gfx::Rect(10, 10, 20, 20)); 2254 top_view.AddChildView(child_view); 2255 2256 top_view.scheduled_paint_rects_.clear(); 2257 child_view->SetBoundsRect(gfx::Rect(30, 30, 20, 20)); 2258 EXPECT_EQ(2U, top_view.scheduled_paint_rects_.size()); 2259 2260 // There should be 2 rects, spanning from (10, 10) to (50, 50). 2261 gfx::Rect paint_rect = top_view.scheduled_paint_rects_[0]; 2262 paint_rect.Union(top_view.scheduled_paint_rects_[1]); 2263 EXPECT_EQ(gfx::Rect(10, 10, 40, 40), paint_rect); 2264} 2265 2266// Tests conversion methods with a transform. 2267TEST_F(ViewTest, ConvertPointToViewWithTransform) { 2268 TestView top_view; 2269 TestView* child = new TestView; 2270 TestView* child_child = new TestView; 2271 2272 top_view.AddChildView(child); 2273 child->AddChildView(child_child); 2274 2275 top_view.SetBoundsRect(gfx::Rect(0, 0, 1000, 1000)); 2276 2277 child->SetBoundsRect(gfx::Rect(7, 19, 500, 500)); 2278 gfx::Transform transform; 2279 transform.Scale(3.0, 4.0); 2280 child->SetTransform(transform); 2281 2282 child_child->SetBoundsRect(gfx::Rect(17, 13, 100, 100)); 2283 transform.MakeIdentity(); 2284 transform.Scale(5.0, 7.0); 2285 child_child->SetTransform(transform); 2286 2287 // Sanity check to make sure basic transforms act as expected. 2288 { 2289 gfx::Transform transform; 2290 transform.Translate(110.0, -110.0); 2291 transform.Scale(100.0, 55.0); 2292 transform.Translate(1.0, 1.0); 2293 2294 // convert to a 3x3 matrix. 2295 const SkMatrix& matrix = transform.matrix(); 2296 2297 EXPECT_EQ(210, matrix.getTranslateX()); 2298 EXPECT_EQ(-55, matrix.getTranslateY()); 2299 EXPECT_EQ(100, matrix.getScaleX()); 2300 EXPECT_EQ(55, matrix.getScaleY()); 2301 EXPECT_EQ(0, matrix.getSkewX()); 2302 EXPECT_EQ(0, matrix.getSkewY()); 2303 } 2304 2305 { 2306 gfx::Transform transform; 2307 transform.Translate(1.0, 1.0); 2308 gfx::Transform t2; 2309 t2.Scale(100.0, 55.0); 2310 gfx::Transform t3; 2311 t3.Translate(110.0, -110.0); 2312 transform.ConcatTransform(t2); 2313 transform.ConcatTransform(t3); 2314 2315 // convert to a 3x3 matrix 2316 const SkMatrix& matrix = transform.matrix(); 2317 2318 EXPECT_EQ(210, matrix.getTranslateX()); 2319 EXPECT_EQ(-55, matrix.getTranslateY()); 2320 EXPECT_EQ(100, matrix.getScaleX()); 2321 EXPECT_EQ(55, matrix.getScaleY()); 2322 EXPECT_EQ(0, matrix.getSkewX()); 2323 EXPECT_EQ(0, matrix.getSkewY()); 2324 } 2325 2326 // Conversions from child->top and top->child. 2327 { 2328 gfx::Point point(5, 5); 2329 View::ConvertPointToTarget(child, &top_view, &point); 2330 EXPECT_EQ(22, point.x()); 2331 EXPECT_EQ(39, point.y()); 2332 2333 point.SetPoint(22, 39); 2334 View::ConvertPointToTarget(&top_view, child, &point); 2335 EXPECT_EQ(5, point.x()); 2336 EXPECT_EQ(5, point.y()); 2337 } 2338 2339 // Conversions from child_child->top and top->child_child. 2340 { 2341 gfx::Point point(5, 5); 2342 View::ConvertPointToTarget(child_child, &top_view, &point); 2343 EXPECT_EQ(133, point.x()); 2344 EXPECT_EQ(211, point.y()); 2345 2346 point.SetPoint(133, 211); 2347 View::ConvertPointToTarget(&top_view, child_child, &point); 2348 EXPECT_EQ(5, point.x()); 2349 EXPECT_EQ(5, point.y()); 2350 } 2351 2352 // Conversions from child_child->child and child->child_child 2353 { 2354 gfx::Point point(5, 5); 2355 View::ConvertPointToTarget(child_child, child, &point); 2356 EXPECT_EQ(42, point.x()); 2357 EXPECT_EQ(48, point.y()); 2358 2359 point.SetPoint(42, 48); 2360 View::ConvertPointToTarget(child, child_child, &point); 2361 EXPECT_EQ(5, point.x()); 2362 EXPECT_EQ(5, point.y()); 2363 } 2364 2365 // Conversions from top_view to child with a value that should be negative. 2366 // This ensures we don't round up with negative numbers. 2367 { 2368 gfx::Point point(6, 18); 2369 View::ConvertPointToTarget(&top_view, child, &point); 2370 EXPECT_EQ(-1, point.x()); 2371 EXPECT_EQ(-1, point.y()); 2372 } 2373} 2374 2375// Tests conversion methods for rectangles. 2376TEST_F(ViewTest, ConvertRectWithTransform) { 2377 scoped_ptr<Widget> widget(new Widget); 2378 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); 2379 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 2380 params.bounds = gfx::Rect(50, 50, 650, 650); 2381 widget->Init(params); 2382 View* root = widget->GetRootView(); 2383 2384 TestView* v1 = new TestView; 2385 TestView* v2 = new TestView; 2386 root->AddChildView(v1); 2387 v1->AddChildView(v2); 2388 2389 v1->SetBoundsRect(gfx::Rect(10, 10, 500, 500)); 2390 v2->SetBoundsRect(gfx::Rect(20, 20, 100, 200)); 2391 2392 // |v2| now occupies (30, 30) to (130, 230) in |widget| 2393 gfx::Rect rect(5, 5, 15, 40); 2394 EXPECT_EQ(gfx::Rect(25, 25, 15, 40), v2->ConvertRectToParent(rect)); 2395 EXPECT_EQ(gfx::Rect(35, 35, 15, 40), v2->ConvertRectToWidget(rect)); 2396 2397 // Rotate |v2| 2398 gfx::Transform t2; 2399 RotateCounterclockwise(&t2); 2400 t2.matrix().setDouble(1, 3, 100.0); 2401 v2->SetTransform(t2); 2402 2403 // |v2| now occupies (30, 30) to (230, 130) in |widget| 2404 EXPECT_EQ(gfx::Rect(25, 100, 40, 15), v2->ConvertRectToParent(rect)); 2405 EXPECT_EQ(gfx::Rect(35, 110, 40, 15), v2->ConvertRectToWidget(rect)); 2406 2407 // Scale down |v1| 2408 gfx::Transform t1; 2409 t1.Scale(0.5, 0.5); 2410 v1->SetTransform(t1); 2411 2412 // The rectangle should remain the same for |v1|. 2413 EXPECT_EQ(gfx::Rect(25, 100, 40, 15), v2->ConvertRectToParent(rect)); 2414 2415 // |v2| now occupies (20, 20) to (120, 70) in |widget| 2416 EXPECT_EQ(gfx::Rect(22, 60, 21, 8).ToString(), 2417 v2->ConvertRectToWidget(rect).ToString()); 2418 2419 widget->CloseNow(); 2420} 2421 2422class ObserverView : public View { 2423 public: 2424 ObserverView(); 2425 virtual ~ObserverView(); 2426 2427 void ResetTestState(); 2428 2429 bool has_add_details() const { return has_add_details_; } 2430 bool has_remove_details() const { return has_remove_details_; } 2431 2432 const ViewHierarchyChangedDetails& add_details() const { 2433 return add_details_; 2434 } 2435 2436 const ViewHierarchyChangedDetails& remove_details() const { 2437 return remove_details_; 2438 } 2439 2440 private: 2441 // View: 2442 virtual void ViewHierarchyChanged( 2443 const ViewHierarchyChangedDetails& details) OVERRIDE; 2444 2445 bool has_add_details_; 2446 bool has_remove_details_; 2447 ViewHierarchyChangedDetails add_details_; 2448 ViewHierarchyChangedDetails remove_details_; 2449 2450 DISALLOW_COPY_AND_ASSIGN(ObserverView); 2451}; 2452 2453ObserverView::ObserverView() 2454 : has_add_details_(false), 2455 has_remove_details_(false) { 2456} 2457 2458ObserverView::~ObserverView() {} 2459 2460void ObserverView::ResetTestState() { 2461 has_add_details_ = false; 2462 has_remove_details_ = false; 2463 add_details_ = ViewHierarchyChangedDetails(); 2464 remove_details_ = ViewHierarchyChangedDetails(); 2465} 2466 2467void ObserverView::ViewHierarchyChanged( 2468 const ViewHierarchyChangedDetails& details) { 2469 if (details.is_add) { 2470 has_add_details_ = true; 2471 add_details_ = details; 2472 } else { 2473 has_remove_details_ = true; 2474 remove_details_ = details; 2475 } 2476} 2477 2478// Verifies that the ViewHierarchyChanged() notification is sent correctly when 2479// a child view is added or removed to all the views in the hierarchy (up and 2480// down). 2481// The tree looks like this: 2482// v1 2483// +-- v2 2484// +-- v3 2485// +-- v4 (starts here, then get reparented to v1) 2486TEST_F(ViewTest, ViewHierarchyChanged) { 2487 ObserverView v1; 2488 2489 ObserverView* v3 = new ObserverView(); 2490 2491 // Add |v3| to |v2|. 2492 scoped_ptr<ObserverView> v2(new ObserverView()); 2493 v2->AddChildView(v3); 2494 2495 // Make sure both |v2| and |v3| receive the ViewHierarchyChanged() 2496 // notification. 2497 EXPECT_TRUE(v2->has_add_details()); 2498 EXPECT_FALSE(v2->has_remove_details()); 2499 EXPECT_EQ(v2.get(), v2->add_details().parent); 2500 EXPECT_EQ(v3, v2->add_details().child); 2501 EXPECT_EQ(NULL, v2->add_details().move_view); 2502 2503 EXPECT_TRUE(v3->has_add_details()); 2504 EXPECT_FALSE(v3->has_remove_details()); 2505 EXPECT_EQ(v2.get(), v3->add_details().parent); 2506 EXPECT_EQ(v3, v3->add_details().child); 2507 EXPECT_EQ(NULL, v3->add_details().move_view); 2508 2509 // Reset everything to the initial state. 2510 v2->ResetTestState(); 2511 v3->ResetTestState(); 2512 2513 // Add |v2| to v1. 2514 v1.AddChildView(v2.get()); 2515 2516 // Verifies that |v2| is the child view *added* and the parent view is |v1|. 2517 // Make sure all the views (v1, v2, v3) received _that_ information. 2518 EXPECT_TRUE(v1.has_add_details()); 2519 EXPECT_FALSE(v1.has_remove_details()); 2520 EXPECT_EQ(&v1, v1.add_details().parent); 2521 EXPECT_EQ(v2.get(), v1.add_details().child); 2522 EXPECT_EQ(NULL, v1.add_details().move_view); 2523 2524 EXPECT_TRUE(v2->has_add_details()); 2525 EXPECT_FALSE(v2->has_remove_details()); 2526 EXPECT_EQ(&v1, v2->add_details().parent); 2527 EXPECT_EQ(v2.get(), v2->add_details().child); 2528 EXPECT_EQ(NULL, v2->add_details().move_view); 2529 2530 EXPECT_TRUE(v3->has_add_details()); 2531 EXPECT_FALSE(v3->has_remove_details()); 2532 EXPECT_EQ(&v1, v3->add_details().parent); 2533 EXPECT_EQ(v2.get(), v3->add_details().child); 2534 EXPECT_EQ(NULL, v3->add_details().move_view); 2535 2536 // Reset everything to the initial state. 2537 v1.ResetTestState(); 2538 v2->ResetTestState(); 2539 v3->ResetTestState(); 2540 2541 // Remove |v2| from |v1|. 2542 v1.RemoveChildView(v2.get()); 2543 2544 // Verifies that |v2| is the child view *removed* and the parent view is |v1|. 2545 // Make sure all the views (v1, v2, v3) received _that_ information. 2546 EXPECT_FALSE(v1.has_add_details()); 2547 EXPECT_TRUE(v1.has_remove_details()); 2548 EXPECT_EQ(&v1, v1.remove_details().parent); 2549 EXPECT_EQ(v2.get(), v1.remove_details().child); 2550 EXPECT_EQ(NULL, v1.remove_details().move_view); 2551 2552 EXPECT_FALSE(v2->has_add_details()); 2553 EXPECT_TRUE(v2->has_remove_details()); 2554 EXPECT_EQ(&v1, v2->remove_details().parent); 2555 EXPECT_EQ(v2.get(), v2->remove_details().child); 2556 EXPECT_EQ(NULL, v2->remove_details().move_view); 2557 2558 EXPECT_FALSE(v3->has_add_details()); 2559 EXPECT_TRUE(v3->has_remove_details()); 2560 EXPECT_EQ(&v1, v3->remove_details().parent); 2561 EXPECT_EQ(v3, v3->remove_details().child); 2562 EXPECT_EQ(NULL, v3->remove_details().move_view); 2563 2564 // Verifies notifications when reparenting a view. 2565 ObserverView* v4 = new ObserverView(); 2566 // Add |v4| to |v2|. 2567 v2->AddChildView(v4); 2568 2569 // Reset everything to the initial state. 2570 v1.ResetTestState(); 2571 v2->ResetTestState(); 2572 v3->ResetTestState(); 2573 v4->ResetTestState(); 2574 2575 // Reparent |v4| to |v1|. 2576 v1.AddChildView(v4); 2577 2578 // Verifies that all views receive the correct information for all the child, 2579 // parent and move views. 2580 2581 // |v1| is the new parent, |v4| is the child for add, |v2| is the old parent. 2582 EXPECT_TRUE(v1.has_add_details()); 2583 EXPECT_FALSE(v1.has_remove_details()); 2584 EXPECT_EQ(&v1, v1.add_details().parent); 2585 EXPECT_EQ(v4, v1.add_details().child); 2586 EXPECT_EQ(v2.get(), v1.add_details().move_view); 2587 2588 // |v2| is the old parent, |v4| is the child for remove, |v1| is the new 2589 // parent. 2590 EXPECT_FALSE(v2->has_add_details()); 2591 EXPECT_TRUE(v2->has_remove_details()); 2592 EXPECT_EQ(v2.get(), v2->remove_details().parent); 2593 EXPECT_EQ(v4, v2->remove_details().child); 2594 EXPECT_EQ(&v1, v2->remove_details().move_view); 2595 2596 // |v3| is not impacted by this operation, and hence receives no notification. 2597 EXPECT_FALSE(v3->has_add_details()); 2598 EXPECT_FALSE(v3->has_remove_details()); 2599 2600 // |v4| is the reparented child, so it receives notifications for the remove 2601 // and then the add. |v2| is its old parent, |v1| is its new parent. 2602 EXPECT_TRUE(v4->has_remove_details()); 2603 EXPECT_TRUE(v4->has_add_details()); 2604 EXPECT_EQ(v2.get(), v4->remove_details().parent); 2605 EXPECT_EQ(&v1, v4->add_details().parent); 2606 EXPECT_EQ(v4, v4->add_details().child); 2607 EXPECT_EQ(v4, v4->remove_details().child); 2608 EXPECT_EQ(&v1, v4->remove_details().move_view); 2609 EXPECT_EQ(v2.get(), v4->add_details().move_view); 2610} 2611 2612// Verifies if the child views added under the root are all deleted when calling 2613// RemoveAllChildViews. 2614// The tree looks like this: 2615// root 2616// +-- child1 2617// +-- foo 2618// +-- bar0 2619// +-- bar1 2620// +-- bar2 2621// +-- child2 2622// +-- child3 2623TEST_F(ViewTest, RemoveAllChildViews) { 2624 View root; 2625 2626 View* child1 = new View; 2627 root.AddChildView(child1); 2628 2629 for (int i = 0; i < 2; ++i) 2630 root.AddChildView(new View); 2631 2632 View* foo = new View; 2633 child1->AddChildView(foo); 2634 2635 // Add some nodes to |foo|. 2636 for (int i = 0; i < 3; ++i) 2637 foo->AddChildView(new View); 2638 2639 EXPECT_EQ(3, root.child_count()); 2640 EXPECT_EQ(1, child1->child_count()); 2641 EXPECT_EQ(3, foo->child_count()); 2642 2643 // Now remove all child views from root. 2644 root.RemoveAllChildViews(true); 2645 2646 EXPECT_EQ(0, root.child_count()); 2647 EXPECT_FALSE(root.has_children()); 2648} 2649 2650TEST_F(ViewTest, Contains) { 2651 View v1; 2652 View* v2 = new View; 2653 View* v3 = new View; 2654 2655 v1.AddChildView(v2); 2656 v2->AddChildView(v3); 2657 2658 EXPECT_FALSE(v1.Contains(NULL)); 2659 EXPECT_TRUE(v1.Contains(&v1)); 2660 EXPECT_TRUE(v1.Contains(v2)); 2661 EXPECT_TRUE(v1.Contains(v3)); 2662 2663 EXPECT_FALSE(v2->Contains(NULL)); 2664 EXPECT_TRUE(v2->Contains(v2)); 2665 EXPECT_FALSE(v2->Contains(&v1)); 2666 EXPECT_TRUE(v2->Contains(v3)); 2667 2668 EXPECT_FALSE(v3->Contains(NULL)); 2669 EXPECT_TRUE(v3->Contains(v3)); 2670 EXPECT_FALSE(v3->Contains(&v1)); 2671 EXPECT_FALSE(v3->Contains(v2)); 2672} 2673 2674// Verifies if GetIndexOf() returns the correct index for the specified child 2675// view. 2676// The tree looks like this: 2677// root 2678// +-- child1 2679// +-- foo1 2680// +-- child2 2681TEST_F(ViewTest, GetIndexOf) { 2682 View root; 2683 2684 View* child1 = new View; 2685 root.AddChildView(child1); 2686 2687 View* child2 = new View; 2688 root.AddChildView(child2); 2689 2690 View* foo1 = new View; 2691 child1->AddChildView(foo1); 2692 2693 EXPECT_EQ(-1, root.GetIndexOf(NULL)); 2694 EXPECT_EQ(-1, root.GetIndexOf(&root)); 2695 EXPECT_EQ(0, root.GetIndexOf(child1)); 2696 EXPECT_EQ(1, root.GetIndexOf(child2)); 2697 EXPECT_EQ(-1, root.GetIndexOf(foo1)); 2698 2699 EXPECT_EQ(-1, child1->GetIndexOf(NULL)); 2700 EXPECT_EQ(-1, child1->GetIndexOf(&root)); 2701 EXPECT_EQ(-1, child1->GetIndexOf(child1)); 2702 EXPECT_EQ(-1, child1->GetIndexOf(child2)); 2703 EXPECT_EQ(0, child1->GetIndexOf(foo1)); 2704 2705 EXPECT_EQ(-1, child2->GetIndexOf(NULL)); 2706 EXPECT_EQ(-1, child2->GetIndexOf(&root)); 2707 EXPECT_EQ(-1, child2->GetIndexOf(child2)); 2708 EXPECT_EQ(-1, child2->GetIndexOf(child1)); 2709 EXPECT_EQ(-1, child2->GetIndexOf(foo1)); 2710} 2711 2712// Verifies that the child views can be reordered correctly. 2713TEST_F(ViewTest, ReorderChildren) { 2714 View root; 2715 2716 View* child = new View(); 2717 root.AddChildView(child); 2718 2719 View* foo1 = new View(); 2720 child->AddChildView(foo1); 2721 View* foo2 = new View(); 2722 child->AddChildView(foo2); 2723 View* foo3 = new View(); 2724 child->AddChildView(foo3); 2725 foo1->set_focusable(true); 2726 foo2->set_focusable(true); 2727 foo3->set_focusable(true); 2728 2729 ASSERT_EQ(0, child->GetIndexOf(foo1)); 2730 ASSERT_EQ(1, child->GetIndexOf(foo2)); 2731 ASSERT_EQ(2, child->GetIndexOf(foo3)); 2732 ASSERT_EQ(foo2, foo1->GetNextFocusableView()); 2733 ASSERT_EQ(foo3, foo2->GetNextFocusableView()); 2734 ASSERT_EQ(NULL, foo3->GetNextFocusableView()); 2735 2736 // Move |foo2| at the end. 2737 child->ReorderChildView(foo2, -1); 2738 ASSERT_EQ(0, child->GetIndexOf(foo1)); 2739 ASSERT_EQ(1, child->GetIndexOf(foo3)); 2740 ASSERT_EQ(2, child->GetIndexOf(foo2)); 2741 ASSERT_EQ(foo3, foo1->GetNextFocusableView()); 2742 ASSERT_EQ(foo2, foo3->GetNextFocusableView()); 2743 ASSERT_EQ(NULL, foo2->GetNextFocusableView()); 2744 2745 // Move |foo1| at the end. 2746 child->ReorderChildView(foo1, -1); 2747 ASSERT_EQ(0, child->GetIndexOf(foo3)); 2748 ASSERT_EQ(1, child->GetIndexOf(foo2)); 2749 ASSERT_EQ(2, child->GetIndexOf(foo1)); 2750 ASSERT_EQ(NULL, foo1->GetNextFocusableView()); 2751 ASSERT_EQ(foo2, foo1->GetPreviousFocusableView()); 2752 ASSERT_EQ(foo2, foo3->GetNextFocusableView()); 2753 ASSERT_EQ(foo1, foo2->GetNextFocusableView()); 2754 2755 // Move |foo2| to the front. 2756 child->ReorderChildView(foo2, 0); 2757 ASSERT_EQ(0, child->GetIndexOf(foo2)); 2758 ASSERT_EQ(1, child->GetIndexOf(foo3)); 2759 ASSERT_EQ(2, child->GetIndexOf(foo1)); 2760 ASSERT_EQ(NULL, foo1->GetNextFocusableView()); 2761 ASSERT_EQ(foo3, foo1->GetPreviousFocusableView()); 2762 ASSERT_EQ(foo3, foo2->GetNextFocusableView()); 2763 ASSERT_EQ(foo1, foo3->GetNextFocusableView()); 2764} 2765 2766// Verifies that GetViewByID returns the correctly child view from the specified 2767// ID. 2768// The tree looks like this: 2769// v1 2770// +-- v2 2771// +-- v3 2772// +-- v4 2773TEST_F(ViewTest, GetViewByID) { 2774 View v1; 2775 const int kV1ID = 1; 2776 v1.set_id(kV1ID); 2777 2778 View v2; 2779 const int kV2ID = 2; 2780 v2.set_id(kV2ID); 2781 2782 View v3; 2783 const int kV3ID = 3; 2784 v3.set_id(kV3ID); 2785 2786 View v4; 2787 const int kV4ID = 4; 2788 v4.set_id(kV4ID); 2789 2790 const int kV5ID = 5; 2791 2792 v1.AddChildView(&v2); 2793 v2.AddChildView(&v3); 2794 v2.AddChildView(&v4); 2795 2796 EXPECT_EQ(&v1, v1.GetViewByID(kV1ID)); 2797 EXPECT_EQ(&v2, v1.GetViewByID(kV2ID)); 2798 EXPECT_EQ(&v4, v1.GetViewByID(kV4ID)); 2799 2800 EXPECT_EQ(NULL, v1.GetViewByID(kV5ID)); // No V5 exists. 2801 EXPECT_EQ(NULL, v2.GetViewByID(kV1ID)); // It can get only from child views. 2802 2803 const int kGroup = 1; 2804 v3.SetGroup(kGroup); 2805 v4.SetGroup(kGroup); 2806 2807 View::Views views; 2808 v1.GetViewsInGroup(kGroup, &views); 2809 EXPECT_EQ(2U, views.size()); 2810 2811 View::Views::const_iterator i(std::find(views.begin(), views.end(), &v3)); 2812 EXPECT_NE(views.end(), i); 2813 2814 i = std::find(views.begin(), views.end(), &v4); 2815 EXPECT_NE(views.end(), i); 2816} 2817 2818TEST_F(ViewTest, AddExistingChild) { 2819 View v1, v2, v3; 2820 2821 v1.AddChildView(&v2); 2822 v1.AddChildView(&v3); 2823 EXPECT_EQ(0, v1.GetIndexOf(&v2)); 2824 EXPECT_EQ(1, v1.GetIndexOf(&v3)); 2825 2826 // Check that there's no change in order when adding at same index. 2827 v1.AddChildViewAt(&v2, 0); 2828 EXPECT_EQ(0, v1.GetIndexOf(&v2)); 2829 EXPECT_EQ(1, v1.GetIndexOf(&v3)); 2830 v1.AddChildViewAt(&v3, 1); 2831 EXPECT_EQ(0, v1.GetIndexOf(&v2)); 2832 EXPECT_EQ(1, v1.GetIndexOf(&v3)); 2833 2834 // Add it at a different index and check for change in order. 2835 v1.AddChildViewAt(&v2, 1); 2836 EXPECT_EQ(1, v1.GetIndexOf(&v2)); 2837 EXPECT_EQ(0, v1.GetIndexOf(&v3)); 2838 v1.AddChildViewAt(&v2, 0); 2839 EXPECT_EQ(0, v1.GetIndexOf(&v2)); 2840 EXPECT_EQ(1, v1.GetIndexOf(&v3)); 2841 2842 // Check that calling |AddChildView()| does not change the order. 2843 v1.AddChildView(&v2); 2844 EXPECT_EQ(0, v1.GetIndexOf(&v2)); 2845 EXPECT_EQ(1, v1.GetIndexOf(&v3)); 2846 v1.AddChildView(&v3); 2847 EXPECT_EQ(0, v1.GetIndexOf(&v2)); 2848 EXPECT_EQ(1, v1.GetIndexOf(&v3)); 2849} 2850 2851//////////////////////////////////////////////////////////////////////////////// 2852// Layers 2853//////////////////////////////////////////////////////////////////////////////// 2854 2855#if defined(USE_AURA) 2856 2857namespace { 2858 2859// Test implementation of LayerAnimator. 2860class TestLayerAnimator : public ui::LayerAnimator { 2861 public: 2862 TestLayerAnimator(); 2863 2864 const gfx::Rect& last_bounds() const { return last_bounds_; } 2865 2866 // LayerAnimator. 2867 virtual void SetBounds(const gfx::Rect& bounds) OVERRIDE; 2868 2869 protected: 2870 virtual ~TestLayerAnimator() { } 2871 2872 private: 2873 gfx::Rect last_bounds_; 2874 2875 DISALLOW_COPY_AND_ASSIGN(TestLayerAnimator); 2876}; 2877 2878TestLayerAnimator::TestLayerAnimator() 2879 : ui::LayerAnimator(base::TimeDelta::FromMilliseconds(0)) { 2880} 2881 2882void TestLayerAnimator::SetBounds(const gfx::Rect& bounds) { 2883 last_bounds_ = bounds; 2884} 2885 2886} // namespace 2887 2888class ViewLayerTest : public ViewsTestBase { 2889 public: 2890 ViewLayerTest() : widget_(NULL), old_use_acceleration_(false) {} 2891 2892 virtual ~ViewLayerTest() { 2893 } 2894 2895 // Returns the Layer used by the RootView. 2896 ui::Layer* GetRootLayer() { 2897 return widget()->GetLayer(); 2898 } 2899 2900 virtual void SetUp() OVERRIDE { 2901 ViewTest::SetUp(); 2902 old_use_acceleration_ = View::get_use_acceleration_when_possible(); 2903 View::set_use_acceleration_when_possible(true); 2904 2905 widget_ = new Widget; 2906 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); 2907 params.bounds = gfx::Rect(50, 50, 200, 200); 2908 widget_->Init(params); 2909 widget_->Show(); 2910 widget_->GetRootView()->SetBounds(0, 0, 200, 200); 2911 } 2912 2913 virtual void TearDown() OVERRIDE { 2914 View::set_use_acceleration_when_possible(old_use_acceleration_); 2915 widget_->CloseNow(); 2916 ViewsTestBase::TearDown(); 2917 } 2918 2919 Widget* widget() { return widget_; } 2920 2921 private: 2922 Widget* widget_; 2923 bool old_use_acceleration_; 2924}; 2925 2926 2927TEST_F(ViewLayerTest, LayerToggling) { 2928 // Because we lazily create textures the calls to DrawTree are necessary to 2929 // ensure we trigger creation of textures. 2930 ui::Layer* root_layer = widget()->GetLayer(); 2931 View* content_view = new View; 2932 widget()->SetContentsView(content_view); 2933 2934 // Create v1, give it a bounds and verify everything is set up correctly. 2935 View* v1 = new View; 2936 v1->SetPaintToLayer(true); 2937 EXPECT_TRUE(v1->layer() != NULL); 2938 v1->SetBoundsRect(gfx::Rect(20, 30, 140, 150)); 2939 content_view->AddChildView(v1); 2940 ASSERT_TRUE(v1->layer() != NULL); 2941 EXPECT_EQ(root_layer, v1->layer()->parent()); 2942 EXPECT_EQ(gfx::Rect(20, 30, 140, 150), v1->layer()->bounds()); 2943 2944 // Create v2 as a child of v1 and do basic assertion testing. 2945 View* v2 = new View; 2946 v1->AddChildView(v2); 2947 EXPECT_TRUE(v2->layer() == NULL); 2948 v2->SetBoundsRect(gfx::Rect(10, 20, 30, 40)); 2949 v2->SetPaintToLayer(true); 2950 ASSERT_TRUE(v2->layer() != NULL); 2951 EXPECT_EQ(v1->layer(), v2->layer()->parent()); 2952 EXPECT_EQ(gfx::Rect(10, 20, 30, 40), v2->layer()->bounds()); 2953 2954 // Turn off v1s layer. v2 should still have a layer but its parent should have 2955 // changed. 2956 v1->SetPaintToLayer(false); 2957 EXPECT_TRUE(v1->layer() == NULL); 2958 EXPECT_TRUE(v2->layer() != NULL); 2959 EXPECT_EQ(root_layer, v2->layer()->parent()); 2960 ASSERT_EQ(1u, root_layer->children().size()); 2961 EXPECT_EQ(root_layer->children()[0], v2->layer()); 2962 // The bounds of the layer should have changed to be relative to the root view 2963 // now. 2964 EXPECT_EQ(gfx::Rect(30, 50, 30, 40), v2->layer()->bounds()); 2965 2966 // Make v1 have a layer again and verify v2s layer is wired up correctly. 2967 gfx::Transform transform; 2968 transform.Scale(2.0, 2.0); 2969 v1->SetTransform(transform); 2970 EXPECT_TRUE(v1->layer() != NULL); 2971 EXPECT_TRUE(v2->layer() != NULL); 2972 EXPECT_EQ(root_layer, v1->layer()->parent()); 2973 EXPECT_EQ(v1->layer(), v2->layer()->parent()); 2974 ASSERT_EQ(1u, root_layer->children().size()); 2975 EXPECT_EQ(root_layer->children()[0], v1->layer()); 2976 ASSERT_EQ(1u, v1->layer()->children().size()); 2977 EXPECT_EQ(v1->layer()->children()[0], v2->layer()); 2978 EXPECT_EQ(gfx::Rect(10, 20, 30, 40), v2->layer()->bounds()); 2979} 2980 2981// Verifies turning on a layer wires up children correctly. 2982TEST_F(ViewLayerTest, NestedLayerToggling) { 2983 View* content_view = new View; 2984 widget()->SetContentsView(content_view); 2985 2986 // Create v1, give it a bounds and verify everything is set up correctly. 2987 View* v1 = new View; 2988 content_view->AddChildView(v1); 2989 v1->SetBoundsRect(gfx::Rect(20, 30, 140, 150)); 2990 2991 View* v2 = new View; 2992 v1->AddChildView(v2); 2993 2994 View* v3 = new View; 2995 v3->SetPaintToLayer(true); 2996 v2->AddChildView(v3); 2997 ASSERT_TRUE(v3->layer() != NULL); 2998 2999 // At this point we have v1-v2-v3. v3 has a layer, v1 and v2 don't. 3000 3001 v1->SetPaintToLayer(true); 3002 EXPECT_EQ(v1->layer(), v3->layer()->parent()); 3003} 3004 3005TEST_F(ViewLayerTest, LayerAnimator) { 3006 View* content_view = new View; 3007 widget()->SetContentsView(content_view); 3008 3009 View* v1 = new View; 3010 content_view->AddChildView(v1); 3011 v1->SetPaintToLayer(true); 3012 EXPECT_TRUE(v1->layer() != NULL); 3013 3014 TestLayerAnimator* animator = new TestLayerAnimator(); 3015 v1->layer()->SetAnimator(animator); 3016 3017 gfx::Rect bounds(1, 2, 3, 4); 3018 v1->SetBoundsRect(bounds); 3019 EXPECT_EQ(bounds, animator->last_bounds()); 3020 // TestLayerAnimator doesn't update the layer. 3021 EXPECT_NE(bounds, v1->layer()->bounds()); 3022} 3023 3024// Verifies the bounds of a layer are updated if the bounds of ancestor that 3025// doesn't have a layer change. 3026TEST_F(ViewLayerTest, BoundsChangeWithLayer) { 3027 View* content_view = new View; 3028 widget()->SetContentsView(content_view); 3029 3030 View* v1 = new View; 3031 content_view->AddChildView(v1); 3032 v1->SetBoundsRect(gfx::Rect(20, 30, 140, 150)); 3033 3034 View* v2 = new View; 3035 v2->SetBoundsRect(gfx::Rect(10, 11, 40, 50)); 3036 v1->AddChildView(v2); 3037 v2->SetPaintToLayer(true); 3038 ASSERT_TRUE(v2->layer() != NULL); 3039 EXPECT_EQ(gfx::Rect(30, 41, 40, 50), v2->layer()->bounds()); 3040 3041 v1->SetPosition(gfx::Point(25, 36)); 3042 EXPECT_EQ(gfx::Rect(35, 47, 40, 50), v2->layer()->bounds()); 3043 3044 v2->SetPosition(gfx::Point(11, 12)); 3045 EXPECT_EQ(gfx::Rect(36, 48, 40, 50), v2->layer()->bounds()); 3046 3047 // Bounds of the layer should change even if the view is not invisible. 3048 v1->SetVisible(false); 3049 v1->SetPosition(gfx::Point(20, 30)); 3050 EXPECT_EQ(gfx::Rect(31, 42, 40, 50), v2->layer()->bounds()); 3051 3052 v2->SetVisible(false); 3053 v2->SetBoundsRect(gfx::Rect(10, 11, 20, 30)); 3054 EXPECT_EQ(gfx::Rect(30, 41, 20, 30), v2->layer()->bounds()); 3055} 3056 3057// Make sure layers are positioned correctly in RTL. 3058TEST_F(ViewLayerTest, BoundInRTL) { 3059 std::string locale = l10n_util::GetApplicationLocale(std::string()); 3060 base::i18n::SetICUDefaultLocale("he"); 3061 3062 View* view = new View; 3063 widget()->SetContentsView(view); 3064 3065 int content_width = view->width(); 3066 3067 // |v1| is initially not attached to anything. So its layer will have the same 3068 // bounds as the view. 3069 View* v1 = new View; 3070 v1->SetPaintToLayer(true); 3071 v1->SetBounds(10, 10, 20, 10); 3072 EXPECT_EQ(gfx::Rect(10, 10, 20, 10), 3073 v1->layer()->bounds()); 3074 3075 // Once |v1| is attached to the widget, its layer will get RTL-appropriate 3076 // bounds. 3077 view->AddChildView(v1); 3078 EXPECT_EQ(gfx::Rect(content_width - 30, 10, 20, 10), 3079 v1->layer()->bounds()); 3080 gfx::Rect l1bounds = v1->layer()->bounds(); 3081 3082 // Now attach a View to the widget first, then create a layer for it. Make 3083 // sure the bounds are correct. 3084 View* v2 = new View; 3085 v2->SetBounds(50, 10, 30, 10); 3086 EXPECT_FALSE(v2->layer()); 3087 view->AddChildView(v2); 3088 v2->SetPaintToLayer(true); 3089 EXPECT_EQ(gfx::Rect(content_width - 80, 10, 30, 10), 3090 v2->layer()->bounds()); 3091 gfx::Rect l2bounds = v2->layer()->bounds(); 3092 3093 view->SetPaintToLayer(true); 3094 EXPECT_EQ(l1bounds, v1->layer()->bounds()); 3095 EXPECT_EQ(l2bounds, v2->layer()->bounds()); 3096 3097 // Move one of the views. Make sure the layer is positioned correctly 3098 // afterwards. 3099 v1->SetBounds(v1->x() - 5, v1->y(), v1->width(), v1->height()); 3100 l1bounds.set_x(l1bounds.x() + 5); 3101 EXPECT_EQ(l1bounds, v1->layer()->bounds()); 3102 3103 view->SetPaintToLayer(false); 3104 EXPECT_EQ(l1bounds, v1->layer()->bounds()); 3105 EXPECT_EQ(l2bounds, v2->layer()->bounds()); 3106 3107 // Move a view again. 3108 v2->SetBounds(v2->x() + 5, v2->y(), v2->width(), v2->height()); 3109 l2bounds.set_x(l2bounds.x() - 5); 3110 EXPECT_EQ(l2bounds, v2->layer()->bounds()); 3111 3112 // Reset locale. 3113 base::i18n::SetICUDefaultLocale(locale); 3114} 3115 3116// Makes sure a transform persists after toggling the visibility. 3117TEST_F(ViewLayerTest, ToggleVisibilityWithTransform) { 3118 View* view = new View; 3119 gfx::Transform transform; 3120 transform.Scale(2.0, 2.0); 3121 view->SetTransform(transform); 3122 widget()->SetContentsView(view); 3123 EXPECT_EQ(2.0f, view->GetTransform().matrix().get(0, 0)); 3124 3125 view->SetVisible(false); 3126 EXPECT_EQ(2.0f, view->GetTransform().matrix().get(0, 0)); 3127 3128 view->SetVisible(true); 3129 EXPECT_EQ(2.0f, view->GetTransform().matrix().get(0, 0)); 3130} 3131 3132// Verifies a transform persists after removing/adding a view with a transform. 3133TEST_F(ViewLayerTest, ResetTransformOnLayerAfterAdd) { 3134 View* view = new View; 3135 gfx::Transform transform; 3136 transform.Scale(2.0, 2.0); 3137 view->SetTransform(transform); 3138 widget()->SetContentsView(view); 3139 EXPECT_EQ(2.0f, view->GetTransform().matrix().get(0, 0)); 3140 ASSERT_TRUE(view->layer() != NULL); 3141 EXPECT_EQ(2.0f, view->layer()->transform().matrix().get(0, 0)); 3142 3143 View* parent = view->parent(); 3144 parent->RemoveChildView(view); 3145 parent->AddChildView(view); 3146 3147 EXPECT_EQ(2.0f, view->GetTransform().matrix().get(0, 0)); 3148 ASSERT_TRUE(view->layer() != NULL); 3149 EXPECT_EQ(2.0f, view->layer()->transform().matrix().get(0, 0)); 3150} 3151 3152// Makes sure that layer visibility is correct after toggling View visibility. 3153TEST_F(ViewLayerTest, ToggleVisibilityWithLayer) { 3154 View* content_view = new View; 3155 widget()->SetContentsView(content_view); 3156 3157 // The view isn't attached to a widget or a parent view yet. But it should 3158 // still have a layer, but the layer should not be attached to the root 3159 // layer. 3160 View* v1 = new View; 3161 v1->SetPaintToLayer(true); 3162 EXPECT_TRUE(v1->layer()); 3163 EXPECT_FALSE(LayerIsAncestor(widget()->GetCompositor()->root_layer(), 3164 v1->layer())); 3165 3166 // Once the view is attached to a widget, its layer should be attached to the 3167 // root layer and visible. 3168 content_view->AddChildView(v1); 3169 EXPECT_TRUE(LayerIsAncestor(widget()->GetCompositor()->root_layer(), 3170 v1->layer())); 3171 EXPECT_TRUE(v1->layer()->IsDrawn()); 3172 3173 v1->SetVisible(false); 3174 EXPECT_FALSE(v1->layer()->IsDrawn()); 3175 3176 v1->SetVisible(true); 3177 EXPECT_TRUE(v1->layer()->IsDrawn()); 3178 3179 widget()->Hide(); 3180 EXPECT_FALSE(v1->layer()->IsDrawn()); 3181 3182 widget()->Show(); 3183 EXPECT_TRUE(v1->layer()->IsDrawn()); 3184} 3185 3186// Tests that the layers in the subtree are orphaned after a View is removed 3187// from the parent. 3188TEST_F(ViewLayerTest, OrphanLayerAfterViewRemove) { 3189 View* content_view = new View; 3190 widget()->SetContentsView(content_view); 3191 3192 View* v1 = new View; 3193 content_view->AddChildView(v1); 3194 3195 View* v2 = new View; 3196 v1->AddChildView(v2); 3197 v2->SetPaintToLayer(true); 3198 EXPECT_TRUE(LayerIsAncestor(widget()->GetCompositor()->root_layer(), 3199 v2->layer())); 3200 EXPECT_TRUE(v2->layer()->IsDrawn()); 3201 3202 content_view->RemoveChildView(v1); 3203 3204 EXPECT_FALSE(LayerIsAncestor(widget()->GetCompositor()->root_layer(), 3205 v2->layer())); 3206 3207 // Reparent |v2|. 3208 content_view->AddChildView(v2); 3209 delete v1; 3210 v1 = NULL; 3211 EXPECT_TRUE(LayerIsAncestor(widget()->GetCompositor()->root_layer(), 3212 v2->layer())); 3213 EXPECT_TRUE(v2->layer()->IsDrawn()); 3214} 3215 3216class PaintTrackingView : public View { 3217 public: 3218 PaintTrackingView() : painted_(false) { 3219 } 3220 3221 bool painted() const { return painted_; } 3222 void set_painted(bool value) { painted_ = value; } 3223 3224 virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE { 3225 painted_ = true; 3226 } 3227 3228 private: 3229 bool painted_; 3230 3231 DISALLOW_COPY_AND_ASSIGN(PaintTrackingView); 3232}; 3233 3234// Makes sure child views with layers aren't painted when paint starts at an 3235// ancestor. 3236TEST_F(ViewLayerTest, DontPaintChildrenWithLayers) { 3237 PaintTrackingView* content_view = new PaintTrackingView; 3238 widget()->SetContentsView(content_view); 3239 content_view->SetPaintToLayer(true); 3240 GetRootLayer()->GetCompositor()->ScheduleDraw(); 3241 ui::DrawWaiterForTest::Wait(GetRootLayer()->GetCompositor()); 3242 GetRootLayer()->SchedulePaint(gfx::Rect(0, 0, 10, 10)); 3243 content_view->set_painted(false); 3244 // content_view no longer has a dirty rect. Paint from the root and make sure 3245 // PaintTrackingView isn't painted. 3246 GetRootLayer()->GetCompositor()->ScheduleDraw(); 3247 ui::DrawWaiterForTest::Wait(GetRootLayer()->GetCompositor()); 3248 EXPECT_FALSE(content_view->painted()); 3249 3250 // Make content_view have a dirty rect, paint the layers and make sure 3251 // PaintTrackingView is painted. 3252 content_view->layer()->SchedulePaint(gfx::Rect(0, 0, 10, 10)); 3253 GetRootLayer()->GetCompositor()->ScheduleDraw(); 3254 ui::DrawWaiterForTest::Wait(GetRootLayer()->GetCompositor()); 3255 EXPECT_TRUE(content_view->painted()); 3256} 3257 3258// Tests that the visibility of child layers are updated correctly when a View's 3259// visibility changes. 3260TEST_F(ViewLayerTest, VisibilityChildLayers) { 3261 View* v1 = new View; 3262 v1->SetPaintToLayer(true); 3263 widget()->SetContentsView(v1); 3264 3265 View* v2 = new View; 3266 v1->AddChildView(v2); 3267 3268 View* v3 = new View; 3269 v2->AddChildView(v3); 3270 v3->SetVisible(false); 3271 3272 View* v4 = new View; 3273 v4->SetPaintToLayer(true); 3274 v3->AddChildView(v4); 3275 3276 EXPECT_TRUE(v1->layer()->IsDrawn()); 3277 EXPECT_FALSE(v4->layer()->IsDrawn()); 3278 3279 v2->SetVisible(false); 3280 EXPECT_TRUE(v1->layer()->IsDrawn()); 3281 EXPECT_FALSE(v4->layer()->IsDrawn()); 3282 3283 v2->SetVisible(true); 3284 EXPECT_TRUE(v1->layer()->IsDrawn()); 3285 EXPECT_FALSE(v4->layer()->IsDrawn()); 3286 3287 v2->SetVisible(false); 3288 EXPECT_TRUE(v1->layer()->IsDrawn()); 3289 EXPECT_FALSE(v4->layer()->IsDrawn()); 3290 EXPECT_TRUE(ViewAndLayerTreeAreConsistent(v1, v1->layer())); 3291 3292 v3->SetVisible(true); 3293 EXPECT_TRUE(v1->layer()->IsDrawn()); 3294 EXPECT_FALSE(v4->layer()->IsDrawn()); 3295 EXPECT_TRUE(ViewAndLayerTreeAreConsistent(v1, v1->layer())); 3296 3297 // Reparent |v3| to |v1|. 3298 v1->AddChildView(v3); 3299 EXPECT_TRUE(v1->layer()->IsDrawn()); 3300 EXPECT_TRUE(v4->layer()->IsDrawn()); 3301 EXPECT_TRUE(ViewAndLayerTreeAreConsistent(v1, v1->layer())); 3302} 3303 3304// This test creates a random View tree, and then randomly reorders child views, 3305// reparents views etc. Unrelated changes can appear to break this test. So 3306// marking this as FLAKY. 3307TEST_F(ViewLayerTest, DISABLED_ViewLayerTreesInSync) { 3308 View* content = new View; 3309 content->SetPaintToLayer(true); 3310 widget()->SetContentsView(content); 3311 widget()->Show(); 3312 3313 ConstructTree(content, 5); 3314 EXPECT_TRUE(ViewAndLayerTreeAreConsistent(content, content->layer())); 3315 3316 ScrambleTree(content); 3317 EXPECT_TRUE(ViewAndLayerTreeAreConsistent(content, content->layer())); 3318 3319 ScrambleTree(content); 3320 EXPECT_TRUE(ViewAndLayerTreeAreConsistent(content, content->layer())); 3321 3322 ScrambleTree(content); 3323 EXPECT_TRUE(ViewAndLayerTreeAreConsistent(content, content->layer())); 3324} 3325 3326// Verifies when views are reordered the layer is also reordered. The widget is 3327// providing the parent layer. 3328TEST_F(ViewLayerTest, ReorderUnderWidget) { 3329 View* content = new View; 3330 widget()->SetContentsView(content); 3331 View* c1 = new View; 3332 c1->SetPaintToLayer(true); 3333 content->AddChildView(c1); 3334 View* c2 = new View; 3335 c2->SetPaintToLayer(true); 3336 content->AddChildView(c2); 3337 3338 ui::Layer* parent_layer = c1->layer()->parent(); 3339 ASSERT_TRUE(parent_layer); 3340 ASSERT_EQ(2u, parent_layer->children().size()); 3341 EXPECT_EQ(c1->layer(), parent_layer->children()[0]); 3342 EXPECT_EQ(c2->layer(), parent_layer->children()[1]); 3343 3344 // Move c1 to the front. The layers should have moved too. 3345 content->ReorderChildView(c1, -1); 3346 EXPECT_EQ(c1->layer(), parent_layer->children()[1]); 3347 EXPECT_EQ(c2->layer(), parent_layer->children()[0]); 3348} 3349 3350// Verifies that the layer of a view can be acquired properly. 3351TEST_F(ViewLayerTest, AcquireLayer) { 3352 View* content = new View; 3353 widget()->SetContentsView(content); 3354 scoped_ptr<View> c1(new View); 3355 c1->SetPaintToLayer(true); 3356 EXPECT_TRUE(c1->layer()); 3357 content->AddChildView(c1.get()); 3358 3359 scoped_ptr<ui::Layer> layer(c1->AcquireLayer()); 3360 EXPECT_EQ(layer.get(), c1->layer()); 3361 3362 scoped_ptr<ui::Layer> layer2(c1->RecreateLayer()); 3363 EXPECT_NE(c1->layer(), layer2.get()); 3364 3365 // Destroy view before destroying layer. 3366 c1.reset(); 3367} 3368 3369// Verify that new layer scales content only if the old layer does. 3370TEST_F(ViewLayerTest, RecreateLayer) { 3371 scoped_ptr<View> v(new View()); 3372 v->SetPaintToLayer(true); 3373 // Set to non default value. 3374 v->layer()->set_scale_content(false); 3375 scoped_ptr<ui::Layer> old_layer(v->RecreateLayer()); 3376 ui::Layer* new_layer = v->layer(); 3377 EXPECT_FALSE(new_layer->scale_content()); 3378} 3379 3380#endif // USE_AURA 3381 3382} // namespace views 3383