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 kMaximumClientWidth
= 300;
28 const int kMaximumClientHeight
= 300;
29 const int kPreferredClientWidth
= 150;
30 const int kPreferredClientHeight
= 250;
31 const int kExpectedBorderWidth
= 22;
32 const int kExpectedBorderHeight
= 29;
34 class TestBubbleFrameViewWidgetDelegate
: public WidgetDelegate
{
36 TestBubbleFrameViewWidgetDelegate(Widget
* widget
) : widget_(widget
) {}
38 ~TestBubbleFrameViewWidgetDelegate() override
{}
40 // WidgetDelegate overrides:
41 Widget
* GetWidget() override
{ return widget_
; }
42 const Widget
* GetWidget() const override
{ return widget_
; }
44 View
* GetContentsView() override
{
45 if (!contents_view_
) {
46 StaticSizedView
* contents_view
= new StaticSizedView(
47 gfx::Size(kPreferredClientWidth
, kPreferredClientHeight
));
48 contents_view
->set_minimum_size(
49 gfx::Size(kMinimumClientWidth
, kMinimumClientHeight
));
50 contents_view
->set_maximum_size(
51 gfx::Size(kMaximumClientWidth
, kMaximumClientHeight
));
52 contents_view_
= contents_view
;
54 return contents_view_
;
59 View
* contents_view_
= nullptr; // Owned by |widget_|.
62 class TestBubbleFrameView
: public BubbleFrameView
{
64 TestBubbleFrameView(ViewsTestBase
* test_base
)
65 : BubbleFrameView(gfx::Insets(kMargin
, kMargin
, kMargin
, kMargin
)),
66 test_base_(test_base
),
67 available_bounds_(gfx::Rect(0, 0, 1000, 1000)) {
68 SetBubbleBorder(scoped_ptr
<BubbleBorder
>(
69 new BubbleBorder(kArrow
, BubbleBorder::NO_SHADOW
, kColor
)));
71 ~TestBubbleFrameView() override
{}
74 const Widget
* GetWidget() const override
{
76 widget_
.reset(new Widget
);
77 widget_delegate_
.reset(
78 new TestBubbleFrameViewWidgetDelegate(widget_
.get()));
79 Widget::InitParams params
=
80 test_base_
->CreateParams(Widget::InitParams::TYPE_BUBBLE
);
81 params
.delegate
= widget_delegate_
.get();
82 params
.ownership
= Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
83 widget_
->Init(params
);
89 // BubbleFrameView overrides:
90 gfx::Rect
GetAvailableScreenBounds(const gfx::Rect
& rect
) override
{
91 return available_bounds_
;
95 ViewsTestBase
* test_base_
;
97 gfx::Rect available_bounds_
;
99 // Widget returned by GetWidget(). Only created if GetWidget() is called.
100 mutable scoped_ptr
<TestBubbleFrameViewWidgetDelegate
> widget_delegate_
;
101 mutable scoped_ptr
<Widget
> widget_
;
103 DISALLOW_COPY_AND_ASSIGN(TestBubbleFrameView
);
108 TEST_F(BubbleFrameViewTest
, GetBoundsForClientView
) {
109 TestBubbleFrameView
frame(this);
110 EXPECT_EQ(kArrow
, frame
.bubble_border()->arrow());
111 EXPECT_EQ(kColor
, frame
.bubble_border()->background_color());
113 int margin_x
= frame
.content_margins().left();
114 int margin_y
= frame
.content_margins().top();
115 gfx::Insets insets
= frame
.bubble_border()->GetInsets();
116 EXPECT_EQ(insets
.left() + margin_x
, frame
.GetBoundsForClientView().x());
117 EXPECT_EQ(insets
.top() + margin_y
, frame
.GetBoundsForClientView().y());
120 // Tests that the arrow is mirrored as needed to better fit the screen.
121 TEST_F(BubbleFrameViewTest
, GetUpdatedWindowBounds
) {
122 TestBubbleFrameView
frame(this);
123 gfx::Rect window_bounds
;
125 gfx::Insets insets
= frame
.bubble_border()->GetInsets();
126 int xposition
= 95 - insets
.width();
128 // Test that the info bubble displays normally when it fits.
129 frame
.bubble_border()->set_arrow(BubbleBorder::TOP_LEFT
);
130 window_bounds
= frame
.GetUpdatedWindowBounds(
131 gfx::Rect(100, 100, 50, 50), // |anchor_rect|
132 gfx::Size(500, 500), // |client_size|
133 true); // |adjust_if_offscreen|
134 EXPECT_EQ(BubbleBorder::TOP_LEFT
, frame
.bubble_border()->arrow());
135 EXPECT_GT(window_bounds
.x(), xposition
);
136 EXPECT_GT(window_bounds
.y(), 100 + 50 - 10); // -10 to roughly compensate for
139 // Test bubble not fitting on left.
140 frame
.bubble_border()->set_arrow(BubbleBorder::TOP_RIGHT
);
141 window_bounds
= frame
.GetUpdatedWindowBounds(
142 gfx::Rect(100, 100, 50, 50), // |anchor_rect|
143 gfx::Size(500, 500), // |client_size|
144 true); // |adjust_if_offscreen|
145 EXPECT_EQ(BubbleBorder::TOP_LEFT
, frame
.bubble_border()->arrow());
146 EXPECT_GT(window_bounds
.x(), xposition
);
147 EXPECT_GT(window_bounds
.y(), 100 + 50 - 10); // -10 to roughly compensate for
150 // Test bubble not fitting on left or top.
151 frame
.bubble_border()->set_arrow(BubbleBorder::BOTTOM_RIGHT
);
152 window_bounds
= frame
.GetUpdatedWindowBounds(
153 gfx::Rect(100, 100, 50, 50), // |anchor_rect|
154 gfx::Size(500, 500), // |client_size|
155 true); // |adjust_if_offscreen|
156 EXPECT_EQ(BubbleBorder::TOP_LEFT
, frame
.bubble_border()->arrow());
157 EXPECT_GT(window_bounds
.x(), xposition
);
158 EXPECT_GT(window_bounds
.y(), 100 + 50 - 10); // -10 to roughly compensate for
161 // Test bubble not fitting on top.
162 frame
.bubble_border()->set_arrow(BubbleBorder::BOTTOM_LEFT
);
163 window_bounds
= frame
.GetUpdatedWindowBounds(
164 gfx::Rect(100, 100, 50, 50), // |anchor_rect|
165 gfx::Size(500, 500), // |client_size|
166 true); // |adjust_if_offscreen|
167 EXPECT_EQ(BubbleBorder::TOP_LEFT
, frame
.bubble_border()->arrow());
168 EXPECT_GT(window_bounds
.x(), xposition
);
169 EXPECT_GT(window_bounds
.y(), 100 + 50 - 10); // -10 to roughly compensate for
172 // Test bubble not fitting on top and right.
173 frame
.bubble_border()->set_arrow(BubbleBorder::BOTTOM_LEFT
);
174 window_bounds
= frame
.GetUpdatedWindowBounds(
175 gfx::Rect(900, 100, 50, 50), // |anchor_rect|
176 gfx::Size(500, 500), // |client_size|
177 true); // |adjust_if_offscreen|
178 EXPECT_EQ(BubbleBorder::TOP_RIGHT
, frame
.bubble_border()->arrow());
179 EXPECT_LT(window_bounds
.x(), 900 + 50 - 500);
180 EXPECT_GT(window_bounds
.y(), 100 + 50 - 10); // -10 to roughly compensate for
183 // Test bubble not fitting on right.
184 frame
.bubble_border()->set_arrow(BubbleBorder::TOP_LEFT
);
185 window_bounds
= frame
.GetUpdatedWindowBounds(
186 gfx::Rect(900, 100, 50, 50), // |anchor_rect|
187 gfx::Size(500, 500), // |client_size|
188 true); // |adjust_if_offscreen|
189 EXPECT_EQ(BubbleBorder::TOP_RIGHT
, frame
.bubble_border()->arrow());
190 EXPECT_LT(window_bounds
.x(), 900 + 50 - 500);
191 EXPECT_GT(window_bounds
.y(), 100 + 50 - 10); // -10 to roughly compensate for
194 // Test bubble not fitting on bottom and right.
195 frame
.bubble_border()->set_arrow(BubbleBorder::TOP_LEFT
);
196 window_bounds
= frame
.GetUpdatedWindowBounds(
197 gfx::Rect(900, 900, 50, 50), // |anchor_rect|
198 gfx::Size(500, 500), // |client_size|
199 true); // |adjust_if_offscreen|
200 EXPECT_EQ(BubbleBorder::BOTTOM_RIGHT
, frame
.bubble_border()->arrow());
201 EXPECT_LT(window_bounds
.x(), 900 + 50 - 500);
202 EXPECT_LT(window_bounds
.y(), 900 - 500 - 15); // -15 to roughly compensate
205 // Test bubble not fitting at the bottom.
206 frame
.bubble_border()->set_arrow(BubbleBorder::TOP_LEFT
);
207 window_bounds
= frame
.GetUpdatedWindowBounds(
208 gfx::Rect(100, 900, 50, 50), // |anchor_rect|
209 gfx::Size(500, 500), // |client_size|
210 true); // |adjust_if_offscreen|
211 EXPECT_EQ(BubbleBorder::BOTTOM_LEFT
, frame
.bubble_border()->arrow());
212 // The window should be right aligned with the anchor_rect.
213 EXPECT_LT(window_bounds
.x(), 900 + 50 - 500);
214 EXPECT_LT(window_bounds
.y(), 900 - 500 - 15); // -15 to roughly compensate
217 // Test bubble not fitting at the bottom and left.
218 frame
.bubble_border()->set_arrow(BubbleBorder::TOP_RIGHT
);
219 window_bounds
= frame
.GetUpdatedWindowBounds(
220 gfx::Rect(100, 900, 50, 50), // |anchor_rect|
221 gfx::Size(500, 500), // |client_size|
222 true); // |adjust_if_offscreen|
223 EXPECT_EQ(BubbleBorder::BOTTOM_LEFT
, frame
.bubble_border()->arrow());
224 // The window should be right aligned with the anchor_rect.
225 EXPECT_LT(window_bounds
.x(), 900 + 50 - 500);
226 EXPECT_LT(window_bounds
.y(), 900 - 500 - 15); // -15 to roughly compensate
230 // Tests that the arrow is not moved when the info-bubble does not fit the
231 // screen but moving it would make matter worse.
232 TEST_F(BubbleFrameViewTest
, GetUpdatedWindowBoundsMirroringFails
) {
233 TestBubbleFrameView
frame(this);
234 frame
.bubble_border()->set_arrow(BubbleBorder::TOP_LEFT
);
235 gfx::Rect window_bounds
= frame
.GetUpdatedWindowBounds(
236 gfx::Rect(400, 100, 50, 50), // |anchor_rect|
237 gfx::Size(500, 700), // |client_size|
238 true); // |adjust_if_offscreen|
239 EXPECT_EQ(BubbleBorder::TOP_LEFT
, frame
.bubble_border()->arrow());
242 TEST_F(BubbleFrameViewTest
, TestMirroringForCenteredArrow
) {
243 TestBubbleFrameView
frame(this);
245 // Test bubble not fitting above the anchor.
246 frame
.bubble_border()->set_arrow(BubbleBorder::BOTTOM_CENTER
);
247 gfx::Rect window_bounds
= frame
.GetUpdatedWindowBounds(
248 gfx::Rect(100, 100, 50, 50), // |anchor_rect|
249 gfx::Size(500, 700), // |client_size|
250 true); // |adjust_if_offscreen|
251 EXPECT_EQ(BubbleBorder::TOP_CENTER
, frame
.bubble_border()->arrow());
253 // Test bubble not fitting below the anchor.
254 frame
.bubble_border()->set_arrow(BubbleBorder::TOP_CENTER
);
255 window_bounds
= frame
.GetUpdatedWindowBounds(
256 gfx::Rect(300, 800, 50, 50), // |anchor_rect|
257 gfx::Size(500, 200), // |client_size|
258 true); // |adjust_if_offscreen|
259 EXPECT_EQ(BubbleBorder::BOTTOM_CENTER
, frame
.bubble_border()->arrow());
261 // Test bubble not fitting to the right of the anchor.
262 frame
.bubble_border()->set_arrow(BubbleBorder::LEFT_CENTER
);
263 window_bounds
= frame
.GetUpdatedWindowBounds(
264 gfx::Rect(800, 300, 50, 50), // |anchor_rect|
265 gfx::Size(200, 500), // |client_size|
266 true); // |adjust_if_offscreen|
267 EXPECT_EQ(BubbleBorder::RIGHT_CENTER
, frame
.bubble_border()->arrow());
269 // Test bubble not fitting to the left of the anchor.
270 frame
.bubble_border()->set_arrow(BubbleBorder::RIGHT_CENTER
);
271 window_bounds
= frame
.GetUpdatedWindowBounds(
272 gfx::Rect(100, 300, 50, 50), // |anchor_rect|
273 gfx::Size(500, 500), // |client_size|
274 true); // |adjust_if_offscreen|
275 EXPECT_EQ(BubbleBorder::LEFT_CENTER
, frame
.bubble_border()->arrow());
278 // Test that the arrow will not be mirrored when |adjust_if_offscreen| is false.
279 TEST_F(BubbleFrameViewTest
, GetUpdatedWindowBoundsDontTryMirror
) {
280 TestBubbleFrameView
frame(this);
281 frame
.bubble_border()->set_arrow(BubbleBorder::TOP_RIGHT
);
282 gfx::Rect window_bounds
= frame
.GetUpdatedWindowBounds(
283 gfx::Rect(100, 900, 50, 50), // |anchor_rect|
284 gfx::Size(500, 500), // |client_size|
285 false); // |adjust_if_offscreen|
286 EXPECT_EQ(BubbleBorder::TOP_RIGHT
, frame
.bubble_border()->arrow());
287 // The coordinates should be pointing to anchor_rect from TOP_RIGHT.
288 EXPECT_LT(window_bounds
.x(), 100 + 50 - 500);
289 EXPECT_GT(window_bounds
.y(), 900 + 50 - 10); // -10 to roughly compensate for
293 // Test that the center arrow is moved as needed to fit the screen.
294 TEST_F(BubbleFrameViewTest
, GetUpdatedWindowBoundsCenterArrows
) {
295 TestBubbleFrameView
frame(this);
296 gfx::Rect window_bounds
;
298 // Test that the bubble displays normally when it fits.
299 frame
.bubble_border()->set_arrow(BubbleBorder::TOP_CENTER
);
300 window_bounds
= frame
.GetUpdatedWindowBounds(
301 gfx::Rect(500, 100, 50, 50), // |anchor_rect|
302 gfx::Size(500, 500), // |client_size|
303 true); // |adjust_if_offscreen|
304 EXPECT_EQ(BubbleBorder::TOP_CENTER
, frame
.bubble_border()->arrow());
305 EXPECT_EQ(window_bounds
.x() + window_bounds
.width() / 2, 525);
307 frame
.bubble_border()->set_arrow(BubbleBorder::BOTTOM_CENTER
);
308 window_bounds
= frame
.GetUpdatedWindowBounds(
309 gfx::Rect(500, 900, 50, 50), // |anchor_rect|
310 gfx::Size(500, 500), // |client_size|
311 true); // |adjust_if_offscreen|
312 EXPECT_EQ(BubbleBorder::BOTTOM_CENTER
, frame
.bubble_border()->arrow());
313 EXPECT_EQ(window_bounds
.x() + window_bounds
.width() / 2, 525);
315 frame
.bubble_border()->set_arrow(BubbleBorder::LEFT_CENTER
);
316 window_bounds
= frame
.GetUpdatedWindowBounds(
317 gfx::Rect(100, 400, 50, 50), // |anchor_rect|
318 gfx::Size(500, 500), // |client_size|
319 true); // |adjust_if_offscreen|
320 EXPECT_EQ(BubbleBorder::LEFT_CENTER
, frame
.bubble_border()->arrow());
321 EXPECT_EQ(window_bounds
.y() + window_bounds
.height() / 2, 425);
323 frame
.bubble_border()->set_arrow(BubbleBorder::RIGHT_CENTER
);
324 window_bounds
= frame
.GetUpdatedWindowBounds(
325 gfx::Rect(900, 400, 50, 50), // |anchor_rect|
326 gfx::Size(500, 500), // |client_size|
327 true); // |adjust_if_offscreen|
328 EXPECT_EQ(BubbleBorder::RIGHT_CENTER
, frame
.bubble_border()->arrow());
329 EXPECT_EQ(window_bounds
.y() + window_bounds
.height() / 2, 425);
331 // Test bubble not fitting left screen edge.
332 frame
.bubble_border()->set_arrow(BubbleBorder::TOP_CENTER
);
333 window_bounds
= frame
.GetUpdatedWindowBounds(
334 gfx::Rect(100, 100, 50, 50), // |anchor_rect|
335 gfx::Size(500, 500), // |client_size|
336 true); // |adjust_if_offscreen|
337 EXPECT_EQ(BubbleBorder::TOP_CENTER
, frame
.bubble_border()->arrow());
338 EXPECT_EQ(window_bounds
.x(), 0);
339 EXPECT_EQ(window_bounds
.x() +
340 frame
.bubble_border()->GetArrowOffset(window_bounds
.size()), 125);
342 frame
.bubble_border()->set_arrow(BubbleBorder::BOTTOM_CENTER
);
343 window_bounds
= frame
.GetUpdatedWindowBounds(
344 gfx::Rect(100, 900, 50, 50), // |anchor_rect|
345 gfx::Size(500, 500), // |client_size|
346 true); // |adjust_if_offscreen|
347 EXPECT_EQ(BubbleBorder::BOTTOM_CENTER
, frame
.bubble_border()->arrow());
348 EXPECT_EQ(window_bounds
.x(), 0);
349 EXPECT_EQ(window_bounds
.x() +
350 frame
.bubble_border()->GetArrowOffset(window_bounds
.size()), 125);
352 // Test bubble not fitting right screen edge.
353 frame
.bubble_border()->set_arrow(BubbleBorder::TOP_CENTER
);
354 window_bounds
= frame
.GetUpdatedWindowBounds(
355 gfx::Rect(900, 100, 50, 50), // |anchor_rect|
356 gfx::Size(500, 500), // |client_size|
357 true); // |adjust_if_offscreen|
358 EXPECT_EQ(BubbleBorder::TOP_CENTER
, frame
.bubble_border()->arrow());
359 EXPECT_EQ(window_bounds
.right(), 1000);
360 EXPECT_EQ(window_bounds
.x() +
361 frame
.bubble_border()->GetArrowOffset(window_bounds
.size()), 925);
363 frame
.bubble_border()->set_arrow(BubbleBorder::BOTTOM_CENTER
);
364 window_bounds
= frame
.GetUpdatedWindowBounds(
365 gfx::Rect(900, 900, 50, 50), // |anchor_rect|
366 gfx::Size(500, 500), // |client_size|
367 true); // |adjust_if_offscreen|
368 EXPECT_EQ(BubbleBorder::BOTTOM_CENTER
, frame
.bubble_border()->arrow());
369 EXPECT_EQ(window_bounds
.right(), 1000);
370 EXPECT_EQ(window_bounds
.x() +
371 frame
.bubble_border()->GetArrowOffset(window_bounds
.size()), 925);
373 // Test bubble not fitting top screen edge.
374 frame
.bubble_border()->set_arrow(BubbleBorder::LEFT_CENTER
);
375 window_bounds
= frame
.GetUpdatedWindowBounds(
376 gfx::Rect(100, 100, 50, 50), // |anchor_rect|
377 gfx::Size(500, 500), // |client_size|
378 true); // |adjust_if_offscreen|
379 EXPECT_EQ(BubbleBorder::LEFT_CENTER
, frame
.bubble_border()->arrow());
380 EXPECT_EQ(window_bounds
.y(), 0);
381 EXPECT_EQ(window_bounds
.y() +
382 frame
.bubble_border()->GetArrowOffset(window_bounds
.size()), 125);
384 frame
.bubble_border()->set_arrow(BubbleBorder::RIGHT_CENTER
);
385 window_bounds
= frame
.GetUpdatedWindowBounds(
386 gfx::Rect(900, 100, 50, 50), // |anchor_rect|
387 gfx::Size(500, 500), // |client_size|
388 true); // |adjust_if_offscreen|
389 EXPECT_EQ(BubbleBorder::RIGHT_CENTER
, frame
.bubble_border()->arrow());
390 EXPECT_EQ(window_bounds
.y(), 0);
391 EXPECT_EQ(window_bounds
.y() +
392 frame
.bubble_border()->GetArrowOffset(window_bounds
.size()), 125);
394 // Test bubble not fitting bottom screen edge.
395 frame
.bubble_border()->set_arrow(BubbleBorder::LEFT_CENTER
);
396 window_bounds
= frame
.GetUpdatedWindowBounds(
397 gfx::Rect(100, 900, 50, 50), // |anchor_rect|
398 gfx::Size(500, 500), // |client_size|
399 true); // |adjust_if_offscreen|
400 EXPECT_EQ(BubbleBorder::LEFT_CENTER
, frame
.bubble_border()->arrow());
401 EXPECT_EQ(window_bounds
.bottom(), 1000);
402 EXPECT_EQ(window_bounds
.y() +
403 frame
.bubble_border()->GetArrowOffset(window_bounds
.size()), 925);
405 frame
.bubble_border()->set_arrow(BubbleBorder::RIGHT_CENTER
);
406 window_bounds
= frame
.GetUpdatedWindowBounds(
407 gfx::Rect(900, 900, 50, 50), // |anchor_rect|
408 gfx::Size(500, 500), // |client_size|
409 true); // |adjust_if_offscreen|
410 EXPECT_EQ(BubbleBorder::RIGHT_CENTER
, frame
.bubble_border()->arrow());
411 EXPECT_EQ(window_bounds
.bottom(), 1000);
412 EXPECT_EQ(window_bounds
.y() +
413 frame
.bubble_border()->GetArrowOffset(window_bounds
.size()), 925);
416 TEST_F(BubbleFrameViewTest
, GetPreferredSize
) {
417 TestBubbleFrameView
frame(this);
418 gfx::Size preferred_size
= frame
.GetPreferredSize();
419 // Expect that a border has been added to the preferred size.
420 EXPECT_EQ(kPreferredClientWidth
+ kExpectedBorderWidth
,
421 preferred_size
.width());
422 EXPECT_EQ(kPreferredClientHeight
+ kExpectedBorderHeight
,
423 preferred_size
.height());
426 TEST_F(BubbleFrameViewTest
, GetMinimumSize
) {
427 TestBubbleFrameView
frame(this);
428 gfx::Size minimum_size
= frame
.GetMinimumSize();
429 // Expect that a border has been added to the minimum size.
430 EXPECT_EQ(kMinimumClientWidth
+ kExpectedBorderWidth
, minimum_size
.width());
431 EXPECT_EQ(kMinimumClientHeight
+ kExpectedBorderHeight
,
432 minimum_size
.height());
435 TEST_F(BubbleFrameViewTest
, GetMaximumSize
) {
436 TestBubbleFrameView
frame(this);
437 gfx::Size maximum_size
= frame
.GetMaximumSize();
439 // On Windows, GetMaximumSize causes problems with DWM, so it should just be 0
440 // (unlimited). See http://crbug.com/506206.
441 EXPECT_EQ(0, maximum_size
.width());
442 EXPECT_EQ(0, maximum_size
.height());
444 // Should ignore the contents view's maximum size and use the preferred size.
445 EXPECT_EQ(kPreferredClientWidth
+ kExpectedBorderWidth
, maximum_size
.width());
446 EXPECT_EQ(kPreferredClientHeight
+ kExpectedBorderHeight
,
447 maximum_size
.height());