Commit e397c546 authored by msw@chromium.org's avatar msw@chromium.org

Merge BorderContentsView into BubbleFrameView; simplify.

Merge these redundant classes and their unit tests.
Do not try_mirroring_arrow with dummy anchor rects.
Deprecate unused allow_bubble_offscreen bubble flag.
Replace GetArrowLocation virtual with simple setter/getter pair.
Return anchor view bounds in BubbleDelegateView::GetAnchorRect.
Other refactoring and simplification.

BUG=106050
TEST=Bubbles are anchored correctly with their arrows in the right location.

Review URL: http://codereview.chromium.org/8870003

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@114598 0039d316-1c4b-4281-b951-d872f2087c98
parent f70f4435
......@@ -112,11 +112,11 @@ void SpeechInputBubbleView::OnWidgetActivationChanged(views::Widget* widget,
gfx::Rect SpeechInputBubbleView::GetAnchorRect() {
gfx::Rect container_rect;
tab_contents_->GetContainerBounds(&container_rect);
gfx::Point anchor(container_rect.x() + element_rect_.CenterPoint().x(),
container_rect.y() + element_rect_.bottom());
if (!container_rect.Contains(anchor))
gfx::Rect anchor(element_rect_);
anchor.Offset(container_rect.origin());
if (!container_rect.Intersects(anchor))
return BubbleDelegateView::GetAnchorRect();
return gfx::Rect(anchor, gfx::Size());
return anchor;
}
void SpeechInputBubbleView::Init() {
......
......@@ -121,7 +121,7 @@ views::View* BookmarkBubbleView::GetInitiallyFocusedView() {
gfx::Rect BookmarkBubbleView::GetAnchorRect() {
// Compensate for some built-in padding in the star image.
gfx::Rect rect(BubbleDelegateView::GetAnchorRect());
rect.Offset(0, -5);
rect.Inset(0, anchor_view() ? 5 : 0);
return rect;
}
......
......@@ -135,7 +135,7 @@ gfx::Size ContentSettingBubbleContents::GetPreferredSize() {
gfx::Rect ContentSettingBubbleContents::GetAnchorRect() {
gfx::Rect rect(BubbleDelegateView::GetAnchorRect());
rect.Offset(0, -5);
rect.Inset(0, anchor_view() ? 5 : 0);
return rect;
}
......
......@@ -390,6 +390,8 @@ void ExtensionInstalledBubble::ShowInternal() {
reference_view = browser_view->GetToolbarView()->app_menu();
set_anchor_view(reference_view);
set_arrow_location(type_ == OMNIBOX_KEYWORD ? views::BubbleBorder::TOP_LEFT :
views::BubbleBorder::TOP_RIGHT);
SetLayoutManager(new views::FillLayout());
AddChildView(
new InstalledBubbleContent(browser_, extension_, type_, &icon_, this));
......@@ -403,19 +405,12 @@ gfx::Rect ExtensionInstalledBubble::GetAnchorRect() {
if (type_ == OMNIBOX_KEYWORD) {
LocationBarView* location_bar_view =
BrowserView::GetBrowserViewForBrowser(browser_)->GetLocationBarView();
return gfx::Rect(location_bar_view->GetLocationEntryOrigin().Add(
gfx::Point(0, location_bar_view->location_entry_view()->height())),
gfx::Size());
return gfx::Rect(location_bar_view->GetLocationEntryOrigin(),
gfx::Size(0, location_bar_view->location_entry_view()->height()));
}
return views::BubbleDelegateView::GetAnchorRect();
}
views::BubbleBorder::ArrowLocation
ExtensionInstalledBubble::GetArrowLocation() const {
return type_ == OMNIBOX_KEYWORD ? views::BubbleBorder::TOP_LEFT :
views::BubbleBorder::TOP_RIGHT;
}
void ExtensionInstalledBubble::WindowClosing() {
if (extension_ && type_ == PAGE_ACTION) {
BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser_);
......
......@@ -65,7 +65,6 @@ class ExtensionInstalledBubble
// views::BubbleDelegate
virtual gfx::Rect GetAnchorRect() OVERRIDE;
virtual views::BubbleBorder::ArrowLocation GetArrowLocation() const OVERRIDE;
const Extension* extension_;
Browser* browser_;
......
......@@ -30,7 +30,7 @@
using content::UserMetricsAction;
namespace {
const int kAnchorVerticalOffset = -4;
const int kAnchorVerticalInset = 5;
const int kLayoutTopInset = 1;
const int kLayoutLeftInset = 2;
const int kLayoutBottomInset = 7;
......@@ -102,7 +102,7 @@ void FirstRunBubble::Init() {
gfx::Rect FirstRunBubble::GetAnchorRect() {
// Compensate for padding in anchor.
gfx::Rect rect(BubbleDelegateView::GetAnchorRect());
rect.Offset(0, anchor_view() ? kAnchorVerticalOffset : 0);
rect.Inset(0, anchor_view() ? kAnchorVerticalInset : 0);
return rect;
}
......
......@@ -29,8 +29,8 @@ const int kMaxBubbleViewWidth = 262;
// The horizontal padding between the title and the icon.
const int kTitleHorizontalPadding = 3;
// The vertical offset of the wrench bubble from the wrench menu button.
const int kWrenchBubblePointOffsetY = -6;
// The vertical inset of the wrench bubble anchor from the wrench menu button.
const int kAnchorVerticalInset = 5;
const int kLayoutBottomPadding = 2;
......@@ -125,7 +125,7 @@ GlobalErrorBubbleView::~GlobalErrorBubbleView() {
gfx::Rect GlobalErrorBubbleView::GetAnchorRect() {
gfx::Rect rect(views::BubbleDelegateView::GetAnchorRect());
rect.Offset(0, anchor_view() ? kWrenchBubblePointOffsetY : 0);
rect.Inset(0, anchor_view() ? kAnchorVerticalInset : 0);
return rect;
}
......
......@@ -298,8 +298,7 @@ void PageInfoBubbleView::OnPageInfoModelChanged() {
gfx::Rect PageInfoBubbleView::GetAnchorRect() {
// Compensate for some built-in padding in the icon.
gfx::Rect anchor(BubbleDelegateView::GetAnchorRect());
if (anchor_view())
anchor.Offset(0, -5);
anchor.Inset(0, anchor_view() ? 5 : 0);
return anchor;
}
......
// Copyright (c) 2011 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/views/bubble/border_contents_view.h"
#include "testing/gtest/include/gtest/gtest.h"
typedef testing::Test BorderContentsTest;
class TestBorderContentsView : public views::BorderContentsView {
public:
TestBorderContentsView() {}
void set_monitor_bounds(const gfx::Rect& bounds) {
monitor_bounds_ = bounds;
}
views::BubbleBorder* bubble_border() const {
return BorderContentsView::bubble_border();
}
protected:
virtual gfx::Rect GetMonitorBounds(const gfx::Rect& rect) {
return monitor_bounds_;
}
private:
gfx::Rect monitor_bounds_;
DISALLOW_COPY_AND_ASSIGN(TestBorderContentsView);
};
// Tests that the arrow is moved appropriately when the info-bubble does not fit
// the screen.
TEST_F(BorderContentsTest, BorderContentsSizeAndGetBounds) {
TestBorderContentsView border_contents;
border_contents.Init();
gfx::Insets insets;
border_contents.bubble_border()->GetInsets(&insets);
int xposition = 95 - insets.width();
// Test that the info bubble displays normally when it fits.
gfx::Rect contents_bounds;
gfx::Rect window_bounds;
border_contents.set_monitor_bounds(gfx::Rect(0, 0, 1000, 1000));
border_contents.SizeAndGetBounds(
gfx::Rect(100, 100, 50, 50), // |position_relative_to|
views::BubbleBorder::TOP_LEFT,
false, // |allow_bubble_offscreen|
gfx::Size(500, 500), // |contents_size|
&contents_bounds, &window_bounds);
// The arrow shouldn't have changed from TOP_LEFT.
views::BubbleBorder::ArrowLocation arrow_location =
border_contents.bubble_border()->arrow_location();
EXPECT_TRUE(views::BubbleBorder::has_arrow(arrow_location));
EXPECT_TRUE(views::BubbleBorder::is_arrow_on_top(arrow_location));
EXPECT_TRUE(views::BubbleBorder::is_arrow_on_left(arrow_location));
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.
border_contents.SizeAndGetBounds(
gfx::Rect(100, 100, 50, 50), // |position_relative_to|
views::BubbleBorder::TOP_RIGHT,
false, // |allow_bubble_offscreen|
gfx::Size(500, 500), // |contents_size|
&contents_bounds, &window_bounds);
arrow_location = border_contents.bubble_border()->arrow_location();
// The arrow should have changed to TOP_LEFT.
EXPECT_TRUE(views::BubbleBorder::has_arrow(arrow_location));
EXPECT_TRUE(views::BubbleBorder::is_arrow_on_top(arrow_location));
EXPECT_TRUE(views::BubbleBorder::is_arrow_on_left(arrow_location));
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.
border_contents.SizeAndGetBounds(
gfx::Rect(100, 100, 50, 50), // |position_relative_to|
views::BubbleBorder::BOTTOM_RIGHT,
false, // |allow_bubble_offscreen|
gfx::Size(500, 500), // |contents_size|
&contents_bounds, &window_bounds);
arrow_location = border_contents.bubble_border()->arrow_location();
// The arrow should have changed to TOP_LEFT.
EXPECT_TRUE(views::BubbleBorder::has_arrow(arrow_location));
EXPECT_TRUE(views::BubbleBorder::is_arrow_on_top(arrow_location));
EXPECT_TRUE(views::BubbleBorder::is_arrow_on_left(arrow_location));
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.
border_contents.SizeAndGetBounds(
gfx::Rect(100, 100, 50, 50), // |position_relative_to|
views::BubbleBorder::BOTTOM_LEFT,
false, // |allow_bubble_offscreen|
gfx::Size(500, 500), // |contents_size|
&contents_bounds, &window_bounds);
arrow_location = border_contents.bubble_border()->arrow_location();
// The arrow should have changed to TOP_LEFT.
EXPECT_TRUE(views::BubbleBorder::has_arrow(arrow_location));
EXPECT_TRUE(views::BubbleBorder::is_arrow_on_top(arrow_location));
EXPECT_TRUE(views::BubbleBorder::is_arrow_on_left(arrow_location));
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.
border_contents.SizeAndGetBounds(
gfx::Rect(900, 100, 50, 50), // |position_relative_to|
views::BubbleBorder::BOTTOM_LEFT,
false, // |allow_bubble_offscreen|
gfx::Size(500, 500), // |contents_size|
&contents_bounds, &window_bounds);
arrow_location = border_contents.bubble_border()->arrow_location();
// The arrow should have changed to TOP_RIGHT.
EXPECT_TRUE(views::BubbleBorder::has_arrow(arrow_location));
EXPECT_TRUE(views::BubbleBorder::is_arrow_on_top(arrow_location));
EXPECT_FALSE(views::BubbleBorder::is_arrow_on_left(arrow_location));
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.
border_contents.SizeAndGetBounds(
gfx::Rect(900, 100, 50, 50), // |position_relative_to|
views::BubbleBorder::TOP_LEFT,
false, // |allow_bubble_offscreen|
gfx::Size(500, 500), // |contents_size|
&contents_bounds, &window_bounds);
arrow_location = border_contents.bubble_border()->arrow_location();
// The arrow should have changed to TOP_RIGHT.
EXPECT_TRUE(views::BubbleBorder::has_arrow(arrow_location));
EXPECT_TRUE(views::BubbleBorder::is_arrow_on_top(arrow_location));
EXPECT_FALSE(views::BubbleBorder::is_arrow_on_left(arrow_location));
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.
border_contents.SizeAndGetBounds(
gfx::Rect(900, 900, 50, 50), // |position_relative_to|
views::BubbleBorder::TOP_LEFT,
false, // |allow_bubble_offscreen|
gfx::Size(500, 500), // |contents_size|
&contents_bounds, &window_bounds);
arrow_location = border_contents.bubble_border()->arrow_location();
// The arrow should have changed to BOTTOM_RIGHT.
EXPECT_TRUE(views::BubbleBorder::has_arrow(arrow_location));
EXPECT_FALSE(views::BubbleBorder::is_arrow_on_top(arrow_location));
EXPECT_FALSE(views::BubbleBorder::is_arrow_on_left(arrow_location));
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.
border_contents.SizeAndGetBounds(
gfx::Rect(100, 900, 50, 50), // |position_relative_to|
views::BubbleBorder::TOP_LEFT,
false, // |allow_bubble_offscreen|
gfx::Size(500, 500), // |contents_size|
&contents_bounds, &window_bounds);
arrow_location = border_contents.bubble_border()->arrow_location();
// The arrow should have changed to BOTTOM_LEFT.
EXPECT_TRUE(views::BubbleBorder::has_arrow(arrow_location));
EXPECT_FALSE(views::BubbleBorder::is_arrow_on_top(arrow_location));
EXPECT_TRUE(views::BubbleBorder::is_arrow_on_left(arrow_location));
// The window should be right aligned with the position_relative_to.
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.
border_contents.SizeAndGetBounds(
gfx::Rect(100, 900, 50, 50), // |position_relative_to|
views::BubbleBorder::TOP_RIGHT,
false, // |allow_bubble_offscreen|
gfx::Size(500, 500), // |contents_size|
&contents_bounds, &window_bounds);
arrow_location = border_contents.bubble_border()->arrow_location();
// The arrow should have changed to BOTTOM_LEFT.
EXPECT_TRUE(views::BubbleBorder::has_arrow(arrow_location));
EXPECT_FALSE(views::BubbleBorder::is_arrow_on_top(arrow_location));
EXPECT_TRUE(views::BubbleBorder::is_arrow_on_left(arrow_location));
// The window should be right aligned with the position_relative_to.
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(BorderContentsTest, BorderContentsSizeAndGetBoundsDontMoveArrow) {
TestBorderContentsView border_contents;
border_contents.Init();
gfx::Rect contents_bounds;
gfx::Rect window_bounds;
border_contents.set_monitor_bounds(gfx::Rect(0, 0, 1000, 1000));
border_contents.SizeAndGetBounds(
gfx::Rect(400, 100, 50, 50), // |position_relative_to|
views::BubbleBorder::TOP_LEFT,
false, // |allow_bubble_offscreen|
gfx::Size(500, 700), // |contents_size|
&contents_bounds, &window_bounds);
// The arrow should not have changed, as it would make it the bubble even more
// offscreen.
views::BubbleBorder::ArrowLocation arrow_location =
border_contents.bubble_border()->arrow_location();
EXPECT_TRUE(views::BubbleBorder::has_arrow(arrow_location));
EXPECT_TRUE(views::BubbleBorder::is_arrow_on_top(arrow_location));
EXPECT_TRUE(views::BubbleBorder::is_arrow_on_left(arrow_location));
}
// Test that the 'allow offscreen' prevents the bubble from moving.
TEST_F(BorderContentsTest, BorderContentsSizeAndGetBoundsAllowOffscreen) {
TestBorderContentsView border_contents;
border_contents.Init();
gfx::Rect contents_bounds;
gfx::Rect window_bounds;
border_contents.set_monitor_bounds(gfx::Rect(0, 0, 1000, 1000));
border_contents.SizeAndGetBounds(
gfx::Rect(100, 900, 50, 50), // |position_relative_to|
views::BubbleBorder::TOP_RIGHT,
true, // |allow_bubble_offscreen|
gfx::Size(500, 500), // |contents_size|
&contents_bounds, &window_bounds);
// The arrow should not have changed (eventhough the bubble does not fit).
views::BubbleBorder::ArrowLocation arrow_location =
border_contents.bubble_border()->arrow_location();
EXPECT_TRUE(views::BubbleBorder::has_arrow(arrow_location));
EXPECT_TRUE(views::BubbleBorder::is_arrow_on_top(arrow_location));
EXPECT_FALSE(views::BubbleBorder::is_arrow_on_left(arrow_location));
// The coordinates should be pointing to 'positive relative to' 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.
}
// Copyright (c) 2011 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/views/bubble/border_contents_view.h"
#include <algorithm>
#include "ui/gfx/screen.h"
static const int kTopMargin = 6;
static const int kLeftMargin = 6;
static const int kBottomMargin = 6;
static const int kRightMargin = 6;
namespace {
// Computes how much |window_bounds| is off-screen of the monitor bounds
// |monitor_bounds| and puts the values in |offscreen_insets|.
// Returns false if |window_bounds| is actually contained in |monitor_bounds|,
// in which case |offscreen_insets| is not modified.
bool ComputeOffScreenInsets(const gfx::Rect& monitor_bounds,
const gfx::Rect& window_bounds,
gfx::Insets* offscreen_insets) {
if (monitor_bounds.Contains(window_bounds))
return false;
if (!offscreen_insets)
return true;
// window_bounds
// +-------------------------------+
// | top |
// | +----------------+ |
// | left | monitor_bounds | right |
// | +----------------+ |
// | bottom |
// +-------------------------------+
int top = std::max(0, monitor_bounds.y() - window_bounds.y());
int left = std::max(0, monitor_bounds.x() - window_bounds.x());
int bottom = std::max(0, window_bounds.bottom() - monitor_bounds.bottom());
int right = std::max(0, window_bounds.right() - monitor_bounds.right());
offscreen_insets->Set(top, left, bottom, right);
return true;
}
// Convenience method that returns the height of |insets| if |vertical| is
// true, its width otherwise.
int GetInsetsLength(const gfx::Insets& insets, bool vertical) {
return vertical ? insets.height() : insets.width();
}
} // namespace
namespace views {
BorderContentsView::BorderContentsView()
: bubble_border_(NULL),
content_margins_(kTopMargin, kLeftMargin, kBottomMargin, kRightMargin) {
}
BorderContentsView::BorderContentsView(int top_margin,
int left_margin,
int bottom_margin,
int right_margin)
: bubble_border_(NULL),
content_margins_(top_margin, left_margin, bottom_margin, right_margin) {
}
BorderContentsView::~BorderContentsView() {}
void BorderContentsView::Init() {
// Default arrow location.
BubbleBorder::ArrowLocation arrow_location =
BubbleBorder::TOP_LEFT;
if (base::i18n::IsRTL())
arrow_location = BubbleBorder::horizontal_mirror(arrow_location);
DCHECK(!bubble_border_);
// TODO(alicet): Expose the shadow option in BorderContentsView when we make
// the fullscreen exit bubble use the new bubble code.
bubble_border_ = new BubbleBorder(arrow_location,
views::BubbleBorder::NO_SHADOW);
set_border(bubble_border_);
set_background(new BubbleBackground(bubble_border_));
}
void BorderContentsView::SetBackgroundColor(SkColor color) {
bubble_border_->set_background_color(color);
}
void BorderContentsView::SetAlignment(
views::BubbleBorder::BubbleAlignment alignment) {
bubble_border_->set_alignment(alignment);
}
void BorderContentsView::SizeAndGetBounds(
const gfx::Rect& position_relative_to,
BubbleBorder::ArrowLocation arrow_location,
bool allow_bubble_offscreen,
const gfx::Size& contents_size,
gfx::Rect* contents_bounds,
gfx::Rect* window_bounds) {
if (base::i18n::IsRTL())
arrow_location = BubbleBorder::horizontal_mirror(arrow_location);
bubble_border_->set_arrow_location(arrow_location);
// Set the border.
set_border(bubble_border_);
// Give the contents a margin.
gfx::Size local_contents_size(contents_size);
local_contents_size.Enlarge(content_margins_.width(),
content_margins_.height());
// Try putting the arrow in its initial location, and calculating the bounds.
*window_bounds =
bubble_border_->GetBounds(position_relative_to, local_contents_size);
if (!allow_bubble_offscreen) {
gfx::Rect monitor_bounds = GetMonitorBounds(position_relative_to);
if (!monitor_bounds.IsEmpty()) {
// Try to resize vertically if this does not fit on the screen.
MirrorArrowIfOffScreen(true, // |vertical|.
position_relative_to, monitor_bounds,
local_contents_size, &arrow_location,
window_bounds);
// Then try to resize horizontally if it still does not fit on the screen.
MirrorArrowIfOffScreen(false, // |vertical|.
position_relative_to, monitor_bounds,
local_contents_size, &arrow_location,
window_bounds);
}
}
// Calculate the bounds of the contained contents (in window coordinates) by
// subtracting the border dimensions and margin amounts.
*contents_bounds = gfx::Rect(gfx::Point(), window_bounds->size());
gfx::Insets insets;
bubble_border_->GetInsets(&insets);
insets += content_margins_;
contents_bounds->Inset(insets);
}
gfx::Rect BorderContentsView::GetMonitorBounds(const gfx::Rect& rect) {
return gfx::Screen::GetMonitorWorkAreaNearestPoint(rect.CenterPoint());
}
void BorderContentsView::MirrorArrowIfOffScreen(
bool vertical,
const gfx::Rect& position_relative_to,
const gfx::Rect& monitor_bounds,
const gfx::Size& local_contents_size,
BubbleBorder::ArrowLocation* arrow_location,
gfx::Rect* window_bounds) {
// If the bounds don't fit, move the arrow to its mirrored position to see if
// it improves things.
gfx::Insets offscreen_insets;
if (ComputeOffScreenInsets(monitor_bounds, *window_bounds,
&offscreen_insets) &&
GetInsetsLength(offscreen_insets, vertical) > 0) {
BubbleBorder::ArrowLocation original_arrow_location =
*arrow_location;
*arrow_location =
vertical ? BubbleBorder::vertical_mirror(*arrow_location) :
BubbleBorder::horizontal_mirror(*arrow_location);
// Change the arrow and get the new bounds.
bubble_border_->set_arrow_location(*arrow_location);
*window_bounds = bubble_border_->GetBounds(position_relative_to,
local_contents_size);
gfx::Insets new_offscreen_insets;
// If there is more of the window offscreen, we'll keep the old arrow.
if (ComputeOffScreenInsets(monitor_bounds, *window_bounds,
&new_offscreen_insets) &&
GetInsetsLength(new_offscreen_insets, vertical) >=
GetInsetsLength(offscreen_insets, vertical)) {
*arrow_location = original_arrow_location;
bubble_border_->set_arrow_location(*arrow_location);
*window_bounds = bubble_border_->GetBounds(position_relative_to,
local_contents_size);
}
}
}
} // namespace views
// Copyright (c) 2011 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.
#ifndef UI_VIEWS_BUBBLE_BORDER_CONTENTS_VIEW_H_
#define UI_VIEWS_BUBBLE_BORDER_CONTENTS_VIEW_H_
#pragma once
#include "third_party/skia/include/core/SkColor.h"
#include "ui/views/bubble/bubble_border.h"
#include "ui/views/view.h"
namespace views {
// This is used to paint the border and background of the Bubble.
class VIEWS_EXPORT BorderContentsView : public View {
public:
BorderContentsView();
BorderContentsView(int top_margin,
int left_margin,
int bottom_margin,
int right_margin);
// Must be called before this object can be used.
void Init();
// Sets the background color.
void SetBackgroundColor(SkColor color);
// Sets the bubble alignment.
void SetAlignment(views::BubbleBorder::BubbleAlignment alignment);
// Given the size of the contents and the rect to point at, returns the bounds
// of both the border and the contents inside the bubble.
// |arrow_location| specifies the preferred location for the arrow
// anchor. If the bubble does not fit on the monitor and
// |allow_bubble_offscreen| is false, the arrow location may change so the
// bubble shows entirely.
virtual void SizeAndGetBounds(
const gfx::Rect& position_relative_to, // In screen coordinates
BubbleBorder::ArrowLocation arrow_location,
bool allow_bubble_offscreen,
const gfx::Size& contents_size,
gfx::Rect* contents_bounds, // Returned in window coordinates
gfx::Rect* window_bounds); // Returned in screen coordinates
// Sets content margins.
void set_content_margins(const gfx::Insets& margins) {
content_margins_ = margins;
}
// Accessor for |content_margins_|.
const gfx::Insets& content_margins() const {
return content_margins_;
}
protected:
virtual ~BorderContentsView();
// Returns the bounds for the monitor showing the specified |rect|.
virtual gfx::Rect GetMonitorBounds(const gfx::Rect& rect);
BubbleBorder* bubble_border() const { return bubble_border_; }
private:
// Changes |arrow_location| to its mirrored version, vertically if |vertical|
// is true, horizontally otherwise, if |window_bounds| don't fit in
// |monitor_bounds|.
void MirrorArrowIfOffScreen(
bool vertical,
const gfx::Rect& position_relative_to,
const gfx::Rect& monitor_bounds,
const gfx::Size& local_contents_size,
BubbleBorder::ArrowLocation* arrow_location,
gfx::Rect* window_bounds);
// The bubble border.
BubbleBorder* bubble_border_;
// Margins between the content and the inside of the border, in pixels.
gfx::Insets content_margins_;
DISALLOW_COPY_AND_ASSIGN(BorderContentsView);
};
} // namespace views
#endif // UI_VIEWS_BUBBLE_BORDER_CONTENTS_VIEW_H_
......@@ -88,8 +88,7 @@ gfx::Rect BubbleBorder::GetBounds(const gfx::Rect& position_relative_to,
gfx::Size border_size(contents_size);
gfx::Insets insets;
GetInsets(&insets);
border_size.Enlarge(insets.left() + insets.right(),
insets.top() + insets.bottom());
border_size.Enlarge(insets.width(), insets.height());
// Screen position depends on the arrow location.
// The arrow should overlap the target by some amount since there is space
......
......@@ -83,7 +83,6 @@ const SkColor BubbleDelegateView::kBackgroundColor = SK_ColorWHITE;
BubbleDelegateView::BubbleDelegateView()
: close_on_esc_(true),
close_on_deactivate_(true),
allow_bubble_offscreen_(false),
anchor_view_(NULL),
arrow_location_(BubbleBorder::TOP_LEFT),
color_(kBackgroundColor),
......@@ -99,7 +98,6 @@ BubbleDelegateView::BubbleDelegateView(
BubbleBorder::ArrowLocation arrow_location)
: close_on_esc_(true),
close_on_deactivate_(true),
allow_bubble_offscreen_(false),
anchor_view_(anchor_view),
arrow_location_(arrow_location),
color_(kBackgroundColor),
......@@ -146,10 +144,7 @@ View* BubbleDelegateView::GetContentsView() {
}
NonClientFrameView* BubbleDelegateView::CreateNonClientFrameView() {
return new BubbleFrameView(GetArrowLocation(),
GetPreferredSize(),
color(),
allow_bubble_offscreen());
return new BubbleFrameView(arrow_location(), color());
}
void BubbleDelegateView::OnWidgetActivationChanged(Widget* widget,
......@@ -161,28 +156,7 @@ void BubbleDelegateView::OnWidgetActivationChanged(Widget* widget,
}
gfx::Rect BubbleDelegateView::GetAnchorRect() {
if (!anchor_view())
return gfx::Rect();
BubbleBorder::ArrowLocation location = GetArrowLocation();
gfx::Point anchor;
// By default, pick the middle of |anchor_view_|'s edge opposite the arrow.
if (BubbleBorder::is_arrow_on_horizontal(location)) {
anchor.SetPoint(anchor_view()->width() / 2,
BubbleBorder::is_arrow_on_top(location) ? anchor_view()->height() : 0);
} else if (BubbleBorder::has_arrow(location)) {
anchor.SetPoint(
BubbleBorder::is_arrow_on_left(location) ? anchor_view()->width() : 0,
anchor_view_->height() / 2);
} else {
anchor = anchor_view()->bounds().CenterPoint();
}
View::ConvertPointToScreen(anchor_view(), &anchor);
return gfx::Rect(anchor, gfx::Size());
}
BubbleBorder::ArrowLocation BubbleDelegateView::GetArrowLocation() const {
return arrow_location_;
return anchor_view() ? anchor_view()->GetScreenBounds() : gfx::Rect();
}
void BubbleDelegateView::Show() {
......@@ -286,8 +260,8 @@ BubbleFrameView* BubbleDelegateView::GetBubbleFrameView() const {
gfx::Rect BubbleDelegateView::GetBubbleBounds() {
// The argument rect has its origin at the bubble's arrow anchor point;
// its size is the preferred size of the bubble's client view (this view).
return GetBubbleFrameView()->GetWindowBoundsForAnchorAndClientSize(
GetAnchorRect(), GetPreferredSize());
return GetBubbleFrameView()->GetUpdatedWindowBounds(GetAnchorRect(),
GetPreferredSize(), true /*try_mirroring_arrow*/);
}
#if defined(OS_WIN) && !defined(USE_AURA)
......
......@@ -56,14 +56,14 @@ class VIEWS_EXPORT BubbleDelegateView : public WidgetDelegateView,
close_on_deactivate_ = close_on_deactivate;
}
bool allow_bubble_offscreen() const { return allow_bubble_offscreen_; }
void set_allow_bubble_offscreen(bool allow_bubble_offscreen) {
allow_bubble_offscreen_ = allow_bubble_offscreen;
}
View* anchor_view() const { return anchor_view_; }
void set_anchor_view(View* anchor_view) { anchor_view_ = anchor_view; }
BubbleBorder::ArrowLocation arrow_location() const { return arrow_location_; }
void set_arrow_location(BubbleBorder::ArrowLocation arrow_location) {
arrow_location_ = arrow_location;
}
SkColor color() const { return color_; }
void set_color(SkColor color) { color_ = color; }
......@@ -75,9 +75,6 @@ class VIEWS_EXPORT BubbleDelegateView : public WidgetDelegateView,
// Get the arrow's anchor rect in screen space.
virtual gfx::Rect GetAnchorRect();
// Get the arrow's location on the bubble.
virtual BubbleBorder::ArrowLocation GetArrowLocation() const;
// Show the bubble's widget (and |border_widget_| on Windows).
void Show();
......@@ -110,7 +107,7 @@ class VIEWS_EXPORT BubbleDelegateView : public WidgetDelegateView,
BubbleFrameView* GetBubbleFrameView() const;
private:
FRIEND_TEST_ALL_PREFIXES(BubbleFrameViewBasicTest, NonClientHitTest);
FRIEND_TEST_ALL_PREFIXES(BubbleFrameViewTest, NonClientHitTest);
FRIEND_TEST_ALL_PREFIXES(BubbleDelegateTest, CreateDelegate);
// Get bubble bounds from the anchor point and client view's preferred size.
......@@ -128,10 +125,6 @@ class VIEWS_EXPORT BubbleDelegateView : public WidgetDelegateView,
bool close_on_esc_;
bool close_on_deactivate_;
// Whether the bubble is allowed to be displayed offscreen, or if auto
// re-positioning should be performed.
bool allow_bubble_offscreen_;
// The view hosting this bubble; the arrow is anchored to this view.
View* anchor_view_;
......
......@@ -22,7 +22,7 @@ TEST_F(BubbleDelegateTest, CreateDelegate) {
BubbleBorder* border =
bubble_delegate->GetBubbleFrameView()->bubble_border();
EXPECT_EQ(bubble_delegate->GetArrowLocation(), border->arrow_location());
EXPECT_EQ(bubble_delegate->arrow_location(), border->arrow_location());
EXPECT_EQ(bubble_delegate->color(), border->background_color());
bubble_widget->CloseNow();
......
......@@ -6,31 +6,53 @@
#include <algorithm>
#include "ui/views/bubble/border_contents_view.h"
#include "ui/gfx/screen.h"
#include "ui/views/bubble/bubble_border.h"
#include "ui/views/layout/fill_layout.h"
#include "ui/views/widget/widget.h"
#include "ui/views/window/client_view.h"
static const int kMargin = 6;
namespace {
// Get the |vertical| or horizontal screen overflow of the |window_bounds|.
int GetOffScreenLength(const gfx::Rect& monitor_bounds,
const gfx::Rect& window_bounds,
bool vertical) {
if (monitor_bounds.IsEmpty() || monitor_bounds.Contains(window_bounds))
return 0;
// window_bounds
// +-------------------------------+
// | top |
// | +----------------+ |
// | left | monitor_bounds | right |
// | +----------------+ |
// | bottom |
// +-------------------------------+
if (vertical)
return std::max(0, monitor_bounds.y() - window_bounds.y()) +
std::max(0, window_bounds.bottom() - monitor_bounds.bottom());
return std::max(0, monitor_bounds.x() - window_bounds.x()) +
std::max(0, window_bounds.right() - monitor_bounds.right());
}
} // namespace
namespace views {
BubbleFrameView::BubbleFrameView(BubbleBorder::ArrowLocation location,
const gfx::Size& client_size,
SkColor color,
bool allow_bubble_offscreen)
: border_contents_(new BorderContentsView()),
location_(location),
allow_bubble_offscreen_(allow_bubble_offscreen) {
border_contents_->Init();
bubble_border()->set_arrow_location(location_);
BubbleFrameView::BubbleFrameView(BubbleBorder::ArrowLocation arrow_location,
SkColor color)
: bubble_border_(NULL),
content_margins_(kMargin, kMargin, kMargin, kMargin) {
if (base::i18n::IsRTL())
arrow_location = BubbleBorder::horizontal_mirror(arrow_location);
// TODO(alicet): Expose the shadow option in BorderContentsView when we make
// the fullscreen exit bubble use the new bubble code.
bubble_border_ = new BubbleBorder(arrow_location, BubbleBorder::NO_SHADOW);
set_border(bubble_border_);
set_background(new BubbleBackground(bubble_border_));
bubble_border()->set_background_color(color);
SetLayoutManager(new views::FillLayout());
AddChildView(border_contents_);
gfx::Rect windows_bounds =
GetWindowBoundsForAnchorAndClientSize(gfx::Rect(), client_size);
border_contents_->SetBoundsRect(
gfx::Rect(gfx::Point(), windows_bounds.size()));
SetBoundsRect(windows_bounds);
}
BubbleFrameView::~BubbleFrameView() {}
......@@ -38,33 +60,16 @@ BubbleFrameView::~BubbleFrameView() {}
gfx::Rect BubbleFrameView::GetBoundsForClientView() const {
gfx::Insets margin;
bubble_border()->GetInsets(&margin);
margin += border_contents_->content_margins();
return gfx::Rect(margin.left(),
margin.top(),
margin += content_margins();
return gfx::Rect(margin.left(), margin.top(),
std::max(width() - margin.width(), 0),
std::max(height() - margin.height(), 0));
}
gfx::Rect BubbleFrameView::GetWindowBoundsForClientBounds(
const gfx::Rect& client_bounds) const {
// The |client_bounds| origin is the bubble arrow anchor point.
gfx::Rect anchor(client_bounds.origin(), gfx::Size());
// The |client_bounds| size is the bubble client view size.
return GetWindowBoundsForAnchorAndClientSize(anchor, client_bounds.size());
}
gfx::Rect BubbleFrameView::GetWindowBoundsForAnchorAndClientSize(
const gfx::Rect& anchor,
const gfx::Size& client_size) const {
gfx::Rect content_bounds;
gfx::Rect window_bounds;
border_contents_->SizeAndGetBounds(anchor,
location_,
allow_bubble_offscreen_,
client_size,
&content_bounds,
&window_bounds);
return window_bounds;
return const_cast<BubbleFrameView*>(this)->GetUpdatedWindowBounds(
gfx::Rect(), client_bounds.size(), false);
}
int BubbleFrameView::NonClientHitTest(const gfx::Point& point) {
......@@ -72,13 +77,51 @@ int BubbleFrameView::NonClientHitTest(const gfx::Point& point) {
}
gfx::Size BubbleFrameView::GetPreferredSize() {
Widget* widget = GetWidget();
gfx::Rect rect(gfx::Point(), widget->client_view()->GetPreferredSize());
return widget->non_client_view()->GetWindowBoundsForClientBounds(rect).size();
gfx::Size client_size(GetWidget()->client_view()->GetPreferredSize());
return GetUpdatedWindowBounds(gfx::Rect(), client_size, false).size();
}
gfx::Rect BubbleFrameView::GetUpdatedWindowBounds(const gfx::Rect& anchor_rect,
gfx::Size client_size,
bool try_mirroring_arrow) {
// Give the contents a margin.
client_size.Enlarge(content_margins_.width(), content_margins_.height());
if (try_mirroring_arrow) {
// Try to mirror the anchoring if the bubble does not fit on the screen.
MirrorArrowIfOffScreen(true, anchor_rect, client_size);
MirrorArrowIfOffScreen(false, anchor_rect, client_size);
}
// Calculate the bounds with the arrow in its updated location.
return bubble_border_->GetBounds(anchor_rect, client_size);
}
gfx::Rect BubbleFrameView::GetMonitorBounds(const gfx::Rect& rect) {
return gfx::Screen::GetMonitorWorkAreaNearestPoint(rect.CenterPoint());
}
BubbleBorder* BubbleFrameView::bubble_border() const {
return static_cast<BubbleBorder*>(border_contents_->border());
void BubbleFrameView::MirrorArrowIfOffScreen(
bool vertical,
const gfx::Rect& anchor_rect,
const gfx::Size& client_size) {
// Check if the bounds don't fit on screen.
gfx::Rect monitor_rect(GetMonitorBounds(anchor_rect));
gfx::Rect window_bounds(bubble_border_->GetBounds(anchor_rect, client_size));
if (GetOffScreenLength(monitor_rect, window_bounds, vertical) > 0) {
BubbleBorder::ArrowLocation arrow = bubble_border()->arrow_location();
// Mirror the arrow and get the new bounds.
bubble_border_->set_arrow_location(
vertical ? BubbleBorder::vertical_mirror(arrow) :
BubbleBorder::horizontal_mirror(arrow));
gfx::Rect mirror_bounds =
bubble_border_->GetBounds(anchor_rect, client_size);
// Restore the original arrow if mirroring doesn't show more of the bubble.
if (GetOffScreenLength(monitor_rect, mirror_bounds, vertical) >=
GetOffScreenLength(monitor_rect, window_bounds, vertical)) {
bubble_border_->set_arrow_location(arrow);
}
}
}
} // namespace views
......@@ -7,6 +7,7 @@
#pragma once
#include "base/gtest_prod_util.h"
#include "third_party/skia/include/core/SkColor.h"
#include "ui/views/bubble/bubble_border.h"
#include "ui/views/window/non_client_view.h"
......@@ -19,10 +20,7 @@ class BorderContentsView;
////////////////////////////////////////////////////////////////////////////////
class VIEWS_EXPORT BubbleFrameView : public NonClientFrameView {
public:
BubbleFrameView(BubbleBorder::ArrowLocation location,
const gfx::Size& client_size,
SkColor color,
bool allow_bubble_offscreen);
BubbleFrameView(BubbleBorder::ArrowLocation arrow_location, SkColor color);
virtual ~BubbleFrameView();
// NonClientFrameView overrides:
......@@ -38,19 +36,36 @@ class VIEWS_EXPORT BubbleFrameView : public NonClientFrameView {
// View overrides:
virtual gfx::Size GetPreferredSize() OVERRIDE;
// Accessor for bubble border inside border contents.
BubbleBorder* bubble_border() const;
BubbleBorder* bubble_border() const { return bubble_border_; }
gfx::Rect GetWindowBoundsForAnchorAndClientSize(
const gfx::Rect& anchor,
const gfx::Size& client_size) const;
gfx::Insets content_margins() const { return content_margins_; }
// Given the size of the contents and the rect to point at, returns the bounds
// of the bubble window. The bubble's arrow location may change if the bubble
// does not fit on the monitor and |try_mirroring_arrow| is true.
gfx::Rect GetUpdatedWindowBounds(const gfx::Rect& anchor_rect,
gfx::Size client_size,
bool try_mirroring_arrow);
protected:
// Returns the bounds for the monitor showing the specified |rect|.
// This function is virtual to support testing environments.
virtual gfx::Rect GetMonitorBounds(const gfx::Rect& rect);
private:
FRIEND_TEST_ALL_PREFIXES(BubbleFrameViewBasicTest, GetBoundsForClientView);
FRIEND_TEST_ALL_PREFIXES(BubbleFrameViewTest, GetBoundsForClientView);
// Mirrors the bubble's arrow location on the |vertical| or horizontal axis,
// if the generated window bounds don't fit in the monitor bounds.
void MirrorArrowIfOffScreen(bool vertical,
const gfx::Rect& anchor_rect,
const gfx::Size& client_size);
// The bubble border.
BubbleBorder* bubble_border_;
BorderContentsView* border_contents_;
BubbleBorder::ArrowLocation location_;
bool allow_bubble_offscreen_;
// Margins between the content and the inside of the border, in pixels.
gfx::Insets content_margins_;
DISALLOW_COPY_AND_ASSIGN(BubbleFrameView);
};
......
......@@ -3,7 +3,6 @@
// found in the LICENSE file.
#include "ui/base/hit_test.h"
#include "ui/views/bubble/border_contents_view.h"
#include "ui/views/bubble/bubble_border.h"
#include "ui/views/bubble/bubble_delegate.h"
#include "ui/views/bubble/bubble_frame_view.h"
......@@ -12,34 +11,18 @@
namespace views {
typedef ViewsTestBase BubbleFrameViewBasicTest;
typedef ViewsTestBase BubbleFrameViewTest;
const BubbleBorder::ArrowLocation kArrow = BubbleBorder::TOP_LEFT;
const gfx::Rect kRect(10, 10, 200, 200);
const SkColor kBackgroundColor = SK_ColorRED;
const bool kAllowBubbleOffscreen = true;
TEST_F(BubbleFrameViewBasicTest, GetBoundsForClientView) {
BubbleFrameView frame(kArrow, kRect.size(), kBackgroundColor,
kAllowBubbleOffscreen);
EXPECT_EQ(frame.GetWindowBoundsForClientBounds(kRect).size(), frame.size());
EXPECT_EQ(kArrow, frame.bubble_border()->arrow_location());
EXPECT_EQ(kBackgroundColor, frame.bubble_border()->background_color());
int margin_x = frame.border_contents_->content_margins().left();
int margin_y = frame.border_contents_->content_margins().top();
gfx::Insets insets;
frame.bubble_border()->GetInsets(&insets);
EXPECT_EQ(insets.left() + margin_x, frame.GetBoundsForClientView().x());
EXPECT_EQ(insets.top() + margin_y, frame.GetBoundsForClientView().y());
}
namespace {
class SizedBubbleDelegateView : public BubbleDelegateView {
public:
SizedBubbleDelegateView() {}
virtual ~SizedBubbleDelegateView() {}
SizedBubbleDelegateView();
virtual ~SizedBubbleDelegateView();
// View overrides:
virtual gfx::Size GetPreferredSize() OVERRIDE;
......@@ -48,11 +31,53 @@ class SizedBubbleDelegateView : public BubbleDelegateView {
DISALLOW_COPY_AND_ASSIGN(SizedBubbleDelegateView);
};
SizedBubbleDelegateView::SizedBubbleDelegateView() {}
SizedBubbleDelegateView::~SizedBubbleDelegateView() {}
gfx::Size SizedBubbleDelegateView::GetPreferredSize() { return kRect.size(); }
class TestBubbleFrameView : public BubbleFrameView {
public:
TestBubbleFrameView(const gfx::Rect& bounds);
virtual ~TestBubbleFrameView();
protected:
virtual gfx::Rect GetMonitorBounds(const gfx::Rect& rect) OVERRIDE;
private:
gfx::Rect monitor_bounds_;
DISALLOW_COPY_AND_ASSIGN(TestBubbleFrameView);
};
TestBubbleFrameView::TestBubbleFrameView(const gfx::Rect& bounds)
: BubbleFrameView(kArrow, kBackgroundColor),
monitor_bounds_(bounds) {
}
TestBubbleFrameView::~TestBubbleFrameView() {}
gfx::Rect TestBubbleFrameView::GetMonitorBounds(const gfx::Rect& rect) {
return monitor_bounds_;
}
} // namespace
TEST_F(BubbleFrameViewBasicTest, NonClientHitTest) {
TEST_F(BubbleFrameViewTest, GetBoundsForClientView) {
BubbleFrameView frame(kArrow, kBackgroundColor);
EXPECT_EQ(kArrow, frame.bubble_border()->arrow_location());
EXPECT_EQ(kBackgroundColor, 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(&insets);
EXPECT_EQ(insets.left() + margin_x, frame.GetBoundsForClientView().x());
EXPECT_EQ(insets.top() + margin_y, frame.GetBoundsForClientView().y());
}
TEST_F(BubbleFrameViewTest, NonClientHitTest) {
BubbleDelegateView* delegate = new SizedBubbleDelegateView();
Widget* widget(BubbleDelegateView::CreateBubble(delegate));
delegate->Show();
......@@ -65,4 +90,143 @@ TEST_F(BubbleFrameViewBasicTest, NonClientHitTest) {
RunPendingMessages();
}
// Tests that the arrow is mirrored as needed to better fit the screen.
TEST_F(BubbleFrameViewTest, GetUpdatedWindowBounds) {
TestBubbleFrameView frame(gfx::Rect(0, 0, 1000, 1000));
gfx::Rect window_bounds;
gfx::Insets insets;
frame.bubble_border()->GetInsets(&insets);
int xposition = 95 - insets.width();
// Test that the info bubble displays normally when it fits.
frame.bubble_border()->set_arrow_location(BubbleBorder::TOP_LEFT);
window_bounds = frame.GetUpdatedWindowBounds(
gfx::Rect(100, 100, 50, 50), // |anchor_rect|
gfx::Size(500, 500), // |client_size|
true); // |try_mirroring_arrow|
EXPECT_EQ(BubbleBorder::TOP_LEFT, frame.bubble_border()->arrow_location());
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_location(BubbleBorder::TOP_RIGHT);
window_bounds = frame.GetUpdatedWindowBounds(
gfx::Rect(100, 100, 50, 50), // |anchor_rect|
gfx::Size(500, 500), // |client_size|
true); // |try_mirroring_arrow|
EXPECT_EQ(BubbleBorder::TOP_LEFT, frame.bubble_border()->arrow_location());
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_location(BubbleBorder::BOTTOM_RIGHT);
window_bounds = frame.GetUpdatedWindowBounds(
gfx::Rect(100, 100, 50, 50), // |anchor_rect|
gfx::Size(500, 500), // |client_size|
true); // |try_mirroring_arrow|
EXPECT_EQ(BubbleBorder::TOP_LEFT, frame.bubble_border()->arrow_location());
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_location(BubbleBorder::BOTTOM_LEFT);
window_bounds = frame.GetUpdatedWindowBounds(
gfx::Rect(100, 100, 50, 50), // |anchor_rect|
gfx::Size(500, 500), // |client_size|
true); // |try_mirroring_arrow|
EXPECT_EQ(BubbleBorder::TOP_LEFT, frame.bubble_border()->arrow_location());
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_location(BubbleBorder::BOTTOM_LEFT);
window_bounds = frame.GetUpdatedWindowBounds(
gfx::Rect(900, 100, 50, 50), // |anchor_rect|
gfx::Size(500, 500), // |client_size|
true); // |try_mirroring_arrow|
EXPECT_EQ(BubbleBorder::TOP_RIGHT, frame.bubble_border()->arrow_location());
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_location(BubbleBorder::TOP_LEFT);
window_bounds = frame.GetUpdatedWindowBounds(
gfx::Rect(900, 100, 50, 50), // |anchor_rect|
gfx::Size(500, 500), // |client_size|
true); // |try_mirroring_arrow|
EXPECT_EQ(BubbleBorder::TOP_RIGHT, frame.bubble_border()->arrow_location());
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_location(BubbleBorder::TOP_LEFT);
window_bounds = frame.GetUpdatedWindowBounds(
gfx::Rect(900, 900, 50, 50), // |anchor_rect|
gfx::Size(500, 500), // |client_size|
true); // |try_mirroring_arrow|
EXPECT_EQ(BubbleBorder::BOTTOM_RIGHT,
frame.bubble_border()->arrow_location());
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_location(BubbleBorder::TOP_LEFT);
window_bounds = frame.GetUpdatedWindowBounds(
gfx::Rect(100, 900, 50, 50), // |anchor_rect|
gfx::Size(500, 500), // |client_size|
true); // |try_mirroring_arrow|
EXPECT_EQ(BubbleBorder::BOTTOM_LEFT, frame.bubble_border()->arrow_location());
// 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_location(BubbleBorder::TOP_RIGHT);
window_bounds = frame.GetUpdatedWindowBounds(
gfx::Rect(100, 900, 50, 50), // |anchor_rect|
gfx::Size(500, 500), // |client_size|
true); // |try_mirroring_arrow|
EXPECT_EQ(BubbleBorder::BOTTOM_LEFT, frame.bubble_border()->arrow_location());
// 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(gfx::Rect(0, 0, 1000, 1000));
frame.bubble_border()->set_arrow_location(BubbleBorder::TOP_LEFT);
gfx::Rect window_bounds = frame.GetUpdatedWindowBounds(
gfx::Rect(400, 100, 50, 50), // |anchor_rect|
gfx::Size(500, 700), // |client_size|
true); // |try_mirroring_arrow|
EXPECT_EQ(BubbleBorder::TOP_LEFT, frame.bubble_border()->arrow_location());
}
// Test that the arrow will not be mirrored when |try_mirroring_arrow| is false.
TEST_F(BubbleFrameViewTest, GetUpdatedWindowBoundsDontTryMirror) {
TestBubbleFrameView frame(gfx::Rect(0, 0, 1000, 1000));
frame.bubble_border()->set_arrow_location(BubbleBorder::TOP_RIGHT);
gfx::Rect window_bounds = frame.GetUpdatedWindowBounds(
gfx::Rect(100, 900, 50, 50), // |anchor_rect|
gfx::Size(500, 500), // |client_size|
false); // |try_mirroring_arrow|
EXPECT_EQ(BubbleBorder::TOP_RIGHT, frame.bubble_border()->arrow_location());
// 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.
}
} // namespace views
......@@ -55,8 +55,6 @@
'background.h',
'border.cc',
'border.h',
'bubble/border_contents_view.cc',
'bubble/border_contents_view.h',
'bubble/bubble_border.cc',
'bubble/bubble_border.h',
'bubble/bubble_delegate.cc',
......@@ -508,7 +506,6 @@
'sources': [
'accessible_pane_view_unittest.cc',
'animation/bounds_animator_unittest.cc',
'bubble/border_contents_unittest.cc',
'bubble/bubble_delegate_unittest.cc',
'bubble/bubble_frame_view_unittest.cc',
'controls/combobox/native_combobox_views_unittest.cc',
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment