diff options
23 files changed, 478 insertions, 446 deletions
diff --git a/chrome/app/theme/bubble_b.png b/chrome/app/theme/bubble_b.png Binary files differindex f278cada..1a407a2 100644 --- a/chrome/app/theme/bubble_b.png +++ b/chrome/app/theme/bubble_b.png diff --git a/chrome/app/theme/bubble_b_arrow.png b/chrome/app/theme/bubble_b_arrow.png Binary files differnew file mode 100644 index 0000000..f4b18ea --- /dev/null +++ b/chrome/app/theme/bubble_b_arrow.png diff --git a/chrome/app/theme/bubble_bl.png b/chrome/app/theme/bubble_bl.png Binary files differindex 66e4e2a..ca18efe 100644 --- a/chrome/app/theme/bubble_bl.png +++ b/chrome/app/theme/bubble_bl.png diff --git a/chrome/app/theme/bubble_br.png b/chrome/app/theme/bubble_br.png Binary files differindex 8f5684e..7b6950d 100644 --- a/chrome/app/theme/bubble_br.png +++ b/chrome/app/theme/bubble_br.png diff --git a/chrome/app/theme/bubble_corner_bl.png b/chrome/app/theme/bubble_corner_bl.png Binary files differdeleted file mode 100644 index d406821..0000000 --- a/chrome/app/theme/bubble_corner_bl.png +++ /dev/null diff --git a/chrome/app/theme/bubble_corner_br.png b/chrome/app/theme/bubble_corner_br.png Binary files differdeleted file mode 100644 index 7c731c6..0000000 --- a/chrome/app/theme/bubble_corner_br.png +++ /dev/null diff --git a/chrome/app/theme/bubble_corner_tl.png b/chrome/app/theme/bubble_corner_tl.png Binary files differdeleted file mode 100644 index a5dd5c4..0000000 --- a/chrome/app/theme/bubble_corner_tl.png +++ /dev/null diff --git a/chrome/app/theme/bubble_corner_tr.png b/chrome/app/theme/bubble_corner_tr.png Binary files differdeleted file mode 100644 index ba957f5..0000000 --- a/chrome/app/theme/bubble_corner_tr.png +++ /dev/null diff --git a/chrome/app/theme/bubble_l.png b/chrome/app/theme/bubble_l.png Binary files differindex 72f56ad..e3f71c8 100644 --- a/chrome/app/theme/bubble_l.png +++ b/chrome/app/theme/bubble_l.png diff --git a/chrome/app/theme/bubble_r.png b/chrome/app/theme/bubble_r.png Binary files differindex 4489da5..e3ad5af 100644 --- a/chrome/app/theme/bubble_r.png +++ b/chrome/app/theme/bubble_r.png diff --git a/chrome/app/theme/bubble_t.png b/chrome/app/theme/bubble_t.png Binary files differindex 6516c51..d468e21 100644 --- a/chrome/app/theme/bubble_t.png +++ b/chrome/app/theme/bubble_t.png diff --git a/chrome/app/theme/bubble_t_arrow.png b/chrome/app/theme/bubble_t_arrow.png Binary files differnew file mode 100644 index 0000000..66c28f1 --- /dev/null +++ b/chrome/app/theme/bubble_t_arrow.png diff --git a/chrome/app/theme/bubble_tl.png b/chrome/app/theme/bubble_tl.png Binary files differindex 55bb092..1b05004 100644 --- a/chrome/app/theme/bubble_tl.png +++ b/chrome/app/theme/bubble_tl.png diff --git a/chrome/app/theme/bubble_tr.png b/chrome/app/theme/bubble_tr.png Binary files differindex b4a8777..319af31 100644 --- a/chrome/app/theme/bubble_tr.png +++ b/chrome/app/theme/bubble_tr.png 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); } |