OLD | NEW |
| (Empty) |
1 // Copyright (c) 2010 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/gtk/tabs/dragged_tab_gtk.h" | |
6 | |
7 #include <gdk/gdk.h> | |
8 | |
9 #include <algorithm> | |
10 | |
11 #include "app/x11_util.h" | |
12 #include "base/i18n/rtl.h" | |
13 #include "chrome/browser/gtk/gtk_util.h" | |
14 #include "chrome/browser/gtk/tabs/tab_renderer_gtk.h" | |
15 #include "chrome/browser/profiles/profile.h" | |
16 #include "chrome/browser/renderer_host/backing_store_x.h" | |
17 #include "chrome/browser/renderer_host/render_view_host.h" | |
18 #include "chrome/browser/tab_contents/tab_contents.h" | |
19 #include "chrome/browser/tabs/tab_strip_model.h" | |
20 #include "chrome/browser/themes/browser_theme_provider.h" | |
21 #include "gfx/gtk_util.h" | |
22 #include "third_party/skia/include/core/SkShader.h" | |
23 | |
24 namespace { | |
25 | |
26 // The size of the dragged window frame. | |
27 const int kDragFrameBorderSize = 1; | |
28 const int kTwiceDragFrameBorderSize = 2 * kDragFrameBorderSize; | |
29 | |
30 // Used to scale the dragged window sizes. | |
31 const float kScalingFactor = 0.5; | |
32 | |
33 const int kAnimateToBoundsDurationMs = 150; | |
34 | |
35 const gdouble kTransparentAlpha = (200.0f / 255.0f); | |
36 const gdouble kOpaqueAlpha = 1.0f; | |
37 const double kDraggedTabBorderColor[] = { 103.0 / 0xff, | |
38 129.0 / 0xff, | |
39 162.0 / 0xff }; | |
40 | |
41 } // namespace | |
42 | |
43 //////////////////////////////////////////////////////////////////////////////// | |
44 // DraggedTabGtk, public: | |
45 | |
46 DraggedTabGtk::DraggedTabGtk(TabContents* datasource, | |
47 const gfx::Point& mouse_tab_offset, | |
48 const gfx::Size& contents_size, | |
49 bool mini) | |
50 : data_source_(datasource), | |
51 renderer_(new TabRendererGtk(datasource->profile()->GetThemeProvider())), | |
52 attached_(false), | |
53 mouse_tab_offset_(mouse_tab_offset), | |
54 attached_tab_size_(TabRendererGtk::GetMinimumSelectedSize()), | |
55 contents_size_(contents_size), | |
56 close_animation_(this) { | |
57 renderer_->UpdateData(datasource, | |
58 datasource->is_app(), | |
59 false); // loading_only | |
60 renderer_->set_mini(mini); | |
61 | |
62 container_ = gtk_window_new(GTK_WINDOW_POPUP); | |
63 SetContainerColorMap(); | |
64 gtk_widget_set_app_paintable(container_, TRUE); | |
65 g_signal_connect(container_, "expose-event", | |
66 G_CALLBACK(OnExposeEvent), this); | |
67 gtk_widget_add_events(container_, GDK_STRUCTURE_MASK); | |
68 | |
69 // We contain the tab renderer in a GtkFixed in order to maintain the | |
70 // requested size. Otherwise, the widget will fill the entire window and | |
71 // cause a crash when rendering because the bounds don't match our images. | |
72 fixed_ = gtk_fixed_new(); | |
73 gtk_fixed_put(GTK_FIXED(fixed_), renderer_->widget(), 0, 0); | |
74 gtk_container_add(GTK_CONTAINER(container_), fixed_); | |
75 gtk_widget_show_all(container_); | |
76 } | |
77 | |
78 DraggedTabGtk::~DraggedTabGtk() { | |
79 gtk_widget_destroy(container_); | |
80 } | |
81 | |
82 void DraggedTabGtk::MoveTo(const gfx::Point& screen_point) { | |
83 int x = screen_point.x() + mouse_tab_offset_.x() - | |
84 ScaleValue(mouse_tab_offset_.x()); | |
85 int y = screen_point.y() + mouse_tab_offset_.y() - | |
86 ScaleValue(mouse_tab_offset_.y()); | |
87 | |
88 gtk_window_move(GTK_WINDOW(container_), x, y); | |
89 } | |
90 | |
91 void DraggedTabGtk::Attach(int selected_width) { | |
92 attached_ = true; | |
93 Resize(selected_width); | |
94 | |
95 if (gtk_util::IsScreenComposited()) | |
96 gdk_window_set_opacity(container_->window, kOpaqueAlpha); | |
97 } | |
98 | |
99 void DraggedTabGtk::Resize(int width) { | |
100 attached_tab_size_.set_width(width); | |
101 ResizeContainer(); | |
102 } | |
103 | |
104 void DraggedTabGtk::Detach() { | |
105 attached_ = false; | |
106 ResizeContainer(); | |
107 | |
108 if (gtk_util::IsScreenComposited()) | |
109 gdk_window_set_opacity(container_->window, kTransparentAlpha); | |
110 } | |
111 | |
112 void DraggedTabGtk::Update() { | |
113 gtk_widget_queue_draw(container_); | |
114 } | |
115 | |
116 void DraggedTabGtk::AnimateToBounds(const gfx::Rect& bounds, | |
117 AnimateToBoundsCallback* callback) { | |
118 animation_callback_.reset(callback); | |
119 | |
120 gint x, y, width, height; | |
121 gdk_window_get_origin(container_->window, &x, &y); | |
122 gdk_window_get_geometry(container_->window, NULL, NULL, | |
123 &width, &height, NULL); | |
124 | |
125 animation_start_bounds_ = gfx::Rect(x, y, width, height); | |
126 animation_end_bounds_ = bounds; | |
127 | |
128 close_animation_.SetSlideDuration(kAnimateToBoundsDurationMs); | |
129 close_animation_.SetTweenType(ui::Tween::EASE_OUT); | |
130 if (!close_animation_.IsShowing()) { | |
131 close_animation_.Reset(); | |
132 close_animation_.Show(); | |
133 } | |
134 } | |
135 | |
136 //////////////////////////////////////////////////////////////////////////////// | |
137 // DraggedTabGtk, ui::AnimationDelegate implementation: | |
138 | |
139 void DraggedTabGtk::AnimationProgressed(const ui::Animation* animation) { | |
140 int delta_x = (animation_end_bounds_.x() - animation_start_bounds_.x()); | |
141 int x = animation_start_bounds_.x() + | |
142 static_cast<int>(delta_x * animation->GetCurrentValue()); | |
143 int y = animation_end_bounds_.y(); | |
144 gdk_window_move(container_->window, x, y); | |
145 } | |
146 | |
147 void DraggedTabGtk::AnimationEnded(const ui::Animation* animation) { | |
148 animation_callback_->Run(); | |
149 } | |
150 | |
151 void DraggedTabGtk::AnimationCanceled(const ui::Animation* animation) { | |
152 AnimationEnded(animation); | |
153 } | |
154 | |
155 //////////////////////////////////////////////////////////////////////////////// | |
156 // DraggedTabGtk, private: | |
157 | |
158 void DraggedTabGtk::Layout() { | |
159 if (attached_) { | |
160 renderer_->SetBounds(gfx::Rect(GetPreferredSize())); | |
161 } else { | |
162 int left = 0; | |
163 if (base::i18n::IsRTL()) | |
164 left = GetPreferredSize().width() - attached_tab_size_.width(); | |
165 | |
166 // The renderer_'s width should be attached_tab_size_.width() in both LTR | |
167 // and RTL locales. Wrong width will cause the wrong positioning of the tab | |
168 // view in dragging. Please refer to http://crbug.com/6223 for details. | |
169 renderer_->SetBounds(gfx::Rect(left, 0, attached_tab_size_.width(), | |
170 attached_tab_size_.height())); | |
171 } | |
172 } | |
173 | |
174 gfx::Size DraggedTabGtk::GetPreferredSize() { | |
175 if (attached_) | |
176 return attached_tab_size_; | |
177 | |
178 int width = std::max(attached_tab_size_.width(), contents_size_.width()) + | |
179 kTwiceDragFrameBorderSize; | |
180 int height = attached_tab_size_.height() + kDragFrameBorderSize + | |
181 contents_size_.height(); | |
182 return gfx::Size(width, height); | |
183 } | |
184 | |
185 void DraggedTabGtk::ResizeContainer() { | |
186 gfx::Size size = GetPreferredSize(); | |
187 gtk_window_resize(GTK_WINDOW(container_), | |
188 ScaleValue(size.width()), ScaleValue(size.height())); | |
189 Layout(); | |
190 } | |
191 | |
192 int DraggedTabGtk::ScaleValue(int value) { | |
193 return attached_ ? value : static_cast<int>(value * kScalingFactor); | |
194 } | |
195 | |
196 gfx::Rect DraggedTabGtk::bounds() const { | |
197 gint x, y, width, height; | |
198 gtk_window_get_position(GTK_WINDOW(container_), &x, &y); | |
199 gtk_window_get_size(GTK_WINDOW(container_), &width, &height); | |
200 return gfx::Rect(x, y, width, height); | |
201 } | |
202 | |
203 void DraggedTabGtk::SetContainerColorMap() { | |
204 GdkScreen* screen = gtk_widget_get_screen(container_); | |
205 GdkColormap* colormap = gdk_screen_get_rgba_colormap(screen); | |
206 | |
207 // If rgba is not available, use rgb instead. | |
208 if (!colormap) | |
209 colormap = gdk_screen_get_rgb_colormap(screen); | |
210 | |
211 gtk_widget_set_colormap(container_, colormap); | |
212 } | |
213 | |
214 void DraggedTabGtk::SetContainerTransparency() { | |
215 cairo_t* cairo_context = gdk_cairo_create(container_->window); | |
216 if (!cairo_context) | |
217 return; | |
218 | |
219 // Make the background of the dragged tab window fully transparent. All of | |
220 // the content of the window (child widgets) will be completely opaque. | |
221 gfx::Size size = bounds().size(); | |
222 cairo_scale(cairo_context, static_cast<double>(size.width()), | |
223 static_cast<double>(size.height())); | |
224 cairo_set_source_rgba(cairo_context, 1.0f, 1.0f, 1.0f, 0.0f); | |
225 cairo_set_operator(cairo_context, CAIRO_OPERATOR_SOURCE); | |
226 cairo_paint(cairo_context); | |
227 cairo_destroy(cairo_context); | |
228 } | |
229 | |
230 void DraggedTabGtk::SetContainerShapeMask(cairo_surface_t* surface) { | |
231 // Create a 1bpp bitmap the size of |container_|. | |
232 gfx::Size size = bounds().size(); | |
233 GdkPixmap* pixmap = gdk_pixmap_new(NULL, size.width(), size.height(), 1); | |
234 cairo_t* cairo_context = gdk_cairo_create(GDK_DRAWABLE(pixmap)); | |
235 | |
236 // Set the transparency. | |
237 cairo_set_source_rgba(cairo_context, 1.0f, 1.0f, 1.0f, 0.0f); | |
238 | |
239 // Blit the rendered bitmap into a pixmap. Any pixel set in the pixmap will | |
240 // be opaque in the container window. | |
241 cairo_set_operator(cairo_context, CAIRO_OPERATOR_SOURCE); | |
242 if (!attached_) | |
243 cairo_scale(cairo_context, kScalingFactor, kScalingFactor); | |
244 cairo_set_source_surface(cairo_context, surface, 0, 0); | |
245 cairo_paint(cairo_context); | |
246 | |
247 if (!attached_) { | |
248 // Make the render area depiction opaque (leaving enough room for the | |
249 // border). | |
250 cairo_identity_matrix(cairo_context); | |
251 // On Lucid running VNC, the X server will reject RGBA (1,1,1,1) as an | |
252 // invalid value below in gdk_window_shape_combine_mask(). Using (0,0,0,1) | |
253 // instead. The value doesn't really matter, as long as the alpha is not 0. | |
254 cairo_set_source_rgba(cairo_context, 0.0f, 0.0f, 0.0f, 1.0f); | |
255 int tab_height = static_cast<int>(kScalingFactor * | |
256 renderer_->height() - | |
257 kDragFrameBorderSize); | |
258 cairo_rectangle(cairo_context, | |
259 0, tab_height, | |
260 size.width(), size.height() - tab_height); | |
261 cairo_fill(cairo_context); | |
262 } | |
263 | |
264 cairo_destroy(cairo_context); | |
265 | |
266 // Set the shape mask. | |
267 gdk_window_shape_combine_mask(container_->window, pixmap, 0, 0); | |
268 g_object_unref(pixmap); | |
269 } | |
270 | |
271 // static | |
272 gboolean DraggedTabGtk::OnExposeEvent(GtkWidget* widget, | |
273 GdkEventExpose* event, | |
274 DraggedTabGtk* dragged_tab) { | |
275 cairo_surface_t* surface = dragged_tab->renderer_->PaintToSurface(); | |
276 if (gtk_util::IsScreenComposited()) { | |
277 dragged_tab->SetContainerTransparency(); | |
278 } else { | |
279 dragged_tab->SetContainerShapeMask(surface); | |
280 } | |
281 | |
282 // Only used when not attached. | |
283 int tab_width = static_cast<int>(kScalingFactor * | |
284 dragged_tab->renderer_->width()); | |
285 int tab_height = static_cast<int>(kScalingFactor * | |
286 dragged_tab->renderer_->height()); | |
287 | |
288 // Draw the render area. | |
289 BackingStore* backing_store = | |
290 dragged_tab->data_source_->render_view_host()->GetBackingStore(false); | |
291 if (backing_store && !dragged_tab->attached_) { | |
292 // This leaves room for the border. | |
293 static_cast<BackingStoreX*>(backing_store)->PaintToRect( | |
294 gfx::Rect(kDragFrameBorderSize, tab_height, | |
295 widget->allocation.width - kTwiceDragFrameBorderSize, | |
296 widget->allocation.height - tab_height - | |
297 kDragFrameBorderSize), | |
298 GDK_DRAWABLE(widget->window)); | |
299 } | |
300 | |
301 cairo_t* cr = gdk_cairo_create(GDK_DRAWABLE(widget->window)); | |
302 // Draw the border. | |
303 if (!dragged_tab->attached_) { | |
304 cairo_set_line_width(cr, kDragFrameBorderSize); | |
305 cairo_set_source_rgb(cr, kDraggedTabBorderColor[0], | |
306 kDraggedTabBorderColor[1], | |
307 kDraggedTabBorderColor[2]); | |
308 // |offset| is the distance from the edge of the image to the middle of | |
309 // the border line. | |
310 double offset = kDragFrameBorderSize / 2.0 - 0.5; | |
311 double left_x = offset; | |
312 double top_y = tab_height - kDragFrameBorderSize + offset; | |
313 double right_x = widget->allocation.width - offset; | |
314 double bottom_y = widget->allocation.height - offset; | |
315 double middle_x = tab_width + offset; | |
316 | |
317 // We don't use cairo_rectangle() because we don't want to draw the border | |
318 // under the tab itself. | |
319 cairo_move_to(cr, left_x, top_y); | |
320 cairo_line_to(cr, left_x, bottom_y); | |
321 cairo_line_to(cr, right_x, bottom_y); | |
322 cairo_line_to(cr, right_x, top_y); | |
323 cairo_line_to(cr, middle_x, top_y); | |
324 cairo_stroke(cr); | |
325 } | |
326 | |
327 // Draw the tab. | |
328 if (!dragged_tab->attached_) | |
329 cairo_scale(cr, kScalingFactor, kScalingFactor); | |
330 cairo_set_source_surface(cr, surface, 0, 0); | |
331 cairo_paint(cr); | |
332 | |
333 cairo_destroy(cr); | |
334 | |
335 cairo_surface_destroy(surface); | |
336 | |
337 // We've already drawn the tab, so don't propagate the expose-event signal. | |
338 return TRUE; | |
339 } | |
OLD | NEW |