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

Side by Side Diff: chrome/browser/ui/libgtkui/gtk_util.cc

Issue 2683953005: Gtk3: More fixes and refactorings (Closed)
Patch Set: Remove lambda Created 3 years, 10 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
« no previous file with comments | « chrome/browser/ui/libgtkui/gtk_util.h ('k') | chrome/browser/ui/libgtkui/native_theme_gtk3.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/ui/libgtkui/gtk_util.h" 5 #include "chrome/browser/ui/libgtkui/gtk_util.h"
6 6
7 #include <dlfcn.h> 7 #include <dlfcn.h>
8 #include <gdk/gdk.h> 8 #include <gdk/gdk.h>
9 #include <gdk/gdkx.h> 9 #include <gdk/gdkx.h>
10 #include <gtk/gtk.h> 10 #include <gtk/gtk.h>
(...skipping 202 matching lines...) Expand 10 before | Expand all | Expand 10 after
213 CAIRO_FORMAT_ARGB32, 213 CAIRO_FORMAT_ARGB32,
214 bitmap.width(), 214 bitmap.width(),
215 bitmap.height(), 215 bitmap.height(),
216 cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, bitmap.width()))), 216 cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, bitmap.width()))),
217 cairo_(cairo_create(surface_)) {} 217 cairo_(cairo_create(surface_)) {}
218 218
219 CairoSurface::CairoSurface(const gfx::Size& size) 219 CairoSurface::CairoSurface(const gfx::Size& size)
220 : surface_(cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 220 : surface_(cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
221 size.width(), 221 size.width(),
222 size.height())), 222 size.height())),
223 cairo_(cairo_create(surface_)) {} 223 cairo_(cairo_create(surface_)) {
224 DCHECK(cairo_surface_status(surface_) == CAIRO_STATUS_SUCCESS);
225 // Clear the surface.
226 cairo_save(cairo_);
227 cairo_set_source_rgba(cairo_, 0, 0, 0, 0);
228 cairo_set_operator(cairo_, CAIRO_OPERATOR_SOURCE);
229 cairo_paint(cairo_);
230 cairo_restore(cairo_);
231 }
224 232
225 CairoSurface::~CairoSurface() { 233 CairoSurface::~CairoSurface() {
226 cairo_destroy(cairo_); 234 cairo_destroy(cairo_);
227 cairo_surface_destroy(surface_); 235 cairo_surface_destroy(surface_);
228 } 236 }
229 237
230 SkColor CairoSurface::GetAveragePixelValue(bool only_frame_pixels) { 238 SkColor CairoSurface::GetAveragePixelValue(bool frame) {
231 cairo_surface_flush(surface_); 239 cairo_surface_flush(surface_);
232 int num_samples = 0;
233 long a = 0, r = 0, g = 0, b = 0;
234 SkColor* data = 240 SkColor* data =
235 reinterpret_cast<SkColor*>(cairo_image_surface_get_data(surface_)); 241 reinterpret_cast<SkColor*>(cairo_image_surface_get_data(surface_));
236 int width = cairo_image_surface_get_width(surface_); 242 int width = cairo_image_surface_get_width(surface_);
237 int height = cairo_image_surface_get_height(surface_); 243 int height = cairo_image_surface_get_height(surface_);
238 DCHECK(4 * width == cairo_image_surface_get_stride(surface_)); 244 DCHECK(4 * width == cairo_image_surface_get_stride(surface_));
239 auto accumulate = [&](int x, int y) mutable { 245 long a = 0, r = 0, g = 0, b = 0;
240 SkColor color = data[y * width + x]; 246 unsigned int max_alpha = 0;
241 int alpha = SkColorGetA(color); 247 for (int i = 0; i < width * height; i++) {
242 a += alpha; 248 SkColor color = data[i];
243 r += alpha * SkColorGetR(color); 249 max_alpha = std::max(SkColorGetA(color), max_alpha);
244 g += alpha * SkColorGetG(color); 250 a += SkColorGetA(color);
245 b += alpha * SkColorGetB(color); 251 r += SkColorGetR(color);
246 num_samples++; 252 g += SkColorGetG(color);
247 }; 253 b += SkColorGetB(color);
248 if (width == 1 || height == 1 || !only_frame_pixels) {
249 // Count every pixel in the surface.
250 for (int x = 0; x < width; x++)
251 for (int y = 0; y < height; y++)
252 accumulate(x, y);
253 } else {
254 // Count the pixels in the top and bottom rows.
255 for (int x = 0; x < width; x++)
256 for (int y : {0, height - 1})
257 accumulate(x, y);
258 // Count the pixels in the left and right columns.
259 for (int x : {0, width - 1})
260 for (int y = 1; y < height - 1; y++)
261 accumulate(x, y);
262 } 254 }
263 if (a == 0) 255 if (a == 0)
264 return SK_ColorTRANSPARENT; 256 return SK_ColorTRANSPARENT;
265 return SkColorSetARGB(a / num_samples, r / a, g / a, b / a); 257 return SkColorSetARGB(frame ? max_alpha : a / (width * height), r * 255 / a,
258 g * 255 / a, b * 255 / a);
266 } 259 }
267 260
268 bool GtkVersionCheck(int major, int minor, int micro) { 261 bool GtkVersionCheck(int major, int minor, int micro) {
269 static int actual_major = gtk_get_major_version(); 262 static int actual_major = gtk_get_major_version();
270 if (actual_major > major) 263 if (actual_major > major)
271 return true; 264 return true;
272 else if (actual_major < major) 265 else if (actual_major < major)
273 return false; 266 return false;
274 267
275 static int actual_minor = gtk_get_minor_version(); 268 static int actual_minor = gtk_get_minor_version();
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
329 case '.': 322 case '.':
330 part_type = CSS_CLASS; 323 part_type = CSS_CLASS;
331 break; 324 break;
332 case ':': 325 case ':':
333 part_type = CSS_PSEUDOCLASS; 326 part_type = CSS_PSEUDOCLASS;
334 break; 327 break;
335 default: 328 default:
336 NOTREACHED(); 329 NOTREACHED();
337 } 330 }
338 } else { 331 } else {
332 static auto* _gtk_widget_path_iter_set_object_name =
333 reinterpret_cast<void (*)(GtkWidgetPath*, gint, const char*)>(dlsym(
334 GetGtk3SharedLibrary(), "gtk_widget_path_iter_set_object_name"));
339 switch (part_type) { 335 switch (part_type) {
340 case CSS_NAME: { 336 case CSS_NAME: {
341 if (GtkVersionCheck(3, 20)) { 337 if (GtkVersionCheck(3, 20)) {
342 static auto* _gtk_widget_path_iter_set_object_name =
343 reinterpret_cast<void (*)(GtkWidgetPath*, gint, const char*)>(
344 dlsym(GetGtk3SharedLibrary(),
345 "gtk_widget_path_iter_set_object_name"));
346 DCHECK(_gtk_widget_path_iter_set_object_name);
347 _gtk_widget_path_iter_set_object_name(path, -1, t.token().c_str()); 338 _gtk_widget_path_iter_set_object_name(path, -1, t.token().c_str());
348 } else { 339 } else {
349 gtk_widget_path_iter_add_class(path, -1, t.token().c_str()); 340 gtk_widget_path_iter_add_class(path, -1, t.token().c_str());
350 } 341 }
351 break; 342 break;
352 } 343 }
353 case CSS_TYPE: { 344 case CSS_TYPE: {
354 GType type = g_type_from_name(t.token().c_str()); 345 GType type = g_type_from_name(t.token().c_str());
355 DCHECK(type); 346 DCHECK(type);
356 gtk_widget_path_append_type(path, type); 347 gtk_widget_path_append_type(path, type);
348 if (GtkVersionCheck(3, 20)) {
349 if (t.token() == "GtkLabel")
350 _gtk_widget_path_iter_set_object_name(path, -1, "label");
351 }
357 break; 352 break;
358 } 353 }
359 case CSS_CLASS: { 354 case CSS_CLASS: {
360 gtk_widget_path_iter_add_class(path, -1, t.token().c_str()); 355 gtk_widget_path_iter_add_class(path, -1, t.token().c_str());
361 break; 356 break;
362 } 357 }
363 case CSS_PSEUDOCLASS: { 358 case CSS_PSEUDOCLASS: {
364 GtkStateFlags state_flag = GTK_STATE_FLAG_NORMAL; 359 GtkStateFlags state_flag = GTK_STATE_FLAG_NORMAL;
365 for (const auto& pseudo_class_entry : pseudo_classes) { 360 for (const auto& pseudo_class_entry : pseudo_classes) {
366 if (strcmp(pseudo_class_entry.name, t.token().c_str()) == 0) { 361 if (strcmp(pseudo_class_entry.name, t.token().c_str()) == 0) {
367 state_flag = pseudo_class_entry.state_flag; 362 state_flag = pseudo_class_entry.state_flag;
368 break; 363 break;
369 } 364 }
370 } 365 }
371 state = static_cast<GtkStateFlags>(state | state_flag); 366 state = static_cast<GtkStateFlags>(state | state_flag);
372 break; 367 break;
373 } 368 }
374 } 369 }
375 } 370 }
376 } 371 }
377 372
378 // Always add a "chromium" class so that themes can style chromium 373 // Always add a "chromium" class so that themes can style chromium
379 // widgets specially if they want to. 374 // widgets specially if they want to.
380 gtk_widget_path_iter_add_class(path, -1, "chromium"); 375 gtk_widget_path_iter_add_class(path, -1, "chromium");
381 376
382 auto child_context = ScopedStyleContext(gtk_style_context_new()); 377 ScopedStyleContext child_context(gtk_style_context_new());
383 gtk_style_context_set_path(child_context, path); 378 gtk_style_context_set_path(child_context, path);
384 gtk_style_context_set_state(child_context, state); 379 gtk_style_context_set_state(child_context, state);
385 gtk_style_context_set_parent(child_context, context); 380 gtk_style_context_set_parent(child_context, context);
386 gtk_widget_path_unref(path); 381 gtk_widget_path_unref(path);
387 return child_context; 382 return child_context;
388 } 383 }
389 384
390 ScopedStyleContext GetStyleContextFromCss(const char* css_selector) { 385 ScopedStyleContext GetStyleContextFromCss(const char* css_selector) {
391 // Prepend "GtkWindow.background" to the selector since all widgets must live 386 // Prepend a window node to the selector since all widgets must live
392 // in a window, but we don't want to specify that every time. 387 // in a window, but we don't want to specify that every time.
393 auto context = AppendCssNodeToStyleContext(nullptr, "GtkWindow.background"); 388 auto context =
389 AppendCssNodeToStyleContext(nullptr, "GtkWindow#window.background");
394 390
395 for (const auto& widget_type : 391 for (const auto& widget_type :
396 base::SplitString(css_selector, base::kWhitespaceASCII, 392 base::SplitString(css_selector, base::kWhitespaceASCII,
397 base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY)) { 393 base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY)) {
398 context = AppendCssNodeToStyleContext(context, widget_type); 394 context = AppendCssNodeToStyleContext(context, widget_type);
399 } 395 }
400 return context; 396 return context;
401 } 397 }
402 398
403 SkColor GdkRgbaToSkColor(const GdkRGBA& color) { 399 SkColor GdkRgbaToSkColor(const GdkRGBA& color) {
404 return SkColorSetARGB(color.alpha * 255, color.red * 255, color.green * 255, 400 return SkColorSetARGB(color.alpha * 255, color.red * 255, color.green * 255,
405 color.blue * 255); 401 color.blue * 255);
406 } 402 }
407 403
408 SkColor SkColorFromStyleContext(GtkStyleContext* context) { 404 SkColor GetFgColorFromStyleContext(GtkStyleContext* context) {
409 GdkRGBA color; 405 GdkRGBA color;
410 gtk_style_context_get_color(context, gtk_style_context_get_state(context), 406 gtk_style_context_get_color(context, gtk_style_context_get_state(context),
411 &color); 407 &color);
412 return GdkRgbaToSkColor(color); 408 return GdkRgbaToSkColor(color);
413 } 409 }
414 410
415 SkColor GetFgColor(const char* css_selector) { 411 SkColor GetBgColorFromStyleContext(GtkStyleContext* context) {
416 return SkColorFromStyleContext(GetStyleContextFromCss(css_selector)); 412 // Backgrounds are more general than solid colors (eg. gradients),
413 // but chromium requires us to boil this down to one color. We
414 // cannot use the background-color here because some themes leave it
415 // set to a garbage color because a background-image will cover it
416 // anyway. So we instead render the background into a 24x24 bitmap,
417 // removing any borders, and hope that we get a good color.
418 ApplyCssToContext(context,
419 "* {"
420 "border-radius: 0px;"
421 "border-style: none;"
422 "box-shadow: none;"
423 "}");
424 gfx::Size size(24, 24);
425 CairoSurface surface(size);
426 RenderBackground(size, surface.cairo(), context);
427 return surface.GetAveragePixelValue(false);
417 } 428 }
418 429
419 GtkCssProvider* GetCssProvider(const char* css) { 430 SkColor GetFgColor(const char* css_selector) {
431 return GetFgColorFromStyleContext(GetStyleContextFromCss(css_selector));
432 }
433
434 ScopedCssProvider GetCssProvider(const char* css) {
420 GtkCssProvider* provider = gtk_css_provider_new(); 435 GtkCssProvider* provider = gtk_css_provider_new();
421 GError* error = nullptr; 436 GError* error = nullptr;
422 gtk_css_provider_load_from_data(provider, css, -1, &error); 437 gtk_css_provider_load_from_data(provider, css, -1, &error);
423 DCHECK(!error); 438 DCHECK(!error);
424 return provider; 439 return ScopedCssProvider(provider);
425 } 440 }
426 441
427 void ApplyCssToContext(GtkStyleContext* context, GtkCssProvider* provider) { 442 void ApplyCssProviderToContext(GtkStyleContext* context,
443 GtkCssProvider* provider) {
428 while (context) { 444 while (context) {
429 gtk_style_context_add_provider(context, GTK_STYLE_PROVIDER(provider), 445 gtk_style_context_add_provider(context, GTK_STYLE_PROVIDER(provider),
430 G_MAXUINT); 446 G_MAXUINT);
431 context = gtk_style_context_get_parent(context); 447 context = gtk_style_context_get_parent(context);
432 } 448 }
433 } 449 }
434 450
435 void RemoveBorders(GtkStyleContext* context) { 451 void ApplyCssToContext(GtkStyleContext* context, const char* css) {
436 static GtkCssProvider* provider = GetCssProvider( 452 auto provider = GetCssProvider(css);
437 "* {" 453 ApplyCssProviderToContext(context, provider);
438 "border-style: none;"
439 "border-radius: 0px;"
440 "border-width: 0px;"
441 "border-image-width: 0px;"
442 "box-shadow: none;"
443 "padding: 0px;"
444 "margin: 0px;"
445 "outline: none;"
446 "outline-width: 0px;"
447 "}");
448 ApplyCssToContext(context, provider);
449 }
450
451 void AddBorders(GtkStyleContext* context) {
452 static GtkCssProvider* provider = GetCssProvider(
453 "* {"
454 "border-style: solid;"
455 "border-radius: 0px;"
456 "border-width: 1px;"
457 "box-shadow: none;"
458 "padding: 0px;"
459 "margin: 0px;"
460 "outline: none;"
461 "outline-width: 0px;"
462 "}");
463 ApplyCssToContext(context, provider);
464 } 454 }
465 455
466 void RenderBackground(const gfx::Size& size, 456 void RenderBackground(const gfx::Size& size,
467 cairo_t* cr, 457 cairo_t* cr,
468 GtkStyleContext* context) { 458 GtkStyleContext* context) {
469 if (!context) 459 if (!context)
470 return; 460 return;
471 RenderBackground(size, cr, gtk_style_context_get_parent(context)); 461 RenderBackground(size, cr, gtk_style_context_get_parent(context));
472 gtk_render_background(context, cr, 0, 0, size.width(), size.height()); 462 gtk_render_background(context, cr, 0, 0, size.width(), size.height());
473 } 463 }
474 464
475 gfx::Size GetMinimumWidgetSize(GtkStyleContext* context) {
476 if (!GtkVersionCheck(3, 20))
477 return gfx::Size(1, 1);
478 int w = 1, h = 1;
479 gtk_style_context_get(context, gtk_style_context_get_state(context),
480 "min-width", &w, "min-height", &h, NULL);
481 DCHECK(w >= 0 && h >= 0);
482 return gfx::Size(w ? w : 1, h ? h : 1);
483 }
484
485 SkColor GetBgColor(const char* css_selector) { 465 SkColor GetBgColor(const char* css_selector) {
486 // Backgrounds are more general than solid colors (eg. gradients), 466 return GetBgColorFromStyleContext(GetStyleContextFromCss(css_selector));
487 // but chromium requires us to boil this down to one color. We
488 // cannot use the background-color here because some themes leave it
489 // set to a garbage color because a background-image will cover it
490 // anyway. So we instead render the background into a single pixel,
491 // removing any borders, and hope that we get a good color.
492 auto context = GetStyleContextFromCss(css_selector);
493 RemoveBorders(context);
494 gfx::Size size = GetMinimumWidgetSize(context);
495 CairoSurface surface(size);
496 RenderBackground(size, surface.cairo(), context);
497 return surface.GetAveragePixelValue(false);
498 } 467 }
499 468
500 SkColor GetBorderColor(const char* css_selector) { 469 SkColor GetBorderColor(const char* css_selector) {
501 // Borders have the same issue as backgrounds, due to the 470 // Borders have the same issue as backgrounds, due to the
502 // border-image property. 471 // border-image property.
503 auto context = GetStyleContextFromCss(css_selector); 472 auto context = GetStyleContextFromCss(css_selector);
504 GtkStateFlags state = gtk_style_context_get_state(context); 473 gfx::Size size(24, 24);
505 GtkBorderStyle border_style = GTK_BORDER_STYLE_NONE;
506 gtk_style_context_get(context, state, GTK_STYLE_PROPERTY_BORDER_STYLE,
507 &border_style, nullptr);
508 GtkBorder border;
509 gtk_style_context_get_border(context, state, &border);
510 if ((border_style == GTK_BORDER_STYLE_NONE ||
511 border_style == GTK_BORDER_STYLE_HIDDEN) ||
512 (!border.left && !border.right && !border.top && !border.bottom)) {
513 return SK_ColorTRANSPARENT;
514 }
515
516 AddBorders(context);
517 gfx::Size size = GetMinimumWidgetSize(context);
518 CairoSurface surface(size); 474 CairoSurface surface(size);
519 RenderBackground(size, surface.cairo(), context);
520 gtk_render_frame(context, surface.cairo(), 0, 0, size.width(), size.height()); 475 gtk_render_frame(context, surface.cairo(), 0, 0, size.width(), size.height());
521 return surface.GetAveragePixelValue(true); 476 return surface.GetAveragePixelValue(true);
522 } 477 }
523 478
479 ScopedStyleContext GetSelectedStyleContext(const char* css_selector) {
480 auto context = GetStyleContextFromCss(css_selector);
481 if (GtkVersionCheck(3, 20)) {
482 context = AppendCssNodeToStyleContext(context, "#selection");
483 } else {
484 GtkStateFlags state = gtk_style_context_get_state(context);
485 state = static_cast<GtkStateFlags>(state | GTK_STATE_FLAG_SELECTED);
486 gtk_style_context_set_state(context, state);
487 }
488 return context;
489 }
490
491 SkColor GetSelectedTextColor(const char* css_selector) {
492 return GetFgColorFromStyleContext(GetSelectedStyleContext(css_selector));
493 }
494
495 SkColor GetSelectedBgColor(const char* css_selector) {
496 auto context = GetSelectedStyleContext(css_selector);
497 if (GtkVersionCheck(3, 20))
498 return GetBgColorFromStyleContext(context);
499 // This is verbatim how Gtk gets the selection color on versions before 3.20.
500 GdkRGBA selection_color;
501 G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
502 gtk_style_context_get_background_color(
503 context, gtk_style_context_get_state(context), &selection_color);
504 G_GNUC_END_IGNORE_DEPRECATIONS;
505 return GdkRgbaToSkColor(selection_color);
506 }
507
524 SkColor GetSeparatorColor(const char* css_selector) { 508 SkColor GetSeparatorColor(const char* css_selector) {
525 if (!GtkVersionCheck(3, 20)) 509 if (!GtkVersionCheck(3, 20))
526 return GetFgColor(css_selector); 510 return GetFgColor(css_selector);
527 511
528 // Some themes use borders to render separators, others use a 512 auto context = GetStyleContextFromCss(css_selector);
529 // background color. Only return the border color if there is one. 513 int w = 1, h = 1;
530 SkColor border = GetBorderColor(css_selector); 514 gtk_style_context_get(context, gtk_style_context_get_state(context),
531 if (SkColorGetA(border)) 515 "min-width", &w, "min-height", &h, nullptr);
532 return border; 516 GtkBorder border, padding;
517 GtkStateFlags state = gtk_style_context_get_state(context);
518 gtk_style_context_get_border(context, state, &border);
519 gtk_style_context_get_padding(context, state, &padding);
520 w += border.left + padding.left + padding.right + border.right;
521 h += border.top + padding.top + padding.bottom + border.bottom;
533 522
534 return GetBgColor(css_selector); 523 bool horizontal = gtk_style_context_has_class(context, "horizontal");
524 if (horizontal) {
525 w = 24;
526 h = std::max(h, 1);
527 } else {
528 DCHECK(gtk_style_context_has_class(context, "vertical"));
529 h = 24;
530 w = std::max(w, 1);
531 }
532
533 CairoSurface surface(gfx::Size(w, h));
534 gtk_render_background(context, surface.cairo(), 0, 0, w, h);
535 gtk_render_frame(context, surface.cairo(), 0, 0, w, h);
536 return surface.GetAveragePixelValue(false);
535 } 537 }
536 #endif 538 #endif
537 539
538 } // namespace libgtkui 540 } // namespace libgtkui
OLDNEW
« no previous file with comments | « chrome/browser/ui/libgtkui/gtk_util.h ('k') | chrome/browser/ui/libgtkui/native_theme_gtk3.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698