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/gfx/insets.h"
6#include "ui/views/bubble/bubble_border.h"
7#include "ui/views/bubble/bubble_frame_view.h"
8#include "ui/views/test/views_test_base.h"
9#include "ui/views/widget/widget.h"
10
11namespace views {
12
13typedef ViewsTestBase BubbleFrameViewTest;
14
15namespace {
16
17const BubbleBorder::Arrow kArrow = BubbleBorder::TOP_LEFT;
18const SkColor kColor = SK_ColorRED;
19const int kMargin = 6;
20
21class TestBubbleFrameView : public BubbleFrameView {
22 public:
23  TestBubbleFrameView()
24      : BubbleFrameView(gfx::Insets(kMargin, kMargin, kMargin, kMargin)),
25        available_bounds_(gfx::Rect(0, 0, 1000, 1000)) {
26    SetBubbleBorder(scoped_ptr<views::BubbleBorder>(
27        new BubbleBorder(kArrow, BubbleBorder::NO_SHADOW, kColor)));
28  }
29  virtual ~TestBubbleFrameView() {}
30
31  // BubbleFrameView overrides:
32  virtual gfx::Rect GetAvailableScreenBounds(const gfx::Rect& rect) OVERRIDE {
33    return available_bounds_;
34  }
35
36 private:
37  gfx::Rect available_bounds_;
38
39  DISALLOW_COPY_AND_ASSIGN(TestBubbleFrameView);
40};
41
42}  // namespace
43
44TEST_F(BubbleFrameViewTest, GetBoundsForClientView) {
45  TestBubbleFrameView frame;
46  EXPECT_EQ(kArrow, frame.bubble_border()->arrow());
47  EXPECT_EQ(kColor, frame.bubble_border()->background_color());
48
49  int margin_x = frame.content_margins().left();
50  int margin_y = frame.content_margins().top();
51  gfx::Insets insets = frame.bubble_border()->GetInsets();
52  EXPECT_EQ(insets.left() + margin_x, frame.GetBoundsForClientView().x());
53  EXPECT_EQ(insets.top() + margin_y, frame.GetBoundsForClientView().y());
54}
55
56// Tests that the arrow is mirrored as needed to better fit the screen.
57TEST_F(BubbleFrameViewTest, GetUpdatedWindowBounds) {
58  TestBubbleFrameView frame;
59  gfx::Rect window_bounds;
60
61  gfx::Insets insets = frame.bubble_border()->GetInsets();
62  int xposition = 95 - insets.width();
63
64  // Test that the info bubble displays normally when it fits.
65  frame.bubble_border()->set_arrow(BubbleBorder::TOP_LEFT);
66  window_bounds = frame.GetUpdatedWindowBounds(
67      gfx::Rect(100, 100, 50, 50),  // |anchor_rect|
68      gfx::Size(500, 500),          // |client_size|
69      true);                        // |adjust_if_offscreen|
70  EXPECT_EQ(BubbleBorder::TOP_LEFT, frame.bubble_border()->arrow());
71  EXPECT_GT(window_bounds.x(), xposition);
72  EXPECT_GT(window_bounds.y(), 100 + 50 - 10);  // -10 to roughly compensate for
73                                                // arrow overlap.
74
75  // Test bubble not fitting on left.
76  frame.bubble_border()->set_arrow(BubbleBorder::TOP_RIGHT);
77  window_bounds = frame.GetUpdatedWindowBounds(
78      gfx::Rect(100, 100, 50, 50),  // |anchor_rect|
79      gfx::Size(500, 500),          // |client_size|
80      true);                        // |adjust_if_offscreen|
81  EXPECT_EQ(BubbleBorder::TOP_LEFT, frame.bubble_border()->arrow());
82  EXPECT_GT(window_bounds.x(), xposition);
83  EXPECT_GT(window_bounds.y(), 100 + 50 - 10);  // -10 to roughly compensate for
84                                                // arrow overlap.
85
86  // Test bubble not fitting on left or top.
87  frame.bubble_border()->set_arrow(BubbleBorder::BOTTOM_RIGHT);
88  window_bounds = frame.GetUpdatedWindowBounds(
89      gfx::Rect(100, 100, 50, 50),  // |anchor_rect|
90      gfx::Size(500, 500),          // |client_size|
91      true);                        // |adjust_if_offscreen|
92  EXPECT_EQ(BubbleBorder::TOP_LEFT, frame.bubble_border()->arrow());
93  EXPECT_GT(window_bounds.x(), xposition);
94  EXPECT_GT(window_bounds.y(), 100 + 50 - 10);  // -10 to roughly compensate for
95                                                // arrow overlap.
96
97  // Test bubble not fitting on top.
98  frame.bubble_border()->set_arrow(BubbleBorder::BOTTOM_LEFT);
99  window_bounds = frame.GetUpdatedWindowBounds(
100      gfx::Rect(100, 100, 50, 50),  // |anchor_rect|
101      gfx::Size(500, 500),          // |client_size|
102      true);                        // |adjust_if_offscreen|
103  EXPECT_EQ(BubbleBorder::TOP_LEFT, frame.bubble_border()->arrow());
104  EXPECT_GT(window_bounds.x(), xposition);
105  EXPECT_GT(window_bounds.y(), 100 + 50 - 10);  // -10 to roughly compensate for
106                                                // arrow overlap.
107
108  // Test bubble not fitting on top and right.
109  frame.bubble_border()->set_arrow(BubbleBorder::BOTTOM_LEFT);
110  window_bounds = frame.GetUpdatedWindowBounds(
111      gfx::Rect(900, 100, 50, 50),  // |anchor_rect|
112      gfx::Size(500, 500),          // |client_size|
113      true);                        // |adjust_if_offscreen|
114  EXPECT_EQ(BubbleBorder::TOP_RIGHT, frame.bubble_border()->arrow());
115  EXPECT_LT(window_bounds.x(), 900 + 50 - 500);
116  EXPECT_GT(window_bounds.y(), 100 + 50 - 10);  // -10 to roughly compensate for
117                                                // arrow overlap.
118
119  // Test bubble not fitting on right.
120  frame.bubble_border()->set_arrow(BubbleBorder::TOP_LEFT);
121  window_bounds = frame.GetUpdatedWindowBounds(
122      gfx::Rect(900, 100, 50, 50),  // |anchor_rect|
123      gfx::Size(500, 500),          // |client_size|
124      true);                        // |adjust_if_offscreen|
125  EXPECT_EQ(BubbleBorder::TOP_RIGHT, frame.bubble_border()->arrow());
126  EXPECT_LT(window_bounds.x(), 900 + 50 - 500);
127  EXPECT_GT(window_bounds.y(), 100 + 50 - 10);  // -10 to roughly compensate for
128                                                // arrow overlap.
129
130  // Test bubble not fitting on bottom and right.
131  frame.bubble_border()->set_arrow(BubbleBorder::TOP_LEFT);
132  window_bounds = frame.GetUpdatedWindowBounds(
133      gfx::Rect(900, 900, 50, 50),  // |anchor_rect|
134      gfx::Size(500, 500),          // |client_size|
135      true);                        // |adjust_if_offscreen|
136  EXPECT_EQ(BubbleBorder::BOTTOM_RIGHT, frame.bubble_border()->arrow());
137  EXPECT_LT(window_bounds.x(), 900 + 50 - 500);
138  EXPECT_LT(window_bounds.y(), 900 - 500 - 15);  // -15 to roughly compensate
139                                                 // for arrow height.
140
141  // Test bubble not fitting at the bottom.
142  frame.bubble_border()->set_arrow(BubbleBorder::TOP_LEFT);
143  window_bounds = frame.GetUpdatedWindowBounds(
144      gfx::Rect(100, 900, 50, 50),  // |anchor_rect|
145      gfx::Size(500, 500),          // |client_size|
146      true);                        // |adjust_if_offscreen|
147  EXPECT_EQ(BubbleBorder::BOTTOM_LEFT, frame.bubble_border()->arrow());
148  // The window should be right aligned with the anchor_rect.
149  EXPECT_LT(window_bounds.x(), 900 + 50 - 500);
150  EXPECT_LT(window_bounds.y(), 900 - 500 - 15);  // -15 to roughly compensate
151                                                 // for arrow height.
152
153  // Test bubble not fitting at the bottom and left.
154  frame.bubble_border()->set_arrow(BubbleBorder::TOP_RIGHT);
155  window_bounds = frame.GetUpdatedWindowBounds(
156      gfx::Rect(100, 900, 50, 50),  // |anchor_rect|
157      gfx::Size(500, 500),          // |client_size|
158      true);                        // |adjust_if_offscreen|
159  EXPECT_EQ(BubbleBorder::BOTTOM_LEFT, frame.bubble_border()->arrow());
160  // The window should be right aligned with the anchor_rect.
161  EXPECT_LT(window_bounds.x(), 900 + 50 - 500);
162  EXPECT_LT(window_bounds.y(), 900 - 500 - 15);  // -15 to roughly compensate
163                                                 // for arrow height.
164}
165
166// Tests that the arrow is not moved when the info-bubble does not fit the
167// screen but moving it would make matter worse.
168TEST_F(BubbleFrameViewTest, GetUpdatedWindowBoundsMirroringFails) {
169  TestBubbleFrameView frame;
170  frame.bubble_border()->set_arrow(BubbleBorder::TOP_LEFT);
171  gfx::Rect window_bounds = frame.GetUpdatedWindowBounds(
172      gfx::Rect(400, 100, 50, 50),  // |anchor_rect|
173      gfx::Size(500, 700),          // |client_size|
174      true);                        // |adjust_if_offscreen|
175  EXPECT_EQ(BubbleBorder::TOP_LEFT, frame.bubble_border()->arrow());
176}
177
178TEST_F(BubbleFrameViewTest, TestMirroringForCenteredArrow) {
179  TestBubbleFrameView frame;
180
181  // Test bubble not fitting above the anchor.
182  frame.bubble_border()->set_arrow(BubbleBorder::BOTTOM_CENTER);
183  gfx::Rect window_bounds = frame.GetUpdatedWindowBounds(
184      gfx::Rect(100, 100, 50, 50),  // |anchor_rect|
185      gfx::Size(500, 700),          // |client_size|
186      true);                        // |adjust_if_offscreen|
187  EXPECT_EQ(BubbleBorder::TOP_CENTER, frame.bubble_border()->arrow());
188
189  // Test bubble not fitting below the anchor.
190  frame.bubble_border()->set_arrow(BubbleBorder::TOP_CENTER);
191  window_bounds = frame.GetUpdatedWindowBounds(
192      gfx::Rect(300, 800, 50, 50),  // |anchor_rect|
193      gfx::Size(500, 200),          // |client_size|
194      true);                        // |adjust_if_offscreen|
195  EXPECT_EQ(BubbleBorder::BOTTOM_CENTER, frame.bubble_border()->arrow());
196
197  // Test bubble not fitting to the right of the anchor.
198  frame.bubble_border()->set_arrow(BubbleBorder::LEFT_CENTER);
199  window_bounds = frame.GetUpdatedWindowBounds(
200      gfx::Rect(800, 300, 50, 50),  // |anchor_rect|
201      gfx::Size(200, 500),          // |client_size|
202      true);                        // |adjust_if_offscreen|
203  EXPECT_EQ(BubbleBorder::RIGHT_CENTER, frame.bubble_border()->arrow());
204
205  // Test bubble not fitting to the left of the anchor.
206  frame.bubble_border()->set_arrow(BubbleBorder::RIGHT_CENTER);
207  window_bounds = frame.GetUpdatedWindowBounds(
208      gfx::Rect(100, 300, 50, 50),  // |anchor_rect|
209      gfx::Size(500, 500),          // |client_size|
210      true);                        // |adjust_if_offscreen|
211  EXPECT_EQ(BubbleBorder::LEFT_CENTER, frame.bubble_border()->arrow());
212}
213
214// Test that the arrow will not be mirrored when |adjust_if_offscreen| is false.
215TEST_F(BubbleFrameViewTest, GetUpdatedWindowBoundsDontTryMirror) {
216  TestBubbleFrameView frame;
217  frame.bubble_border()->set_arrow(BubbleBorder::TOP_RIGHT);
218  gfx::Rect window_bounds = frame.GetUpdatedWindowBounds(
219      gfx::Rect(100, 900, 50, 50),  // |anchor_rect|
220      gfx::Size(500, 500),          // |client_size|
221      false);                       // |adjust_if_offscreen|
222  EXPECT_EQ(BubbleBorder::TOP_RIGHT, frame.bubble_border()->arrow());
223  // The coordinates should be pointing to anchor_rect from TOP_RIGHT.
224  EXPECT_LT(window_bounds.x(), 100 + 50 - 500);
225  EXPECT_GT(window_bounds.y(), 900 + 50 - 10);  // -10 to roughly compensate for
226                                                // arrow overlap.
227}
228
229// Test that the center arrow is moved as needed to fit the screen.
230TEST_F(BubbleFrameViewTest, GetUpdatedWindowBoundsCenterArrows) {
231  TestBubbleFrameView frame;
232  gfx::Rect window_bounds;
233
234  // Test that the bubble displays normally when it fits.
235  frame.bubble_border()->set_arrow(BubbleBorder::TOP_CENTER);
236  window_bounds = frame.GetUpdatedWindowBounds(
237      gfx::Rect(500, 100, 50, 50),  // |anchor_rect|
238      gfx::Size(500, 500),          // |client_size|
239      true);                        // |adjust_if_offscreen|
240  EXPECT_EQ(BubbleBorder::TOP_CENTER, frame.bubble_border()->arrow());
241  EXPECT_EQ(window_bounds.x() + window_bounds.width() / 2, 525);
242
243  frame.bubble_border()->set_arrow(BubbleBorder::BOTTOM_CENTER);
244  window_bounds = frame.GetUpdatedWindowBounds(
245      gfx::Rect(500, 900, 50, 50),  // |anchor_rect|
246      gfx::Size(500, 500),          // |client_size|
247      true);                        // |adjust_if_offscreen|
248  EXPECT_EQ(BubbleBorder::BOTTOM_CENTER, frame.bubble_border()->arrow());
249  EXPECT_EQ(window_bounds.x() + window_bounds.width() / 2, 525);
250
251  frame.bubble_border()->set_arrow(BubbleBorder::LEFT_CENTER);
252  window_bounds = frame.GetUpdatedWindowBounds(
253      gfx::Rect(100, 400, 50, 50),  // |anchor_rect|
254      gfx::Size(500, 500),          // |client_size|
255      true);                        // |adjust_if_offscreen|
256  EXPECT_EQ(BubbleBorder::LEFT_CENTER, frame.bubble_border()->arrow());
257  EXPECT_EQ(window_bounds.y() + window_bounds.height() / 2, 425);
258
259  frame.bubble_border()->set_arrow(BubbleBorder::RIGHT_CENTER);
260  window_bounds = frame.GetUpdatedWindowBounds(
261      gfx::Rect(900, 400, 50, 50),  // |anchor_rect|
262      gfx::Size(500, 500),          // |client_size|
263      true);                        // |adjust_if_offscreen|
264  EXPECT_EQ(BubbleBorder::RIGHT_CENTER, frame.bubble_border()->arrow());
265  EXPECT_EQ(window_bounds.y() + window_bounds.height() / 2, 425);
266
267  // Test bubble not fitting left screen edge.
268  frame.bubble_border()->set_arrow(BubbleBorder::TOP_CENTER);
269  window_bounds = frame.GetUpdatedWindowBounds(
270      gfx::Rect(100, 100, 50, 50),  // |anchor_rect|
271      gfx::Size(500, 500),          // |client_size|
272      true);                        // |adjust_if_offscreen|
273  EXPECT_EQ(BubbleBorder::TOP_CENTER, frame.bubble_border()->arrow());
274  EXPECT_EQ(window_bounds.x(), 0);
275  EXPECT_EQ(window_bounds.x() +
276            frame.bubble_border()->GetArrowOffset(window_bounds.size()), 125);
277
278  frame.bubble_border()->set_arrow(BubbleBorder::BOTTOM_CENTER);
279  window_bounds = frame.GetUpdatedWindowBounds(
280      gfx::Rect(100, 900, 50, 50),  // |anchor_rect|
281      gfx::Size(500, 500),          // |client_size|
282      true);                        // |adjust_if_offscreen|
283  EXPECT_EQ(BubbleBorder::BOTTOM_CENTER, frame.bubble_border()->arrow());
284  EXPECT_EQ(window_bounds.x(), 0);
285  EXPECT_EQ(window_bounds.x() +
286            frame.bubble_border()->GetArrowOffset(window_bounds.size()), 125);
287
288  // Test bubble not fitting right screen edge.
289  frame.bubble_border()->set_arrow(BubbleBorder::TOP_CENTER);
290  window_bounds = frame.GetUpdatedWindowBounds(
291      gfx::Rect(900, 100, 50, 50),  // |anchor_rect|
292      gfx::Size(500, 500),          // |client_size|
293      true);                        // |adjust_if_offscreen|
294  EXPECT_EQ(BubbleBorder::TOP_CENTER, frame.bubble_border()->arrow());
295  EXPECT_EQ(window_bounds.right(), 1000);
296  EXPECT_EQ(window_bounds.x() +
297            frame.bubble_border()->GetArrowOffset(window_bounds.size()), 925);
298
299  frame.bubble_border()->set_arrow(BubbleBorder::BOTTOM_CENTER);
300  window_bounds = frame.GetUpdatedWindowBounds(
301      gfx::Rect(900, 900, 50, 50),  // |anchor_rect|
302      gfx::Size(500, 500),          // |client_size|
303      true);                        // |adjust_if_offscreen|
304  EXPECT_EQ(BubbleBorder::BOTTOM_CENTER, frame.bubble_border()->arrow());
305  EXPECT_EQ(window_bounds.right(), 1000);
306  EXPECT_EQ(window_bounds.x() +
307            frame.bubble_border()->GetArrowOffset(window_bounds.size()), 925);
308
309  // Test bubble not fitting top screen edge.
310  frame.bubble_border()->set_arrow(BubbleBorder::LEFT_CENTER);
311  window_bounds = frame.GetUpdatedWindowBounds(
312      gfx::Rect(100, 100, 50, 50),  // |anchor_rect|
313      gfx::Size(500, 500),          // |client_size|
314      true);                        // |adjust_if_offscreen|
315  EXPECT_EQ(BubbleBorder::LEFT_CENTER, frame.bubble_border()->arrow());
316  EXPECT_EQ(window_bounds.y(), 0);
317  EXPECT_EQ(window_bounds.y() +
318            frame.bubble_border()->GetArrowOffset(window_bounds.size()), 125);
319
320  frame.bubble_border()->set_arrow(BubbleBorder::RIGHT_CENTER);
321  window_bounds = frame.GetUpdatedWindowBounds(
322      gfx::Rect(900, 100, 50, 50),  // |anchor_rect|
323      gfx::Size(500, 500),          // |client_size|
324      true);                        // |adjust_if_offscreen|
325  EXPECT_EQ(BubbleBorder::RIGHT_CENTER, frame.bubble_border()->arrow());
326  EXPECT_EQ(window_bounds.y(), 0);
327  EXPECT_EQ(window_bounds.y() +
328            frame.bubble_border()->GetArrowOffset(window_bounds.size()), 125);
329
330  // Test bubble not fitting bottom screen edge.
331  frame.bubble_border()->set_arrow(BubbleBorder::LEFT_CENTER);
332  window_bounds = frame.GetUpdatedWindowBounds(
333      gfx::Rect(100, 900, 50, 50),  // |anchor_rect|
334      gfx::Size(500, 500),          // |client_size|
335      true);                        // |adjust_if_offscreen|
336  EXPECT_EQ(BubbleBorder::LEFT_CENTER, frame.bubble_border()->arrow());
337  EXPECT_EQ(window_bounds.bottom(), 1000);
338  EXPECT_EQ(window_bounds.y() +
339            frame.bubble_border()->GetArrowOffset(window_bounds.size()), 925);
340
341  frame.bubble_border()->set_arrow(BubbleBorder::RIGHT_CENTER);
342  window_bounds = frame.GetUpdatedWindowBounds(
343      gfx::Rect(900, 900, 50, 50),  // |anchor_rect|
344      gfx::Size(500, 500),          // |client_size|
345      true);                        // |adjust_if_offscreen|
346  EXPECT_EQ(BubbleBorder::RIGHT_CENTER, frame.bubble_border()->arrow());
347  EXPECT_EQ(window_bounds.bottom(), 1000);
348  EXPECT_EQ(window_bounds.y() +
349            frame.bubble_border()->GetArrowOffset(window_bounds.size()), 925);
350}
351
352}  // namespace views
353