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_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"
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
,
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
),
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.
55 if (fg_hsl
.l
< hue_hsl
.l
)
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
),
74 color_utils::HSL bg_hsl
;
75 color_utils::SkColorToHSL(libgtk2ui::GdkColorToSkColor(background
),
78 color_utils::HSL hue_hsl
;
79 color_utils::SkColorToHSL(libgtk2ui::GdkColorToSkColor(kURLTextColor
),
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
)));
105 namespace libgtk2ui
{
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
),
116 fake_menu_item_(NULL
) {
119 NativeThemeGtk2::~NativeThemeGtk2() {
121 gtk_widget_destroy(fake_window_
);
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
,
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
,
144 const gfx::Rect
& rect
,
145 const ExtraParams
& extra
) const {
151 PaintComboboxArrow(canvas
, GetGtkState(state
), rect
);
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(
168 const gfx::Size
& size
,
169 const MenuBackgroundExtraParams
& menu_background
) const {
170 if (menu_background
.corner_radius
> 0) {
172 paint
.setStyle(SkPaint::kFill_Style
);
173 paint
.setFlags(SkPaint::kAntiAlias_Flag
);
174 paint
.setColor(GetSystemColor(kColorId_MenuBackgroundColor
));
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
);
186 canvas
->drawColor(GetSystemColor(kColorId_MenuBackgroundColor
),
187 SkXfermode::kSrc_Mode
);
191 void NativeThemeGtk2::PaintMenuItemBackground(
194 const gfx::Rect
& rect
,
195 const MenuListExtraParams
& menu_list
) const {
199 case NativeTheme::kNormal
:
200 case NativeTheme::kDisabled
:
201 color
= GetSystemColor(NativeTheme::kColorId_MenuBackgroundColor
);
202 paint
.setColor(color
);
204 case NativeTheme::kHovered
:
205 color
= GetSystemColor(
206 NativeTheme::kColorId_FocusedMenuItemBackgroundColor
);
207 paint
.setColor(color
);
210 NOTREACHED() << "Invalid state " << state
;
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);
221 case kColorId_WindowBackground
:
222 return GetWindowStyle()->bg
[GTK_STATE_NORMAL
];
225 case kColorId_DialogBackground
:
226 return GetWindowStyle()->bg
[GTK_STATE_NORMAL
];
229 case kColorId_FocusedBorderColor
:
230 return GetEntryStyle()->bg
[GTK_STATE_SELECTED
];
231 case kColorId_UnfocusedBorderColor
:
232 return GetEntryStyle()->text_aa
[GTK_STATE_NORMAL
];
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
];
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
];
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().
287 return GetButtonStyle()->text
[GTK_STATE_NORMAL
];
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
];
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
];
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
]);
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);
421 case kColorId_ChromeIconGrey
:
422 case kColorId_GoogleBlue
:
423 case kColorId_NumColors
:
428 return SkColorToGdkColor(kInvalidColorIdColor
);
431 GtkWidget
* NativeThemeGtk2::GetRealizedWindow() const {
433 fake_window_
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
434 gtk_widget_realize(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
,
506 const gfx::Rect
& rect
) const {
507 GdkPixmap
* pm
= gdk_pixmap_new(gtk_widget_get_window(GetRealizedWindow()),
511 // Paint the background.
512 gtk_paint_flat_box(GetWindowStyle(),
518 NULL
, 0, 0, rect
.width(), rect
.height());
519 gtk_paint_arrow(GetWindowStyle(),
528 0, 0, rect
.width(), rect
.height());
529 GdkPixbuf
* pb
= gdk_pixbuf_get_from_drawable(NULL
,
531 gdk_drawable_get_colormap(pm
),
534 rect
.width(), rect
.height());
535 SkBitmap arrow
= GdkPixbufToImageSkia(pb
);
536 canvas
->drawBitmap(arrow
, rect
.x(), rect
.y());
542 } // namespace libgtk2ui