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.
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"
17 class TestBubbleDelegateView
: public BubbleDelegateView
{
19 TestBubbleDelegateView(View
* anchor_view
)
20 : BubbleDelegateView(anchor_view
, BubbleBorder::TOP_LEFT
),
22 view_
->SetFocusable(true);
25 virtual ~TestBubbleDelegateView() {}
27 void SetAnchorRectForTest(gfx::Rect rect
) {
31 void SetAnchorViewForTest(View
* view
) {
35 // BubbleDelegateView overrides:
36 virtual View
* GetInitiallyFocusedView() OVERRIDE
{ return view_
; }
37 virtual gfx::Size
GetPreferredSize() const OVERRIDE
{
38 return gfx::Size(200, 200);
44 DISALLOW_COPY_AND_ASSIGN(TestBubbleDelegateView
);
47 class BubbleDelegateTest
: public ViewsTestBase
{
49 BubbleDelegateTest() {}
50 virtual ~BubbleDelegateTest() {}
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
;
62 DISALLOW_COPY_AND_ASSIGN(BubbleDelegateTest
);
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();
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());
82 EXPECT_FALSE(bubble_observer
.widget_closed());
83 bubble_widget
->CloseNow();
84 EXPECT_TRUE(bubble_observer
.widget_closed());
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());
100 bubble_widget
->Show();
101 EXPECT_EQ(anchor_widget
, bubble_delegate
->anchor_widget());
102 EXPECT_FALSE(bubble_observer
.widget_closed());
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());
109 // Ensure that closing the anchor widget also closes the bubble itself.
110 anchor_widget
->CloseNow();
111 EXPECT_TRUE(bubble_observer
.widget_closed());
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(
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
);
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
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());
137 // Create the bubble.
138 bubble_widget
->Show();
139 EXPECT_EQ(anchor_widget
, bubble_delegate
->anchor_widget());
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());
145 EXPECT_EQ(NULL
, bubble_delegate
->GetAnchorView());
146 EXPECT_EQ(view_rect
.ToString(), bubble_delegate
->GetAnchorRect().ToString());
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
);
157 anchor_widget
->GetContentsView()->SetBounds(10, 10, 100, 100);
158 const gfx::Rect view_rect
= bubble_delegate
->GetAnchorRect();
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());
165 TEST_F(BubbleDelegateTest
, ResetAnchorWidget
) {
166 scoped_ptr
<Widget
> anchor_widget(CreateTestWidget());
167 BubbleDelegateView
* bubble_delegate
= new BubbleDelegateView(
168 anchor_widget
->GetContentsView(), BubbleBorder::NONE
);
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());
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());
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());
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());
200 // Ensure that closing the parent widget also closes the bubble itself.
201 parent_widget
->CloseNow();
202 EXPECT_TRUE(bubble_observer
.widget_closed());
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();
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();
227 { border
, HTNOWHERE
},
228 { border
+ 50, HTCLIENT
},
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
;
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());
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());