| 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 |