summaryrefslogtreecommitdiffstats
path: root/ui/views/bubble
diff options
context:
space:
mode:
authorvarunjain@chromium.org <varunjain@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-08-04 00:39:58 +0000
committervarunjain@chromium.org <varunjain@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-08-04 00:39:58 +0000
commit62fffee065933a9be261ef76ccc4450f0e400f31 (patch)
tree42a22f3cc8000ad62dd8bcc0b9a5ae97b4e74319 /ui/views/bubble
parent92daaa430772d8affc2797a3670bae088ff826c8 (diff)
downloadchromium_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.cc4
-rw-r--r--ui/views/bubble/bubble_border.h6
-rw-r--r--ui/views/bubble/bubble_border_2.cc399
-rw-r--r--ui/views/bubble/bubble_border_2.h90
-rw-r--r--ui/views/bubble/bubble_delegate.cc6
-rw-r--r--ui/views/bubble/bubble_delegate.h6
-rw-r--r--ui/views/bubble/bubble_frame_view.cc3
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) {