Index: chrome/browser/gtk/tabs/dragged_tab_controller_gtk.cc |
=================================================================== |
--- chrome/browser/gtk/tabs/dragged_tab_controller_gtk.cc (revision 16177) |
+++ chrome/browser/gtk/tabs/dragged_tab_controller_gtk.cc (working copy) |
@@ -4,9 +4,11 @@ |
#include "chrome/browser/gtk/tabs/dragged_tab_controller_gtk.h" |
+#include "chrome/browser/gtk/tabs/dragged_tab_gtk.h" |
#include "chrome/browser/gtk/tabs/tab_strip_gtk.h" |
#include "chrome/browser/tabs/tab_strip_model.h" |
#include "chrome/browser/tab_contents/tab_contents.h" |
+#include "chrome/common/gtk_util.h" |
#include "chrome/common/notification_service.h" |
namespace { |
@@ -25,22 +27,41 @@ |
source_tabstrip_(source_tabstrip), |
source_model_index_(source_tabstrip->GetIndexOfTab(source_tab)), |
attached_tabstrip_(source_tabstrip), |
+ in_destructor_(false), |
last_move_screen_x_(0) { |
SetDraggedContents( |
source_tabstrip_->model()->GetTabContentsAt(source_model_index_)); |
} |
DraggedTabControllerGtk::~DraggedTabControllerGtk() { |
+ in_destructor_ = true; |
+ // Need to delete the dragged tab here manually _before_ we reset the dragged |
+ // contents to NULL, otherwise if the view is animating to its destination |
+ // bounds, it won't be able to clean up properly since its cleanup routine |
+ // uses GetIndexForDraggedContents, which will be invalid. |
+ dragged_tab_.reset(); |
+ SetDraggedContents(NULL); |
} |
void DraggedTabControllerGtk::CaptureDragInfo(const gfx::Point& mouse_offset) { |
start_screen_point_ = GetCursorScreenPoint(); |
mouse_offset_ = mouse_offset; |
- snap_bounds_ = source_tab_->bounds(); |
} |
void DraggedTabControllerGtk::Drag() { |
- ContinueDragging(); |
+ if (!source_tab_) |
+ return; |
+ |
+ // Before we get to dragging anywhere, ensure that we consider ourselves |
+ // attached to the source tabstrip. |
+ if (source_tab_->IsVisible()) { |
+ Attach(source_tabstrip_, gfx::Point()); |
+ } |
+ |
+ if (!source_tab_->IsVisible()) { |
+ // TODO(jhawkins): Save focus. |
+ ContinueDragging(); |
+ } |
} |
bool DraggedTabControllerGtk::EndDrag(bool canceled) { |
@@ -66,6 +87,8 @@ |
void DraggedTabControllerGtk::NavigationStateChanged(const TabContents* source, |
unsigned changed_flags) { |
+ if (dragged_tab_.get()) |
+ dragged_tab_->Update(); |
} |
void DraggedTabControllerGtk::AddNewContents(TabContents* source, |
@@ -88,6 +111,10 @@ |
} |
void DraggedTabControllerGtk::LoadingStateChanged(TabContents* source) { |
+ // TODO(jhawkins): It would be nice to respond to this message by changing the |
+ // screen shot in the dragged tab. |
+ if (dragged_tab_.get()) |
+ dragged_tab_->Update(); |
} |
void DraggedTabControllerGtk::CloseContents(TabContents* source) { |
@@ -132,6 +159,10 @@ |
EndDragImpl(TAB_DESTROYED); |
} |
+void DraggedTabControllerGtk::InitWindowCreatePoint() { |
+ window_create_point_.SetPoint(mouse_offset_.x(), mouse_offset_.y()); |
+} |
+ |
void DraggedTabControllerGtk::SetDraggedContents(TabContents* new_contents) { |
if (dragged_contents_) { |
registrar_.Remove(this, |
@@ -157,6 +188,8 @@ |
} |
void DraggedTabControllerGtk::ContinueDragging() { |
+ EnsureDraggedTab(); |
+ |
// TODO(jhawkins): We don't handle the situation where the last tab is dragged |
// out of a window, so we'll just go with the way Windows handles dragging for |
// now. |
@@ -165,7 +198,7 @@ |
} |
void DraggedTabControllerGtk::MoveTab(const gfx::Point& screen_point) { |
- gfx::Point dragged_point = GetDraggedPoint(screen_point); |
+ gfx::Point dragged_tab_point = GetDraggedTabPoint(screen_point); |
if (attached_tabstrip_) { |
// Determine the horizontal move threshold. This is dependent on the width |
@@ -183,23 +216,18 @@ |
TabStripModel* attached_model = attached_tabstrip_->model(); |
int from_index = |
attached_model->GetIndexOfTabContents(dragged_contents_); |
- gfx::Rect bounds = source_tab_->bounds(); |
+ gfx::Rect bounds = GetDraggedTabTabStripBounds(dragged_tab_point); |
int to_index = GetInsertionIndexForDraggedBounds(bounds); |
to_index = NormalizeIndexToAttachedTabStrip(to_index); |
if (from_index != to_index) { |
last_move_screen_x_ = screen_point.x(); |
- snap_bounds_ = attached_tabstrip_->GetTabAt(to_index)->bounds(); |
attached_model->MoveTabContentsAt(from_index, to_index, true); |
} |
} |
} |
- // Move the tab. There are no changes to the model if we're detached. |
- gfx::Rect bounds = source_tab_->bounds(); |
- bounds.set_x(dragged_point.x()); |
- source_tab_->SetBounds(bounds); |
- gtk_fixed_move(GTK_FIXED(source_tabstrip_->tabstrip_.get()), |
- source_tab_->widget(), bounds.x(), bounds.y()); |
+ // Move the dragged tab. There are no changes to the model if we're detached. |
+ dragged_tab_->MoveTo(dragged_tab_point); |
} |
TabStripGtk* DraggedTabControllerGtk::GetTabStripForPoint( |
@@ -208,6 +236,91 @@ |
return source_tabstrip_; |
} |
+void DraggedTabControllerGtk::Attach(TabStripGtk* attached_tabstrip, |
+ const gfx::Point& screen_point) { |
+ attached_tabstrip_ = attached_tabstrip; |
+ InitWindowCreatePoint(); |
+ attached_tabstrip_->GenerateIdealBounds(); |
+ |
+ TabGtk* tab = GetTabMatchingDraggedContents(attached_tabstrip_); |
+ |
+ // Update the tab first, so we can ask it for its bounds and determine |
+ // where to insert the hidden tab. |
+ |
+ // If this is the first time Attach is called for this drag, we're attaching |
+ // to the source tabstrip, and we should assume the tab count already |
+ // includes this tab since we haven't been detached yet. If we don't do this, |
+ // the dragged representation will be a different size to others in the |
+ // tabstrip. |
+ int tab_count = attached_tabstrip_->GetTabCount(); |
+ if (!tab) |
+ ++tab_count; |
+ double unselected_width = 0, selected_width = 0; |
+ attached_tabstrip_->GetDesiredTabWidths(tab_count, &unselected_width, |
+ &selected_width); |
+ EnsureDraggedTab(); |
+ dragged_tab_->Attach(static_cast<int>(selected_width)); |
+ |
+ if (!tab) { |
+ // There is no tab in |attached_tabstrip| that corresponds to the dragged |
+ // TabContents. We must now create one. |
+ |
+ // Remove ourselves as the delegate now that the dragged TabContents is |
+ // being inserted back into a Browser. |
+ dragged_contents_->set_delegate(NULL); |
+ original_delegate_ = NULL; |
+ |
+ // Return the TabContents' to normalcy. |
+ dragged_contents_->set_capturing_contents(false); |
+ |
+ // We need to ask the tabstrip we're attached to ensure that the ideal |
+ // bounds for all its tabs are correctly generated, because the calculation |
+ // in GetInsertionIndexForDraggedBounds needs them to be to figure out the |
+ // appropriate insertion index. |
+ attached_tabstrip_->GenerateIdealBounds(); |
+ |
+ // Inserting counts as a move. We don't want the tabs to jitter when the |
+ // user moves the tab immediately after attaching it. |
+ last_move_screen_x_ = screen_point.x(); |
+ |
+ // Figure out where to insert the tab based on the bounds of the dragged |
+ // representation and the ideal bounds of the other tabs already in the |
+ // strip. ("ideal bounds" are stable even if the tabs' actual bounds are |
+ // changing due to animation). |
+ gfx::Rect bounds = GetDraggedTabTabStripBounds(screen_point); |
+ int index = GetInsertionIndexForDraggedBounds(bounds); |
+ index = std::max(std::min(index, attached_tabstrip_->model()->count()), 0); |
+ attached_tabstrip_->model()->InsertTabContentsAt(index, dragged_contents_, |
+ true, false); |
+ |
+ tab = GetTabMatchingDraggedContents(attached_tabstrip_); |
+ } |
+ DCHECK(tab); // We should now have a tab. |
+ tab->SetVisible(false); |
+ |
+ // TODO(jhawkins): Move the corresponding window to the front. |
+} |
+ |
+void DraggedTabControllerGtk::Detach() { |
+ // TODO(jhawkins): Detach the dragged tab. |
+} |
+ |
+gfx::Point DraggedTabControllerGtk::ConvertScreenPointToTabStripPoint( |
+ TabStripGtk* tabstrip, const gfx::Point& screen_point) { |
+ gint x, y; |
+ gdk_window_get_origin(tabstrip->tabstrip_.get()->window, &x, &y); |
+ return gfx::Point(screen_point.x() - x, screen_point.y() - y); |
+} |
+ |
+gfx::Rect DraggedTabControllerGtk::GetDraggedTabTabStripBounds( |
+ const gfx::Point& screen_point) { |
+ gfx::Point client_point = |
+ ConvertScreenPointToTabStripPoint(attached_tabstrip_, screen_point); |
+ gfx::Size tab_size = dragged_tab_->attached_tab_size(); |
+ return gfx::Rect(client_point.x(), client_point.y(), |
+ tab_size.width(), tab_size.height()); |
+} |
+ |
int DraggedTabControllerGtk::GetInsertionIndexForDraggedBounds( |
const gfx::Rect& dragged_bounds) const { |
int right_tab_x = 0; |
@@ -243,19 +356,41 @@ |
return TabStripModel::kNoTab; |
} |
-gfx::Point DraggedTabControllerGtk::GetDraggedPoint(const gfx::Point& point) { |
- int x = point.x() - mouse_offset_.x(); |
- int y = point.y() - mouse_offset_.y(); |
+gfx::Point DraggedTabControllerGtk::GetDraggedTabPoint( |
+ const gfx::Point& screen_point) { |
+ int x = screen_point.x() - mouse_offset_.x(); |
+ int y = screen_point.y() - mouse_offset_.y(); |
- // Snap the dragged tab to the tab strip. |
- if (x < 0) |
- x = 0; |
+ // If we're not attached, we just use x and y from above. |
+ if (attached_tabstrip_) { |
+ gfx::Rect tabstrip_bounds = |
+ gtk_util::GetWidgetScreenBounds(attached_tabstrip_->tabstrip_.get()); |
+ // Snap the dragged tab to the tabstrip if we are attached, detaching |
+ // only when the mouse position (screen_point) exceeds the screen bounds |
+ // of the tabstrip. |
+ if (x < tabstrip_bounds.x() && screen_point.x() >= tabstrip_bounds.x()) |
+ x = tabstrip_bounds.x(); |
- // Make sure the tab can't be dragged off the right side of the tab strip. |
- int max_x = attached_tabstrip_->bounds_.right() - source_tab_->width(); |
- if (x > max_x) |
- x = max_x; |
+ gfx::Size tab_size = dragged_tab_->attached_tab_size(); |
+ int vertical_drag_magnetism = tab_size.height() * 2; |
+ int vertical_detach_point = tabstrip_bounds.y() - vertical_drag_magnetism; |
+ if (y < tabstrip_bounds.y() && screen_point.y() >= vertical_detach_point) |
+ y = tabstrip_bounds.y(); |
+ // Make sure the tab can't be dragged off the right side of the tabstrip |
+ // unless the mouse pointer passes outside the bounds of the strip by |
+ // clamping the position of the dragged window to the tabstrip width less |
+ // the width of one tab until the mouse pointer (screen_point) exceeds the |
+ // screen bounds of the tabstrip. |
+ int max_x = tabstrip_bounds.right() - tab_size.width(); |
+ int max_y = tabstrip_bounds.bottom() - tab_size.height(); |
+ if (x > max_x && screen_point.x() <= tabstrip_bounds.right()) |
+ x = max_x; |
+ if (y > max_y && screen_point.y() <= |
+ (tabstrip_bounds.bottom() + vertical_drag_magnetism)) { |
+ y = max_y; |
+ } |
+ } |
return gfx::Point(x, y); |
} |
@@ -318,7 +453,7 @@ |
int index = attached_tabstrip_->model()->GetIndexOfTabContents( |
dragged_contents_); |
if (attached_tabstrip_ != source_tabstrip_) { |
- // The Tab was inserted into another TabStrip. We need to put it back |
+ // The tab was inserted into another tabstrip. We need to put it back |
// into the original one. |
attached_tabstrip_->model()->DetachTabContentsAt(index); |
// TODO(beng): (Cleanup) seems like we should use Attach() for this |
@@ -327,7 +462,7 @@ |
source_tabstrip_->model()->InsertTabContentsAt(source_model_index_, |
dragged_contents_, true, false); |
} else { |
- // The Tab was moved within the TabStrip where the drag was initiated. |
+ // The tab was moved within the tabstrip where the drag was initiated. |
// Move it back to the starting location. |
source_tabstrip_->model()->MoveTabContentsAt(index, source_model_index_, |
true); |
@@ -336,26 +471,37 @@ |
// TODO(beng): (Cleanup) seems like we should use Attach() for this |
// somehow. |
attached_tabstrip_ = source_tabstrip_; |
- // The Tab was detached from the TabStrip where the drag began, and has not |
- // been attached to any other TabStrip. We need to put it back into the |
- // source TabStrip. |
+ // The tab was detached from the tabstrip where the drag began, and has not |
+ // been attached to any other tabstrip. We need to put it back into the |
+ // source tabstrip. |
source_tabstrip_->model()->InsertTabContentsAt(source_model_index_, |
dragged_contents_, true, false); |
} |
+ |
+ source_tab_->SetVisible(true); |
} |
bool DraggedTabControllerGtk::CompleteDrag() { |
- // We don't need to do anything other than make the Tab visible again, |
+ // We don't need to do anything other than make the tab visible again, |
// since the dragged tab is going away. |
- gfx::Rect bounds = source_tab_->bounds(); |
- bounds.set_x(snap_bounds_.x()); |
- source_tab_->SetBounds(bounds); |
- gtk_fixed_move(GTK_FIXED(source_tabstrip_->tabstrip_.get()), |
- source_tab_->widget(), bounds.x(), bounds.y()); |
+ TabGtk* tab = GetTabMatchingDraggedContents(attached_tabstrip_); |
+ gfx::Rect rect = GetTabScreenBounds(tab); |
+ dragged_tab_->AnimateToBounds(GetTabScreenBounds(tab), |
+ NewCallback(this, &DraggedTabControllerGtk::OnAnimateToBoundsComplete)); |
return false; |
} |
+void DraggedTabControllerGtk::EnsureDraggedTab() { |
+ if (!dragged_tab_.get()) { |
+ gfx::Rect rect; |
+ dragged_contents_->GetContainerBounds(&rect); |
+ |
+ dragged_tab_.reset(new DraggedTabGtk(dragged_contents_, mouse_offset_, |
+ gfx::Size(rect.width(), rect.height()))); |
+ } |
+} |
+ |
gfx::Point DraggedTabControllerGtk::GetCursorScreenPoint() const { |
// Get default display and screen. |
GdkDisplay* display = gdk_display_get_default(); |
@@ -366,3 +512,38 @@ |
return gfx::Point(x, y); |
} |
+ |
+// static |
+gfx::Rect DraggedTabControllerGtk::GetTabScreenBounds(TabGtk* tab) { |
+ // A hidden widget moved with gtk_fixed_move in a GtkFixed container doesn't |
+ // update its allocation until after the widget is shown, so we have to use |
+ // the tab bounds we keep track of. |
+ int x, y; |
+ x = tab->bounds().x(); |
+ y = tab->bounds().y(); |
+ |
+ GtkWidget* widget = tab->widget(); |
+ GtkWidget* parent = gtk_widget_get_parent(widget); |
+ gfx::Point point = gtk_util::GetWidgetScreenPosition(parent); |
+ x += point.x(); |
+ y += point.y(); |
+ |
+ return gfx::Rect(x, y, widget->allocation.width, widget->allocation.height); |
+} |
+ |
+void DraggedTabControllerGtk::OnAnimateToBoundsComplete() { |
+ // Sometimes, for some reason, in automation we can be called back on a |
+ // detach even though we aren't attached to a tabstrip. Guard against that. |
+ if (attached_tabstrip_) { |
+ TabGtk* tab = GetTabMatchingDraggedContents(attached_tabstrip_); |
+ if (tab) { |
+ tab->SetVisible(true); |
+ // Paint the tab now, otherwise there may be slight flicker between the |
+ // time the dragged tab window is destroyed and we paint. |
+ tab->SchedulePaint(); |
+ } |
+ } |
+ |
+ if (!in_destructor_) |
+ source_tabstrip_->DestroyDragController(); |
+} |