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/skia_utils_gtk2.h"
11 #include "ui/gfx/color_utils.h"
12 #include "ui/gfx/path.h"
13 #include "ui/gfx/rect.h"
14 #include "ui/gfx/size.h"
15 #include "ui/gfx/skia_util.h"
16 #include "ui/native_theme/common_theme.h"
20 // Theme colors returned by GetSystemColor().
21 const SkColor kInvalidColorIdColor
= SkColorSetRGB(255, 0, 128);
23 const GdkColor kURLTextColor
= GDK_COLOR_RGB(0x00, 0x88, 0x00);
25 GdkColor
GdkAlphaBlend(GdkColor foreground
,
28 return libgtk2ui::SkColorToGdkColor(
29 color_utils::AlphaBlend(libgtk2ui::GdkColorToSkColor(foreground
),
30 libgtk2ui::GdkColorToSkColor(background
), alpha
));
33 // Generates the normal URL color, a green color used in unhighlighted URL
34 // text. It is a mix of |kURLTextColor| and the current text color. Unlike the
35 // selected text color, it is more important to match the qualities of the
36 // foreground typeface color instead of taking the background into account.
37 GdkColor
NormalURLColor(GdkColor foreground
) {
38 color_utils::HSL fg_hsl
;
39 color_utils::SkColorToHSL(libgtk2ui::GdkColorToSkColor(foreground
), &fg_hsl
);
41 color_utils::HSL hue_hsl
;
42 color_utils::SkColorToHSL(libgtk2ui::GdkColorToSkColor(kURLTextColor
),
45 // Only allow colors that have a fair amount of saturation in them (color vs
46 // white). This means that our output color will always be fairly green.
47 double s
= std::max(0.5, fg_hsl
.s
);
49 // Make sure the luminance is at least as bright as the |kURLTextColor| green
50 // would be if we were to use that.
52 if (fg_hsl
.l
< hue_hsl
.l
)
55 l
= (fg_hsl
.l
+ hue_hsl
.l
) / 2;
57 color_utils::HSL output
= { hue_hsl
.h
, s
, l
};
58 return libgtk2ui::SkColorToGdkColor(color_utils::HSLToSkColor(output
, 255));
61 // Generates the selected URL color, a green color used on URL text in the
62 // currently highlighted entry in the autocomplete popup. It's a mix of
63 // |kURLTextColor|, the current text color, and the background color (the
64 // select highlight). It is more important to contrast with the background
65 // saturation than to look exactly like the foreground color.
66 GdkColor
SelectedURLColor(GdkColor foreground
, GdkColor background
) {
67 color_utils::HSL fg_hsl
;
68 color_utils::SkColorToHSL(libgtk2ui::GdkColorToSkColor(foreground
),
71 color_utils::HSL bg_hsl
;
72 color_utils::SkColorToHSL(libgtk2ui::GdkColorToSkColor(background
),
75 color_utils::HSL hue_hsl
;
76 color_utils::SkColorToHSL(libgtk2ui::GdkColorToSkColor(kURLTextColor
),
79 // The saturation of the text should be opposite of the background, clamped
80 // to 0.2-0.8. We make sure it's greater than 0.2 so there's some color, but
81 // less than 0.8 so it's not the oversaturated neon-color.
82 double opposite_s
= 1 - bg_hsl
.s
;
83 double s
= std::max(0.2, std::min(0.8, opposite_s
));
85 // The luminance should match the luminance of the foreground text. Again,
86 // we clamp so as to have at some amount of color (green) in the text.
87 double opposite_l
= fg_hsl
.l
;
88 double l
= std::max(0.1, std::min(0.9, opposite_l
));
90 color_utils::HSL output
= { hue_hsl
.h
, s
, l
};
91 return libgtk2ui::SkColorToGdkColor(color_utils::HSLToSkColor(output
, 255));
100 NativeThemeGtk2
* NativeThemeGtk2::instance() {
101 CR_DEFINE_STATIC_LOCAL(NativeThemeGtk2
, s_native_theme
, ());
102 return &s_native_theme
;
105 NativeThemeGtk2::NativeThemeGtk2()
106 : fake_window_(NULL
),
107 fake_menu_item_(NULL
) {
110 NativeThemeGtk2::~NativeThemeGtk2() {
112 gtk_widget_destroy(fake_window_
);
113 fake_entry_
.Destroy();
114 fake_label_
.Destroy();
115 fake_button_
.Destroy();
116 fake_tree_
.Destroy();
117 fake_menu_
.Destroy();
120 SkColor
NativeThemeGtk2::GetSystemColor(ColorId color_id
) const {
121 return GdkColorToSkColor(GetSystemGdkColor(color_id
));
124 void NativeThemeGtk2::PaintMenuPopupBackground(
126 const gfx::Size
& size
,
127 const MenuBackgroundExtraParams
& menu_background
) const {
128 if (menu_background
.corner_radius
> 0) {
130 paint
.setStyle(SkPaint::kFill_Style
);
131 paint
.setFlags(SkPaint::kAntiAlias_Flag
);
132 paint
.setColor(GetSystemColor(kColorId_MenuBackgroundColor
));
135 SkRect rect
= SkRect::MakeWH(SkIntToScalar(size
.width()),
136 SkIntToScalar(size
.height()));
137 SkScalar radius
= SkIntToScalar(menu_background
.corner_radius
);
138 SkScalar radii
[8] = {radius
, radius
, radius
, radius
,
139 radius
, radius
, radius
, radius
};
140 path
.addRoundRect(rect
, radii
);
142 canvas
->drawPath(path
, paint
);
144 canvas
->drawColor(GetSystemColor(kColorId_MenuBackgroundColor
),
145 SkXfermode::kSrc_Mode
);
149 void NativeThemeGtk2::PaintMenuItemBackground(
152 const gfx::Rect
& rect
,
153 const MenuListExtraParams
& menu_list
) const {
157 case NativeTheme::kNormal
:
158 case NativeTheme::kDisabled
:
159 color
= GetSystemColor(NativeTheme::kColorId_MenuBackgroundColor
);
160 paint
.setColor(color
);
162 case NativeTheme::kHovered
:
163 color
= GetSystemColor(
164 NativeTheme::kColorId_FocusedMenuItemBackgroundColor
);
165 paint
.setColor(color
);
168 NOTREACHED() << "Invalid state " << state
;
171 canvas
->drawRect(gfx::RectToSkRect(rect
), paint
);
174 void NativeThemeGtk2::NotifyNativeThemeObservers() {
178 GdkColor
NativeThemeGtk2::GetSystemGdkColor(ColorId color_id
) const {
181 case kColorId_WindowBackground
:
182 return GetWindowStyle()->bg
[GTK_STATE_NORMAL
];
185 case kColorId_DialogBackground
:
186 return GetWindowStyle()->bg
[GTK_STATE_NORMAL
];
189 case kColorId_FocusedBorderColor
:
190 return GetEntryStyle()->bg
[GTK_STATE_SELECTED
];
191 case kColorId_UnfocusedBorderColor
:
192 return GetEntryStyle()->text_aa
[GTK_STATE_NORMAL
];
195 case kColorId_EnabledMenuItemForegroundColor
:
196 case kColorId_DisabledEmphasizedMenuItemForegroundColor
:
197 return GetMenuItemStyle()->text
[GTK_STATE_NORMAL
];
198 case kColorId_DisabledMenuItemForegroundColor
:
199 return GetMenuItemStyle()->text
[GTK_STATE_INSENSITIVE
];
200 case kColorId_SelectedMenuItemForegroundColor
:
201 return GetMenuItemStyle()->text
[GTK_STATE_SELECTED
];
202 case kColorId_FocusedMenuItemBackgroundColor
:
203 return GetMenuItemStyle()->bg
[GTK_STATE_SELECTED
];
204 case kColorId_HoverMenuItemBackgroundColor
:
205 return GetMenuItemStyle()->bg
[GTK_STATE_PRELIGHT
];
206 case kColorId_FocusedMenuButtonBorderColor
:
207 return GetEntryStyle()->bg
[GTK_STATE_NORMAL
];
208 case kColorId_HoverMenuButtonBorderColor
:
209 return GetEntryStyle()->text_aa
[GTK_STATE_PRELIGHT
];
210 case kColorId_MenuBorderColor
:
211 case kColorId_EnabledMenuButtonBorderColor
:
212 case kColorId_MenuSeparatorColor
: {
213 return GetMenuItemStyle()->text
[GTK_STATE_INSENSITIVE
];
215 case kColorId_MenuBackgroundColor
:
216 return GetMenuStyle()->bg
[GTK_STATE_NORMAL
];
219 case kColorId_LabelEnabledColor
:
220 return GetLabelStyle()->text
[GTK_STATE_NORMAL
];
221 case kColorId_LabelDisabledColor
:
222 return GetLabelStyle()->text
[GTK_STATE_INSENSITIVE
];
223 case kColorId_LabelBackgroundColor
:
224 return GetWindowStyle()->bg
[GTK_STATE_NORMAL
];
227 case kColorId_ButtonBackgroundColor
:
228 return GetButtonStyle()->bg
[GTK_STATE_NORMAL
];
229 case kColorId_ButtonEnabledColor
:
230 return GetButtonStyle()->text
[GTK_STATE_NORMAL
];
231 case kColorId_ButtonDisabledColor
:
232 return GetButtonStyle()->text
[GTK_STATE_INSENSITIVE
];
233 case kColorId_ButtonHighlightColor
:
234 return GetButtonStyle()->base
[GTK_STATE_SELECTED
];
235 case kColorId_ButtonHoverColor
:
236 return GetButtonStyle()->text
[GTK_STATE_PRELIGHT
];
237 case kColorId_ButtonHoverBackgroundColor
:
238 return GetButtonStyle()->bg
[GTK_STATE_PRELIGHT
];
241 case kColorId_TextfieldDefaultColor
:
242 return GetEntryStyle()->text
[GTK_STATE_NORMAL
];
243 case kColorId_TextfieldDefaultBackground
:
244 return GetEntryStyle()->base
[GTK_STATE_NORMAL
];
245 case kColorId_TextfieldReadOnlyColor
:
246 return GetEntryStyle()->text
[GTK_STATE_INSENSITIVE
];
247 case kColorId_TextfieldReadOnlyBackground
:
248 return GetEntryStyle()->base
[GTK_STATE_INSENSITIVE
];
249 case kColorId_TextfieldSelectionColor
:
250 return GetEntryStyle()->text
[GTK_STATE_SELECTED
];
251 case kColorId_TextfieldSelectionBackgroundFocused
:
252 return GetEntryStyle()->base
[GTK_STATE_SELECTED
];
255 case kColorId_TooltipBackground
:
256 return GetWindowStyle()->bg
[GTK_STATE_NORMAL
];
258 // Trees and Tables (implemented on GTK using the same class)
259 case kColorId_TableBackground
:
260 case kColorId_TreeBackground
:
261 return GetTreeStyle()->bg
[GTK_STATE_NORMAL
];
262 case kColorId_TableText
:
263 case kColorId_TreeText
:
264 return GetTreeStyle()->text
[GTK_STATE_NORMAL
];
265 case kColorId_TableSelectedText
:
266 case kColorId_TableSelectedTextUnfocused
:
267 case kColorId_TreeSelectedText
:
268 case kColorId_TreeSelectedTextUnfocused
:
269 return GetTreeStyle()->text
[GTK_STATE_SELECTED
];
270 case kColorId_TableSelectionBackgroundFocused
:
271 case kColorId_TableSelectionBackgroundUnfocused
:
272 case kColorId_TreeSelectionBackgroundFocused
:
273 case kColorId_TreeSelectionBackgroundUnfocused
:
274 return GetTreeStyle()->bg
[GTK_STATE_SELECTED
];
275 case kColorId_TreeArrow
:
276 return GetTreeStyle()->fg
[GTK_STATE_NORMAL
];
277 case kColorId_TableGroupingIndicatorColor
:
278 return GetTreeStyle()->text_aa
[GTK_STATE_NORMAL
];
281 case kColorId_ResultsTableNormalBackground
:
282 return GetEntryStyle()->base
[GTK_STATE_NORMAL
];
283 case kColorId_ResultsTableHoveredBackground
: {
284 GtkStyle
* entry_style
= GetEntryStyle();
285 return GdkAlphaBlend(
286 entry_style
->base
[GTK_STATE_NORMAL
],
287 entry_style
->base
[GTK_STATE_SELECTED
], 0x80);
289 case kColorId_ResultsTableSelectedBackground
:
290 return GetEntryStyle()->base
[GTK_STATE_SELECTED
];
291 case kColorId_ResultsTableNormalText
:
292 case kColorId_ResultsTableHoveredText
:
293 return GetEntryStyle()->text
[GTK_STATE_NORMAL
];
294 case kColorId_ResultsTableSelectedText
:
295 return GetEntryStyle()->text
[GTK_STATE_SELECTED
];
296 case kColorId_ResultsTableNormalDimmedText
:
297 case kColorId_ResultsTableHoveredDimmedText
: {
298 GtkStyle
* entry_style
= GetEntryStyle();
299 return GdkAlphaBlend(
300 entry_style
->text
[GTK_STATE_NORMAL
],
301 entry_style
->base
[GTK_STATE_NORMAL
], 0x80);
303 case kColorId_ResultsTableSelectedDimmedText
: {
304 GtkStyle
* entry_style
= GetEntryStyle();
305 return GdkAlphaBlend(
306 entry_style
->text
[GTK_STATE_SELECTED
],
307 entry_style
->base
[GTK_STATE_NORMAL
], 0x80);
309 case kColorId_ResultsTableNormalUrl
:
310 case kColorId_ResultsTableHoveredUrl
: {
311 return NormalURLColor(GetEntryStyle()->text
[GTK_STATE_NORMAL
]);
313 case kColorId_ResultsTableSelectedUrl
: {
314 GtkStyle
* entry_style
= GetEntryStyle();
315 return SelectedURLColor(entry_style
->text
[GTK_STATE_SELECTED
],
316 entry_style
->base
[GTK_STATE_SELECTED
]);
318 case kColorId_ResultsTableNormalDivider
: {
319 GtkStyle
* win_style
= GetWindowStyle();
320 return GdkAlphaBlend(win_style
->text
[GTK_STATE_NORMAL
],
321 win_style
->bg
[GTK_STATE_NORMAL
], 0x34);
323 case kColorId_ResultsTableHoveredDivider
: {
324 GtkStyle
* win_style
= GetWindowStyle();
325 return GdkAlphaBlend(win_style
->text
[GTK_STATE_PRELIGHT
],
326 win_style
->bg
[GTK_STATE_PRELIGHT
], 0x34);
328 case kColorId_ResultsTableSelectedDivider
: {
329 GtkStyle
* win_style
= GetWindowStyle();
330 return GdkAlphaBlend(win_style
->text
[GTK_STATE_SELECTED
],
331 win_style
->bg
[GTK_STATE_SELECTED
], 0x34);
335 return SkColorToGdkColor(kInvalidColorIdColor
);
338 GtkWidget
* NativeThemeGtk2::GetRealizedWindow() const {
340 fake_window_
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
341 gtk_widget_realize(fake_window_
);
347 GtkStyle
* NativeThemeGtk2::GetWindowStyle() const {
348 return gtk_rc_get_style(GetRealizedWindow());
351 GtkStyle
* NativeThemeGtk2::GetEntryStyle() const {
352 if (!fake_entry_
.get()) {
353 fake_entry_
.Own(gtk_entry_new());
355 // The fake entry needs to be in the window so it can be realized sow e can
356 // use the computed parts of the style.
357 gtk_container_add(GTK_CONTAINER(GetRealizedWindow()), fake_entry_
.get());
358 gtk_widget_realize(fake_entry_
.get());
360 return gtk_rc_get_style(fake_entry_
.get());
363 GtkStyle
* NativeThemeGtk2::GetLabelStyle() const {
364 if (!fake_label_
.get())
365 fake_label_
.Own(gtk_label_new(""));
367 return gtk_rc_get_style(fake_label_
.get());
370 GtkStyle
* NativeThemeGtk2::GetButtonStyle() const {
371 if (!fake_button_
.get())
372 fake_button_
.Own(gtk_button_new());
374 return gtk_rc_get_style(fake_button_
.get());
377 GtkStyle
* NativeThemeGtk2::GetTreeStyle() const {
378 if (!fake_tree_
.get())
379 fake_tree_
.Own(gtk_tree_view_new());
381 return gtk_rc_get_style(fake_tree_
.get());
384 GtkStyle
* NativeThemeGtk2::GetMenuStyle() const {
385 if (!fake_menu_
.get())
386 fake_menu_
.Own(gtk_menu_new());
387 return gtk_rc_get_style(fake_menu_
.get());
390 GtkStyle
* NativeThemeGtk2::GetMenuItemStyle() const {
391 if (!fake_menu_item_
) {
392 if (!fake_menu_
.get())
393 fake_menu_
.Own(gtk_custom_menu_new());
395 fake_menu_item_
= gtk_custom_menu_item_new();
396 gtk_menu_shell_append(GTK_MENU_SHELL(fake_menu_
.get()), fake_menu_item_
);
399 return gtk_rc_get_style(fake_menu_item_
);
402 } // namespace libgtk2ui