// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "ui/gfx/insets.h" #include "ui/views/bubble/bubble_border.h" #include "ui/views/bubble/bubble_frame_view.h" #include "ui/views/test/views_test_base.h" #include "ui/views/widget/widget.h" namespace views { typedef ViewsTestBase BubbleFrameViewTest; namespace { const BubbleBorder::Arrow kArrow = BubbleBorder::TOP_LEFT; const SkColor kColor = SK_ColorRED; const int kMargin = 6; class TestBubbleFrameView : public BubbleFrameView { public: TestBubbleFrameView() : BubbleFrameView(gfx::Insets(kMargin, kMargin, kMargin, kMargin)), monitor_bounds_(gfx::Rect(0, 0, 1000, 1000)) { SetBubbleBorder(new BubbleBorder(kArrow, BubbleBorder::NO_SHADOW, kColor)); } virtual ~TestBubbleFrameView() {} // BubbleFrameView overrides: virtual gfx::Rect GetMonitorBounds(const gfx::Rect& rect) OVERRIDE { return monitor_bounds_; } private: gfx::Rect monitor_bounds_; DISALLOW_COPY_AND_ASSIGN(TestBubbleFrameView); }; } // namespace TEST_F(BubbleFrameViewTest, GetBoundsForClientView) { TestBubbleFrameView frame; EXPECT_EQ(kArrow, frame.bubble_border()->arrow()); EXPECT_EQ(kColor, frame.bubble_border()->background_color()); int margin_x = frame.content_margins().left(); int margin_y = frame.content_margins().top(); gfx::Insets insets = frame.bubble_border()->GetInsets(); EXPECT_EQ(insets.left() + margin_x, frame.GetBoundsForClientView().x()); EXPECT_EQ(insets.top() + margin_y, frame.GetBoundsForClientView().y()); } // Tests that the arrow is mirrored as needed to better fit the screen. TEST_F(BubbleFrameViewTest, GetUpdatedWindowBounds) { TestBubbleFrameView frame; gfx::Rect window_bounds; gfx::Insets insets = frame.bubble_border()->GetInsets(); int xposition = 95 - insets.width(); // Test that the info bubble displays normally when it fits. frame.bubble_border()->set_arrow(BubbleBorder::TOP_LEFT); window_bounds = frame.GetUpdatedWindowBounds( gfx::Rect(100, 100, 50, 50), // |anchor_rect| gfx::Size(500, 500), // |client_size| true); // |adjust_if_offscreen| EXPECT_EQ(BubbleBorder::TOP_LEFT, frame.bubble_border()->arrow()); EXPECT_GT(window_bounds.x(), xposition); EXPECT_GT(window_bounds.y(), 100 + 50 - 10); // -10 to roughly compensate for // arrow overlap. // Test bubble not fitting on left. frame.bubble_border()->set_arrow(BubbleBorder::TOP_RIGHT); window_bounds = frame.GetUpdatedWindowBounds( gfx::Rect(100, 100, 50, 50), // |anchor_rect| gfx::Size(500, 500), // |client_size| true); // |adjust_if_offscreen| EXPECT_EQ(BubbleBorder::TOP_LEFT, frame.bubble_border()->arrow()); EXPECT_GT(window_bounds.x(), xposition); EXPECT_GT(window_bounds.y(), 100 + 50 - 10); // -10 to roughly compensate for // arrow overlap. // Test bubble not fitting on left or top. frame.bubble_border()->set_arrow(BubbleBorder::BOTTOM_RIGHT); window_bounds = frame.GetUpdatedWindowBounds( gfx::Rect(100, 100, 50, 50), // |anchor_rect| gfx::Size(500, 500), // |client_size| true); // |adjust_if_offscreen| EXPECT_EQ(BubbleBorder::TOP_LEFT, frame.bubble_border()->arrow()); EXPECT_GT(window_bounds.x(), xposition); EXPECT_GT(window_bounds.y(), 100 + 50 - 10); // -10 to roughly compensate for // arrow overlap. // Test bubble not fitting on top. frame.bubble_border()->set_arrow(BubbleBorder::BOTTOM_LEFT); window_bounds = frame.GetUpdatedWindowBounds( gfx::Rect(100, 100, 50, 50), // |anchor_rect| gfx::Size(500, 500), // |client_size| true); // |adjust_if_offscreen| EXPECT_EQ(BubbleBorder::TOP_LEFT, frame.bubble_border()->arrow()); EXPECT_GT(window_bounds.x(), xposition); EXPECT_GT(window_bounds.y(), 100 + 50 - 10); // -10 to roughly compensate for // arrow overlap. // Test bubble not fitting on top and right. frame.bubble_border()->set_arrow(BubbleBorder::BOTTOM_LEFT); window_bounds = frame.GetUpdatedWindowBounds( gfx::Rect(900, 100, 50, 50), // |anchor_rect| gfx::Size(500, 500), // |client_size| true); // |adjust_if_offscreen| EXPECT_EQ(BubbleBorder::TOP_RIGHT, frame.bubble_border()->arrow()); EXPECT_LT(window_bounds.x(), 900 + 50 - 500); EXPECT_GT(window_bounds.y(), 100 + 50 - 10); // -10 to roughly compensate for // arrow overlap. // Test bubble not fitting on right. frame.bubble_border()->set_arrow(BubbleBorder::TOP_LEFT); window_bounds = frame.GetUpdatedWindowBounds( gfx::Rect(900, 100, 50, 50), // |anchor_rect| gfx::Size(500, 500), // |client_size| true); // |adjust_if_offscreen| EXPECT_EQ(BubbleBorder::TOP_RIGHT, frame.bubble_border()->arrow()); EXPECT_LT(window_bounds.x(), 900 + 50 - 500); EXPECT_GT(window_bounds.y(), 100 + 50 - 10); // -10 to roughly compensate for // arrow overlap. // Test bubble not fitting on bottom and right. frame.bubble_border()->set_arrow(BubbleBorder::TOP_LEFT); window_bounds = frame.GetUpdatedWindowBounds( gfx::Rect(900, 900, 50, 50), // |anchor_rect| gfx::Size(500, 500), // |client_size| true); // |adjust_if_offscreen| EXPECT_EQ(BubbleBorder::BOTTOM_RIGHT, frame.bubble_border()->arrow()); EXPECT_LT(window_bounds.x(), 900 + 50 - 500); EXPECT_LT(window_bounds.y(), 900 - 500 - 15); // -15 to roughly compensate // for arrow height. // Test bubble not fitting at the bottom. frame.bubble_border()->set_arrow(BubbleBorder::TOP_LEFT); window_bounds = frame.GetUpdatedWindowBounds( gfx::Rect(100, 900, 50, 50), // |anchor_rect| gfx::Size(500, 500), // |client_size| true); // |adjust_if_offscreen| EXPECT_EQ(BubbleBorder::BOTTOM_LEFT, frame.bubble_border()->arrow()); // The window should be right aligned with the anchor_rect. EXPECT_LT(window_bounds.x(), 900 + 50 - 500); EXPECT_LT(window_bounds.y(), 900 - 500 - 15); // -15 to roughly compensate // for arrow height. // Test bubble not fitting at the bottom and left. frame.bubble_border()->set_arrow(BubbleBorder::TOP_RIGHT); window_bounds = frame.GetUpdatedWindowBounds( gfx::Rect(100, 900, 50, 50), // |anchor_rect| gfx::Size(500, 500), // |client_size| true); // |adjust_if_offscreen| EXPECT_EQ(BubbleBorder::BOTTOM_LEFT, frame.bubble_border()->arrow()); // The window should be right aligned with the anchor_rect. EXPECT_LT(window_bounds.x(), 900 + 50 - 500); EXPECT_LT(window_bounds.y(), 900 - 500 - 15); // -15 to roughly compensate // for arrow height. } // Tests that the arrow is not moved when the info-bubble does not fit the // screen but moving it would make matter worse. TEST_F(BubbleFrameViewTest, GetUpdatedWindowBoundsMirroringFails) { TestBubbleFrameView frame; frame.bubble_border()->set_arrow(BubbleBorder::TOP_LEFT); gfx::Rect window_bounds = frame.GetUpdatedWindowBounds( gfx::Rect(400, 100, 50, 50), // |anchor_rect| gfx::Size(500, 700), // |client_size| true); // |adjust_if_offscreen| EXPECT_EQ(BubbleBorder::TOP_LEFT, frame.bubble_border()->arrow()); } TEST_F(BubbleFrameViewTest, TestMirroringForCenteredArrow) { TestBubbleFrameView frame; // Test bubble not fitting above the anchor. frame.bubble_border()->set_arrow(BubbleBorder::BOTTOM_CENTER); gfx::Rect window_bounds = frame.GetUpdatedWindowBounds( gfx::Rect(100, 100, 50, 50), // |anchor_rect| gfx::Size(500, 700), // |client_size| true); // |adjust_if_offscreen| EXPECT_EQ(BubbleBorder::TOP_CENTER, frame.bubble_border()->arrow()); // Test bubble not fitting below the anchor. frame.bubble_border()->set_arrow(BubbleBorder::TOP_CENTER); window_bounds = frame.GetUpdatedWindowBounds( gfx::Rect(300, 800, 50, 50), // |anchor_rect| gfx::Size(500, 200), // |client_size| true); // |adjust_if_offscreen| EXPECT_EQ(BubbleBorder::BOTTOM_CENTER, frame.bubble_border()->arrow()); // Test bubble not fitting to the right of the anchor. frame.bubble_border()->set_arrow(BubbleBorder::LEFT_CENTER); window_bounds = frame.GetUpdatedWindowBounds( gfx::Rect(800, 300, 50, 50), // |anchor_rect| gfx::Size(200, 500), // |client_size| true); // |adjust_if_offscreen| EXPECT_EQ(BubbleBorder::RIGHT_CENTER, frame.bubble_border()->arrow()); // Test bubble not fitting to the left of the anchor. frame.bubble_border()->set_arrow(BubbleBorder::RIGHT_CENTER); window_bounds = frame.GetUpdatedWindowBounds( gfx::Rect(100, 300, 50, 50), // |anchor_rect| gfx::Size(500, 500), // |client_size| true); // |adjust_if_offscreen| EXPECT_EQ(BubbleBorder::LEFT_CENTER, frame.bubble_border()->arrow()); } // Test that the arrow will not be mirrored when |adjust_if_offscreen| is false. TEST_F(BubbleFrameViewTest, GetUpdatedWindowBoundsDontTryMirror) { TestBubbleFrameView frame; frame.bubble_border()->set_arrow(BubbleBorder::TOP_RIGHT); gfx::Rect window_bounds = frame.GetUpdatedWindowBounds( gfx::Rect(100, 900, 50, 50), // |anchor_rect| gfx::Size(500, 500), // |client_size| false); // |adjust_if_offscreen| EXPECT_EQ(BubbleBorder::TOP_RIGHT, frame.bubble_border()->arrow()); // The coordinates should be pointing to anchor_rect from TOP_RIGHT. EXPECT_LT(window_bounds.x(), 100 + 50 - 500); EXPECT_GT(window_bounds.y(), 900 + 50 - 10); // -10 to roughly compensate for // arrow overlap. } // Test that the center arrow is moved as needed to fit the screen. TEST_F(BubbleFrameViewTest, GetUpdatedWindowBoundsCenterArrows) { TestBubbleFrameView frame; gfx::Rect window_bounds; // Test that the bubble displays normally when it fits. frame.bubble_border()->set_arrow(BubbleBorder::TOP_CENTER); window_bounds = frame.GetUpdatedWindowBounds( gfx::Rect(500, 100, 50, 50), // |anchor_rect| gfx::Size(500, 500), // |client_size| true); // |adjust_if_offscreen| EXPECT_EQ(BubbleBorder::TOP_CENTER, frame.bubble_border()->arrow()); EXPECT_EQ(window_bounds.x() + window_bounds.width() / 2, 525); frame.bubble_border()->set_arrow(BubbleBorder::BOTTOM_CENTER); window_bounds = frame.GetUpdatedWindowBounds( gfx::Rect(500, 900, 50, 50), // |anchor_rect| gfx::Size(500, 500), // |client_size| true); // |adjust_if_offscreen| EXPECT_EQ(BubbleBorder::BOTTOM_CENTER, frame.bubble_border()->arrow()); EXPECT_EQ(window_bounds.x() + window_bounds.width() / 2, 525); frame.bubble_border()->set_arrow(BubbleBorder::LEFT_CENTER); window_bounds = frame.GetUpdatedWindowBounds( gfx::Rect(100, 400, 50, 50), // |anchor_rect| gfx::Size(500, 500), // |client_size| true); // |adjust_if_offscreen| EXPECT_EQ(BubbleBorder::LEFT_CENTER, frame.bubble_border()->arrow()); EXPECT_EQ(window_bounds.y() + window_bounds.height() / 2, 425); frame.bubble_border()->set_arrow(BubbleBorder::RIGHT_CENTER); window_bounds = frame.GetUpdatedWindowBounds( gfx::Rect(900, 400, 50, 50), // |anchor_rect| gfx::Size(500, 500), // |client_size| true); // |adjust_if_offscreen| EXPECT_EQ(BubbleBorder::RIGHT_CENTER, frame.bubble_border()->arrow()); EXPECT_EQ(window_bounds.y() + window_bounds.height() / 2, 425); // Test bubble not fitting left screen edge. frame.bubble_border()->set_arrow(BubbleBorder::TOP_CENTER); window_bounds = frame.GetUpdatedWindowBounds( gfx::Rect(100, 100, 50, 50), // |anchor_rect| gfx::Size(500, 500), // |client_size| true); // |adjust_if_offscreen| EXPECT_EQ(BubbleBorder::TOP_CENTER, frame.bubble_border()->arrow()); EXPECT_EQ(window_bounds.x(), 0); EXPECT_EQ(window_bounds.x() + frame.bubble_border()->GetArrowOffset(window_bounds.size()), 125); frame.bubble_border()->set_arrow(BubbleBorder::BOTTOM_CENTER); window_bounds = frame.GetUpdatedWindowBounds( gfx::Rect(100, 900, 50, 50), // |anchor_rect| gfx::Size(500, 500), // |client_size| true); // |adjust_if_offscreen| EXPECT_EQ(BubbleBorder::BOTTOM_CENTER, frame.bubble_border()->arrow()); EXPECT_EQ(window_bounds.x(), 0); EXPECT_EQ(window_bounds.x() + frame.bubble_border()->GetArrowOffset(window_bounds.size()), 125); // Test bubble not fitting right screen edge. frame.bubble_border()->set_arrow(BubbleBorder::TOP_CENTER); window_bounds = frame.GetUpdatedWindowBounds( gfx::Rect(900, 100, 50, 50), // |anchor_rect| gfx::Size(500, 500), // |client_size| true); // |adjust_if_offscreen| EXPECT_EQ(BubbleBorder::TOP_CENTER, frame.bubble_border()->arrow()); EXPECT_EQ(window_bounds.right(), 1000); EXPECT_EQ(window_bounds.x() + frame.bubble_border()->GetArrowOffset(window_bounds.size()), 925); frame.bubble_border()->set_arrow(BubbleBorder::BOTTOM_CENTER); window_bounds = frame.GetUpdatedWindowBounds( gfx::Rect(900, 900, 50, 50), // |anchor_rect| gfx::Size(500, 500), // |client_size| true); // |adjust_if_offscreen| EXPECT_EQ(BubbleBorder::BOTTOM_CENTER, frame.bubble_border()->arrow()); EXPECT_EQ(window_bounds.right(), 1000); EXPECT_EQ(window_bounds.x() + frame.bubble_border()->GetArrowOffset(window_bounds.size()), 925); // Test bubble not fitting top screen edge. frame.bubble_border()->set_arrow(BubbleBorder::LEFT_CENTER); window_bounds = frame.GetUpdatedWindowBounds( gfx::Rect(100, 100, 50, 50), // |anchor_rect| gfx::Size(500, 500), // |client_size| true); // |adjust_if_offscreen| EXPECT_EQ(BubbleBorder::LEFT_CENTER, frame.bubble_border()->arrow()); EXPECT_EQ(window_bounds.y(), 0); EXPECT_EQ(window_bounds.y() + frame.bubble_border()->GetArrowOffset(window_bounds.size()), 125); frame.bubble_border()->set_arrow(BubbleBorder::RIGHT_CENTER); window_bounds = frame.GetUpdatedWindowBounds( gfx::Rect(900, 100, 50, 50), // |anchor_rect| gfx::Size(500, 500), // |client_size| true); // |adjust_if_offscreen| EXPECT_EQ(BubbleBorder::RIGHT_CENTER, frame.bubble_border()->arrow()); EXPECT_EQ(window_bounds.y(), 0); EXPECT_EQ(window_bounds.y() + frame.bubble_border()->GetArrowOffset(window_bounds.size()), 125); // Test bubble not fitting bottom screen edge. frame.bubble_border()->set_arrow(BubbleBorder::LEFT_CENTER); window_bounds = frame.GetUpdatedWindowBounds( gfx::Rect(100, 900, 50, 50), // |anchor_rect| gfx::Size(500, 500), // |client_size| true); // |adjust_if_offscreen| EXPECT_EQ(BubbleBorder::LEFT_CENTER, frame.bubble_border()->arrow()); EXPECT_EQ(window_bounds.bottom(), 1000); EXPECT_EQ(window_bounds.y() + frame.bubble_border()->GetArrowOffset(window_bounds.size()), 925); frame.bubble_border()->set_arrow(BubbleBorder::RIGHT_CENTER); window_bounds = frame.GetUpdatedWindowBounds( gfx::Rect(900, 900, 50, 50), // |anchor_rect| gfx::Size(500, 500), // |client_size| true); // |adjust_if_offscreen| EXPECT_EQ(BubbleBorder::RIGHT_CENTER, frame.bubble_border()->arrow()); EXPECT_EQ(window_bounds.bottom(), 1000); EXPECT_EQ(window_bounds.y() + frame.bubble_border()->GetArrowOffset(window_bounds.size()), 925); } } // namespace views