diff options
author | pkasting@chromium.org <pkasting@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-09-16 21:33:45 +0000 |
---|---|---|
committer | pkasting@chromium.org <pkasting@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-09-16 21:33:45 +0000 |
commit | 4a879f60ee8a75b1e6a2d9456d6fe762e87bb70d (patch) | |
tree | 01a08174fc6dd01100cfb80810dd5046d148cd55 | |
parent | 665caa21cf5904f6935dfa6e091522206a8f05e9 (diff) | |
download | chromium_src-4a879f60ee8a75b1e6a2d9456d6fe762e87bb70d.zip chromium_src-4a879f60ee8a75b1e6a2d9456d6fe762e87bb70d.tar.gz chromium_src-4a879f60ee8a75b1e6a2d9456d6fe762e87bb70d.tar.bz2 |
Convert InfoBubble to using BubbleBorder. This also replaces the border graphics with a slightly darker set from Nicholas.
The downside here is that the Linux views code is definitely going to be broken by this. I'm not sure of the best design for Linux views, so I haven't implemented one.
BUG=21028
TEST=Open info bubbles (click star button, hover lock icon) and observe they are rendered with an identical border/shadow to the Omnibox dropdown, and at the same vertical position.
Review URL: http://codereview.chromium.org/195099
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@26394 0039d316-1c4b-4281-b951-d872f2087c98
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); } |