OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/ui/libgtkui/gtk_util.h" | 5 #include "chrome/browser/ui/libgtkui/gtk_util.h" |
6 | 6 |
7 #include <gdk/gdk.h> | 7 #include <gdk/gdk.h> |
8 #include <gdk/gdkx.h> | 8 #include <gdk/gdkx.h> |
9 #include <gtk/gtk.h> | 9 #include <gtk/gtk.h> |
10 #include <stddef.h> | 10 #include <stddef.h> |
11 | 11 |
12 #include <memory> | 12 #include <memory> |
13 | 13 |
14 #include "base/command_line.h" | 14 #include "base/command_line.h" |
15 #include "base/debug/leak_annotations.h" | 15 #include "base/debug/leak_annotations.h" |
16 #include "base/environment.h" | 16 #include "base/environment.h" |
| 17 #include "base/strings/string_split.h" |
| 18 #include "base/strings/string_tokenizer.h" |
| 19 #include "base/strings/string_util.h" |
17 #include "ui/aura/window.h" | 20 #include "ui/aura/window.h" |
18 #include "ui/aura/window_tree_host.h" | 21 #include "ui/aura/window_tree_host.h" |
19 #include "ui/base/accelerators/accelerator.h" | 22 #include "ui/base/accelerators/accelerator.h" |
20 #include "ui/events/event_constants.h" | 23 #include "ui/events/event_constants.h" |
21 #include "ui/events/keycodes/keyboard_code_conversion_x.h" | 24 #include "ui/events/keycodes/keyboard_code_conversion_x.h" |
22 #include "ui/gfx/color_utils.h" | 25 #include "ui/gfx/color_utils.h" |
23 #include "ui/gfx/geometry/size.h" | 26 #include "ui/gfx/geometry/size.h" |
24 | 27 |
25 namespace { | 28 namespace { |
26 | 29 |
(...skipping 154 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
181 | 184 |
182 aura::Window* GetAuraTransientParent(GtkWidget* dialog) { | 185 aura::Window* GetAuraTransientParent(GtkWidget* dialog) { |
183 return reinterpret_cast<aura::Window*>( | 186 return reinterpret_cast<aura::Window*>( |
184 g_object_get_data(G_OBJECT(dialog), kAuraTransientParent)); | 187 g_object_get_data(G_OBJECT(dialog), kAuraTransientParent)); |
185 } | 188 } |
186 | 189 |
187 void ClearAuraTransientParent(GtkWidget* dialog) { | 190 void ClearAuraTransientParent(GtkWidget* dialog) { |
188 g_object_set_data(G_OBJECT(dialog), kAuraTransientParent, NULL); | 191 g_object_set_data(G_OBJECT(dialog), kAuraTransientParent, NULL); |
189 } | 192 } |
190 | 193 |
| 194 #if GTK_MAJOR_VERSION > 2 |
| 195 ScopedStyleContext AppendNode(GtkStyleContext* context, |
| 196 const std::string& css_node) { |
| 197 GtkWidgetPath* path = |
| 198 context ? gtk_widget_path_copy(gtk_style_context_get_path(context)) |
| 199 : gtk_widget_path_new(); |
| 200 |
| 201 enum { |
| 202 // TODO(thomasanderson): Add CSS_NAME here to handle the Gtk3.20 case. |
| 203 CSS_TYPE, |
| 204 CSS_CLASS, |
| 205 CSS_PSEUDOCLASS, |
| 206 } part_type = CSS_TYPE; |
| 207 static const struct { |
| 208 const char* name; |
| 209 GtkStateFlags state_flag; |
| 210 } pseudo_classes[] = { |
| 211 {"active", GTK_STATE_FLAG_ACTIVE}, |
| 212 {"hover", GTK_STATE_FLAG_PRELIGHT}, |
| 213 {"selected", GTK_STATE_FLAG_SELECTED}, |
| 214 {"disabled", GTK_STATE_FLAG_INSENSITIVE}, |
| 215 {"indeterminate", GTK_STATE_FLAG_INCONSISTENT}, |
| 216 {"focus", GTK_STATE_FLAG_FOCUSED}, |
| 217 {"backdrop", GTK_STATE_FLAG_BACKDROP}, |
| 218 // TODO(thomasanderson): These state flags are only available in |
| 219 // GTK 3.10 or later, which is unavailable in the wheezy |
| 220 // sysroot. Add them once the sysroot is updated to jessie. |
| 221 // { "link", GTK_STATE_FLAG_LINK }, |
| 222 // { "visited", GTK_STATE_FLAG_VISITED }, |
| 223 // { "checked", GTK_STATE_FLAG_CHECKED }, |
| 224 }; |
| 225 GtkStateFlags state = |
| 226 context ? gtk_style_context_get_state(context) : GTK_STATE_FLAG_NORMAL; |
| 227 base::StringTokenizer t(css_node, ".:"); |
| 228 t.set_options(base::StringTokenizer::RETURN_DELIMS); |
| 229 while (t.GetNext()) { |
| 230 if (t.token_is_delim()) { |
| 231 if (t.token_begin() == css_node.begin()) { |
| 232 // Special case for the first token. |
| 233 gtk_widget_path_append_type(path, G_TYPE_NONE); |
| 234 } |
| 235 switch (*t.token_begin()) { |
| 236 case '.': |
| 237 part_type = CSS_CLASS; |
| 238 break; |
| 239 case ':': |
| 240 part_type = CSS_PSEUDOCLASS; |
| 241 break; |
| 242 default: |
| 243 NOTREACHED(); |
| 244 } |
| 245 } else { |
| 246 switch (part_type) { |
| 247 case CSS_TYPE: { |
| 248 GType type = g_type_from_name(t.token().c_str()); |
| 249 DCHECK(type); |
| 250 gtk_widget_path_append_type(path, type); |
| 251 break; |
| 252 } |
| 253 case CSS_CLASS: { |
| 254 gtk_widget_path_iter_add_class(path, -1, t.token().c_str()); |
| 255 break; |
| 256 } |
| 257 case CSS_PSEUDOCLASS: { |
| 258 GtkStateFlags state_flag = GTK_STATE_FLAG_NORMAL; |
| 259 for (const auto& pseudo_class_entry : pseudo_classes) { |
| 260 if (strcmp(pseudo_class_entry.name, t.token().c_str()) == 0) { |
| 261 state_flag = pseudo_class_entry.state_flag; |
| 262 break; |
| 263 } |
| 264 } |
| 265 state = static_cast<GtkStateFlags>(state | state_flag); |
| 266 break; |
| 267 } |
| 268 } |
| 269 } |
| 270 } |
| 271 auto child_context = ScopedStyleContext(gtk_style_context_new()); |
| 272 gtk_style_context_set_path(child_context, path); |
| 273 gtk_style_context_set_state(child_context, state); |
| 274 gtk_style_context_set_parent(child_context, context); |
| 275 gtk_widget_path_unref(path); |
| 276 return child_context; |
| 277 } |
| 278 |
| 279 ScopedStyleContext GetStyleContextFromCss(const char* css_selector) { |
| 280 // Prepend "GtkWindow.background" to the selector since all widgets must live |
| 281 // in a window, but we don't want to specify that every time. |
| 282 auto context = AppendNode(nullptr, "GtkWindow.background"); |
| 283 |
| 284 for (const auto& widget_type : |
| 285 base::SplitString(css_selector, base::kWhitespaceASCII, |
| 286 base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY)) { |
| 287 context = AppendNode(context, widget_type); |
| 288 } |
| 289 return context; |
| 290 } |
| 291 |
| 292 SkColor GdkRgbaToSkColor(const GdkRGBA& color) { |
| 293 return SkColorSetARGB(color.alpha * 255, color.red * 255, color.green * 255, |
| 294 color.blue * 255); |
| 295 } |
| 296 |
| 297 SkColor GetFGColor(const char* css_selector) { |
| 298 auto context = GetStyleContextFromCss(css_selector); |
| 299 GdkRGBA color; |
| 300 gtk_style_context_get_color(context, gtk_style_context_get_state(context), |
| 301 &color); |
| 302 return GdkRgbaToSkColor(color); |
| 303 } |
| 304 |
| 305 GtkCssProvider* GetCssProvider(const char* css) { |
| 306 GtkCssProvider* provider = gtk_css_provider_new(); |
| 307 GError* error = nullptr; |
| 308 gtk_css_provider_load_from_data(provider, css, -1, &error); |
| 309 DCHECK(!error); |
| 310 return provider; |
| 311 } |
| 312 |
| 313 void ApplyCssToContext(GtkStyleContext* context, GtkCssProvider* provider) { |
| 314 while (context) { |
| 315 gtk_style_context_add_provider(context, GTK_STYLE_PROVIDER(provider), |
| 316 G_MAXUINT); |
| 317 context = gtk_style_context_get_parent(context); |
| 318 } |
| 319 } |
| 320 |
| 321 void RemoveBorders(GtkStyleContext* context) { |
| 322 static GtkCssProvider* provider = GetCssProvider( |
| 323 "* {" |
| 324 "border-style: none;" |
| 325 "border-radius: 0px;" |
| 326 "border-width: 0px;" |
| 327 "border-image-width: 0px;" |
| 328 "padding: 0px;" |
| 329 "margin: 0px;" |
| 330 "}"); |
| 331 ApplyCssToContext(context, provider); |
| 332 } |
| 333 |
| 334 void AddBorders(GtkStyleContext* context) { |
| 335 static GtkCssProvider* provider = GetCssProvider( |
| 336 "* {" |
| 337 "border-style: solid;" |
| 338 "border-radius: 0px;" |
| 339 "border-width: 1px;" |
| 340 "padding: 0px;" |
| 341 "margin: 0px;" |
| 342 "}"); |
| 343 ApplyCssToContext(context, provider); |
| 344 } |
| 345 |
| 346 // A 1x1 cairo surface that GTK can render into. |
| 347 class PixelSurface { |
| 348 public: |
| 349 PixelSurface() |
| 350 : surface_(cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 1, 1)), |
| 351 cairo_(cairo_create(surface_)) {} |
| 352 |
| 353 ~PixelSurface() { |
| 354 cairo_destroy(cairo_); |
| 355 cairo_surface_destroy(surface_); |
| 356 } |
| 357 |
| 358 // Get the drawing context for GTK to use. |
| 359 cairo_t* cairo() { return cairo_; } |
| 360 |
| 361 // Get the color value of the single pixel. |
| 362 SkColor GetPixelValue() { |
| 363 return *reinterpret_cast<SkColor*>(cairo_image_surface_get_data(surface_)); |
| 364 } |
| 365 |
| 366 private: |
| 367 cairo_surface_t* surface_; |
| 368 cairo_t* cairo_; |
| 369 }; |
| 370 |
| 371 SkColor GetBGColor(const char* css_selector) { |
| 372 // Backgrounds are more general than solid colors (eg. gradients), |
| 373 // but chromium requires us to boil this down to one color. We |
| 374 // cannot use the background-color here because some themes leave it |
| 375 // set to a garbage color because a background-image will cover it |
| 376 // anyway. So we instead render the background into a single pixel, |
| 377 // removing any borders, and hope that we get a good color. |
| 378 auto context = GetStyleContextFromCss(css_selector); |
| 379 RemoveBorders(context); |
| 380 PixelSurface surface; |
| 381 gtk_render_background(context, surface.cairo(), 0, 0, 1, 1); |
| 382 return surface.GetPixelValue(); |
| 383 } |
| 384 |
| 385 SkColor GetBorderColor(const char* css_selector) { |
| 386 // Borders have the same issue as backgrounds, due to the |
| 387 // border-image property. |
| 388 auto context = GetStyleContextFromCss(css_selector); |
| 389 GtkStateFlags state = gtk_style_context_get_state(context); |
| 390 GtkBorderStyle border_style = GTK_BORDER_STYLE_NONE; |
| 391 gtk_style_context_get(context, state, GTK_STYLE_PROPERTY_BORDER_STYLE, |
| 392 &border_style, nullptr); |
| 393 GtkBorder border; |
| 394 gtk_style_context_get_border(context, state, &border); |
| 395 if ((border_style == GTK_BORDER_STYLE_NONE || |
| 396 border_style == GTK_BORDER_STYLE_HIDDEN) || |
| 397 (!border.left && !border.right && !border.top && !border.bottom)) { |
| 398 return SK_ColorTRANSPARENT; |
| 399 } |
| 400 |
| 401 AddBorders(context); |
| 402 PixelSurface surface; |
| 403 gtk_render_frame(context, surface.cairo(), 0, 0, 1, 1); |
| 404 return surface.GetPixelValue(); |
| 405 } |
| 406 #endif |
| 407 |
191 } // namespace libgtkui | 408 } // namespace libgtkui |
OLD | NEW |