| OLD | NEW |
| (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", >k_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 } | |
| OLD | NEW |