1 // Copyright (c) 2012 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/gtk2_ui.h"
9 #include "base/command_line.h"
10 #include "base/debug/leak_annotations.h"
11 #include "base/environment.h"
12 #include "base/i18n/rtl.h"
13 #include "base/logging.h"
14 #include "base/nix/mime_util_xdg.h"
15 #include "base/stl_util.h"
16 #include "base/strings/stringprintf.h"
17 #include "chrome/browser/themes/theme_properties.h"
18 #include "chrome/browser/ui/libgtk2ui/app_indicator_icon.h"
19 #include "chrome/browser/ui/libgtk2ui/chrome_gtk_frame.h"
20 #include "chrome/browser/ui/libgtk2ui/gtk2_border.h"
21 #include "chrome/browser/ui/libgtk2ui/gtk2_event_loop.h"
22 #include "chrome/browser/ui/libgtk2ui/gtk2_key_bindings_handler.h"
23 #include "chrome/browser/ui/libgtk2ui/gtk2_signal_registrar.h"
24 #include "chrome/browser/ui/libgtk2ui/gtk2_util.h"
25 #include "chrome/browser/ui/libgtk2ui/native_theme_gtk2.h"
26 #include "chrome/browser/ui/libgtk2ui/print_dialog_gtk2.h"
27 #include "chrome/browser/ui/libgtk2ui/printing_gtk2_util.h"
28 #include "chrome/browser/ui/libgtk2ui/select_file_dialog_impl.h"
29 #include "chrome/browser/ui/libgtk2ui/skia_utils_gtk2.h"
30 #include "chrome/browser/ui/libgtk2ui/unity_service.h"
31 #include "chrome/browser/ui/libgtk2ui/x11_input_method_context_impl_gtk2.h"
32 #include "grit/theme_resources.h"
33 #include "grit/ui_resources.h"
34 #include "printing/printing_context_linux.h"
35 #include "third_party/skia/include/core/SkBitmap.h"
36 #include "third_party/skia/include/core/SkCanvas.h"
37 #include "third_party/skia/include/core/SkColor.h"
38 #include "third_party/skia/include/core/SkShader.h"
39 #include "ui/base/resource/resource_bundle.h"
40 #include "ui/gfx/canvas.h"
41 #include "ui/gfx/image/image.h"
42 #include "ui/gfx/rect.h"
43 #include "ui/gfx/size.h"
44 #include "ui/gfx/skbitmap_operations.h"
45 #include "ui/gfx/skia_util.h"
46 #include "ui/views/controls/button/label_button.h"
47 #include "ui/views/linux_ui/window_button_order_observer.h"
49 #if defined(USE_GCONF)
50 #include "chrome/browser/ui/libgtk2ui/gconf_listener.h"
53 // A minimized port of GtkThemeService into something that can provide colors
54 // and images for aura.
56 // TODO(erg): There's still a lot that needs ported or done for the first time:
58 // - Render and inject the omnibox background.
59 // - Make sure to test with a light on dark theme, too.
63 struct GObjectDeleter {
64 void operator()(void* ptr) {
68 struct GtkIconInfoDeleter {
69 void operator()(GtkIconInfo* ptr) {
70 gtk_icon_info_free(ptr);
73 typedef scoped_ptr<GIcon, GObjectDeleter> ScopedGIcon;
74 typedef scoped_ptr<GtkIconInfo, GtkIconInfoDeleter> ScopedGtkIconInfo;
75 typedef scoped_ptr<GdkPixbuf, GObjectDeleter> ScopedGdkPixbuf;
77 // Prefix for app indicator ids
78 const char kAppIndicatorIdPrefix[] = "chrome_app_indicator_";
80 // Number of app indicators used (used as part of app-indicator id).
83 // The unknown content type.
84 const char* kUnknownContentType = "application/octet-stream";
86 // The size of the rendered toolbar image.
87 const int kToolbarImageWidth = 64;
88 const int kToolbarImageHeight = 128;
90 // How much to tint the GTK+ color lighter at the top of the window.
91 const color_utils::HSL kGtkFrameShift = { -1, -1, 0.58 };
93 // How much to tint the GTK+ color when an explicit frame color hasn't been
95 const color_utils::HSL kDefaultFrameShift = { -1, -1, 0.4 };
97 // Values used as the new luminance and saturation values in the inactive tab
99 const double kDarkInactiveLuminance = 0.85;
100 const double kLightInactiveLuminance = 0.15;
101 const double kHeavyInactiveSaturation = 0.7;
102 const double kLightInactiveSaturation = 0.3;
104 // Default color for links on the NTP when the GTK+ theme doesn't define a
105 // link color. Constant taken from gtklinkbutton.c.
106 const GdkColor kDefaultLinkColor = { 0, 0, 0, 0xeeee };
108 const int kSkiaToGDKMultiplier = 257;
110 // TODO(erg): ThemeService has a whole interface just for reading default
111 // constants. Figure out what to do with that more long term; for now, just
112 // copy the constants themselves here.
115 const color_utils::HSL kDefaultTintButtons = { -1, -1, -1 };
116 const color_utils::HSL kDefaultTintFrame = { -1, -1, -1 };
117 const color_utils::HSL kDefaultTintFrameInactive = { -1, -1, 0.75f };
118 const color_utils::HSL kDefaultTintFrameIncognito = { -1, 0.2f, 0.35f };
119 const color_utils::HSL kDefaultTintFrameIncognitoInactive = { -1, 0.3f, 0.6f };
120 const color_utils::HSL kDefaultTintBackgroundTab = { -1, 0.5, 0.75 };
122 // A list of images that we provide while in gtk mode.
123 const int kThemeImages[] = {
125 IDR_THEME_TAB_BACKGROUND,
126 IDR_THEME_TAB_BACKGROUND_INCOGNITO,
130 IDR_THEME_FRAME_INACTIVE,
131 IDR_THEME_FRAME_INCOGNITO,
132 IDR_THEME_FRAME_INCOGNITO_INACTIVE,
135 // A list of icons used in the autocomplete view that should be tinted to the
136 // current gtk theme selection color so they stand out against the GtkEntry's
138 // TODO(erg): Decide what to do about other icons that appear in the omnibox,
139 // e.g. content settings icons.
140 const int kAutocompleteImages[] = {
141 IDR_OMNIBOX_EXTENSION_APP,
143 IDR_OMNIBOX_HTTP_DARK,
145 IDR_OMNIBOX_SEARCH_DARK,
147 IDR_OMNIBOX_STAR_DARK,
149 IDR_OMNIBOX_TTS_DARK,
152 // This table converts button ids into a pair of gtk-stock id and state.
153 struct IDRGtkMapping {
155 const char* stock_id;
156 GtkStateType gtk_state;
157 } const kGtkIcons[] = {
158 { IDR_BACK, GTK_STOCK_GO_BACK, GTK_STATE_NORMAL },
159 { IDR_BACK_D, GTK_STOCK_GO_BACK, GTK_STATE_INSENSITIVE },
160 { IDR_BACK_H, GTK_STOCK_GO_BACK, GTK_STATE_PRELIGHT },
161 { IDR_BACK_P, GTK_STOCK_GO_BACK, GTK_STATE_ACTIVE },
163 { IDR_FORWARD, GTK_STOCK_GO_FORWARD, GTK_STATE_NORMAL },
164 { IDR_FORWARD_D, GTK_STOCK_GO_FORWARD, GTK_STATE_INSENSITIVE },
165 { IDR_FORWARD_H, GTK_STOCK_GO_FORWARD, GTK_STATE_PRELIGHT },
166 { IDR_FORWARD_P, GTK_STOCK_GO_FORWARD, GTK_STATE_ACTIVE },
168 { IDR_HOME, GTK_STOCK_HOME, GTK_STATE_NORMAL },
169 { IDR_HOME_H, GTK_STOCK_HOME, GTK_STATE_PRELIGHT },
170 { IDR_HOME_P, GTK_STOCK_HOME, GTK_STATE_ACTIVE },
172 { IDR_RELOAD, GTK_STOCK_REFRESH, GTK_STATE_NORMAL },
173 { IDR_RELOAD_D, GTK_STOCK_REFRESH, GTK_STATE_INSENSITIVE },
174 { IDR_RELOAD_H, GTK_STOCK_REFRESH, GTK_STATE_PRELIGHT },
175 { IDR_RELOAD_P, GTK_STOCK_REFRESH, GTK_STATE_ACTIVE },
177 { IDR_STOP, GTK_STOCK_STOP, GTK_STATE_NORMAL },
178 { IDR_STOP_D, GTK_STOCK_STOP, GTK_STATE_INSENSITIVE },
179 { IDR_STOP_H, GTK_STOCK_STOP, GTK_STATE_PRELIGHT },
180 { IDR_STOP_P, GTK_STOCK_STOP, GTK_STATE_ACTIVE },
183 // The image resources that will be tinted by the 'button' tint value.
184 const int kOtherToolbarButtonIDs[] = {
185 IDR_TOOLBAR_BEZEL_HOVER,
186 IDR_TOOLBAR_BEZEL_PRESSED,
187 IDR_BROWSER_ACTION_H,
188 IDR_BROWSER_ACTION_P,
189 IDR_BROWSER_ACTIONS_OVERFLOW,
190 IDR_BROWSER_ACTIONS_OVERFLOW_H,
191 IDR_BROWSER_ACTIONS_OVERFLOW_P,
193 IDR_THROBBER_WAITING,
196 // TODO(erg): The dropdown arrow should be tinted because we're injecting
197 // various background GTK colors, but the code that accesses them needs to be
198 // modified so that they ask their ui::ThemeProvider instead of the
199 // ResourceBundle. (i.e. in a light on dark theme, the dropdown arrow will be
204 bool IsOverridableImage(int id) {
205 CR_DEFINE_STATIC_LOCAL(std::set<int>, images, ());
206 if (images.empty()) {
207 images.insert(kThemeImages, kThemeImages + arraysize(kThemeImages));
208 images.insert(kAutocompleteImages,
209 kAutocompleteImages + arraysize(kAutocompleteImages));
211 for (unsigned int i = 0; i < arraysize(kGtkIcons); ++i)
212 images.insert(kGtkIcons[i].idr);
214 images.insert(kOtherToolbarButtonIDs,
215 kOtherToolbarButtonIDs + arraysize(kOtherToolbarButtonIDs));
218 return images.count(id) > 0;
221 // Picks a button tint from a set of background colors. While
222 // |accent_gdk_color| will usually be the same color through a theme, this
223 // function will get called with the normal GtkLabel |text_color|/GtkWindow
224 // |background_color| pair and the GtkEntry |text_color|/|background_color|
225 // pair. While 3/4 of the time the resulting tint will be the same, themes that
226 // have a dark window background (with light text) and a light text entry (with
227 // dark text) will get better icons with this separated out.
228 void PickButtonTintFromColors(const GdkColor& accent_gdk_color,
229 const GdkColor& text_color,
230 const GdkColor& background_color,
231 color_utils::HSL* tint) {
232 SkColor accent_color = libgtk2ui::GdkColorToSkColor(accent_gdk_color);
233 color_utils::HSL accent_tint;
234 color_utils::SkColorToHSL(accent_color, &accent_tint);
236 color_utils::HSL text_tint;
237 color_utils::SkColorToHSL(libgtk2ui::GdkColorToSkColor(text_color),
240 color_utils::HSL background_tint;
241 color_utils::SkColorToHSL(libgtk2ui::GdkColorToSkColor(background_color),
244 // If the accent color is gray, then our normal HSL tomfoolery will bring out
245 // whatever color is oddly dominant (for example, in rgb space [125, 128,
246 // 125] will tint green instead of gray). Slight differences (+/-10 (4%) to
247 // all color components) should be interpreted as this color being gray and
248 // we should switch into a special grayscale mode.
249 int rb_diff = abs(SkColorGetR(accent_color) - SkColorGetB(accent_color));
250 int rg_diff = abs(SkColorGetR(accent_color) - SkColorGetG(accent_color));
251 int bg_diff = abs(SkColorGetB(accent_color) - SkColorGetG(accent_color));
252 if (rb_diff < 10 && rg_diff < 10 && bg_diff < 10) {
253 // Our accent is white/gray/black. Only the luminance of the accent color
257 // Use the saturation of the text.
258 tint->s = text_tint.s;
260 // Use the luminance of the accent color UNLESS there isn't enough
261 // luminance contrast between the accent color and the base color.
262 if (fabs(accent_tint.l - background_tint.l) > 0.3)
263 tint->l = accent_tint.l;
265 tint->l = text_tint.l;
267 // Our accent is a color.
268 tint->h = accent_tint.h;
270 // Don't modify the saturation; the amount of color doesn't matter.
273 // If the text wants us to darken the icon, don't change the luminance (the
274 // icons are already dark enough). Otherwise, lighten the icon by no more
275 // than 0.9 since we don't want a pure-white icon even if the text is pure
277 if (text_tint.l < 0.5)
279 else if (text_tint.l <= 0.9)
280 tint->l = text_tint.l;
286 // Applies an HSL shift to a GdkColor (instead of an SkColor)
287 void GdkColorHSLShift(const color_utils::HSL& shift, GdkColor* frame_color) {
288 SkColor shifted = color_utils::HSLShift(
289 libgtk2ui::GdkColorToSkColor(*frame_color), shift);
291 frame_color->pixel = 0;
292 frame_color->red = SkColorGetR(shifted) * kSkiaToGDKMultiplier;
293 frame_color->green = SkColorGetG(shifted) * kSkiaToGDKMultiplier;
294 frame_color->blue = SkColorGetB(shifted) * kSkiaToGDKMultiplier;
297 // Copied Default blah sections from ThemeService.
298 color_utils::HSL GetDefaultTint(int id) {
300 case ThemeProperties::TINT_FRAME:
301 return kDefaultTintFrame;
302 case ThemeProperties::TINT_FRAME_INACTIVE:
303 return kDefaultTintFrameInactive;
304 case ThemeProperties::TINT_FRAME_INCOGNITO:
305 return kDefaultTintFrameIncognito;
306 case ThemeProperties::TINT_FRAME_INCOGNITO_INACTIVE:
307 return kDefaultTintFrameIncognitoInactive;
308 case ThemeProperties::TINT_BUTTONS:
309 return kDefaultTintButtons;
310 case ThemeProperties::TINT_BACKGROUND_TAB:
311 return kDefaultTintBackgroundTab;
313 color_utils::HSL result = {-1, -1, -1};
320 namespace libgtk2ui {
322 Gtk2UI::Gtk2UI() : middle_click_action_(MIDDLE_CLICK_ACTION_LOWER) {
323 GtkInitFromCommandLine(*CommandLine::ForCurrentProcess());
326 void Gtk2UI::Initialize() {
327 signals_.reset(new Gtk2SignalRegistrar);
329 // Create our fake widgets.
330 fake_window_ = gtk_window_new(GTK_WINDOW_TOPLEVEL);
331 fake_frame_ = chrome_gtk_frame_new();
332 fake_label_.Own(gtk_label_new(""));
333 fake_entry_.Own(gtk_entry_new());
335 // Only realized widgets receive style-set notifications, which we need to
336 // broadcast new theme images and colors. Only realized widgets have style
337 // properties, too, which we query for some colors.
338 gtk_widget_realize(fake_frame_);
339 gtk_widget_realize(fake_window_);
341 signals_->Connect(fake_frame_, "style-set",
342 G_CALLBACK(&OnStyleSetThunk), this);
346 printing::PrintingContextLinux::SetCreatePrintDialogFunction(
347 &PrintDialogGtk2::CreatePrintDialog);
348 printing::PrintingContextLinux::SetPdfPaperSizeFunction(
349 &GetPdfPaperSizeDeviceUnitsGtk);
351 #if defined(USE_GCONF)
352 // We must build this after GTK gets initialized.
353 gconf_listener_.reset(new GConfListener(this));
354 #endif // defined(USE_GCONF)
356 indicators_count = 0;
358 // Instantiate the singleton instance of Gtk2EventLoop.
359 Gtk2EventLoop::GetInstance();
363 gtk_widget_destroy(fake_window_);
364 gtk_widget_destroy(fake_frame_);
365 fake_label_.Destroy();
366 fake_entry_.Destroy();
371 gfx::Image Gtk2UI::GetThemeImageNamed(int id) const {
372 // Try to get our cached version:
373 ImageCache::const_iterator it = gtk_images_.find(id);
374 if (it != gtk_images_.end())
377 if (IsOverridableImage(id)) {
378 gfx::Image image = gfx::Image(
379 gfx::ImageSkia::CreateFrom1xBitmap(GenerateGtkThemeBitmap(id)));
380 gtk_images_[id] = image;
387 bool Gtk2UI::GetColor(int id, SkColor* color) const {
388 ColorMap::const_iterator it = colors_.find(id);
389 if (it != colors_.end()) {
397 bool Gtk2UI::HasCustomImage(int id) const {
398 return IsOverridableImage(id);
401 SkColor Gtk2UI::GetFocusRingColor() const {
402 return focus_ring_color_;
405 SkColor Gtk2UI::GetThumbActiveColor() const {
406 return thumb_active_color_;
409 SkColor Gtk2UI::GetThumbInactiveColor() const {
410 return thumb_inactive_color_;
413 SkColor Gtk2UI::GetTrackColor() const {
417 SkColor Gtk2UI::GetActiveSelectionBgColor() const {
418 return active_selection_bg_color_;
421 SkColor Gtk2UI::GetActiveSelectionFgColor() const {
422 return active_selection_fg_color_;
425 SkColor Gtk2UI::GetInactiveSelectionBgColor() const {
426 return inactive_selection_bg_color_;
429 SkColor Gtk2UI::GetInactiveSelectionFgColor() const {
430 return inactive_selection_fg_color_;
433 double Gtk2UI::GetCursorBlinkInterval() const {
434 // From http://library.gnome.org/devel/gtk/unstable/GtkSettings.html, this is
435 // the default value for gtk-cursor-blink-time.
436 static const gint kGtkDefaultCursorBlinkTime = 1200;
438 // Dividing GTK's cursor blink cycle time (in milliseconds) by this value
439 // yields an appropriate value for
440 // content::RendererPreferences::caret_blink_interval. This matches the
441 // logic in the WebKit GTK port.
442 static const double kGtkCursorBlinkCycleFactor = 2000.0;
444 gint cursor_blink_time = kGtkDefaultCursorBlinkTime;
445 gboolean cursor_blink = TRUE;
446 g_object_get(gtk_settings_get_default(),
447 "gtk-cursor-blink-time", &cursor_blink_time,
448 "gtk-cursor-blink", &cursor_blink,
450 return cursor_blink ? (cursor_blink_time / kGtkCursorBlinkCycleFactor) : 0.0;
453 ui::NativeTheme* Gtk2UI::GetNativeTheme(aura::Window* window) const {
454 ui::NativeTheme* native_theme_override = NULL;
455 if (!native_theme_overrider_.is_null())
456 native_theme_override = native_theme_overrider_.Run(window);
458 if (native_theme_override)
459 return native_theme_override;
461 return NativeThemeGtk2::instance();
464 void Gtk2UI::SetNativeThemeOverride(const NativeThemeGetter& callback) {
465 native_theme_overrider_ = callback;
468 bool Gtk2UI::GetDefaultUsesSystemTheme() const {
469 scoped_ptr<base::Environment> env(base::Environment::Create());
471 switch (base::nix::GetDesktopEnvironment(env.get())) {
472 case base::nix::DESKTOP_ENVIRONMENT_GNOME:
473 case base::nix::DESKTOP_ENVIRONMENT_UNITY:
474 case base::nix::DESKTOP_ENVIRONMENT_XFCE:
476 case base::nix::DESKTOP_ENVIRONMENT_KDE3:
477 case base::nix::DESKTOP_ENVIRONMENT_KDE4:
478 case base::nix::DESKTOP_ENVIRONMENT_OTHER:
481 // Unless GetDesktopEnvironment() badly misbehaves, this should never happen.
486 void Gtk2UI::SetDownloadCount(int count) const {
487 if (unity::IsRunning())
488 unity::SetDownloadCount(count);
491 void Gtk2UI::SetProgressFraction(float percentage) const {
492 if (unity::IsRunning())
493 unity::SetProgressFraction(percentage);
496 bool Gtk2UI::IsStatusIconSupported() const {
497 return AppIndicatorIcon::CouldOpen();
500 scoped_ptr<views::StatusIconLinux> Gtk2UI::CreateLinuxStatusIcon(
501 const gfx::ImageSkia& image,
502 const base::string16& tool_tip) const {
503 if (AppIndicatorIcon::CouldOpen()) {
505 return scoped_ptr<views::StatusIconLinux>(new AppIndicatorIcon(
506 base::StringPrintf("%s%d", kAppIndicatorIdPrefix, indicators_count),
510 return scoped_ptr<views::StatusIconLinux>();
514 gfx::Image Gtk2UI::GetIconForContentType(
515 const std::string& content_type,
517 // This call doesn't take a reference.
518 GtkIconTheme* theme = gtk_icon_theme_get_default();
520 std::string content_types[] = {
521 content_type, kUnknownContentType
524 for (size_t i = 0; i < arraysize(content_types); ++i) {
525 ScopedGIcon icon(g_content_type_get_icon(content_types[i].c_str()));
526 ScopedGtkIconInfo icon_info(
527 gtk_icon_theme_lookup_by_gicon(
528 theme, icon.get(), size,
529 static_cast<GtkIconLookupFlags>(GTK_ICON_LOOKUP_FORCE_SIZE)));
532 ScopedGdkPixbuf pixbuf(gtk_icon_info_load_icon(icon_info.get(), NULL));
536 SkBitmap bitmap = GdkPixbufToImageSkia(pixbuf.get());
537 DCHECK_EQ(size, bitmap.width());
538 DCHECK_EQ(size, bitmap.height());
539 gfx::ImageSkia image_skia = gfx::ImageSkia::CreateFrom1xBitmap(bitmap);
540 image_skia.MakeThreadSafe();
541 return gfx::Image(image_skia);
546 scoped_ptr<views::Border> Gtk2UI::CreateNativeBorder(
547 views::LabelButton* owning_button,
548 scoped_ptr<views::Border> border) {
549 if (owning_button->GetNativeTheme() != NativeThemeGtk2::instance())
550 return border.Pass();
552 return scoped_ptr<views::Border>(new Gtk2Border(this, owning_button));
555 void Gtk2UI::AddWindowButtonOrderObserver(
556 views::WindowButtonOrderObserver* observer) {
557 if (!leading_buttons_.empty() || !trailing_buttons_.empty()) {
558 observer->OnWindowButtonOrderingChange(leading_buttons_,
562 observer_list_.AddObserver(observer);
565 void Gtk2UI::RemoveWindowButtonOrderObserver(
566 views::WindowButtonOrderObserver* observer) {
567 observer_list_.RemoveObserver(observer);
570 void Gtk2UI::SetWindowButtonOrdering(
571 const std::vector<views::FrameButton>& leading_buttons,
572 const std::vector<views::FrameButton>& trailing_buttons) {
573 leading_buttons_ = leading_buttons;
574 trailing_buttons_ = trailing_buttons;
576 FOR_EACH_OBSERVER(views::WindowButtonOrderObserver, observer_list_,
577 OnWindowButtonOrderingChange(leading_buttons_,
581 void Gtk2UI::SetNonClientMiddleClickAction(NonClientMiddleClickAction action) {
582 middle_click_action_ = action;
585 scoped_ptr<ui::LinuxInputMethodContext> Gtk2UI::CreateInputMethodContext(
586 ui::LinuxInputMethodContextDelegate* delegate) const {
587 return scoped_ptr<ui::LinuxInputMethodContext>(
588 new X11InputMethodContextImplGtk2(delegate));
591 bool Gtk2UI::UseAntialiasing() const {
592 GtkSettings* gtk_settings = gtk_settings_get_default();
594 gint gtk_antialias = 0;
595 g_object_get(gtk_settings,
596 "gtk-xft-antialias", >k_antialias,
598 return gtk_antialias != 0;
601 gfx::FontRenderParams::Hinting Gtk2UI::GetHintingStyle() const {
602 GtkSettings* gtk_settings = gtk_settings_get_default();
604 gfx::FontRenderParams::Hinting hinting =
605 gfx::FontRenderParams::HINTING_SLIGHT;
606 gint gtk_hinting = 0;
607 gchar* gtk_hint_style = NULL;
608 g_object_get(gtk_settings,
609 "gtk-xft-hinting", >k_hinting,
610 "gtk-xft-hintstyle", >k_hint_style,
613 if (gtk_hint_style) {
614 if (gtk_hinting == 0 || strcmp(gtk_hint_style, "hintnone") == 0)
615 hinting = gfx::FontRenderParams::HINTING_NONE;
616 else if (strcmp(gtk_hint_style, "hintslight") == 0)
617 hinting = gfx::FontRenderParams::HINTING_SLIGHT;
618 else if (strcmp(gtk_hint_style, "hintmedium") == 0)
619 hinting = gfx::FontRenderParams::HINTING_MEDIUM;
620 else if (strcmp(gtk_hint_style, "hintfull") == 0)
621 hinting = gfx::FontRenderParams::HINTING_FULL;
623 g_free(gtk_hint_style);
629 gfx::FontRenderParams::SubpixelRendering
630 Gtk2UI::GetSubpixelRenderingStyle() const {
631 GtkSettings* gtk_settings = gtk_settings_get_default();
633 gfx::FontRenderParams::SubpixelRendering subpixel_rendering =
634 gfx::FontRenderParams::SUBPIXEL_RENDERING_NONE;
635 gchar* gtk_rgba = NULL;
636 g_object_get(gtk_settings,
637 "gtk-xft-rgba", >k_rgba,
641 if (strcmp(gtk_rgba, "none") == 0)
642 subpixel_rendering = gfx::FontRenderParams::SUBPIXEL_RENDERING_NONE;
643 else if (strcmp(gtk_rgba, "rgb") == 0)
644 subpixel_rendering = gfx::FontRenderParams::SUBPIXEL_RENDERING_RGB;
645 else if (strcmp(gtk_rgba, "bgr") == 0)
646 subpixel_rendering = gfx::FontRenderParams::SUBPIXEL_RENDERING_BGR;
647 else if (strcmp(gtk_rgba, "vrgb") == 0)
648 subpixel_rendering = gfx::FontRenderParams::SUBPIXEL_RENDERING_VRGB;
649 else if (strcmp(gtk_rgba, "vbgr") == 0)
650 subpixel_rendering = gfx::FontRenderParams::SUBPIXEL_RENDERING_VBGR;
655 return subpixel_rendering;
658 std::string Gtk2UI::GetDefaultFontName() const {
659 GtkSettings* gtk_settings = gtk_settings_get_default();
662 std::string out_font_name = "sans 10";
663 gchar* font_name = NULL;
664 g_object_get(gtk_settings, "gtk-font-name", &font_name, NULL);
667 out_font_name = std::string(font_name);
671 return out_font_name;
674 ui::SelectFileDialog* Gtk2UI::CreateSelectFileDialog(
675 ui::SelectFileDialog::Listener* listener,
676 ui::SelectFilePolicy* policy) const {
677 return SelectFileDialogImpl::Create(listener, policy);
680 bool Gtk2UI::UnityIsRunning() {
681 return unity::IsRunning();
684 views::LinuxUI::NonClientMiddleClickAction
685 Gtk2UI::GetNonClientMiddleClickAction() {
686 return middle_click_action_;
689 void Gtk2UI::NotifyWindowManagerStartupComplete() {
690 // TODO(port) Implement this using _NET_STARTUP_INFO_BEGIN/_NET_STARTUP_INFO
691 // from http://standards.freedesktop.org/startup-notification-spec/ instead.
692 gdk_notify_startup_complete();
695 bool Gtk2UI::MatchEvent(const ui::Event& event,
696 std::vector<ui::TextEditCommandAuraLinux>* commands) {
697 // Ensure that we have a keyboard handler.
698 if (!key_bindings_handler_)
699 key_bindings_handler_.reset(new Gtk2KeyBindingsHandler);
701 return key_bindings_handler_->MatchEvent(event, commands);
704 void Gtk2UI::GetScrollbarColors(GdkColor* thumb_active_color,
705 GdkColor* thumb_inactive_color,
706 GdkColor* track_color) {
707 GdkColor* theme_thumb_active = NULL;
708 GdkColor* theme_thumb_inactive = NULL;
709 GdkColor* theme_trough_color = NULL;
710 gtk_widget_style_get(GTK_WIDGET(fake_frame_),
711 "scrollbar-slider-prelight-color", &theme_thumb_active,
712 "scrollbar-slider-normal-color", &theme_thumb_inactive,
713 "scrollbar-trough-color", &theme_trough_color,
716 // Ask the theme if the theme specifies all the scrollbar colors and short
717 // circuit the expensive painting/compositing if we have all of them.
718 if (theme_thumb_active && theme_thumb_inactive && theme_trough_color) {
719 *thumb_active_color = *theme_thumb_active;
720 *thumb_inactive_color = *theme_thumb_inactive;
721 *track_color = *theme_trough_color;
723 gdk_color_free(theme_thumb_active);
724 gdk_color_free(theme_thumb_inactive);
725 gdk_color_free(theme_trough_color);
729 // Create window containing scrollbar elements
730 GtkWidget* window = gtk_window_new(GTK_WINDOW_POPUP);
731 GtkWidget* fixed = gtk_fixed_new();
732 GtkWidget* scrollbar = gtk_hscrollbar_new(NULL);
733 gtk_container_add(GTK_CONTAINER(window), fixed);
734 gtk_container_add(GTK_CONTAINER(fixed), scrollbar);
735 gtk_widget_realize(window);
736 gtk_widget_realize(scrollbar);
738 // Draw scrollbar thumb part and track into offscreen image
739 const int kWidth = 100;
740 const int kHeight = 20;
741 GtkStyle* style = gtk_rc_get_style(scrollbar);
742 GdkWindow* gdk_window = gtk_widget_get_window(window);
743 GdkPixmap* pm = gdk_pixmap_new(gdk_window, kWidth, kHeight, -1);
744 GdkRectangle rect = { 0, 0, kWidth, kHeight };
745 unsigned char data[3 * kWidth * kHeight];
746 for (int i = 0; i < 3; ++i) {
749 gtk_paint_slider(style, pm,
750 i == 0 ? GTK_STATE_PRELIGHT : GTK_STATE_NORMAL,
751 GTK_SHADOW_OUT, &rect, scrollbar, "slider", 0, 0,
752 kWidth, kHeight, GTK_ORIENTATION_HORIZONTAL);
755 gtk_paint_box(style, pm, GTK_STATE_ACTIVE, GTK_SHADOW_IN, &rect,
756 scrollbar, "trough-upper", 0, 0, kWidth, kHeight);
758 GdkPixbuf* pb = gdk_pixbuf_new_from_data(data, GDK_COLORSPACE_RGB,
759 FALSE, 8, kWidth, kHeight,
761 gdk_pixbuf_get_from_drawable(pb, pm, NULL, 0, 0, 0, 0, kWidth, kHeight);
764 int components[3] = { 0 };
765 for (int y = 2; y < kHeight - 2; ++y) {
766 for (int c = 0; c < 3; ++c) {
767 // Sample a vertical slice of pixels at about one-thirds from the
768 // left edge. This allows us to avoid any fixed graphics that might be
769 // located at the edges or in the center of the scrollbar.
770 // Each pixel is made up of a red, green, and blue component; taking up
771 // a total of three bytes.
772 components[c] += data[3 * (kWidth / 3 + y * kWidth) + c];
775 GdkColor* color = i == 0 ? thumb_active_color :
776 i == 1 ? thumb_inactive_color :
779 // We sampled pixels across the full height of the image, ignoring a two
780 // pixel border. In some themes, the border has a completely different
781 // color which we do not want to factor into our average color computation.
783 // We now need to scale the colors from the 0..255 range, to the wider
784 // 0..65535 range, and we need to actually compute the average color; so,
785 // we divide by the total number of pixels in the sample.
786 color->red = components[0] * 65535 / (255 * (kHeight - 4));
787 color->green = components[1] * 65535 / (255 * (kHeight - 4));
788 color->blue = components[2] * 65535 / (255 * (kHeight - 4));
794 gtk_widget_destroy(window);
796 // Override any of the default colors with ones that were specified by the
798 if (theme_thumb_active) {
799 *thumb_active_color = *theme_thumb_active;
800 gdk_color_free(theme_thumb_active);
803 if (theme_thumb_inactive) {
804 *thumb_inactive_color = *theme_thumb_inactive;
805 gdk_color_free(theme_thumb_inactive);
808 if (theme_trough_color) {
809 *track_color = *theme_trough_color;
810 gdk_color_free(theme_trough_color);
814 void Gtk2UI::LoadGtkValues() {
815 // TODO(erg): GtkThemeService had a comment here about having to muck with
816 // the raw Prefs object to remove prefs::kCurrentThemeImages or else we'd
817 // regress startup time. Figure out how to do that when we can't access the
818 // prefs system from here.
820 GtkStyle* frame_style = gtk_rc_get_style(fake_frame_);
822 GtkStyle* window_style = gtk_rc_get_style(fake_window_);
823 SetThemeColorFromGtk(ThemeProperties::COLOR_CONTROL_BACKGROUND,
824 &window_style->bg[GTK_STATE_NORMAL]);
826 GdkColor toolbar_color = window_style->bg[GTK_STATE_NORMAL];
827 SetThemeColorFromGtk(ThemeProperties::COLOR_TOOLBAR, &toolbar_color);
829 GdkColor button_color = window_style->bg[GTK_STATE_SELECTED];
830 SetThemeTintFromGtk(ThemeProperties::TINT_BUTTONS, &button_color);
832 GtkStyle* label_style = gtk_rc_get_style(fake_label_.get());
833 GdkColor label_color = label_style->fg[GTK_STATE_NORMAL];
834 SetThemeColorFromGtk(ThemeProperties::COLOR_TAB_TEXT, &label_color);
835 SetThemeColorFromGtk(ThemeProperties::COLOR_BOOKMARK_TEXT, &label_color);
836 SetThemeColorFromGtk(ThemeProperties::COLOR_STATUS_BAR_TEXT, &label_color);
838 // Build the various icon tints.
839 GetNormalButtonTintHSL(&button_tint_);
840 GetNormalEntryForegroundHSL(&entry_tint_);
841 GetSelectedEntryForegroundHSL(&selected_entry_tint_);
842 GdkColor frame_color = BuildFrameColors(frame_style);
844 // The inactive frame color never occurs naturally in the theme, as it is a
845 // tinted version of |frame_color|. We generate another color based on the
846 // background tab color, with the lightness and saturation moved in the
847 // opposite direction. (We don't touch the hue, since there should be subtle
848 // hints of the color in the text.)
849 color_utils::HSL inactive_tab_text_hsl =
850 tints_[ThemeProperties::TINT_BACKGROUND_TAB];
851 if (inactive_tab_text_hsl.l < 0.5)
852 inactive_tab_text_hsl.l = kDarkInactiveLuminance;
854 inactive_tab_text_hsl.l = kLightInactiveLuminance;
856 if (inactive_tab_text_hsl.s < 0.5)
857 inactive_tab_text_hsl.s = kHeavyInactiveSaturation;
859 inactive_tab_text_hsl.s = kLightInactiveSaturation;
861 colors_[ThemeProperties::COLOR_BACKGROUND_TAB_TEXT] =
862 color_utils::HSLToSkColor(inactive_tab_text_hsl, 255);
864 // We pick the text and background colors for the NTP out of the colors for a
865 // GtkEntry. We do this because GtkEntries background color is never the same
866 // as |toolbar_color|, is usually a white, and when it isn't a white,
867 // provides sufficient contrast to |toolbar_color|. Try this out with
868 // Darklooks, HighContrastInverse or ThinIce.
869 GtkStyle* entry_style = gtk_rc_get_style(fake_entry_.get());
870 GdkColor ntp_background = entry_style->base[GTK_STATE_NORMAL];
871 GdkColor ntp_foreground = entry_style->text[GTK_STATE_NORMAL];
872 SetThemeColorFromGtk(ThemeProperties::COLOR_NTP_BACKGROUND,
874 SetThemeColorFromGtk(ThemeProperties::COLOR_NTP_TEXT,
877 // The NTP header is the color that surrounds the current active thumbnail on
878 // the NTP, and acts as the border of the "Recent Links" box. It would be
879 // awesome if they were separated so we could use GetBorderColor() for the
880 // border around the "Recent Links" section, but matching the frame color is
882 SetThemeColorFromGtk(ThemeProperties::COLOR_NTP_HEADER,
884 SetThemeColorFromGtk(ThemeProperties::COLOR_NTP_SECTION,
886 SetThemeColorFromGtk(ThemeProperties::COLOR_NTP_SECTION_TEXT,
889 // Override the link color if the theme provides it.
890 const GdkColor* link_color = NULL;
891 gtk_widget_style_get(GTK_WIDGET(fake_window_),
892 "link-color", &link_color, NULL);
894 bool is_default_link_color = false;
896 link_color = &kDefaultLinkColor;
897 is_default_link_color = true;
900 SetThemeColorFromGtk(ThemeProperties::COLOR_NTP_LINK,
902 SetThemeColorFromGtk(ThemeProperties::COLOR_NTP_LINK_UNDERLINE,
904 SetThemeColorFromGtk(ThemeProperties::COLOR_NTP_SECTION_LINK,
906 SetThemeColorFromGtk(ThemeProperties::COLOR_NTP_SECTION_LINK_UNDERLINE,
909 if (!is_default_link_color)
910 gdk_color_free(const_cast<GdkColor*>(link_color));
912 // Generate the colors that we pass to WebKit.
913 focus_ring_color_ = GdkColorToSkColor(frame_color);
915 GdkColor thumb_active_color, thumb_inactive_color, track_color;
916 Gtk2UI::GetScrollbarColors(&thumb_active_color,
917 &thumb_inactive_color,
919 thumb_active_color_ = GdkColorToSkColor(thumb_active_color);
920 thumb_inactive_color_ = GdkColorToSkColor(thumb_inactive_color);
921 track_color_ = GdkColorToSkColor(track_color);
923 // Some GTK themes only define the text selection colors on the GtkEntry
924 // class, so we need to use that for getting selection colors.
925 active_selection_bg_color_ =
926 GdkColorToSkColor(entry_style->base[GTK_STATE_SELECTED]);
927 active_selection_fg_color_ =
928 GdkColorToSkColor(entry_style->text[GTK_STATE_SELECTED]);
929 inactive_selection_bg_color_ =
930 GdkColorToSkColor(entry_style->base[GTK_STATE_ACTIVE]);
931 inactive_selection_fg_color_ =
932 GdkColorToSkColor(entry_style->text[GTK_STATE_ACTIVE]);
934 // Update the insets that we hand to Gtk2Border.
935 UpdateButtonInsets();
938 GdkColor Gtk2UI::BuildFrameColors(GtkStyle* frame_style) {
939 GdkColor* theme_frame = NULL;
940 GdkColor* theme_inactive_frame = NULL;
941 GdkColor* theme_incognito_frame = NULL;
942 GdkColor* theme_incognito_inactive_frame = NULL;
943 gtk_widget_style_get(GTK_WIDGET(fake_frame_),
944 "frame-color", &theme_frame,
945 "inactive-frame-color", &theme_inactive_frame,
946 "incognito-frame-color", &theme_incognito_frame,
947 "incognito-inactive-frame-color",
948 &theme_incognito_inactive_frame,
951 GdkColor frame_color = BuildAndSetFrameColor(
952 &frame_style->bg[GTK_STATE_SELECTED],
955 ThemeProperties::COLOR_FRAME,
956 ThemeProperties::TINT_FRAME);
958 gdk_color_free(theme_frame);
959 SetThemeTintFromGtk(ThemeProperties::TINT_BACKGROUND_TAB, &frame_color);
961 BuildAndSetFrameColor(
962 &frame_style->bg[GTK_STATE_INSENSITIVE],
963 theme_inactive_frame,
965 ThemeProperties::COLOR_FRAME_INACTIVE,
966 ThemeProperties::TINT_FRAME_INACTIVE);
967 if (theme_inactive_frame)
968 gdk_color_free(theme_inactive_frame);
970 BuildAndSetFrameColor(
972 theme_incognito_frame,
973 GetDefaultTint(ThemeProperties::TINT_FRAME_INCOGNITO),
974 ThemeProperties::COLOR_FRAME_INCOGNITO,
975 ThemeProperties::TINT_FRAME_INCOGNITO);
976 if (theme_incognito_frame)
977 gdk_color_free(theme_incognito_frame);
979 BuildAndSetFrameColor(
981 theme_incognito_inactive_frame,
982 GetDefaultTint(ThemeProperties::TINT_FRAME_INCOGNITO_INACTIVE),
983 ThemeProperties::COLOR_FRAME_INCOGNITO_INACTIVE,
984 ThemeProperties::TINT_FRAME_INCOGNITO_INACTIVE);
985 if (theme_incognito_inactive_frame)
986 gdk_color_free(theme_incognito_inactive_frame);
991 void Gtk2UI::SetThemeColorFromGtk(int id, const GdkColor* color) {
992 colors_[id] = GdkColorToSkColor(*color);
995 void Gtk2UI::SetThemeTintFromGtk(int id, const GdkColor* color) {
996 color_utils::HSL default_tint = GetDefaultTint(id);
997 color_utils::HSL hsl;
998 color_utils::SkColorToHSL(GdkColorToSkColor(*color), &hsl);
1000 if (default_tint.s != -1)
1001 hsl.s = default_tint.s;
1003 if (default_tint.l != -1)
1004 hsl.l = default_tint.l;
1009 GdkColor Gtk2UI::BuildAndSetFrameColor(const GdkColor* base,
1010 const GdkColor* gtk_base,
1011 const color_utils::HSL& tint,
1014 GdkColor out_color = *base;
1016 // The theme author specified a color to use, use it without modification.
1017 out_color = *gtk_base;
1019 // Tint the basic color since this is a heuristic color instead of one
1020 // specified by the theme author.
1021 GdkColorHSLShift(tint, &out_color);
1023 SetThemeColorFromGtk(color_id, &out_color);
1024 SetThemeTintFromGtk(tint_id, &out_color);
1029 SkBitmap Gtk2UI::GenerateGtkThemeBitmap(int id) const {
1031 case IDR_THEME_TOOLBAR: {
1032 GtkStyle* style = gtk_rc_get_style(fake_window_);
1033 GdkColor* color = &style->bg[GTK_STATE_NORMAL];
1035 bitmap.setConfig(SkBitmap::kARGB_8888_Config,
1036 kToolbarImageWidth, kToolbarImageHeight);
1037 bitmap.allocPixels();
1038 bitmap.eraseRGB(color->red >> 8, color->green >> 8, color->blue >> 8);
1041 case IDR_THEME_TAB_BACKGROUND:
1042 return GenerateTabImage(IDR_THEME_FRAME);
1043 case IDR_THEME_TAB_BACKGROUND_INCOGNITO:
1044 return GenerateTabImage(IDR_THEME_FRAME_INCOGNITO);
1046 case IDR_THEME_FRAME:
1047 return GenerateFrameImage(ThemeProperties::COLOR_FRAME,
1048 "frame-gradient-color");
1049 case IDR_FRAME_INACTIVE:
1050 case IDR_THEME_FRAME_INACTIVE:
1051 return GenerateFrameImage(ThemeProperties::COLOR_FRAME_INACTIVE,
1052 "inactive-frame-gradient-color");
1053 case IDR_THEME_FRAME_INCOGNITO:
1054 return GenerateFrameImage(ThemeProperties::COLOR_FRAME_INCOGNITO,
1055 "incognito-frame-gradient-color");
1056 case IDR_THEME_FRAME_INCOGNITO_INACTIVE: {
1057 return GenerateFrameImage(
1058 ThemeProperties::COLOR_FRAME_INCOGNITO_INACTIVE,
1059 "incognito-inactive-frame-gradient-color");
1061 // Icons that sit inside the omnibox shouldn't receive TINT_BUTTONS and
1062 // instead should tint based on the foreground text entry color in GTK+
1063 // mode because some themes that try to be dark *and* light have very
1064 // different colors between the omnibox and the normal background area.
1065 // TODO(erg): Decide what to do about other icons that appear in the
1066 // omnibox, e.g. content settings icons.
1067 case IDR_OMNIBOX_EXTENSION_APP:
1068 case IDR_OMNIBOX_HTTP:
1069 case IDR_OMNIBOX_SEARCH:
1070 case IDR_OMNIBOX_STAR:
1071 case IDR_OMNIBOX_TTS: {
1072 return GenerateTintedIcon(id, entry_tint_);
1074 // In GTK mode, the dark versions of the omnibox icons only ever appear in
1075 // the autocomplete popup and only against the current theme's GtkEntry
1076 // base[GTK_STATE_SELECTED] color, so tint the icons so they won't collide
1077 // with the selected color.
1078 case IDR_OMNIBOX_EXTENSION_APP_DARK:
1079 case IDR_OMNIBOX_HTTP_DARK:
1080 case IDR_OMNIBOX_SEARCH_DARK:
1081 case IDR_OMNIBOX_STAR_DARK:
1082 case IDR_OMNIBOX_TTS_DARK: {
1083 return GenerateTintedIcon(id, selected_entry_tint_);
1085 // In GTK mode, we need to manually render several icons.
1105 return GenerateGTKIcon(id);
1107 case IDR_TOOLBAR_BEZEL_HOVER:
1108 return GenerateToolbarBezel(GTK_STATE_PRELIGHT, IDR_TOOLBAR_BEZEL_HOVER);
1109 case IDR_TOOLBAR_BEZEL_PRESSED:
1110 return GenerateToolbarBezel(GTK_STATE_ACTIVE, IDR_TOOLBAR_BEZEL_PRESSED);
1111 case IDR_BROWSER_ACTION_H:
1112 return GenerateToolbarBezel(GTK_STATE_PRELIGHT, IDR_BROWSER_ACTION_H);
1113 case IDR_BROWSER_ACTION_P:
1114 return GenerateToolbarBezel(GTK_STATE_ACTIVE, IDR_BROWSER_ACTION_P);
1116 return GenerateTintedIcon(id, button_tint_);
1123 SkBitmap Gtk2UI::GenerateFrameImage(
1125 const char* gradient_name) const {
1126 // We use two colors: the main color (passed in) and a lightened version of
1127 // that color (which is supposed to match the light gradient at the top of
1128 // several GTK+ themes, such as Ambiance, Clearlooks or Bluebird).
1129 ColorMap::const_iterator it = colors_.find(color_id);
1130 DCHECK(it != colors_.end());
1131 SkColor base = it->second;
1133 gfx::Canvas canvas(gfx::Size(kToolbarImageWidth, kToolbarImageHeight),
1137 GdkColor* gradient_top_color = NULL;
1138 gtk_widget_style_get(GTK_WIDGET(fake_frame_),
1139 "frame-gradient-size", &gradient_size,
1140 gradient_name, &gradient_top_color,
1142 if (gradient_size) {
1143 SkColor lighter = gradient_top_color ?
1144 GdkColorToSkColor(*gradient_top_color) :
1145 color_utils::HSLShift(base, kGtkFrameShift);
1146 if (gradient_top_color)
1147 gdk_color_free(gradient_top_color);
1148 skia::RefPtr<SkShader> shader = gfx::CreateGradientShader(
1149 0, gradient_size, lighter, base);
1151 paint.setStyle(SkPaint::kFill_Style);
1152 paint.setAntiAlias(true);
1153 paint.setShader(shader.get());
1155 canvas.DrawRect(gfx::Rect(0, 0, kToolbarImageWidth, gradient_size), paint);
1158 canvas.FillRect(gfx::Rect(0, gradient_size, kToolbarImageWidth,
1159 kToolbarImageHeight - gradient_size), base);
1160 return canvas.ExtractImageRep().sk_bitmap();
1163 SkBitmap Gtk2UI::GenerateTabImage(int base_id) const {
1164 const SkBitmap* base_image = GetThemeImageNamed(base_id).ToSkBitmap();
1165 SkBitmap bg_tint = SkBitmapOperations::CreateHSLShiftedBitmap(
1166 *base_image, GetDefaultTint(ThemeProperties::TINT_BACKGROUND_TAB));
1167 return SkBitmapOperations::CreateTiledBitmap(
1168 bg_tint, 0, 0, bg_tint.width(), bg_tint.height());
1171 SkBitmap Gtk2UI::GenerateTintedIcon(
1173 const color_utils::HSL& tint) const {
1174 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
1175 return SkBitmapOperations::CreateHSLShiftedBitmap(
1176 rb.GetImageNamed(base_id).AsBitmap(), tint);
1179 SkBitmap Gtk2UI::GenerateGTKIcon(int base_id) const {
1180 const char* stock_id = NULL;
1181 GtkStateType gtk_state = GTK_STATE_NORMAL;
1182 for (unsigned int i = 0; i < arraysize(kGtkIcons); ++i) {
1183 if (kGtkIcons[i].idr == base_id) {
1184 stock_id = kGtkIcons[i].stock_id;
1185 gtk_state = kGtkIcons[i].gtk_state;
1191 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
1192 SkBitmap default_bitmap = rb.GetImageNamed(base_id).AsBitmap();
1194 gtk_widget_ensure_style(fake_frame_);
1195 GtkStyle* style = gtk_widget_get_style(fake_frame_);
1196 GtkIconSet* icon_set = gtk_style_lookup_icon_set(style, stock_id);
1198 return default_bitmap;
1200 // Ask GTK to render the icon to a buffer, which we will steal from.
1201 GdkPixbuf* gdk_icon = gtk_icon_set_render_icon(
1204 base::i18n::IsRTL() ? GTK_TEXT_DIR_RTL : GTK_TEXT_DIR_LTR,
1206 GTK_ICON_SIZE_SMALL_TOOLBAR,
1211 // This can theoretically happen if an icon theme doesn't provide a
1212 // specific image. This should realistically never happen, but I bet there
1213 // are some theme authors who don't reliably provide all icons.
1214 return default_bitmap;
1218 retval.setConfig(SkBitmap::kARGB_8888_Config,
1219 default_bitmap.width(),
1220 default_bitmap.height());
1221 retval.allocPixels();
1222 retval.eraseColor(0);
1224 const SkBitmap icon = GdkPixbufToImageSkia(gdk_icon);
1225 g_object_unref(gdk_icon);
1227 SkCanvas canvas(retval);
1229 if (gtk_state == GTK_STATE_ACTIVE || gtk_state == GTK_STATE_PRELIGHT) {
1230 SkBitmap border = DrawGtkButtonBorder(gtk_state,
1232 default_bitmap.width(),
1233 default_bitmap.height());
1234 canvas.drawBitmap(border, 0, 0);
1237 canvas.drawBitmap(icon,
1238 (default_bitmap.width() / 2) - (icon.width() / 2),
1239 (default_bitmap.height() / 2) - (icon.height() / 2));
1244 SkBitmap Gtk2UI::GenerateToolbarBezel(int gtk_state, int sizing_idr) const {
1245 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
1246 SkBitmap default_bitmap =
1247 rb.GetImageNamed(sizing_idr).AsBitmap();
1250 retval.setConfig(SkBitmap::kARGB_8888_Config,
1251 default_bitmap.width(),
1252 default_bitmap.height());
1253 retval.allocPixels();
1254 retval.eraseColor(0);
1256 SkCanvas canvas(retval);
1257 SkBitmap border = DrawGtkButtonBorder(
1260 default_bitmap.width(),
1261 default_bitmap.height());
1262 canvas.drawBitmap(border, 0, 0);
1267 void Gtk2UI::GetNormalButtonTintHSL(color_utils::HSL* tint) const {
1268 GtkStyle* window_style = gtk_rc_get_style(fake_window_);
1269 const GdkColor accent_gdk_color = window_style->bg[GTK_STATE_SELECTED];
1270 const GdkColor base_color = window_style->base[GTK_STATE_NORMAL];
1272 GtkStyle* label_style = gtk_rc_get_style(fake_label_.get());
1273 const GdkColor text_color = label_style->fg[GTK_STATE_NORMAL];
1275 PickButtonTintFromColors(accent_gdk_color, text_color, base_color, tint);
1278 void Gtk2UI::GetNormalEntryForegroundHSL(color_utils::HSL* tint) const {
1279 GtkStyle* window_style = gtk_rc_get_style(fake_window_);
1280 const GdkColor accent_gdk_color = window_style->bg[GTK_STATE_SELECTED];
1282 GtkStyle* style = gtk_rc_get_style(fake_entry_.get());
1283 const GdkColor text_color = style->text[GTK_STATE_NORMAL];
1284 const GdkColor base_color = style->base[GTK_STATE_NORMAL];
1286 PickButtonTintFromColors(accent_gdk_color, text_color, base_color, tint);
1289 void Gtk2UI::GetSelectedEntryForegroundHSL(color_utils::HSL* tint) const {
1290 // The simplest of all the tints. We just use the selected text in the entry
1291 // since the icons tinted this way will only be displayed against
1292 // base[GTK_STATE_SELECTED].
1293 GtkStyle* style = gtk_rc_get_style(fake_entry_.get());
1294 const GdkColor color = style->text[GTK_STATE_SELECTED];
1295 color_utils::SkColorToHSL(GdkColorToSkColor(color), tint);
1298 SkBitmap Gtk2UI::DrawGtkButtonBorder(int gtk_state,
1302 // Create a temporary GTK button to snapshot
1303 GtkWidget* window = gtk_offscreen_window_new();
1304 GtkWidget* button = gtk_button_new();
1305 gtk_widget_set_size_request(button, width, height);
1306 gtk_container_add(GTK_CONTAINER(window), button);
1307 gtk_widget_realize(window);
1308 gtk_widget_realize(button);
1309 gtk_widget_show(button);
1310 gtk_widget_show(window);
1313 // We can't just use gtk_widget_grab_focus() here because that sets
1314 // gtk_widget_is_focus(), but not gtk_widget_has_focus(), which is what the
1315 // GtkButton's paint checks.
1316 GTK_WIDGET_SET_FLAGS(button, GTK_HAS_FOCUS);
1319 gtk_widget_set_state(button, static_cast<GtkStateType>(gtk_state));
1323 // http://crbug.com/346740
1324 ANNOTATE_SCOPED_MEMORY_LEAK;
1325 pixmap = gtk_widget_get_snapshot(button, NULL);
1328 gdk_drawable_get_size(GDK_DRAWABLE(pixmap), &w, &h);
1329 DCHECK_EQ(w, width);
1330 DCHECK_EQ(h, height);
1332 // We render the Pixmap to a Pixbuf. This can be slow, as we're scrapping
1334 GdkColormap* colormap = gdk_drawable_get_colormap(pixmap);
1335 GdkPixbuf* pixbuf = gdk_pixbuf_get_from_drawable(NULL,
1336 GDK_DRAWABLE(pixmap),
1340 // Finally, we convert our pixbuf into a type we can use.
1341 SkBitmap border = GdkPixbufToImageSkia(pixbuf);
1342 g_object_unref(pixbuf);
1343 g_object_unref(pixmap);
1344 gtk_widget_destroy(window);
1349 gfx::Insets Gtk2UI::GetButtonInsets() const {
1350 return button_insets_;
1353 void Gtk2UI::UpdateButtonInsets() {
1354 GtkWidget* window = gtk_offscreen_window_new();
1355 GtkWidget* button = gtk_button_new();
1356 gtk_container_add(GTK_CONTAINER(window), button);
1358 GtkBorder* border = NULL;
1359 gtk_widget_style_get(GTK_WIDGET(button),
1366 button_insets_ = gfx::Insets(border->top, border->left,
1367 border->bottom, border->right);
1368 gtk_border_free(border);
1370 // Defined in gtkbutton.c:
1371 button_insets_ = gfx::Insets(1, 1, 1, 1);
1374 gtk_widget_destroy(window);
1377 void Gtk2UI::ClearAllThemeData() {
1378 gtk_images_.clear();
1381 void Gtk2UI::OnStyleSet(GtkWidget* widget, GtkStyle* previous_style) {
1382 ClearAllThemeData();
1384 NativeThemeGtk2::instance()->NotifyObservers();
1387 } // namespace libgtk2ui
1389 views::LinuxUI* BuildGtk2UI() {
1390 return new libgtk2ui::Gtk2UI;