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

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

Issue 337055: Mac: implement drag-and-drop of URLs to tabs/tab strip. (Closed)
Patch Set: Give another line back. Created 11 years 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
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 #import "chrome/browser/cocoa/tab_strip_controller.h" 5 #import "chrome/browser/cocoa/tab_strip_controller.h"
6 6
7 #import <QuartzCore/QuartzCore.h> 7 #import <QuartzCore/QuartzCore.h>
8 8
9 #include <limits> 9 #include <limits>
10 #include <string>
10 11
11 #include "app/l10n_util.h" 12 #include "app/l10n_util.h"
12 #include "app/resource_bundle.h" 13 #include "app/resource_bundle.h"
13 #include "base/mac_util.h" 14 #include "base/mac_util.h"
14 #include "base/nsimage_cache_mac.h" 15 #include "base/nsimage_cache_mac.h"
15 #include "base/sys_string_conversions.h" 16 #include "base/sys_string_conversions.h"
16 #include "chrome/app/chrome_dll_resource.h" 17 #include "chrome/app/chrome_dll_resource.h"
17 #include "chrome/browser/browser.h" 18 #include "chrome/browser/browser.h"
18 #include "chrome/browser/find_bar.h" 19 #include "chrome/browser/find_bar.h"
19 #include "chrome/browser/find_bar_controller.h" 20 #include "chrome/browser/find_bar_controller.h"
20 #include "chrome/browser/metrics/user_metrics.h" 21 #include "chrome/browser/metrics/user_metrics.h"
21 #include "chrome/browser/profile.h" 22 #include "chrome/browser/profile.h"
22 #import "chrome/browser/cocoa/browser_window_controller.h" 23 #import "chrome/browser/cocoa/browser_window_controller.h"
23 #import "chrome/browser/cocoa/constrained_window_mac.h" 24 #import "chrome/browser/cocoa/constrained_window_mac.h"
24 #import "chrome/browser/cocoa/tab_strip_view.h" 25 #import "chrome/browser/cocoa/tab_strip_view.h"
25 #import "chrome/browser/cocoa/tab_contents_controller.h" 26 #import "chrome/browser/cocoa/tab_contents_controller.h"
26 #import "chrome/browser/cocoa/tab_controller.h" 27 #import "chrome/browser/cocoa/tab_controller.h"
27 #import "chrome/browser/cocoa/tab_strip_model_observer_bridge.h" 28 #import "chrome/browser/cocoa/tab_strip_model_observer_bridge.h"
28 #import "chrome/browser/cocoa/tab_view.h" 29 #import "chrome/browser/cocoa/tab_view.h"
29 #import "chrome/browser/cocoa/throbber_view.h" 30 #import "chrome/browser/cocoa/throbber_view.h"
31 #include "chrome/browser/net/url_fixer_upper.h"
30 #include "chrome/browser/tab_contents/navigation_controller.h" 32 #include "chrome/browser/tab_contents/navigation_controller.h"
31 #include "chrome/browser/tab_contents/navigation_entry.h" 33 #include "chrome/browser/tab_contents/navigation_entry.h"
32 #include "chrome/browser/tab_contents/tab_contents.h" 34 #include "chrome/browser/tab_contents/tab_contents.h"
33 #include "chrome/browser/tab_contents/tab_contents_view.h" 35 #include "chrome/browser/tab_contents/tab_contents_view.h"
34 #include "chrome/browser/tabs/tab_strip_model.h" 36 #include "chrome/browser/tabs/tab_strip_model.h"
35 #include "grit/app_resources.h" 37 #include "grit/app_resources.h"
36 #include "grit/generated_resources.h" 38 #include "grit/generated_resources.h"
37 #include "grit/theme_resources.h" 39 #include "grit/theme_resources.h"
38 #include "skia/ext/skia_utils_mac.h" 40 #include "skia/ext/skia_utils_mac.h"
39 #import "third_party/GTM/AppKit/GTMNSAnimation+Duration.h" 41 #import "third_party/GTM/AppKit/GTMNSAnimation+Duration.h"
40 42
41 NSString* const kTabStripNumberOfTabsChanged = @"kTabStripNumberOfTabsChanged"; 43 NSString* const kTabStripNumberOfTabsChanged = @"kTabStripNumberOfTabsChanged";
42 44
45 namespace {
46
43 // A value to indicate tab layout should use the full available width of the 47 // A value to indicate tab layout should use the full available width of the
44 // view. 48 // view.
45 static const CGFloat kUseFullAvailableWidth = -1.0; 49 const CGFloat kUseFullAvailableWidth = -1.0;
46 50
47 // Left-side indent for tab layout so tabs don't overlap with the window 51 // Left-side indent for tab layout so tabs don't overlap with the window
48 // controls. 52 // controls.
49 static const CGFloat kIndentLeavingSpaceForControls = 64.0; 53 const CGFloat kIndentLeavingSpaceForControls = 64.0;
54
55 // The amount by which tabs overlap.
56 const CGFloat kTabOverlap = 20.0;
57
58 // The amount by which the new tab button is offset (from the tabs).
59 const CGFloat kNewTabButtonOffset = 8.0;
50 60
51 // Time (in seconds) in which tabs animate to their final position. 61 // Time (in seconds) in which tabs animate to their final position.
52 static const NSTimeInterval kAnimationDuration = 0.2; 62 const NSTimeInterval kAnimationDuration = 0.2;
53
54 namespace {
55 63
56 // Helper class for doing NSAnimationContext calls that takes a bool to disable 64 // Helper class for doing NSAnimationContext calls that takes a bool to disable
57 // all the work. Useful for code that wants to conditionally animate. 65 // all the work. Useful for code that wants to conditionally animate.
58 class ScopedNSAnimationContextGroup { 66 class ScopedNSAnimationContextGroup {
59 public: 67 public:
60 explicit ScopedNSAnimationContextGroup(bool animate) 68 explicit ScopedNSAnimationContextGroup(bool animate)
61 : animate_(animate) { 69 : animate_(animate) {
62 if (animate_) { 70 if (animate_) {
63 [NSAnimationContext beginGrouping]; 71 [NSAnimationContext beginGrouping];
64 } 72 }
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
105 - (void)layoutTabsWithAnimation:(BOOL)animate 113 - (void)layoutTabsWithAnimation:(BOOL)animate
106 regenerateSubviews:(BOOL)doUpdate; 114 regenerateSubviews:(BOOL)doUpdate;
107 - (void)animationDidStopForController:(TabController*)controller 115 - (void)animationDidStopForController:(TabController*)controller
108 finished:(BOOL)finished; 116 finished:(BOOL)finished;
109 - (NSInteger)indexFromModelIndex:(NSInteger)index; 117 - (NSInteger)indexFromModelIndex:(NSInteger)index;
110 - (NSInteger)numberOfOpenTabs; 118 - (NSInteger)numberOfOpenTabs;
111 - (NSInteger)numberOfOpenPinnedTabs; 119 - (NSInteger)numberOfOpenPinnedTabs;
112 - (NSInteger)numberOfOpenUnpinnedTabs; 120 - (NSInteger)numberOfOpenUnpinnedTabs;
113 - (void)mouseMoved:(NSEvent*)event; 121 - (void)mouseMoved:(NSEvent*)event;
114 - (void)setTabTrackingAreasEnabled:(BOOL)enabled; 122 - (void)setTabTrackingAreasEnabled:(BOOL)enabled;
123 - (void)droppingURLsAt:(NSPoint)location
124 givesIndex:(NSInteger*)index
125 disposition:(WindowOpenDisposition*)disposition;
115 @end 126 @end
116 127
117 // A simple view class that prevents the Window Server from dragging the area 128 // A simple view class that prevents the Window Server from dragging the area
118 // behind tabs. Sometimes core animation confuses it. Unfortunately, it can also 129 // behind tabs. Sometimes core animation confuses it. Unfortunately, it can also
119 // falsely pick up clicks during rapid tab closure, so we have to account for 130 // falsely pick up clicks during rapid tab closure, so we have to account for
120 // that. 131 // that.
121 @interface TabStripControllerDragBlockingView : NSView { 132 @interface TabStripControllerDragBlockingView : NSView {
122 TabStripController* controller_; // weak; owns us 133 TabStripController* controller_; // weak; owns us
123 } 134 }
124 135
(...skipping 157 matching lines...) Expand 10 before | Expand all | Expand 10 after
282 [newTabButton_ setAction:@selector(commandDispatch:)]; 293 [newTabButton_ setAction:@selector(commandDispatch:)];
283 [newTabButton_ setTag:IDC_NEW_TAB]; 294 [newTabButton_ setTag:IDC_NEW_TAB];
284 newTabTrackingArea_.reset( 295 newTabTrackingArea_.reset(
285 [[NSTrackingArea alloc] initWithRect:[newTabButton_ bounds] 296 [[NSTrackingArea alloc] initWithRect:[newTabButton_ bounds]
286 options:(NSTrackingMouseEnteredAndExited | 297 options:(NSTrackingMouseEnteredAndExited |
287 NSTrackingActiveAlways) 298 NSTrackingActiveAlways)
288 owner:self 299 owner:self
289 userInfo:nil]); 300 userInfo:nil]);
290 [newTabButton_ addTrackingArea:newTabTrackingArea_.get()]; 301 [newTabButton_ addTrackingArea:newTabTrackingArea_.get()];
291 targetFrames_.reset([[NSMutableDictionary alloc] init]); 302 targetFrames_.reset([[NSMutableDictionary alloc] init]);
303
292 dragBlockingView_.reset( 304 dragBlockingView_.reset(
293 [[TabStripControllerDragBlockingView alloc] initWithFrame:NSZeroRect 305 [[TabStripControllerDragBlockingView alloc] initWithFrame:NSZeroRect
294 controller:self]); 306 controller:self]);
295 [self addSubviewToPermanentList:dragBlockingView_]; 307 [self addSubviewToPermanentList:dragBlockingView_];
308
296 newTabTargetFrame_ = NSMakeRect(0, 0, 0, 0); 309 newTabTargetFrame_ = NSMakeRect(0, 0, 0, 0);
297 availableResizeWidth_ = kUseFullAvailableWidth; 310 availableResizeWidth_ = kUseFullAvailableWidth;
298 311
299 closingControllers_.reset([[NSMutableSet alloc] init]); 312 closingControllers_.reset([[NSMutableSet alloc] init]);
300 313
301 // Install the permanent subviews. 314 // Install the permanent subviews.
302 [self regenerateSubviewList]; 315 [self regenerateSubviewList];
303 316
304 // Watch for notifications that the tab strip view has changed size so 317 // Watch for notifications that the tab strip view has changed size so
305 // we can tell it to layout for the new size. 318 // we can tell it to layout for the new size.
(...skipping 12 matching lines...) Expand all
318 owner:self 331 owner:self
319 userInfo:nil]); 332 userInfo:nil]);
320 [tabView_ addTrackingArea:trackingArea_.get()]; 333 [tabView_ addTrackingArea:trackingArea_.get()];
321 } 334 }
322 return self; 335 return self;
323 } 336 }
324 337
325 - (void)dealloc { 338 - (void)dealloc {
326 if (trackingArea_.get()) 339 if (trackingArea_.get())
327 [tabView_ removeTrackingArea:trackingArea_.get()]; 340 [tabView_ removeTrackingArea:trackingArea_.get()];
341
328 [newTabButton_ removeTrackingArea:newTabTrackingArea_.get()]; 342 [newTabButton_ removeTrackingArea:newTabTrackingArea_.get()];
329 // Invalidate all closing animations so they don't call back to us after 343 // Invalidate all closing animations so they don't call back to us after
330 // we're gone. 344 // we're gone.
331 for (TabController* controller in closingControllers_.get()) { 345 for (TabController* controller in closingControllers_.get()) {
332 NSView* view = [controller view]; 346 NSView* view = [controller view];
333 [[[view animationForKey:@"frameOrigin"] delegate] invalidate]; 347 [[[view animationForKey:@"frameOrigin"] delegate] invalidate];
334 } 348 }
335 [[NSNotificationCenter defaultCenter] removeObserver:self]; 349 [[NSNotificationCenter defaultCenter] removeObserver:self];
336 [super dealloc]; 350 [super dealloc];
337 } 351 }
(...skipping 255 matching lines...) Expand 10 before | Expand all | Expand 10 after
593 // subclass of TabContentsController with everything stubbed out or by 607 // subclass of TabContentsController with everything stubbed out or by
594 // abstracting a base class interface. 608 // abstracting a base class interface.
595 // TODO(pinkerton): Note this doesn't do too well when the number of min-sized 609 // TODO(pinkerton): Note this doesn't do too well when the number of min-sized
596 // tabs would cause an overflow. 610 // tabs would cause an overflow.
597 - (void)layoutTabsWithAnimation:(BOOL)animate 611 - (void)layoutTabsWithAnimation:(BOOL)animate
598 regenerateSubviews:(BOOL)doUpdate { 612 regenerateSubviews:(BOOL)doUpdate {
599 DCHECK([NSThread isMainThread]); 613 DCHECK([NSThread isMainThread]);
600 if (![tabArray_ count]) 614 if (![tabArray_ count])
601 return; 615 return;
602 616
603 const CGFloat kTabOverlap = 20.0;
604 const CGFloat kNewTabButtonOffset = 8.0;
605 const CGFloat kMaxTabWidth = [TabController maxTabWidth]; 617 const CGFloat kMaxTabWidth = [TabController maxTabWidth];
606 const CGFloat kMinTabWidth = [TabController minTabWidth]; 618 const CGFloat kMinTabWidth = [TabController minTabWidth];
607 const CGFloat kMinSelectedTabWidth = [TabController minSelectedTabWidth]; 619 const CGFloat kMinSelectedTabWidth = [TabController minSelectedTabWidth];
608 const CGFloat kPinnedTabWidth = [TabController pinnedTabWidth]; 620 const CGFloat kPinnedTabWidth = [TabController pinnedTabWidth];
609 621
610 NSRect enclosingRect = NSZeroRect; 622 NSRect enclosingRect = NSZeroRect;
611 ScopedNSAnimationContextGroup mainAnimationGroup(animate); 623 ScopedNSAnimationContextGroup mainAnimationGroup(animate);
612 mainAnimationGroup.SetCurrentContextDuration(kAnimationDuration); 624 mainAnimationGroup.SetCurrentContextDuration(kAnimationDuration);
613 625
614 // Update the current subviews and their z-order if requested. 626 // Update the current subviews and their z-order if requested.
(...skipping 779 matching lines...) Expand 10 before | Expand all | Expand 10 after
1394 [subviews addObject:tabView]; 1406 [subviews addObject:tabView];
1395 } 1407 }
1396 } 1408 }
1397 if (selectedTabView) { 1409 if (selectedTabView) {
1398 [subviews addObject:selectedTabView]; 1410 [subviews addObject:selectedTabView];
1399 } 1411 }
1400 [tabView_ setSubviews:subviews]; 1412 [tabView_ setSubviews:subviews];
1401 [self setTabTrackingAreasEnabled:mouseInside_]; 1413 [self setTabTrackingAreasEnabled:mouseInside_];
1402 } 1414 }
1403 1415
1416 // Get the index and disposition for a potential URL(s) drop given a location
1417 // (in window base coordinates). It considers x coordinate of the given
1418 // location. If it's in the "middle" of a tab, it drops on that tab. If it's to
1419 // the left, it inserts to the left, and similarly for the right.
1420 - (void)droppingURLsAt:(NSPoint)location
1421 givesIndex:(NSInteger*)index
1422 disposition:(WindowOpenDisposition*)disposition {
1423 // Proportion of the tab which is considered the "middle" (and causes things
1424 // to drop on that tab).
1425 const double kMiddleProportion = 0.5;
1426 const double kLRProportion = (1.0 - kMiddleProportion) / 2.0;
1427
1428 DCHECK(index && disposition);
1429 NSInteger i = 0;
1430 for (TabController* tab in tabArray_.get()) {
1431 NSView* view = [tab view];
1432 DCHECK([view isKindOfClass:[TabView class]]);
1433
1434 // Recall that |-[NSView frame]| is in its superview's coordinates, so a
1435 // |TabView|'s frame is in the coordinates of the |TabStripView| (which is
1436 // confusingly called |tabView_|).
1437 NSRect frame = [tabView_ convertRectToBase:[view frame]];
1438
1439 // Modify the frame to make it "unoverlapped".
1440 frame.origin.x += kTabOverlap / 2.0;
1441 frame.size.width -= kTabOverlap;
1442 if (frame.size.width < 1.0)
1443 frame.size.width = 1.0; // try to avoid complete failure
1444
1445 // Drop in a new tab to the left of tab |i|?
1446 if (location.x < (frame.origin.x + kLRProportion * frame.size.width)) {
1447 *index = i;
1448 *disposition = NEW_FOREGROUND_TAB;
1449 return;
1450 }
1451
1452 // Drop on tab |i|?
1453 if (location.x <= (frame.origin.x +
1454 (1.0 - kLRProportion) * frame.size.width)) {
1455 *index = i;
1456 *disposition = CURRENT_TAB;
1457 return;
1458 }
1459
1460 // (Dropping in a new tab to the right of tab |i| will be taken care of in
1461 // the next iteration.)
1462 i++;
1463 }
1464
1465 // If we've made it here, we want to append a new tab to the end.
1466 *index = -1;
1467 *disposition = NEW_FOREGROUND_TAB;
1468 }
1469
1470 // Drop URLs at the given location.
1471 - (void)dropURLs:(NSArray*)urls at:(NSPoint)location {
1472 if ([urls count] < 1) {
1473 NOTREACHED();
1474 return;
1475 }
1476
1477 //TODO(viettrungluu): dropping multiple URLs.
1478 if ([urls count] > 1)
1479 NOTIMPLEMENTED();
1480
1481 // Get the first URL and fix it up.
1482 GURL url(URLFixerUpper::FixupURL(
1483 base::SysNSStringToUTF8([urls objectAtIndex:0]), std::string()));
1484
1485 // Get the index and disposition.
1486 NSInteger index;
1487 WindowOpenDisposition disposition;
1488 [self droppingURLsAt:location
1489 givesIndex:&index
1490 disposition:&disposition];
1491
1492 // Either insert a new tab or open in a current tab.
1493 switch (disposition) {
1494 case NEW_FOREGROUND_TAB:
1495 browser_->AddTabWithURL(url, GURL(), PageTransition::TYPED, true, index,
1496 true, NULL);
1497 break;
1498 case CURRENT_TAB:
1499 tabModel_->GetTabContentsAt(index)->OpenURL(url, GURL(), CURRENT_TAB,
1500 PageTransition::TYPED);
1501 tabModel_->SelectTabContentsAt(index, true);
1502 break;
1503 default:
1504 NOTIMPLEMENTED();
1505 }
1506 }
1507
1508 // Update (possibly showing) the indicator which indicates where an URL drop
1509 // would happen.
1510 - (void)indicateDropURLsAt:(NSPoint)location {
1511 // TODO(viettrungluu): Make the tabs move around, show an indicator, etc. This
1512 // requires a re-work of the tabs code....
1513 #if 0
1514 NSInteger index;
1515 WindowOpenDisposition disposition;
1516 [self droppingURLsAt:location
1517 givesIndex:&index
1518 disposition:&disposition];
1519
1520 if (index == -1) {
1521 // Append a tab at the end.
1522 DCHECK(disposition == NEW_FOREGROUND_TAB);
1523 // TODO(viettrungluu): ...
1524 } else {
1525 NSRect overRect = [[[tabArray_ objectAtIndex:index] view] frame];
1526 switch (disposition) {
1527 case NEW_FOREGROUND_TAB:
1528 // Insert tab (to the left of the given tab).
1529 // TODO(viettrungluu): ...
1530 break;
1531 case CURRENT_TAB:
1532 // Overwrite the given tab.
1533 // TODO(viettrungluu): ...
1534 break;
1535 default:
1536 NOTREACHED();
1537 }
1538 }
1539
1540 // TODO(viettrungluu): ...
1541 #endif
1542 }
1543
1544 // Hide the indicator which indicates where an URL drop would happen.
1545 - (void)hideDropURLsIndicator {
1546 // TODO(viettrungluu): See TODO in |-indicateDropURLsAt:| above.
1547 }
1548
1404 - (GTMWindowSheetController*)sheetController { 1549 - (GTMWindowSheetController*)sheetController {
1405 if (!sheetController_.get()) 1550 if (!sheetController_.get())
1406 sheetController_.reset([[GTMWindowSheetController alloc] 1551 sheetController_.reset([[GTMWindowSheetController alloc]
1407 initWithWindow:[switchView_ window] delegate:self]); 1552 initWithWindow:[switchView_ window] delegate:self]);
1408 return sheetController_.get(); 1553 return sheetController_.get();
1409 } 1554 }
1410 1555
1411 - (void)gtm_systemRequestsVisibilityForView:(NSView*)view { 1556 - (void)gtm_systemRequestsVisibilityForView:(NSView*)view {
1412 // This implementation is required by GTMWindowSheetController. 1557 // This implementation is required by GTMWindowSheetController.
1413 1558
(...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after
1496 // Show next sheet 1641 // Show next sheet
1497 NSWindowController* controller = [[tab window] windowController]; 1642 NSWindowController* controller = [[tab window] windowController];
1498 DCHECK([controller isKindOfClass:[BrowserWindowController class]]); 1643 DCHECK([controller isKindOfClass:[BrowserWindowController class]]);
1499 windows.front()->Realize( 1644 windows.front()->Realize(
1500 static_cast<BrowserWindowController*>(controller)); 1645 static_cast<BrowserWindowController*>(controller));
1501 } 1646 }
1502 } 1647 }
1503 } 1648 }
1504 1649
1505 @end 1650 @end
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698