Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(154)

Side by Side Diff: chrome/browser/cocoa/tab_view.mm

Issue 199079: Allow windows with a single tab to be merged into other windows with drag and... (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: Created 11 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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/cocoa/tab_view.h" 5 #import "chrome/browser/cocoa/tab_view.h"
6 6
7 #include "base/logging.h"
7 #include "chrome/browser/cocoa/nsimage_cache.h" 8 #include "chrome/browser/cocoa/nsimage_cache.h"
8 #include "chrome/browser/cocoa/tab_controller.h" 9 #import "chrome/browser/cocoa/tab_controller.h"
9 #include "chrome/browser/cocoa/tab_window_controller.h" 10 #import "chrome/browser/cocoa/tab_window_controller.h"
10 11
11 // Constants for inset and control points for tab shape. 12 // Constants for inset and control points for tab shape.
12 static const CGFloat kInsetMultiplier = 2.0/3.0; 13 static const CGFloat kInsetMultiplier = 2.0/3.0;
13 static const CGFloat kControlPoint1Multiplier = 1.0/3.0; 14 static const CGFloat kControlPoint1Multiplier = 1.0/3.0;
14 static const CGFloat kControlPoint2Multiplier = 3.0/8.0; 15 static const CGFloat kControlPoint2Multiplier = 3.0/8.0;
15 16
16 static const NSTimeInterval kAnimationShowDuration = 0.2; 17 static const NSTimeInterval kAnimationShowDuration = 0.2;
17 static const NSTimeInterval kAnimationHideDuration = 0.4; 18 static const NSTimeInterval kAnimationHideDuration = 0.4;
18 19
19 @implementation TabView 20 @implementation TabView
(...skipping 115 matching lines...) Expand 10 before | Expand all | Expand 10 after
135 - (BOOL)canBeDragged { 136 - (BOOL)canBeDragged {
136 NSWindowController *controller = [sourceWindow_ windowController]; 137 NSWindowController *controller = [sourceWindow_ windowController];
137 if ([controller isKindOfClass:[TabWindowController class]]) { 138 if ([controller isKindOfClass:[TabWindowController class]]) {
138 TabWindowController* realController = 139 TabWindowController* realController =
139 static_cast<TabWindowController*>(controller); 140 static_cast<TabWindowController*>(controller);
140 return [realController isTabDraggable:self]; 141 return [realController isTabDraggable:self];
141 } 142 }
142 return YES; 143 return YES;
143 } 144 }
144 145
146 // Find all the windows that could be a target. It has to be of the
147 // appropriate class, and visible (obviously). Note that the window cannot be
148 // a target for itself.
149 - (NSArray*)dropTargetsForController:(TabWindowController*)dragController {
150 NSMutableArray* targets = [NSMutableArray array];
151 NSWindow* dragWindow = [dragController window];
152 for (NSWindow* window in [NSApp windows]) {
153 if (window == dragWindow) continue;
154 if (![window isVisible]) continue;
155 NSWindowController *controller = [window windowController];
156 if ([controller isKindOfClass:[TabWindowController class]]) {
157 TabWindowController* realController =
158 static_cast<TabWindowController*>(controller);
159 if ([realController canReceiveFrom:dragController]) {
160 [targets addObject:controller];
161 }
162 }
163 }
164 return targets;
165 }
166
145 // Handle clicks and drags in this button. We get here because we have 167 // Handle clicks and drags in this button. We get here because we have
146 // overridden acceptsFirstMouse: and the click is within our bounds. 168 // overridden acceptsFirstMouse: and the click is within our bounds.
147 // TODO(pinkerton/alcor): This routine needs *a lot* of work to marry Cole's 169 // TODO(pinkerton/alcor): This routine needs *a lot* of work to marry Cole's
148 // ideas of dragging cocoa views between windows and how the Browser and 170 // ideas of dragging cocoa views between windows and how the Browser and
149 // TabStrip models want to manage tabs. 171 // TabStrip models want to manage tabs.
150 172
151 static const CGFloat kTearDistance = 36.0; 173 static const CGFloat kTearDistance = 36.0;
152 static const NSTimeInterval kTearDuration = 0.333; 174 static const NSTimeInterval kTearDuration = 0.333;
153 175
154 // This is used to judge whether the mouse has moved during rapid closure; if it 176 // This is used to judge whether the mouse has moved during rapid closure; if it
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
186 sourceTabFrame_ = [self frame]; 208 sourceTabFrame_ = [self frame];
187 sourceController_ = [sourceWindow_ windowController]; 209 sourceController_ = [sourceWindow_ windowController];
188 draggedController_ = nil; 210 draggedController_ = nil;
189 dragWindow_ = nil; 211 dragWindow_ = nil;
190 dragOverlay_ = nil; 212 dragOverlay_ = nil;
191 targetController_ = nil; 213 targetController_ = nil;
192 tabWasDragged_ = NO; 214 tabWasDragged_ = NO;
193 tearTime_ = 0.0; 215 tearTime_ = 0.0;
194 draggingWithinTabStrip_ = YES; 216 draggingWithinTabStrip_ = YES;
195 217
196 // We don't want to "tear off" a tab if there's only one in the window. Treat 218 // If there's more than one potential window to be a drop target, we want to
197 // it like we're dragging around a tab we've already detached. Note that 219 // treat a drag of a tab just like dragging around a tab that's already
198 // unit tests might have |-numberOfTabs| reporting zero since the model 220 // detached. Note that unit tests might have |-numberOfTabs| reporting zero
199 // won't be fully hooked up. We need to be prepared for that and not send 221 // since the model won't be fully hooked up. We need to be prepared for that
200 // them into the "magnetic" codepath. 222 // and not send them into the "magnetic" codepath.
223 NSArray* targets = [self dropTargetsForController:sourceController_];
201 moveWindowOnDrag_ = 224 moveWindowOnDrag_ =
202 [sourceController_ numberOfTabs] <= 1 || ![self canBeDragged]; 225 ([sourceController_ numberOfTabs] < 2 && ![targets count]) ||
226 ![self canBeDragged];
rohitrao (ping after 24h) 2009/09/10 19:03:56 Should this be in line with the "(" above?
pink (ping after 24hrs) 2009/09/10 19:05:55 No, the first two should bind together, or'd with
pink (ping after 24hrs) 2009/09/10 19:06:31 Oops, my bad, yes. Misunderstood.
227 // If we are dragging a tab, a window with a single tab should immediately
228 // snap off and not drag within the tab strip.
229 if (!moveWindowOnDrag_)
230 draggingWithinTabStrip_ = [sourceController_ numberOfTabs] > 1;
203 231
204 dragOrigin_ = [NSEvent mouseLocation]; 232 dragOrigin_ = [NSEvent mouseLocation];
205 233
206 // Because we move views between windows, we need to handle the event loop 234 // Because we move views between windows, we need to handle the event loop
207 // ourselves. Ideally we should use the standard event loop. 235 // ourselves. Ideally we should use the standard event loop.
208 while (1) { 236 while (1) {
209 theEvent = 237 theEvent =
210 [NSApp nextEventMatchingMask:NSLeftMouseUpMask | NSLeftMouseDraggedMask 238 [NSApp nextEventMatchingMask:NSLeftMouseUpMask | NSLeftMouseDraggedMask
211 untilDate:[NSDate distantFuture] 239 untilDate:[NSDate distantFuture]
212 inMode:NSDefaultRunLoopMode dequeue:YES]; 240 inMode:NSDefaultRunLoopMode dequeue:YES];
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after
279 } else { 307 } else {
280 return; 308 return;
281 } 309 }
282 } 310 }
283 311
284 NSPoint lastPoint = 312 NSPoint lastPoint =
285 [[theEvent window] convertBaseToScreen:[theEvent locationInWindow]]; 313 [[theEvent window] convertBaseToScreen:[theEvent locationInWindow]];
286 314
287 // Do not start dragging until the user has "torn" the tab off by 315 // Do not start dragging until the user has "torn" the tab off by
288 // moving more than 3 pixels. 316 // moving more than 3 pixels.
289 NSDate* targetDwellDate = nil; // The date this target was first chosen 317 NSDate* targetDwellDate = nil; // The date this target was first chosen.
290 NSMutableArray* targets = [NSMutableArray array];
291 318
292 NSPoint thisPoint = [NSEvent mouseLocation]; 319 NSPoint thisPoint = [NSEvent mouseLocation];
293 320
294 // Find all the windows that could be a target. It has to be of the
295 // appropriate class, and visible (obviously).
296 if (![targets count]) {
297 for (NSWindow* window in [NSApp windows]) {
298 if (window == dragWindow_) continue;
299 if (![window isVisible]) continue;
300 NSWindowController *controller = [window windowController];
301 if ([controller isKindOfClass:[TabWindowController class]]) {
302 TabWindowController* realController =
303 static_cast<TabWindowController*>(controller);
304 if ([realController canReceiveFrom:sourceController_]) {
305 [targets addObject:controller];
306 }
307 }
308 }
309 }
310
311 // Iterate over possible targets checking for the one the mouse is in. 321 // Iterate over possible targets checking for the one the mouse is in.
312 // The mouse can be in either the tab or window frame. 322 // The mouse can be in either the tab or window frame.
323 NSArray* targets = [self dropTargetsForController:draggedController_];
313 TabWindowController* newTarget = nil; 324 TabWindowController* newTarget = nil;
314 for (TabWindowController* target in targets) { 325 for (TabWindowController* target in targets) {
315 NSRect windowFrame = [[target window] frame]; 326 NSRect windowFrame = [[target window] frame];
316 if (NSPointInRect(thisPoint, windowFrame)) { 327 if (NSPointInRect(thisPoint, windowFrame)) {
317 NSRect tabStripFrame = [[target tabStripView] frame]; 328 NSRect tabStripFrame = [[target tabStripView] frame];
318 tabStripFrame.origin = [[target window] 329 tabStripFrame.origin = [[target window]
319 convertBaseToScreen:tabStripFrame.origin]; 330 convertBaseToScreen:tabStripFrame.origin];
320 if (NSPointInRect(thisPoint, tabStripFrame)) { 331 if (NSPointInRect(thisPoint, tabStripFrame)) {
321 newTarget = target; 332 newTarget = target;
322 } 333 }
323 break; 334 break;
324 } 335 }
325 } 336 }
326 337
327 // If we're now targeting a new window, re-layout the tabs in the old 338 // If we're now targeting a new window, re-layout the tabs in the old
328 // target and reset how long we've been hovering over this new one. 339 // target and reset how long we've been hovering over this new one.
329 if (targetController_ != newTarget) { 340 if (targetController_ != newTarget) {
330 targetDwellDate = [NSDate date]; 341 targetDwellDate = [NSDate date];
331 [targetController_ removePlaceholder]; 342 [targetController_ removePlaceholder];
332 targetController_ = newTarget; 343 targetController_ = newTarget;
333 if (!newTarget) { 344 if (!newTarget) {
334 tearTime_ = [NSDate timeIntervalSinceReferenceDate]; 345 tearTime_ = [NSDate timeIntervalSinceReferenceDate];
335 tearOrigin_ = [dragWindow_ frame].origin; 346 tearOrigin_ = [dragWindow_ frame].origin;
336 } 347 }
337 } 348 }
338 349
339 // Create or identify the dragged controller. 350 // Create or identify the dragged controller.
340 if (!draggedController_) { 351 if (!draggedController_) {
341 // Detach from the current window and put it in a new window. 352 // Detach from the current window and put it in a new window. If there are
353 // no more tabs remaining after detaching, the source window is about to
354 // go away (it's been autoreleased) so we need to ensure we don't reference
355 // it any more. In that case the new controller becomes our source
356 // controller.
342 draggedController_ = [sourceController_ detachTabToNewWindow:self]; 357 draggedController_ = [sourceController_ detachTabToNewWindow:self];
343 dragWindow_ = [draggedController_ window]; 358 dragWindow_ = [draggedController_ window];
344 [dragWindow_ setAlphaValue:0.0]; 359 [dragWindow_ setAlphaValue:0.0];
360 if (![sourceController_ numberOfTabs]) {
361 sourceController_ = draggedController_;
362 sourceWindow_ = dragWindow_;
363 }
345 364
346 // If dragging the tab only moves the current window, do not show overlay 365 // If dragging the tab only moves the current window, do not show overlay
347 // so that sheets stay on top of the window. 366 // so that sheets stay on top of the window.
348 // Bring the target window to the front and make sure it has a border. 367 // Bring the target window to the front and make sure it has a border.
349 [dragWindow_ setLevel:NSFloatingWindowLevel]; 368 [dragWindow_ setLevel:NSFloatingWindowLevel];
350 [dragWindow_ orderFront:nil]; 369 [dragWindow_ orderFront:nil];
351 [dragWindow_ makeMainWindow]; 370 [dragWindow_ makeMainWindow];
352 [draggedController_ showOverlay]; 371 [draggedController_ showOverlay];
353 dragOverlay_ = [draggedController_ overlayWindow]; 372 dragOverlay_ = [draggedController_ overlayWindow];
354 // Force the new tab button to be hidden. We'll reset it on mouse up. 373 // Force the new tab button to be hidden. We'll reset it on mouse up.
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
391 } 410 }
392 [dragWindow_ setFrameOrigin:NSMakePoint(origin.x, origin.y)]; 411 [dragWindow_ setFrameOrigin:NSMakePoint(origin.x, origin.y)];
393 412
394 // If we're not hovering over any window, make the window is fully 413 // If we're not hovering over any window, make the window is fully
395 // opaque. Otherwise, find where the tab might be dropped and insert 414 // opaque. Otherwise, find where the tab might be dropped and insert
396 // a placeholder so it appears like it's part of that window. 415 // a placeholder so it appears like it's part of that window.
397 if (targetController_) { 416 if (targetController_) {
398 if (![[targetController_ window] isKeyWindow]) { 417 if (![[targetController_ window] isKeyWindow]) {
399 // && ([targetDwellDate timeIntervalSinceNow] < -REQUIRED_DWELL)) { 418 // && ([targetDwellDate timeIntervalSinceNow] < -REQUIRED_DWELL)) {
400 [[targetController_ window] orderFront:nil]; 419 [[targetController_ window] orderFront:nil];
401 [targets removeAllObjects];
rohitrao (ping after 24h) 2009/09/10 19:03:56 This is ok to delete because targets is never refe
pink (ping after 24hrs) 2009/09/10 19:05:55 Correct. It must have been leftover from something
402 targetDwellDate = nil; 420 targetDwellDate = nil;
403 } 421 }
404 422
405 // Compute where placeholder should go and insert it into the 423 // Compute where placeholder should go and insert it into the
406 // destination tab strip. 424 // destination tab strip.
407 NSRect dropTabFrame = [[targetController_ tabStripView] frame]; 425 NSRect dropTabFrame = [[targetController_ tabStripView] frame];
408 TabView *draggedTabView = (TabView *)[draggedController_ selectedTabView]; 426 TabView *draggedTabView = (TabView *)[draggedController_ selectedTabView];
409 NSRect tabFrame = [draggedTabView frame]; 427 NSRect tabFrame = [draggedTabView frame];
410 tabFrame.origin = [dragWindow_ convertBaseToScreen:tabFrame.origin]; 428 tabFrame.origin = [dragWindow_ convertBaseToScreen:tabFrame.origin];
411 tabFrame.origin = [[targetController_ window] 429 tabFrame.origin = [[targetController_ window]
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
451 return; 469 return;
452 470
453 // We are now free to re-display the new tab button in the window we're 471 // We are now free to re-display the new tab button in the window we're
454 // dragging. It will show when the next call to -layoutTabs (which happens 472 // dragging. It will show when the next call to -layoutTabs (which happens
455 // indrectly by several of the calls below, such as removing the placeholder). 473 // indrectly by several of the calls below, such as removing the placeholder).
456 [draggedController_ showNewTabButton:YES]; 474 [draggedController_ showNewTabButton:YES];
457 475
458 if (draggingWithinTabStrip_) { 476 if (draggingWithinTabStrip_) {
459 if (tabWasDragged_) { 477 if (tabWasDragged_) {
460 // Move tab to new location. 478 // Move tab to new location.
479 DCHECK([sourceController_ numberOfTabs]);
461 TabWindowController* dropController = sourceController_; 480 TabWindowController* dropController = sourceController_;
462 [dropController moveTabView:[dropController selectedTabView] 481 [dropController moveTabView:[dropController selectedTabView]
463 fromController:nil]; 482 fromController:nil];
464 } 483 }
465 } else if (targetController_) { 484 } else if (targetController_) {
466 // Move between windows. If |targetController_| is nil, we're not dropping 485 // Move between windows. If |targetController_| is nil, we're not dropping
467 // into any existing window. 486 // into any existing window.
468 NSView* draggedTabView = [draggedController_ selectedTabView]; 487 NSView* draggedTabView = [draggedController_ selectedTabView];
469 [draggedController_ removeOverlay]; 488 [draggedController_ removeOverlay];
470 [targetController_ moveTabView:draggedTabView 489 [targetController_ moveTabView:draggedTabView
(...skipping 181 matching lines...) Expand 10 before | Expand all | Expand 10 after
652 [[NSGraphicsContext currentContext] restoreGraphicsState]; 671 [[NSGraphicsContext currentContext] restoreGraphicsState];
653 } 672 }
654 673
655 // Called when the user hits the right mouse button (or control-clicks) to 674 // Called when the user hits the right mouse button (or control-clicks) to
656 // show a context menu. 675 // show a context menu.
657 - (void)rightMouseDown:(NSEvent*)theEvent { 676 - (void)rightMouseDown:(NSEvent*)theEvent {
658 [NSMenu popUpContextMenu:[self menu] withEvent:theEvent forView:self]; 677 [NSMenu popUpContextMenu:[self menu] withEvent:theEvent forView:self];
659 } 678 }
660 679
661 @end 680 @end
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698