Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(87)

Side by Side Diff: chrome/browser/ui/libgtk2ui/gtk2_ui.cc

Issue 2449243002: Gtk3 ui: Add libgtk3ui as a separate build component (Closed)
Patch Set: Add theme_properties dep to //chrome/browser/ui Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « chrome/browser/ui/libgtk2ui/gtk2_ui.h ('k') | chrome/browser/ui/libgtk2ui/gtk2_util.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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.
4
5 #include "chrome/browser/ui/libgtk2ui/gtk2_ui.h"
6
7 #include <math.h>
8 #include <pango/pango.h>
9 #include <X11/Xcursor/Xcursor.h>
10 #include <set>
11 #include <utility>
12
13 #include "base/command_line.h"
14 #include "base/debug/leak_annotations.h"
15 #include "base/environment.h"
16 #include "base/i18n/rtl.h"
17 #include "base/logging.h"
18 #include "base/macros.h"
19 #include "base/nix/mime_util_xdg.h"
20 #include "base/nix/xdg_util.h"
21 #include "base/stl_util.h"
22 #include "base/strings/string_split.h"
23 #include "base/strings/stringprintf.h"
24 #include "chrome/browser/themes/theme_properties.h"
25 #include "chrome/browser/ui/libgtk2ui/app_indicator_icon.h"
26 #include "chrome/browser/ui/libgtk2ui/gtk2_event_loop.h"
27 #include "chrome/browser/ui/libgtk2ui/gtk2_key_bindings_handler.h"
28 #include "chrome/browser/ui/libgtk2ui/gtk2_status_icon.h"
29 #include "chrome/browser/ui/libgtk2ui/gtk2_util.h"
30 #include "chrome/browser/ui/libgtk2ui/native_theme_gtk2.h"
31 #include "chrome/browser/ui/libgtk2ui/print_dialog_gtk2.h"
32 #include "chrome/browser/ui/libgtk2ui/printing_gtk2_util.h"
33 #include "chrome/browser/ui/libgtk2ui/select_file_dialog_impl.h"
34 #include "chrome/browser/ui/libgtk2ui/skia_utils_gtk2.h"
35 #include "chrome/browser/ui/libgtk2ui/unity_service.h"
36 #include "chrome/browser/ui/libgtk2ui/x11_input_method_context_impl_gtk2.h"
37 #include "chrome/grit/theme_resources.h"
38 #include "components/grit/components_scaled_resources.h"
39 #include "third_party/skia/include/core/SkBitmap.h"
40 #include "third_party/skia/include/core/SkCanvas.h"
41 #include "third_party/skia/include/core/SkColor.h"
42 #include "third_party/skia/include/core/SkShader.h"
43 #include "ui/base/resource/resource_bundle.h"
44 #include "ui/display/display.h"
45 #include "ui/gfx/canvas.h"
46 #include "ui/gfx/geometry/rect.h"
47 #include "ui/gfx/geometry/size.h"
48 #include "ui/gfx/image/image.h"
49 #include "ui/gfx/image/image_skia_source.h"
50 #include "ui/gfx/skbitmap_operations.h"
51 #include "ui/gfx/skia_util.h"
52 #include "ui/gfx/x/x11_types.h"
53 #include "ui/native_theme/native_theme.h"
54 #include "ui/resources/grit/ui_resources.h"
55 #include "ui/views/controls/button/blue_button.h"
56 #include "ui/views/controls/button/label_button.h"
57 #include "ui/views/controls/button/label_button_border.h"
58 #include "ui/views/linux_ui/window_button_order_observer.h"
59 #include "ui/views/resources/grit/views_resources.h"
60
61 #if defined(ENABLE_BASIC_PRINTING)
62 #include "printing/printing_context_linux.h"
63 #endif
64 #if defined(USE_GCONF)
65 #include "chrome/browser/ui/libgtk2ui/gconf_listener.h"
66 #endif
67
68 // A minimized port of GtkThemeService into something that can provide colors
69 // and images for aura.
70 //
71 // TODO(erg): There's still a lot that needs ported or done for the first time:
72 //
73 // - Render and inject the omnibox background.
74 // - Make sure to test with a light on dark theme, too.
75
76 // Work around a header bug:
77 // linux/debian_wheezy_i386-sysroot/usr/include/linux/stddef.h redefines NULL
78 // to 0, which breaks -Wsentinel. Get back the normal definition of NULL.
79 // TODO(thakis): Remove this once we update sysroots.
80 #define __need_NULL
81 #include <stddef.h>
82
83 namespace libgtk2ui {
84
85 namespace {
86
87 class GtkButtonImageSource : public gfx::ImageSkiaSource {
88 public:
89 GtkButtonImageSource(const char* idr_string, gfx::Size size)
90 : width_(size.width()), height_(size.height()) {
91 is_blue_ = !!strstr(idr_string, "IDR_BLUE");
92 focus_ = !!strstr(idr_string, "_FOCUSED_");
93
94 if (strstr(idr_string, "_DISABLED")) {
95 state_ = ui::NativeTheme::kDisabled;
96 } else if (strstr(idr_string, "_HOVER")) {
97 state_ = ui::NativeTheme::kHovered;
98 } else if (strstr(idr_string, "_PRESSED")) {
99 state_ = ui::NativeTheme::kPressed;
100 } else {
101 state_ = ui::NativeTheme::kNormal;
102 }
103 }
104
105 ~GtkButtonImageSource() override {}
106
107 gfx::ImageSkiaRep GetImageForScale(float scale) override {
108 int width = width_ * scale;
109 int height = height_ * scale;
110
111 SkBitmap border;
112 border.allocN32Pixels(width, height);
113 border.eraseColor(0);
114
115 // Create a temporary GTK button to snapshot
116 GtkWidget* window = gtk_offscreen_window_new();
117 GtkWidget* button = gtk_toggle_button_new();
118
119 if (state_ == ui::NativeTheme::kPressed)
120 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), true);
121 else if (state_ == ui::NativeTheme::kDisabled)
122 gtk_widget_set_sensitive(button, false);
123
124 gtk_widget_set_size_request(button, width, height);
125 gtk_container_add(GTK_CONTAINER(window), button);
126
127 if (is_blue_)
128 TurnButtonBlue(button);
129
130 gtk_widget_show_all(window);
131
132 cairo_surface_t* surface = cairo_image_surface_create_for_data(
133 static_cast<unsigned char*>(border.getAddr(0, 0)),
134 CAIRO_FORMAT_ARGB32, width, height, width * 4);
135 cairo_t* cr = cairo_create(surface);
136
137 #if GTK_MAJOR_VERSION == 2
138 if (focus_)
139 GTK_WIDGET_SET_FLAGS(button, GTK_HAS_FOCUS);
140
141 int w, h;
142 GdkPixmap* pixmap;
143
144 {
145 // http://crbug.com/346740
146 ANNOTATE_SCOPED_MEMORY_LEAK;
147 pixmap = gtk_widget_get_snapshot(button, NULL);
148 }
149
150 gdk_drawable_get_size(GDK_DRAWABLE(pixmap), &w, &h);
151 GdkColormap* colormap = gdk_drawable_get_colormap(pixmap);
152 GdkPixbuf* pixbuf = gdk_pixbuf_get_from_drawable(
153 NULL, GDK_DRAWABLE(pixmap), colormap, 0, 0, 0, 0, w, h);
154
155 gdk_cairo_set_source_pixbuf(cr, pixbuf, 0, 0);
156 cairo_paint(cr);
157
158 g_object_unref(pixbuf);
159 g_object_unref(pixmap);
160 #else
161 gtk_widget_draw(button, cr);
162
163 // There's probably a better way to do this
164 if (focus_)
165 gtk_render_focus(gtk_widget_get_style_context(button), cr, 0, 0,
166 width, height);
167 #endif
168
169 cairo_destroy(cr);
170 cairo_surface_destroy(surface);
171
172 gtk_widget_destroy(window);
173
174 return gfx::ImageSkiaRep(border, scale);
175 }
176
177 private:
178 bool is_blue_;
179 bool focus_;
180 ui::NativeTheme::State state_;
181 int width_;
182 int height_;
183
184 DISALLOW_COPY_AND_ASSIGN(GtkButtonImageSource);
185 };
186
187 class GtkButtonPainter : public views::Painter {
188 public:
189 explicit GtkButtonPainter(std::string idr) : idr_(idr) {}
190 ~GtkButtonPainter() override {}
191
192 gfx::Size GetMinimumSize() const override { return gfx::Size(); }
193 void Paint(gfx::Canvas* canvas, const gfx::Size& size) override {
194 gfx::ImageSkiaSource* source = new GtkButtonImageSource(idr_.c_str(), size);
195 gfx::ImageSkia image(source, 1);
196 canvas->DrawImageInt(image, 0, 0);
197 }
198
199 private:
200 std::string idr_;
201
202 DISALLOW_COPY_AND_ASSIGN(GtkButtonPainter);
203 };
204
205 struct GObjectDeleter {
206 void operator()(void* ptr) {
207 g_object_unref(ptr);
208 }
209 };
210 struct GtkIconInfoDeleter {
211 void operator()(GtkIconInfo* ptr) {
212 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
213 gtk_icon_info_free(ptr);
214 G_GNUC_END_IGNORE_DEPRECATIONS
215 }
216 };
217 typedef std::unique_ptr<GIcon, GObjectDeleter> ScopedGIcon;
218 typedef std::unique_ptr<GtkIconInfo, GtkIconInfoDeleter> ScopedGtkIconInfo;
219 typedef std::unique_ptr<GdkPixbuf, GObjectDeleter> ScopedGdkPixbuf;
220
221 // Prefix for app indicator ids
222 const char kAppIndicatorIdPrefix[] = "chrome_app_indicator_";
223
224 // Number of app indicators used (used as part of app-indicator id).
225 int indicators_count;
226
227 // The unknown content type.
228 const char* kUnknownContentType = "application/octet-stream";
229
230 // TODO(erg): ThemeService has a whole interface just for reading default
231 // constants. Figure out what to do with that more long term; for now, just
232 // copy the constants themselves here.
233 //
234 // Default tints.
235 const color_utils::HSL kDefaultTintFrameIncognito = { -1, 0.2f, 0.35f };
236 const color_utils::HSL kDefaultTintFrameIncognitoInactive = { -1, 0.3f, 0.6f };
237
238 #if GTK_MAJOR_VERSION == 3
239 const color_utils::HSL kDefaultTintFrameInactive = { -1, -1, 0.75f };
240 #endif // GTK_MAJOR_VERSION == 3
241
242 // Picks a button tint from a set of background colors. While
243 // |accent_color| will usually be the same color through a theme, this
244 // function will get called with the normal GtkLabel |text_color|/GtkWindow
245 // |background_color| pair and the GtkEntry |text_color|/|background_color|
246 // pair. While 3/4 of the time the resulting tint will be the same, themes that
247 // have a dark window background (with light text) and a light text entry (with
248 // dark text) will get better icons with this separated out.
249 void PickButtonTintFromColors(SkColor accent_color,
250 SkColor text_color,
251 SkColor background_color,
252 color_utils::HSL* tint) {
253 color_utils::HSL accent_tint, text_tint, background_tint;
254 color_utils::SkColorToHSL(accent_color, &accent_tint);
255 color_utils::SkColorToHSL(text_color, &text_tint);
256 color_utils::SkColorToHSL(background_color, &background_tint);
257
258 // If the accent color is gray, then our normal HSL tomfoolery will bring out
259 // whatever color is oddly dominant (for example, in rgb space [125, 128,
260 // 125] will tint green instead of gray). Slight differences (+/-10 (4%) to
261 // all color components) should be interpreted as this color being gray and
262 // we should switch into a special grayscale mode.
263 int rb_diff = abs(static_cast<int>(SkColorGetR(accent_color)) -
264 static_cast<int>(SkColorGetB(accent_color)));
265 int rg_diff = abs(static_cast<int>(SkColorGetR(accent_color)) -
266 static_cast<int>(SkColorGetG(accent_color)));
267 int bg_diff = abs(static_cast<int>(SkColorGetB(accent_color)) -
268 static_cast<int>(SkColorGetG(accent_color)));
269 if (rb_diff < 10 && rg_diff < 10 && bg_diff < 10) {
270 // Our accent is white/gray/black. Only the luminance of the accent color
271 // matters.
272 tint->h = -1;
273
274 // Use the saturation of the text.
275 tint->s = text_tint.s;
276
277 // Use the luminance of the accent color UNLESS there isn't enough
278 // luminance contrast between the accent color and the base color.
279 if (fabs(accent_tint.l - background_tint.l) > 0.3)
280 tint->l = accent_tint.l;
281 else
282 tint->l = text_tint.l;
283 } else {
284 // Our accent is a color.
285 tint->h = accent_tint.h;
286
287 // Don't modify the saturation; the amount of color doesn't matter.
288 tint->s = -1;
289
290 // If the text wants us to darken the icon, don't change the luminance (the
291 // icons are already dark enough). Otherwise, lighten the icon by no more
292 // than 0.9 since we don't want a pure-white icon even if the text is pure
293 // white.
294 if (text_tint.l < 0.5)
295 tint->l = -1;
296 else if (text_tint.l <= 0.9)
297 tint->l = text_tint.l;
298 else
299 tint->l = 0.9;
300 }
301 }
302
303 // Returns a gfx::FontRenderParams corresponding to GTK's configuration.
304 gfx::FontRenderParams GetGtkFontRenderParams() {
305 GtkSettings* gtk_settings = gtk_settings_get_default();
306 CHECK(gtk_settings);
307 gint antialias = 0;
308 gint hinting = 0;
309 gchar* hint_style = NULL;
310 gchar* rgba = NULL;
311 g_object_get(gtk_settings,
312 "gtk-xft-antialias", &antialias,
313 "gtk-xft-hinting", &hinting,
314 "gtk-xft-hintstyle", &hint_style,
315 "gtk-xft-rgba", &rgba,
316 NULL);
317
318 gfx::FontRenderParams params;
319 params.antialiasing = antialias != 0;
320
321 if (hinting == 0 || !hint_style || strcmp(hint_style, "hintnone") == 0) {
322 params.hinting = gfx::FontRenderParams::HINTING_NONE;
323 } else if (strcmp(hint_style, "hintslight") == 0) {
324 params.hinting = gfx::FontRenderParams::HINTING_SLIGHT;
325 } else if (strcmp(hint_style, "hintmedium") == 0) {
326 params.hinting = gfx::FontRenderParams::HINTING_MEDIUM;
327 } else if (strcmp(hint_style, "hintfull") == 0) {
328 params.hinting = gfx::FontRenderParams::HINTING_FULL;
329 } else {
330 LOG(WARNING) << "Unexpected gtk-xft-hintstyle \"" << hint_style << "\"";
331 params.hinting = gfx::FontRenderParams::HINTING_NONE;
332 }
333
334 if (!rgba || strcmp(rgba, "none") == 0) {
335 params.subpixel_rendering = gfx::FontRenderParams::SUBPIXEL_RENDERING_NONE;
336 } else if (strcmp(rgba, "rgb") == 0) {
337 params.subpixel_rendering = gfx::FontRenderParams::SUBPIXEL_RENDERING_RGB;
338 } else if (strcmp(rgba, "bgr") == 0) {
339 params.subpixel_rendering = gfx::FontRenderParams::SUBPIXEL_RENDERING_BGR;
340 } else if (strcmp(rgba, "vrgb") == 0) {
341 params.subpixel_rendering = gfx::FontRenderParams::SUBPIXEL_RENDERING_VRGB;
342 } else if (strcmp(rgba, "vbgr") == 0) {
343 params.subpixel_rendering = gfx::FontRenderParams::SUBPIXEL_RENDERING_VBGR;
344 } else {
345 LOG(WARNING) << "Unexpected gtk-xft-rgba \"" << rgba << "\"";
346 params.subpixel_rendering = gfx::FontRenderParams::SUBPIXEL_RENDERING_NONE;
347 }
348
349 g_free(hint_style);
350 g_free(rgba);
351
352 return params;
353 }
354
355 double GetDPI() {
356 // Linux chrome currently does not support dynamic DPI changes.
357 // Keep using the first value detected.
358 static double dpi = -1.f;
359 if (dpi < 0) {
360 const double kDefaultDPI = 96;
361
362 if (display::Display::HasForceDeviceScaleFactor()) {
363 dpi = display::Display::GetForcedDeviceScaleFactor() * kDefaultDPI;
364 return dpi;
365 }
366
367 GtkSettings* gtk_settings = gtk_settings_get_default();
368 CHECK(gtk_settings);
369 gint gtk_dpi = -1;
370 g_object_get(gtk_settings, "gtk-xft-dpi", &gtk_dpi, NULL);
371
372 // GTK multiplies the DPI by 1024 before storing it.
373 dpi = (gtk_dpi > 0) ? gtk_dpi / 1024.0 : kDefaultDPI;
374
375 // DSF is always >=1.0 on win/cros and lower DSF has never been considered
376 // nor tested.
377 dpi = std::max(kDefaultDPI, dpi);
378 }
379 return dpi;
380 }
381
382 // Queries GTK for its font DPI setting and returns the number of pixels in a
383 // point.
384 double GetPixelsInPoint(float device_scale_factor) {
385 double dpi = GetDPI();
386
387 // Take device_scale_factor into account — if Chrome already scales the
388 // entire UI up by 2x, we should not also scale up.
389 dpi /= device_scale_factor;
390
391 // There are 72 points in an inch.
392 return dpi / 72.0;
393 }
394
395 views::LinuxUI::NonClientMiddleClickAction GetDefaultMiddleClickAction() {
396 std::unique_ptr<base::Environment> env(base::Environment::Create());
397 switch (base::nix::GetDesktopEnvironment(env.get())) {
398 case base::nix::DESKTOP_ENVIRONMENT_KDE4:
399 case base::nix::DESKTOP_ENVIRONMENT_KDE5:
400 // Starting with KDE 4.4, windows' titlebars can be dragged with the
401 // middle mouse button to create tab groups. We don't support that in
402 // Chrome, but at least avoid lowering windows in response to middle
403 // clicks to avoid surprising users who expect the KDE behavior.
404 return views::LinuxUI::MIDDLE_CLICK_ACTION_NONE;
405 default:
406 return views::LinuxUI::MIDDLE_CLICK_ACTION_LOWER;
407 }
408 }
409
410 } // namespace
411
412 Gtk2UI::Gtk2UI()
413 : default_font_size_pixels_(0),
414 default_font_style_(gfx::Font::NORMAL),
415 default_font_weight_(gfx::Font::Weight::NORMAL),
416 middle_click_action_(GetDefaultMiddleClickAction()),
417 device_scale_factor_(1.0) {
418 GtkInitFromCommandLine(*base::CommandLine::ForCurrentProcess());
419 }
420
421 Gtk2UI::~Gtk2UI() {}
422
423 void OnThemeChanged(GObject* obj, GParamSpec* param, Gtk2UI* gtkui) {
424 gtkui->ResetStyle();
425 }
426
427 void Gtk2UI::Initialize() {
428 GtkSettings* settings = gtk_settings_get_default();
429 g_signal_connect_after(settings,
430 "notify::gtk-theme-name",
431 G_CALLBACK(OnThemeChanged),
432 this);
433 g_signal_connect_after(settings,
434 "notify::gtk-icon-theme-name",
435 G_CALLBACK(OnThemeChanged),
436 this);
437
438 LoadGtkValues();
439
440 LoadCursorTheme();
441
442 #if defined(ENABLE_BASIC_PRINTING)
443 printing::PrintingContextLinux::SetCreatePrintDialogFunction(
444 &PrintDialogGtk2::CreatePrintDialog);
445 printing::PrintingContextLinux::SetPdfPaperSizeFunction(
446 &GetPdfPaperSizeDeviceUnitsGtk);
447 #endif
448
449 #if defined(USE_GCONF)
450 // We must build this after GTK gets initialized.
451 gconf_listener_.reset(new GConfListener(this));
452 #endif // defined(USE_GCONF)
453
454 indicators_count = 0;
455
456 // Instantiate the singleton instance of Gtk2EventLoop.
457 Gtk2EventLoop::GetInstance();
458 }
459
460 bool Gtk2UI::GetTint(int id, color_utils::HSL* tint) const {
461 switch (id) {
462 // Tints for which the cross-platform default is fine. Before adding new
463 // values here, specifically verify they work well on Linux.
464 case ThemeProperties::TINT_BACKGROUND_TAB:
465 // TODO(estade): Return something useful for TINT_BUTTONS so that chrome://
466 // page icons are colored appropriately.
467 case ThemeProperties::TINT_BUTTONS:
468 break;
469 default:
470 // Assume any tints not specifically verified on Linux aren't usable.
471 // TODO(pkasting): Try to remove values from |colors_| that could just be
472 // added to the group above instead.
473 NOTREACHED();
474 }
475 return false;
476 }
477
478 bool Gtk2UI::GetColor(int id, SkColor* color) const {
479 ColorMap::const_iterator it = colors_.find(id);
480 if (it != colors_.end()) {
481 *color = it->second;
482 return true;
483 }
484
485 return false;
486 }
487
488 SkColor Gtk2UI::GetFocusRingColor() const {
489 return focus_ring_color_;
490 }
491
492 SkColor Gtk2UI::GetThumbActiveColor() const {
493 return thumb_active_color_;
494 }
495
496 SkColor Gtk2UI::GetThumbInactiveColor() const {
497 return thumb_inactive_color_;
498 }
499
500 SkColor Gtk2UI::GetTrackColor() const {
501 return track_color_;
502 }
503
504 SkColor Gtk2UI::GetActiveSelectionBgColor() const {
505 return active_selection_bg_color_;
506 }
507
508 SkColor Gtk2UI::GetActiveSelectionFgColor() const {
509 return active_selection_fg_color_;
510 }
511
512 SkColor Gtk2UI::GetInactiveSelectionBgColor() const {
513 return inactive_selection_bg_color_;
514 }
515
516 SkColor Gtk2UI::GetInactiveSelectionFgColor() const {
517 return inactive_selection_fg_color_;
518 }
519
520 double Gtk2UI::GetCursorBlinkInterval() const {
521 // From http://library.gnome.org/devel/gtk/unstable/GtkSettings.html, this is
522 // the default value for gtk-cursor-blink-time.
523 static const gint kGtkDefaultCursorBlinkTime = 1200;
524
525 // Dividing GTK's cursor blink cycle time (in milliseconds) by this value
526 // yields an appropriate value for
527 // content::RendererPreferences::caret_blink_interval. This matches the
528 // logic in the WebKit GTK port.
529 static const double kGtkCursorBlinkCycleFactor = 2000.0;
530
531 gint cursor_blink_time = kGtkDefaultCursorBlinkTime;
532 gboolean cursor_blink = TRUE;
533 g_object_get(gtk_settings_get_default(),
534 "gtk-cursor-blink-time", &cursor_blink_time,
535 "gtk-cursor-blink", &cursor_blink,
536 NULL);
537 return cursor_blink ? (cursor_blink_time / kGtkCursorBlinkCycleFactor) : 0.0;
538 }
539
540 ui::NativeTheme* Gtk2UI::GetNativeTheme(aura::Window* window) const {
541 ui::NativeTheme* native_theme_override = NULL;
542 if (!native_theme_overrider_.is_null())
543 native_theme_override = native_theme_overrider_.Run(window);
544
545 if (native_theme_override)
546 return native_theme_override;
547
548 return NativeThemeGtk2::instance();
549 }
550
551 void Gtk2UI::SetNativeThemeOverride(const NativeThemeGetter& callback) {
552 native_theme_overrider_ = callback;
553 }
554
555 bool Gtk2UI::GetDefaultUsesSystemTheme() const {
556 std::unique_ptr<base::Environment> env(base::Environment::Create());
557
558 switch (base::nix::GetDesktopEnvironment(env.get())) {
559 case base::nix::DESKTOP_ENVIRONMENT_GNOME:
560 case base::nix::DESKTOP_ENVIRONMENT_UNITY:
561 case base::nix::DESKTOP_ENVIRONMENT_XFCE:
562 return true;
563 case base::nix::DESKTOP_ENVIRONMENT_KDE3:
564 case base::nix::DESKTOP_ENVIRONMENT_KDE4:
565 case base::nix::DESKTOP_ENVIRONMENT_KDE5:
566 case base::nix::DESKTOP_ENVIRONMENT_OTHER:
567 return false;
568 }
569 // Unless GetDesktopEnvironment() badly misbehaves, this should never happen.
570 NOTREACHED();
571 return false;
572 }
573
574 void Gtk2UI::SetDownloadCount(int count) const {
575 if (unity::IsRunning())
576 unity::SetDownloadCount(count);
577 }
578
579 void Gtk2UI::SetProgressFraction(float percentage) const {
580 if (unity::IsRunning())
581 unity::SetProgressFraction(percentage);
582 }
583
584 bool Gtk2UI::IsStatusIconSupported() const {
585 return true;
586 }
587
588 std::unique_ptr<views::StatusIconLinux> Gtk2UI::CreateLinuxStatusIcon(
589 const gfx::ImageSkia& image,
590 const base::string16& tool_tip) const {
591 if (AppIndicatorIcon::CouldOpen()) {
592 ++indicators_count;
593 return std::unique_ptr<views::StatusIconLinux>(new AppIndicatorIcon(
594 base::StringPrintf("%s%d", kAppIndicatorIdPrefix, indicators_count),
595 image, tool_tip));
596 } else {
597 return std::unique_ptr<views::StatusIconLinux>(
598 new Gtk2StatusIcon(image, tool_tip));
599 }
600 }
601
602 gfx::Image Gtk2UI::GetIconForContentType(
603 const std::string& content_type,
604 int size) const {
605 // This call doesn't take a reference.
606 GtkIconTheme* theme = gtk_icon_theme_get_default();
607
608 std::string content_types[] = {
609 content_type, kUnknownContentType
610 };
611
612 for (size_t i = 0; i < arraysize(content_types); ++i) {
613 ScopedGIcon icon(g_content_type_get_icon(content_types[i].c_str()));
614 ScopedGtkIconInfo icon_info(
615 gtk_icon_theme_lookup_by_gicon(
616 theme, icon.get(), size,
617 static_cast<GtkIconLookupFlags>(GTK_ICON_LOOKUP_FORCE_SIZE)));
618 if (!icon_info)
619 continue;
620 ScopedGdkPixbuf pixbuf(gtk_icon_info_load_icon(icon_info.get(), NULL));
621 if (!pixbuf)
622 continue;
623
624 SkBitmap bitmap = GdkPixbufToImageSkia(pixbuf.get());
625 DCHECK_EQ(size, bitmap.width());
626 DCHECK_EQ(size, bitmap.height());
627 gfx::ImageSkia image_skia = gfx::ImageSkia::CreateFrom1xBitmap(bitmap);
628 image_skia.MakeThreadSafe();
629 return gfx::Image(image_skia);
630 }
631 return gfx::Image();
632 }
633
634 std::unique_ptr<views::Border> Gtk2UI::CreateNativeBorder(
635 views::LabelButton* owning_button,
636 std::unique_ptr<views::LabelButtonBorder> border) {
637 if (owning_button->GetNativeTheme() != NativeThemeGtk2::instance())
638 return std::move(border);
639
640 std::unique_ptr<views::LabelButtonAssetBorder> gtk_border(
641 new views::LabelButtonAssetBorder(owning_button->style()));
642
643 gtk_border->set_insets(border->GetInsets());
644
645 static struct {
646 const char* idr;
647 const char* idr_blue;
648 bool focus;
649 views::Button::ButtonState state;
650 } const paintstate[] = {
651 { "IDR_BUTTON_NORMAL",
652 "IDR_BLUE_BUTTON_NORMAL",
653 false, views::Button::STATE_NORMAL, },
654 { "IDR_BUTTON_HOVER",
655 "IDR_BLUE_BUTTON_HOVER",
656 false, views::Button::STATE_HOVERED, },
657 { "IDR_BUTTON_PRESSED",
658 "IDR_BLUE_BUTTON_PRESSED",
659 false, views::Button::STATE_PRESSED, },
660 { "IDR_BUTTON_DISABLED",
661 "IDR_BLUE_BUTTON_DISABLED",
662 false, views::Button::STATE_DISABLED, },
663
664 { "IDR_BUTTON_FOCUSED_NORMAL",
665 "IDR_BLUE_BUTTON_FOCUSED_NORMAL",
666 true, views::Button::STATE_NORMAL, },
667 { "IDR_BUTTON_FOCUSED_HOVER",
668 "IDR_BLUE_BUTTON_FOCUSED_HOVER",
669 true, views::Button::STATE_HOVERED, },
670 { "IDR_BUTTON_FOCUSED_PRESSED",
671 "IDR_BLUE_BUTTON_FOCUSED_PRESSED",
672 true, views::Button::STATE_PRESSED, },
673 { "IDR_BUTTON_DISABLED",
674 "IDR_BLUE_BUTTON_DISABLED",
675 true, views::Button::STATE_DISABLED, },
676 };
677
678 bool is_blue =
679 owning_button->GetClassName() == views::BlueButton::kViewClassName;
680
681 for (unsigned i = 0; i < arraysize(paintstate); i++) {
682 views::Painter* painter = nullptr;
683
684 if (border->PaintsButtonState(paintstate[i].focus, paintstate[i].state)) {
685 std::string idr = is_blue ? paintstate[i].idr_blue : paintstate[i].idr;
686 painter = new GtkButtonPainter(idr);
687 }
688
689 gtk_border->SetPainter(paintstate[i].focus, paintstate[i].state, painter);
690 }
691
692 return std::move(gtk_border);
693 }
694
695 void Gtk2UI::AddWindowButtonOrderObserver(
696 views::WindowButtonOrderObserver* observer) {
697 if (!leading_buttons_.empty() || !trailing_buttons_.empty()) {
698 observer->OnWindowButtonOrderingChange(leading_buttons_,
699 trailing_buttons_);
700 }
701
702 observer_list_.AddObserver(observer);
703 }
704
705 void Gtk2UI::RemoveWindowButtonOrderObserver(
706 views::WindowButtonOrderObserver* observer) {
707 observer_list_.RemoveObserver(observer);
708 }
709
710 void Gtk2UI::SetWindowButtonOrdering(
711 const std::vector<views::FrameButton>& leading_buttons,
712 const std::vector<views::FrameButton>& trailing_buttons) {
713 leading_buttons_ = leading_buttons;
714 trailing_buttons_ = trailing_buttons;
715
716 for (views::WindowButtonOrderObserver& observer : observer_list_)
717 observer.OnWindowButtonOrderingChange(leading_buttons_, trailing_buttons_);
718 }
719
720 void Gtk2UI::SetNonClientMiddleClickAction(NonClientMiddleClickAction action) {
721 middle_click_action_ = action;
722 }
723
724 std::unique_ptr<ui::LinuxInputMethodContext> Gtk2UI::CreateInputMethodContext(
725 ui::LinuxInputMethodContextDelegate* delegate,
726 bool is_simple) const {
727 return std::unique_ptr<ui::LinuxInputMethodContext>(
728 new X11InputMethodContextImplGtk2(delegate, is_simple));
729 }
730
731 gfx::FontRenderParams Gtk2UI::GetDefaultFontRenderParams() const {
732 static gfx::FontRenderParams params = GetGtkFontRenderParams();
733 return params;
734 }
735
736 void Gtk2UI::GetDefaultFontDescription(
737 std::string* family_out,
738 int* size_pixels_out,
739 int* style_out,
740 gfx::Font::Weight* weight_out,
741 gfx::FontRenderParams* params_out) const {
742 *family_out = default_font_family_;
743 *size_pixels_out = default_font_size_pixels_;
744 *style_out = default_font_style_;
745 *weight_out = default_font_weight_;
746 *params_out = default_font_render_params_;
747 }
748
749 ui::SelectFileDialog* Gtk2UI::CreateSelectFileDialog(
750 ui::SelectFileDialog::Listener* listener,
751 ui::SelectFilePolicy* policy) const {
752 return SelectFileDialogImpl::Create(listener, policy);
753 }
754
755 bool Gtk2UI::UnityIsRunning() {
756 return unity::IsRunning();
757 }
758
759 views::LinuxUI::NonClientMiddleClickAction
760 Gtk2UI::GetNonClientMiddleClickAction() {
761 return middle_click_action_;
762 }
763
764 void Gtk2UI::NotifyWindowManagerStartupComplete() {
765 // TODO(port) Implement this using _NET_STARTUP_INFO_BEGIN/_NET_STARTUP_INFO
766 // from http://standards.freedesktop.org/startup-notification-spec/ instead.
767 gdk_notify_startup_complete();
768 }
769
770 bool Gtk2UI::MatchEvent(const ui::Event& event,
771 std::vector<ui::TextEditCommandAuraLinux>* commands) {
772 // Ensure that we have a keyboard handler.
773 if (!key_bindings_handler_)
774 key_bindings_handler_.reset(new Gtk2KeyBindingsHandler);
775
776 return key_bindings_handler_->MatchEvent(event, commands);
777 }
778
779 void Gtk2UI::SetScrollbarColors() {
780 thumb_active_color_ = SkColorSetRGB(244, 244, 244);
781 thumb_inactive_color_ = SkColorSetRGB(234, 234, 234);
782 track_color_ = SkColorSetRGB(211, 211, 211);
783
784 NativeThemeGtk2::instance()->GetChromeStyleColor(
785 "scrollbar-slider-prelight-color", &thumb_active_color_);
786 NativeThemeGtk2::instance()->GetChromeStyleColor(
787 "scrollbar-slider-normal-color", &thumb_inactive_color_);
788 NativeThemeGtk2::instance()->GetChromeStyleColor("scrollbar-trough-color",
789 &track_color_);
790 }
791
792 void Gtk2UI::LoadGtkValues() {
793 // TODO(erg): GtkThemeService had a comment here about having to muck with
794 // the raw Prefs object to remove prefs::kCurrentThemeImages or else we'd
795 // regress startup time. Figure out how to do that when we can't access the
796 // prefs system from here.
797
798 NativeThemeGtk2* theme = NativeThemeGtk2::instance();
799
800 SkColor toolbar_color =
801 theme->GetSystemColor(ui::NativeTheme::kColorId_DialogBackground);
802 SkColor label_color =
803 theme->GetSystemColor(ui::NativeTheme::kColorId_LabelEnabledColor);
804
805 colors_[ThemeProperties::COLOR_CONTROL_BACKGROUND] = toolbar_color;
806 colors_[ThemeProperties::COLOR_TOOLBAR] = toolbar_color;
807
808 colors_[ThemeProperties::COLOR_TOOLBAR_BUTTON_ICON] =
809 color_utils::DeriveDefaultIconColor(label_color);
810
811 colors_[ThemeProperties::COLOR_TAB_TEXT] = label_color;
812 colors_[ThemeProperties::COLOR_BOOKMARK_TEXT] = label_color;
813 colors_[ThemeProperties::COLOR_BACKGROUND_TAB_TEXT] =
814 color_utils::BlendTowardOppositeLuma(label_color, 50);
815
816 UpdateDefaultFont();
817
818 // Build the various icon tints.
819 GetNormalButtonTintHSL(&button_tint_);
820 GetNormalEntryForegroundHSL(&entry_tint_);
821 GetSelectedEntryForegroundHSL(&selected_entry_tint_);
822
823 // We pick the text and background colors for the NTP out of the colors for a
824 // GtkEntry. We do this because GtkEntries background color is never the same
825 // as |toolbar_color|, is usually a white, and when it isn't a white,
826 // provides sufficient contrast to |toolbar_color|. Try this out with
827 // Darklooks, HighContrastInverse or ThinIce.
828
829 SkColor ntp_background =
830 theme->GetSystemColor(
831 ui::NativeTheme::kColorId_TextfieldDefaultBackground);
832 SkColor ntp_foreground =
833 theme->GetSystemColor(
834 ui::NativeTheme::kColorId_TextfieldDefaultColor);
835
836 colors_[ThemeProperties::COLOR_NTP_BACKGROUND] = ntp_background;
837 colors_[ThemeProperties::COLOR_NTP_TEXT] = ntp_foreground;
838
839 // The NTP header is the color that surrounds the current active thumbnail on
840 // the NTP, and acts as the border of the "Recent Links" box. It would be
841 // awesome if they were separated so we could use GetBorderColor() for the
842 // border around the "Recent Links" section, but matching the frame color is
843 // more important.
844
845 BuildFrameColors();
846 SkColor frame_color = colors_[ThemeProperties::COLOR_FRAME];
847 colors_[ThemeProperties::COLOR_NTP_HEADER] = frame_color;
848 colors_[ThemeProperties::COLOR_NTP_SECTION] = toolbar_color;
849 colors_[ThemeProperties::COLOR_NTP_SECTION_TEXT] = label_color;
850
851 SkColor link_color =
852 theme->GetSystemColor(ui::NativeTheme::kColorId_LinkEnabled);
853 colors_[ThemeProperties::COLOR_NTP_LINK] = link_color;
854 colors_[ThemeProperties::COLOR_NTP_LINK_UNDERLINE] = link_color;
855 colors_[ThemeProperties::COLOR_NTP_SECTION_LINK] = link_color;
856 colors_[ThemeProperties::COLOR_NTP_SECTION_LINK_UNDERLINE] = link_color;
857
858 // Generate the colors that we pass to WebKit.
859 focus_ring_color_ = frame_color;
860
861 SetScrollbarColors();
862
863 // Some GTK themes only define the text selection colors on the GtkEntry
864 // class, so we need to use that for getting selection colors.
865 active_selection_bg_color_ =
866 theme->GetSystemColor(
867 ui::NativeTheme::kColorId_TextfieldSelectionBackgroundFocused);
868 active_selection_fg_color_ =
869 theme->GetSystemColor(
870 ui::NativeTheme::kColorId_TextfieldSelectionColor);
871 inactive_selection_bg_color_ =
872 theme->GetSystemColor(
873 ui::NativeTheme::kColorId_TextfieldReadOnlyBackground);
874 inactive_selection_fg_color_ =
875 theme->GetSystemColor(
876 ui::NativeTheme::kColorId_TextfieldReadOnlyColor);
877
878 colors_[ThemeProperties::COLOR_TAB_THROBBER_SPINNING] =
879 theme->GetSystemColor(ui::NativeTheme::kColorId_ThrobberSpinningColor);
880 colors_[ThemeProperties::COLOR_TAB_THROBBER_WAITING] =
881 theme->GetSystemColor(ui::NativeTheme::kColorId_ThrobberWaitingColor);
882 }
883
884 void Gtk2UI::LoadCursorTheme() {
885 GtkSettings* settings = gtk_settings_get_default();
886
887 gchar* theme = nullptr;
888 gint size = 0;
889 g_object_get(settings,
890 "gtk-cursor-theme-name", &theme,
891 "gtk-cursor-theme-size", &size,
892 nullptr);
893
894 if (theme)
895 XcursorSetTheme(gfx::GetXDisplay(), theme);
896 if (size)
897 XcursorSetDefaultSize(gfx::GetXDisplay(), size);
898
899 g_free(theme);
900 }
901
902 void Gtk2UI::BuildFrameColors() {
903 #if GTK_MAJOR_VERSION == 2
904 NativeThemeGtk2* theme = NativeThemeGtk2::instance();
905 color_utils::HSL kDefaultFrameShift = { -1, -1, 0.4 };
906 SkColor frame_color =
907 theme->GetSystemColor(ui::NativeTheme::kColorId_WindowBackground);
908 frame_color = color_utils::HSLShift(frame_color, kDefaultFrameShift);
909 theme->GetChromeStyleColor("frame-color", &frame_color);
910 colors_[ThemeProperties::COLOR_FRAME] = frame_color;
911
912 GtkStyle* style = gtk_rc_get_style(theme->GetWindow());
913 SkColor temp_color = color_utils::HSLShift(
914 GdkColorToSkColor(style->bg[GTK_STATE_INSENSITIVE]),
915 kDefaultFrameShift);
916 theme->GetChromeStyleColor("inactive-frame-color", &temp_color);
917 colors_[ThemeProperties::COLOR_FRAME_INACTIVE] = temp_color;
918
919 temp_color = color_utils::HSLShift(
920 frame_color,
921 kDefaultTintFrameIncognito);
922 theme->GetChromeStyleColor("incognito-frame-color", &temp_color);
923 colors_[ThemeProperties::COLOR_FRAME_INCOGNITO] = temp_color;
924
925 temp_color = color_utils::HSLShift(
926 frame_color,
927 kDefaultTintFrameIncognitoInactive);
928 theme->GetChromeStyleColor("incognito-inactive-frame-color", &temp_color);
929 colors_[ThemeProperties::COLOR_FRAME_INCOGNITO_INACTIVE] = temp_color;
930 #else
931 auto set_frame_color = [this](int color_id) {
932 // Render a GtkHeaderBar as our title bar, cropping out any curved edges
933 // on the left and right sides. Also remove the bottom border for good
934 // measure.
935 SkBitmap bitmap;
936 bitmap.allocN32Pixels(1, 1);
937 bitmap.eraseColor(0);
938
939 static GtkWidget* menu = nullptr;
940 if (!menu) {
941 menu = gtk_menu_bar_new();
942 gtk_widget_set_size_request(menu, 1, 1);
943
944 GtkWidget* window = gtk_offscreen_window_new();
945 gtk_container_add(GTK_CONTAINER(window), menu);
946
947 gtk_widget_show_all(window);
948 }
949
950 cairo_surface_t* surface = cairo_image_surface_create_for_data(
951 static_cast<unsigned char*>(bitmap.getAddr(0, 0)), CAIRO_FORMAT_ARGB32,
952 bitmap.width(), bitmap.height(),
953 cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, 1));
954 cairo_t* cr = cairo_create(surface);
955 gtk_widget_draw(menu, cr);
956 cairo_destroy(cr);
957 cairo_surface_destroy(surface);
958
959 switch (color_id) {
960 case ThemeProperties::COLOR_FRAME_INACTIVE:
961 bitmap = SkBitmapOperations::CreateHSLShiftedBitmap(
962 bitmap, kDefaultTintFrameInactive);
963 break;
964 case ThemeProperties::COLOR_FRAME_INCOGNITO:
965 bitmap = SkBitmapOperations::CreateHSLShiftedBitmap(
966 bitmap, kDefaultTintFrameIncognito);
967 break;
968 case ThemeProperties::COLOR_FRAME_INCOGNITO_INACTIVE:
969 bitmap = SkBitmapOperations::CreateHSLShiftedBitmap(
970 bitmap, kDefaultTintFrameIncognitoInactive);
971 break;
972 }
973
974 bitmap.lockPixels();
975 colors_[color_id] = bitmap.getColor(0, 0);
976 bitmap.unlockPixels();
977 };
978
979 set_frame_color(ThemeProperties::COLOR_FRAME);
980 set_frame_color(ThemeProperties::COLOR_FRAME_INACTIVE);
981 set_frame_color(ThemeProperties::COLOR_FRAME_INCOGNITO);
982 set_frame_color(ThemeProperties::COLOR_FRAME_INCOGNITO_INACTIVE);
983 #endif
984 }
985
986 void Gtk2UI::GetNormalButtonTintHSL(color_utils::HSL* tint) const {
987 NativeThemeGtk2* theme = NativeThemeGtk2::instance();
988
989 SkColor accent_color =
990 theme->GetSystemColor(ui::NativeTheme::kColorId_ProminentButtonColor);
991 SkColor text_color =
992 theme->GetSystemColor(
993 ui::NativeTheme::kColorId_LabelEnabledColor);
994 SkColor base_color =
995 theme->GetSystemColor(ui::NativeTheme::kColorId_DialogBackground);
996
997 PickButtonTintFromColors(accent_color, text_color, base_color, tint);
998 }
999
1000 void Gtk2UI::GetNormalEntryForegroundHSL(color_utils::HSL* tint) const {
1001 NativeThemeGtk2* theme = NativeThemeGtk2::instance();
1002
1003 SkColor accent_color =
1004 theme->GetSystemColor(ui::NativeTheme::kColorId_ProminentButtonColor);
1005 SkColor text_color =
1006 theme->GetSystemColor(
1007 ui::NativeTheme::kColorId_TextfieldDefaultColor);
1008 SkColor base_color =
1009 theme->GetSystemColor(
1010 ui::NativeTheme::kColorId_TextfieldDefaultBackground);
1011
1012 PickButtonTintFromColors(accent_color, text_color, base_color, tint);
1013 }
1014
1015 void Gtk2UI::GetSelectedEntryForegroundHSL(color_utils::HSL* tint) const {
1016 // The simplest of all the tints. We just use the selected text in the entry
1017 // since the icons tinted this way will only be displayed against
1018 // base[GTK_STATE_SELECTED].
1019 SkColor color =
1020 NativeThemeGtk2::instance()->GetSystemColor(
1021 ui::NativeTheme::kColorId_TextfieldSelectionColor);
1022
1023 color_utils::SkColorToHSL(color, tint);
1024 }
1025
1026 void Gtk2UI::UpdateDefaultFont() {
1027 PangoContext* pc = gtk_widget_get_pango_context(
1028 NativeThemeGtk2::instance()->GetLabel());
1029 const PangoFontDescription* desc = pango_context_get_font_description(pc);
1030
1031 // Use gfx::FontRenderParams to select a family and determine the rendering
1032 // settings.
1033 gfx::FontRenderParamsQuery query;
1034 query.families = base::SplitString(pango_font_description_get_family(desc),
1035 ",", base::TRIM_WHITESPACE,
1036 base::SPLIT_WANT_ALL);
1037
1038 if (pango_font_description_get_size_is_absolute(desc)) {
1039 // If the size is absolute, it's specified in Pango units. There are
1040 // PANGO_SCALE Pango units in a device unit (pixel).
1041 const int size_pixels = pango_font_description_get_size(desc) / PANGO_SCALE;
1042 default_font_size_pixels_ = size_pixels;
1043 query.pixel_size = size_pixels;
1044 } else {
1045 // Non-absolute sizes are in points (again scaled by PANGO_SIZE).
1046 // Round the value when converting to pixels to match GTK's logic.
1047 const double size_points = pango_font_description_get_size(desc) /
1048 static_cast<double>(PANGO_SCALE);
1049 default_font_size_pixels_ = static_cast<int>(
1050 GetPixelsInPoint(device_scale_factor_) * size_points + 0.5);
1051 query.point_size = static_cast<int>(size_points);
1052 }
1053
1054 query.style = gfx::Font::NORMAL;
1055 query.weight =
1056 static_cast<gfx::Font::Weight>(pango_font_description_get_weight(desc));
1057 // TODO(davemoore): What about PANGO_STYLE_OBLIQUE?
1058 if (pango_font_description_get_style(desc) == PANGO_STYLE_ITALIC)
1059 query.style |= gfx::Font::ITALIC;
1060
1061 default_font_render_params_ =
1062 gfx::GetFontRenderParams(query, &default_font_family_);
1063 default_font_style_ = query.style;
1064 }
1065
1066 void Gtk2UI::ResetStyle() {
1067 LoadGtkValues();
1068 NativeThemeGtk2::instance()->NotifyObservers();
1069 }
1070
1071 void Gtk2UI::UpdateDeviceScaleFactor(float device_scale_factor) {
1072 device_scale_factor_ = device_scale_factor;
1073 UpdateDefaultFont();
1074 }
1075
1076 float Gtk2UI::GetDeviceScaleFactor() const {
1077 if (display::Display::HasForceDeviceScaleFactor())
1078 return display::Display::GetForcedDeviceScaleFactor();
1079 const int kCSSDefaultDPI = 96;
1080 const float scale = GetDPI() / kCSSDefaultDPI;
1081
1082 // Blacklist scaling factors <130% (crbug.com/484400) and round
1083 // to 1 decimal to prevent rendering problems (crbug.com/485183).
1084 return scale < 1.3f ? 1.0f : roundf(scale * 10) / 10;
1085 }
1086
1087 } // namespace libgtk2ui
1088
1089 views::LinuxUI* BuildGtk2UI() {
1090 return new libgtk2ui::Gtk2UI;
1091 }
OLDNEW
« no previous file with comments | « chrome/browser/ui/libgtk2ui/gtk2_ui.h ('k') | chrome/browser/ui/libgtk2ui/gtk2_util.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698