Switch global error menu icon to vectorized MD asset
[chromium-blink-merge.git] / chrome / browser / ui / libgtk2ui / native_theme_gtk2.cc
blobb7a61f170cda981dc422c1031a5b139f1f3e73f5
1 // Copyright (c) 2013 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.
5 #include "chrome/browser/ui/libgtk2ui/native_theme_gtk2.h"
7 #include <gtk/gtk.h>
9 #include "chrome/browser/ui/libgtk2ui/chrome_gtk_menu_subclasses.h"
10 #include "chrome/browser/ui/libgtk2ui/gtk2_ui.h"
11 #include "chrome/browser/ui/libgtk2ui/gtk2_util.h"
12 #include "chrome/browser/ui/libgtk2ui/skia_utils_gtk2.h"
13 #include "third_party/skia/include/core/SkColor.h"
14 #include "ui/gfx/color_utils.h"
15 #include "ui/gfx/geometry/rect.h"
16 #include "ui/gfx/geometry/size.h"
17 #include "ui/gfx/path.h"
18 #include "ui/gfx/skia_util.h"
19 #include "ui/native_theme/common_theme.h"
21 namespace {
23 // Theme colors returned by GetSystemColor().
24 const SkColor kInvalidColorIdColor = SkColorSetRGB(255, 0, 128);
26 const GdkColor kURLTextColor = GDK_COLOR_RGB(0x0b, 0x80, 0x43);
28 GdkColor GdkAlphaBlend(GdkColor foreground,
29 GdkColor background,
30 SkAlpha alpha) {
31 return libgtk2ui::SkColorToGdkColor(
32 color_utils::AlphaBlend(libgtk2ui::GdkColorToSkColor(foreground),
33 libgtk2ui::GdkColorToSkColor(background), alpha));
36 // Generates the normal URL color, a green color used in unhighlighted URL
37 // text. It is a mix of |kURLTextColor| and the current text color. Unlike the
38 // selected text color, it is more important to match the qualities of the
39 // foreground typeface color instead of taking the background into account.
40 GdkColor NormalURLColor(GdkColor foreground) {
41 color_utils::HSL fg_hsl;
42 color_utils::SkColorToHSL(libgtk2ui::GdkColorToSkColor(foreground), &fg_hsl);
44 color_utils::HSL hue_hsl;
45 color_utils::SkColorToHSL(libgtk2ui::GdkColorToSkColor(kURLTextColor),
46 &hue_hsl);
48 // Only allow colors that have a fair amount of saturation in them (color vs
49 // white). This means that our output color will always be fairly green.
50 double s = std::max(0.5, fg_hsl.s);
52 // Make sure the luminance is at least as bright as the |kURLTextColor| green
53 // would be if we were to use that.
54 double l;
55 if (fg_hsl.l < hue_hsl.l)
56 l = hue_hsl.l;
57 else
58 l = (fg_hsl.l + hue_hsl.l) / 2;
60 color_utils::HSL output = { hue_hsl.h, s, l };
61 return libgtk2ui::SkColorToGdkColor(color_utils::HSLToSkColor(output, 255));
64 // Generates the selected URL color, a green color used on URL text in the
65 // currently highlighted entry in the autocomplete popup. It's a mix of
66 // |kURLTextColor|, the current text color, and the background color (the
67 // select highlight). It is more important to contrast with the background
68 // saturation than to look exactly like the foreground color.
69 GdkColor SelectedURLColor(GdkColor foreground, GdkColor background) {
70 color_utils::HSL fg_hsl;
71 color_utils::SkColorToHSL(libgtk2ui::GdkColorToSkColor(foreground),
72 &fg_hsl);
74 color_utils::HSL bg_hsl;
75 color_utils::SkColorToHSL(libgtk2ui::GdkColorToSkColor(background),
76 &bg_hsl);
78 color_utils::HSL hue_hsl;
79 color_utils::SkColorToHSL(libgtk2ui::GdkColorToSkColor(kURLTextColor),
80 &hue_hsl);
82 // The saturation of the text should be opposite of the background, clamped
83 // to 0.2-0.8. We make sure it's greater than 0.2 so there's some color, but
84 // less than 0.8 so it's not the oversaturated neon-color.
85 double opposite_s = 1 - bg_hsl.s;
86 double s = std::max(0.2, std::min(0.8, opposite_s));
88 // The luminance should match the luminance of the foreground text. Again,
89 // we clamp so as to have at some amount of color (green) in the text.
90 double opposite_l = fg_hsl.l;
91 double l = std::max(0.1, std::min(0.9, opposite_l));
93 color_utils::HSL output = { hue_hsl.h, s, l };
94 return libgtk2ui::SkColorToGdkColor(color_utils::HSLToSkColor(output, 255));
97 GdkColor GetReadableColor(SkColor color, const GdkColor& background) {
98 return libgtk2ui::SkColorToGdkColor(color_utils::GetReadableColor(
99 color, libgtk2ui::GdkColorToSkColor(background)));
102 } // namespace
105 namespace libgtk2ui {
107 // static
108 NativeThemeGtk2* NativeThemeGtk2::instance() {
109 CR_DEFINE_STATIC_LOCAL(NativeThemeGtk2, s_native_theme, ());
110 return &s_native_theme;
113 NativeThemeGtk2::NativeThemeGtk2()
114 : fake_window_(NULL),
115 fake_tooltip_(NULL),
116 fake_menu_item_(NULL) {
119 NativeThemeGtk2::~NativeThemeGtk2() {
120 if (fake_window_)
121 gtk_widget_destroy(fake_window_);
122 if (fake_tooltip_)
123 gtk_widget_destroy(fake_tooltip_);
125 fake_entry_.Destroy();
126 fake_label_.Destroy();
127 fake_button_.Destroy();
128 fake_tree_.Destroy();
129 fake_menu_.Destroy();
132 gfx::Size NativeThemeGtk2::GetPartSize(Part part,
133 State state,
134 const ExtraParams& extra) const {
135 if (part == kComboboxArrow)
136 return gfx::Size(12, 12);
138 return ui::NativeThemeBase::GetPartSize(part, state, extra);
141 void NativeThemeGtk2::Paint(SkCanvas* canvas,
142 Part part,
143 State state,
144 const gfx::Rect& rect,
145 const ExtraParams& extra) const {
146 if (rect.IsEmpty())
147 return;
149 switch (part) {
150 case kComboboxArrow:
151 PaintComboboxArrow(canvas, GetGtkState(state), rect);
152 return;
154 default:
155 NativeThemeBase::Paint(canvas, part, state, rect, extra);
159 SkColor NativeThemeGtk2::GetSystemColor(ColorId color_id) const {
160 if (color_id == kColorId_BlueButtonShadowColor)
161 return SK_ColorTRANSPARENT;
163 return GdkColorToSkColor(GetSystemGdkColor(color_id));
166 void NativeThemeGtk2::PaintMenuPopupBackground(
167 SkCanvas* canvas,
168 const gfx::Size& size,
169 const MenuBackgroundExtraParams& menu_background) const {
170 if (menu_background.corner_radius > 0) {
171 SkPaint paint;
172 paint.setStyle(SkPaint::kFill_Style);
173 paint.setFlags(SkPaint::kAntiAlias_Flag);
174 paint.setColor(GetSystemColor(kColorId_MenuBackgroundColor));
176 gfx::Path path;
177 SkRect rect = SkRect::MakeWH(SkIntToScalar(size.width()),
178 SkIntToScalar(size.height()));
179 SkScalar radius = SkIntToScalar(menu_background.corner_radius);
180 SkScalar radii[8] = {radius, radius, radius, radius,
181 radius, radius, radius, radius};
182 path.addRoundRect(rect, radii);
184 canvas->drawPath(path, paint);
185 } else {
186 canvas->drawColor(GetSystemColor(kColorId_MenuBackgroundColor),
187 SkXfermode::kSrc_Mode);
191 void NativeThemeGtk2::PaintMenuItemBackground(
192 SkCanvas* canvas,
193 State state,
194 const gfx::Rect& rect,
195 const MenuListExtraParams& menu_list) const {
196 SkColor color;
197 SkPaint paint;
198 switch (state) {
199 case NativeTheme::kNormal:
200 case NativeTheme::kDisabled:
201 color = GetSystemColor(NativeTheme::kColorId_MenuBackgroundColor);
202 paint.setColor(color);
203 break;
204 case NativeTheme::kHovered:
205 color = GetSystemColor(
206 NativeTheme::kColorId_FocusedMenuItemBackgroundColor);
207 paint.setColor(color);
208 break;
209 default:
210 NOTREACHED() << "Invalid state " << state;
211 break;
213 canvas->drawRect(gfx::RectToSkRect(rect), paint);
216 GdkColor NativeThemeGtk2::GetSystemGdkColor(ColorId color_id) const {
217 const SkColor kPositiveTextColor = SkColorSetRGB(0x0b, 0x80, 0x43);
218 const SkColor kNegativeTextColor = SkColorSetRGB(0xc5, 0x39, 0x29);
219 switch (color_id) {
220 // Windows
221 case kColorId_WindowBackground:
222 return GetWindowStyle()->bg[GTK_STATE_NORMAL];
224 // Dialogs
225 case kColorId_DialogBackground:
226 return GetWindowStyle()->bg[GTK_STATE_NORMAL];
228 // FocusableBorder
229 case kColorId_FocusedBorderColor:
230 return GetEntryStyle()->bg[GTK_STATE_SELECTED];
231 case kColorId_UnfocusedBorderColor:
232 return GetEntryStyle()->text_aa[GTK_STATE_NORMAL];
234 // MenuItem
235 case kColorId_EnabledMenuItemForegroundColor:
236 case kColorId_DisabledEmphasizedMenuItemForegroundColor:
237 return GetMenuItemStyle()->text[GTK_STATE_NORMAL];
238 case kColorId_DisabledMenuItemForegroundColor:
239 return GetMenuItemStyle()->text[GTK_STATE_INSENSITIVE];
240 case kColorId_SelectedMenuItemForegroundColor:
241 return GetMenuItemStyle()->text[GTK_STATE_SELECTED];
242 case kColorId_FocusedMenuItemBackgroundColor:
243 return GetMenuItemStyle()->bg[GTK_STATE_SELECTED];
244 case kColorId_HoverMenuItemBackgroundColor:
245 return GetMenuItemStyle()->bg[GTK_STATE_PRELIGHT];
246 case kColorId_FocusedMenuButtonBorderColor:
247 return GetEntryStyle()->bg[GTK_STATE_NORMAL];
248 case kColorId_HoverMenuButtonBorderColor:
249 return GetEntryStyle()->text_aa[GTK_STATE_PRELIGHT];
250 case kColorId_MenuBorderColor:
251 case kColorId_EnabledMenuButtonBorderColor:
252 case kColorId_MenuSeparatorColor: {
253 return GetMenuItemStyle()->text[GTK_STATE_INSENSITIVE];
255 case kColorId_MenuBackgroundColor:
256 return GetMenuStyle()->bg[GTK_STATE_NORMAL];
258 // Label
259 case kColorId_LabelEnabledColor:
260 return GetLabelStyle()->text[GTK_STATE_NORMAL];
261 case kColorId_LabelDisabledColor:
262 return GetLabelStyle()->text[GTK_STATE_INSENSITIVE];
263 case kColorId_LabelBackgroundColor:
264 return GetWindowStyle()->bg[GTK_STATE_NORMAL];
266 // Button
267 case kColorId_ButtonBackgroundColor:
268 return GetButtonStyle()->bg[GTK_STATE_NORMAL];
269 case kColorId_ButtonEnabledColor:
270 case kColorId_BlueButtonEnabledColor:
271 return GetButtonStyle()->text[GTK_STATE_NORMAL];
272 case kColorId_ButtonDisabledColor:
273 case kColorId_BlueButtonDisabledColor:
274 return GetButtonStyle()->text[GTK_STATE_INSENSITIVE];
275 case kColorId_ButtonHighlightColor:
276 return GetButtonStyle()->base[GTK_STATE_SELECTED];
277 case kColorId_ButtonHoverColor:
278 case kColorId_BlueButtonHoverColor:
279 return GetButtonStyle()->text[GTK_STATE_PRELIGHT];
280 case kColorId_ButtonHoverBackgroundColor:
281 return GetButtonStyle()->bg[GTK_STATE_PRELIGHT];
282 case kColorId_BlueButtonPressedColor:
283 return GetButtonStyle()->text[GTK_STATE_ACTIVE];
284 case kColorId_BlueButtonShadowColor:
285 // Should be handled in GetSystemColor().
286 NOTREACHED();
287 return GetButtonStyle()->text[GTK_STATE_NORMAL];
289 // Textfield
290 case kColorId_TextfieldDefaultColor:
291 return GetEntryStyle()->text[GTK_STATE_NORMAL];
292 case kColorId_TextfieldDefaultBackground:
293 return GetEntryStyle()->base[GTK_STATE_NORMAL];
294 case kColorId_TextfieldReadOnlyColor:
295 return GetEntryStyle()->text[GTK_STATE_INSENSITIVE];
296 case kColorId_TextfieldReadOnlyBackground:
297 return GetEntryStyle()->base[GTK_STATE_INSENSITIVE];
298 case kColorId_TextfieldSelectionColor:
299 return GetEntryStyle()->text[GTK_STATE_SELECTED];
300 case kColorId_TextfieldSelectionBackgroundFocused:
301 return GetEntryStyle()->base[GTK_STATE_SELECTED];
303 // Tooltips
304 case kColorId_TooltipBackground:
305 return GetTooltipStyle()->bg[GTK_STATE_NORMAL];
306 case kColorId_TooltipText:
307 return GetTooltipStyle()->fg[GTK_STATE_NORMAL];
309 // Trees and Tables (implemented on GTK using the same class)
310 case kColorId_TableBackground:
311 case kColorId_TreeBackground:
312 return GetTreeStyle()->bg[GTK_STATE_NORMAL];
313 case kColorId_TableText:
314 case kColorId_TreeText:
315 return GetTreeStyle()->text[GTK_STATE_NORMAL];
316 case kColorId_TableSelectedText:
317 case kColorId_TableSelectedTextUnfocused:
318 case kColorId_TreeSelectedText:
319 case kColorId_TreeSelectedTextUnfocused:
320 return GetTreeStyle()->text[GTK_STATE_SELECTED];
321 case kColorId_TableSelectionBackgroundFocused:
322 case kColorId_TableSelectionBackgroundUnfocused:
323 case kColorId_TreeSelectionBackgroundFocused:
324 case kColorId_TreeSelectionBackgroundUnfocused:
325 return GetTreeStyle()->bg[GTK_STATE_SELECTED];
326 case kColorId_TreeArrow:
327 return GetTreeStyle()->fg[GTK_STATE_NORMAL];
328 case kColorId_TableGroupingIndicatorColor:
329 return GetTreeStyle()->text_aa[GTK_STATE_NORMAL];
331 // Results Table
332 case kColorId_ResultsTableNormalBackground:
333 return GetEntryStyle()->base[GTK_STATE_NORMAL];
334 case kColorId_ResultsTableHoveredBackground: {
335 GtkStyle* entry_style = GetEntryStyle();
336 return GdkAlphaBlend(
337 entry_style->base[GTK_STATE_NORMAL],
338 entry_style->base[GTK_STATE_SELECTED], 0x80);
340 case kColorId_ResultsTableSelectedBackground:
341 return GetEntryStyle()->base[GTK_STATE_SELECTED];
342 case kColorId_ResultsTableNormalText:
343 case kColorId_ResultsTableHoveredText:
344 return GetEntryStyle()->text[GTK_STATE_NORMAL];
345 case kColorId_ResultsTableSelectedText:
346 return GetEntryStyle()->text[GTK_STATE_SELECTED];
347 case kColorId_ResultsTableNormalDimmedText:
348 case kColorId_ResultsTableHoveredDimmedText: {
349 GtkStyle* entry_style = GetEntryStyle();
350 return GdkAlphaBlend(
351 entry_style->text[GTK_STATE_NORMAL],
352 entry_style->base[GTK_STATE_NORMAL], 0x80);
354 case kColorId_ResultsTableSelectedDimmedText: {
355 GtkStyle* entry_style = GetEntryStyle();
356 return GdkAlphaBlend(
357 entry_style->text[GTK_STATE_SELECTED],
358 entry_style->base[GTK_STATE_NORMAL], 0x80);
360 case kColorId_ResultsTableNormalUrl:
361 case kColorId_ResultsTableHoveredUrl: {
362 return NormalURLColor(GetEntryStyle()->text[GTK_STATE_NORMAL]);
364 case kColorId_ResultsTableSelectedUrl: {
365 GtkStyle* entry_style = GetEntryStyle();
366 return SelectedURLColor(entry_style->text[GTK_STATE_SELECTED],
367 entry_style->base[GTK_STATE_SELECTED]);
369 case kColorId_ResultsTableNormalDivider: {
370 GtkStyle* win_style = GetWindowStyle();
371 return GdkAlphaBlend(win_style->text[GTK_STATE_NORMAL],
372 win_style->bg[GTK_STATE_NORMAL], 0x34);
374 case kColorId_ResultsTableHoveredDivider: {
375 GtkStyle* win_style = GetWindowStyle();
376 return GdkAlphaBlend(win_style->text[GTK_STATE_PRELIGHT],
377 win_style->bg[GTK_STATE_PRELIGHT], 0x34);
379 case kColorId_ResultsTableSelectedDivider: {
380 GtkStyle* win_style = GetWindowStyle();
381 return GdkAlphaBlend(win_style->text[GTK_STATE_SELECTED],
382 win_style->bg[GTK_STATE_SELECTED], 0x34);
384 case kColorId_ResultsTablePositiveText: {
385 return GetReadableColor(kPositiveTextColor,
386 GetEntryStyle()->base[GTK_STATE_NORMAL]);
388 case kColorId_ResultsTablePositiveHoveredText: {
389 return GetReadableColor(kPositiveTextColor,
390 GetEntryStyle()->base[GTK_STATE_PRELIGHT]);
392 case kColorId_ResultsTablePositiveSelectedText: {
393 return GetReadableColor(kPositiveTextColor,
394 GetEntryStyle()->base[GTK_STATE_SELECTED]);
396 case kColorId_ResultsTableNegativeText: {
397 return GetReadableColor(kNegativeTextColor,
398 GetEntryStyle()->base[GTK_STATE_NORMAL]);
400 case kColorId_ResultsTableNegativeHoveredText: {
401 return GetReadableColor(kNegativeTextColor,
402 GetEntryStyle()->base[GTK_STATE_PRELIGHT]);
404 case kColorId_ResultsTableNegativeSelectedText: {
405 return GetReadableColor(kNegativeTextColor,
406 GetEntryStyle()->base[GTK_STATE_SELECTED]);
409 // Throbber
410 case kColorId_ThrobberSpinningColor:
411 case kColorId_ThrobberLightColor: {
412 return GetEntryStyle()->bg[GTK_STATE_SELECTED];
415 case kColorId_ThrobberWaitingColor: {
416 return GdkAlphaBlend(GetEntryStyle()->bg[GTK_STATE_SELECTED],
417 GetWindowStyle()->bg[GTK_STATE_NORMAL], 0xff / 2);
420 case kColorId_Amber:
421 case kColorId_ChromeIconGrey:
422 case kColorId_GoogleBlue:
423 case kColorId_NumColors:
424 NOTREACHED();
425 break;
428 return SkColorToGdkColor(kInvalidColorIdColor);
431 GtkWidget* NativeThemeGtk2::GetRealizedWindow() const {
432 if (!fake_window_) {
433 fake_window_ = gtk_window_new(GTK_WINDOW_TOPLEVEL);
434 gtk_widget_realize(fake_window_);
437 return fake_window_;
440 GtkStyle* NativeThemeGtk2::GetWindowStyle() const {
441 return gtk_rc_get_style(GetRealizedWindow());
444 GtkStyle* NativeThemeGtk2::GetEntryStyle() const {
445 if (!fake_entry_.get()) {
446 fake_entry_.Own(gtk_entry_new());
448 // The fake entry needs to be in the window so it can be realized so we can
449 // use the computed parts of the style.
450 gtk_container_add(GTK_CONTAINER(GetRealizedWindow()), fake_entry_.get());
451 gtk_widget_realize(fake_entry_.get());
453 return gtk_rc_get_style(fake_entry_.get());
456 GtkStyle* NativeThemeGtk2::GetLabelStyle() const {
457 if (!fake_label_.get())
458 fake_label_.Own(gtk_label_new(""));
460 return gtk_rc_get_style(fake_label_.get());
463 GtkStyle* NativeThemeGtk2::GetButtonStyle() const {
464 if (!fake_button_.get())
465 fake_button_.Own(gtk_button_new());
467 return gtk_rc_get_style(fake_button_.get());
470 GtkStyle* NativeThemeGtk2::GetTreeStyle() const {
471 if (!fake_tree_.get())
472 fake_tree_.Own(gtk_tree_view_new());
474 return gtk_rc_get_style(fake_tree_.get());
477 GtkStyle* NativeThemeGtk2::GetTooltipStyle() const {
478 if (!fake_tooltip_) {
479 fake_tooltip_ = gtk_window_new(GTK_WINDOW_TOPLEVEL);
480 gtk_widget_set_name(fake_tooltip_, "gtk-tooltip");
481 gtk_widget_realize(fake_tooltip_);
483 return gtk_rc_get_style(fake_tooltip_);
486 GtkStyle* NativeThemeGtk2::GetMenuStyle() const {
487 if (!fake_menu_.get())
488 fake_menu_.Own(gtk_custom_menu_new());
489 return gtk_rc_get_style(fake_menu_.get());
492 GtkStyle* NativeThemeGtk2::GetMenuItemStyle() const {
493 if (!fake_menu_item_) {
494 if (!fake_menu_.get())
495 fake_menu_.Own(gtk_custom_menu_new());
497 fake_menu_item_ = gtk_custom_menu_item_new();
498 gtk_menu_shell_append(GTK_MENU_SHELL(fake_menu_.get()), fake_menu_item_);
501 return gtk_rc_get_style(fake_menu_item_);
504 void NativeThemeGtk2::PaintComboboxArrow(SkCanvas* canvas,
505 GtkStateType state,
506 const gfx::Rect& rect) const {
507 GdkPixmap* pm = gdk_pixmap_new(gtk_widget_get_window(GetRealizedWindow()),
508 rect.width(),
509 rect.height(),
510 -1);
511 // Paint the background.
512 gtk_paint_flat_box(GetWindowStyle(),
514 state,
515 GTK_SHADOW_NONE,
516 NULL,
517 GetRealizedWindow(),
518 NULL, 0, 0, rect.width(), rect.height());
519 gtk_paint_arrow(GetWindowStyle(),
521 state,
522 GTK_SHADOW_NONE,
523 NULL,
524 GetRealizedWindow(),
525 NULL,
526 GTK_ARROW_DOWN,
527 true,
528 0, 0, rect.width(), rect.height());
529 GdkPixbuf* pb = gdk_pixbuf_get_from_drawable(NULL,
531 gdk_drawable_get_colormap(pm),
532 0, 0,
533 0, 0,
534 rect.width(), rect.height());
535 SkBitmap arrow = GdkPixbufToImageSkia(pb);
536 canvas->drawBitmap(arrow, rect.x(), rect.y());
538 g_object_unref(pb);
539 g_object_unref(pm);
542 } // namespace libgtk2ui