summaryrefslogtreecommitdiffstats
path: root/chrome/browser/gtk
diff options
context:
space:
mode:
authorerg@google.com <erg@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2009-08-28 23:05:33 +0000
committererg@google.com <erg@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2009-08-28 23:05:33 +0000
commitfda418552b5a32755b4414c172f4b5c52615e502 (patch)
tree3f7eedbde68c5feafe3da104e4ef9a7e3a09c0d6 /chrome/browser/gtk
parenta0c878693f22701d83e90ffdd610747ffc8b7111 (diff)
downloadchromium_src-fda418552b5a32755b4414c172f4b5c52615e502.zip
chromium_src-fda418552b5a32755b4414c172f4b5c52615e502.tar.gz
chromium_src-fda418552b5a32755b4414c172f4b5c52615e502.tar.bz2
GTK: Refactoring and rounds the "Search site for:" hint.
Factor out common window rounding code from two places where it was used, generalize it, and use it to round the "Search site for:" hint in the location bar. Theoretically, people should be able to use this anywhere they need a window to have rounded corners. http://crbug.com/18310 Review URL: http://codereview.chromium.org/179026 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@24828 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/gtk')
-rw-r--r--chrome/browser/gtk/blocked_popup_container_view_gtk.cc102
-rw-r--r--chrome/browser/gtk/blocked_popup_container_view_gtk.h12
-rw-r--r--chrome/browser/gtk/location_bar_view_gtk.cc18
-rw-r--r--chrome/browser/gtk/location_bar_view_gtk.h1
-rw-r--r--chrome/browser/gtk/rounded_window.cc213
-rw-r--r--chrome/browser/gtk/rounded_window.h46
-rw-r--r--chrome/browser/gtk/status_bubble_gtk.cc92
-rw-r--r--chrome/browser/gtk/status_bubble_gtk.h12
8 files changed, 298 insertions, 198 deletions
diff --git a/chrome/browser/gtk/blocked_popup_container_view_gtk.cc b/chrome/browser/gtk/blocked_popup_container_view_gtk.cc
index b48b93e..925c390 100644
--- a/chrome/browser/gtk/blocked_popup_container_view_gtk.cc
+++ b/chrome/browser/gtk/blocked_popup_container_view_gtk.cc
@@ -10,6 +10,7 @@
#include "chrome/browser/gtk/custom_button.h"
#include "chrome/browser/gtk/gtk_chrome_button.h"
#include "chrome/browser/gtk/gtk_theme_provider.h"
+#include "chrome/browser/gtk/rounded_window.h"
#include "chrome/browser/profile.h"
#include "chrome/browser/tab_contents/tab_contents.h"
#include "chrome/browser/tab_contents/tab_contents_view_gtk.h"
@@ -32,48 +33,6 @@ const double kBackgroundColorBottom[] = { 219.0 / 255, 235.0 / 255, 1.0 };
// Rounded corner radius (in pixels).
const int kCornerSize = 4;
-enum FrameType {
- FRAME_MASK,
- FRAME_STROKE,
-};
-
-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;
-
- // Bottom left corner.
- points.push_back(MakeBidiGdkPoint(0, height + y_off, width, ltr));
-
- // Top left (rounded) corner.
- points.push_back(MakeBidiGdkPoint(x_off_r, kCornerSize - 1, width, ltr));
- points.push_back(MakeBidiGdkPoint(kCornerSize + x_off_r - 1, 0, width, ltr));
-
- // Top right (rounded) corner.
- points.push_back(MakeBidiGdkPoint(
- width - kCornerSize + 1 + x_off_l, 0, width, ltr));
- points.push_back(MakeBidiGdkPoint(
- width + x_off_l, kCornerSize - 1, width, ltr));
-
- // Bottom right corner.
- points.push_back(MakeBidiGdkPoint(
- width + x_off_l, height + y_off, width, ltr));
-
- return points;
-}
-
} // namespace
// static
@@ -150,10 +109,13 @@ void BlockedPopupContainerViewGtk::Observe(NotificationType type,
GtkWidget* label = gtk_bin_get_child(GTK_BIN(menu_button_));
if (theme_provider_->UseGtkTheme()) {
gtk_util::SetLabelColor(label, NULL);
+ GdkColor color = theme_provider_->GetBorderColor();
+ gtk_util::SetRoundedWindowBorderColor(container_.get(), color);
} else {
GdkColor color = theme_provider_->GetGdkColor(
BrowserThemeProvider::COLOR_BOOKMARK_TEXT);
gtk_util::SetLabelColor(label, &color);
+ gtk_util::SetRoundedWindowBorderColor(container_.get(), kBorderColor);
}
}
@@ -189,9 +151,7 @@ BlockedPopupContainerViewGtk::BlockedPopupContainerViewGtk(
BlockedPopupContainer* container)
: model_(container),
theme_provider_(GtkThemeProvider::GetFrom(container->profile())),
- close_button_(CustomDrawButton::CloseButton(theme_provider_)),
- notification_width_(-1),
- notification_height_(-1) {
+ close_button_(CustomDrawButton::CloseButton(theme_provider_)) {
Init();
registrar_.Add(this,
@@ -214,10 +174,14 @@ void BlockedPopupContainerViewGtk::Init() {
container_.Own(gtk_util::CreateGtkBorderBin(hbox, NULL,
kSmallPadding, kSmallPadding, kSmallPadding, kSmallPadding));
- // Manually paint the event box.
- gtk_widget_set_app_paintable(container_.get(), TRUE);
+ // Connect an expose signal that draws the background. Most connect before
+ // the ActAsRoundedWindow one.
g_signal_connect(container_.get(), "expose-event",
- G_CALLBACK(OnContainerExpose), this);
+ G_CALLBACK(OnRoundedExposeCallback), this);
+ gtk_util::ActAsRoundedWindow(
+ container_.get(), kBorderColor, kCornerSize,
+ gtk_util::ROUNDED_TOP_LEFT | gtk_util::ROUNDED_TOP_RIGHT,
+ gtk_util::BORDER_LEFT | gtk_util::BORDER_TOP | gtk_util::BORDER_RIGHT);
ContainingView()->AttachBlockedPopupView(this);
}
@@ -258,30 +222,13 @@ void BlockedPopupContainerViewGtk::OnCloseButtonClicked(
container->model_->CloseAll();
}
-gboolean BlockedPopupContainerViewGtk::OnContainerExpose(
+gboolean BlockedPopupContainerViewGtk::OnRoundedExposeCallback(
GtkWidget* widget, GdkEventExpose* event,
BlockedPopupContainerViewGtk* container) {
- int width = widget->allocation.width;
- int height = widget->allocation.height;
-
- // Update our window shape if we need to.
- if (container->notification_width_ != widget->allocation.width ||
- container->notification_height_ != widget->allocation.height) {
- // We need to update the shape of the status bubble whenever our GDK
- // window changes shape.
- std::vector<GdkPoint> mask_points = MakeFramePolygonPoints(
- widget->allocation.width, widget->allocation.height, FRAME_MASK);
- GdkRegion* mask_region = gdk_region_polygon(&mask_points[0],
- mask_points.size(),
- GDK_EVEN_ODD_RULE);
- gdk_window_shape_combine_region(widget->window, mask_region, 0, 0);
- gdk_region_destroy(mask_region);
-
- container->notification_width_ = widget->allocation.width;
- container->notification_height_ = widget->allocation.height;
- }
-
if (!container->theme_provider_->UseGtkTheme()) {
+ int width = widget->allocation.width;
+ int height = widget->allocation.height;
+
// Clip to our damage rect.
cairo_t* cr = gdk_cairo_create(GDK_DRAWABLE(event->window));
cairo_rectangle(cr, event->area.x, event->area.y,
@@ -309,20 +256,5 @@ gboolean BlockedPopupContainerViewGtk::OnContainerExpose(
cairo_destroy(cr);
}
- GdkDrawable* drawable = GDK_DRAWABLE(event->window);
- GdkGC* gc = gdk_gc_new(drawable);
- if (container->theme_provider_->UseGtkTheme()) {
- GdkColor color = container->theme_provider_->GetBorderColor();
- gdk_gc_set_rgb_fg_color(gc, &color);
- } else {
- gdk_gc_set_rgb_fg_color(gc, &kBorderColor);
- }
-
- // Stroke the frame border.
- std::vector<GdkPoint> points = MakeFramePolygonPoints(
- widget->allocation.width, widget->allocation.height, FRAME_STROKE);
- gdk_draw_lines(drawable, gc, &points[0], points.size());
-
- g_object_unref(gc);
- return FALSE; // Allow subwidgets to paint.
+ return FALSE;
}
diff --git a/chrome/browser/gtk/blocked_popup_container_view_gtk.h b/chrome/browser/gtk/blocked_popup_container_view_gtk.h
index f8de623..50a256b3 100644
--- a/chrome/browser/gtk/blocked_popup_container_view_gtk.h
+++ b/chrome/browser/gtk/blocked_popup_container_view_gtk.h
@@ -81,9 +81,10 @@ class BlockedPopupContainerViewGtk : public BlockedPopupContainerView,
static void OnCloseButtonClicked(GtkButton *button,
BlockedPopupContainerViewGtk* container);
- // Draws |container_| with a custom background.
- static gboolean OnContainerExpose(GtkWidget* widget, GdkEventExpose* event,
- BlockedPopupContainerViewGtk* container);
+ // Draw the custom background to our rounded widget.
+ static gboolean OnRoundedExposeCallback(
+ GtkWidget* widget, GdkEventExpose* event,
+ BlockedPopupContainerViewGtk* container);
NotificationRegistrar registrar_;
@@ -105,11 +106,6 @@ class BlockedPopupContainerViewGtk : public BlockedPopupContainerView,
// The popup menu with options to launch blocked popups.
scoped_ptr<MenuGtk> launch_menu_;
- // Cached allocation of |container_|. We keep this on hand so that we can
- // reset the widget's shape when the width/height change.
- int notification_width_;
- int notification_height_;
-
DISALLOW_COPY_AND_ASSIGN(BlockedPopupContainerViewGtk);
};
diff --git a/chrome/browser/gtk/location_bar_view_gtk.cc b/chrome/browser/gtk/location_bar_view_gtk.cc
index 60e9ea8..d99b27a 100644
--- a/chrome/browser/gtk/location_bar_view_gtk.cc
+++ b/chrome/browser/gtk/location_bar_view_gtk.cc
@@ -19,6 +19,7 @@
#include "chrome/browser/command_updater.h"
#include "chrome/browser/gtk/first_run_bubble.h"
#include "chrome/browser/gtk/gtk_theme_provider.h"
+#include "chrome/browser/gtk/rounded_window.h"
#include "chrome/browser/profile.h"
#include "chrome/browser/search_engines/template_url.h"
#include "chrome/browser/search_engines/template_url_model.h"
@@ -74,6 +75,9 @@ const GdkColor kEvTextColor = GDK_COLOR_RGB(0x00, 0x96, 0x14); // Green.
const GdkColor kKeywordBackgroundColor = GDK_COLOR_RGB(0xf0, 0xf4, 0xfa);
const GdkColor kKeywordBorderColor = GDK_COLOR_RGB(0xcb, 0xde, 0xf7);
+// Size of the rounding of the "Search site for:" box.
+const int kCornerSize = 3;
+
// Returns the short name for a keyword.
std::wstring GetKeywordName(Profile* profile,
const std::wstring& keyword) {
@@ -104,7 +108,6 @@ LocationBarViewGtk::LocationBarViewGtk(CommandUpdater* command_updater,
info_label_align_(NULL),
info_label_(NULL),
tab_to_search_(NULL),
- tab_to_search_border_(NULL),
tab_to_search_box_(NULL),
tab_to_search_label_(NULL),
tab_to_search_hint_(NULL),
@@ -174,9 +177,9 @@ void LocationBarViewGtk::Init(bool popup_window_mode) {
// keyword text with a border, background color, and padding around the text.
tab_to_search_box_ = gtk_util::CreateGtkBorderBin(
tab_to_search_label_, NULL, 1, 1, 2, 2);
- tab_to_search_border_ = gtk_util::CreateGtkBorderBin(
- tab_to_search_box_, NULL, 1, 1, 1, 1);
- gtk_container_add(GTK_CONTAINER(tab_to_search_), tab_to_search_border_);
+ gtk_util::ActAsRoundedWindow(tab_to_search_box_, kBorderColor, kCornerSize,
+ gtk_util::ROUNDED_ALL, gtk_util::BORDER_ALL);
+ gtk_container_add(GTK_CONTAINER(tab_to_search_), tab_to_search_box_);
gtk_box_pack_start(GTK_BOX(hbox_.get()), tab_to_search_, FALSE, FALSE, 0);
GtkWidget* align = gtk_alignment_new(0.0, 0.0, 1.0, 1.0);
@@ -418,8 +421,7 @@ void LocationBarViewGtk::Observe(NotificationType type,
GdkColor border_color = theme_provider_->GetGdkColor(
BrowserThemeProvider::COLOR_FRAME);
- gtk_widget_modify_bg(tab_to_search_border_, GTK_STATE_NORMAL,
- &border_color);
+ gtk_util::SetRoundedWindowBorderColor(tab_to_search_box_, border_color);
gtk_util::SetLabelColor(tab_to_search_label_, NULL);
gtk_util::SetLabelColor(tab_to_search_hint_leading_label_, NULL);
@@ -427,8 +429,8 @@ void LocationBarViewGtk::Observe(NotificationType type,
} else {
gtk_widget_modify_bg(tab_to_search_box_, GTK_STATE_NORMAL,
&kKeywordBackgroundColor);
- gtk_widget_modify_bg(tab_to_search_border_, GTK_STATE_NORMAL,
- &kKeywordBorderColor);
+ gtk_util::SetRoundedWindowBorderColor(tab_to_search_box_,
+ kKeywordBorderColor);
gtk_util::SetLabelColor(tab_to_search_label_, &gfx::kGdkBlack);
gtk_util::SetLabelColor(tab_to_search_hint_leading_label_,
diff --git a/chrome/browser/gtk/location_bar_view_gtk.h b/chrome/browser/gtk/location_bar_view_gtk.h
index 8105302..ebf7035 100644
--- a/chrome/browser/gtk/location_bar_view_gtk.h
+++ b/chrome/browser/gtk/location_bar_view_gtk.h
@@ -131,7 +131,6 @@ class LocationBarViewGtk : public AutocompleteEditController,
// Area on the left shown when in tab to search mode.
GtkWidget* tab_to_search_;
- GtkWidget* tab_to_search_border_;
GtkWidget* tab_to_search_box_;
GtkWidget* tab_to_search_label_;
diff --git a/chrome/browser/gtk/rounded_window.cc b/chrome/browser/gtk/rounded_window.cc
new file mode 100644
index 0000000..b549a3f
--- /dev/null
+++ b/chrome/browser/gtk/rounded_window.cc
@@ -0,0 +1,213 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/gtk/rounded_window.h"
+
+#include <gtk/gtk.h>
+
+#include "app/l10n_util.h"
+#include "chrome/common/gtk_util.h"
+
+namespace gtk_util {
+
+namespace {
+
+const char* kRoundedData = "rounded-window-data";
+
+struct RoundedWindowData {
+ // Expected window size. Used to detect when we need to reshape the window.
+ int expected_width;
+ int expected_height;
+
+ // Color of the border.
+ GdkColor border_color;
+
+ // Radius of the edges in pixels.
+ int corner_size;
+
+ // Which corners should be rounded?
+ int rounded_edges;
+
+ // Which sides of the window should have an internal border?
+ int drawn_borders;
+};
+
+// Callback from GTK to release allocated memory.
+void FreeRoundedWindowData(gpointer data) {
+ delete static_cast<RoundedWindowData*>(data);
+}
+
+enum FrameType {
+ FRAME_MASK,
+ FRAME_STROKE,
+};
+
+// Returns a list of points that either form the outline of the status bubble
+// (|type| == FRAME_MASK) or form the inner border around the inner edge
+// (|type| == FRAME_STROKE).
+std::vector<GdkPoint> MakeFramePolygonPoints(RoundedWindowData* data,
+ FrameType type) {
+ using gtk_util::MakeBidiGdkPoint;
+ int width = data->expected_width;
+ int height = data->expected_height;
+ int corner_size = data->corner_size;
+
+ 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;
+
+ // Bottom left corner.
+ if (type == FRAME_MASK ||
+ (data->drawn_borders & (BORDER_LEFT | BORDER_BOTTOM))) {
+ if (data->rounded_edges & ROUNDED_BOTTOM_LEFT) {
+ points.push_back(MakeBidiGdkPoint(
+ corner_size + x_off_l, height + y_off, width, ltr));
+ points.push_back(MakeBidiGdkPoint(
+ x_off_r, height - corner_size, width, ltr));
+ } else {
+ points.push_back(MakeBidiGdkPoint(x_off_r, height + y_off, width, ltr));
+ }
+ }
+
+ // Top left corner.
+ if (type == FRAME_MASK ||
+ (data->drawn_borders & (BORDER_LEFT | BORDER_TOP))) {
+ if (data->rounded_edges & ROUNDED_TOP_LEFT) {
+ points.push_back(MakeBidiGdkPoint(
+ x_off_r, corner_size - 1, width, ltr));
+ points.push_back(MakeBidiGdkPoint(
+ corner_size + x_off_r - 1, 0, width, ltr));
+ } else {
+ points.push_back(MakeBidiGdkPoint(x_off_r, 0, width, ltr));
+ }
+ }
+
+ // Top right corner.
+ if (type == FRAME_MASK ||
+ (data->drawn_borders & (BORDER_TOP | BORDER_RIGHT))) {
+ if (data->rounded_edges & ROUNDED_TOP_RIGHT) {
+ points.push_back(MakeBidiGdkPoint(
+ width - corner_size + 1 + x_off_l, 0, width, ltr));
+ points.push_back(MakeBidiGdkPoint(
+ width + x_off_l, corner_size - 1, width, ltr));
+ } else {
+ points.push_back(MakeBidiGdkPoint(
+ width + x_off_l, 0, width, ltr));
+ }
+ }
+
+ // Bottom right corner.
+ if (type == FRAME_MASK ||
+ (data->drawn_borders & (BORDER_RIGHT | BORDER_BOTTOM))) {
+ if (data->rounded_edges & ROUNDED_BOTTOM_RIGHT) {
+ points.push_back(MakeBidiGdkPoint(
+ width + x_off_l, height - corner_size, width, ltr));
+ points.push_back(MakeBidiGdkPoint(
+ width - corner_size + x_off_r, height + y_off, width, ltr));
+ } else {
+ points.push_back(MakeBidiGdkPoint(
+ width + x_off_l, height + y_off, width, ltr));
+ }
+ }
+
+ return points;
+}
+
+// Set the window shape in needed, lets our owner do some drawing (if it wants
+// to), and finally draw the border.
+gboolean OnRoundedWindowExpose(GtkWidget* widget,
+ GdkEventExpose* event) {
+ RoundedWindowData* data = static_cast<RoundedWindowData*>(
+ g_object_get_data(G_OBJECT(widget), kRoundedData));
+
+ if (data->expected_width != widget->allocation.width ||
+ data->expected_height != widget->allocation.height) {
+ data->expected_width = widget->allocation.width;
+ data->expected_height = widget->allocation.height;
+
+ // We need to update the shape of the status bubble whenever our GDK
+ // window changes shape.
+ std::vector<GdkPoint> mask_points = MakeFramePolygonPoints(
+ data, FRAME_MASK);
+ GdkRegion* mask_region = gdk_region_polygon(&mask_points[0],
+ mask_points.size(),
+ GDK_EVEN_ODD_RULE);
+ gdk_window_shape_combine_region(widget->window, mask_region, 0, 0);
+ gdk_region_destroy(mask_region);
+ }
+
+ GdkDrawable* drawable = GDK_DRAWABLE(event->window);
+ GdkGC* gc = gdk_gc_new(drawable);
+ gdk_gc_set_rgb_fg_color(gc, &data->border_color);
+
+ // Stroke the frame border.
+ std::vector<GdkPoint> points = MakeFramePolygonPoints(
+ data, FRAME_STROKE);
+ if (data->drawn_borders == BORDER_ALL) {
+ // If we want to have borders everywhere, we need to draw a polygon instead
+ // of a set of lines.
+ gdk_draw_polygon(drawable, gc, FALSE, &points[0], points.size());
+ } else {
+ gdk_draw_lines(drawable, gc, &points[0], points.size());
+ }
+
+ g_object_unref(gc);
+ return FALSE; // Propagate so our children paint, etc.
+}
+
+// On theme changes, window shapes are reset, but we detect whether we need to
+// reshape a window by whether its allocation has changed so force it to reset
+// the window shape on next expose.
+void OnStyleSet(GtkWidget* widget, GtkStyle* previous_style) {
+ DCHECK(widget);
+ RoundedWindowData* data = static_cast<RoundedWindowData*>(
+ g_object_get_data(G_OBJECT(widget), kRoundedData));
+ DCHECK(data);
+ data->expected_width = -1;
+ data->expected_height = -1;
+}
+
+} // namespace
+
+void ActAsRoundedWindow(
+ GtkWidget* widget, GdkColor color, int corner_size,
+ int rounded_edges, int drawn_borders) {
+ DCHECK(widget);
+ gtk_widget_set_app_paintable(widget, TRUE);
+ g_signal_connect(G_OBJECT(widget), "expose-event",
+ G_CALLBACK(OnRoundedWindowExpose), NULL);
+ g_signal_connect(G_OBJECT(widget), "style-set", G_CALLBACK(OnStyleSet), NULL);
+
+ RoundedWindowData* data = new RoundedWindowData;
+ data->expected_width = -1;
+ data->expected_height = -1;
+
+ data->border_color = color;
+ data->corner_size = corner_size;
+
+ data->rounded_edges = rounded_edges;
+ data->drawn_borders = drawn_borders;
+
+ g_object_set_data_full(G_OBJECT(widget), kRoundedData,
+ data, FreeRoundedWindowData);
+}
+
+void SetRoundedWindowBorderColor(GtkWidget* widget, GdkColor color) {
+ DCHECK(widget);
+ RoundedWindowData* data = static_cast<RoundedWindowData*>(
+ g_object_get_data(G_OBJECT(widget), kRoundedData));
+ DCHECK(data);
+ data->border_color = color;
+}
+
+} // namespace gtk_util
diff --git a/chrome/browser/gtk/rounded_window.h b/chrome/browser/gtk/rounded_window.h
new file mode 100644
index 0000000..d72991a
--- /dev/null
+++ b/chrome/browser/gtk/rounded_window.h
@@ -0,0 +1,46 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_GTK_ROUNDED_WINDOW_H_
+#define CHROME_BROWSER_GTK_ROUNDED_WINDOW_H_
+
+#include <gtk/gtk.h>
+
+namespace gtk_util {
+
+// Symbolic names for arguments to |rounded_edges| in ActAsRoundedWindow().
+enum RoundedBorders {
+ ROUNDED_BOTTOM_LEFT = 1 << 0,
+ ROUNDED_TOP_LEFT = 1 << 1,
+ ROUNDED_TOP_RIGHT = 1 << 2,
+ ROUNDED_BOTTOM_RIGHT = 1 << 3,
+ ROUNDED_ALL = 0xF
+};
+
+// Symbolic names for arguments to |drawn_borders| in ActAsRoundedWindow().
+enum BorderEdge {
+ BORDER_LEFT = 1 << 0,
+ BORDER_TOP = 1 << 1,
+ BORDER_RIGHT = 1 << 2,
+ BORDER_BOTTOM = 1 << 3,
+ BORDER_ALL = 0xF
+};
+
+// Sets up the passed in widget that has its own GdkWindow with an expose
+// handler that forces the window shape into roundness. Caller should not set
+// an "expose-event" handler on |widget|; if caller needs to do custom
+// rendering, use SetRoundedWindowExposeFunction() instead. |rounded_edges|
+// control which corners are rounded. |drawn_borders| border control which
+// sides have a visible border drawn in |color|.
+void ActAsRoundedWindow(
+ GtkWidget* widget, GdkColor color, int corner_size,
+ int rounded_edges, int drawn_borders);
+
+// Sets the color of the border on a widget that was returned from
+// ActAsRoundedWindow().
+void SetRoundedWindowBorderColor(GtkWidget* widget, GdkColor color);
+
+} // namespace gtk_util
+
+#endif // CHROME_BROWSER_GTK_ROUNDED_WINDOW_H_
diff --git a/chrome/browser/gtk/status_bubble_gtk.cc b/chrome/browser/gtk/status_bubble_gtk.cc
index e21f581..155edeb 100644
--- a/chrome/browser/gtk/status_bubble_gtk.cc
+++ b/chrome/browser/gtk/status_bubble_gtk.cc
@@ -12,6 +12,7 @@
#include "base/message_loop.h"
#include "base/string_util.h"
#include "chrome/browser/gtk/gtk_theme_provider.h"
+#include "chrome/browser/gtk/rounded_window.h"
#include "chrome/browser/gtk/slide_animator_gtk.h"
#include "chrome/common/gtk_util.h"
#include "chrome/common/notification_service.h"
@@ -31,56 +32,10 @@ const int kCornerSize = 3;
// Milliseconds before we hide the status bubble widget when you mouseout.
const int kHideDelay = 250;
-enum FrameType {
- FRAME_MASK,
- FRAME_STROKE,
-};
-
-// Returns a list of points that either form the outline of the status bubble
-// (|type| == FRAME_MASK) or form the inner border around the inner edge
-// (|type| == FRAME_STROKE).
-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;
-
- // Top left corner.
- points.push_back(MakeBidiGdkPoint(0, 0, width, ltr));
-
- // Top right (rounded) corner.
- points.push_back(MakeBidiGdkPoint(
- width - kCornerSize + 1 + x_off_l, 0, width, ltr));
- points.push_back(MakeBidiGdkPoint(
- width + x_off_l, kCornerSize - 1, width, ltr));
-
- // Bottom right corner.
- points.push_back(MakeBidiGdkPoint(
- width + x_off_l, height + y_off, width, ltr));
-
- if (type == FRAME_MASK) {
- // Bottom left corner.
- points.push_back(MakeBidiGdkPoint(0, height + y_off, width, ltr));
- }
-
- return points;
-}
-
} // namespace
StatusBubbleGtk::StatusBubbleGtk(Profile* profile)
: theme_provider_(GtkThemeProvider::GetFrom(profile)),
- bubble_width_(-1),
- bubble_height_(-1),
timer_factory_(this) {
InitWidgets();
@@ -188,11 +143,12 @@ void StatusBubbleGtk::InitWidgets() {
gtk_container_add(GTK_CONTAINER(padding), label_);
container_.Own(gtk_event_box_new());
+ gtk_util::ActAsRoundedWindow(
+ container_.get(), kFrameBorderColor, kCornerSize,
+ gtk_util::ROUNDED_TOP_RIGHT,
+ gtk_util::BORDER_TOP | gtk_util::BORDER_RIGHT);
gtk_widget_set_name(container_.get(), "status-bubble");
gtk_container_add(GTK_CONTAINER(container_.get()), padding);
- gtk_widget_set_app_paintable(container_.get(), TRUE);
- g_signal_connect(G_OBJECT(container_.get()), "expose-event",
- G_CALLBACK(OnExpose), this);
UserChangedTheme();
}
@@ -202,7 +158,8 @@ void StatusBubbleGtk::UserChangedTheme() {
gtk_widget_modify_fg(label_, GTK_STATE_NORMAL, NULL);
gtk_widget_modify_bg(container_.get(), GTK_STATE_NORMAL, NULL);
- border_color_ = theme_provider_->GetBorderColor();
+ gtk_util::SetRoundedWindowBorderColor(container_.get(),
+ theme_provider_->GetBorderColor());
} else {
// TODO(erg): This is the closest to "text that will look good on a
// toolbar" that I can find. Maybe in later iterations of the theme system,
@@ -215,39 +172,6 @@ void StatusBubbleGtk::UserChangedTheme() {
theme_provider_->GetGdkColor(BrowserThemeProvider::COLOR_TOOLBAR);
gtk_widget_modify_bg(container_.get(), GTK_STATE_NORMAL, &toolbar_color);
- border_color_ = kFrameBorderColor;
- }
-}
-
-// static
-gboolean StatusBubbleGtk::OnExpose(GtkWidget* widget,
- GdkEventExpose* event,
- StatusBubbleGtk* bubble) {
- if (bubble->bubble_width_ != widget->allocation.width ||
- bubble->bubble_height_ != widget->allocation.height) {
- // We need to update the shape of the status bubble whenever our GDK
- // window changes shape.
- std::vector<GdkPoint> mask_points = MakeFramePolygonPoints(
- widget->allocation.width, widget->allocation.height, FRAME_MASK);
- GdkRegion* mask_region = gdk_region_polygon(&mask_points[0],
- mask_points.size(),
- GDK_EVEN_ODD_RULE);
- gdk_window_shape_combine_region(widget->window, mask_region, 0, 0);
- gdk_region_destroy(mask_region);
-
- bubble->bubble_width_ = widget->allocation.width;
- bubble->bubble_height_ = widget->allocation.height;
+ gtk_util::SetRoundedWindowBorderColor(container_.get(), kFrameBorderColor);
}
-
- GdkDrawable* drawable = GDK_DRAWABLE(event->window);
- GdkGC* gc = gdk_gc_new(drawable);
- gdk_gc_set_rgb_fg_color(gc, &bubble->border_color_);
-
- // Stroke the frame border.
- std::vector<GdkPoint> points = MakeFramePolygonPoints(
- widget->allocation.width, widget->allocation.height, FRAME_STROKE);
- gdk_draw_lines(drawable, gc, &points[0], points.size());
-
- g_object_unref(gc);
- return FALSE; // Propagate so our children paint, etc.
}
diff --git a/chrome/browser/gtk/status_bubble_gtk.h b/chrome/browser/gtk/status_bubble_gtk.h
index c1a8ede..87b43d3 100644
--- a/chrome/browser/gtk/status_bubble_gtk.h
+++ b/chrome/browser/gtk/status_bubble_gtk.h
@@ -69,10 +69,6 @@ class StatusBubbleGtk : public StatusBubble,
// Notification from the window that we should retheme ourself.
void UserChangedTheme();
- // Draws the inside border.
- static gboolean OnExpose(GtkWidget* widget, GdkEventExpose* event,
- StatusBubbleGtk* bubble);
-
NotificationRegistrar registrar_;
// Provides colors.
@@ -90,14 +86,6 @@ class StatusBubbleGtk : public StatusBubble,
// The url we want to display when there is no status text to display.
std::string url_text_;
- // Color of the lighter border around the edge of the status bubble.
- GdkColor border_color_;
-
- // Cached allocation of |container_|. We keep this on hand so that we can
- // reset the widget's shape when the width/height change.
- int bubble_width_;
- int bubble_height_;
-
// A timer that hides our window after a delay.
ScopedRunnableMethodFactory<StatusBubbleGtk> timer_factory_;
};