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 "base/memory/scoped_ptr.h"
6 #include "ui/gfx/geometry/insets.h"
7 #include "ui/gfx/geometry/rect.h"
8 #include "ui/gfx/geometry/size.h"
9 #include "ui/views/bubble/bubble_border.h"
10 #include "ui/views/bubble/bubble_frame_view.h"
11 #include "ui/views/test/test_views.h"
12 #include "ui/views/test/views_test_base.h"
13 #include "ui/views/widget/widget.h"
14 #include "ui/views/widget/widget_delegate.h"
18 typedef ViewsTestBase BubbleFrameViewTest
;
22 const BubbleBorder::Arrow kArrow
= BubbleBorder::TOP_LEFT
;
23 const SkColor kColor
= SK_ColorRED
;
24 const int kMargin
= 6;
25 const int kMinimumClientWidth
= 100;
26 const int kMinimumClientHeight
= 200;
27 const int kPreferredClientWidth
= 150;
28 const int kPreferredClientHeight
= 250;
29 const int kExpectedBorderWidth
= 22;
30 const int kExpectedBorderHeight
= 29;
32 class TestBubbleFrameViewWidgetDelegate
: public WidgetDelegate
{
34 TestBubbleFrameViewWidgetDelegate(Widget
* widget
) : widget_(widget
) {}
36 ~TestBubbleFrameViewWidgetDelegate() override
{}
38 // WidgetDelegate overrides:
39 Widget
* GetWidget() override
{ return widget_
; }
40 const Widget
* GetWidget() const override
{ return widget_
; }
42 View
* GetContentsView() override
{
43 if (!contents_view_
) {
44 StaticSizedView
* contents_view
= new StaticSizedView(
45 gfx::Size(kPreferredClientWidth
, kPreferredClientHeight
));
46 contents_view
->set_minimum_size(
47 gfx::Size(kMinimumClientWidth
, kMinimumClientHeight
));
48 contents_view_
= contents_view
;
50 return contents_view_
;
55 View
* contents_view_
= nullptr; // Owned by |widget_|.
58 class TestBubbleFrameView
: public BubbleFrameView
{
60 TestBubbleFrameView(ViewsTestBase
* test_base
)
61 : BubbleFrameView(gfx::Insets(kMargin
, kMargin
, kMargin
, kMargin
)),
62 test_base_(test_base
),
63 available_bounds_(gfx::Rect(0, 0, 1000, 1000)) {
64 SetBubbleBorder(scoped_ptr
<BubbleBorder
>(
65 new BubbleBorder(kArrow
, BubbleBorder::NO_SHADOW
, kColor
)));
67 ~TestBubbleFrameView() override
{}
70 const Widget
* GetWidget() const override
{
72 widget_
.reset(new Widget
);
73 widget_delegate_
.reset(
74 new TestBubbleFrameViewWidgetDelegate(widget_
.get()));
75 Widget::InitParams params
=
76 test_base_
->CreateParams(Widget::InitParams::TYPE_BUBBLE
);
77 params
.delegate
= widget_delegate_
.get();
78 params
.ownership
= Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
79 widget_
->Init(params
);
85 // BubbleFrameView overrides:
86 gfx::Rect
GetAvailableScreenBounds(const gfx::Rect
& rect
) override
{
87 return available_bounds_
;
91 ViewsTestBase
* test_base_
;
93 gfx::Rect available_bounds_
;
95 // Widget returned by GetWidget(). Only created if GetWidget() is called.
96 mutable scoped_ptr
<TestBubbleFrameViewWidgetDelegate
> widget_delegate_
;
97 mutable scoped_ptr
<Widget
> widget_
;
99 DISALLOW_COPY_AND_ASSIGN(TestBubbleFrameView
);
104 TEST_F(BubbleFrameViewTest
, GetBoundsForClientView
) {
105 TestBubbleFrameView
frame(this);
106 EXPECT_EQ(kArrow
, frame
.bubble_border()->arrow());
107 EXPECT_EQ(kColor
, frame
.bubble_border()->background_color());
109 int margin_x
= frame
.content_margins().left();
110 int margin_y
= frame
.content_margins().top();
111 gfx::Insets insets
= frame
.bubble_border()->GetInsets();
112 EXPECT_EQ(insets
.left() + margin_x
, frame
.GetBoundsForClientView().x());
113 EXPECT_EQ(insets
.top() + margin_y
, frame
.GetBoundsForClientView().y());
116 // Tests that the arrow is mirrored as needed to better fit the screen.
117 TEST_F(BubbleFrameViewTest
, GetUpdatedWindowBounds
) {
118 TestBubbleFrameView
frame(this);
119 gfx::Rect window_bounds
;
121 gfx::Insets insets
= frame
.bubble_border()->GetInsets();
122 int xposition
= 95 - insets
.width();
124 // Test that the info bubble displays normally when it fits.
125 frame
.bubble_border()->set_arrow(BubbleBorder::TOP_LEFT
);
126 window_bounds
= frame
.GetUpdatedWindowBounds(
127 gfx::Rect(100, 100, 50, 50), // |anchor_rect|
128 gfx::Size(500, 500), // |client_size|
129 true); // |adjust_if_offscreen|
130 EXPECT_EQ(BubbleBorder::TOP_LEFT
, frame
.bubble_border()->arrow());
131 EXPECT_GT(window_bounds
.x(), xposition
);
132 EXPECT_GT(window_bounds
.y(), 100 + 50 - 10); // -10 to roughly compensate for
135 // Test bubble not fitting on left.
136 frame
.bubble_border()->set_arrow(BubbleBorder::TOP_RIGHT
);
137 window_bounds
= frame
.GetUpdatedWindowBounds(
138 gfx::Rect(100, 100, 50, 50), // |anchor_rect|
139 gfx::Size(500, 500), // |client_size|
140 true); // |adjust_if_offscreen|
141 EXPECT_EQ(BubbleBorder::TOP_LEFT
, frame
.bubble_border()->arrow());
142 EXPECT_GT(window_bounds
.x(), xposition
);
143 EXPECT_GT(window_bounds
.y(), 100 + 50 - 10); // -10 to roughly compensate for
146 // Test bubble not fitting on left or top.
147 frame
.bubble_border()->set_arrow(BubbleBorder::BOTTOM_RIGHT
);
148 window_bounds
= frame
.GetUpdatedWindowBounds(
149 gfx::Rect(100, 100, 50, 50), // |anchor_rect|
150 gfx::Size(500, 500), // |client_size|
151 true); // |adjust_if_offscreen|
152 EXPECT_EQ(BubbleBorder::TOP_LEFT
, frame
.bubble_border()->arrow());
153 EXPECT_GT(window_bounds
.x(), xposition
);
154 EXPECT_GT(window_bounds
.y(), 100 + 50 - 10); // -10 to roughly compensate for
157 // Test bubble not fitting on top.
158 frame
.bubble_border()->set_arrow(BubbleBorder::BOTTOM_LEFT
);
159 window_bounds
= frame
.GetUpdatedWindowBounds(
160 gfx::Rect(100, 100, 50, 50), // |anchor_rect|
161 gfx::Size(500, 500), // |client_size|
162 true); // |adjust_if_offscreen|
163 EXPECT_EQ(BubbleBorder::TOP_LEFT
, frame
.bubble_border()->arrow());
164 EXPECT_GT(window_bounds
.x(), xposition
);
165 EXPECT_GT(window_bounds
.y(), 100 + 50 - 10); // -10 to roughly compensate for
168 // Test bubble not fitting on top and right.
169 frame
.bubble_border()->set_arrow(BubbleBorder::BOTTOM_LEFT
);
170 window_bounds
= frame
.GetUpdatedWindowBounds(
171 gfx::Rect(900, 100, 50, 50), // |anchor_rect|
172 gfx::Size(500, 500), // |client_size|
173 true); // |adjust_if_offscreen|
174 EXPECT_EQ(BubbleBorder::TOP_RIGHT
, frame
.bubble_border()->arrow());
175 EXPECT_LT(window_bounds
.x(), 900 + 50 - 500);
176 EXPECT_GT(window_bounds
.y(), 100 + 50 - 10); // -10 to roughly compensate for
179 // Test bubble not fitting on right.
180 frame
.bubble_border()->set_arrow(BubbleBorder::TOP_LEFT
);
181 window_bounds
= frame
.GetUpdatedWindowBounds(
182 gfx::Rect(900, 100, 50, 50), // |anchor_rect|
183 gfx::Size(500, 500), // |client_size|
184 true); // |adjust_if_offscreen|
185 EXPECT_EQ(BubbleBorder::TOP_RIGHT
, frame
.bubble_border()->arrow());
186 EXPECT_LT(window_bounds
.x(), 900 + 50 - 500);
187 EXPECT_GT(window_bounds
.y(), 100 + 50 - 10); // -10 to roughly compensate for
190 // Test bubble not fitting on bottom and right.
191 frame
.bubble_border()->set_arrow(BubbleBorder::TOP_LEFT
);
192 window_bounds
= frame
.GetUpdatedWindowBounds(
193 gfx::Rect(900, 900, 50, 50), // |anchor_rect|
194 gfx::Size(500, 500), // |client_size|
195 true); // |adjust_if_offscreen|
196 EXPECT_EQ(BubbleBorder::BOTTOM_RIGHT
, frame
.bubble_border()->arrow());
197 EXPECT_LT(window_bounds
.x(), 900 + 50 - 500);
198 EXPECT_LT(window_bounds
.y(), 900 - 500 - 15); // -15 to roughly compensate
201 // Test bubble not fitting at the bottom.
202 frame
.bubble_border()->set_arrow(BubbleBorder::TOP_LEFT
);
203 window_bounds
= frame
.GetUpdatedWindowBounds(
204 gfx::Rect(100, 900, 50, 50), // |anchor_rect|
205 gfx::Size(500, 500), // |client_size|
206 true); // |adjust_if_offscreen|
207 EXPECT_EQ(BubbleBorder::BOTTOM_LEFT
, frame
.bubble_border()->arrow());
208 // The window should be right aligned with the anchor_rect.
209 EXPECT_LT(window_bounds
.x(), 900 + 50 - 500);
210 EXPECT_LT(window_bounds
.y(), 900 - 500 - 15); // -15 to roughly compensate
213 // Test bubble not fitting at the bottom and left.
214 frame
.bubble_border()->set_arrow(BubbleBorder::TOP_RIGHT
);
215 window_bounds
= frame
.GetUpdatedWindowBounds(
216 gfx::Rect(100, 900, 50, 50), // |anchor_rect|
217 gfx::Size(500, 500), // |client_size|
218 true); // |adjust_if_offscreen|
219 EXPECT_EQ(BubbleBorder::BOTTOM_LEFT
, frame
.bubble_border()->arrow());
220 // The window should be right aligned with the anchor_rect.
221 EXPECT_LT(window_bounds
.x(), 900 + 50 - 500);
222 EXPECT_LT(window_bounds
.y(), 900 - 500 - 15); // -15 to roughly compensate
226 // Tests that the arrow is not moved when the info-bubble does not fit the
227 // screen but moving it would make matter worse.
228 TEST_F(BubbleFrameViewTest
, GetUpdatedWindowBoundsMirroringFails
) {
229 TestBubbleFrameView
frame(this);
230 frame
.bubble_border()->set_arrow(BubbleBorder::TOP_LEFT
);
231 gfx::Rect window_bounds
= frame
.GetUpdatedWindowBounds(
232 gfx::Rect(400, 100, 50, 50), // |anchor_rect|
233 gfx::Size(500, 700), // |client_size|
234 true); // |adjust_if_offscreen|
235 EXPECT_EQ(BubbleBorder::TOP_LEFT
, frame
.bubble_border()->arrow());
238 TEST_F(BubbleFrameViewTest
, TestMirroringForCenteredArrow
) {
239 TestBubbleFrameView
frame(this);
241 // Test bubble not fitting above the anchor.
242 frame
.bubble_border()->set_arrow(BubbleBorder::BOTTOM_CENTER
);
243 gfx::Rect window_bounds
= frame
.GetUpdatedWindowBounds(
244 gfx::Rect(100, 100, 50, 50), // |anchor_rect|
245 gfx::Size(500, 700), // |client_size|
246 true); // |adjust_if_offscreen|
247 EXPECT_EQ(BubbleBorder::TOP_CENTER
, frame
.bubble_border()->arrow());
249 // Test bubble not fitting below the anchor.
250 frame
.bubble_border()->set_arrow(BubbleBorder::TOP_CENTER
);
251 window_bounds
= frame
.GetUpdatedWindowBounds(
252 gfx::Rect(300, 800, 50, 50), // |anchor_rect|
253 gfx::Size(500, 200), // |client_size|
254 true); // |adjust_if_offscreen|
255 EXPECT_EQ(BubbleBorder::BOTTOM_CENTER
, frame
.bubble_border()->arrow());
257 // Test bubble not fitting to the right of the anchor.
258 frame
.bubble_border()->set_arrow(BubbleBorder::LEFT_CENTER
);
259 window_bounds
= frame
.GetUpdatedWindowBounds(
260 gfx::Rect(800, 300, 50, 50), // |anchor_rect|
261 gfx::Size(200, 500), // |client_size|
262 true); // |adjust_if_offscreen|
263 EXPECT_EQ(BubbleBorder::RIGHT_CENTER
, frame
.bubble_border()->arrow());
265 // Test bubble not fitting to the left of the anchor.
266 frame
.bubble_border()->set_arrow(BubbleBorder::RIGHT_CENTER
);
267 window_bounds
= frame
.GetUpdatedWindowBounds(
268 gfx::Rect(100, 300, 50, 50), // |anchor_rect|
269 gfx::Size(500, 500), // |client_size|
270 true); // |adjust_if_offscreen|
271 EXPECT_EQ(BubbleBorder::LEFT_CENTER
, frame
.bubble_border()->arrow());
274 // Test that the arrow will not be mirrored when |adjust_if_offscreen| is false.
275 TEST_F(BubbleFrameViewTest
, GetUpdatedWindowBoundsDontTryMirror
) {
276 TestBubbleFrameView
frame(this);
277 frame
.bubble_border()->set_arrow(BubbleBorder::TOP_RIGHT
);
278 gfx::Rect window_bounds
= frame
.GetUpdatedWindowBounds(
279 gfx::Rect(100, 900, 50, 50), // |anchor_rect|
280 gfx::Size(500, 500), // |client_size|
281 false); // |adjust_if_offscreen|
282 EXPECT_EQ(BubbleBorder::TOP_RIGHT
, frame
.bubble_border()->arrow());
283 // The coordinates should be pointing to anchor_rect from TOP_RIGHT.
284 EXPECT_LT(window_bounds
.x(), 100 + 50 - 500);
285 EXPECT_GT(window_bounds
.y(), 900 + 50 - 10); // -10 to roughly compensate for
289 // Test that the center arrow is moved as needed to fit the screen.
290 TEST_F(BubbleFrameViewTest
, GetUpdatedWindowBoundsCenterArrows
) {
291 TestBubbleFrameView
frame(this);
292 gfx::Rect window_bounds
;
294 // Test that the bubble displays normally when it fits.
295 frame
.bubble_border()->set_arrow(BubbleBorder::TOP_CENTER
);
296 window_bounds
= frame
.GetUpdatedWindowBounds(
297 gfx::Rect(500, 100, 50, 50), // |anchor_rect|
298 gfx::Size(500, 500), // |client_size|
299 true); // |adjust_if_offscreen|
300 EXPECT_EQ(BubbleBorder::TOP_CENTER
, frame
.bubble_border()->arrow());
301 EXPECT_EQ(window_bounds
.x() + window_bounds
.width() / 2, 525);
303 frame
.bubble_border()->set_arrow(BubbleBorder::BOTTOM_CENTER
);
304 window_bounds
= frame
.GetUpdatedWindowBounds(
305 gfx::Rect(500, 900, 50, 50), // |anchor_rect|
306 gfx::Size(500, 500), // |client_size|
307 true); // |adjust_if_offscreen|
308 EXPECT_EQ(BubbleBorder::BOTTOM_CENTER
, frame
.bubble_border()->arrow());
309 EXPECT_EQ(window_bounds
.x() + window_bounds
.width() / 2, 525);
311 frame
.bubble_border()->set_arrow(BubbleBorder::LEFT_CENTER
);
312 window_bounds
= frame
.GetUpdatedWindowBounds(
313 gfx::Rect(100, 400, 50, 50), // |anchor_rect|
314 gfx::Size(500, 500), // |client_size|
315 true); // |adjust_if_offscreen|
316 EXPECT_EQ(BubbleBorder::LEFT_CENTER
, frame
.bubble_border()->arrow());
317 EXPECT_EQ(window_bounds
.y() + window_bounds
.height() / 2, 425);
319 frame
.bubble_border()->set_arrow(BubbleBorder::RIGHT_CENTER
);
320 window_bounds
= frame
.GetUpdatedWindowBounds(
321 gfx::Rect(900, 400, 50, 50), // |anchor_rect|
322 gfx::Size(500, 500), // |client_size|
323 true); // |adjust_if_offscreen|
324 EXPECT_EQ(BubbleBorder::RIGHT_CENTER
, frame
.bubble_border()->arrow());
325 EXPECT_EQ(window_bounds
.y() + window_bounds
.height() / 2, 425);
327 // Test bubble not fitting left screen edge.
328 frame
.bubble_border()->set_arrow(BubbleBorder::TOP_CENTER
);
329 window_bounds
= frame
.GetUpdatedWindowBounds(
330 gfx::Rect(100, 100, 50, 50), // |anchor_rect|
331 gfx::Size(500, 500), // |client_size|
332 true); // |adjust_if_offscreen|
333 EXPECT_EQ(BubbleBorder::TOP_CENTER
, frame
.bubble_border()->arrow());
334 EXPECT_EQ(window_bounds
.x(), 0);
335 EXPECT_EQ(window_bounds
.x() +
336 frame
.bubble_border()->GetArrowOffset(window_bounds
.size()), 125);
338 frame
.bubble_border()->set_arrow(BubbleBorder::BOTTOM_CENTER
);
339 window_bounds
= frame
.GetUpdatedWindowBounds(
340 gfx::Rect(100, 900, 50, 50), // |anchor_rect|
341 gfx::Size(500, 500), // |client_size|
342 true); // |adjust_if_offscreen|
343 EXPECT_EQ(BubbleBorder::BOTTOM_CENTER
, frame
.bubble_border()->arrow());
344 EXPECT_EQ(window_bounds
.x(), 0);
345 EXPECT_EQ(window_bounds
.x() +
346 frame
.bubble_border()->GetArrowOffset(window_bounds
.size()), 125);
348 // Test bubble not fitting right screen edge.
349 frame
.bubble_border()->set_arrow(BubbleBorder::TOP_CENTER
);
350 window_bounds
= frame
.GetUpdatedWindowBounds(
351 gfx::Rect(900, 100, 50, 50), // |anchor_rect|
352 gfx::Size(500, 500), // |client_size|
353 true); // |adjust_if_offscreen|
354 EXPECT_EQ(BubbleBorder::TOP_CENTER
, frame
.bubble_border()->arrow());
355 EXPECT_EQ(window_bounds
.right(), 1000);
356 EXPECT_EQ(window_bounds
.x() +
357 frame
.bubble_border()->GetArrowOffset(window_bounds
.size()), 925);
359 frame
.bubble_border()->set_arrow(BubbleBorder::BOTTOM_CENTER
);
360 window_bounds
= frame
.GetUpdatedWindowBounds(
361 gfx::Rect(900, 900, 50, 50), // |anchor_rect|
362 gfx::Size(500, 500), // |client_size|
363 true); // |adjust_if_offscreen|
364 EXPECT_EQ(BubbleBorder::BOTTOM_CENTER
, frame
.bubble_border()->arrow());
365 EXPECT_EQ(window_bounds
.right(), 1000);
366 EXPECT_EQ(window_bounds
.x() +
367 frame
.bubble_border()->GetArrowOffset(window_bounds
.size()), 925);
369 // Test bubble not fitting top screen edge.
370 frame
.bubble_border()->set_arrow(BubbleBorder::LEFT_CENTER
);
371 window_bounds
= frame
.GetUpdatedWindowBounds(
372 gfx::Rect(100, 100, 50, 50), // |anchor_rect|
373 gfx::Size(500, 500), // |client_size|
374 true); // |adjust_if_offscreen|
375 EXPECT_EQ(BubbleBorder::LEFT_CENTER
, frame
.bubble_border()->arrow());
376 EXPECT_EQ(window_bounds
.y(), 0);
377 EXPECT_EQ(window_bounds
.y() +
378 frame
.bubble_border()->GetArrowOffset(window_bounds
.size()), 125);
380 frame
.bubble_border()->set_arrow(BubbleBorder::RIGHT_CENTER
);
381 window_bounds
= frame
.GetUpdatedWindowBounds(
382 gfx::Rect(900, 100, 50, 50), // |anchor_rect|
383 gfx::Size(500, 500), // |client_size|
384 true); // |adjust_if_offscreen|
385 EXPECT_EQ(BubbleBorder::RIGHT_CENTER
, frame
.bubble_border()->arrow());
386 EXPECT_EQ(window_bounds
.y(), 0);
387 EXPECT_EQ(window_bounds
.y() +
388 frame
.bubble_border()->GetArrowOffset(window_bounds
.size()), 125);
390 // Test bubble not fitting bottom screen edge.
391 frame
.bubble_border()->set_arrow(BubbleBorder::LEFT_CENTER
);
392 window_bounds
= frame
.GetUpdatedWindowBounds(
393 gfx::Rect(100, 900, 50, 50), // |anchor_rect|
394 gfx::Size(500, 500), // |client_size|
395 true); // |adjust_if_offscreen|
396 EXPECT_EQ(BubbleBorder::LEFT_CENTER
, frame
.bubble_border()->arrow());
397 EXPECT_EQ(window_bounds
.bottom(), 1000);
398 EXPECT_EQ(window_bounds
.y() +
399 frame
.bubble_border()->GetArrowOffset(window_bounds
.size()), 925);
401 frame
.bubble_border()->set_arrow(BubbleBorder::RIGHT_CENTER
);
402 window_bounds
= frame
.GetUpdatedWindowBounds(
403 gfx::Rect(900, 900, 50, 50), // |anchor_rect|
404 gfx::Size(500, 500), // |client_size|
405 true); // |adjust_if_offscreen|
406 EXPECT_EQ(BubbleBorder::RIGHT_CENTER
, frame
.bubble_border()->arrow());
407 EXPECT_EQ(window_bounds
.bottom(), 1000);
408 EXPECT_EQ(window_bounds
.y() +
409 frame
.bubble_border()->GetArrowOffset(window_bounds
.size()), 925);
412 TEST_F(BubbleFrameViewTest
, GetPreferredSize
) {
413 TestBubbleFrameView
frame(this);
414 gfx::Size preferred_size
= frame
.GetPreferredSize();
415 // Expect that a border has been added to the preferred size.
416 EXPECT_EQ(kPreferredClientWidth
+ kExpectedBorderWidth
,
417 preferred_size
.width());
418 EXPECT_EQ(kPreferredClientHeight
+ kExpectedBorderHeight
,
419 preferred_size
.height());
422 TEST_F(BubbleFrameViewTest
, GetMinimumSize
) {
423 TestBubbleFrameView
frame(this);
424 gfx::Size minimum_size
= frame
.GetMinimumSize();
425 // Expect that a border has been added to the minimum size.
426 EXPECT_EQ(kMinimumClientWidth
+ kExpectedBorderWidth
, minimum_size
.width());
427 EXPECT_EQ(kMinimumClientHeight
+ kExpectedBorderHeight
,
428 minimum_size
.height());