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

Side by Side Diff: chrome/browser/ui/gtk/rounded_window.cc

Issue 231733005: Delete the GTK+ port of Chrome. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Remerge to ToT Created 6 years, 8 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 | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2012 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/ui/gtk/rounded_window.h"
6
7 #include <gtk/gtk.h>
8 #include <math.h>
9
10 #include "base/i18n/rtl.h"
11 #include "base/logging.h"
12 #include "chrome/browser/ui/gtk/gtk_util.h"
13 #include "ui/base/gtk/gtk_signal_registrar.h"
14 #include "ui/gfx/gtk_compat.h"
15
16 namespace gtk_util {
17
18 namespace {
19
20 const char* kRoundedData = "rounded-window-data";
21
22 // If the border radius is less than |kMinRoundedBorderSize|, we don't actually
23 // round the corners, we just truncate the corner.
24 const int kMinRoundedBorderSize = 8;
25
26 struct RoundedWindowData {
27 // Expected window size. Used to detect when we need to reshape the window.
28 int expected_width;
29 int expected_height;
30
31 // Color of the border.
32 GdkColor border_color;
33
34 // Radius of the edges in pixels.
35 int corner_size;
36
37 // Which corners should be rounded?
38 int rounded_edges;
39
40 // Which sides of the window should have an internal border?
41 int drawn_borders;
42
43 // Keeps track of attached signal handlers.
44 ui::GtkSignalRegistrar signals;
45 };
46
47 // Callback from GTK to release allocated memory.
48 void FreeRoundedWindowData(gpointer data) {
49 delete static_cast<RoundedWindowData*>(data);
50 }
51
52 enum FrameType {
53 FRAME_MASK,
54 FRAME_STROKE,
55 };
56
57 // Returns a list of points that either form the outline of the status bubble
58 // (|type| == FRAME_MASK) or form the inner border around the inner edge
59 // (|type| == FRAME_STROKE).
60 std::vector<GdkPoint> MakeFramePolygonPoints(RoundedWindowData* data,
61 FrameType type) {
62 using gtk_util::MakeBidiGdkPoint;
63 int width = data->expected_width;
64 int height = data->expected_height;
65 int corner_size = data->corner_size;
66
67 std::vector<GdkPoint> points;
68
69 bool ltr = !base::i18n::IsRTL();
70 // If we have a stroke, we have to offset some of our points by 1 pixel.
71 // We have to inset by 1 pixel when we draw horizontal lines that are on the
72 // bottom or when we draw vertical lines that are closer to the end (end is
73 // right for ltr).
74 int y_off = (type == FRAME_MASK) ? 0 : -1;
75 // We use this one for LTR.
76 int x_off_l = ltr ? y_off : 0;
77 // We use this one for RTL.
78 int x_off_r = !ltr ? -y_off : 0;
79
80 // Build up points starting with the bottom left corner and continuing
81 // clockwise.
82
83 // Bottom left corner.
84 if (type == FRAME_MASK ||
85 (data->drawn_borders & (BORDER_LEFT | BORDER_BOTTOM))) {
86 if (data->rounded_edges & ROUNDED_BOTTOM_LEFT) {
87 if (corner_size >= kMinRoundedBorderSize) {
88 // We are careful to only add points that are horizontal or vertically
89 // offset from the previous point (not both). This avoids rounding
90 // differences when two points are connected.
91 for (int x = 0; x <= corner_size; ++x) {
92 int y = static_cast<int>(sqrt(static_cast<double>(
93 (corner_size * corner_size) - (x * x))));
94 if (x > 0) {
95 points.push_back(MakeBidiGdkPoint(
96 corner_size - x + x_off_r + 1,
97 height - (corner_size - y) + y_off, width, ltr));
98 }
99 points.push_back(MakeBidiGdkPoint(
100 corner_size - x + x_off_r,
101 height - (corner_size - y) + y_off, width, ltr));
102 }
103 } else {
104 points.push_back(MakeBidiGdkPoint(
105 corner_size + x_off_l, height + y_off, width, ltr));
106 points.push_back(MakeBidiGdkPoint(
107 x_off_r, height - corner_size, width, ltr));
108 }
109 } else {
110 points.push_back(MakeBidiGdkPoint(x_off_r, height + y_off, width, ltr));
111 }
112 }
113
114 // Top left corner.
115 if (type == FRAME_MASK ||
116 (data->drawn_borders & (BORDER_LEFT | BORDER_TOP))) {
117 if (data->rounded_edges & ROUNDED_TOP_LEFT) {
118 if (corner_size >= kMinRoundedBorderSize) {
119 for (int x = corner_size; x >= 0; --x) {
120 int y = static_cast<int>(sqrt(static_cast<double>(
121 (corner_size * corner_size) - (x * x))));
122 points.push_back(MakeBidiGdkPoint(corner_size - x + x_off_r,
123 corner_size - y, width, ltr));
124 if (x > 0) {
125 points.push_back(MakeBidiGdkPoint(corner_size - x + 1 + x_off_r,
126 corner_size - y, width, ltr));
127 }
128 }
129 } else {
130 points.push_back(MakeBidiGdkPoint(
131 x_off_r, corner_size - 1, width, ltr));
132 points.push_back(MakeBidiGdkPoint(
133 corner_size + x_off_r - 1, 0, width, ltr));
134 }
135 } else {
136 points.push_back(MakeBidiGdkPoint(x_off_r, 0, width, ltr));
137 }
138 }
139
140 // Top right corner.
141 if (type == FRAME_MASK ||
142 (data->drawn_borders & (BORDER_TOP | BORDER_RIGHT))) {
143 if (data->rounded_edges & ROUNDED_TOP_RIGHT) {
144 if (corner_size >= kMinRoundedBorderSize) {
145 for (int x = 0; x <= corner_size; ++x) {
146 int y = static_cast<int>(sqrt(static_cast<double>(
147 (corner_size * corner_size) - (x * x))));
148 if (x > 0) {
149 points.push_back(MakeBidiGdkPoint(
150 width - (corner_size - x) + x_off_l - 1,
151 corner_size - y, width, ltr));
152 }
153 points.push_back(MakeBidiGdkPoint(
154 width - (corner_size - x) + x_off_l,
155 corner_size - y, width, ltr));
156 }
157 } else {
158 points.push_back(MakeBidiGdkPoint(
159 width - corner_size + 1 + x_off_l, 0, width, ltr));
160 points.push_back(MakeBidiGdkPoint(
161 width + x_off_l, corner_size - 1, width, ltr));
162 }
163 } else {
164 points.push_back(MakeBidiGdkPoint(
165 width + x_off_l, 0, width, ltr));
166 }
167 }
168
169 // Bottom right corner.
170 if (type == FRAME_MASK ||
171 (data->drawn_borders & (BORDER_RIGHT | BORDER_BOTTOM))) {
172 if (data->rounded_edges & ROUNDED_BOTTOM_RIGHT) {
173 if (corner_size >= kMinRoundedBorderSize) {
174 for (int x = corner_size; x >= 0; --x) {
175 int y = static_cast<int>(sqrt(static_cast<double>(
176 (corner_size * corner_size) - (x * x))));
177 points.push_back(MakeBidiGdkPoint(
178 width - (corner_size - x) + x_off_l,
179 height - (corner_size - y) + y_off, width, ltr));
180 if (x > 0) {
181 points.push_back(MakeBidiGdkPoint(
182 width - (corner_size - x) + x_off_l - 1,
183 height - (corner_size - y) + y_off, width, ltr));
184 }
185 }
186 } else {
187 points.push_back(MakeBidiGdkPoint(
188 width + x_off_l, height - corner_size, width, ltr));
189 points.push_back(MakeBidiGdkPoint(
190 width - corner_size + x_off_r, height + y_off, width, ltr));
191 }
192 } else {
193 points.push_back(MakeBidiGdkPoint(
194 width + x_off_l, height + y_off, width, ltr));
195 }
196 }
197
198 return points;
199 }
200
201 // Set the window shape in needed, lets our owner do some drawing (if it wants
202 // to), and finally draw the border.
203 gboolean OnRoundedWindowExpose(GtkWidget* widget,
204 GdkEventExpose* event) {
205 RoundedWindowData* data = static_cast<RoundedWindowData*>(
206 g_object_get_data(G_OBJECT(widget), kRoundedData));
207
208 GtkAllocation allocation;
209 gtk_widget_get_allocation(widget, &allocation);
210
211 if (data->expected_width != allocation.width ||
212 data->expected_height != allocation.height) {
213 data->expected_width = allocation.width;
214 data->expected_height = allocation.height;
215
216 // We need to update the shape of the status bubble whenever our GDK
217 // window changes shape.
218 std::vector<GdkPoint> mask_points = MakeFramePolygonPoints(
219 data, FRAME_MASK);
220 GdkRegion* mask_region = gdk_region_polygon(&mask_points[0],
221 mask_points.size(),
222 GDK_EVEN_ODD_RULE);
223 gdk_window_shape_combine_region(gtk_widget_get_window(widget),
224 mask_region, 0, 0);
225 gdk_region_destroy(mask_region);
226 }
227
228 GdkDrawable* drawable = GDK_DRAWABLE(event->window);
229 GdkGC* gc = gdk_gc_new(drawable);
230 gdk_gc_set_clip_rectangle(gc, &event->area);
231 gdk_gc_set_rgb_fg_color(gc, &data->border_color);
232
233 // Stroke the frame border.
234 std::vector<GdkPoint> points = MakeFramePolygonPoints(
235 data, FRAME_STROKE);
236 if (data->drawn_borders == BORDER_ALL) {
237 // If we want to have borders everywhere, we need to draw a polygon instead
238 // of a set of lines.
239 gdk_draw_polygon(drawable, gc, FALSE, &points[0], points.size());
240 } else if (!points.empty()) {
241 gdk_draw_lines(drawable, gc, &points[0], points.size());
242 }
243
244 g_object_unref(gc);
245 return FALSE; // Propagate so our children paint, etc.
246 }
247
248 // On theme changes, window shapes are reset, but we detect whether we need to
249 // reshape a window by whether its allocation has changed so force it to reset
250 // the window shape on next expose.
251 void OnStyleSet(GtkWidget* widget, GtkStyle* previous_style) {
252 DCHECK(widget);
253 RoundedWindowData* data = static_cast<RoundedWindowData*>(
254 g_object_get_data(G_OBJECT(widget), kRoundedData));
255 DCHECK(data);
256 data->expected_width = -1;
257 data->expected_height = -1;
258 }
259
260 } // namespace
261
262 void ActAsRoundedWindow(
263 GtkWidget* widget, const GdkColor& color, int corner_size,
264 int rounded_edges, int drawn_borders) {
265 DCHECK(widget);
266 DCHECK(!g_object_get_data(G_OBJECT(widget), kRoundedData));
267
268 gtk_widget_set_app_paintable(widget, TRUE);
269
270 RoundedWindowData* data = new RoundedWindowData;
271 data->signals.Connect(widget, "expose-event",
272 G_CALLBACK(OnRoundedWindowExpose), NULL);
273 data->signals.Connect(widget, "style-set", G_CALLBACK(OnStyleSet), NULL);
274
275 data->expected_width = -1;
276 data->expected_height = -1;
277
278 data->border_color = color;
279 data->corner_size = corner_size;
280
281 data->rounded_edges = rounded_edges;
282 data->drawn_borders = drawn_borders;
283
284 g_object_set_data_full(G_OBJECT(widget), kRoundedData,
285 data, FreeRoundedWindowData);
286
287 if (gtk_widget_get_visible(widget))
288 gtk_widget_queue_draw(widget);
289 }
290
291 void StopActingAsRoundedWindow(GtkWidget* widget) {
292 g_object_set_data(G_OBJECT(widget), kRoundedData, NULL);
293
294 if (gtk_widget_get_realized(widget))
295 gdk_window_shape_combine_mask(gtk_widget_get_window(widget), NULL, 0, 0);
296
297 if (gtk_widget_get_visible(widget))
298 gtk_widget_queue_draw(widget);
299 }
300
301 bool IsActingAsRoundedWindow(GtkWidget* widget) {
302 return g_object_get_data(G_OBJECT(widget), kRoundedData) != NULL;
303 }
304
305 void SetRoundedWindowEdgesAndBorders(GtkWidget* widget,
306 int corner_size,
307 int rounded_edges,
308 int drawn_borders) {
309 DCHECK(widget);
310 RoundedWindowData* data = static_cast<RoundedWindowData*>(
311 g_object_get_data(G_OBJECT(widget), kRoundedData));
312 DCHECK(data);
313 data->corner_size = corner_size;
314 data->rounded_edges = rounded_edges;
315 data->drawn_borders = drawn_borders;
316 }
317
318 void SetRoundedWindowBorderColor(GtkWidget* widget, GdkColor color) {
319 DCHECK(widget);
320 RoundedWindowData* data = static_cast<RoundedWindowData*>(
321 g_object_get_data(G_OBJECT(widget), kRoundedData));
322 DCHECK(data);
323 data->border_color = color;
324 }
325
326 } // namespace gtk_util
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698