diff options
author | varunjain@chromium.org <varunjain@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-08-04 00:39:58 +0000 |
---|---|---|
committer | varunjain@chromium.org <varunjain@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-08-04 00:39:58 +0000 |
commit | 62fffee065933a9be261ef76ccc4450f0e400f31 (patch) | |
tree | 42a22f3cc8000ad62dd8bcc0b9a5ae97b4e74319 /ui/views/bubble | |
parent | 92daaa430772d8affc2797a3670bae088ff826c8 (diff) | |
download | chromium_src-62fffee065933a9be261ef76ccc4450f0e400f31.zip chromium_src-62fffee065933a9be261ef76ccc4450f0e400f31.tar.gz chromium_src-62fffee065933a9be261ef76ccc4450f0e400f31.tar.bz2 |
aura: Fix launcher tooltips:
1. Refactor out app_list_bubble_border so that it can be used for launcher
tooltip border.
2. UI polish: increase padding, make tooltip single line, reduce maximum
tooltip width.
BUG=133292
TEST=manual
Review URL: https://chromiumcodereview.appspot.com/10834140
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@149987 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ui/views/bubble')
-rw-r--r-- | ui/views/bubble/bubble_border.cc | 4 | ||||
-rw-r--r-- | ui/views/bubble/bubble_border.h | 6 | ||||
-rw-r--r-- | ui/views/bubble/bubble_border_2.cc | 399 | ||||
-rw-r--r-- | ui/views/bubble/bubble_border_2.h | 90 | ||||
-rw-r--r-- | ui/views/bubble/bubble_delegate.cc | 6 | ||||
-rw-r--r-- | ui/views/bubble/bubble_delegate.h | 6 | ||||
-rw-r--r-- | ui/views/bubble/bubble_frame_view.cc | 3 |
7 files changed, 508 insertions, 6 deletions
diff --git a/ui/views/bubble/bubble_border.cc b/ui/views/bubble/bubble_border.cc index a82382c..881f082 100644 --- a/ui/views/bubble/bubble_border.cc +++ b/ui/views/bubble/bubble_border.cc @@ -212,7 +212,7 @@ void BubbleBorder::GetInsetsForArrowLocation(gfx::Insets* insets, insets->Set(top, left, bottom, right); } -int BubbleBorder::border_thickness() const { +int BubbleBorder::GetBorderThickness() const { return images_->border_thickness; } @@ -505,7 +505,7 @@ void BubbleBackground::Paint(gfx::Canvas* canvas, views::View* view) const { paint.setColor(border_->background_color()); SkPath path; gfx::Rect bounds(view->GetContentsBounds()); - bounds.Inset(-border_->border_thickness(), -border_->border_thickness()); + bounds.Inset(-border_->GetBorderThickness(), -border_->GetBorderThickness()); SkScalar radius = SkIntToScalar(BubbleBorder::GetCornerRadius()); path.addRoundRect(gfx::RectToSkRect(bounds), radius, radius); canvas->DrawPath(path, paint); diff --git a/ui/views/bubble/bubble_border.h b/ui/views/bubble/bubble_border.h index 072a806..17d1de7 100644 --- a/ui/views/bubble/bubble_border.h +++ b/ui/views/bubble/bubble_border.h @@ -118,7 +118,7 @@ class VIEWS_EXPORT BubbleBorder : public Border { virtual void GetInsets(gfx::Insets* insets) const OVERRIDE; // How many pixels the bubble border is from the edge of the images. - int border_thickness() const; + virtual int GetBorderThickness() const; protected: virtual ~BubbleBorder(); @@ -126,8 +126,8 @@ class VIEWS_EXPORT BubbleBorder : public Border { // Calculates the insets for a specific arrow location. Normally called from // GetInsets(arrow_location()), but may be called by specialized BubbleBorder // implementations. - void GetInsetsForArrowLocation(gfx::Insets* insets, - ArrowLocation arrow_loc) const; + virtual void GetInsetsForArrowLocation(gfx::Insets* insets, + ArrowLocation arrow_loc) const; private: struct BorderImages; diff --git a/ui/views/bubble/bubble_border_2.cc b/ui/views/bubble/bubble_border_2.cc new file mode 100644 index 0000000..b8fe817 --- /dev/null +++ b/ui/views/bubble/bubble_border_2.cc @@ -0,0 +1,399 @@ +// 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/views/bubble/bubble_border_2.h" + +#include <algorithm> // for std::max + +#include "base/logging.h" +#include "ui/gfx/canvas.h" +#include "ui/gfx/path.h" +#include "ui/gfx/rect.h" +#include "ui/gfx/screen.h" +#include "ui/gfx/skia_util.h" + +namespace { + +// Bubble border corner radius. +const int kCornerRadius = 2; + +// Arrow width and height. +const int kArrowHeight = 10; +const int kArrowWidth = 20; + +const int kBorderSize = 1; +const SkColor kBorderColor = SkColorSetARGB(0x26, 0, 0, 0); +const SkColor kBackgroundColor = SK_ColorWHITE; + +const int kShadowOffsetX = 0; +const int kShadowOffsetY = 5; +const double kShadowBlur = 30; +const SkColor kShadowColor = SkColorSetARGB(0x72, 0, 0, 0); + +// Builds a bubble shape for given |bounds|. +void BuildShape(const gfx::Rect& bounds, + views::BubbleBorder::ArrowLocation arrow_location, + SkScalar arrow_offset, + SkScalar padding, + SkPath* path, + int corner_radius_int, + int arrow_height_int, + int arrow_width_int) { + const SkScalar corner_radius = SkIntToScalar(corner_radius_int); + + const SkScalar left = SkIntToScalar(bounds.x()) + padding; + const SkScalar top = SkIntToScalar(bounds.y()) + padding; + const SkScalar right = SkIntToScalar(bounds.right()) - padding; + const SkScalar bottom = SkIntToScalar(bounds.bottom()) - padding; + + const SkScalar center_x = SkIntToScalar((bounds.x() + bounds.right()) / 2); + const SkScalar center_y = SkIntToScalar((bounds.y() + bounds.bottom()) / 2); + + const SkScalar half_arrow_width = + (SkIntToScalar(arrow_width_int) - padding) / 2; + const SkScalar arrow_height = SkIntToScalar(arrow_height_int) - padding; + + path->reset(); + path->incReserve(12); + + switch (arrow_location) { + case views::BubbleBorder::TOP_LEFT: + case views::BubbleBorder::TOP_RIGHT: + path->moveTo(center_x, bottom); + path->arcTo(right, bottom, right, center_y, corner_radius); + path->arcTo(right, top, center_x - half_arrow_width, top, + corner_radius); + path->lineTo(center_x + arrow_offset + half_arrow_width, top); + path->lineTo(center_x + arrow_offset, top - arrow_height); + path->lineTo(center_x + arrow_offset - half_arrow_width, top); + path->arcTo(left, top, left, center_y, corner_radius); + path->arcTo(left, bottom, center_x, bottom, corner_radius); + break; + case views::BubbleBorder::BOTTOM_LEFT: + case views::BubbleBorder::BOTTOM_RIGHT: + path->moveTo(center_x, top); + path->arcTo(left, top, left, center_y, corner_radius); + path->arcTo(left, bottom, center_x - half_arrow_width, bottom, + corner_radius); + path->lineTo(center_x + arrow_offset - half_arrow_width, bottom); + path->lineTo(center_x + arrow_offset, bottom + arrow_height); + path->lineTo(center_x + arrow_offset + half_arrow_width, bottom); + path->arcTo(right, bottom, right, center_y, corner_radius); + path->arcTo(right, top, center_x, top, corner_radius); + break; + case views::BubbleBorder::LEFT_TOP: + case views::BubbleBorder::LEFT_BOTTOM: + path->moveTo(right, center_y); + path->arcTo(right, top, center_x, top, corner_radius); + path->arcTo(left, top, left, center_y + arrow_offset - half_arrow_width, + corner_radius); + path->lineTo(left, center_y + arrow_offset - half_arrow_width); + path->lineTo(left - arrow_height, center_y + arrow_offset); + path->lineTo(left, center_y + arrow_offset + half_arrow_width); + path->arcTo(left, bottom, center_x, bottom, corner_radius); + path->arcTo(right, bottom, right, center_y, corner_radius); + break; + case views::BubbleBorder::RIGHT_TOP: + case views::BubbleBorder::RIGHT_BOTTOM: + path->moveTo(left, center_y); + path->arcTo(left, bottom, center_x, bottom, corner_radius); + path->arcTo(right, bottom, + right, center_y + arrow_offset + half_arrow_width, + corner_radius); + path->lineTo(right, center_y + arrow_offset + half_arrow_width); + path->lineTo(right + arrow_height, center_y + arrow_offset); + path->lineTo(right, center_y + arrow_offset - half_arrow_width); + path->arcTo(right, top, center_x, top, corner_radius); + path->arcTo(left, top, left, center_y, corner_radius); + break; + default: + // No arrows. + path->addRoundRect(gfx::RectToSkRect(bounds), + corner_radius, + corner_radius); + break; + } + + path->close(); +} + +} // namespace + +namespace views { + +BubbleBorder2::BubbleBorder2(ArrowLocation arrow_location) + : BubbleBorder(arrow_location, views::BubbleBorder::NO_SHADOW), + corner_radius_(kCornerRadius), + border_size_(kBorderSize), + arrow_height_(kArrowHeight), + arrow_width_(kArrowWidth), + background_color_(kBackgroundColor), + border_color_(kBorderColor) { + SetShadow(gfx::ShadowValue(gfx::Point(kShadowOffsetX, kShadowOffsetY), + kShadowBlur, kShadowColor)); +} + +BubbleBorder2::~BubbleBorder2() {} + +gfx::Rect BubbleBorder2::ComputeOffsetAndUpdateBubbleRect( + gfx::Rect bubble_rect, + const gfx::Rect& anchor_view_rect) { + offset_ = gfx::Point(); + + gfx::Rect monitor_rect = gfx::Screen::GetDisplayNearestPoint( + anchor_view_rect.CenterPoint()).bounds(); + if (monitor_rect.IsEmpty() || monitor_rect.Contains(bubble_rect)) + return bubble_rect; + + gfx::Point offset; + + if (has_arrow(arrow_location())) { + if (is_arrow_on_horizontal(arrow_location())) { + if (bubble_rect.x() < monitor_rect.x()) + offset.set_x(monitor_rect.x() - bubble_rect.x()); + else if (bubble_rect.right() > monitor_rect.right()) + offset.set_x(monitor_rect.right() - bubble_rect.right()); + } else { + if (bubble_rect.y() < monitor_rect.y()) + offset.set_y(monitor_rect.y() - bubble_rect.y()); + else if (bubble_rect.bottom() > monitor_rect.bottom()) + offset.set_y(monitor_rect.bottom() - bubble_rect.bottom()); + } + } + + bubble_rect.Offset(offset); + set_offset(offset); + + return bubble_rect; +} + +void BubbleBorder2::GetMask(const gfx::Rect& bounds, + gfx::Path* mask) const { + gfx::Insets insets; + GetInsets(&insets); + + gfx::Rect content_bounds(bounds); + content_bounds.Inset(insets); + + BuildShape(content_bounds, + arrow_location(), + SkIntToScalar(GetArrowOffset()), + SkIntToScalar(kBorderSize), + mask, + corner_radius_, + arrow_height_, + arrow_width_); +} + +void BubbleBorder2::SetShadow(gfx::ShadowValue shadow) { + shadows_.clear(); + shadows_.push_back(shadow); +} + +int BubbleBorder2::GetBorderThickness() const { + return 0; +} + +void BubbleBorder2::PaintBackground(gfx::Canvas* canvas, + const gfx::Rect& bounds) const { + canvas->FillRect(bounds, background_color_); +} + +int BubbleBorder2::GetArrowOffset() const { + if (has_arrow(arrow_location())) { + if (is_arrow_on_horizontal(arrow_location())) { + // Picks x offset and moves bubble arrow in the opposite direction. + // i.e. If bubble bounds is moved to right (positive offset), we need to + // move arrow to left so that it points to the same position. + return -offset_.x(); + } else { + // Picks y offset and moves bubble arrow in the opposite direction. + return -offset_.y(); + } + } + + // Other style does not have an arrow, so return 0. + return 0; +} + +void BubbleBorder2::GetInsets(gfx::Insets* insets) const { + // Negate to change from outer margin to inner padding. + gfx::Insets shadow_padding(-gfx::ShadowValue::GetMargin(shadows_)); + + if (arrow_location() == views::BubbleBorder::TOP_LEFT || + arrow_location() == views::BubbleBorder::TOP_RIGHT) { + // Arrow at top. + insets->Set(shadow_padding.top() + arrow_height_, + shadow_padding.left(), + shadow_padding.bottom(), + shadow_padding.right()); + } else if (arrow_location() == views::BubbleBorder::BOTTOM_LEFT || + arrow_location() == views::BubbleBorder::BOTTOM_RIGHT) { + // Arrow at bottom. + insets->Set(shadow_padding.top(), + shadow_padding.left(), + shadow_padding.bottom() + arrow_height_, + shadow_padding.right()); + } else if (arrow_location() == views::BubbleBorder::LEFT_TOP || + arrow_location() == views::BubbleBorder::LEFT_BOTTOM) { + // Arrow on left. + insets->Set(shadow_padding.top(), + shadow_padding.left() + arrow_height_, + shadow_padding.bottom(), + shadow_padding.right()); + } else if (arrow_location() == views::BubbleBorder::RIGHT_TOP || + arrow_location() == views::BubbleBorder::RIGHT_BOTTOM) { + // Arrow on right. + insets->Set(shadow_padding.top(), + shadow_padding.left(), + shadow_padding.bottom(), + shadow_padding.right() + arrow_height_); + } +} + +gfx::Rect BubbleBorder2::GetBounds(const gfx::Rect& position_relative_to, + const gfx::Size& contents_size) const { + gfx::Size border_size(contents_size); + gfx::Insets insets; + GetInsets(&insets); + border_size.Enlarge(insets.width(), insets.height()); + + // Negate to change from outer margin to inner padding. + gfx::Insets shadow_padding(-gfx::ShadowValue::GetMargin(shadows_)); + + // Anchor center that arrow aligns with. + const int anchor_center_x = + (position_relative_to.x() + position_relative_to.right()) / 2; + const int anchor_center_y = + (position_relative_to.y() + position_relative_to.bottom()) / 2; + + // Arrow position relative to top-left of bubble. |arrow_tip_x| is used for + // arrow at the top or bottom and |arrow_tip_y| is used for arrow on left or + // right. The 1px offset for |arrow_tip_y| is needed because the app list grid + // icon start at a different position (1px earlier) compared with bottom + // launcher bar. + // TODO(xiyuan): Remove 1px offset when app list icon image asset is updated. + int arrow_tip_x = insets.left() + contents_size.width() / 2 + + GetArrowOffset(); + int arrow_tip_y = insets.top() + contents_size.height() / 2 + + GetArrowOffset() + 1; + + if (arrow_location() == views::BubbleBorder::TOP_LEFT || + arrow_location() == views::BubbleBorder::TOP_RIGHT) { + // Arrow at top. + return gfx::Rect( + gfx::Point(anchor_center_x - arrow_tip_x, + position_relative_to.bottom() - shadow_padding.top()), + border_size); + } else if (arrow_location() == views::BubbleBorder::BOTTOM_LEFT || + arrow_location() == views::BubbleBorder::BOTTOM_RIGHT) { + // Arrow at bottom. + return gfx::Rect( + gfx::Point(anchor_center_x - arrow_tip_x, + position_relative_to.y() - border_size.height() + + shadow_padding.bottom()), + border_size); + } else if (arrow_location() == views::BubbleBorder::LEFT_TOP || + arrow_location() == views::BubbleBorder::LEFT_BOTTOM) { + // Arrow on left. + return gfx::Rect( + gfx::Point(position_relative_to.right() - shadow_padding.left(), + anchor_center_y - arrow_tip_y), + border_size); + } else if (arrow_location() == views::BubbleBorder::RIGHT_TOP || + arrow_location() == views::BubbleBorder::RIGHT_BOTTOM) { + // Arrow on right. + return gfx::Rect( + gfx::Point(position_relative_to.x() - border_size.width() + + shadow_padding.right(), + anchor_center_y - arrow_tip_y), + border_size); + } + + // No arrow bubble, center align with anchor. + return position_relative_to.Center(border_size); +} + +void BubbleBorder2::GetInsetsForArrowLocation(gfx::Insets* insets, + ArrowLocation arrow_loc) const { + int top = border_size_; + int bottom = border_size_; + int left = border_size_; + int right = border_size_; + switch (arrow_loc) { + case TOP_LEFT: + case TOP_RIGHT: + top = std::max(top, arrow_height_); + break; + + case BOTTOM_LEFT: + case BOTTOM_RIGHT: + bottom = std::max(bottom, arrow_height_); + break; + + case LEFT_TOP: + case LEFT_BOTTOM: + left = std::max(left, arrow_height_); + break; + + case RIGHT_TOP: + case RIGHT_BOTTOM: + right = std::max(right, arrow_height_); + break; + + case NONE: + case FLOAT: + // Nothing to do. + break; + } + insets->Set(top, left, bottom, right); +} + +void BubbleBorder2::Paint(const views::View& view, gfx::Canvas* canvas) const { + gfx::Insets insets; + GetInsets(&insets); + + gfx::Rect content_bounds = view.bounds(); + content_bounds.Inset(insets); + + SkPath path; + // Pads with 0.5 pixel since anti alias is used. + BuildShape(content_bounds, + arrow_location(), + SkIntToScalar(GetArrowOffset()), + SkDoubleToScalar(0.5), + &path, + corner_radius_, + arrow_height_, + arrow_width_); + + // Draw border and shadow. Note fill is needed to generate enough shadow. + SkPaint paint; + paint.setAntiAlias(true); + paint.setStyle(SkPaint::kStrokeAndFill_Style); + paint.setStrokeWidth(SkIntToScalar(border_size_)); + paint.setColor(border_color_); + SkSafeUnref(paint.setLooper(gfx::CreateShadowDrawLooper(shadows_))); + canvas->DrawPath(path, paint); + + // Pads with |border_size_| pixels to leave space for border lines. + BuildShape(content_bounds, + arrow_location(), + SkIntToScalar(GetArrowOffset()), + SkIntToScalar(border_size_), + &path, + corner_radius_, + arrow_height_, + arrow_width_); + canvas->Save(); + canvas->ClipPath(path); + + // Use full bounds so that arrow is also painted. + const gfx::Rect& bounds = view.bounds(); + PaintBackground(canvas, bounds); + + canvas->Restore(); +} + +} // namespace views diff --git a/ui/views/bubble/bubble_border_2.h b/ui/views/bubble/bubble_border_2.h new file mode 100644 index 0000000..b0386ec --- /dev/null +++ b/ui/views/bubble/bubble_border_2.h @@ -0,0 +1,90 @@ +// 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. + +#ifndef UI_VIEWS_BUBBLE_BUBBLE_BORDER_2_H_ +#define UI_VIEWS_BUBBLE_BUBBLE_BORDER_2_H_ + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "ui/gfx/shadow_value.h" +#include "ui/views/bubble/bubble_border.h" + +namespace views { + +// A BubbleBorder rendered with Skia drawing commands instead of images. +class VIEWS_EXPORT BubbleBorder2 : public BubbleBorder { + public: + explicit BubbleBorder2(ArrowLocation arrow_location); + virtual ~BubbleBorder2(); + + // Given the |bubble_rect| that this border encloses, and the bounds of the + // anchor view |anchor_view_rect|, compute the right offset to place the + // arrow at shifting the |bubble_rect| to fit inside the display area if + // needed. Returns the shifted |bubble_rect|. + gfx::Rect ComputeOffsetAndUpdateBubbleRect(gfx::Rect bubble_rect, + const gfx::Rect& anchor_view_rect); + + // Returns the path in |mask| that would be created if this border were to be + // applied to the rect specified by |bounds|. + void GetMask(const gfx::Rect& bounds, gfx::Path* mask) const; + + void set_offset(const gfx::Point& offset) { offset_ = offset; } + const gfx::Point& offset() const { return offset_; } + + void set_corner_radius(int corner_radius) { corner_radius_ = corner_radius; } + int corner_radius() const { return corner_radius_; } + + void set_border_size(int border_size) { border_size_ = border_size; } + int border_size() const { return border_size_; } + + void set_arrow_height(int arrow_height) { arrow_height_ = arrow_height; } + int arrow_height() const { return arrow_height_; } + + void set_arrow_width(int arrow_width) { arrow_width_ = arrow_width; } + int arrow_width() const { return arrow_width_; } + + void SetShadow(gfx::ShadowValue shadow); + + // views::BubbleBorder overrides: + int GetBorderThickness() const OVERRIDE; + + protected: + void PaintBackground(gfx::Canvas* canvas, + const gfx::Rect& bounds) const; + + private: + // Gets arrow offset based on arrow location and |offset_|. + int GetArrowOffset() const; + + // views::BubbleBorder overrides: + virtual void GetInsets(gfx::Insets* insets) const OVERRIDE; + virtual gfx::Rect GetBounds(const gfx::Rect& position_relative_to, + const gfx::Size& contents_size) const OVERRIDE; + void GetInsetsForArrowLocation(gfx::Insets* insets, + ArrowLocation arrow_loc) const OVERRIDE; + + // views::Border overrides: + virtual void Paint(const View& view, + gfx::Canvas* canvas) const OVERRIDE; + + int corner_radius_; + int border_size_; + int arrow_height_; + int arrow_width_; + SkColor background_color_; + SkColor border_color_; + + // Offset in pixels by which the arrow is shifted relative the default middle + // position. If the arrow is placed horizontally (at top or bottom), the |x_| + // component of |offset_| specifies the offset, else, the |y_| component. + gfx::Point offset_; + + gfx::ShadowValues shadows_; + + DISALLOW_COPY_AND_ASSIGN(BubbleBorder2); +}; + +} // namespace views + +#endif // UI_VIEWS_BUBBLE_BUBBLE_BORDER_2_H_ diff --git a/ui/views/bubble/bubble_delegate.cc b/ui/views/bubble/bubble_delegate.cc index f714102..0fc6fad 100644 --- a/ui/views/bubble/bubble_delegate.cc +++ b/ui/views/bubble/bubble_delegate.cc @@ -230,7 +230,11 @@ void BubbleDelegateView::OnWidgetMoved(Widget* widget) { } gfx::Rect BubbleDelegateView::GetAnchorRect() { - return anchor_view() ? anchor_view()->GetBoundsInScreen() : gfx::Rect(); + if (!anchor_view()) + return gfx::Rect(); + gfx::Rect anchor_bounds = anchor_view()->GetBoundsInScreen(); + anchor_bounds.Inset(anchor_insets_); + return anchor_bounds; } void BubbleDelegateView::Show() { diff --git a/ui/views/bubble/bubble_delegate.h b/ui/views/bubble/bubble_delegate.h index a76a727..cb9ba9f 100644 --- a/ui/views/bubble/bubble_delegate.h +++ b/ui/views/bubble/bubble_delegate.h @@ -72,6 +72,9 @@ class VIEWS_EXPORT BubbleDelegateView : public WidgetDelegateView, const gfx::Insets& margins() const { return margins_; } void set_margins(const gfx::Insets& margins) { margins_ = margins; } + void set_anchor_insets(const gfx::Insets& insets) { anchor_insets_ = insets; } + const gfx::Insets& anchor_insets() const { return anchor_insets_; } + gfx::NativeView parent_window() const { return parent_window_; } void set_parent_window(gfx::NativeView window) { parent_window_ = window; } @@ -157,6 +160,9 @@ class VIEWS_EXPORT BubbleDelegateView : public WidgetDelegateView, // The margins between the content and the inside of the border. gfx::Insets margins_; + // Insets applied to the |anchor_view_| bounds. + gfx::Insets anchor_insets_; + // Original opacity of the bubble. int original_opacity_; diff --git a/ui/views/bubble/bubble_frame_view.cc b/ui/views/bubble/bubble_frame_view.cc index 509ba6da..9ae1f8d 100644 --- a/ui/views/bubble/bubble_frame_view.cc +++ b/ui/views/bubble/bubble_frame_view.cc @@ -91,6 +91,9 @@ gfx::Rect BubbleFrameView::GetUpdatedWindowBounds(const gfx::Rect& anchor_rect, void BubbleFrameView::SetBubbleBorder(BubbleBorder* border) { bubble_border_ = border; set_border(bubble_border_); + + // Update the background, which relies on the border. + set_background(new views::BubbleBackground(border)); } gfx::Rect BubbleFrameView::GetMonitorBounds(const gfx::Rect& rect) { |