Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(275)

Unified Diff: chrome/browser/gtk/info_bubble_gtk.cc

Issue 316006: GTK: Try to position info bubbles onscreen. (Closed)
Patch Set: merge Created 11 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « chrome/browser/gtk/info_bubble_gtk.h ('k') | chrome/browser/gtk/location_bar_view_gtk.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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;
}
« no previous file with comments | « chrome/browser/gtk/info_bubble_gtk.h ('k') | chrome/browser/gtk/location_bar_view_gtk.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698