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"
9 #include "chrome/browser/ui/libgtk2ui/chrome_gtk_frame.h"
10 #include "chrome/browser/ui/libgtk2ui/chrome_gtk_menu_subclasses.h"
11 #include "chrome/browser/ui/libgtk2ui/gtk2_ui.h"
12 #include "chrome/browser/ui/libgtk2ui/gtk2_util.h"
13 #include "chrome/browser/ui/libgtk2ui/skia_utils_gtk2.h"
14 #include "third_party/skia/include/core/SkColor.h"
15 #include "ui/gfx/color_utils.h"
16 #include "ui/gfx/geometry/rect.h"
17 #include "ui/gfx/geometry/size.h"
18 #include "ui/gfx/path.h"
19 #include "ui/gfx/skia_util.h"
20 #include "ui/native_theme/common_theme.h"
25 // Theme colors returned by GetSystemColor().
26 const SkColor kInvalidColorIdColor
= SkColorSetRGB(255, 0, 128);
27 const SkColor kURLTextColor
= SkColorSetRGB(0x0b, 0x80, 0x43);
29 // Generates the normal URL color, a green color used in unhighlighted URL
30 // text. It is a mix of |kURLTextColor| and the current text color. Unlike the
31 // selected text color, it is more important to match the qualities of the
32 // foreground typeface color instead of taking the background into account.
33 SkColor
NormalURLColor(SkColor foreground
) {
34 color_utils::HSL fg_hsl
, hue_hsl
;
35 color_utils::SkColorToHSL(foreground
, &fg_hsl
);
36 color_utils::SkColorToHSL(kURLTextColor
, &hue_hsl
);
38 // Only allow colors that have a fair amount of saturation in them (color vs
39 // white). This means that our output color will always be fairly green.
40 double s
= std::max(0.5, fg_hsl
.s
);
42 // Make sure the luminance is at least as bright as the |kURLTextColor| green
43 // would be if we were to use that.
45 if (fg_hsl
.l
< hue_hsl
.l
)
48 l
= (fg_hsl
.l
+ hue_hsl
.l
) / 2;
50 color_utils::HSL output
= { hue_hsl
.h
, s
, l
};
51 return color_utils::HSLToSkColor(output
, 255);
54 // Generates the selected URL color, a green color used on URL text in the
55 // currently highlighted entry in the autocomplete popup. It's a mix of
56 // |kURLTextColor|, the current text color, and the background color (the
57 // select highlight). It is more important to contrast with the background
58 // saturation than to look exactly like the foreground color.
59 SkColor
SelectedURLColor(SkColor foreground
, SkColor background
) {
60 color_utils::HSL fg_hsl
, bg_hsl
, hue_hsl
;
61 color_utils::SkColorToHSL(foreground
, &fg_hsl
);
62 color_utils::SkColorToHSL(background
, &bg_hsl
);
63 color_utils::SkColorToHSL(kURLTextColor
, &hue_hsl
);
65 // The saturation of the text should be opposite of the background, clamped
66 // to 0.2-0.8. We make sure it's greater than 0.2 so there's some color, but
67 // less than 0.8 so it's not the oversaturated neon-color.
68 double opposite_s
= 1 - bg_hsl
.s
;
69 double s
= std::max(0.2, std::min(0.8, opposite_s
));
71 // The luminance should match the luminance of the foreground text. Again,
72 // we clamp so as to have at some amount of color (green) in the text.
73 double opposite_l
= fg_hsl
.l
;
74 double l
= std::max(0.1, std::min(0.9, opposite_l
));
76 color_utils::HSL output
= { hue_hsl
.h
, s
, l
};
77 return color_utils::HSLToSkColor(output
, 255);
93 #if GTK_MAJOR_VERSION == 2
94 // Same order as enum WidgetState above
95 const GtkStateType stateMap
[] = {
100 GTK_STATE_INSENSITIVE
,
104 SkColor
GetFGColor(GtkWidget
* widget
, WidgetState state
) {
105 return GdkColorToSkColor(gtk_rc_get_style(widget
)->fg
[stateMap
[state
]]);
107 SkColor
GetBGColor(GtkWidget
* widget
, WidgetState state
) {
108 return GdkColorToSkColor(gtk_rc_get_style(widget
)->bg
[stateMap
[state
]]);
111 SkColor
GetTextColor(GtkWidget
* widget
, WidgetState state
) {
112 return GdkColorToSkColor(gtk_rc_get_style(widget
)->text
[stateMap
[state
]]);
114 SkColor
GetTextAAColor(GtkWidget
* widget
, WidgetState state
) {
115 return GdkColorToSkColor(gtk_rc_get_style(widget
)->text_aa
[stateMap
[state
]]);
117 SkColor
GetBaseColor(GtkWidget
* widget
, WidgetState state
) {
118 return GdkColorToSkColor(gtk_rc_get_style(widget
)->base
[stateMap
[state
]]);
123 // Same order as enum WidgetState above
124 const GtkStateFlags stateMap
[] = {
125 GTK_STATE_FLAG_NORMAL
,
126 GTK_STATE_FLAG_ACTIVE
,
127 GTK_STATE_FLAG_PRELIGHT
,
128 GTK_STATE_FLAG_SELECTED
,
129 GTK_STATE_FLAG_INSENSITIVE
,
133 SkColor
GetFGColor(GtkWidget
* widget
, WidgetState state
) {
135 gtk_style_context_get_color(
136 gtk_widget_get_style_context(widget
), stateMap
[state
], &color
);
137 return SkColorSetRGB(color
.red
* 255, color
.green
* 255, color
.blue
* 255);
139 SkColor
GetBGColor(GtkWidget
* widget
, WidgetState state
) {
142 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
143 gtk_style_context_get_background_color(
144 gtk_widget_get_style_context(widget
), stateMap
[state
], &color
);
145 G_GNUC_END_IGNORE_DEPRECATIONS
147 // Hack for default color
148 if (color
.alpha
== 0.0)
149 color
= {1, 1, 1, 1};
151 return SkColorSetRGB(color
.red
* 255, color
.green
* 255, color
.blue
* 255);
154 SkColor
GetTextColor(GtkWidget
* widget
, WidgetState state
) {
155 return GetFGColor(widget
, state
);
157 SkColor
GetTextAAColor(GtkWidget
* widget
, WidgetState state
) {
158 return GetFGColor(widget
, state
);
160 SkColor
GetBaseColor(GtkWidget
* widget
, WidgetState state
) {
161 return GetBGColor(widget
, state
);
166 NativeThemeGtk2
* NativeThemeGtk2::instance() {
167 CR_DEFINE_STATIC_LOCAL(NativeThemeGtk2
, s_native_theme
, ());
168 return &s_native_theme
;
171 // Constructors automatically called
172 NativeThemeGtk2::NativeThemeGtk2() {}
173 // This doesn't actually get called
174 NativeThemeGtk2::~NativeThemeGtk2() {}
176 void NativeThemeGtk2::PaintMenuPopupBackground(
178 const gfx::Size
& size
,
179 const MenuBackgroundExtraParams
& menu_background
) const {
180 if (menu_background
.corner_radius
> 0) {
182 paint
.setStyle(SkPaint::kFill_Style
);
183 paint
.setFlags(SkPaint::kAntiAlias_Flag
);
184 paint
.setColor(GetSystemColor(kColorId_MenuBackgroundColor
));
187 SkRect rect
= SkRect::MakeWH(SkIntToScalar(size
.width()),
188 SkIntToScalar(size
.height()));
189 SkScalar radius
= SkIntToScalar(menu_background
.corner_radius
);
190 SkScalar radii
[8] = {radius
, radius
, radius
, radius
,
191 radius
, radius
, radius
, radius
};
192 path
.addRoundRect(rect
, radii
);
194 canvas
->drawPath(path
, paint
);
196 canvas
->drawColor(GetSystemColor(kColorId_MenuBackgroundColor
),
197 SkXfermode::kSrc_Mode
);
201 void NativeThemeGtk2::PaintMenuItemBackground(
204 const gfx::Rect
& rect
,
205 const MenuListExtraParams
& menu_list
) const {
209 case NativeTheme::kNormal
:
210 case NativeTheme::kDisabled
:
211 color
= GetSystemColor(NativeTheme::kColorId_MenuBackgroundColor
);
212 paint
.setColor(color
);
214 case NativeTheme::kHovered
:
215 color
= GetSystemColor(
216 NativeTheme::kColorId_FocusedMenuItemBackgroundColor
);
217 paint
.setColor(color
);
220 NOTREACHED() << "Invalid state " << state
;
223 canvas
->drawRect(gfx::RectToSkRect(rect
), paint
);
226 SkColor
NativeThemeGtk2::GetSystemColor(ColorId color_id
) const {
227 const SkColor kPositiveTextColor
= SkColorSetRGB(0x0b, 0x80, 0x43);
228 const SkColor kNegativeTextColor
= SkColorSetRGB(0xc5, 0x39, 0x29);
232 case kColorId_WindowBackground
:
233 return GetBGColor(GetWindow(), SELECTED
);
236 case kColorId_DialogBackground
:
237 return GetBGColor(GetWindow(), NORMAL
);
240 case kColorId_FocusedBorderColor
:
241 return GetBGColor(GetEntry(), SELECTED
);
242 case kColorId_UnfocusedBorderColor
:
243 return GetTextAAColor(GetEntry(), NORMAL
);
246 case kColorId_EnabledMenuItemForegroundColor
:
247 case kColorId_DisabledEmphasizedMenuItemForegroundColor
:
248 return GetTextColor(GetMenuItem(), NORMAL
);
249 case kColorId_DisabledMenuItemForegroundColor
:
250 return GetTextColor(GetMenuItem(), INSENSITIVE
);
251 case kColorId_SelectedMenuItemForegroundColor
:
252 return GetTextColor(GetMenuItem(), SELECTED
);
253 case kColorId_FocusedMenuItemBackgroundColor
:
254 return GetBGColor(GetMenuItem(), SELECTED
);
255 case kColorId_HoverMenuItemBackgroundColor
:
256 return GetBGColor(GetMenuItem(), PRELIGHT
);
257 case kColorId_FocusedMenuButtonBorderColor
:
258 return GetBGColor(GetEntry(), NORMAL
);
259 case kColorId_HoverMenuButtonBorderColor
:
260 return GetTextAAColor(GetEntry(), PRELIGHT
);
261 case kColorId_MenuBorderColor
:
262 case kColorId_EnabledMenuButtonBorderColor
:
263 case kColorId_MenuSeparatorColor
: {
264 return GetTextColor(GetMenuItem(), INSENSITIVE
);
266 case kColorId_MenuBackgroundColor
:
267 return GetBGColor(GetMenu(), NORMAL
);
270 case kColorId_LabelEnabledColor
:
271 return GetTextColor(GetEntry(), NORMAL
);
272 case kColorId_LabelDisabledColor
:
273 return GetTextColor(GetLabel(), INSENSITIVE
);
274 case kColorId_LabelBackgroundColor
:
275 return GetBGColor(GetWindow(), NORMAL
);
278 // TODO(estade): get these from the gtk theme instead of hardcoding.
279 case kColorId_LinkDisabled
:
280 return SK_ColorBLACK
;
281 case kColorId_LinkEnabled
:
282 return SkColorSetRGB(0x33, 0x67, 0xD6);
283 case kColorId_LinkPressed
:
287 case kColorId_ButtonBackgroundColor
:
288 return GetBGColor(GetButton(), NORMAL
);
289 case kColorId_ButtonEnabledColor
:
290 return GetTextColor(GetButton(), NORMAL
);
291 case kColorId_BlueButtonEnabledColor
:
292 return GetTextColor(GetBlueButton(), NORMAL
);
293 case kColorId_ButtonDisabledColor
:
294 return GetTextColor(GetButton(), INSENSITIVE
);
295 case kColorId_BlueButtonDisabledColor
:
296 return GetTextColor(GetBlueButton(), INSENSITIVE
);
297 case kColorId_ButtonHighlightColor
:
298 return GetBaseColor(GetButton(), SELECTED
);
299 case kColorId_ButtonHoverColor
:
300 return GetTextColor(GetButton(), PRELIGHT
);
301 case kColorId_BlueButtonHoverColor
:
302 return GetTextColor(GetBlueButton(), PRELIGHT
);
303 case kColorId_ButtonHoverBackgroundColor
:
304 return GetBGColor(GetButton(), PRELIGHT
);
305 case kColorId_BlueButtonPressedColor
:
306 return GetTextColor(GetBlueButton(), ACTIVE
);
307 case kColorId_BlueButtonShadowColor
:
308 return SK_ColorTRANSPARENT
;
309 // return GetTextColor(GetButton(), NORMAL);
312 case kColorId_TextfieldDefaultColor
:
313 return GetTextColor(GetEntry(), NORMAL
);
314 case kColorId_TextfieldDefaultBackground
:
315 return GetBaseColor(GetEntry(), NORMAL
);
316 case kColorId_TextfieldReadOnlyColor
:
317 return GetTextColor(GetEntry(), INSENSITIVE
);
318 case kColorId_TextfieldReadOnlyBackground
:
319 return GetBaseColor(GetEntry(), INSENSITIVE
);
320 case kColorId_TextfieldSelectionColor
:
321 return GetTextColor(GetEntry(), SELECTED
);
322 case kColorId_TextfieldSelectionBackgroundFocused
:
323 return GetBaseColor(GetEntry(), SELECTED
);
326 case kColorId_TooltipBackground
:
327 return GetBGColor(GetTooltip(), NORMAL
);
328 case kColorId_TooltipText
:
329 return GetFGColor(GetTooltip(), NORMAL
);
331 // Trees and Tables (implemented on GTK using the same class)
332 case kColorId_TableBackground
:
333 case kColorId_TreeBackground
:
334 return GetBGColor(GetTree(), NORMAL
);
335 case kColorId_TableText
:
336 case kColorId_TreeText
:
337 return GetTextColor(GetTree(), NORMAL
);
338 case kColorId_TableSelectedText
:
339 case kColorId_TableSelectedTextUnfocused
:
340 case kColorId_TreeSelectedText
:
341 case kColorId_TreeSelectedTextUnfocused
:
342 return GetTextColor(GetTree(), SELECTED
);
343 case kColorId_TableSelectionBackgroundFocused
:
344 case kColorId_TableSelectionBackgroundUnfocused
:
345 case kColorId_TreeSelectionBackgroundFocused
:
346 case kColorId_TreeSelectionBackgroundUnfocused
:
347 return GetBGColor(GetTree(), SELECTED
);
348 case kColorId_TreeArrow
:
349 return GetFGColor(GetTree(), NORMAL
);
350 case kColorId_TableGroupingIndicatorColor
:
351 return GetTextAAColor(GetTree(), NORMAL
);
354 case kColorId_ResultsTableNormalBackground
:
355 return GetBaseColor(GetEntry(), NORMAL
);
356 case kColorId_ResultsTableHoveredBackground
:
357 return color_utils::AlphaBlend(GetBaseColor(GetEntry(), NORMAL
),
358 GetBaseColor(GetEntry(), SELECTED
),
360 case kColorId_ResultsTableSelectedBackground
:
361 return GetBaseColor(GetEntry(), SELECTED
);
362 case kColorId_ResultsTableNormalText
:
363 case kColorId_ResultsTableHoveredText
:
364 return GetTextColor(GetEntry(), NORMAL
);
365 case kColorId_ResultsTableSelectedText
:
366 return GetTextColor(GetEntry(), SELECTED
);
367 case kColorId_ResultsTableNormalDimmedText
:
368 case kColorId_ResultsTableHoveredDimmedText
:
369 return color_utils::AlphaBlend(GetTextColor(GetEntry(), NORMAL
),
370 GetBaseColor(GetEntry(), NORMAL
),
372 case kColorId_ResultsTableSelectedDimmedText
:
373 return color_utils::AlphaBlend(GetTextColor(GetEntry(), SELECTED
),
374 GetBaseColor(GetEntry(), NORMAL
),
376 case kColorId_ResultsTableNormalUrl
:
377 case kColorId_ResultsTableHoveredUrl
:
378 return NormalURLColor(GetTextColor(GetEntry(), NORMAL
));
380 case kColorId_ResultsTableSelectedUrl
:
381 return SelectedURLColor(GetTextColor(GetEntry(), SELECTED
),
382 GetBaseColor(GetEntry(), SELECTED
));
383 case kColorId_ResultsTableNormalDivider
:
384 return color_utils::AlphaBlend(GetTextColor(GetWindow(), NORMAL
),
385 GetBGColor(GetWindow(), NORMAL
),
387 case kColorId_ResultsTableHoveredDivider
:
388 return color_utils::AlphaBlend(GetTextColor(GetWindow(), PRELIGHT
),
389 GetBGColor(GetWindow(), PRELIGHT
),
391 case kColorId_ResultsTableSelectedDivider
:
392 return color_utils::AlphaBlend(GetTextColor(GetWindow(), SELECTED
),
393 GetBGColor(GetWindow(), SELECTED
),
396 case kColorId_ResultsTablePositiveText
: {
397 return color_utils::GetReadableColor(kPositiveTextColor
,
398 GetBaseColor(GetEntry(), NORMAL
));
400 case kColorId_ResultsTablePositiveHoveredText
: {
401 return color_utils::GetReadableColor(kPositiveTextColor
,
402 GetBaseColor(GetEntry(), PRELIGHT
));
404 case kColorId_ResultsTablePositiveSelectedText
: {
405 return color_utils::GetReadableColor(kPositiveTextColor
,
406 GetBaseColor(GetEntry(), SELECTED
));
408 case kColorId_ResultsTableNegativeText
: {
409 return color_utils::GetReadableColor(kNegativeTextColor
,
410 GetBaseColor(GetEntry(), NORMAL
));
412 case kColorId_ResultsTableNegativeHoveredText
: {
413 return color_utils::GetReadableColor(kNegativeTextColor
,
414 GetBaseColor(GetEntry(), PRELIGHT
));
416 case kColorId_ResultsTableNegativeSelectedText
: {
417 return color_utils::GetReadableColor(kNegativeTextColor
,
418 GetBaseColor(GetEntry(), SELECTED
));
422 case kColorId_ThrobberSpinningColor
:
423 case kColorId_ThrobberLightColor
: {
424 return GetBGColor(GetEntry(), SELECTED
);
427 case kColorId_ThrobberWaitingColor
: {
428 return color_utils::AlphaBlend(GetBGColor(GetEntry(), SELECTED
),
429 GetBGColor(GetWindow(), NORMAL
),
434 case kColorId_ChromeIconGrey
:
435 case kColorId_GoogleBlue
:
436 case kColorId_NumColors
:
441 return kInvalidColorIdColor
;
444 GtkWidget
* NativeThemeGtk2::GetWindow() const {
445 if (!fake_window_
.get()) {
446 fake_window_
.Own(chrome_gtk_frame_new());
447 gtk_widget_realize(fake_window_
.get());
450 return fake_window_
.get();
453 GtkWidget
* NativeThemeGtk2::GetEntry() const {
454 if (!fake_entry_
.get()) {
455 fake_entry_
.Own(gtk_entry_new());
457 // The fake entry needs to be in the window so it can be realized so we can
458 // use the computed parts of the style.
459 gtk_container_add(GTK_CONTAINER(GetWindow()), fake_entry_
.get());
460 gtk_widget_realize(fake_entry_
.get());
463 return fake_entry_
.get();
466 GtkWidget
* NativeThemeGtk2::GetLabel() const {
467 if (!fake_label_
.get())
468 fake_label_
.Own(gtk_label_new(""));
470 return fake_label_
.get();
473 GtkWidget
* NativeThemeGtk2::GetButton() const {
474 if (!fake_button_
.get())
475 fake_button_
.Own(gtk_button_new());
477 return fake_button_
.get();
480 GtkWidget
* NativeThemeGtk2::GetBlueButton() const {
481 if (!fake_bluebutton_
.get()) {
482 fake_bluebutton_
.Own(gtk_button_new());
483 TurnButtonBlue(fake_bluebutton_
.get());
486 return fake_bluebutton_
.get();
489 GtkWidget
* NativeThemeGtk2::GetTree() const {
490 if (!fake_tree_
.get())
491 fake_tree_
.Own(gtk_tree_view_new());
493 return fake_tree_
.get();
496 GtkWidget
* NativeThemeGtk2::GetTooltip() const {
497 if (!fake_tooltip_
.get()) {
498 fake_tooltip_
.Own(gtk_window_new(GTK_WINDOW_TOPLEVEL
));
499 gtk_widget_set_name(fake_tooltip_
.get(), "gtk-tooltip");
500 gtk_widget_realize(fake_tooltip_
.get());
503 return fake_tooltip_
.get();
506 GtkWidget
* NativeThemeGtk2::GetMenu() const {
507 if (!fake_menu_
.get())
508 fake_menu_
.Own(gtk_custom_menu_new());
510 return fake_menu_
.get();
513 GtkWidget
* NativeThemeGtk2::GetMenuItem() const {
514 if (!fake_menu_item_
.get()) {
515 fake_menu_item_
.Own(gtk_custom_menu_item_new());
516 gtk_menu_shell_append(GTK_MENU_SHELL(GetMenu()), fake_menu_item_
.get());
519 return fake_menu_item_
.get();
522 } // namespace libgtk2ui