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

Side by Side Diff: chrome/browser/gtk/info_bubble_gtk.cc

Issue 100203: Implement a mostly working InfoBubble with a shim BookmarkBubble. (Closed)
Patch Set: Comments Created 11 years, 7 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 unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright (c) 2009 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/gtk/info_bubble_gtk.h"
6
7 #include <gtk/gtk.h>
8
9 #include "base/basictypes.h"
10 #include "base/gfx/gtk_util.h"
11 #include "base/gfx/rect.h"
12 #include "base/logging.h"
13 #include "chrome/common/gfx/path.h"
14
15 namespace {
16
17 // The height of the arrow, and the width will be about twice the height.
18 const int kArrowSize = 5;
19 // Number of pixels to the start of the arrow from the edge of the window.
20 const int kArrowX = 13;
21 // Number of pixels between the tip of the arrow and the region we're
22 // pointing to.
23 const int kArrowToContentPadding = -6;
24 // We draw flat diagonal corners, each corner is an NxN square.
25 const int kCornerSize = 3;
26 // Margins around the content.
27 const int kTopMargin = kArrowSize + kCornerSize + 6;
28 const int kBottomMargin = kCornerSize + 6;
29 const int kLeftMargin = kCornerSize + 6;
30 const int kRightMargin = kCornerSize + 6;
31
32 const GdkColor kBackgroundColor = GDK_COLOR_RGB(0xff, 0xff, 0xff);
33 const GdkColor kFrameColor = GDK_COLOR_RGB(0x63, 0x63, 0x63);
34
35 // A small convenience since GdkPoint is a POD without a constructor.
36 GdkPoint MakeGdkPoint(gint x, gint y) {
37 GdkPoint point = {x, y};
38 return point;
39 }
40
41 enum FrameType {
42 FRAME_MASK,
43 FRAME_STROKE,
44 };
45
46 // Make the points for our polygon frame, either for fill (the mask), or for
47 // when we stroke the border. NOTE: This seems a bit overcomplicated, but it
48 // requires a bunch of careful fudging to get the pixels rasterized exactly
49 // where we want them, the arrow to have a 1 pixel point, etc.
50 // TODO(deanm): Windows draws with Skia and uses some PNG images for the
51 // corners. This is a lot more work, but they get anti-aliasing.
52 std::vector<GdkPoint> MakeFramePolygonPoints(int width,
53 int height,
54 FrameType type) {
55 std::vector<GdkPoint> points;
56
57 // If we have a stroke, we have to offset some of our points by 1 pixel.
58 int off = (type == FRAME_MASK) ? 0 : 1;
59
60 // Top left corner.
61 points.push_back(MakeGdkPoint(0, kArrowSize + kCornerSize - 1));
62 points.push_back(MakeGdkPoint(kCornerSize - 1, kArrowSize));
63
64 // The arrow.
65 points.push_back(MakeGdkPoint(kArrowX - kArrowSize, kArrowSize));
66 points.push_back(MakeGdkPoint(kArrowX, 0));
67 points.push_back(MakeGdkPoint(kArrowX + 1 - off, 0));
68 points.push_back(MakeGdkPoint(kArrowX + kArrowSize + 1 - off, kArrowSize));
69
70 // Top right corner.
71 points.push_back(MakeGdkPoint(width - kCornerSize + 1 - off, kArrowSize));
72 points.push_back(MakeGdkPoint(width - off, kArrowSize + kCornerSize - 1));
73
74 // Bottom right corner.
75 points.push_back(MakeGdkPoint(width - off, height - kCornerSize));
76 points.push_back(MakeGdkPoint(width - kCornerSize, height - off));
77
78 // Bottom left corner.
79 points.push_back(MakeGdkPoint(kCornerSize - off, height - off));
80 points.push_back(MakeGdkPoint(0, height - kCornerSize));
81
82 return points;
83 }
84
85 // When our size is initially allocated or changed, we need to recompute
86 // and apply our shape mask region.
87 void HandleSizeAllocate(GtkWidget* widget,
88 GtkAllocation* allocation,
89 gpointer unused) {
90 DCHECK(allocation->x == 0 && allocation->y == 0);
91 std::vector<GdkPoint> points = MakeFramePolygonPoints(
92 allocation->width, allocation->height, FRAME_MASK);
93 GdkRegion* mask_region = gdk_region_polygon(&points[0],
94 points.size(),
95 GDK_EVEN_ODD_RULE);
96 gdk_window_shape_combine_region(widget->window, mask_region, 0, 0);
97 gdk_region_destroy(mask_region);
98 }
99
100 gboolean HandleExpose(GtkWidget* widget,
101 GdkEventExpose* event,
102 gpointer unused) {
103 GdkDrawable* drawable = GDK_DRAWABLE(event->window);
104 GdkGC* gc = gdk_gc_new(drawable);
105 gdk_gc_set_rgb_fg_color(gc, &kFrameColor);
106
107 // Stroke the frame border.
108 std::vector<GdkPoint> points = MakeFramePolygonPoints(
109 widget->allocation.width, widget->allocation.height, FRAME_STROKE);
110 gdk_draw_polygon(drawable, gc, FALSE, &points[0], points.size());
111
112 g_object_unref(gc);
113 return FALSE; // Propagate so our children paint, etc.
114 }
115
116 } // namespace
117
118 // static
119 InfoBubbleGtk* InfoBubbleGtk::Show(const gfx::Rect& rect, GtkWidget* content) {
120 InfoBubbleGtk* bubble = new InfoBubbleGtk();
121 bubble->Init(rect, content);
122 return bubble;
123 }
124
125 InfoBubbleGtk::InfoBubbleGtk()
126 : window_(NULL),
127 screen_x_(0),
128 screen_y_(0),
129 closed_(false) {
130 }
131
132 InfoBubbleGtk::~InfoBubbleGtk() {
133 }
134
135 void InfoBubbleGtk::Init(const gfx::Rect& rect, GtkWidget* content) {
136 DCHECK(!window_);
137 screen_x_ = rect.x() + (rect.width() / 2) - kArrowX;
138 screen_y_ = rect.y() + rect.height() + kArrowToContentPadding;
139
140 window_ = gtk_window_new(GTK_WINDOW_TOPLEVEL);
141 gtk_window_set_decorated(GTK_WINDOW(window_), TRUE);
142 gtk_window_set_resizable(GTK_WINDOW(window_), FALSE);
143 gtk_widget_set_app_paintable(window_, TRUE);
144 // Have GTK double buffer around the expose signal.
145 gtk_widget_set_double_buffered(window_, TRUE);
146 // Set the background color, so we don't need to paint it manually.
147 gtk_widget_modify_bg(window_, GTK_STATE_NORMAL, &kBackgroundColor);
148 // Make sure that our window can be focused.
149 GTK_WIDGET_SET_FLAGS(window_, GTK_CAN_FOCUS);
150
151 GtkWidget* alignment = gtk_alignment_new(0.0, 0.0, 1.0, 1.0);
152 gtk_alignment_set_padding(GTK_ALIGNMENT(alignment),
153 kTopMargin, kBottomMargin,
154 kLeftMargin, kRightMargin);
155
156 gtk_container_add(GTK_CONTAINER(alignment), content);
157 gtk_container_add(GTK_CONTAINER(window_), alignment);
158
159 // GtkWidget only exposes the bitmap mask interface. Use GDK to more
160 // efficently mask a GdkRegion. Make sure the window is realized during
161 // HandleSizeAllocate, so the mask can be applied to the GdkWindow.
162 gtk_widget_realize(window_);
163 gtk_window_move(GTK_WINDOW(window_), screen_x_, screen_y_);
164
165 gtk_widget_add_events(window_, GDK_BUTTON_PRESS_MASK |
166 GDK_BUTTON_RELEASE_MASK);
167
168 g_signal_connect(window_, "size-allocate",
169 G_CALLBACK(HandleSizeAllocate), NULL);
170 g_signal_connect(window_, "expose-event",
171 G_CALLBACK(HandleExpose), NULL);
172 g_signal_connect(window_, "configure-event",
173 G_CALLBACK(&HandleConfigureThunk), this);
174 g_signal_connect(window_, "button-press-event",
175 G_CALLBACK(&HandleButtonPressThunk), this);
176
177 gtk_widget_show_all(window_);
178 gtk_window_present(GTK_WINDOW(window_));
179 gtk_grab_add(window_);
180 }
181
182 void InfoBubbleGtk::Close() {
183 DCHECK(!closed_);
184 DCHECK(window_);
185 gtk_widget_destroy(window_);
186 window_ = NULL;
187 closed_ = true;
188 }
189
190 gboolean InfoBubbleGtk::HandleConfigure(GdkEventConfigure* event) {
191 // If the window is moved someplace besides where we want it, move it back.
192 // TODO(deanm): In the end, I will probably remove this code and just let
193 // the user move around the bubble like a normal dialog. I want to try
194 // this for now and see if it causes problems when any window managers.
195 if (event->x != screen_x_ || event->y != screen_y_)
196 gtk_window_move(GTK_WINDOW(window_), screen_x_, screen_y_);
197 return FALSE;
198 }
199
200 gboolean InfoBubbleGtk::HandleButtonPress(GdkEventButton* event) {
201 // If we got a click in our own window, that's ok.
202 if (event->window == window_->window)
203 return FALSE; // Propagate.
204
205 // Otherwise we had a click outside of our window, close ourself.
206 Close();
207 return TRUE;
208 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698