| Index: chrome/browser/gtk/info_bubble_gtk.cc
|
| diff --git a/chrome/browser/gtk/info_bubble_gtk.cc b/chrome/browser/gtk/info_bubble_gtk.cc
|
| index 1072fb2c772610613646d9fc34cf6e39abc6eebf..68fbc2d3470b9ee904f7fa46bf7ce5c9bd4bffea 100644
|
| --- a/chrome/browser/gtk/info_bubble_gtk.cc
|
| +++ b/chrome/browser/gtk/info_bubble_gtk.cc
|
| @@ -21,13 +21,17 @@ namespace {
|
|
|
| // The height of the arrow, and the width will be about twice the height.
|
| const int kArrowSize = 5;
|
| +
|
| // Number of pixels to the start of the arrow from the edge of the window.
|
| const int kArrowX = 13;
|
| +
|
| // Number of pixels between the tip of the arrow and the region we're
|
| // pointing to.
|
| const int kArrowToContentPadding = -6;
|
| +
|
| // We draw flat diagonal corners, each corner is an NxN square.
|
| const int kCornerSize = 3;
|
| +
|
| // Margins around the content.
|
| const int kTopMargin = kArrowSize + kCornerSize + 6;
|
| const int kBottomMargin = kCornerSize + 6;
|
| @@ -37,87 +41,6 @@ const int kRightMargin = kCornerSize + 6;
|
| const GdkColor kBackgroundColor = GDK_COLOR_RGB(0xff, 0xff, 0xff);
|
| const GdkColor kFrameColor = GDK_COLOR_RGB(0x63, 0x63, 0x63);
|
|
|
| -enum FrameType {
|
| - FRAME_MASK,
|
| - FRAME_STROKE,
|
| -};
|
| -
|
| -// Make the points for our polygon frame, either for fill (the mask), or for
|
| -// when we stroke the border. NOTE: This seems a bit overcomplicated, but it
|
| -// requires a bunch of careful fudging to get the pixels rasterized exactly
|
| -// where we want them, the arrow to have a 1 pixel point, etc.
|
| -// TODO(deanm): Windows draws with Skia and uses some PNG images for the
|
| -// corners. This is a lot more work, but they get anti-aliasing.
|
| -std::vector<GdkPoint> MakeFramePolygonPoints(int width,
|
| - int height,
|
| - FrameType type) {
|
| - using gtk_util::MakeBidiGdkPoint;
|
| - std::vector<GdkPoint> points;
|
| -
|
| - bool ltr = l10n_util::GetTextDirection() == l10n_util::LEFT_TO_RIGHT;
|
| - // If we have a stroke, we have to offset some of our points by 1 pixel.
|
| - // We have to inset by 1 pixel when we draw horizontal lines that are on the
|
| - // bottom or when we draw vertical lines that are closer to the end (end is
|
| - // right for ltr).
|
| - int y_off = (type == FRAME_MASK) ? 0 : -1;
|
| - // We use this one for LTR.
|
| - int x_off_l = ltr ? y_off : 0;
|
| - // We use this one for RTL.
|
| - int x_off_r = !ltr ? -y_off : 0;
|
| -
|
| - // Top left corner.
|
| - points.push_back(MakeBidiGdkPoint(
|
| - x_off_r, kArrowSize + kCornerSize - 1, width, ltr));
|
| - points.push_back(MakeBidiGdkPoint(
|
| - kCornerSize + x_off_r - 1, kArrowSize, width, ltr));
|
| -
|
| - // The arrow.
|
| - points.push_back(MakeBidiGdkPoint(
|
| - kArrowX - kArrowSize + x_off_r, kArrowSize, width, ltr));
|
| - points.push_back(MakeBidiGdkPoint(
|
| - kArrowX + x_off_r, 0, width, ltr));
|
| - points.push_back(MakeBidiGdkPoint(
|
| - kArrowX + 1 + x_off_l, 0, width, ltr));
|
| - points.push_back(MakeBidiGdkPoint(
|
| - kArrowX + kArrowSize + 1 + x_off_l, kArrowSize, width, ltr));
|
| -
|
| - // Top right corner.
|
| - points.push_back(MakeBidiGdkPoint(
|
| - width - kCornerSize + 1 + x_off_l, kArrowSize, width, ltr));
|
| - points.push_back(MakeBidiGdkPoint(
|
| - width + x_off_l, kArrowSize + kCornerSize - 1, width, ltr));
|
| -
|
| - // Bottom right corner.
|
| - points.push_back(MakeBidiGdkPoint(
|
| - width + x_off_l, height - kCornerSize, width, ltr));
|
| - points.push_back(MakeBidiGdkPoint(
|
| - width - kCornerSize + x_off_r, height + y_off, width, ltr));
|
| -
|
| - // Bottom left corner.
|
| - points.push_back(MakeBidiGdkPoint(
|
| - kCornerSize + x_off_l, height + y_off, width, ltr));
|
| - points.push_back(MakeBidiGdkPoint(
|
| - x_off_r, height - kCornerSize, width, ltr));
|
| -
|
| - return points;
|
| -}
|
| -
|
| -gboolean HandleExpose(GtkWidget* widget,
|
| - GdkEventExpose* event,
|
| - gpointer unused) {
|
| - GdkDrawable* drawable = GDK_DRAWABLE(event->window);
|
| - GdkGC* gc = gdk_gc_new(drawable);
|
| - gdk_gc_set_rgb_fg_color(gc, &kFrameColor);
|
| -
|
| - // Stroke the frame border.
|
| - std::vector<GdkPoint> points = MakeFramePolygonPoints(
|
| - widget->allocation.width, widget->allocation.height, FRAME_STROKE);
|
| - gdk_draw_polygon(drawable, gc, FALSE, &points[0], points.size());
|
| -
|
| - g_object_unref(gc);
|
| - return FALSE; // Propagate so our children paint, etc.
|
| -}
|
| -
|
| } // namespace
|
|
|
| // static
|
| @@ -138,7 +61,8 @@ InfoBubbleGtk::InfoBubbleGtk(GtkThemeProvider* provider)
|
| theme_provider_(provider),
|
| accel_group_(gtk_accel_group_new()),
|
| toplevel_window_(NULL),
|
| - mask_region_(NULL) {
|
| + mask_region_(NULL),
|
| + arrow_location_(ARROW_LOCATION_TOP_LEFT) {
|
| }
|
|
|
| InfoBubbleGtk::~InfoBubbleGtk() {
|
| @@ -168,8 +92,6 @@ void InfoBubbleGtk::Init(GtkWindow* toplevel_window,
|
|
|
| window_ = gtk_window_new(GTK_WINDOW_POPUP);
|
| gtk_widget_set_app_paintable(window_, TRUE);
|
| - // Have GTK double buffer around the expose signal.
|
| - gtk_widget_set_double_buffered(window_, TRUE);
|
|
|
| // Attach our accelerator group to the window with an escape accelerator.
|
| gtk_accel_group_connect(accel_group_, GDK_Escape,
|
| @@ -202,7 +124,7 @@ void InfoBubbleGtk::Init(GtkWindow* toplevel_window,
|
| GDK_BUTTON_RELEASE_MASK);
|
|
|
| g_signal_connect(window_, "expose-event",
|
| - G_CALLBACK(HandleExpose), NULL);
|
| + G_CALLBACK(HandleExposeThunk), this);
|
| g_signal_connect(window_, "size-allocate",
|
| G_CALLBACK(HandleSizeAllocateThunk), this);
|
| g_signal_connect(window_, "button-press-event",
|
| @@ -240,17 +162,135 @@ void InfoBubbleGtk::Init(GtkWindow* toplevel_window,
|
| theme_provider_->InitThemesFor(this);
|
| }
|
|
|
| +// NOTE: This seems a bit overcomplicated, but it requires a bunch of careful
|
| +// fudging to get the pixels rasterized exactly where we want them, the arrow to
|
| +// have a 1 pixel point, etc.
|
| +// TODO(deanm): Windows draws with Skia and uses some PNG images for the
|
| +// corners. This is a lot more work, but they get anti-aliasing.
|
| +// static
|
| +std::vector<GdkPoint> InfoBubbleGtk::MakeFramePolygonPoints(
|
| + ArrowLocationGtk arrow_location,
|
| + int width,
|
| + int height,
|
| + FrameType type) {
|
| + using gtk_util::MakeBidiGdkPoint;
|
| + std::vector<GdkPoint> points;
|
| +
|
| + // This name isn't completely accurate; the arrow location can differ from its
|
| + // expected location for LTR/RTL if needed for the bubble to fit onscreen.
|
| + bool ltr = (arrow_location == ARROW_LOCATION_TOP_LEFT);
|
| +
|
| + // If we have a stroke, we have to offset some of our points by 1 pixel.
|
| + // We have to inset by 1 pixel when we draw horizontal lines that are on the
|
| + // bottom or when we draw vertical lines that are closer to the end (end is
|
| + // right for ltr).
|
| + int y_off = (type == FRAME_MASK) ? 0 : -1;
|
| + // We use this one for LTR.
|
| + int x_off_l = ltr ? y_off : 0;
|
| + // We use this one for RTL.
|
| + int x_off_r = !ltr ? -y_off : 0;
|
| +
|
| + // Top left corner.
|
| + points.push_back(MakeBidiGdkPoint(
|
| + x_off_r, kArrowSize + kCornerSize - 1, width, ltr));
|
| + points.push_back(MakeBidiGdkPoint(
|
| + kCornerSize + x_off_r - 1, kArrowSize, width, ltr));
|
| +
|
| + // The arrow.
|
| + points.push_back(MakeBidiGdkPoint(
|
| + kArrowX - kArrowSize + x_off_r, kArrowSize, width, ltr));
|
| + points.push_back(MakeBidiGdkPoint(
|
| + kArrowX + x_off_r, 0, width, ltr));
|
| + points.push_back(MakeBidiGdkPoint(
|
| + kArrowX + 1 + x_off_l, 0, width, ltr));
|
| + points.push_back(MakeBidiGdkPoint(
|
| + kArrowX + kArrowSize + 1 + x_off_l, kArrowSize, width, ltr));
|
| +
|
| + // Top right corner.
|
| + points.push_back(MakeBidiGdkPoint(
|
| + width - kCornerSize + 1 + x_off_l, kArrowSize, width, ltr));
|
| + points.push_back(MakeBidiGdkPoint(
|
| + width + x_off_l, kArrowSize + kCornerSize - 1, width, ltr));
|
| +
|
| + // Bottom right corner.
|
| + points.push_back(MakeBidiGdkPoint(
|
| + width + x_off_l, height - kCornerSize, width, ltr));
|
| + points.push_back(MakeBidiGdkPoint(
|
| + width - kCornerSize + x_off_r, height + y_off, width, ltr));
|
| +
|
| + // Bottom left corner.
|
| + points.push_back(MakeBidiGdkPoint(
|
| + kCornerSize + x_off_l, height + y_off, width, ltr));
|
| + points.push_back(MakeBidiGdkPoint(
|
| + x_off_r, height - kCornerSize, width, ltr));
|
| +
|
| + return points;
|
| +}
|
| +
|
| +InfoBubbleGtk::ArrowLocationGtk InfoBubbleGtk::GetArrowLocation(
|
| + int arrow_x, int width) {
|
| + bool ltr = (l10n_util::GetTextDirection() == l10n_util::LEFT_TO_RIGHT);
|
| + int screen_width = gdk_screen_get_width(gdk_screen_get_default());
|
| +
|
| + bool left_is_onscreen = (arrow_x - kArrowX + width < screen_width);
|
| + bool right_is_onscreen = (arrow_x + kArrowX - width >= 0);
|
| +
|
| + // Use the expected location for our LTR/RTL-ness if it fits onscreen, use
|
| + // whatever fits otherwise, and use the expected location if neither fits.
|
| + if (left_is_onscreen && (ltr || !right_is_onscreen))
|
| + return ARROW_LOCATION_TOP_LEFT;
|
| + if (right_is_onscreen && (!ltr || !left_is_onscreen))
|
| + return ARROW_LOCATION_TOP_RIGHT;
|
| + return (ltr ? ARROW_LOCATION_TOP_LEFT : ARROW_LOCATION_TOP_RIGHT);
|
| +}
|
| +
|
| +bool InfoBubbleGtk::UpdateArrowLocation() {
|
| + gint toplevel_x = 0, toplevel_y = 0;
|
| + gdk_window_get_position(
|
| + GTK_WIDGET(toplevel_window_)->window, &toplevel_x, &toplevel_y);
|
| +
|
| + ArrowLocationGtk old_location = arrow_location_;
|
| + arrow_location_ = GetArrowLocation(
|
| + toplevel_x + rect_.x() + (rect_.width() / 2), // arrow_x
|
| + window_->allocation.width);
|
| +
|
| + if (arrow_location_ != old_location) {
|
| + UpdateWindowShape();
|
| + MoveWindow();
|
| + // We need to redraw the entire window to repaint its border.
|
| + gtk_widget_queue_draw(window_);
|
| + return true;
|
| + }
|
| + return false;
|
| +}
|
| +
|
| +void InfoBubbleGtk::UpdateWindowShape() {
|
| + if (mask_region_) {
|
| + gdk_region_destroy(mask_region_);
|
| + mask_region_ = NULL;
|
| + }
|
| + std::vector<GdkPoint> points = MakeFramePolygonPoints(
|
| + arrow_location_, window_->allocation.width, window_->allocation.height,
|
| + FRAME_MASK);
|
| + mask_region_ = gdk_region_polygon(&points[0],
|
| + points.size(),
|
| + GDK_EVEN_ODD_RULE);
|
| + gdk_window_shape_combine_region(window_->window, mask_region_, 0, 0);
|
| +}
|
| +
|
| void InfoBubbleGtk::MoveWindow() {
|
| gint toplevel_x = 0, toplevel_y = 0;
|
| gdk_window_get_position(
|
| GTK_WIDGET(toplevel_window_)->window, &toplevel_x, &toplevel_y);
|
|
|
| gint screen_x = 0;
|
| - if (l10n_util::GetTextDirection() == l10n_util::RIGHT_TO_LEFT) {
|
| + if (arrow_location_ == ARROW_LOCATION_TOP_LEFT) {
|
| + screen_x = toplevel_x + rect_.x() + (rect_.width() / 2) - kArrowX;
|
| + } else if (arrow_location_ == ARROW_LOCATION_TOP_RIGHT) {
|
| screen_x = toplevel_x + rect_.x() + (rect_.width() / 2) -
|
| window_->allocation.width + kArrowX;
|
| } else {
|
| - screen_x = toplevel_x + rect_.x() + (rect_.width() / 2) - kArrowX;
|
| + NOTREACHED();
|
| }
|
|
|
| gint screen_y = toplevel_y + rect_.y() + rect_.height() +
|
| @@ -325,23 +365,29 @@ gboolean InfoBubbleGtk::HandleEscape() {
|
| return TRUE;
|
| }
|
|
|
| +gboolean InfoBubbleGtk::HandleExpose() {
|
| + GdkDrawable* drawable = GDK_DRAWABLE(window_->window);
|
| + GdkGC* gc = gdk_gc_new(drawable);
|
| + gdk_gc_set_rgb_fg_color(gc, &kFrameColor);
|
| +
|
| + // Stroke the frame border.
|
| + std::vector<GdkPoint> points = MakeFramePolygonPoints(
|
| + arrow_location_, window_->allocation.width, window_->allocation.height,
|
| + FRAME_STROKE);
|
| + gdk_draw_polygon(drawable, gc, FALSE, &points[0], points.size());
|
| +
|
| + g_object_unref(gc);
|
| + return FALSE; // Propagate so our children paint, etc.
|
| +}
|
| +
|
| // When our size is initially allocated or changed, we need to recompute
|
| // and apply our shape mask region.
|
| void InfoBubbleGtk::HandleSizeAllocate() {
|
| - if (l10n_util::GetTextDirection() == l10n_util::RIGHT_TO_LEFT)
|
| - MoveWindow();
|
| -
|
| - DCHECK(window_->allocation.x == 0 && window_->allocation.y == 0);
|
| - if (mask_region_) {
|
| - gdk_region_destroy(mask_region_);
|
| - mask_region_ = NULL;
|
| + if (!UpdateArrowLocation()) {
|
| + UpdateWindowShape();
|
| + if (arrow_location_ == ARROW_LOCATION_TOP_RIGHT)
|
| + MoveWindow();
|
| }
|
| - std::vector<GdkPoint> points = MakeFramePolygonPoints(
|
| - window_->allocation.width, window_->allocation.height, FRAME_MASK);
|
| - mask_region_ = gdk_region_polygon(&points[0],
|
| - points.size(),
|
| - GDK_EVEN_ODD_RULE);
|
| - gdk_window_shape_combine_region(window_->window, mask_region_, 0, 0);
|
| }
|
|
|
| gboolean InfoBubbleGtk::HandleButtonPress(GdkEventButton* event) {
|
| @@ -374,7 +420,8 @@ gboolean InfoBubbleGtk::HandleDestroy() {
|
| }
|
|
|
| gboolean InfoBubbleGtk::HandleToplevelConfigure(GdkEventConfigure* event) {
|
| - MoveWindow();
|
| + if (!UpdateArrowLocation())
|
| + MoveWindow();
|
| StackWindow();
|
| return FALSE;
|
| }
|
|
|