thomasanderson | acc4214 | 2017-01-10 21:11:45 | [diff] [blame] | 1 | # Linux GTK Theme Integration |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 2 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame] | 3 | The GTK+ port of Chromium has a mode where we try to match the user's GTK theme |
thomasanderson | acc4214 | 2017-01-10 21:11:45 | [diff] [blame] | 4 | (which can be enabled under Settings -> Appearance -> Use GTK+ theme). |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 5 | |
thomasanderson | acc4214 | 2017-01-10 21:11:45 | [diff] [blame] | 6 | # GTK3 |
| 7 | |
| 8 | At some point after version 57, Chromium will switch to using the GTK3 theme by |
| 9 | default. |
| 10 | |
| 11 | ## How Chromium determines which colors to use |
| 12 | |
| 13 | GTK3 added a new CSS theming engine which gives fine-tuned control over how |
| 14 | widgets are styled. Chromium's themes, by contrast, are much simpler: it is |
| 15 | mostly a list of about 80 colors (see //src/ui/native_theme/native_theme.h) |
| 16 | overridden by the theme. Chromium usually doesn't use GTK to render entire |
| 17 | widgets, but instead tries to determine colors from them. |
| 18 | |
thomasanderson | 419a91a1 | 2017-02-10 20:05:48 | [diff] [blame^] | 19 | Chromium needs foreground, background and border colors from widgets. The |
| 20 | foreground color is simply taken from the CSS "color" property. Backgrounds and |
| 21 | borders are complicated because in general they might have multiple gradients or |
| 22 | images. To get the color, Chromium uses GTK to render the background or border |
| 23 | into a 24x24 bitmap and uses the average color for theming. This mostly gives |
| 24 | reasonable results, but in case theme authors do not like the resulting color, |
| 25 | they have the option to theme Chromium widgets specially. |
thomasanderson | acc4214 | 2017-01-10 21:11:45 | [diff] [blame] | 26 | |
| 27 | ## Note to GTK theme authors: How to theme Chromium widgets |
| 28 | |
| 29 | Every widget Chromium uses will have a "chromium" style class added to it. For |
thomasanderson | 419a91a1 | 2017-02-10 20:05:48 | [diff] [blame^] | 30 | example, a textfield selector might look like: |
thomasanderson | acc4214 | 2017-01-10 21:11:45 | [diff] [blame] | 31 | |
| 32 | ``` |
| 33 | .window.background.chromium .entry.chromium |
| 34 | ``` |
| 35 | |
| 36 | If themes want to handle Chromium textfields specially, for GTK3.0 - GTK3.19, |
| 37 | they might use: |
| 38 | |
| 39 | ``` |
| 40 | /* Normal case */ |
| 41 | .entry { |
| 42 | color: #ffffff; |
| 43 | background-color: #000000; |
| 44 | } |
| 45 | |
| 46 | /* Chromium-specific case */ |
| 47 | .entry.chromium { |
| 48 | color: #ff0000; |
| 49 | background-color: #00ff00; |
| 50 | } |
| 51 | ``` |
| 52 | |
| 53 | For GTK3.20 or later, themes will as usual have to replace ".entry" with |
| 54 | "entry". |
| 55 | |
thomasanderson | acc4214 | 2017-01-10 21:11:45 | [diff] [blame] | 56 | The list of CSS selectors that Chromium uses to determine its colors is in |
| 57 | //src/chrome/browser/ui/libgtkui/native_theme_gtk3.cc. |
| 58 | |
| 59 | # GTK2 |
| 60 | |
| 61 | Chromium's GTK2 theme will soon be deprecated, and this section will be removed. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 62 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame] | 63 | ## Describing the previous heuristics |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 64 | |
thomasanderson | acc4214 | 2017-01-10 21:11:45 | [diff] [blame] | 65 | The heuristics often don't pick good colors due to a lack of information in the |
| 66 | GTK themes. The frame heuristics were simple. Query the `bg[SELECTED]` and |
| 67 | `bg[INSENSITIVE]` colors on the `MetaFrames` class and darken them |
| 68 | slightly. This usually worked OK until the rise of themes that try to make a |
| 69 | unified titlebar/menubar look. At roughly that time, it seems that people |
| 70 | stopped specifying color information for the `MetaFrames` class and this has |
| 71 | lead to the very orange chrome frame on Maverick. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 72 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame] | 73 | `MetaFrames` is (was?) a class that was used to communicate frame color data to |
| 74 | the window manager around the Hardy days. (It's still defined in most of |
| 75 | [XFCE's themes](http://packages.ubuntu.com/maverick/gtk2-engines-xfce)). In |
| 76 | chrome's implementation, `MetaFrames` derives from `GtkWindow`. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 77 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame] | 78 | If you are happy with the defaults that chrome has picked, no action is |
| 79 | necessary on the part of the theme author. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 80 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame] | 81 | ## Introducing `ChromeGtkFrame` |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 82 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame] | 83 | For cases where you want control of the colors chrome uses, Chrome gives you a |
| 84 | number of style properties for injecting colors and other information about how |
| 85 | to draw the frame. For example, here's the proposed modifications to Ubuntu's |
| 86 | Ambiance: |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 87 | |
| 88 | ``` |
| 89 | style "chrome-gtk-frame" |
| 90 | { |
| 91 | ChromeGtkFrame::frame-color = @fg_color |
| 92 | ChromeGtkFrame::inactive-frame-color = lighter(@fg_color) |
| 93 | |
| 94 | ChromeGtkFrame::frame-gradient-size = 16 |
| 95 | ChromeGtkFrame::frame-gradient-color = "#5c5b56" |
| 96 | |
| 97 | ChromeGtkFrame::scrollbar-trough-color = @bg_color |
| 98 | ChromeGtkFrame::scrollbar-slider-prelight-color = "#F8F6F2" |
| 99 | ChromeGtkFrame::scrollbar-slider-normal-color = "#E7E0D3" |
| 100 | } |
| 101 | |
| 102 | class "ChromeGtkFrame" style "chrome-gtk-frame" |
| 103 | ``` |
| 104 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame] | 105 | ### Frame color properties |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 106 | |
| 107 | These are the frame's main solid color. |
| 108 | |
| 109 | | **Property** | **Type** | **Description** | **If unspecified** | |
| 110 | |:-------------|:---------|:----------------|:-------------------| |
| 111 | | `frame-color` | `GdkColor` | The main color of active chrome windows. | Darkens `MetaFrame::bg[SELECTED]` | |
| 112 | | `inactive-frame-color` | `GdkColor` | The main color of inactive chrome windows. | Darkens `MetaFrame::bg[INSENSITIVE]` | |
| 113 | | `incognito-frame-color` | `GdkColor` | The main color of active incognito windows. | Tints `frame-color` by the default incognito tint | |
| 114 | | `incognito-inactive-frame-color` | `GdkColor` | The main color of inactive incognito windows. | Tints `inactive-frame-color` by the default incognito tint | |
| 115 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame] | 116 | ### Frame gradient properties |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 117 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame] | 118 | Chrome's frame (along with many normal window manager themes) have a slight |
| 119 | gradient at the top, before filling the rest of the frame background image with |
| 120 | a solid color. For example, the top `frame-gradient-size` pixels would be a |
| 121 | gradient starting from `frame-gradient-color` at the top to `frame-color` at the |
| 122 | bottom, with the rest of the frame being filled with `frame-color`. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 123 | |
| 124 | | **Property** | **Type** | **Description** | **If unspecified** | |
| 125 | |:-------------|:---------|:----------------|:-------------------| |
| 126 | | `frame-gradient-size` | Integers 0 through 128 | How large the gradient should be. Set to zero to disable drawing a gradient | Defaults to 16 pixels tall | |
| 127 | | `frame-gradient-color` | `GdkColor` | Top color of the gradient | Lightens `frame-color` | |
| 128 | | `inactive-frame-gradient-color` | `GdkColor` | Top color of the inactive gradient | Lightents `inactive-frame-color` | |
| 129 | | `incognito-frame-gradient-color` | `GdkColor` | Top color of the incognito gradient | Lightens `incognito-frame-color` | |
| 130 | | `incognito-inactive-frame-gradient-color` | `GdkColor` | Top color of the incognito inactive gradient. | Lightens `incognito-inactive-frame-color` | |
| 131 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame] | 132 | ### Scrollbar control |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 133 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame] | 134 | Because widget rendering is done in a separate, sandboxed process that doesn't |
| 135 | have access to the X server or the filesystem, there's no current way to do |
| 136 | GTK+ widget rendering. We instead pass WebKit a few colors and let it draw a |
| 137 | default scrollbar. We have a very |
| 138 | [complex fallback](http://git.chromium.org/gitweb/?p=chromium.git;a=blob;f=chrome/browser/gtk/gtk_theme_provider.cc;h=a57ab6b182b915192c84177f1a574914c44e2e71;hb=3f873177e192f5c6b66ae591b8b7205d8a707918#l424) |
| 139 | where we render the widget and then average colors if this information isn't |
| 140 | provided. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 141 | |
| 142 | | **Property** | **Type** | **Description** | |
| 143 | |:-------------|:---------|:----------------| |
| 144 | | `scrollbar-slider-prelight-color` | `GdkColor` | Color of the slider on mouse hover. | |
| 145 | | `scrollbar-slider-normal-color` | `GdkColor` | Color of the slider otherwise | |
| 146 | | `scrollbar-trough-color` | `GdkColor` | Color of the scrollbar trough | |
| 147 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame] | 148 | ## Anticipated Q&A |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 149 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame] | 150 | ### Will you patch themes upstream? |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 151 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame] | 152 | I am at the very least hoping we can get Radiance and Ambiance patches since we |
| 153 | make very poor frame decisions on those themes, and hopefully a few others. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 154 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame] | 155 | ### How about control over the min/max/close buttons? |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 156 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame] | 157 | I actually tried this locally. There's a sort of uncanny valley effect going on; |
| 158 | as the frame looks more native, it's more obvious that it isn't behaving like a |
| 159 | native frame. (Also my implementation added a startup time hit.) |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 160 | |
nodir | a6074d4c | 2015-09-01 04:26:45 | [diff] [blame] | 161 | ### Why use style properties instead of (i.e.) bg[STATE]? |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 162 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame] | 163 | There's no way to distinguish between colors set on different classes. Using |
| 164 | style properties allows us to be backwards compatible and maintain the |
| 165 | heuristics since not everyone is going to modify their themes for chromium (and |
| 166 | the heuristics do a reasonable job). |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 167 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame] | 168 | ### Why now? |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 169 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame] | 170 | * I (erg@) was putting off major changes to the window frame stuff in |
| 171 | anticipation of finally being able to use GTK+'s theme rendering for the |
| 172 | window border with client side decorations, but client side decorations |
| 173 | either isn't happening or isn't happening anytime soon, so there's no |
| 174 | justification for pushing this task off into the future. |
| 175 | * Chrome looks pretty bad under Ambiance on Maverick. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 176 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame] | 177 | ### Details about `MetaFrames` and `ChromeGtkFrame` relationship and history? |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 178 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame] | 179 | `MetaFrames` is a class that was used in metacity to communicate color |
| 180 | information to the window manager. During the Hardy Heron days, we slurped up |
| 181 | the data and used it as a key part of our heuristics. At least on my Lucid Lynx |
| 182 | machine, none of the GNOME GTK+ themes have `MetaFrames` styling. (As mentioned |
| 183 | above, several of the XFCE themes do, though.) |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 184 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame] | 185 | Internally to chrome, our `ChromeGtkFrame` class inherits from `MetaFrames` |
| 186 | (again, which inherits from `GtkWindow`) so any old themes that style the |
| 187 | `MetaFrames` class are backwards compatible. |