OLD | NEW |
1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2009 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/gtk/tabs/dragged_tab_controller_gtk.h" | 5 #include "chrome/browser/gtk/tabs/dragged_tab_controller_gtk.h" |
6 | 6 |
| 7 #include "chrome/browser/gtk/tabs/dragged_tab_gtk.h" |
7 #include "chrome/browser/gtk/tabs/tab_strip_gtk.h" | 8 #include "chrome/browser/gtk/tabs/tab_strip_gtk.h" |
8 #include "chrome/browser/tabs/tab_strip_model.h" | 9 #include "chrome/browser/tabs/tab_strip_model.h" |
9 #include "chrome/browser/tab_contents/tab_contents.h" | 10 #include "chrome/browser/tab_contents/tab_contents.h" |
| 11 #include "chrome/common/gtk_util.h" |
10 #include "chrome/common/notification_service.h" | 12 #include "chrome/common/notification_service.h" |
11 | 13 |
12 namespace { | 14 namespace { |
13 | 15 |
14 // Used to determine how far a tab must obscure another tab in order to swap | 16 // Used to determine how far a tab must obscure another tab in order to swap |
15 // their indexes. | 17 // their indexes. |
16 const int kHorizontalMoveThreshold = 16; // pixels | 18 const int kHorizontalMoveThreshold = 16; // pixels |
17 | 19 |
18 } // namespace | 20 } // namespace |
19 | 21 |
20 DraggedTabControllerGtk::DraggedTabControllerGtk(TabGtk* source_tab, | 22 DraggedTabControllerGtk::DraggedTabControllerGtk(TabGtk* source_tab, |
21 TabStripGtk* source_tabstrip) | 23 TabStripGtk* source_tabstrip) |
22 : dragged_contents_(NULL), | 24 : dragged_contents_(NULL), |
23 original_delegate_(NULL), | 25 original_delegate_(NULL), |
24 source_tab_(source_tab), | 26 source_tab_(source_tab), |
25 source_tabstrip_(source_tabstrip), | 27 source_tabstrip_(source_tabstrip), |
26 source_model_index_(source_tabstrip->GetIndexOfTab(source_tab)), | 28 source_model_index_(source_tabstrip->GetIndexOfTab(source_tab)), |
27 attached_tabstrip_(source_tabstrip), | 29 attached_tabstrip_(source_tabstrip), |
| 30 in_destructor_(false), |
28 last_move_screen_x_(0) { | 31 last_move_screen_x_(0) { |
29 SetDraggedContents( | 32 SetDraggedContents( |
30 source_tabstrip_->model()->GetTabContentsAt(source_model_index_)); | 33 source_tabstrip_->model()->GetTabContentsAt(source_model_index_)); |
31 } | 34 } |
32 | 35 |
33 DraggedTabControllerGtk::~DraggedTabControllerGtk() { | 36 DraggedTabControllerGtk::~DraggedTabControllerGtk() { |
| 37 in_destructor_ = true; |
| 38 // Need to delete the dragged tab here manually _before_ we reset the dragged |
| 39 // contents to NULL, otherwise if the view is animating to its destination |
| 40 // bounds, it won't be able to clean up properly since its cleanup routine |
| 41 // uses GetIndexForDraggedContents, which will be invalid. |
| 42 dragged_tab_.reset(); |
| 43 SetDraggedContents(NULL); |
34 } | 44 } |
35 | 45 |
36 void DraggedTabControllerGtk::CaptureDragInfo(const gfx::Point& mouse_offset) { | 46 void DraggedTabControllerGtk::CaptureDragInfo(const gfx::Point& mouse_offset) { |
37 start_screen_point_ = GetCursorScreenPoint(); | 47 start_screen_point_ = GetCursorScreenPoint(); |
38 mouse_offset_ = mouse_offset; | 48 mouse_offset_ = mouse_offset; |
39 snap_bounds_ = source_tab_->bounds(); | |
40 } | 49 } |
41 | 50 |
42 void DraggedTabControllerGtk::Drag() { | 51 void DraggedTabControllerGtk::Drag() { |
43 ContinueDragging(); | 52 if (!source_tab_) |
| 53 return; |
| 54 |
| 55 // Before we get to dragging anywhere, ensure that we consider ourselves |
| 56 // attached to the source tabstrip. |
| 57 if (source_tab_->IsVisible()) { |
| 58 Attach(source_tabstrip_, gfx::Point()); |
| 59 } |
| 60 |
| 61 if (!source_tab_->IsVisible()) { |
| 62 // TODO(jhawkins): Save focus. |
| 63 ContinueDragging(); |
| 64 } |
44 } | 65 } |
45 | 66 |
46 bool DraggedTabControllerGtk::EndDrag(bool canceled) { | 67 bool DraggedTabControllerGtk::EndDrag(bool canceled) { |
47 return EndDragImpl(canceled ? CANCELED : NORMAL); | 68 return EndDragImpl(canceled ? CANCELED : NORMAL); |
48 } | 69 } |
49 | 70 |
50 //////////////////////////////////////////////////////////////////////////////// | 71 //////////////////////////////////////////////////////////////////////////////// |
51 // DraggedTabControllerGtk, TabContentsDelegate implementation: | 72 // DraggedTabControllerGtk, TabContentsDelegate implementation: |
52 | 73 |
53 void DraggedTabControllerGtk::OpenURLFromTab(TabContents* source, | 74 void DraggedTabControllerGtk::OpenURLFromTab(TabContents* source, |
54 const GURL& url, | 75 const GURL& url, |
55 const GURL& referrer, | 76 const GURL& referrer, |
56 WindowOpenDisposition disposition, | 77 WindowOpenDisposition disposition, |
57 PageTransition::Type transition) { | 78 PageTransition::Type transition) { |
58 if (original_delegate_) { | 79 if (original_delegate_) { |
59 if (disposition == CURRENT_TAB) | 80 if (disposition == CURRENT_TAB) |
60 disposition = NEW_WINDOW; | 81 disposition = NEW_WINDOW; |
61 | 82 |
62 original_delegate_->OpenURLFromTab(source, url, referrer, | 83 original_delegate_->OpenURLFromTab(source, url, referrer, |
63 disposition, transition); | 84 disposition, transition); |
64 } | 85 } |
65 } | 86 } |
66 | 87 |
67 void DraggedTabControllerGtk::NavigationStateChanged(const TabContents* source, | 88 void DraggedTabControllerGtk::NavigationStateChanged(const TabContents* source, |
68 unsigned changed_flags) { | 89 unsigned changed_flags) { |
| 90 if (dragged_tab_.get()) |
| 91 dragged_tab_->Update(); |
69 } | 92 } |
70 | 93 |
71 void DraggedTabControllerGtk::AddNewContents(TabContents* source, | 94 void DraggedTabControllerGtk::AddNewContents(TabContents* source, |
72 TabContents* new_contents, | 95 TabContents* new_contents, |
73 WindowOpenDisposition disposition, | 96 WindowOpenDisposition disposition, |
74 const gfx::Rect& initial_pos, | 97 const gfx::Rect& initial_pos, |
75 bool user_gesture) { | 98 bool user_gesture) { |
76 DCHECK(disposition != CURRENT_TAB); | 99 DCHECK(disposition != CURRENT_TAB); |
77 | 100 |
78 // Theoretically could be called while dragging if the page tries to | 101 // Theoretically could be called while dragging if the page tries to |
79 // spawn a window. Route this message back to the browser in most cases. | 102 // spawn a window. Route this message back to the browser in most cases. |
80 if (original_delegate_) { | 103 if (original_delegate_) { |
81 original_delegate_->AddNewContents(source, new_contents, disposition, | 104 original_delegate_->AddNewContents(source, new_contents, disposition, |
82 initial_pos, user_gesture); | 105 initial_pos, user_gesture); |
83 } | 106 } |
84 } | 107 } |
85 | 108 |
86 void DraggedTabControllerGtk::ActivateContents(TabContents* contents) { | 109 void DraggedTabControllerGtk::ActivateContents(TabContents* contents) { |
87 // Ignored. | 110 // Ignored. |
88 } | 111 } |
89 | 112 |
90 void DraggedTabControllerGtk::LoadingStateChanged(TabContents* source) { | 113 void DraggedTabControllerGtk::LoadingStateChanged(TabContents* source) { |
| 114 // TODO(jhawkins): It would be nice to respond to this message by changing the |
| 115 // screen shot in the dragged tab. |
| 116 if (dragged_tab_.get()) |
| 117 dragged_tab_->Update(); |
91 } | 118 } |
92 | 119 |
93 void DraggedTabControllerGtk::CloseContents(TabContents* source) { | 120 void DraggedTabControllerGtk::CloseContents(TabContents* source) { |
94 // Theoretically could be called by a window. Should be ignored | 121 // Theoretically could be called by a window. Should be ignored |
95 // because window.close() is ignored (usually, even though this | 122 // because window.close() is ignored (usually, even though this |
96 // method gets called.) | 123 // method gets called.) |
97 } | 124 } |
98 | 125 |
99 void DraggedTabControllerGtk::MoveContents(TabContents* source, | 126 void DraggedTabControllerGtk::MoveContents(TabContents* source, |
100 const gfx::Rect& pos) { | 127 const gfx::Rect& pos) { |
(...skipping 24 matching lines...) Expand all Loading... |
125 // DraggedTabControllerGtk, NotificationObserver implementation: | 152 // DraggedTabControllerGtk, NotificationObserver implementation: |
126 | 153 |
127 void DraggedTabControllerGtk::Observe(NotificationType type, | 154 void DraggedTabControllerGtk::Observe(NotificationType type, |
128 const NotificationSource& source, | 155 const NotificationSource& source, |
129 const NotificationDetails& details) { | 156 const NotificationDetails& details) { |
130 DCHECK(type == NotificationType::TAB_CONTENTS_DESTROYED); | 157 DCHECK(type == NotificationType::TAB_CONTENTS_DESTROYED); |
131 DCHECK(Source<TabContents>(source).ptr() == dragged_contents_); | 158 DCHECK(Source<TabContents>(source).ptr() == dragged_contents_); |
132 EndDragImpl(TAB_DESTROYED); | 159 EndDragImpl(TAB_DESTROYED); |
133 } | 160 } |
134 | 161 |
| 162 void DraggedTabControllerGtk::InitWindowCreatePoint() { |
| 163 window_create_point_.SetPoint(mouse_offset_.x(), mouse_offset_.y()); |
| 164 } |
| 165 |
135 void DraggedTabControllerGtk::SetDraggedContents(TabContents* new_contents) { | 166 void DraggedTabControllerGtk::SetDraggedContents(TabContents* new_contents) { |
136 if (dragged_contents_) { | 167 if (dragged_contents_) { |
137 registrar_.Remove(this, | 168 registrar_.Remove(this, |
138 NotificationType::TAB_CONTENTS_DESTROYED, | 169 NotificationType::TAB_CONTENTS_DESTROYED, |
139 Source<TabContents>(dragged_contents_)); | 170 Source<TabContents>(dragged_contents_)); |
140 if (original_delegate_) | 171 if (original_delegate_) |
141 dragged_contents_->set_delegate(original_delegate_); | 172 dragged_contents_->set_delegate(original_delegate_); |
142 } | 173 } |
143 original_delegate_ = NULL; | 174 original_delegate_ = NULL; |
144 dragged_contents_ = new_contents; | 175 dragged_contents_ = new_contents; |
145 if (dragged_contents_) { | 176 if (dragged_contents_) { |
146 registrar_.Add(this, | 177 registrar_.Add(this, |
147 NotificationType::TAB_CONTENTS_DESTROYED, | 178 NotificationType::TAB_CONTENTS_DESTROYED, |
148 Source<TabContents>(dragged_contents_)); | 179 Source<TabContents>(dragged_contents_)); |
149 | 180 |
150 // We need to be the delegate so we receive messages about stuff, | 181 // We need to be the delegate so we receive messages about stuff, |
151 // otherwise our dragged_contents() may be replaced and subsequently | 182 // otherwise our dragged_contents() may be replaced and subsequently |
152 // collected/destroyed while the drag is in process, leading to | 183 // collected/destroyed while the drag is in process, leading to |
153 // nasty crashes. | 184 // nasty crashes. |
154 original_delegate_ = dragged_contents_->delegate(); | 185 original_delegate_ = dragged_contents_->delegate(); |
155 dragged_contents_->set_delegate(this); | 186 dragged_contents_->set_delegate(this); |
156 } | 187 } |
157 } | 188 } |
158 | 189 |
159 void DraggedTabControllerGtk::ContinueDragging() { | 190 void DraggedTabControllerGtk::ContinueDragging() { |
| 191 EnsureDraggedTab(); |
| 192 |
160 // TODO(jhawkins): We don't handle the situation where the last tab is dragged | 193 // TODO(jhawkins): We don't handle the situation where the last tab is dragged |
161 // out of a window, so we'll just go with the way Windows handles dragging for | 194 // out of a window, so we'll just go with the way Windows handles dragging for |
162 // now. | 195 // now. |
163 gfx::Point screen_point = GetCursorScreenPoint(); | 196 gfx::Point screen_point = GetCursorScreenPoint(); |
164 MoveTab(screen_point); | 197 MoveTab(screen_point); |
165 } | 198 } |
166 | 199 |
167 void DraggedTabControllerGtk::MoveTab(const gfx::Point& screen_point) { | 200 void DraggedTabControllerGtk::MoveTab(const gfx::Point& screen_point) { |
168 gfx::Point dragged_point = GetDraggedPoint(screen_point); | 201 gfx::Point dragged_tab_point = GetDraggedTabPoint(screen_point); |
169 | 202 |
170 if (attached_tabstrip_) { | 203 if (attached_tabstrip_) { |
171 // Determine the horizontal move threshold. This is dependent on the width | 204 // Determine the horizontal move threshold. This is dependent on the width |
172 // of tabs. The smaller the tabs compared to the standard size, the smaller | 205 // of tabs. The smaller the tabs compared to the standard size, the smaller |
173 // the threshold. | 206 // the threshold. |
174 double unselected, selected; | 207 double unselected, selected; |
175 attached_tabstrip_->GetCurrentTabWidths(&unselected, &selected); | 208 attached_tabstrip_->GetCurrentTabWidths(&unselected, &selected); |
176 double ratio = unselected / TabGtk::GetStandardSize().width(); | 209 double ratio = unselected / TabGtk::GetStandardSize().width(); |
177 int threshold = static_cast<int>(ratio * kHorizontalMoveThreshold); | 210 int threshold = static_cast<int>(ratio * kHorizontalMoveThreshold); |
178 | 211 |
179 // Update the model, moving the TabContents from one index to another. Do | 212 // Update the model, moving the TabContents from one index to another. Do |
180 // this only if we have moved a minimum distance since the last reorder (to | 213 // this only if we have moved a minimum distance since the last reorder (to |
181 // prevent jitter). | 214 // prevent jitter). |
182 if (abs(screen_point.x() - last_move_screen_x_) > threshold) { | 215 if (abs(screen_point.x() - last_move_screen_x_) > threshold) { |
183 TabStripModel* attached_model = attached_tabstrip_->model(); | 216 TabStripModel* attached_model = attached_tabstrip_->model(); |
184 int from_index = | 217 int from_index = |
185 attached_model->GetIndexOfTabContents(dragged_contents_); | 218 attached_model->GetIndexOfTabContents(dragged_contents_); |
186 gfx::Rect bounds = source_tab_->bounds(); | 219 gfx::Rect bounds = GetDraggedTabTabStripBounds(dragged_tab_point); |
187 int to_index = GetInsertionIndexForDraggedBounds(bounds); | 220 int to_index = GetInsertionIndexForDraggedBounds(bounds); |
188 to_index = NormalizeIndexToAttachedTabStrip(to_index); | 221 to_index = NormalizeIndexToAttachedTabStrip(to_index); |
189 if (from_index != to_index) { | 222 if (from_index != to_index) { |
190 last_move_screen_x_ = screen_point.x(); | 223 last_move_screen_x_ = screen_point.x(); |
191 snap_bounds_ = attached_tabstrip_->GetTabAt(to_index)->bounds(); | |
192 attached_model->MoveTabContentsAt(from_index, to_index, true); | 224 attached_model->MoveTabContentsAt(from_index, to_index, true); |
193 } | 225 } |
194 } | 226 } |
195 } | 227 } |
196 | 228 |
197 // Move the tab. There are no changes to the model if we're detached. | 229 // Move the dragged tab. There are no changes to the model if we're detached. |
198 gfx::Rect bounds = source_tab_->bounds(); | 230 dragged_tab_->MoveTo(dragged_tab_point); |
199 bounds.set_x(dragged_point.x()); | |
200 source_tab_->SetBounds(bounds); | |
201 gtk_fixed_move(GTK_FIXED(source_tabstrip_->tabstrip_.get()), | |
202 source_tab_->widget(), bounds.x(), bounds.y()); | |
203 } | 231 } |
204 | 232 |
205 TabStripGtk* DraggedTabControllerGtk::GetTabStripForPoint( | 233 TabStripGtk* DraggedTabControllerGtk::GetTabStripForPoint( |
206 const gfx::Point& screen_point) { | 234 const gfx::Point& screen_point) { |
207 // TODO(jhawkins): Actually get the correct tabstrip under |screen_point|. | 235 // TODO(jhawkins): Actually get the correct tabstrip under |screen_point|. |
208 return source_tabstrip_; | 236 return source_tabstrip_; |
209 } | 237 } |
210 | 238 |
| 239 void DraggedTabControllerGtk::Attach(TabStripGtk* attached_tabstrip, |
| 240 const gfx::Point& screen_point) { |
| 241 attached_tabstrip_ = attached_tabstrip; |
| 242 InitWindowCreatePoint(); |
| 243 attached_tabstrip_->GenerateIdealBounds(); |
| 244 |
| 245 TabGtk* tab = GetTabMatchingDraggedContents(attached_tabstrip_); |
| 246 |
| 247 // Update the tab first, so we can ask it for its bounds and determine |
| 248 // where to insert the hidden tab. |
| 249 |
| 250 // If this is the first time Attach is called for this drag, we're attaching |
| 251 // to the source tabstrip, and we should assume the tab count already |
| 252 // includes this tab since we haven't been detached yet. If we don't do this, |
| 253 // the dragged representation will be a different size to others in the |
| 254 // tabstrip. |
| 255 int tab_count = attached_tabstrip_->GetTabCount(); |
| 256 if (!tab) |
| 257 ++tab_count; |
| 258 double unselected_width = 0, selected_width = 0; |
| 259 attached_tabstrip_->GetDesiredTabWidths(tab_count, &unselected_width, |
| 260 &selected_width); |
| 261 EnsureDraggedTab(); |
| 262 dragged_tab_->Attach(static_cast<int>(selected_width)); |
| 263 |
| 264 if (!tab) { |
| 265 // There is no tab in |attached_tabstrip| that corresponds to the dragged |
| 266 // TabContents. We must now create one. |
| 267 |
| 268 // Remove ourselves as the delegate now that the dragged TabContents is |
| 269 // being inserted back into a Browser. |
| 270 dragged_contents_->set_delegate(NULL); |
| 271 original_delegate_ = NULL; |
| 272 |
| 273 // Return the TabContents' to normalcy. |
| 274 dragged_contents_->set_capturing_contents(false); |
| 275 |
| 276 // We need to ask the tabstrip we're attached to ensure that the ideal |
| 277 // bounds for all its tabs are correctly generated, because the calculation |
| 278 // in GetInsertionIndexForDraggedBounds needs them to be to figure out the |
| 279 // appropriate insertion index. |
| 280 attached_tabstrip_->GenerateIdealBounds(); |
| 281 |
| 282 // Inserting counts as a move. We don't want the tabs to jitter when the |
| 283 // user moves the tab immediately after attaching it. |
| 284 last_move_screen_x_ = screen_point.x(); |
| 285 |
| 286 // Figure out where to insert the tab based on the bounds of the dragged |
| 287 // representation and the ideal bounds of the other tabs already in the |
| 288 // strip. ("ideal bounds" are stable even if the tabs' actual bounds are |
| 289 // changing due to animation). |
| 290 gfx::Rect bounds = GetDraggedTabTabStripBounds(screen_point); |
| 291 int index = GetInsertionIndexForDraggedBounds(bounds); |
| 292 index = std::max(std::min(index, attached_tabstrip_->model()->count()), 0); |
| 293 attached_tabstrip_->model()->InsertTabContentsAt(index, dragged_contents_, |
| 294 true, false); |
| 295 |
| 296 tab = GetTabMatchingDraggedContents(attached_tabstrip_); |
| 297 } |
| 298 DCHECK(tab); // We should now have a tab. |
| 299 tab->SetVisible(false); |
| 300 |
| 301 // TODO(jhawkins): Move the corresponding window to the front. |
| 302 } |
| 303 |
| 304 void DraggedTabControllerGtk::Detach() { |
| 305 // TODO(jhawkins): Detach the dragged tab. |
| 306 } |
| 307 |
| 308 gfx::Point DraggedTabControllerGtk::ConvertScreenPointToTabStripPoint( |
| 309 TabStripGtk* tabstrip, const gfx::Point& screen_point) { |
| 310 gint x, y; |
| 311 gdk_window_get_origin(tabstrip->tabstrip_.get()->window, &x, &y); |
| 312 return gfx::Point(screen_point.x() - x, screen_point.y() - y); |
| 313 } |
| 314 |
| 315 gfx::Rect DraggedTabControllerGtk::GetDraggedTabTabStripBounds( |
| 316 const gfx::Point& screen_point) { |
| 317 gfx::Point client_point = |
| 318 ConvertScreenPointToTabStripPoint(attached_tabstrip_, screen_point); |
| 319 gfx::Size tab_size = dragged_tab_->attached_tab_size(); |
| 320 return gfx::Rect(client_point.x(), client_point.y(), |
| 321 tab_size.width(), tab_size.height()); |
| 322 } |
| 323 |
211 int DraggedTabControllerGtk::GetInsertionIndexForDraggedBounds( | 324 int DraggedTabControllerGtk::GetInsertionIndexForDraggedBounds( |
212 const gfx::Rect& dragged_bounds) const { | 325 const gfx::Rect& dragged_bounds) const { |
213 int right_tab_x = 0; | 326 int right_tab_x = 0; |
214 | 327 |
215 // TODO(jhawkins): Handle RTL layout. | 328 // TODO(jhawkins): Handle RTL layout. |
216 | 329 |
217 // Divides each tab into two halves to see if the dragged tab has crossed | 330 // Divides each tab into two halves to see if the dragged tab has crossed |
218 // the halfway boundary necessary to move past the next tab. | 331 // the halfway boundary necessary to move past the next tab. |
219 for (int i = 0; i < attached_tabstrip_->GetTabCount(); i++) { | 332 for (int i = 0; i < attached_tabstrip_->GetTabCount(); i++) { |
220 gfx::Rect ideal_bounds = attached_tabstrip_->GetIdealBounds(i); | 333 gfx::Rect ideal_bounds = attached_tabstrip_->GetIdealBounds(i); |
(...skipping 15 matching lines...) Expand all Loading... |
236 return i; | 349 return i; |
237 } | 350 } |
238 } | 351 } |
239 | 352 |
240 if (dragged_bounds.right() > right_tab_x) | 353 if (dragged_bounds.right() > right_tab_x) |
241 return attached_tabstrip_->model()->count(); | 354 return attached_tabstrip_->model()->count(); |
242 | 355 |
243 return TabStripModel::kNoTab; | 356 return TabStripModel::kNoTab; |
244 } | 357 } |
245 | 358 |
246 gfx::Point DraggedTabControllerGtk::GetDraggedPoint(const gfx::Point& point) { | 359 gfx::Point DraggedTabControllerGtk::GetDraggedTabPoint( |
247 int x = point.x() - mouse_offset_.x(); | 360 const gfx::Point& screen_point) { |
248 int y = point.y() - mouse_offset_.y(); | 361 int x = screen_point.x() - mouse_offset_.x(); |
| 362 int y = screen_point.y() - mouse_offset_.y(); |
249 | 363 |
250 // Snap the dragged tab to the tab strip. | 364 // If we're not attached, we just use x and y from above. |
251 if (x < 0) | 365 if (attached_tabstrip_) { |
252 x = 0; | 366 gfx::Rect tabstrip_bounds = |
| 367 gtk_util::GetWidgetScreenBounds(attached_tabstrip_->tabstrip_.get()); |
| 368 // Snap the dragged tab to the tabstrip if we are attached, detaching |
| 369 // only when the mouse position (screen_point) exceeds the screen bounds |
| 370 // of the tabstrip. |
| 371 if (x < tabstrip_bounds.x() && screen_point.x() >= tabstrip_bounds.x()) |
| 372 x = tabstrip_bounds.x(); |
253 | 373 |
254 // Make sure the tab can't be dragged off the right side of the tab strip. | 374 gfx::Size tab_size = dragged_tab_->attached_tab_size(); |
255 int max_x = attached_tabstrip_->bounds_.right() - source_tab_->width(); | 375 int vertical_drag_magnetism = tab_size.height() * 2; |
256 if (x > max_x) | 376 int vertical_detach_point = tabstrip_bounds.y() - vertical_drag_magnetism; |
257 x = max_x; | 377 if (y < tabstrip_bounds.y() && screen_point.y() >= vertical_detach_point) |
| 378 y = tabstrip_bounds.y(); |
258 | 379 |
| 380 // Make sure the tab can't be dragged off the right side of the tabstrip |
| 381 // unless the mouse pointer passes outside the bounds of the strip by |
| 382 // clamping the position of the dragged window to the tabstrip width less |
| 383 // the width of one tab until the mouse pointer (screen_point) exceeds the |
| 384 // screen bounds of the tabstrip. |
| 385 int max_x = tabstrip_bounds.right() - tab_size.width(); |
| 386 int max_y = tabstrip_bounds.bottom() - tab_size.height(); |
| 387 if (x > max_x && screen_point.x() <= tabstrip_bounds.right()) |
| 388 x = max_x; |
| 389 if (y > max_y && screen_point.y() <= |
| 390 (tabstrip_bounds.bottom() + vertical_drag_magnetism)) { |
| 391 y = max_y; |
| 392 } |
| 393 } |
259 return gfx::Point(x, y); | 394 return gfx::Point(x, y); |
260 } | 395 } |
261 | 396 |
262 int DraggedTabControllerGtk::NormalizeIndexToAttachedTabStrip(int index) const { | 397 int DraggedTabControllerGtk::NormalizeIndexToAttachedTabStrip(int index) const { |
263 if (index >= attached_tabstrip_->model_->count()) | 398 if (index >= attached_tabstrip_->model_->count()) |
264 return attached_tabstrip_->model_->count() - 1; | 399 return attached_tabstrip_->model_->count() - 1; |
265 if (index == TabStripModel::kNoTab) | 400 if (index == TabStripModel::kNoTab) |
266 return 0; | 401 return 0; |
267 return index; | 402 return index; |
268 } | 403 } |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
311 | 446 |
312 return destroy_now; | 447 return destroy_now; |
313 } | 448 } |
314 | 449 |
315 void DraggedTabControllerGtk::RevertDrag() { | 450 void DraggedTabControllerGtk::RevertDrag() { |
316 // We save this here because code below will modify |attached_tabstrip_|. | 451 // We save this here because code below will modify |attached_tabstrip_|. |
317 if (attached_tabstrip_) { | 452 if (attached_tabstrip_) { |
318 int index = attached_tabstrip_->model()->GetIndexOfTabContents( | 453 int index = attached_tabstrip_->model()->GetIndexOfTabContents( |
319 dragged_contents_); | 454 dragged_contents_); |
320 if (attached_tabstrip_ != source_tabstrip_) { | 455 if (attached_tabstrip_ != source_tabstrip_) { |
321 // The Tab was inserted into another TabStrip. We need to put it back | 456 // The tab was inserted into another tabstrip. We need to put it back |
322 // into the original one. | 457 // into the original one. |
323 attached_tabstrip_->model()->DetachTabContentsAt(index); | 458 attached_tabstrip_->model()->DetachTabContentsAt(index); |
324 // TODO(beng): (Cleanup) seems like we should use Attach() for this | 459 // TODO(beng): (Cleanup) seems like we should use Attach() for this |
325 // somehow. | 460 // somehow. |
326 attached_tabstrip_ = source_tabstrip_; | 461 attached_tabstrip_ = source_tabstrip_; |
327 source_tabstrip_->model()->InsertTabContentsAt(source_model_index_, | 462 source_tabstrip_->model()->InsertTabContentsAt(source_model_index_, |
328 dragged_contents_, true, false); | 463 dragged_contents_, true, false); |
329 } else { | 464 } else { |
330 // The Tab was moved within the TabStrip where the drag was initiated. | 465 // The tab was moved within the tabstrip where the drag was initiated. |
331 // Move it back to the starting location. | 466 // Move it back to the starting location. |
332 source_tabstrip_->model()->MoveTabContentsAt(index, source_model_index_, | 467 source_tabstrip_->model()->MoveTabContentsAt(index, source_model_index_, |
333 true); | 468 true); |
334 } | 469 } |
335 } else { | 470 } else { |
336 // TODO(beng): (Cleanup) seems like we should use Attach() for this | 471 // TODO(beng): (Cleanup) seems like we should use Attach() for this |
337 // somehow. | 472 // somehow. |
338 attached_tabstrip_ = source_tabstrip_; | 473 attached_tabstrip_ = source_tabstrip_; |
339 // The Tab was detached from the TabStrip where the drag began, and has not | 474 // The tab was detached from the tabstrip where the drag began, and has not |
340 // been attached to any other TabStrip. We need to put it back into the | 475 // been attached to any other tabstrip. We need to put it back into the |
341 // source TabStrip. | 476 // source tabstrip. |
342 source_tabstrip_->model()->InsertTabContentsAt(source_model_index_, | 477 source_tabstrip_->model()->InsertTabContentsAt(source_model_index_, |
343 dragged_contents_, true, false); | 478 dragged_contents_, true, false); |
344 } | 479 } |
| 480 |
| 481 source_tab_->SetVisible(true); |
345 } | 482 } |
346 | 483 |
347 bool DraggedTabControllerGtk::CompleteDrag() { | 484 bool DraggedTabControllerGtk::CompleteDrag() { |
348 // We don't need to do anything other than make the Tab visible again, | 485 // We don't need to do anything other than make the tab visible again, |
349 // since the dragged tab is going away. | 486 // since the dragged tab is going away. |
350 gfx::Rect bounds = source_tab_->bounds(); | 487 TabGtk* tab = GetTabMatchingDraggedContents(attached_tabstrip_); |
351 bounds.set_x(snap_bounds_.x()); | 488 gfx::Rect rect = GetTabScreenBounds(tab); |
352 source_tab_->SetBounds(bounds); | 489 dragged_tab_->AnimateToBounds(GetTabScreenBounds(tab), |
353 gtk_fixed_move(GTK_FIXED(source_tabstrip_->tabstrip_.get()), | 490 NewCallback(this, &DraggedTabControllerGtk::OnAnimateToBoundsComplete)); |
354 source_tab_->widget(), bounds.x(), bounds.y()); | |
355 | 491 |
356 return false; | 492 return false; |
357 } | 493 } |
358 | 494 |
| 495 void DraggedTabControllerGtk::EnsureDraggedTab() { |
| 496 if (!dragged_tab_.get()) { |
| 497 gfx::Rect rect; |
| 498 dragged_contents_->GetContainerBounds(&rect); |
| 499 |
| 500 dragged_tab_.reset(new DraggedTabGtk(dragged_contents_, mouse_offset_, |
| 501 gfx::Size(rect.width(), rect.height()))); |
| 502 } |
| 503 } |
| 504 |
359 gfx::Point DraggedTabControllerGtk::GetCursorScreenPoint() const { | 505 gfx::Point DraggedTabControllerGtk::GetCursorScreenPoint() const { |
360 // Get default display and screen. | 506 // Get default display and screen. |
361 GdkDisplay* display = gdk_display_get_default(); | 507 GdkDisplay* display = gdk_display_get_default(); |
362 | 508 |
363 // Get cursor position. | 509 // Get cursor position. |
364 int x, y; | 510 int x, y; |
365 gdk_display_get_pointer(display, NULL, &x, &y, NULL); | 511 gdk_display_get_pointer(display, NULL, &x, &y, NULL); |
366 | 512 |
367 return gfx::Point(x, y); | 513 return gfx::Point(x, y); |
368 } | 514 } |
| 515 |
| 516 // static |
| 517 gfx::Rect DraggedTabControllerGtk::GetTabScreenBounds(TabGtk* tab) { |
| 518 // A hidden widget moved with gtk_fixed_move in a GtkFixed container doesn't |
| 519 // update its allocation until after the widget is shown, so we have to use |
| 520 // the tab bounds we keep track of. |
| 521 int x, y; |
| 522 x = tab->bounds().x(); |
| 523 y = tab->bounds().y(); |
| 524 |
| 525 GtkWidget* widget = tab->widget(); |
| 526 GtkWidget* parent = gtk_widget_get_parent(widget); |
| 527 gfx::Point point = gtk_util::GetWidgetScreenPosition(parent); |
| 528 x += point.x(); |
| 529 y += point.y(); |
| 530 |
| 531 return gfx::Rect(x, y, widget->allocation.width, widget->allocation.height); |
| 532 } |
| 533 |
| 534 void DraggedTabControllerGtk::OnAnimateToBoundsComplete() { |
| 535 // Sometimes, for some reason, in automation we can be called back on a |
| 536 // detach even though we aren't attached to a tabstrip. Guard against that. |
| 537 if (attached_tabstrip_) { |
| 538 TabGtk* tab = GetTabMatchingDraggedContents(attached_tabstrip_); |
| 539 if (tab) { |
| 540 tab->SetVisible(true); |
| 541 // Paint the tab now, otherwise there may be slight flicker between the |
| 542 // time the dragged tab window is destroyed and we paint. |
| 543 tab->SchedulePaint(); |
| 544 } |
| 545 } |
| 546 |
| 547 if (!in_destructor_) |
| 548 source_tabstrip_->DestroyDragController(); |
| 549 } |
OLD | NEW |