summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/app/theme/bubble_b.pngbin129 -> 125 bytes
-rw-r--r--chrome/app/theme/bubble_b_arrow.pngbin0 -> 327 bytes
-rw-r--r--chrome/app/theme/bubble_bl.pngbin247 -> 223 bytes
-rw-r--r--chrome/app/theme/bubble_br.pngbin252 -> 236 bytes
-rw-r--r--chrome/app/theme/bubble_corner_bl.pngbin123 -> 0 bytes
-rw-r--r--chrome/app/theme/bubble_corner_br.pngbin131 -> 0 bytes
-rw-r--r--chrome/app/theme/bubble_corner_tl.pngbin122 -> 0 bytes
-rw-r--r--chrome/app/theme/bubble_corner_tr.pngbin131 -> 0 bytes
-rw-r--r--chrome/app/theme/bubble_l.pngbin123 -> 117 bytes
-rw-r--r--chrome/app/theme/bubble_r.pngbin123 -> 117 bytes
-rw-r--r--chrome/app/theme/bubble_t.pngbin117 -> 113 bytes
-rw-r--r--chrome/app/theme/bubble_t_arrow.pngbin0 -> 268 bytes
-rw-r--r--chrome/app/theme/bubble_tl.pngbin207 -> 197 bytes
-rw-r--r--chrome/app/theme/bubble_tr.pngbin217 -> 212 bytes
-rw-r--r--chrome/app/theme/theme_resources.grd6
-rw-r--r--chrome/browser/bubble_positioner.h6
-rw-r--r--chrome/browser/views/bubble_border.cc178
-rw-r--r--chrome/browser/views/bubble_border.h53
-rw-r--r--chrome/browser/views/info_bubble.cc493
-rw-r--r--chrome/browser/views/info_bubble.h116
-rw-r--r--chrome/browser/views/location_bar_view.cc36
-rw-r--r--chrome/browser/views/location_bar_view.h13
-rw-r--r--chrome/browser/views/toolbar_star_toggle.cc23
23 files changed, 478 insertions, 446 deletions
diff --git a/chrome/app/theme/bubble_b.png b/chrome/app/theme/bubble_b.png
index f278cada..1a407a2 100644
--- a/chrome/app/theme/bubble_b.png
+++ b/chrome/app/theme/bubble_b.png
Binary files differ
diff --git a/chrome/app/theme/bubble_b_arrow.png b/chrome/app/theme/bubble_b_arrow.png
new file mode 100644
index 0000000..f4b18ea
--- /dev/null
+++ b/chrome/app/theme/bubble_b_arrow.png
Binary files differ
diff --git a/chrome/app/theme/bubble_bl.png b/chrome/app/theme/bubble_bl.png
index 66e4e2a..ca18efe 100644
--- a/chrome/app/theme/bubble_bl.png
+++ b/chrome/app/theme/bubble_bl.png
Binary files differ
diff --git a/chrome/app/theme/bubble_br.png b/chrome/app/theme/bubble_br.png
index 8f5684e..7b6950d 100644
--- a/chrome/app/theme/bubble_br.png
+++ b/chrome/app/theme/bubble_br.png
Binary files differ
diff --git a/chrome/app/theme/bubble_corner_bl.png b/chrome/app/theme/bubble_corner_bl.png
deleted file mode 100644
index d406821..0000000
--- a/chrome/app/theme/bubble_corner_bl.png
+++ /dev/null
Binary files differ
diff --git a/chrome/app/theme/bubble_corner_br.png b/chrome/app/theme/bubble_corner_br.png
deleted file mode 100644
index 7c731c6..0000000
--- a/chrome/app/theme/bubble_corner_br.png
+++ /dev/null
Binary files differ
diff --git a/chrome/app/theme/bubble_corner_tl.png b/chrome/app/theme/bubble_corner_tl.png
deleted file mode 100644
index a5dd5c4..0000000
--- a/chrome/app/theme/bubble_corner_tl.png
+++ /dev/null
Binary files differ
diff --git a/chrome/app/theme/bubble_corner_tr.png b/chrome/app/theme/bubble_corner_tr.png
deleted file mode 100644
index ba957f5..0000000
--- a/chrome/app/theme/bubble_corner_tr.png
+++ /dev/null
Binary files differ
diff --git a/chrome/app/theme/bubble_l.png b/chrome/app/theme/bubble_l.png
index 72f56ad..e3f71c8 100644
--- a/chrome/app/theme/bubble_l.png
+++ b/chrome/app/theme/bubble_l.png
Binary files differ
diff --git a/chrome/app/theme/bubble_r.png b/chrome/app/theme/bubble_r.png
index 4489da5..e3ad5af 100644
--- a/chrome/app/theme/bubble_r.png
+++ b/chrome/app/theme/bubble_r.png
Binary files differ
diff --git a/chrome/app/theme/bubble_t.png b/chrome/app/theme/bubble_t.png
index 6516c51..d468e21 100644
--- a/chrome/app/theme/bubble_t.png
+++ b/chrome/app/theme/bubble_t.png
Binary files differ
diff --git a/chrome/app/theme/bubble_t_arrow.png b/chrome/app/theme/bubble_t_arrow.png
new file mode 100644
index 0000000..66c28f1
--- /dev/null
+++ b/chrome/app/theme/bubble_t_arrow.png
Binary files differ
diff --git a/chrome/app/theme/bubble_tl.png b/chrome/app/theme/bubble_tl.png
index 55bb092..1b05004 100644
--- a/chrome/app/theme/bubble_tl.png
+++ b/chrome/app/theme/bubble_tl.png
Binary files differ
diff --git a/chrome/app/theme/bubble_tr.png b/chrome/app/theme/bubble_tr.png
index b4a8777..319af31 100644
--- a/chrome/app/theme/bubble_tr.png
+++ b/chrome/app/theme/bubble_tr.png
Binary files differ
diff --git a/chrome/app/theme/theme_resources.grd b/chrome/app/theme/theme_resources.grd
index 0372c9b..c92b4ce 100644
--- a/chrome/app/theme/theme_resources.grd
+++ b/chrome/app/theme/theme_resources.grd
@@ -228,10 +228,6 @@
<include name="IDR_INFOBAR_PLUGIN_INSTALL" file="infobar_plugin.png" type="BINDATA" />
<include name="IDR_INFOBAR_QUESTION_MARK" file="infobar_questionmark.png" type="BINDATA" />
<include name="IDR_INFOBAR_THEME" file="infobar_theme.png" type="BINDATA" />
- <include name="IDR_INFO_BUBBLE_CORNER_TOP_LEFT" file="bubble_corner_tl.png" type="BINDATA" />
- <include name="IDR_INFO_BUBBLE_CORNER_TOP_RIGHT" file="bubble_corner_tr.png" type="BINDATA" />
- <include name="IDR_INFO_BUBBLE_CORNER_BOTTOM_LEFT" file="bubble_corner_bl.png" type="BINDATA" />
- <include name="IDR_INFO_BUBBLE_CORNER_BOTTOM_RIGHT" file="bubble_corner_br.png" type="BINDATA" />
<include name="IDR_MENU_MARKER" file="menu_marker.png" type="BINDATA" />
<include name="IDR_FROZEN_TAB_ICON" file="frozen_tab.png" type="BINDATA" />
<include name="IDR_UPDATE_AVAILABLE" file="update_available.png" type="BINDATA" />
@@ -279,6 +275,8 @@
<include name="IDR_BUBBLE_BR" file="bubble_br.png" type="BINDATA" />
<include name="IDR_BUBBLE_B" file="bubble_b.png" type="BINDATA" />
<include name="IDR_BUBBLE_BL" file="bubble_bl.png" type="BINDATA" />
+ <include name="IDR_BUBBLE_T_ARROW" file="bubble_t_arrow.png" type="BINDATA" />
+ <include name="IDR_BUBBLE_B_ARROW" file="bubble_b_arrow.png" type="BINDATA" />
<include name="IDR_O2_GLOBE" file="o2_globe.png" type="BINDATA" />
<include name="IDR_O2_HISTORY" file="o2_history.png" type="BINDATA" />
<include name="IDR_O2_MORE" file="o2_more.png" type="BINDATA" />
diff --git a/chrome/browser/bubble_positioner.h b/chrome/browser/bubble_positioner.h
index 5336b1b..7ea4df0 100644
--- a/chrome/browser/bubble_positioner.h
+++ b/chrome/browser/bubble_positioner.h
@@ -10,12 +10,14 @@ class Rect;
}
// An object in the browser UI can implement this interface to provide display
-// bounds for the omnibox bubble.
+// bounds for the omnibox bubble and info bubble views.
class BubblePositioner {
public:
// Returns the bounds of the "location bar" stack (including star/go buttons
// where relevant). The omnibox dropdown uses this to calculate its width and
- // y-coordinate.
+ // y-coordinate, and views showing InfoBubbles use it to find the y-coordinate
+ // they should show at, so that all "bubble" UIs show up at the same vertical
+ // position.
virtual gfx::Rect GetLocationStackBounds() const = 0;
};
diff --git a/chrome/browser/views/bubble_border.cc b/chrome/browser/views/bubble_border.cc
index 8bf0170..9796a46 100644
--- a/chrome/browser/views/bubble_border.cc
+++ b/chrome/browser/views/bubble_border.cc
@@ -20,6 +20,14 @@ SkBitmap* BubbleBorder::right_ = NULL;
SkBitmap* BubbleBorder::bottom_right_ = NULL;
SkBitmap* BubbleBorder::bottom_ = NULL;
SkBitmap* BubbleBorder::bottom_left_ = NULL;
+SkBitmap* BubbleBorder::top_arrow_ = NULL;
+SkBitmap* BubbleBorder::bottom_arrow_ = NULL;
+
+//static
+int BubbleBorder::arrow_x_offset_;
+
+// The height inside the arrow image, in pixels.
+static const int kArrowInteriorHeight = 7;
gfx::Rect BubbleBorder::GetBounds(const gfx::Rect& position_relative_to,
const gfx::Size& contents_size) const {
@@ -34,16 +42,35 @@ gfx::Rect BubbleBorder::GetBounds(const gfx::Rect& position_relative_to,
border_size.Enlarge(insets.left() + insets.right(),
insets.top() + insets.bottom());
- int x = position_relative_to.x() + (position_relative_to.width() / 2) -
- (contents_size.width() / 2) - insets.left();
- int y = position_relative_to.bottom() - (top_->height() - kBubbleSpacing);
+ // Screen position depends on the arrow location.
+ int x = position_relative_to.x() + (position_relative_to.width() / 2);
+ if (arrow_is_left())
+ x -= arrow_x_offset_;
+ else if (arrow_location_ == NONE)
+ x -= ((contents_size.width() / 2) + insets.left());
+ else
+ x += (arrow_x_offset_ - border_size.width() + 1);
+ int y = position_relative_to.y();
+ if (arrow_is_top()) {
+ y += (position_relative_to.height() -
+ (top_arrow_->height() - kBubbleSpacing));
+ } else if (arrow_location_ == NONE) {
+ y += (position_relative_to.height() - (top_->height() - kBubbleSpacing));
+ } else {
+ y += ((bottom_arrow_->height() - kBubbleSpacing) - border_size.height());
+ }
return gfx::Rect(x, y, border_size.width(), border_size.height());
}
void BubbleBorder::GetInsets(gfx::Insets* insets) const {
- insets->Set(top_->height(), left_->width(), bottom_->height(),
- right_->width());
+ int top = top_->height();
+ int bottom = bottom_->height();
+ if (arrow_is_top())
+ top = std::max(top, top_arrow_->height());
+ else if (arrow_location_ != NONE)
+ bottom = std::max(bottom, bottom_arrow_->height());
+ insets->Set(top, left_->width(), bottom, right_->width());
}
// static
@@ -60,6 +87,15 @@ void BubbleBorder::InitClass() {
bottom_right_ = rb.GetBitmapNamed(IDR_BUBBLE_BR);
bottom_ = rb.GetBitmapNamed(IDR_BUBBLE_B);
bottom_left_ = rb.GetBitmapNamed(IDR_BUBBLE_BL);
+ top_arrow_ = rb.GetBitmapNamed(IDR_BUBBLE_T_ARROW);
+ bottom_arrow_ = rb.GetBitmapNamed(IDR_BUBBLE_B_ARROW);
+
+ // Calculate horizontal inset for arrow by ensuring that the widest arrow
+ // and corner images will have enough room to avoid overlap.
+ arrow_x_offset_ =
+ (std::max(top_arrow_->width(), bottom_arrow_->width()) / 2) +
+ std::max(std::max(top_left_->width(), top_right_->width()),
+ std::max(bottom_left_->width(), bottom_right_->width()));
initialized = true;
}
@@ -82,17 +118,21 @@ void BubbleBorder::Paint(const views::View& view, gfx::Canvas* canvas) const {
/* The variables below can be confusing; here's what they mean:
*
- * border_top∙∙∙∙∙∙∙┌────┬────────┬────┐
- * │ / │--------│ \ │
- * top∙∙∙∙∙∙∙∙∙∙∙∙∙∙│ / ├────────┤ \ │
- * tl_bottom∙∙∙∙∙∙├───┬┘ └┬───┤∙∙∙∙∙∙tr_bottom
- * │ | │ │ | │
- * │ | │ │ | │
- * │ | │ │ | │
- * bl_y∙∙∙∙∙∙∙∙∙∙∙├───┴┐ ┌┴───┤∙∙∙∙∙∙br_y
- * bottom∙∙∙∙∙∙∙∙∙∙∙│ \ ├────────┤ / │
- * │ \ │--------│ / │
- * border_bottom∙∙∙∙└────┴────────┴────┘
+ * 0∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙┌───┐ ┌───┐
+ * border_top∙∙∙∙∙∙∙┌────┬─┤ ▲ ├──────┤ ▲ ├─┬────┐
+ * │ / │-│∙ ∙│------│∙ ∙│-│ \ │
+ * top∙∙∙∙∙∙∙∙∙∙∙∙∙∙│ / ├─┴───┴──────┴───┴─┤ \ │
+ * tl_bottom∙∙∙∙∙∙├───┬┘ └┬───┤∙∙∙∙∙∙tr_bottom
+ * │ | │ │ | │
+ * │ | │ │ | │
+ * │ | │ │ | │
+ * bl_y∙∙∙∙∙∙∙∙∙∙∙├───┴┐ ┌┴───┤∙∙∙∙∙∙br_y
+ * bottom∙∙∙∙∙∙∙∙∙∙∙│ \ ├─┬───┬──────┬───┬─┤ / │
+ * │ \ │-│. .│------│. .│-│ / │
+ * border_bottom∙∙∙∙└────┴─┤ ▼ ├──────┤ ▼ ├─┴────┘
+ * view.height()∙∙∙∙∙∙∙∙∙∙∙└───┘ └───┘
+ *
+ * (At most one of the arrows will be drawn)
*/
gfx::Insets insets;
@@ -109,10 +149,6 @@ void BubbleBorder::Paint(const views::View& view, gfx::Canvas* canvas) const {
// Top left corner
canvas->DrawBitmapInt(*top_left_, 0, border_top);
- // Top edge
- canvas->TileImageInt(*top_, tl_width, border_top, width - tl_width - tr_width,
- t_height);
-
// Top right corner
canvas->DrawBitmapInt(*top_right_, width - tr_width, border_top);
@@ -123,13 +159,109 @@ void BubbleBorder::Paint(const views::View& view, gfx::Canvas* canvas) const {
// Bottom right corner
canvas->DrawBitmapInt(*bottom_right_, width - br_width, br_y);
- // Bottom edge
- canvas->TileImageInt(*bottom_, bl_width, bottom, width - bl_width - br_width,
- b_height);
// Bottom left corner
canvas->DrawBitmapInt(*bottom_left_, 0, bl_y);
// Left edge
canvas->TileImageInt(*left_, 0, tl_bottom, left_->width(), bl_y - tl_bottom);
+
+ // Arrow edge, if necessary
+ bool should_draw_top_edge = true;
+ bool should_draw_bottom_edge = true;
+ if (arrow_location_ != NONE) {
+ /* Here's what the variables below mean (without loss of generality):
+ *
+ * arrow_center
+ * arrow_x │ arrow_r
+ * │ │ │
+ * left_of_edge─┬────┐ │ │ │ ┌────┬─right_of_edge
+ * arrow_y∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙┌───┐
+ * edge_y∙∙∙∙∙∙∙┌────┬───┤ ▲ ├───────┬────┐ ┐
+ * │ / │---│∙ ∙│-------│ \ │ ├─e_height
+ * │ / ├───┴───┴───────┤ \ │ ┘
+ * ├───┬┘ └┬───┤
+ * | | └─┬─┘ | |
+ * ∙ ∙└─┬─┘ │ └───┬───┘∙ ∙
+ * left_of_arrow─┘ │ └─right_of_arrow
+ * arrow_width
+ *
+ * Not shown: border_y and tip_y contain the base and tip coordinates inside
+ * the arrow for use filling the arrow interior with the background color.
+ */
+
+ SkBitmap* edge;
+ SkBitmap* arrow;
+ int left_of_edge, right_of_edge, edge_y, arrow_y;
+ SkScalar border_y, tip_y;
+ if (arrow_is_top()) {
+ should_draw_top_edge = false;
+ edge = top_;
+ arrow = top_arrow_;
+ left_of_edge = tl_width;
+ right_of_edge = tr_width;
+ edge_y = border_top;
+ arrow_y = top - top_arrow_->height();
+ border_y = SkIntToScalar(top);
+ tip_y = SkIntToScalar(top - kArrowInteriorHeight);
+ } else {
+ should_draw_bottom_edge = false;
+ edge = bottom_;
+ arrow = bottom_arrow_;
+ left_of_edge = bl_width;
+ right_of_edge = br_width;
+ edge_y = arrow_y = bottom;
+ border_y = SkIntToScalar(bottom);
+ tip_y = SkIntToScalar(bottom + kArrowInteriorHeight);
+ }
+ int arrow_width = (arrow_is_top() ? top_arrow_ : bottom_arrow_)->width();
+ int arrow_center = arrow_is_left() ?
+ arrow_x_offset_ : width - arrow_x_offset_ - 1;
+ int arrow_x = arrow_center - (arrow_width / 2);
+ SkScalar arrow_interior_x =
+ SkIntToScalar(arrow_center - kArrowInteriorHeight);
+ SkScalar arrow_interior_r =
+ SkIntToScalar(arrow_center + kArrowInteriorHeight);
+ int arrow_r = arrow_x + arrow_width;
+ int e_height = edge->height();
+
+ // Edge to the left of the arrow
+ int left_of_arrow = arrow_x - left_of_edge;
+ if (left_of_arrow) {
+ canvas->TileImageInt(*edge, left_of_edge, edge_y, left_of_arrow,
+ e_height);
+ }
+
+ // Interior of the arrow (filled with background color)
+ SkPaint paint;
+ paint.setStyle(SkPaint::kFill_Style);
+ paint.setColor(background_color_);
+ gfx::Path path;
+ path.incReserve(4);
+ path.moveTo(arrow_interior_x, border_y);
+ path.lineTo(SkIntToScalar(arrow_center), tip_y);
+ path.lineTo(arrow_interior_r, border_y);
+ path.close();
+ canvas->drawPath(path, paint);
+
+ // Arrow border
+ canvas->DrawBitmapInt(*arrow, arrow_x, arrow_y);
+
+ // Edge to the right of the arrow
+ int right_of_arrow = width - arrow_r - right_of_edge;
+ if (right_of_arrow)
+ canvas->TileImageInt(*edge, arrow_r, edge_y, right_of_arrow, e_height);
+ }
+
+ // Top edge, if not already drawn
+ if (should_draw_top_edge) {
+ canvas->TileImageInt(*top_, tl_width, border_top,
+ width - tl_width - tr_width, t_height);
+ }
+
+ // Bottom edge, if not already drawn
+ if (should_draw_bottom_edge) {
+ canvas->TileImageInt(*bottom_, bl_width, bottom,
+ width - bl_width - br_width, b_height);
+ }
}
diff --git a/chrome/browser/views/bubble_border.h b/chrome/browser/views/bubble_border.h
index 7b0ee47..75f7a02 100644
--- a/chrome/browser/views/bubble_border.h
+++ b/chrome/browser/views/bubble_border.h
@@ -10,11 +10,20 @@
class SkBitmap;
-// Renders a round-rect border and a custom dropshadow. This can be used to
-// produce floating "bubble" objects.
+// Renders a round-rect border, with optional arrow (off by default), and a
+// custom dropshadow. This can be used to produce floating "bubble" objects.
class BubbleBorder : public views::Border {
public:
- BubbleBorder() {
+ // Possible locations for the (optional) arrow.
+ enum ArrowLocation {
+ NONE,
+ TOP_LEFT,
+ TOP_RIGHT,
+ BOTTOM_LEFT,
+ BOTTOM_RIGHT
+ };
+
+ BubbleBorder() : arrow_location_(NONE), background_color_(SK_ColorWHITE) {
InitClass();
}
@@ -27,9 +36,24 @@ class BubbleBorder : public views::Border {
return 4;
}
- // Gives the desired bounds (in screen coordinates) given the rect to position
- // relative to and the size of the contained contents. The contents are
- // centered underneath the supplied rect.
+ // Sets the location for the arrow.
+ void set_arrow_location(ArrowLocation arrow_location) {
+ arrow_location_ = arrow_location;
+ }
+
+ // Sets the background color for the arrow body. This is irrelevant if you do
+ // not also set the arrow location to something other than NONE.
+ void set_background_color(SkColor background_color) {
+ background_color_ = background_color;
+ }
+
+ // For borders with an arrow, gives the desired bounds (in screen coordinates)
+ // given the rect to point to and the size of the contained contents. This
+ // depends on the arrow location, so if you change that, you should call this
+ // again to find out the new coordinates.
+ //
+ // For borders without an arrow, gives the bounds with the content centered
+ // underneath the supplied rect.
gfx::Rect GetBounds(const gfx::Rect& position_relative_to,
const gfx::Size& contents_size) const;
@@ -42,6 +66,16 @@ class BubbleBorder : public views::Border {
virtual ~BubbleBorder() { }
+ // Returns true if there is an arrow and it is positioned on the top edge.
+ bool arrow_is_top() const {
+ return (arrow_location_ == TOP_LEFT) || (arrow_location_ == TOP_RIGHT);
+ }
+
+ // Returns true if there is an arrow and it is positioned on the left side.
+ bool arrow_is_left() const {
+ return (arrow_location_ == TOP_LEFT) || (arrow_location_ == BOTTOM_LEFT);
+ }
+
// Overridden from views::Border:
virtual void Paint(const views::View& view, gfx::Canvas* canvas) const;
@@ -54,6 +88,13 @@ class BubbleBorder : public views::Border {
static SkBitmap* bottom_right_;
static SkBitmap* bottom_;
static SkBitmap* bottom_left_;
+ static SkBitmap* top_arrow_;
+ static SkBitmap* bottom_arrow_;
+
+ static int arrow_x_offset_;
+
+ ArrowLocation arrow_location_;
+ SkColor background_color_;
DISALLOW_COPY_AND_ASSIGN(BubbleBorder);
};
diff --git a/chrome/browser/views/info_bubble.cc b/chrome/browser/views/info_bubble.cc
index e38c10a..bbd8231 100644
--- a/chrome/browser/views/info_bubble.cc
+++ b/chrome/browser/views/info_bubble.cc
@@ -7,36 +7,15 @@
#include "app/gfx/canvas.h"
#include "app/gfx/color_utils.h"
#include "app/gfx/path.h"
-#include "app/resource_bundle.h"
#include "chrome/browser/window_sizer.h"
#include "chrome/common/notification_service.h"
-#include "grit/theme_resources.h"
+#include "third_party/Skia/include/core/SkPaint.h"
+#include "views/fill_layout.h"
#include "views/widget/root_view.h"
#include "views/window/window.h"
-#if defined(OS_WIN)
-#include "base/win_util.h"
-#endif
-
-using views::View;
-
namespace {
-// All sizes are in pixels.
-
-// Size of the border, along each edge.
-const int kBorderSize = 1;
-
-// Size of the arrow.
-const int kArrowSize = 5;
-
-// Number of pixels to the start of the arrow from the edge of the window.
-const int kArrowXOffset = 13;
-
-// Number of pixels between the tip of the arrow and the region we're
-// pointing to.
-const int kArrowToContentPadding = -4;
-
// Background color of the bubble.
#if defined(OS_WIN)
const SkColor kBackgroundColor = color_utils::GetSysSkColor(COLOR_WINDOW);
@@ -45,29 +24,171 @@ const SkColor kBackgroundColor = color_utils::GetSysSkColor(COLOR_WINDOW);
const SkColor kBackgroundColor = SK_ColorWHITE;
#endif
-// Color of the border and arrow.
-const SkColor kBorderColor1 = SkColorSetRGB(99, 99, 99);
-// Border shadow color.
-const SkColor kBorderColor2 = SkColorSetRGB(160, 160, 160);
+}
+
+#if defined(OS_WIN)
+// BorderContents -------------------------------------------------------------
+
+// This is used to paint the border; see comments on BorderWidget below.
+class BorderContents : public views::View {
+ public:
+ BorderContents() { }
+
+ // Given the size of the contents and the rect (in screen coordinates) to
+ // point at, initializes the bubble and returns the bounds (in screen
+ // coordinates) of both the border and the contents inside the bubble.
+ // |is_rtl| is true if the UI is RTL and thus the arrow should default to the
+ // right side of the bubble; otherwise it defaults to the left top corner, and
+ // then is moved as necessary to try and fit the whole bubble on the same
+ // monitor as the rect being pointed to.
+ //
+ // TODO(pkasting): Maybe this should use mirroring transformations instead,
+ // which would hopefully simplify this code.
+ void InitAndGetBounds(const gfx::Rect& position_relative_to,
+ const gfx::Size& contents_size,
+ bool is_rtl,
+ gfx::Rect* inner_bounds,
+ gfx::Rect* outer_bounds);
+
+ private:
+ virtual ~BorderContents() { }
+
+ // Overridden from View:
+ virtual void Paint(gfx::Canvas* canvas);
+
+ DISALLOW_COPY_AND_ASSIGN(BorderContents);
+};
+
+void BorderContents::InitAndGetBounds(
+ const gfx::Rect& position_relative_to,
+ const gfx::Size& contents_size,
+ bool is_rtl,
+ gfx::Rect* inner_bounds,
+ gfx::Rect* outer_bounds) {
+ // Set the border.
+ BubbleBorder* bubble_border = new BubbleBorder;
+ set_border(bubble_border);
+ bubble_border->set_background_color(kBackgroundColor);
+
+ // Try putting the arrow in its default location, and calculating the bounds.
+ BubbleBorder::ArrowLocation arrow_location(is_rtl ?
+ BubbleBorder::TOP_RIGHT : BubbleBorder::TOP_LEFT);
+ bubble_border->set_arrow_location(arrow_location);
+ *outer_bounds = bubble_border->GetBounds(position_relative_to, contents_size);
+
+ // See if those bounds will fit on the monitor.
+ scoped_ptr<WindowSizer::MonitorInfoProvider> monitor_provider(
+ WindowSizer::CreateDefaultMonitorInfoProvider());
+ gfx::Rect monitor_bounds(
+ monitor_provider->GetMonitorWorkAreaMatching(position_relative_to));
+ if (!monitor_bounds.IsEmpty() && !monitor_bounds.Contains(*outer_bounds)) {
+ // The bounds don't fit. Move the arrow to try and improve things.
+ bool arrow_on_left =
+ (is_rtl ? (outer_bounds->x() < monitor_bounds.x()) :
+ (outer_bounds->right() <= monitor_bounds.right()));
+ if (outer_bounds->bottom() > monitor_bounds.bottom()) {
+ arrow_location = arrow_on_left ?
+ BubbleBorder::BOTTOM_LEFT : BubbleBorder::BOTTOM_RIGHT;
+ } else {
+ arrow_location = arrow_on_left ?
+ BubbleBorder::TOP_LEFT : BubbleBorder::TOP_RIGHT;
+ }
+ bubble_border->set_arrow_location(arrow_location);
+
+ // Now get the recalculated bounds.
+ *outer_bounds = bubble_border->GetBounds(position_relative_to,
+ contents_size);
+ }
-// Intended dimensions of the bubble's corner images. If you update these,
-// make sure that the OnSize code works.
-const int kInfoBubbleCornerWidth = 3;
-const int kInfoBubbleCornerHeight = 3;
+ // Calculate the bounds of the contained contents by subtracting the border
+ // dimensions.
+ *inner_bounds = *outer_bounds;
+ gfx::Insets insets;
+ bubble_border->GetInsets(&insets);
+ inner_bounds->Inset(insets.left(), insets.top(), insets.right(),
+ insets.bottom());
+}
-// Bubble corner images.
-const SkBitmap* kInfoBubbleCornerTopLeft = NULL;
-const SkBitmap* kInfoBubbleCornerTopRight = NULL;
-const SkBitmap* kInfoBubbleCornerBottomLeft = NULL;
-const SkBitmap* kInfoBubbleCornerBottomRight = NULL;
+void BorderContents::Paint(gfx::Canvas* canvas) {
+ // The border of this view creates an anti-aliased round-rect region for the
+ // contents, which we need to fill with the background color.
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ paint.setStyle(SkPaint::kFill_Style);
+ paint.setColor(kBackgroundColor);
+ gfx::Path path;
+ gfx::Rect bounds(GetLocalBounds(false));
+ SkRect rect;
+ rect.set(SkIntToScalar(bounds.x()), SkIntToScalar(bounds.y()),
+ SkIntToScalar(bounds.right()), SkIntToScalar(bounds.bottom()));
+ SkScalar radius = SkIntToScalar(BubbleBorder::GetCornerRadius());
+ path.addRoundRect(rect, radius, radius);
+ canvas->drawPath(path, paint);
+
+ // Now we paint the border, so it will be alpha-blended atop the contents.
+ // This looks slightly better in the corners than drawing the contents atop
+ // the border.
+ PaintBorder(canvas);
+}
-// Margins around the content.
-const int kInfoBubbleViewTopMargin = 6;
-const int kInfoBubbleViewBottomMargin = 9;
-const int kInfoBubbleViewLeftMargin = 6;
-const int kInfoBubbleViewRightMargin = 6;
+// BorderWidget ---------------------------------------------------------------
-} // namespace
+BorderWidget::BorderWidget() {
+ set_delete_on_destroy(false); // Our owner will free us manually.
+ set_window_style(WS_POPUP);
+ set_window_ex_style(WS_EX_TOOLWINDOW | WS_EX_LAYERED);
+}
+
+gfx::Rect BorderWidget::InitAndGetBounds(
+ HWND owner,
+ const gfx::Rect& position_relative_to,
+ const gfx::Size& contents_size,
+ bool is_rtl) {
+ // Margins around the contents.
+ const int kLeftMargin = 6;
+ const int kTopMargin = 6;
+ const int kRightMargin = 6;
+ const int kBottomMargin = 9;
+
+ // Set up the border view and ask it to calculate our bounds (and our
+ // contents').
+ gfx::Size local_contents_size(contents_size);
+ local_contents_size.Enlarge(kLeftMargin + kRightMargin,
+ kTopMargin + kBottomMargin);
+ BorderContents* border_contents = new BorderContents;
+ gfx::Rect inner_bounds, outer_bounds;
+ border_contents->InitAndGetBounds(position_relative_to, local_contents_size,
+ is_rtl, &inner_bounds, &outer_bounds);
+
+ // Initialize ourselves.
+ WidgetWin::Init(GetAncestor(owner, GA_ROOT), outer_bounds);
+ SetContentsView(border_contents);
+ SetWindowPos(owner, 0, 0, 0, 0,
+ SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOREDRAW);
+
+ // Chop a hole out of our region to show the contents through.
+ // CreateRectRgn() expects (left, top, right, bottom) in window coordinates.
+ inner_bounds.Inset(kLeftMargin, kTopMargin, kRightMargin, kBottomMargin);
+ gfx::Rect region_bounds(inner_bounds);
+ region_bounds.Offset(-outer_bounds.x(), -outer_bounds.y());
+ HRGN inner_region = CreateRectRgn(region_bounds.x(), region_bounds.y(),
+ region_bounds.right(), region_bounds.bottom());
+ HRGN outer_region = CreateRectRgn(0, 0,
+ outer_bounds.right(), outer_bounds.bottom());
+ CombineRgn(outer_region, outer_region, inner_region, RGN_XOR);
+ DeleteObject(inner_region);
+ SetWindowRgn(outer_region, true);
+
+ return inner_bounds;
+}
+
+LRESULT BorderWidget::OnMouseActivate(HWND window,
+ UINT hit_test,
+ UINT mouse_message) {
+ // Never activate.
+ return MA_NOACTIVATE;
+}
+#endif
// InfoBubble -----------------------------------------------------------------
@@ -76,7 +197,7 @@ InfoBubble* InfoBubble::Show(views::Window* parent,
const gfx::Rect& position_relative_to,
views::View* contents,
InfoBubbleDelegate* delegate) {
- InfoBubble* window = new InfoBubble();
+ InfoBubble* window = new InfoBubble;
window->Init(parent, position_relative_to, contents, delegate);
return window;
}
@@ -92,7 +213,6 @@ InfoBubble::InfoBubble()
#endif
delegate_(NULL),
parent_(NULL),
- content_view_(NULL),
closed_(false) {
}
@@ -105,24 +225,11 @@ void InfoBubble::Init(views::Window* parent,
delegate_ = delegate;
- if (kInfoBubbleCornerTopLeft == NULL) {
- kInfoBubbleCornerTopLeft = ResourceBundle::GetSharedInstance()
- .GetBitmapNamed(IDR_INFO_BUBBLE_CORNER_TOP_LEFT);
- kInfoBubbleCornerTopRight = ResourceBundle::GetSharedInstance()
- .GetBitmapNamed(IDR_INFO_BUBBLE_CORNER_TOP_RIGHT);
- kInfoBubbleCornerBottomLeft = ResourceBundle::GetSharedInstance()
- .GetBitmapNamed(IDR_INFO_BUBBLE_CORNER_BOTTOM_LEFT);
- kInfoBubbleCornerBottomRight = ResourceBundle::GetSharedInstance()
- .GetBitmapNamed(IDR_INFO_BUBBLE_CORNER_BOTTOM_RIGHT);
- }
#if defined(OS_WIN)
set_window_style(WS_POPUP | WS_CLIPCHILDREN);
set_window_ex_style(WS_EX_TOOLWINDOW);
- set_initial_class_style(
- (win_util::GetWinVersion() < win_util::WINVERSION_XP) ?
- 0 : CS_DROPSHADOW);
+ border_.reset(new BorderWidget);
#endif
- content_view_ = CreateContentView(contents);
#if defined(OS_WIN)
WidgetWin::Init(parent->GetNativeWindow(), gfx::Rect());
@@ -130,18 +237,28 @@ void InfoBubble::Init(views::Window* parent,
WidgetGtk::Init(GTK_WIDGET(parent->GetNativeWindow()), gfx::Rect());
#endif
- SetContentsView(content_view_);
- // The preferred size may differ when parented. Ask for the bounds again
- // and if they differ reset the bounds.
- gfx::Rect parented_bounds =
- content_view_->CalculateWindowBoundsAndAjust(position_relative_to);
- SetBounds(parented_bounds);
-
+ views::View* contents_view = new views::View;
+ contents_view->set_background(
+ views::Background::CreateSolidBackground(kBackgroundColor));
+ contents_view->SetLayoutManager(new views::FillLayout);
+ // Adding |contents| as a child has to be done before we call
+ // contents->GetPreferredSize() below, since some supplied views don't
+ // actually set themselves up until they're added to a hierarchy.
+ contents_view->AddChildView(contents);
+ SetContentsView(contents_view);
+
+ gfx::Rect bounds;
#if defined(OS_WIN)
+ bounds = border_->InitAndGetBounds(GetNativeView(),
+ position_relative_to,
+ contents_view->GetPreferredSize(),
+ contents->UILayoutIsRightToLeft());
+
// Register the Escape accelerator for closing.
GetFocusManager()->RegisterAccelerator(
views::Accelerator(VK_ESCAPE, false, false, false), this);
#endif
+ SetBounds(bounds);
NotificationService::current()->Notify(NotificationType::INFO_BUBBLE_CREATED,
Source<InfoBubble>(this),
@@ -149,16 +266,13 @@ void InfoBubble::Init(views::Window* parent,
// Show the window.
#if defined(OS_WIN)
+ border_->ShowWindow(SW_SHOW);
ShowWindow(SW_SHOW);
#else
views::WidgetGtk::Show();
#endif
}
-InfoBubble::ContentView* InfoBubble::CreateContentView(View* content) {
- return new ContentView(content, this);
-}
-
#if defined(OS_WIN)
void InfoBubble::OnActivate(UINT action, BOOL minimized, HWND window) {
// The popup should close when it is deactivated.
@@ -169,22 +283,6 @@ void InfoBubble::OnActivate(UINT action, BOOL minimized, HWND window) {
GetRootView()->GetChildViewAt(0)->RequestFocus();
}
}
-
-void InfoBubble::OnSize(UINT param, const CSize& size) {
- // See OnSizeAllocate for the Linux version.
- gfx::Path path;
- content_view_->GetMask(gfx::Size(size.cx, size.cy), &path);
- SetWindowRgn(path.CreateHRGN(), TRUE);
- WidgetWin::OnSize(param, size);
-}
-#elif defined(OS_LINUX)
-void InfoBubble::OnSizeAllocate(GtkWidget* widget, GtkAllocation* allocation) {
- gfx::Path path;
- content_view_->GetMask(gfx::Size(allocation->width, allocation->height),
- &path);
- SetShape(path);
- WidgetGtk::OnSizeAllocate(widget, allocation);
-}
#endif
void InfoBubble::Close(bool closed_by_escape) {
@@ -195,6 +293,7 @@ void InfoBubble::Close(bool closed_by_escape) {
delegate_->InfoBubbleClosing(this, closed_by_escape);
closed_ = true;
#if defined(OS_WIN)
+ border_->Close();
WidgetWin::Close();
#else
WidgetGtk::Close();
@@ -208,227 +307,3 @@ bool InfoBubble::AcceleratorPressed(const views::Accelerator& accelerator) {
}
return false;
}
-
-// ContentView ----------------------------------------------------------------
-
-InfoBubble::ContentView::ContentView(views::View* content, InfoBubble* host)
- : content_(content),
- host_(host) {
- if (UILayoutIsRightToLeft()) {
- arrow_edge_ = TOP_RIGHT;
- } else {
- arrow_edge_ = TOP_LEFT;
- }
-}
-
-gfx::Rect InfoBubble::ContentView::CalculateWindowBoundsAndAjust(
- const gfx::Rect& position_relative_to) {
- scoped_ptr<WindowSizer::MonitorInfoProvider> monitor_provider(
- WindowSizer::CreateDefaultMonitorInfoProvider());
- gfx::Rect monitor_bounds(
- monitor_provider->GetMonitorWorkAreaMatching(position_relative_to));
- // Calculate the bounds using TOP_LEFT (the default).
- gfx::Rect window_bounds = CalculateWindowBounds(position_relative_to);
- if (monitor_bounds.IsEmpty() || monitor_bounds.Contains(window_bounds))
- return window_bounds;
- // Didn't fit, adjust the edge to fit as much as we can.
- if (window_bounds.bottom() > monitor_bounds.bottom())
- SetArrowEdge(BOTTOM_LEFT);
- if (window_bounds.right() > monitor_bounds.right()) {
- if (IsTop())
- SetArrowEdge(TOP_RIGHT);
- else
- SetArrowEdge(BOTTOM_RIGHT);
- }
- // And return new bounds.
- return CalculateWindowBounds(position_relative_to);
-}
-
-gfx::Size InfoBubble::ContentView::GetPreferredSize() {
- DCHECK(GetChildViewCount() == 1);
- View* content = GetChildViewAt(0);
- gfx::Size pref = content->GetPreferredSize();
- pref.Enlarge(kBorderSize + kBorderSize + kInfoBubbleViewLeftMargin +
- kInfoBubbleViewRightMargin,
- kBorderSize + kBorderSize + kArrowSize +
- kInfoBubbleViewTopMargin + kInfoBubbleViewBottomMargin);
- return pref;
-}
-
-void InfoBubble::ContentView::Layout() {
- DCHECK(GetChildViewCount() == 1);
- View* content = GetChildViewAt(0);
- int x = kBorderSize;
- int y = kBorderSize;
- int content_width = width() - kBorderSize - kBorderSize -
- kInfoBubbleViewLeftMargin - kInfoBubbleViewRightMargin;
- int content_height = height() - kBorderSize - kBorderSize - kArrowSize -
- kInfoBubbleViewTopMargin - kInfoBubbleViewBottomMargin;
- if (IsTop())
- y += kArrowSize;
- x += kInfoBubbleViewLeftMargin;
- y += kInfoBubbleViewTopMargin;
- content->SetBounds(x, y, content_width, content_height);
-}
-
-void InfoBubble::ContentView::GetMask(const gfx::Size& size, gfx::Path* mask) {
- // Redefine the window visible region so that our dropshadows look right.
- SkScalar width = SkIntToScalar(size.width());
- SkScalar height = SkIntToScalar(size.height());
- SkScalar arrow_size = SkIntToScalar(kArrowSize);
- SkScalar arrow_x = SkIntToScalar(
- (IsLeft() ? kArrowXOffset : width - kArrowXOffset) - 1);
- SkScalar corner_size = SkIntToScalar(kInfoBubbleCornerHeight);
-
- if (IsTop()) {
- // Top left corner.
- mask->moveTo(0, arrow_size + corner_size - 1);
- mask->lineTo(corner_size - 1, arrow_size);
-
- // Draw the arrow and the notch of the arrow.
- mask->lineTo(arrow_x - arrow_size, arrow_size);
- mask->lineTo(arrow_x, 0);
- mask->lineTo(arrow_x + 3, 0);
- mask->lineTo(arrow_x + arrow_size + 3, arrow_size);
-
- // Top right corner.
- mask->lineTo(width - corner_size + 1, arrow_size);
- mask->lineTo(width, arrow_size + corner_size - 1);
-
- // Bottom right corner.
- mask->lineTo(width, height - corner_size);
- mask->lineTo(width - corner_size, height);
-
- // Bottom left corner.
- mask->lineTo(corner_size, height);
- mask->lineTo(0, height - corner_size);
- } else {
- // Top left corner.
- mask->moveTo(0, corner_size - 1);
- mask->lineTo(corner_size - 1, 0);
-
- // Top right corner.
- mask->lineTo(width - corner_size + 1, 0);
- mask->lineTo(width, corner_size - 1);
-
- // Bottom right corner.
- mask->lineTo(width, height - corner_size - arrow_size);
- mask->lineTo(width - corner_size, height - arrow_size);
-
- // Draw the arrow and the notch of the arrow.
- mask->lineTo(arrow_x + arrow_size + 2, height - arrow_size);
- mask->lineTo(arrow_x + 2, height);
- mask->lineTo(arrow_x + 1, height);
- mask->lineTo(arrow_x - arrow_size + 1, height - arrow_size);
-
- // Bottom left corner.
- mask->lineTo(corner_size, height - arrow_size);
- mask->lineTo(0, height - corner_size - arrow_size);
- }
-
- mask->close();
-}
-
-void InfoBubble::ContentView::Paint(gfx::Canvas* canvas) {
- int bubble_x = 0;
- int bubble_y = 0;
- int bubble_w = width();
- int bubble_h = height() - kArrowSize;
-
- int border_w = bubble_w - 2 * kInfoBubbleCornerWidth;
- int border_h = bubble_h - 2 * kInfoBubbleCornerHeight;
-
- if (IsTop())
- bubble_y += kArrowSize;
-
- // Fill in the background.
- // Left side.
- canvas->FillRectInt(kBackgroundColor,
- bubble_x, bubble_y + kInfoBubbleCornerHeight,
- kInfoBubbleCornerWidth, border_h);
- // Center Column.
- canvas->FillRectInt(kBackgroundColor,
- kInfoBubbleCornerWidth, bubble_y,
- border_w, bubble_h);
- // Right Column.
- canvas->FillRectInt(kBackgroundColor,
- bubble_w - kInfoBubbleCornerWidth,
- bubble_y + kInfoBubbleCornerHeight,
- kInfoBubbleCornerWidth, border_h);
-
- // Draw the border.
- // Top border.
- canvas->DrawLineInt(kBorderColor1,
- kInfoBubbleCornerWidth, bubble_y,
- kInfoBubbleCornerWidth + border_w, bubble_y);
- // Bottom border.
- canvas->DrawLineInt(kBorderColor1,
- kInfoBubbleCornerWidth, bubble_y + bubble_h - 1,
- kInfoBubbleCornerWidth + border_w,
- bubble_y + bubble_h - 1);
- // Left border.
- canvas->DrawLineInt(kBorderColor1,
- bubble_x, bubble_y + kInfoBubbleCornerHeight,
- bubble_x, bubble_y + kInfoBubbleCornerHeight + border_h);
-
- // Right border.
- canvas->DrawLineInt(kBorderColor1,
- width() - 1, bubble_y + kInfoBubbleCornerHeight,
- width() - 1,
- bubble_y + kInfoBubbleCornerHeight + border_h);
-
- // Draw the corners.
- canvas->DrawBitmapInt(*kInfoBubbleCornerTopLeft, 0, bubble_y);
- canvas->DrawBitmapInt(*kInfoBubbleCornerTopRight,
- bubble_w - kInfoBubbleCornerWidth, bubble_y);
- canvas->DrawBitmapInt(*kInfoBubbleCornerBottomLeft, 0,
- bubble_y + bubble_h - kInfoBubbleCornerHeight);
- canvas->DrawBitmapInt(*kInfoBubbleCornerBottomRight,
- bubble_w - kInfoBubbleCornerWidth,
- bubble_y + bubble_h - kInfoBubbleCornerHeight);
-
- // Draw the arrow and the notch of the arrow.
- int arrow_x = IsLeft() ? kArrowXOffset : width() - kArrowXOffset;
- int arrow_y = IsTop() ? bubble_y : bubble_y + bubble_h - 1;
- const int arrow_delta = IsTop() ? -1 : 1;
- for (int i = 0, y = arrow_y; i <= kArrowSize; ++i, y += arrow_delta) {
- if (kArrowSize != i) {
- // Draw the notch formed by the arrow.
- canvas->FillRectInt(kBackgroundColor, arrow_x - (kArrowSize - i) + 1,
- y, (kArrowSize - i) * 2 - 1, 1);
- }
- // Draw the sides of the arrow.
- canvas->FillRectInt(kBorderColor1, arrow_x - (kArrowSize - i), y, 1, 1);
- canvas->FillRectInt(kBorderColor1, arrow_x + (kArrowSize - i), y, 1, 1);
- if (i != 0) {
- canvas->FillRectInt(kBorderColor2, arrow_x - (kArrowSize - i) - 1, y, 1,
- 1);
- canvas->FillRectInt(kBorderColor2, arrow_x + (kArrowSize - i) + 1, y, 1,
- 1);
- }
- }
-}
-
-void InfoBubble::ContentView::ViewHierarchyChanged(bool is_add,
- View* parent,
- View* child) {
- if (is_add && child == this)
- AddChildView(content_);
-}
-
-gfx::Rect InfoBubble::ContentView::CalculateWindowBounds(
- const gfx::Rect& position_relative_to) {
- gfx::Size pref = GetPreferredSize();
- int x = position_relative_to.x() + position_relative_to.width() / 2;
- int y;
- if (IsLeft())
- x -= kArrowXOffset;
- else
- x = x + kArrowXOffset - pref.width();
- if (IsTop()) {
- y = position_relative_to.bottom() + kArrowToContentPadding;
- } else {
- y = position_relative_to.y() - kArrowToContentPadding - pref.height();
- }
- return gfx::Rect(x, y, pref.width(), pref.height());
-}
diff --git a/chrome/browser/views/info_bubble.h b/chrome/browser/views/info_bubble.h
index fe3231c..6e8698d 100644
--- a/chrome/browser/views/info_bubble.h
+++ b/chrome/browser/views/info_bubble.h
@@ -5,7 +5,7 @@
#ifndef CHROME_BROWSER_VIEWS_INFO_BUBBLE_H_
#define CHROME_BROWSER_VIEWS_INFO_BUBBLE_H_
-#include "views/view.h"
+#include "chrome/browser/views/bubble_border.h"
#if defined(OS_WIN)
#include "views/widget/widget_win.h"
@@ -22,6 +22,7 @@
// InfoBubble insets the contents for you, so the contents typically shouldn't
// have any additional margins.
+class BorderWidget;
class InfoBubble;
namespace views {
@@ -32,6 +33,37 @@ namespace gfx {
class Path;
}
+#if defined(OS_WIN)
+// This is a window that surrounds the info bubble and paints the margin and
+// border. It is a separate window so that it can be a layered window, so that
+// we can use >1-bit alpha shadow images on the borders, which look nicer than
+// the Windows CS_DROPSHADOW shadows. The info bubble window itself cannot be a
+// layered window because that prevents it from hosting native child controls.
+class BorderWidget : public views::WidgetWin {
+ public:
+ BorderWidget();
+ virtual ~BorderWidget() { }
+
+ // Given the owning (parent) window, the size of the contained contents
+ // (without margins), and the rect (in screen coordinates) to point to,
+ // initializes the window and returns the bounds (in screen coordinates) the
+ // contents should use. |is_rtl| is supplied to
+ // BorderContents::InitAndGetBounds(), see its declaration for details.
+ gfx::Rect InitAndGetBounds(HWND owner,
+ const gfx::Rect& position_relative_to,
+ const gfx::Size& contents_size,
+ bool is_rtl);
+
+ private:
+ // Overridden from WidgetWin:
+ virtual LRESULT OnMouseActivate(HWND window,
+ UINT hit_test,
+ UINT mouse_message);
+
+ DISALLOW_COPY_AND_ASSIGN(BorderWidget);
+};
+#endif
+
class InfoBubbleDelegate {
public:
// Called when the InfoBubble is closing and is about to be deleted.
@@ -70,75 +102,6 @@ class InfoBubble : public views::WidgetGtk {
virtual void Close();
protected:
- // InfoBubble::CreateContentView() creates one of these. ContentView houses
- // the supplied content as its only child view, renders the arrow/border of
- // the bubble and sizes the content.
- class ContentView : public views::View {
- public:
- // Possible edges the arrow is aligned along.
- enum ArrowEdge {
- TOP_LEFT = 0,
- TOP_RIGHT = 1,
- BOTTOM_LEFT = 2,
- BOTTOM_RIGHT = 3
- };
-
- // Creates the ContentView. The supplied view is added as the only child of
- // the ContentView.
- ContentView(views::View* content, InfoBubble* host);
-
- virtual ~ContentView() {}
-
- // Returns the bounds for the window to contain this view.
- //
- // This invokes CalculateWindowBounds, if the returned bounds don't fit on
- // the monitor containing position_relative_to, the arrow edge is adjusted.
- virtual gfx::Rect CalculateWindowBoundsAndAjust(
- const gfx::Rect& position_relative_to);
-
- // Sets the edge the arrow is rendered at.
- void SetArrowEdge(ArrowEdge arrow_edge) { arrow_edge_ = arrow_edge; }
-
- // Returns the preferred size, which is the sum of the preferred size of
- // the content and the border/arrow.
- virtual gfx::Size GetPreferredSize();
-
- // Positions the content relative to the border.
- virtual void Layout();
-
- // Return the mask for the content view.
- void GetMask(const gfx::Size& size, gfx::Path* mask);
-
- // Paints the background and arrow appropriately.
- virtual void Paint(gfx::Canvas* canvas);
-
- // Returns true if the arrow is positioned along the top edge of the
- // view. If this returns false the arrow is positioned along the bottom
- // edge.
- bool IsTop() { return (arrow_edge_ & 2) == 0; }
-
- // Returns true if the arrow is positioned along the left edge of the
- // view. If this returns false the arrow is positioned along the right edge.
- bool IsLeft() { return (arrow_edge_ & 1) == 0; }
-
- virtual void ViewHierarchyChanged(bool is_add, View* parent, View* child);
-
- private:
- // Returns the bounds for the window containing us based on the current
- // arrow edge.
- gfx::Rect CalculateWindowBounds(const gfx::Rect& position_relative_to);
-
- views::View* content_;
-
- // Edge to draw the arrow at.
- ArrowEdge arrow_edge_;
-
- // The bubble we're in.
- InfoBubble* host_;
-
- DISALLOW_COPY_AND_ASSIGN(ContentView);
- };
-
InfoBubble();
virtual ~InfoBubble() {}
@@ -148,16 +111,9 @@ class InfoBubble : public views::WidgetGtk {
views::View* contents,
InfoBubbleDelegate* delegate);
- // Creates and return a new ContentView containing content.
- virtual ContentView* CreateContentView(views::View* content);
-
#if defined(OS_WIN)
// Overridden from WidgetWin:
virtual void OnActivate(UINT action, BOOL minimized, HWND window);
- virtual void OnSize(UINT param, const CSize& size);
-#elif defined(OS_LINUX)
- // Overridden from WidgetGtk:
- virtual void OnSizeAllocate(GtkWidget* widget, GtkAllocation* allocation);
#endif
private:
@@ -174,8 +130,10 @@ class InfoBubble : public views::WidgetGtk {
// The window that this InfoBubble is parented to.
views::Window* parent_;
- // The content view contained by the infobubble.
- ContentView* content_view_;
+#if defined(OS_WIN)
+ // The window used to render the padding, border and arrow.
+ scoped_ptr<BorderWidget> border_;
+#endif
// Have we been closed?
bool closed_;
diff --git a/chrome/browser/views/location_bar_view.cc b/chrome/browser/views/location_bar_view.cc
index 267423b..4becb2f 100644
--- a/chrome/browser/views/location_bar_view.cc
+++ b/chrome/browser/views/location_bar_view.cc
@@ -22,6 +22,7 @@
#include "chrome/browser/alternate_nav_url_fetcher.h"
#include "chrome/browser/browser.h"
#include "chrome/browser/browser_list.h"
+#include "chrome/browser/bubble_positioner.h"
#include "chrome/browser/command_updater.h"
#include "chrome/browser/extensions/extension_browser_event_router.h"
#include "chrome/browser/extensions/extension_tabs_module.h"
@@ -96,7 +97,7 @@ LocationBarView::LocationBarView(Profile* profile,
selected_keyword_view_(profile),
keyword_hint_view_(profile),
type_to_search_view_(l10n_util::GetString(IDS_OMNIBOX_EMPTY_TEXT)),
- security_image_view_(profile, model),
+ security_image_view_(profile, model, bubble_positioner),
popup_window_mode_(popup_window_mode),
first_run_bubble_(this),
bubble_positioner_(bubble_positioner) {
@@ -666,8 +667,8 @@ void LocationBarView::RefreshPageActionViews() {
page_action_image_views_.resize(page_actions.size());
for (size_t i = 0; i < page_actions.size(); ++i) {
- page_action_image_views_[i] =
- new PageActionImageView(this, profile_, page_actions[i]);
+ page_action_image_views_[i] = new PageActionImageView(this, profile_,
+ page_actions[i], bubble_positioner_);
page_action_image_views_[i]->SetVisible(false);
page_action_image_views_[i]->SetParentOwned(false);
AddChildView(page_action_image_views_[i]);
@@ -1060,9 +1061,11 @@ void LocationBarView::ShowFirstRunBubbleInternal(bool use_OEM_bubble) {
// LocationBarImageView---------------------------------------------------------
-LocationBarView::LocationBarImageView::LocationBarImageView()
- : info_bubble_(NULL),
- show_info_bubble_task_(NULL) {
+LocationBarView::LocationBarImageView::LocationBarImageView(
+ const BubblePositioner* bubble_positioner)
+ : info_bubble_(NULL),
+ show_info_bubble_task_(NULL),
+ bubble_positioner_(bubble_positioner) {
}
LocationBarView::LocationBarImageView::~LocationBarImageView() {
@@ -1108,9 +1111,11 @@ void LocationBarView::LocationBarImageView::InfoBubbleClosing(
void LocationBarView::LocationBarImageView::ShowInfoBubbleImpl(
const std::wstring& text, SkColor text_color) {
+ gfx::Rect bounds(bubble_positioner_->GetLocationStackBounds());
gfx::Point location;
views::View::ConvertPointToScreen(this, &location);
- gfx::Rect bounds(location.x(), location.y(), width(), height());
+ bounds.set_x(location.x());
+ bounds.set_width(width());
views::Label* label = new views::Label(text);
label->SetMultiLine(true);
@@ -1130,11 +1135,13 @@ void LocationBarView::LocationBarImageView::ShowInfoBubbleImpl(
SkBitmap* LocationBarView::SecurityImageView::lock_icon_ = NULL;
SkBitmap* LocationBarView::SecurityImageView::warning_icon_ = NULL;
-LocationBarView::SecurityImageView::SecurityImageView(Profile* profile,
- ToolbarModel* model)
- : LocationBarImageView(),
- profile_(profile),
- model_(model) {
+LocationBarView::SecurityImageView::SecurityImageView(
+ Profile* profile,
+ ToolbarModel* model,
+ const BubblePositioner* bubble_positioner)
+ : LocationBarImageView(bubble_positioner),
+ profile_(profile),
+ model_(model) {
if (!lock_icon_) {
ResourceBundle& rb = ResourceBundle::GetSharedInstance();
lock_icon_ = rb.GetBitmapNamed(IDR_LOCK);
@@ -1185,8 +1192,9 @@ void LocationBarView::SecurityImageView::ShowInfoBubble() {
LocationBarView::PageActionImageView::PageActionImageView(
LocationBarView* owner,
Profile* profile,
- const PageAction* page_action)
- : LocationBarImageView(),
+ const PageAction* page_action,
+ const BubblePositioner* bubble_positioner)
+ : LocationBarImageView(bubble_positioner),
owner_(owner),
profile_(profile),
page_action_(page_action),
diff --git a/chrome/browser/views/location_bar_view.h b/chrome/browser/views/location_bar_view.h
index b72eca5..343e7e6 100644
--- a/chrome/browser/views/location_bar_view.h
+++ b/chrome/browser/views/location_bar_view.h
@@ -258,7 +258,7 @@ class LocationBarView : public LocationBar,
class LocationBarImageView : public views::ImageView,
public InfoBubbleDelegate {
public:
- LocationBarImageView();
+ explicit LocationBarImageView(const BubblePositioner* bubble_positioner);
virtual ~LocationBarImageView();
// Overridden from view for the mouse hovering.
@@ -285,6 +285,10 @@ class LocationBarView : public LocationBar,
// image.
ShowInfoBubbleTask* show_info_bubble_task_;
+ // A positioner used to give the info bubble the correct target bounds. The
+ // caller maintains ownership of this and must ensure it's kept alive.
+ const BubblePositioner* bubble_positioner_;
+
DISALLOW_COPY_AND_ASSIGN(LocationBarImageView);
};
@@ -300,7 +304,9 @@ class LocationBarView : public LocationBar,
WARNING
};
- SecurityImageView(Profile* profile, ToolbarModel* model_);
+ SecurityImageView(Profile* profile,
+ ToolbarModel* model_,
+ const BubblePositioner* bubble_positioner);
virtual ~SecurityImageView();
// Sets the image that should be displayed.
@@ -341,7 +347,8 @@ class LocationBarView : public LocationBar,
public:
PageActionImageView(LocationBarView* owner,
Profile* profile,
- const PageAction* page_action);
+ const PageAction* page_action,
+ const BubblePositioner* bubble_positioner);
virtual ~PageActionImageView();
// Overridden from view for the mouse hovering.
diff --git a/chrome/browser/views/toolbar_star_toggle.cc b/chrome/browser/views/toolbar_star_toggle.cc
index 0d9a8ff..9cd2e7b 100644
--- a/chrome/browser/views/toolbar_star_toggle.cc
+++ b/chrome/browser/views/toolbar_star_toggle.cc
@@ -35,14 +35,25 @@ ToolbarStarToggle::ToolbarStarToggle(views::ButtonListener* listener,
}
void ToolbarStarToggle::ShowStarBubble(const GURL& url, bool newly_bookmarked) {
+ gfx::Rect bounds(host_->GetLocationStackBounds());
gfx::Point star_location;
views::View::ConvertPointToScreen(this, &star_location);
- // Shift the x location by 1 as visually the center of the star appears 1
- // pixel to the right. By doing this bubble arrow points to the center
- // of the star.
- gfx::Rect star_bounds(star_location.x() + 1, star_location.y(), width(),
- height());
- browser::ShowBookmarkBubbleView(host_->GetWindow(), star_bounds, this,
+ // The visual center of the star is not centered within the bounds. The star
+ // has a single central pixel; there are 13 pixels on the "inside" side of it
+ // (toward the location bar) and 16 on the "outside". This means we need to
+ // shift the bounds one pixel toward the location bar in order to place the
+ // star's outside edge at the horizontal center. However, even this isn't
+ // good enough in RTL mode, because the InfoBubble's arrow's central pixel is
+ // drawn with its left edge on the target rect center-line in both LTR and RTL
+ // modes. So in RTL mode, we need to shift the bounds one more pixel left, in
+ // order to place the star's central pixel on the right side of the bounds'
+ // center-line, so that the arrow's center will line up.
+ //
+ // TODO: If the InfoBubble used mirroring transformations maybe this could
+ // become symmetric (-1 : 1).
+ bounds.set_x(star_location.x() + (UILayoutIsRightToLeft() ? -2 : 1));
+ bounds.set_width(width());
+ browser::ShowBookmarkBubbleView(host_->GetWindow(), bounds, this,
host_->profile(), url, newly_bookmarked);
}