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/libgtk2ui/gtk2_ui.h" | 5 #include "chrome/browser/ui/libgtk2ui/gtk2_ui.h" |
6 | 6 |
7 #include <math.h> | 7 #include <math.h> |
8 #include <pango/pango.h> | 8 #include <pango/pango.h> |
9 #include <set> | 9 #include <set> |
10 #include <utility> | 10 #include <utility> |
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
74 // to 0, which breaks -Wsentinel. Get back the normal definition of NULL. | 74 // to 0, which breaks -Wsentinel. Get back the normal definition of NULL. |
75 // TODO(thakis): Remove this once we update sysroots. | 75 // TODO(thakis): Remove this once we update sysroots. |
76 #define __need_NULL | 76 #define __need_NULL |
77 #include <stddef.h> | 77 #include <stddef.h> |
78 | 78 |
79 namespace libgtk2ui { | 79 namespace libgtk2ui { |
80 | 80 |
81 namespace { | 81 namespace { |
82 | 82 |
83 class GtkThemeIconSource : public gfx::ImageSkiaSource { | 83 class GtkThemeIconSource : public gfx::ImageSkiaSource { |
84 public: | 84 public: |
85 GtkThemeIconSource(int id, const char* icon, bool enabled) | 85 GtkThemeIconSource(int id, const char* icon, bool enabled) |
86 : id_(id), | 86 : id_(id), icon_(icon), enabled_(enabled) {} |
87 icon_(icon), | 87 |
88 enabled_(enabled) { | 88 ~GtkThemeIconSource() override {} |
| 89 |
| 90 gfx::ImageSkiaRep GetImageForScale(float scale) override { |
| 91 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); |
| 92 SkBitmap default_icon = rb.GetImageNamed(id_).AsBitmap(); |
| 93 |
| 94 int scalew = default_icon.width() * scale; |
| 95 int scaleh = default_icon.height() * scale; |
| 96 |
| 97 // Ask GTK to render the icon to a buffer, which we will steal from. |
| 98 GtkIconTheme* icon_theme = gtk_icon_theme_get_default(); |
| 99 GdkPixbuf* gdk_icon = gtk_icon_theme_load_icon( |
| 100 icon_theme, icon_, 20 * scale, (GtkIconLookupFlags)0, NULL); |
| 101 |
| 102 // This can theoretically happen if an icon theme doesn't provide a |
| 103 // specific image. This should realistically never happen, but I bet there |
| 104 // are some theme authors who don't reliably provide all icons. |
| 105 if (!gdk_icon) |
| 106 return gfx::ImageSkiaRep(); |
| 107 |
| 108 #if GTK_MAJOR_VERSION == 2 |
| 109 GtkIconSource* icon_source = gtk_icon_source_new(); |
| 110 gtk_icon_source_set_pixbuf(icon_source, gdk_icon); |
| 111 |
| 112 GdkPixbuf* temp = gtk_style_render_icon( |
| 113 gtk_rc_get_style(NativeThemeGtk2::instance()->GetButton()), icon_source, |
| 114 GTK_TEXT_DIR_NONE, enabled_ ? GTK_STATE_NORMAL : GTK_STATE_INSENSITIVE, |
| 115 (GtkIconSize)-1, NativeThemeGtk2::instance()->GetButton(), NULL); |
| 116 |
| 117 gtk_icon_source_free(icon_source); |
| 118 g_object_unref(gdk_icon); |
| 119 |
| 120 gdk_icon = temp; |
| 121 #endif |
| 122 |
| 123 SkBitmap retval; |
| 124 retval.allocN32Pixels(scalew, scaleh); |
| 125 retval.eraseColor(0); |
| 126 |
| 127 const SkBitmap icon = GdkPixbufToImageSkia(gdk_icon); |
| 128 g_object_unref(gdk_icon); |
| 129 |
| 130 SkCanvas canvas(retval); |
| 131 SkPaint paint; |
| 132 |
| 133 #if GTK_MAJOR_VERSION > 2 |
| 134 if (!enabled_) |
| 135 paint.setAlpha(128); |
| 136 #endif |
| 137 |
| 138 canvas.drawBitmap(icon, (scalew / 2) - (icon.width() / 2), |
| 139 (scaleh / 2) - (icon.height() / 2), &paint); |
| 140 |
| 141 return gfx::ImageSkiaRep(retval, scale); |
| 142 } |
| 143 |
| 144 private: |
| 145 int id_; |
| 146 const char* icon_; |
| 147 bool enabled_; |
| 148 |
| 149 DISALLOW_COPY_AND_ASSIGN(GtkThemeIconSource); |
| 150 }; |
| 151 |
| 152 class GtkButtonImageSource : public gfx::ImageSkiaSource { |
| 153 public: |
| 154 GtkButtonImageSource(const char* idr_string, gfx::Size size) |
| 155 : width_(size.width()), height_(size.height()) { |
| 156 is_blue_ = !!strstr(idr_string, "IDR_BLUE"); |
| 157 focus_ = !!strstr(idr_string, "_FOCUSED_"); |
| 158 |
| 159 if (strstr(idr_string, "_DISABLED")) { |
| 160 state_ = ui::NativeTheme::kDisabled; |
| 161 } else if (strstr(idr_string, "_HOVER")) { |
| 162 state_ = ui::NativeTheme::kHovered; |
| 163 } else if (strstr(idr_string, "_PRESSED")) { |
| 164 state_ = ui::NativeTheme::kPressed; |
| 165 } else { |
| 166 state_ = ui::NativeTheme::kNormal; |
| 167 } |
| 168 } |
| 169 |
| 170 ~GtkButtonImageSource() override {} |
| 171 |
| 172 gfx::ImageSkiaRep GetImageForScale(float scale) override { |
| 173 int width = width_ * scale; |
| 174 int height = height_ * scale; |
| 175 |
| 176 SkBitmap border; |
| 177 border.allocN32Pixels(width, height); |
| 178 border.eraseColor(0); |
| 179 |
| 180 // Create a temporary GTK button to snapshot |
| 181 GtkWidget* window = gtk_offscreen_window_new(); |
| 182 GtkWidget* button = gtk_toggle_button_new(); |
| 183 |
| 184 if (state_ == ui::NativeTheme::kPressed) |
| 185 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), true); |
| 186 else if (state_ == ui::NativeTheme::kDisabled) |
| 187 gtk_widget_set_sensitive(button, false); |
| 188 |
| 189 gtk_widget_set_size_request(button, width, height); |
| 190 gtk_container_add(GTK_CONTAINER(window), button); |
| 191 |
| 192 if (is_blue_) |
| 193 TurnButtonBlue(button); |
| 194 |
| 195 gtk_widget_show_all(window); |
| 196 |
| 197 cairo_surface_t* surface = cairo_image_surface_create_for_data( |
| 198 static_cast<unsigned char*>(border.getAddr(0, 0)), |
| 199 CAIRO_FORMAT_ARGB32, width, height, width * 4); |
| 200 cairo_t* cr = cairo_create(surface); |
| 201 |
| 202 #if GTK_MAJOR_VERSION == 2 |
| 203 if (focus_) |
| 204 GTK_WIDGET_SET_FLAGS(button, GTK_HAS_FOCUS); |
| 205 |
| 206 int w, h; |
| 207 GdkPixmap* pixmap; |
| 208 |
| 209 { |
| 210 // http://crbug.com/346740 |
| 211 ANNOTATE_SCOPED_MEMORY_LEAK; |
| 212 pixmap = gtk_widget_get_snapshot(button, NULL); |
89 } | 213 } |
90 | 214 |
91 ~GtkThemeIconSource() override {} | 215 gdk_drawable_get_size(GDK_DRAWABLE(pixmap), &w, &h); |
| 216 GdkColormap* colormap = gdk_drawable_get_colormap(pixmap); |
| 217 GdkPixbuf* pixbuf = gdk_pixbuf_get_from_drawable( |
| 218 NULL, GDK_DRAWABLE(pixmap), colormap, 0, 0, 0, 0, w, h); |
92 | 219 |
93 gfx::ImageSkiaRep GetImageForScale(float scale) override { | 220 gdk_cairo_set_source_pixbuf(cr, pixbuf, 0, 0); |
94 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); | 221 cairo_paint(cr); |
95 SkBitmap default_icon = rb.GetImageNamed(id_).AsBitmap(); | |
96 | 222 |
97 int scalew = default_icon.width() * scale; | 223 g_object_unref(pixbuf); |
98 int scaleh = default_icon.height() * scale; | 224 g_object_unref(pixmap); |
| 225 #else |
| 226 gtk_widget_draw(button, cr); |
99 | 227 |
100 // Ask GTK to render the icon to a buffer, which we will steal from. | 228 // There's probably a better way to do this |
101 GtkIconTheme* icon_theme = gtk_icon_theme_get_default(); | 229 if (focus_) |
102 GdkPixbuf* gdk_icon = gtk_icon_theme_load_icon( | 230 gtk_render_focus(gtk_widget_get_style_context(button), cr, 0, 0, |
103 icon_theme, | 231 width, height); |
104 icon_, | |
105 20 * scale, | |
106 (GtkIconLookupFlags)0, | |
107 NULL); | |
108 | |
109 // This can theoretically happen if an icon theme doesn't provide a | |
110 // specific image. This should realistically never happen, but I bet there | |
111 // are some theme authors who don't reliably provide all icons. | |
112 if (!gdk_icon) | |
113 return gfx::ImageSkiaRep(); | |
114 | |
115 #if GTK_MAJOR_VERSION == 2 | |
116 GtkIconSource* icon_source = gtk_icon_source_new(); | |
117 gtk_icon_source_set_pixbuf(icon_source, gdk_icon); | |
118 | |
119 GdkPixbuf* temp = gtk_style_render_icon( | |
120 gtk_rc_get_style(NativeThemeGtk2::instance()->GetButton()), | |
121 icon_source, | |
122 GTK_TEXT_DIR_NONE, | |
123 enabled_ ? GTK_STATE_NORMAL : GTK_STATE_INSENSITIVE, | |
124 (GtkIconSize)-1, | |
125 NativeThemeGtk2::instance()->GetButton(), | |
126 NULL); | |
127 | |
128 gtk_icon_source_free(icon_source); | |
129 g_object_unref(gdk_icon); | |
130 | |
131 gdk_icon = temp; | |
132 #endif | 232 #endif |
133 | 233 |
134 SkBitmap retval; | 234 cairo_destroy(cr); |
135 retval.allocN32Pixels(scalew, scaleh); | 235 cairo_surface_destroy(surface); |
136 retval.eraseColor(0); | |
137 | 236 |
138 const SkBitmap icon = GdkPixbufToImageSkia(gdk_icon); | 237 gtk_widget_destroy(window); |
139 g_object_unref(gdk_icon); | |
140 | 238 |
141 SkCanvas canvas(retval); | 239 return gfx::ImageSkiaRep(border, scale); |
142 SkPaint paint; | 240 } |
143 | 241 |
144 #if GTK_MAJOR_VERSION > 2 | 242 private: |
145 if (!enabled_) | 243 bool is_blue_; |
146 paint.setAlpha(128); | 244 bool focus_; |
147 #endif | 245 ui::NativeTheme::State state_; |
| 246 int width_; |
| 247 int height_; |
148 | 248 |
149 canvas.drawBitmap(icon, | 249 DISALLOW_COPY_AND_ASSIGN(GtkButtonImageSource); |
150 (scalew / 2) - (icon.width() / 2), | |
151 (scaleh / 2) - (icon.height() / 2), | |
152 &paint); | |
153 | |
154 return gfx::ImageSkiaRep(retval, scale); | |
155 } | |
156 | |
157 private: | |
158 int id_; | |
159 const char* icon_; | |
160 bool enabled_; | |
161 | |
162 DISALLOW_COPY_AND_ASSIGN(GtkThemeIconSource); | |
163 }; | 250 }; |
164 | 251 |
| 252 class GtkButtonPainter : public views::Painter { |
| 253 public: |
| 254 explicit GtkButtonPainter(std::string idr) : idr_(idr) {} |
| 255 ~GtkButtonPainter() override {} |
165 | 256 |
166 class GtkButtonImageSource : public gfx::ImageSkiaSource { | 257 gfx::Size GetMinimumSize() const override { return gfx::Size(); } |
167 public: | 258 void Paint(gfx::Canvas* canvas, const gfx::Size& size) override { |
168 GtkButtonImageSource(const char* idr_string, gfx::Size size) | 259 gfx::ImageSkiaSource* source = new GtkButtonImageSource(idr_.c_str(), size); |
169 : width_(size.width()), | 260 gfx::ImageSkia image(source, 1); |
170 height_(size.height()) { | 261 canvas->DrawImageInt(image, 0, 0); |
171 is_blue_ = !!strstr(idr_string, "IDR_BLUE"); | 262 } |
172 focus_ = !!strstr(idr_string, "_FOCUSED_"); | |
173 | 263 |
174 if (strstr(idr_string, "_DISABLED")) { | 264 private: |
175 state_ = ui::NativeTheme::kDisabled; | 265 std::string idr_; |
176 } else if (strstr(idr_string, "_HOVER")) { | |
177 state_ = ui::NativeTheme::kHovered; | |
178 } else if (strstr(idr_string, "_PRESSED")) { | |
179 state_ = ui::NativeTheme::kPressed; | |
180 } else { | |
181 state_ = ui::NativeTheme::kNormal; | |
182 } | |
183 } | |
184 | 266 |
185 ~GtkButtonImageSource() override {} | 267 DISALLOW_COPY_AND_ASSIGN(GtkButtonPainter); |
186 | |
187 gfx::ImageSkiaRep GetImageForScale(float scale) override { | |
188 int width = width_ * scale; | |
189 int height = height_ * scale; | |
190 | |
191 SkBitmap border; | |
192 border.allocN32Pixels(width, height); | |
193 border.eraseColor(0); | |
194 | |
195 // Create a temporary GTK button to snapshot | |
196 GtkWidget* window = gtk_offscreen_window_new(); | |
197 GtkWidget* button = gtk_toggle_button_new(); | |
198 | |
199 if (state_ == ui::NativeTheme::kPressed) | |
200 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), true); | |
201 else if (state_ == ui::NativeTheme::kDisabled) | |
202 gtk_widget_set_sensitive(button, false); | |
203 | |
204 gtk_widget_set_size_request(button, width, height); | |
205 gtk_container_add(GTK_CONTAINER(window), button); | |
206 | |
207 if (is_blue_) | |
208 TurnButtonBlue(button); | |
209 | |
210 gtk_widget_show_all(window); | |
211 | |
212 | |
213 cairo_surface_t* surface = cairo_image_surface_create_for_data( | |
214 static_cast<unsigned char*>(border.getAddr(0, 0)), | |
215 CAIRO_FORMAT_ARGB32, | |
216 width, height, | |
217 width * 4); | |
218 cairo_t* cr = cairo_create(surface); | |
219 | |
220 #if GTK_MAJOR_VERSION == 2 | |
221 if (focus_) | |
222 GTK_WIDGET_SET_FLAGS(button, GTK_HAS_FOCUS); | |
223 | |
224 int w, h; | |
225 GdkPixmap* pixmap; | |
226 | |
227 { | |
228 // http://crbug.com/346740 | |
229 ANNOTATE_SCOPED_MEMORY_LEAK; | |
230 pixmap = gtk_widget_get_snapshot(button, NULL); | |
231 } | |
232 | |
233 gdk_drawable_get_size(GDK_DRAWABLE(pixmap), &w, &h); | |
234 GdkColormap* colormap = gdk_drawable_get_colormap(pixmap); | |
235 GdkPixbuf* pixbuf = gdk_pixbuf_get_from_drawable(NULL, | |
236 GDK_DRAWABLE(pixmap), | |
237 colormap, | |
238 0, 0, 0, 0, w, h); | |
239 | |
240 gdk_cairo_set_source_pixbuf(cr, pixbuf, 0, 0); | |
241 cairo_paint(cr); | |
242 | |
243 g_object_unref(pixbuf); | |
244 g_object_unref(pixmap); | |
245 #else | |
246 gtk_widget_draw(button, cr); | |
247 | |
248 // There's probably a better way to do this | |
249 if (focus_) | |
250 gtk_render_focus(gtk_widget_get_style_context(button), | |
251 cr, 0, 0, width, height); | |
252 #endif | |
253 | |
254 cairo_destroy(cr); | |
255 cairo_surface_destroy(surface); | |
256 | |
257 gtk_widget_destroy(window); | |
258 | |
259 return gfx::ImageSkiaRep(border, scale); | |
260 } | |
261 | |
262 private: | |
263 bool is_blue_; | |
264 bool focus_; | |
265 ui::NativeTheme::State state_; | |
266 int width_; | |
267 int height_; | |
268 | |
269 DISALLOW_COPY_AND_ASSIGN(GtkButtonImageSource); | |
270 }; | 268 }; |
271 | 269 |
272 | |
273 class GtkButtonPainter : public views::Painter { | |
274 public: | |
275 explicit GtkButtonPainter(std::string idr) : idr_(idr) {} | |
276 ~GtkButtonPainter() override {} | |
277 | |
278 gfx::Size GetMinimumSize() const override { | |
279 return gfx::Size(); | |
280 } | |
281 void Paint(gfx::Canvas* canvas, const gfx::Size& size) override { | |
282 gfx::ImageSkiaSource* source = | |
283 new GtkButtonImageSource(idr_.c_str(), size); | |
284 gfx::ImageSkia image(source, 1); | |
285 canvas->DrawImageInt(image, 0, 0); | |
286 } | |
287 | |
288 private: | |
289 std::string idr_; | |
290 | |
291 DISALLOW_COPY_AND_ASSIGN(GtkButtonPainter); | |
292 }; | |
293 | |
294 | |
295 | |
296 struct GObjectDeleter { | 270 struct GObjectDeleter { |
297 void operator()(void* ptr) { | 271 void operator()(void* ptr) { |
298 g_object_unref(ptr); | 272 g_object_unref(ptr); |
299 } | 273 } |
300 }; | 274 }; |
301 struct GtkIconInfoDeleter { | 275 struct GtkIconInfoDeleter { |
302 void operator()(GtkIconInfo* ptr) { | 276 void operator()(GtkIconInfo* ptr) { |
303 G_GNUC_BEGIN_IGNORE_DEPRECATIONS | 277 G_GNUC_BEGIN_IGNORE_DEPRECATIONS |
304 gtk_icon_info_free(ptr); | 278 gtk_icon_info_free(ptr); |
305 G_GNUC_END_IGNORE_DEPRECATIONS | 279 G_GNUC_END_IGNORE_DEPRECATIONS |
(...skipping 470 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
776 | 750 |
777 if (border->PaintsButtonState(paintstate[i].focus, paintstate[i].state)) { | 751 if (border->PaintsButtonState(paintstate[i].focus, paintstate[i].state)) { |
778 std::string idr = is_blue ? paintstate[i].idr_blue : paintstate[i].idr; | 752 std::string idr = is_blue ? paintstate[i].idr_blue : paintstate[i].idr; |
779 painter = new GtkButtonPainter(idr); | 753 painter = new GtkButtonPainter(idr); |
780 } | 754 } |
781 | 755 |
782 gtk_border->SetPainter(paintstate[i].focus, paintstate[i].state, painter); | 756 gtk_border->SetPainter(paintstate[i].focus, paintstate[i].state, painter); |
783 } | 757 } |
784 | 758 |
785 return std::move(gtk_border); | 759 return std::move(gtk_border); |
786 ; | |
787 } | 760 } |
788 | 761 |
789 void Gtk2UI::AddWindowButtonOrderObserver( | 762 void Gtk2UI::AddWindowButtonOrderObserver( |
790 views::WindowButtonOrderObserver* observer) { | 763 views::WindowButtonOrderObserver* observer) { |
791 if (!leading_buttons_.empty() || !trailing_buttons_.empty()) { | 764 if (!leading_buttons_.empty() || !trailing_buttons_.empty()) { |
792 observer->OnWindowButtonOrderingChange(leading_buttons_, | 765 observer->OnWindowButtonOrderingChange(leading_buttons_, |
793 trailing_buttons_); | 766 trailing_buttons_); |
794 } | 767 } |
795 | 768 |
796 observer_list_.AddObserver(observer); | 769 observer_list_.AddObserver(observer); |
(...skipping 586 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1383 const float rounded = roundf(scale * 10) / 10; | 1356 const float rounded = roundf(scale * 10) / 10; |
1384 // See crbug.com/484400 | 1357 // See crbug.com/484400 |
1385 return rounded < 1.3 ? 1.0 : rounded; | 1358 return rounded < 1.3 ? 1.0 : rounded; |
1386 } | 1359 } |
1387 | 1360 |
1388 } // namespace libgtk2ui | 1361 } // namespace libgtk2ui |
1389 | 1362 |
1390 views::LinuxUI* BuildGtk2UI() { | 1363 views::LinuxUI* BuildGtk2UI() { |
1391 return new libgtk2ui::Gtk2UI; | 1364 return new libgtk2ui::Gtk2UI; |
1392 } | 1365 } |
OLD | NEW |