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 "ui/base/hit_test.h" 6 #include "ui/views/bubble/bubble_delegate.h" 7 #include "ui/views/bubble/bubble_frame_view.h" 8 #include "ui/views/test/test_widget_observer.h" 9 #include "ui/views/test/views_test_base.h" 10 #include "ui/views/widget/widget.h" 11 #include "ui/views/widget/widget_observer.h" 12 13 namespace views { 14 15 namespace { 16 17 class TestBubbleDelegateView : public BubbleDelegateView { 18 public: 19 TestBubbleDelegateView(View* anchor_view) 20 : BubbleDelegateView(anchor_view, BubbleBorder::TOP_LEFT), 21 view_(new View()) { 22 view_->SetFocusable(true); 23 AddChildView(view_); 24 } 25 virtual ~TestBubbleDelegateView() {} 26 27 void SetAnchorRectForTest(gfx::Rect rect) { 28 SetAnchorRect(rect); 29 } 30 31 void SetAnchorViewForTest(View* view) { 32 SetAnchorView(view); 33 } 34 35 // BubbleDelegateView overrides: 36 virtual View* GetInitiallyFocusedView() OVERRIDE { return view_; } 37 virtual gfx::Size GetPreferredSize() const OVERRIDE { 38 return gfx::Size(200, 200); 39 } 40 41 private: 42 View* view_; 43 44 DISALLOW_COPY_AND_ASSIGN(TestBubbleDelegateView); 45 }; 46 47 class BubbleDelegateTest : public ViewsTestBase { 48 public: 49 BubbleDelegateTest() {} 50 virtual ~BubbleDelegateTest() {} 51 52 // Creates a test widget that owns its native widget. 53 Widget* CreateTestWidget() { 54 Widget* widget = new Widget(); 55 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW); 56 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 57 widget->Init(params); 58 return widget; 59 } 60 61 private: 62 DISALLOW_COPY_AND_ASSIGN(BubbleDelegateTest); 63 }; 64 65 } // namespace 66 67 TEST_F(BubbleDelegateTest, CreateDelegate) { 68 scoped_ptr<Widget> anchor_widget(CreateTestWidget()); 69 BubbleDelegateView* bubble_delegate = new BubbleDelegateView( 70 anchor_widget->GetContentsView(), BubbleBorder::NONE); 71 bubble_delegate->set_color(SK_ColorGREEN); 72 Widget* bubble_widget = BubbleDelegateView::CreateBubble(bubble_delegate); 73 EXPECT_EQ(bubble_delegate, bubble_widget->widget_delegate()); 74 EXPECT_EQ(bubble_widget, bubble_delegate->GetWidget()); 75 test::TestWidgetObserver bubble_observer(bubble_widget); 76 bubble_widget->Show(); 77 78 BubbleBorder* border = bubble_delegate->GetBubbleFrameView()->bubble_border(); 79 EXPECT_EQ(bubble_delegate->arrow(), border->arrow()); 80 EXPECT_EQ(bubble_delegate->color(), border->background_color()); 81 82 EXPECT_FALSE(bubble_observer.widget_closed()); 83 bubble_widget->CloseNow(); 84 EXPECT_TRUE(bubble_observer.widget_closed()); 85 } 86 87 TEST_F(BubbleDelegateTest, CloseAnchorWidget) { 88 scoped_ptr<Widget> anchor_widget(CreateTestWidget()); 89 BubbleDelegateView* bubble_delegate = new BubbleDelegateView( 90 anchor_widget->GetContentsView(), BubbleBorder::NONE); 91 // Preventing close on deactivate should not prevent closing with the anchor. 92 bubble_delegate->set_close_on_deactivate(false); 93 Widget* bubble_widget = BubbleDelegateView::CreateBubble(bubble_delegate); 94 EXPECT_EQ(bubble_delegate, bubble_widget->widget_delegate()); 95 EXPECT_EQ(bubble_widget, bubble_delegate->GetWidget()); 96 EXPECT_EQ(anchor_widget, bubble_delegate->anchor_widget()); 97 test::TestWidgetObserver bubble_observer(bubble_widget); 98 EXPECT_FALSE(bubble_observer.widget_closed()); 99 100 bubble_widget->Show(); 101 EXPECT_EQ(anchor_widget, bubble_delegate->anchor_widget()); 102 EXPECT_FALSE(bubble_observer.widget_closed()); 103 104 // TODO(msw): Remove activation hack to prevent bookkeeping errors in: 105 // aura::test::TestActivationClient::OnWindowDestroyed(). 106 scoped_ptr<Widget> smoke_and_mirrors_widget(CreateTestWidget()); 107 EXPECT_FALSE(bubble_observer.widget_closed()); 108 109 // Ensure that closing the anchor widget also closes the bubble itself. 110 anchor_widget->CloseNow(); 111 EXPECT_TRUE(bubble_observer.widget_closed()); 112 } 113 114 // This test checks that the bubble delegate is capable to handle an early 115 // destruction of the used anchor view. (Animations and delayed closure of the 116 // bubble will call upon the anchor view to get its location). 117 TEST_F(BubbleDelegateTest, CloseAnchorViewTest) { 118 // Create an anchor widget and add a view to be used as an anchor view. 119 scoped_ptr<Widget> anchor_widget(CreateTestWidget()); 120 scoped_ptr<View> anchor_view(new View()); 121 anchor_widget->GetContentsView()->AddChildView(anchor_view.get()); 122 TestBubbleDelegateView* bubble_delegate = new TestBubbleDelegateView( 123 anchor_view.get()); 124 // Prevent flakes by avoiding closing on activation changes. 125 bubble_delegate->set_close_on_deactivate(false); 126 Widget* bubble_widget = BubbleDelegateView::CreateBubble(bubble_delegate); 127 128 // Check that the anchor view is correct and set up an anchor view rect. 129 // Make sure that this rect will get ignored (as long as the anchor view is 130 // attached). 131 EXPECT_EQ(anchor_view, bubble_delegate->GetAnchorView()); 132 const gfx::Rect set_anchor_rect = gfx::Rect(10, 10, 100, 100); 133 bubble_delegate->SetAnchorRectForTest(set_anchor_rect); 134 const gfx::Rect view_rect = bubble_delegate->GetAnchorRect(); 135 EXPECT_NE(view_rect.ToString(), set_anchor_rect.ToString()); 136 137 // Create the bubble. 138 bubble_widget->Show(); 139 EXPECT_EQ(anchor_widget, bubble_delegate->anchor_widget()); 140 141 // Remove now the anchor view and make sure that the original found rect 142 // is still kept, so that the bubble does not jump when the view gets deleted. 143 anchor_widget->GetContentsView()->RemoveChildView(anchor_view.get()); 144 anchor_view.reset(); 145 EXPECT_EQ(NULL, bubble_delegate->GetAnchorView()); 146 EXPECT_EQ(view_rect.ToString(), bubble_delegate->GetAnchorRect().ToString()); 147 } 148 149 // Testing that a move of the anchor view will lead to new bubble locations. 150 TEST_F(BubbleDelegateTest, TestAnchorRectMovesWithViewTest) { 151 // Create an anchor widget and add a view to be used as anchor view. 152 scoped_ptr<Widget> anchor_widget(CreateTestWidget()); 153 TestBubbleDelegateView* bubble_delegate = new TestBubbleDelegateView( 154 anchor_widget->GetContentsView()); 155 BubbleDelegateView::CreateBubble(bubble_delegate); 156 157 anchor_widget->GetContentsView()->SetBounds(10, 10, 100, 100); 158 const gfx::Rect view_rect = bubble_delegate->GetAnchorRect(); 159 160 anchor_widget->GetContentsView()->SetBounds(20, 10, 100, 100); 161 const gfx::Rect view_rect_2 = bubble_delegate->GetAnchorRect(); 162 EXPECT_NE(view_rect.ToString(), view_rect_2.ToString()); 163 } 164 165 TEST_F(BubbleDelegateTest, ResetAnchorWidget) { 166 scoped_ptr<Widget> anchor_widget(CreateTestWidget()); 167 BubbleDelegateView* bubble_delegate = new BubbleDelegateView( 168 anchor_widget->GetContentsView(), BubbleBorder::NONE); 169 170 // Make sure the bubble widget is parented to a widget other than the anchor 171 // widget so that closing the anchor widget does not close the bubble widget. 172 scoped_ptr<Widget> parent_widget(CreateTestWidget()); 173 bubble_delegate->set_parent_window(parent_widget->GetNativeView()); 174 // Preventing close on deactivate should not prevent closing with the parent. 175 bubble_delegate->set_close_on_deactivate(false); 176 Widget* bubble_widget = BubbleDelegateView::CreateBubble(bubble_delegate); 177 EXPECT_EQ(bubble_delegate, bubble_widget->widget_delegate()); 178 EXPECT_EQ(bubble_widget, bubble_delegate->GetWidget()); 179 EXPECT_EQ(anchor_widget, bubble_delegate->anchor_widget()); 180 test::TestWidgetObserver bubble_observer(bubble_widget); 181 EXPECT_FALSE(bubble_observer.widget_closed()); 182 183 // Showing and hiding the bubble widget should have no effect on its anchor. 184 bubble_widget->Show(); 185 EXPECT_EQ(anchor_widget, bubble_delegate->anchor_widget()); 186 bubble_widget->Hide(); 187 EXPECT_EQ(anchor_widget, bubble_delegate->anchor_widget()); 188 189 // Ensure that closing the anchor widget clears the bubble's reference to that 190 // anchor widget, but the bubble itself does not close. 191 anchor_widget->CloseNow(); 192 EXPECT_NE(anchor_widget, bubble_delegate->anchor_widget()); 193 EXPECT_FALSE(bubble_observer.widget_closed()); 194 195 // TODO(msw): Remove activation hack to prevent bookkeeping errors in: 196 // aura::test::TestActivationClient::OnWindowDestroyed(). 197 scoped_ptr<Widget> smoke_and_mirrors_widget(CreateTestWidget()); 198 EXPECT_FALSE(bubble_observer.widget_closed()); 199 200 // Ensure that closing the parent widget also closes the bubble itself. 201 parent_widget->CloseNow(); 202 EXPECT_TRUE(bubble_observer.widget_closed()); 203 } 204 205 TEST_F(BubbleDelegateTest, InitiallyFocusedView) { 206 scoped_ptr<Widget> anchor_widget(CreateTestWidget()); 207 BubbleDelegateView* bubble_delegate = new BubbleDelegateView( 208 anchor_widget->GetContentsView(), BubbleBorder::NONE); 209 Widget* bubble_widget = BubbleDelegateView::CreateBubble(bubble_delegate); 210 EXPECT_EQ(bubble_delegate->GetInitiallyFocusedView(), 211 bubble_widget->GetFocusManager()->GetFocusedView()); 212 bubble_widget->CloseNow(); 213 } 214 215 TEST_F(BubbleDelegateTest, NonClientHitTest) { 216 scoped_ptr<Widget> anchor_widget(CreateTestWidget()); 217 TestBubbleDelegateView* bubble_delegate = 218 new TestBubbleDelegateView(anchor_widget->GetContentsView()); 219 BubbleDelegateView::CreateBubble(bubble_delegate); 220 BubbleFrameView* frame = bubble_delegate->GetBubbleFrameView(); 221 const int border = frame->bubble_border()->GetBorderThickness(); 222 223 struct { 224 const int point; 225 const int hit; 226 } cases[] = { 227 { border, HTNOWHERE }, 228 { border + 50, HTCLIENT }, 229 { 1000, HTNOWHERE }, 230 }; 231 232 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) { 233 gfx::Point point(cases[i].point, cases[i].point); 234 EXPECT_EQ(cases[i].hit, frame->NonClientHitTest(point)) 235 << " with border: " << border << ", at point " << cases[i].point; 236 } 237 } 238 239 TEST_F(BubbleDelegateTest, VisibleWhenAnchorWidgetBoundsChanged) { 240 scoped_ptr<Widget> anchor_widget(CreateTestWidget()); 241 BubbleDelegateView* bubble_delegate = new BubbleDelegateView( 242 anchor_widget->GetContentsView(), BubbleBorder::NONE); 243 Widget* bubble_widget = BubbleDelegateView::CreateBubble(bubble_delegate); 244 test::TestWidgetObserver bubble_observer(bubble_widget); 245 EXPECT_FALSE(bubble_observer.widget_closed()); 246 247 bubble_widget->Show(); 248 EXPECT_TRUE(bubble_widget->IsVisible()); 249 anchor_widget->SetBounds(gfx::Rect(10, 10, 100, 100)); 250 EXPECT_TRUE(bubble_widget->IsVisible()); 251 } 252 253 // Test that setting WidgetDelegate::set_can_activate() to false makes the 254 // widget created via BubbleDelegateView::CreateBubble() not activatable. 255 TEST_F(BubbleDelegateTest, NotActivatable) { 256 scoped_ptr<Widget> anchor_widget(CreateTestWidget()); 257 BubbleDelegateView* bubble_delegate = new BubbleDelegateView( 258 anchor_widget->GetContentsView(), BubbleBorder::NONE); 259 bubble_delegate->set_can_activate(false); 260 Widget* bubble_widget = BubbleDelegateView::CreateBubble(bubble_delegate); 261 bubble_widget->Show(); 262 EXPECT_FALSE(bubble_widget->CanActivate()); 263 } 264 265 } // namespace views 266