Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / libgtk2ui / native_theme_gtk2.cc
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.
4
5 #include "chrome/browser/ui/libgtk2ui/native_theme_gtk2.h"
6
7 #include <gtk/gtk.h>
8
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 "ui/gfx/color_utils.h"
14 #include "ui/gfx/path.h"
15 #include "ui/gfx/rect.h"
16 #include "ui/gfx/size.h"
17 #include "ui/gfx/skia_util.h"
18 #include "ui/native_theme/common_theme.h"
19
20 namespace {
21
22 // Theme colors returned by GetSystemColor().
23 const SkColor kInvalidColorIdColor = SkColorSetRGB(255, 0, 128);
24
25 const GdkColor kURLTextColor = GDK_COLOR_RGB(0x00, 0x88, 0x00);
26
27 GdkColor GdkAlphaBlend(GdkColor foreground,
28                        GdkColor background,
29                        SkAlpha alpha) {
30   return libgtk2ui::SkColorToGdkColor(
31       color_utils::AlphaBlend(libgtk2ui::GdkColorToSkColor(foreground),
32                               libgtk2ui::GdkColorToSkColor(background), alpha));
33 }
34
35 // Generates the normal URL color, a green color used in unhighlighted URL
36 // text. It is a mix of |kURLTextColor| and the current text color.  Unlike the
37 // selected text color, it is more important to match the qualities of the
38 // foreground typeface color instead of taking the background into account.
39 GdkColor NormalURLColor(GdkColor foreground) {
40   color_utils::HSL fg_hsl;
41   color_utils::SkColorToHSL(libgtk2ui::GdkColorToSkColor(foreground), &fg_hsl);
42
43   color_utils::HSL hue_hsl;
44   color_utils::SkColorToHSL(libgtk2ui::GdkColorToSkColor(kURLTextColor),
45                             &hue_hsl);
46
47   // Only allow colors that have a fair amount of saturation in them (color vs
48   // white). This means that our output color will always be fairly green.
49   double s = std::max(0.5, fg_hsl.s);
50
51   // Make sure the luminance is at least as bright as the |kURLTextColor| green
52   // would be if we were to use that.
53   double l;
54   if (fg_hsl.l < hue_hsl.l)
55     l = hue_hsl.l;
56   else
57     l = (fg_hsl.l + hue_hsl.l) / 2;
58
59   color_utils::HSL output = { hue_hsl.h, s, l };
60   return libgtk2ui::SkColorToGdkColor(color_utils::HSLToSkColor(output, 255));
61 }
62
63 // Generates the selected URL color, a green color used on URL text in the
64 // currently highlighted entry in the autocomplete popup. It's a mix of
65 // |kURLTextColor|, the current text color, and the background color (the
66 // select highlight). It is more important to contrast with the background
67 // saturation than to look exactly like the foreground color.
68 GdkColor SelectedURLColor(GdkColor foreground, GdkColor background) {
69   color_utils::HSL fg_hsl;
70   color_utils::SkColorToHSL(libgtk2ui::GdkColorToSkColor(foreground),
71                             &fg_hsl);
72
73   color_utils::HSL bg_hsl;
74   color_utils::SkColorToHSL(libgtk2ui::GdkColorToSkColor(background),
75                             &bg_hsl);
76
77   color_utils::HSL hue_hsl;
78   color_utils::SkColorToHSL(libgtk2ui::GdkColorToSkColor(kURLTextColor),
79                             &hue_hsl);
80
81   // The saturation of the text should be opposite of the background, clamped
82   // to 0.2-0.8. We make sure it's greater than 0.2 so there's some color, but
83   // less than 0.8 so it's not the oversaturated neon-color.
84   double opposite_s = 1 - bg_hsl.s;
85   double s = std::max(0.2, std::min(0.8, opposite_s));
86
87   // The luminance should match the luminance of the foreground text.  Again,
88   // we clamp so as to have at some amount of color (green) in the text.
89   double opposite_l = fg_hsl.l;
90   double l = std::max(0.1, std::min(0.9, opposite_l));
91
92   color_utils::HSL output = { hue_hsl.h, s, l };
93   return libgtk2ui::SkColorToGdkColor(color_utils::HSLToSkColor(output, 255));
94 }
95
96 }  // namespace
97
98
99 namespace libgtk2ui {
100
101 // static
102 NativeThemeGtk2* NativeThemeGtk2::instance() {
103   CR_DEFINE_STATIC_LOCAL(NativeThemeGtk2, s_native_theme, ());
104   return &s_native_theme;
105 }
106
107 NativeThemeGtk2::NativeThemeGtk2()
108     : fake_window_(NULL),
109       fake_tooltip_(NULL),
110       fake_menu_item_(NULL) {
111 }
112
113 NativeThemeGtk2::~NativeThemeGtk2() {
114   if (fake_window_)
115     gtk_widget_destroy(fake_window_);
116   if (fake_tooltip_)
117     gtk_widget_destroy(fake_tooltip_);
118
119   fake_entry_.Destroy();
120   fake_label_.Destroy();
121   fake_button_.Destroy();
122   fake_tree_.Destroy();
123   fake_menu_.Destroy();
124 }
125
126 gfx::Size NativeThemeGtk2::GetPartSize(Part part,
127                                        State state,
128                                        const ExtraParams& extra) const {
129   if (part == kComboboxArrow)
130     return gfx::Size(12, 12);
131
132   return ui::NativeThemeBase::GetPartSize(part, state, extra);
133 }
134
135 void NativeThemeGtk2::Paint(SkCanvas* canvas,
136                             Part part,
137                             State state,
138                             const gfx::Rect& rect,
139                             const ExtraParams& extra) const {
140   if (rect.IsEmpty())
141     return;
142
143   switch (part) {
144     case kComboboxArrow:
145       PaintComboboxArrow(canvas, GetGtkState(state), rect);
146       return;
147
148     default:
149       NativeThemeBase::Paint(canvas, part, state, rect, extra);
150   }
151 }
152
153 SkColor NativeThemeGtk2::GetSystemColor(ColorId color_id) const {
154   if (color_id == kColorId_BlueButtonShadowColor)
155     return SK_ColorTRANSPARENT;
156
157   return GdkColorToSkColor(GetSystemGdkColor(color_id));
158 }
159
160 void NativeThemeGtk2::PaintMenuPopupBackground(
161     SkCanvas* canvas,
162     const gfx::Size& size,
163     const MenuBackgroundExtraParams& menu_background) const {
164   if (menu_background.corner_radius > 0) {
165     SkPaint paint;
166     paint.setStyle(SkPaint::kFill_Style);
167     paint.setFlags(SkPaint::kAntiAlias_Flag);
168     paint.setColor(GetSystemColor(kColorId_MenuBackgroundColor));
169
170     gfx::Path path;
171     SkRect rect = SkRect::MakeWH(SkIntToScalar(size.width()),
172                                  SkIntToScalar(size.height()));
173     SkScalar radius = SkIntToScalar(menu_background.corner_radius);
174     SkScalar radii[8] = {radius, radius, radius, radius,
175                          radius, radius, radius, radius};
176     path.addRoundRect(rect, radii);
177
178     canvas->drawPath(path, paint);
179   } else {
180     canvas->drawColor(GetSystemColor(kColorId_MenuBackgroundColor),
181                       SkXfermode::kSrc_Mode);
182   }
183 }
184
185 void NativeThemeGtk2::PaintMenuItemBackground(
186     SkCanvas* canvas,
187     State state,
188     const gfx::Rect& rect,
189     const MenuListExtraParams& menu_list) const {
190   SkColor color;
191   SkPaint paint;
192   switch (state) {
193     case NativeTheme::kNormal:
194     case NativeTheme::kDisabled:
195       color = GetSystemColor(NativeTheme::kColorId_MenuBackgroundColor);
196       paint.setColor(color);
197       break;
198     case NativeTheme::kHovered:
199       color = GetSystemColor(
200           NativeTheme::kColorId_FocusedMenuItemBackgroundColor);
201       paint.setColor(color);
202       break;
203     default:
204       NOTREACHED() << "Invalid state " << state;
205       break;
206   }
207   canvas->drawRect(gfx::RectToSkRect(rect), paint);
208 }
209
210 GdkColor NativeThemeGtk2::GetSystemGdkColor(ColorId color_id) const {
211   switch (color_id) {
212     // Windows
213     case kColorId_WindowBackground:
214       return GetWindowStyle()->bg[GTK_STATE_NORMAL];
215
216     // Dialogs
217     case kColorId_DialogBackground:
218       return GetWindowStyle()->bg[GTK_STATE_NORMAL];
219
220     // FocusableBorder
221     case kColorId_FocusedBorderColor:
222       return GetEntryStyle()->bg[GTK_STATE_SELECTED];
223     case kColorId_UnfocusedBorderColor:
224       return GetEntryStyle()->text_aa[GTK_STATE_NORMAL];
225
226     // MenuItem
227     case kColorId_EnabledMenuItemForegroundColor:
228     case kColorId_DisabledEmphasizedMenuItemForegroundColor:
229       return GetMenuItemStyle()->text[GTK_STATE_NORMAL];
230     case kColorId_DisabledMenuItemForegroundColor:
231       return GetMenuItemStyle()->text[GTK_STATE_INSENSITIVE];
232     case kColorId_SelectedMenuItemForegroundColor:
233       return GetMenuItemStyle()->text[GTK_STATE_SELECTED];
234     case kColorId_FocusedMenuItemBackgroundColor:
235       return GetMenuItemStyle()->bg[GTK_STATE_SELECTED];
236     case kColorId_HoverMenuItemBackgroundColor:
237       return GetMenuItemStyle()->bg[GTK_STATE_PRELIGHT];
238     case kColorId_FocusedMenuButtonBorderColor:
239       return GetEntryStyle()->bg[GTK_STATE_NORMAL];
240     case kColorId_HoverMenuButtonBorderColor:
241       return GetEntryStyle()->text_aa[GTK_STATE_PRELIGHT];
242     case kColorId_MenuBorderColor:
243     case kColorId_EnabledMenuButtonBorderColor:
244     case kColorId_MenuSeparatorColor: {
245       return GetMenuItemStyle()->text[GTK_STATE_INSENSITIVE];
246     }
247     case kColorId_MenuBackgroundColor:
248       return GetMenuStyle()->bg[GTK_STATE_NORMAL];
249
250     // Label
251     case kColorId_LabelEnabledColor:
252       return GetLabelStyle()->text[GTK_STATE_NORMAL];
253     case kColorId_LabelDisabledColor:
254       return GetLabelStyle()->text[GTK_STATE_INSENSITIVE];
255     case kColorId_LabelBackgroundColor:
256       return GetWindowStyle()->bg[GTK_STATE_NORMAL];
257
258     // Button
259     case kColorId_ButtonBackgroundColor:
260       return GetButtonStyle()->bg[GTK_STATE_NORMAL];
261     case kColorId_ButtonEnabledColor:
262     case kColorId_BlueButtonEnabledColor:
263       return GetButtonStyle()->text[GTK_STATE_NORMAL];
264     case kColorId_ButtonDisabledColor:
265     case kColorId_BlueButtonDisabledColor:
266       return GetButtonStyle()->text[GTK_STATE_INSENSITIVE];
267     case kColorId_ButtonHighlightColor:
268       return GetButtonStyle()->base[GTK_STATE_SELECTED];
269     case kColorId_ButtonHoverColor:
270     case kColorId_BlueButtonHoverColor:
271       return GetButtonStyle()->text[GTK_STATE_PRELIGHT];
272     case kColorId_ButtonHoverBackgroundColor:
273       return GetButtonStyle()->bg[GTK_STATE_PRELIGHT];
274     case kColorId_BlueButtonPressedColor:
275       return GetButtonStyle()->text[GTK_STATE_ACTIVE];
276     case kColorId_BlueButtonShadowColor:
277       // Should be handled in GetSystemColor().
278       NOTREACHED();
279       return GetButtonStyle()->text[GTK_STATE_NORMAL];
280
281     // Textfield
282     case kColorId_TextfieldDefaultColor:
283       return GetEntryStyle()->text[GTK_STATE_NORMAL];
284     case kColorId_TextfieldDefaultBackground:
285       return GetEntryStyle()->base[GTK_STATE_NORMAL];
286     case kColorId_TextfieldReadOnlyColor:
287       return GetEntryStyle()->text[GTK_STATE_INSENSITIVE];
288     case kColorId_TextfieldReadOnlyBackground:
289       return GetEntryStyle()->base[GTK_STATE_INSENSITIVE];
290     case kColorId_TextfieldSelectionColor:
291       return GetEntryStyle()->text[GTK_STATE_SELECTED];
292     case kColorId_TextfieldSelectionBackgroundFocused:
293       return GetEntryStyle()->base[GTK_STATE_SELECTED];
294
295     // Tooltips
296     case kColorId_TooltipBackground:
297       return GetTooltipStyle()->bg[GTK_STATE_NORMAL];
298     case kColorId_TooltipText:
299       return GetTooltipStyle()->fg[GTK_STATE_NORMAL];
300
301     // Trees and Tables (implemented on GTK using the same class)
302     case kColorId_TableBackground:
303     case kColorId_TreeBackground:
304       return GetTreeStyle()->bg[GTK_STATE_NORMAL];
305     case kColorId_TableText:
306     case kColorId_TreeText:
307       return GetTreeStyle()->text[GTK_STATE_NORMAL];
308     case kColorId_TableSelectedText:
309     case kColorId_TableSelectedTextUnfocused:
310     case kColorId_TreeSelectedText:
311     case kColorId_TreeSelectedTextUnfocused:
312       return GetTreeStyle()->text[GTK_STATE_SELECTED];
313     case kColorId_TableSelectionBackgroundFocused:
314     case kColorId_TableSelectionBackgroundUnfocused:
315     case kColorId_TreeSelectionBackgroundFocused:
316     case kColorId_TreeSelectionBackgroundUnfocused:
317       return GetTreeStyle()->bg[GTK_STATE_SELECTED];
318     case kColorId_TreeArrow:
319       return GetTreeStyle()->fg[GTK_STATE_NORMAL];
320     case kColorId_TableGroupingIndicatorColor:
321       return GetTreeStyle()->text_aa[GTK_STATE_NORMAL];
322
323       // Results Table
324     case kColorId_ResultsTableNormalBackground:
325       return GetEntryStyle()->base[GTK_STATE_NORMAL];
326     case kColorId_ResultsTableHoveredBackground: {
327       GtkStyle* entry_style = GetEntryStyle();
328       return GdkAlphaBlend(
329           entry_style->base[GTK_STATE_NORMAL],
330           entry_style->base[GTK_STATE_SELECTED], 0x80);
331     }
332     case kColorId_ResultsTableSelectedBackground:
333       return GetEntryStyle()->base[GTK_STATE_SELECTED];
334     case kColorId_ResultsTableNormalText:
335     case kColorId_ResultsTableHoveredText:
336       return GetEntryStyle()->text[GTK_STATE_NORMAL];
337     case kColorId_ResultsTableSelectedText:
338       return GetEntryStyle()->text[GTK_STATE_SELECTED];
339     case kColorId_ResultsTableNormalDimmedText:
340     case kColorId_ResultsTableHoveredDimmedText: {
341       GtkStyle* entry_style = GetEntryStyle();
342       return GdkAlphaBlend(
343           entry_style->text[GTK_STATE_NORMAL],
344           entry_style->base[GTK_STATE_NORMAL], 0x80);
345     }
346     case kColorId_ResultsTableSelectedDimmedText: {
347       GtkStyle* entry_style = GetEntryStyle();
348       return GdkAlphaBlend(
349           entry_style->text[GTK_STATE_SELECTED],
350           entry_style->base[GTK_STATE_NORMAL], 0x80);
351     }
352     case kColorId_ResultsTableNormalUrl:
353     case kColorId_ResultsTableHoveredUrl: {
354       return NormalURLColor(GetEntryStyle()->text[GTK_STATE_NORMAL]);
355     }
356     case kColorId_ResultsTableSelectedUrl: {
357       GtkStyle* entry_style = GetEntryStyle();
358       return SelectedURLColor(entry_style->text[GTK_STATE_SELECTED],
359                               entry_style->base[GTK_STATE_SELECTED]);
360     }
361     case kColorId_ResultsTableNormalDivider: {
362       GtkStyle* win_style = GetWindowStyle();
363       return GdkAlphaBlend(win_style->text[GTK_STATE_NORMAL],
364                            win_style->bg[GTK_STATE_NORMAL], 0x34);
365     }
366     case kColorId_ResultsTableHoveredDivider: {
367       GtkStyle* win_style = GetWindowStyle();
368       return GdkAlphaBlend(win_style->text[GTK_STATE_PRELIGHT],
369                            win_style->bg[GTK_STATE_PRELIGHT], 0x34);
370     }
371     case kColorId_ResultsTableSelectedDivider: {
372       GtkStyle* win_style = GetWindowStyle();
373       return GdkAlphaBlend(win_style->text[GTK_STATE_SELECTED],
374                            win_style->bg[GTK_STATE_SELECTED], 0x34);
375     }
376     case kColorId_NumColors:
377       NOTREACHED();
378       break;
379   }
380
381   return SkColorToGdkColor(kInvalidColorIdColor);
382 }
383
384 GtkWidget* NativeThemeGtk2::GetRealizedWindow() const {
385   if (!fake_window_) {
386     fake_window_ = gtk_window_new(GTK_WINDOW_TOPLEVEL);
387     gtk_widget_realize(fake_window_);
388   }
389
390   return fake_window_;
391 }
392
393 GtkStyle* NativeThemeGtk2::GetWindowStyle() const {
394   return gtk_rc_get_style(GetRealizedWindow());
395 }
396
397 GtkStyle* NativeThemeGtk2::GetEntryStyle() const {
398   if (!fake_entry_.get()) {
399     fake_entry_.Own(gtk_entry_new());
400
401     // The fake entry needs to be in the window so it can be realized so we can
402     // use the computed parts of the style.
403     gtk_container_add(GTK_CONTAINER(GetRealizedWindow()), fake_entry_.get());
404     gtk_widget_realize(fake_entry_.get());
405   }
406   return gtk_rc_get_style(fake_entry_.get());
407 }
408
409 GtkStyle* NativeThemeGtk2::GetLabelStyle() const {
410   if (!fake_label_.get())
411     fake_label_.Own(gtk_label_new(""));
412
413   return gtk_rc_get_style(fake_label_.get());
414 }
415
416 GtkStyle* NativeThemeGtk2::GetButtonStyle() const {
417   if (!fake_button_.get())
418     fake_button_.Own(gtk_button_new());
419
420   return gtk_rc_get_style(fake_button_.get());
421 }
422
423 GtkStyle* NativeThemeGtk2::GetTreeStyle() const {
424   if (!fake_tree_.get())
425     fake_tree_.Own(gtk_tree_view_new());
426
427   return gtk_rc_get_style(fake_tree_.get());
428 }
429
430 GtkStyle* NativeThemeGtk2::GetTooltipStyle() const {
431   if (!fake_tooltip_) {
432     fake_tooltip_ = gtk_window_new(GTK_WINDOW_TOPLEVEL);
433     gtk_widget_set_name(fake_tooltip_, "gtk-tooltip");
434     gtk_widget_realize(fake_tooltip_);
435   }
436   return gtk_rc_get_style(fake_tooltip_);
437 }
438
439 GtkStyle* NativeThemeGtk2::GetMenuStyle() const {
440   if (!fake_menu_.get())
441     fake_menu_.Own(gtk_custom_menu_new());
442   return gtk_rc_get_style(fake_menu_.get());
443 }
444
445 GtkStyle* NativeThemeGtk2::GetMenuItemStyle() const {
446   if (!fake_menu_item_) {
447     if (!fake_menu_.get())
448       fake_menu_.Own(gtk_custom_menu_new());
449
450     fake_menu_item_ = gtk_custom_menu_item_new();
451     gtk_menu_shell_append(GTK_MENU_SHELL(fake_menu_.get()), fake_menu_item_);
452   }
453
454   return gtk_rc_get_style(fake_menu_item_);
455 }
456
457 void NativeThemeGtk2::PaintComboboxArrow(SkCanvas* canvas,
458                                          GtkStateType state,
459                                          const gfx::Rect& rect) const {
460   GdkPixmap* pm = gdk_pixmap_new(gtk_widget_get_window(GetRealizedWindow()),
461                                  rect.width(),
462                                  rect.height(),
463                                  -1);
464   // Paint the background.
465   gtk_paint_flat_box(GetWindowStyle(),
466                      pm,
467                      state,
468                      GTK_SHADOW_NONE,
469                      NULL,
470                      GetRealizedWindow(),
471                      NULL, 0, 0, rect.width(), rect.height());
472   gtk_paint_arrow(GetWindowStyle(),
473                   pm,
474                   state,
475                   GTK_SHADOW_NONE,
476                   NULL,
477                   GetRealizedWindow(),
478                   NULL,
479                   GTK_ARROW_DOWN,
480                   true,
481                   0, 0, rect.width(), rect.height());
482   GdkPixbuf* pb = gdk_pixbuf_get_from_drawable(NULL,
483                                                pm,
484                                                gdk_drawable_get_colormap(pm),
485                                                0, 0,
486                                                0, 0,
487                                                rect.width(), rect.height());
488   SkBitmap arrow = GdkPixbufToImageSkia(pb);
489   canvas->drawBitmap(arrow, rect.x(), rect.y());
490
491   g_object_unref(pb);
492   g_object_unref(pm);
493 }
494
495 }  // namespace libgtk2ui