diff options
author | alicet@chromium.org <alicet@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-11-03 17:32:48 +0000 |
---|---|---|
committer | alicet@chromium.org <alicet@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-11-03 17:32:48 +0000 |
commit | 2a7731e1e254d62b43d4f52f337cdd13505b0090 (patch) | |
tree | c1591183e09e1088724eb3909f7437a3398dbebd /views | |
parent | 8f23e2b29fbae4af1b841b1519609f9883732666 (diff) | |
download | chromium_src-2a7731e1e254d62b43d4f52f337cdd13505b0090.zip chromium_src-2a7731e1e254d62b43d4f52f337cdd13505b0090.tar.gz chromium_src-2a7731e1e254d62b43d4f52f337cdd13505b0090.tar.bz2 |
Move bubble contents to under views/bubbles.
Separate out code that depends on chrome/browser code to stay in the existing directory.
BUG=None
TEST=None
Review URL: http://codereview.chromium.org/8417021
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@108493 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'views')
-rw-r--r-- | views/bubble/border_contents_view.cc | 177 | ||||
-rw-r--r-- | views/bubble/border_contents_view.h | 85 | ||||
-rw-r--r-- | views/bubble/bubble_delegate.cc | 5 | ||||
-rw-r--r-- | views/bubble/bubble_delegate.h | 9 | ||||
-rw-r--r-- | views/bubble/bubble_delegate_unittest.cc | 4 | ||||
-rw-r--r-- | views/bubble/bubble_frame_view.cc | 54 | ||||
-rw-r--r-- | views/bubble/bubble_frame_view.h | 16 | ||||
-rw-r--r-- | views/bubble/bubble_frame_view_unittest.cc | 36 | ||||
-rw-r--r-- | views/examples/bubble_example.cc | 1 | ||||
-rw-r--r-- | views/views.gyp | 2 |
10 files changed, 350 insertions, 39 deletions
diff --git a/views/bubble/border_contents_view.cc b/views/bubble/border_contents_view.cc new file mode 100644 index 0000000..1582751 --- /dev/null +++ b/views/bubble/border_contents_view.cc @@ -0,0 +1,177 @@ +// 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 "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_); + + bubble_border_ = new BubbleBorder(arrow_location); + set_border(bubble_border_); + set_background(new BubbleBackground(bubble_border_)); +} + +void BorderContentsView::SetBackgroundColor(SkColor color) { + bubble_border_->set_background_color(color); +} + +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 diff --git a/views/bubble/border_contents_view.h b/views/bubble/border_contents_view.h new file mode 100644 index 0000000..3c5a162 --- /dev/null +++ b/views/bubble/border_contents_view.h @@ -0,0 +1,85 @@ +// 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 VIEWS_BUBBLE_BORDER_CONTENTS_VIEW_H_ +#define VIEWS_BUBBLE_BORDER_CONTENTS_VIEW_H_ +#pragma once + +#include "views/bubble/bubble_border.h" +#include "third_party/skia/include/core/SkColor.h" +#include "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); + + // 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 // VIEWS_BUBBLE_BORDER_CONTENTS_VIEW_H_ diff --git a/views/bubble/bubble_delegate.cc b/views/bubble/bubble_delegate.cc index 1b40c29..0fb15cc 100644 --- a/views/bubble/bubble_delegate.cc +++ b/views/bubble/bubble_delegate.cc @@ -75,6 +75,7 @@ Widget* CreateBorderWidget(BubbleDelegateView* bubble, Widget* parent) { BubbleDelegateView::BubbleDelegateView() : close_on_esc_(true), + allow_bubble_offscreen_(true), arrow_location_(BubbleBorder::TOP_LEFT), color_(SK_ColorWHITE), border_widget_(NULL) { @@ -87,6 +88,7 @@ BubbleDelegateView::BubbleDelegateView( BubbleBorder::ArrowLocation arrow_location, const SkColor& color) : close_on_esc_(true), + allow_bubble_offscreen_(true), anchor_point_(anchor_point), arrow_location_(arrow_location), color_(color), @@ -129,7 +131,8 @@ View* BubbleDelegateView::GetContentsView() { NonClientFrameView* BubbleDelegateView::CreateNonClientFrameView() { return new BubbleFrameView(GetArrowLocation(), GetPreferredSize(), - GetColor()); + GetColor(), + allow_bubble_offscreen_); } gfx::Point BubbleDelegateView::GetAnchorPoint() { diff --git a/views/bubble/bubble_delegate.h b/views/bubble/bubble_delegate.h index a5f6ab9..28b1691 100644 --- a/views/bubble/bubble_delegate.h +++ b/views/bubble/bubble_delegate.h @@ -44,6 +44,11 @@ class VIEWS_EXPORT BubbleDelegateView : public WidgetDelegateView, bool close_on_esc() const { return close_on_esc_; } void set_close_on_esc(bool close_on_esc) { close_on_esc_ = close_on_esc; } + bool allow_bubble_offscreen() const { return allow_bubble_offscreen_; } + void set_allow_bubble_offscreen(bool allow_bubble_offscreen) { + allow_bubble_offscreen_ = allow_bubble_offscreen; + } + // Get the arrow's anchor point in screen space. virtual gfx::Point GetAnchorPoint(); @@ -98,6 +103,10 @@ class VIEWS_EXPORT BubbleDelegateView : public WidgetDelegateView, // Should this bubble close on the escape key? bool close_on_esc_; + // Whether the bubble is allowed to be displayed offscreen, or if auto + // re-positioning should be performed. + bool allow_bubble_offscreen_; + // The screen point where this bubble's arrow is anchored. gfx::Point anchor_point_; diff --git a/views/bubble/bubble_delegate_unittest.cc b/views/bubble/bubble_delegate_unittest.cc index ce561f5d..6b13386 100644 --- a/views/bubble/bubble_delegate_unittest.cc +++ b/views/bubble/bubble_delegate_unittest.cc @@ -18,8 +18,8 @@ TEST_F(BubbleDelegateTest, CreateDelegate) { EXPECT_EQ(bubble_delegate, bubble_widget->widget_delegate()); EXPECT_EQ(bubble_widget, bubble_delegate->GetWidget()); - BubbleBorder* border = static_cast<BubbleBorder*>( - bubble_delegate->GetBubbleFrameView()->border()); + BubbleBorder* border = + bubble_delegate->GetBubbleFrameView()->bubble_border(); EXPECT_EQ(bubble_delegate->GetArrowLocation(), border->arrow_location()); EXPECT_EQ(bubble_delegate->GetColor(), border->background_color()); diff --git a/views/bubble/bubble_frame_view.cc b/views/bubble/bubble_frame_view.cc index aa6ce9c..5671dee 100644 --- a/views/bubble/bubble_frame_view.cc +++ b/views/bubble/bubble_frame_view.cc @@ -4,8 +4,10 @@ #include "views/bubble/bubble_frame_view.h" -#include "ui/gfx/canvas.h" +#include <algorithm> + #include "views/bubble/bubble_border.h" +#include "views/bubble/border_contents_view.h" #include "views/widget/widget.h" #include "views/window/client_view.h" @@ -13,22 +15,32 @@ namespace views { BubbleFrameView::BubbleFrameView(BubbleBorder::ArrowLocation location, const gfx::Size& client_size, - SkColor color) { - BubbleBorder* bubble_border = new BubbleBorder(location); - bubble_border->set_background_color(color); - set_border(bubble_border); - set_background(new BubbleBackground(bubble_border)); - // Calculate the frame size from the 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_); + bubble_border()->set_background_color(color); + AddChildView(border_contents_); gfx::Rect bounds(gfx::Point(), client_size); - SetBoundsRect(GetWindowBoundsForClientBounds(bounds)); + gfx::Rect windows_bounds = GetWindowBoundsForClientBounds(bounds); + border_contents_->SetBoundsRect( + gfx::Rect(gfx::Point(), windows_bounds.size())); + SetBoundsRect(windows_bounds); } BubbleFrameView::~BubbleFrameView() {} gfx::Rect BubbleFrameView::GetBoundsForClientView() const { - gfx::Rect client_bounds(gfx::Point(), size()); - client_bounds.Inset(GetInsets()); - return client_bounds; + gfx::Insets margin; + bubble_border()->GetInsets(&margin); + margin += border_contents_->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( @@ -36,13 +48,15 @@ gfx::Rect BubbleFrameView::GetWindowBoundsForClientBounds( // The |client_bounds| origin is the bubble arrow anchor point. gfx::Rect position_relative_to(client_bounds.origin(), gfx::Size()); // The |client_bounds| size is the bubble client view size. - return static_cast<const BubbleBorder*>(border())->GetBounds( - position_relative_to, client_bounds.size()); -} - -void BubbleFrameView::OnPaint(gfx::Canvas* canvas) { - border()->Paint(*this, canvas); - background()->Paint(canvas, this); + gfx::Rect content_bounds; + gfx::Rect window_bounds; + border_contents_->SizeAndGetBounds(position_relative_to, + location_, + allow_bubble_offscreen_, + client_bounds.size(), + &content_bounds, + &window_bounds); + return window_bounds; } int BubbleFrameView::NonClientHitTest(const gfx::Point& point) { @@ -55,4 +69,8 @@ gfx::Size BubbleFrameView::GetPreferredSize() { return widget->non_client_view()->GetWindowBoundsForClientBounds(rect).size(); } +BubbleBorder* BubbleFrameView::bubble_border() const { + return static_cast<BubbleBorder*>(border_contents_->border()); +} + } // namespace views diff --git a/views/bubble/bubble_frame_view.h b/views/bubble/bubble_frame_view.h index d7ccd9f..597afe3 100644 --- a/views/bubble/bubble_frame_view.h +++ b/views/bubble/bubble_frame_view.h @@ -6,11 +6,14 @@ #define VIEWS_BUBBLE_BUBBLE_FRAME_VIEW_H_ #pragma once +#include "base/gtest_prod_util.h" #include "views/bubble/bubble_border.h" #include "views/window/non_client_view.h" namespace views { +class BorderContentsView; + // BubbleFrameView to render BubbleBorder. // //////////////////////////////////////////////////////////////////////////////// @@ -18,7 +21,8 @@ class VIEWS_EXPORT BubbleFrameView : public NonClientFrameView { public: BubbleFrameView(BubbleBorder::ArrowLocation location, const gfx::Size& client_size, - SkColor color); + SkColor color, + bool allow_bubble_offscreen); virtual ~BubbleFrameView(); // NonClientFrameView overrides: @@ -33,10 +37,18 @@ class VIEWS_EXPORT BubbleFrameView : public NonClientFrameView { virtual void UpdateWindowIcon() OVERRIDE {} // View overrides: - virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE; virtual gfx::Size GetPreferredSize() OVERRIDE; + // Accessor for bubble border inside border contents. + BubbleBorder* bubble_border() const; + private: + FRIEND_TEST_ALL_PREFIXES(BubbleFrameViewBasicTest, GetBoundsForClientView); + + BorderContentsView* border_contents_; + BubbleBorder::ArrowLocation location_; + bool allow_bubble_offscreen_; + DISALLOW_COPY_AND_ASSIGN(BubbleFrameView); }; diff --git a/views/bubble/bubble_frame_view_unittest.cc b/views/bubble/bubble_frame_view_unittest.cc index bbdd203..3981e32 100644 --- a/views/bubble/bubble_frame_view_unittest.cc +++ b/views/bubble/bubble_frame_view_unittest.cc @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "views/bubble/bubble_border.h" +#include "views/bubble/border_contents_view.h" #include "views/bubble/bubble_delegate.h" #include "views/bubble/bubble_frame_view.h" #include "views/test/views_test_base.h" @@ -12,35 +14,37 @@ namespace views { -namespace { - typedef ViewsTestBase BubbleFrameViewBasicTest; const BubbleBorder::ArrowLocation kArrow = BubbleBorder::LEFT_BOTTOM; 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); + BubbleFrameView frame(kArrow, kRect.size(), kBackgroundColor, + kAllowBubbleOffscreen); EXPECT_EQ(frame.GetWindowBoundsForClientBounds(kRect).size(), frame.size()); - BubbleBorder* bubble_border = static_cast<BubbleBorder*>(frame.border()); - EXPECT_EQ(kArrow, bubble_border->arrow_location()); - EXPECT_EQ(kBackgroundColor, bubble_border->background_color()); + EXPECT_EQ(kArrow, frame.bubble_border()->arrow_location()); + EXPECT_EQ(kBackgroundColor, frame.bubble_border()->background_color()); - gfx::Insets expected_insets(frame.GetInsets()); - EXPECT_EQ(expected_insets.left(), frame.GetBoundsForClientView().x()); - EXPECT_EQ(expected_insets.top(), frame.GetBoundsForClientView().y()); + 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 +namespace { class SizedBubbleDelegateView : public BubbleDelegateView { public: - SizedBubbleDelegateView() {} - virtual ~SizedBubbleDelegateView() {} + SizedBubbleDelegateView() {} + virtual ~SizedBubbleDelegateView() {} - // View overrides: - virtual gfx::Size GetPreferredSize() OVERRIDE; + // View overrides: + virtual gfx::Size GetPreferredSize() OVERRIDE; private: DISALLOW_COPY_AND_ASSIGN(SizedBubbleDelegateView); @@ -48,8 +52,10 @@ class SizedBubbleDelegateView : public BubbleDelegateView { gfx::Size SizedBubbleDelegateView::GetPreferredSize() { return kRect.size(); } +} // namespace + TEST_F(BubbleFrameViewBasicTest, NonClientHitTest) { - SizedBubbleDelegateView* delegate = new SizedBubbleDelegateView(); + BubbleDelegateView* delegate = new SizedBubbleDelegateView(); scoped_ptr<Widget> widget(BubbleDelegateView::CreateBubble(delegate, NULL)); delegate->Show(); gfx::Point kPtInBound(100, 100); diff --git a/views/examples/bubble_example.cc b/views/examples/bubble_example.cc index b0d5ec4..d7011a1 100644 --- a/views/examples/bubble_example.cc +++ b/views/examples/bubble_example.cc @@ -44,7 +44,6 @@ class ExampleBubbleDelegateView : public views::BubbleDelegateView { virtual void Init() OVERRIDE { SetLayoutManager(new views::FillLayout()); views::Label* label = new views::Label(label_); - label->set_border(views::Border::CreateSolidBorder(10, GetColor())); AddChildView(label); } diff --git a/views/views.gyp b/views/views.gyp index b8082a4..7360bb3 100644 --- a/views/views.gyp +++ b/views/views.gyp @@ -63,6 +63,8 @@ 'border.h', 'bubble/bubble_border.cc', 'bubble/bubble_border.h', + 'bubble/border_contents_view.cc', + 'bubble/border_contents_view.h', 'bubble/bubble_delegate.cc', 'bubble/bubble_delegate.h', 'bubble/bubble_frame_view.cc', |