 Chromium Code Reviews
 Chromium Code Reviews Issue 5694001:
  Rework how bookmark bar folder menus and submenus are layed out when scrollin...  (Closed) 
  Base URL: svn://svn.chromium.org/chrome/trunk/src/
    
  
    Issue 5694001:
  Rework how bookmark bar folder menus and submenus are layed out when scrollin...  (Closed) 
  Base URL: svn://svn.chromium.org/chrome/trunk/src/| OLD | NEW | 
|---|---|
| 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 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 | 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/ui/cocoa/bookmarks/bookmark_bar_folder_controller.h" | 5 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_folder_controller.h" | 
| 6 | 6 | 
| 7 #include "app/mac/nsimage_cache.h" | |
| 8 #include "base/mac/mac_util.h" | 7 #include "base/mac/mac_util.h" | 
| 9 #include "base/sys_string_conversions.h" | 8 #include "base/sys_string_conversions.h" | 
| 10 #include "chrome/browser/bookmarks/bookmark_model.h" | 9 #include "chrome/browser/bookmarks/bookmark_model.h" | 
| 11 #include "chrome/browser/bookmarks/bookmark_utils.h" | 10 #include "chrome/browser/bookmarks/bookmark_utils.h" | 
| 12 #import "chrome/browser/themes/browser_theme_provider.h" | 11 #import "chrome/browser/themes/browser_theme_provider.h" | 
| 13 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_constants.h" | 12 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_constants.h" | 
| 14 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_controller.h" | 13 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_controller.h" | 
| 15 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_folder_button_cell.h" | 14 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_folder_button_cell.h" | 
| 16 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_folder_hover_state.h" | 15 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_folder_hover_state.h" | 
| 17 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_folder_view.h" | 16 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_folder_view.h" | 
| 17 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_folder_window.h" | |
| 18 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_folder_target.h" | 18 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_folder_target.h" | 
| 19 #import "chrome/browser/ui/cocoa/browser_window_controller.h" | 19 #import "chrome/browser/ui/cocoa/browser_window_controller.h" | 
| 20 #import "chrome/browser/ui/cocoa/event_utils.h" | 20 #import "chrome/browser/ui/cocoa/event_utils.h" | 
| 21 | 21 | 
| 22 using bookmarks::kBookmarkBarMenuCornerRadius; | |
| 23 | |
| 22 namespace { | 24 namespace { | 
| 23 | 25 | 
| 24 // Frequency of the scrolling timer in seconds. | 26 // Frequency of the scrolling timer in seconds. | 
| 25 const NSTimeInterval kBookmarkBarFolderScrollInterval = 0.1; | 27 const NSTimeInterval kBookmarkBarFolderScrollInterval = 0.1; | 
| 26 | 28 | 
| 27 // Amount to scroll by per timer fire. We scroll rather slowly; to | 29 // Amount to scroll by per timer fire. We scroll rather slowly; to | 
| 28 // accomodate we do several at a time. | 30 // accomodate we do several at a time. | 
| 29 const CGFloat kBookmarkBarFolderScrollAmount = | 31 const CGFloat kBookmarkBarFolderScrollAmount = | 
| 30 3 * bookmarks::kBookmarkButtonVerticalSpan; | 32 3 * bookmarks::kBookmarkButtonVerticalSpan; | 
| 31 | 33 | 
| 32 // Amount to scroll for each scroll wheel delta. | 34 // Amount to scroll for each scroll wheel roll. | 
| 33 const CGFloat kBookmarkBarFolderScrollWheelAmount = | 35 const CGFloat kBookmarkBarFolderScrollWheelAmount = | 
| 34 1 * bookmarks::kBookmarkButtonVerticalSpan; | 36 1 * bookmarks::kBookmarkButtonVerticalSpan; | 
| 35 | 37 | 
| 36 // When constraining a scrolling bookmark bar folder window to the | 38 // Determining adjustments to the layout of the folder menu window in response | 
| 37 // screen, shrink the "constrain" by this much vertically. Currently | 39 // to resizing and scrolling relies on many visual factors. The following | 
| 38 // this is 0.0 to avoid a problem with tracking areas leaving the | 40 // struct is used to pass around these factors to the several support | 
| 39 // window, but should probably be 8.0 or something. | 41 // functions involved in the adjustment calculations and application. | 
| 40 // TODO(jrg): http://crbug.com/36225 | 42 struct LayoutMetrics { | 
| 41 const CGFloat kScrollWindowVerticalMargin = 0.0; | 43 // Metrics applied during the final layout adjustments to the window, | 
| 44 // the main visible content view, and the menu content view (i.e. the | |
| 45 // scroll view). | |
| 46 CGFloat windowLeft; | |
| 47 NSSize windowSize; | |
| 48 CGFloat scrollDelta; | |
| 49 NSRect windowFrame; | |
| 50 NSRect visibleFrame; | |
| 51 NSRect scrollerFrame; | |
| 52 NSPoint scrollPoint; | |
| 53 BOOL couldScrollUp; | |
| 
John Grabowski
2011/01/11 02:34:20
difference between couldScrollUp and canScrollUp?
 
mrossetti
2011/01/11 04:12:18
Added comments, thanks.
 | |
| 54 BOOL canScrollUp; | |
| 55 BOOL couldScrollDown; | |
| 56 BOOL canScrollDown; | |
| 57 BOOL preScroll; | |
| 58 | |
| 59 // Intermediate metrics used in determining window vertical layout changes. | |
| 60 CGFloat deltaWindowHeight; | |
| 61 CGFloat deltaWindowY; | |
| 62 CGFloat deltaVisibleHeight; | |
| 63 CGFloat deltaVisibleY; | |
| 64 CGFloat deltaScrollerHeight; | |
| 65 CGFloat deltaScrollerY; | |
| 66 | |
| 67 // Convenience metrics used in multiple functions (carried along here in | |
| 68 // order to eliminate the need to calculate in multiple places and | |
| 69 // reduce the possibility of bugs). | |
| 70 CGFloat minimumY; | |
| 71 CGFloat oldWindowY; | |
| 72 CGFloat folderY; | |
| 73 CGFloat folderTop; | |
| 74 | |
| 75 LayoutMetrics(CGFloat windowLeft, NSSize windowSize, CGFloat scrollDelta) : | |
| 76 windowLeft(windowLeft), | |
| 77 windowSize(windowSize), | |
| 78 scrollDelta(scrollDelta), | |
| 79 couldScrollUp(NO), | |
| 80 canScrollUp(NO), | |
| 81 couldScrollDown(NO), | |
| 82 canScrollDown(NO), | |
| 83 preScroll(NO), | |
| 84 deltaWindowHeight(0.0), | |
| 85 deltaWindowY(0.0), | |
| 86 deltaVisibleHeight(0.0), | |
| 87 deltaVisibleY(0.0), | |
| 88 deltaScrollerHeight(0.0), | |
| 89 deltaScrollerY(0.0), | |
| 90 oldWindowY(0.0), | |
| 91 folderY(0.0), | |
| 92 folderTop(0.0) {} | |
| 93 }; | |
| 42 | 94 | 
| 43 } // namespace | 95 } // namespace | 
| 44 | 96 | 
| 45 @interface BookmarkBarFolderController(Private) | 97 @interface BookmarkBarFolderController(Private) | 
| 46 - (void)configureWindow; | 98 - (void)configureWindow; | 
| 47 - (void)addOrUpdateScrollTracking; | 99 - (void)addOrUpdateScrollTracking; | 
| 48 - (void)removeScrollTracking; | 100 - (void)removeScrollTracking; | 
| 49 - (void)endScroll; | 101 - (void)endScroll; | 
| 50 - (void)addScrollTimerWithDelta:(CGFloat)delta; | 102 - (void)addScrollTimerWithDelta:(CGFloat)delta; | 
| 51 | 103 | 
| 104 // Helper function to configureWindow which performs a basic layout of | |
| 105 // the window subviews, in particular the menu buttons and the window width. | |
| 106 - (void)layOutWindowWithHeight:(CGFloat)height; | |
| 107 | |
| 52 // Determine the best button width (which will be the widest button or the | 108 // Determine the best button width (which will be the widest button or the | 
| 53 // maximum allowable button width, whichever is less) and resize all buttons. | 109 // maximum allowable button width, whichever is less) and resize all buttons. | 
| 54 // Return the new width (so that the window can be adjusted, if necessary). | 110 // Return the new width so that the window can be adjusted. | 
| 55 - (CGFloat)adjustButtonWidths; | 111 - (CGFloat)adjustButtonWidths; | 
| 56 | 112 | 
| 57 // Returns the total menu height needed to display |buttonCount| buttons. | 113 // Returns the total menu height needed to display |buttonCount| buttons. | 
| 58 // Does not do any fancy tricks like trimming the height to fit on the screen. | 114 // Does not do any fancy tricks like trimming the height to fit on the screen. | 
| 59 - (int)windowHeightForButtonCount:(int)buttonCount; | 115 - (int)menuHeightForButtonCount:(int)buttonCount; | 
| 60 | 116 | 
| 61 // Adjust the height and horizontal position of the window such that the | 117 // Adjust layout of the folder menu window components, showing/hiding the | 
| 62 // scroll arrows are shown as needed and the window appears completely | 118 // scroll up/down arrows, and resizing as necessary for a proper disaplay. | 
| 63 // on screen. | 119 // In order to reduce window flicker, all layout changes are deferred until | 
| 64 - (void)adjustWindowForHeight:(int)windowHeight; | 120 // the final step of the adjustment. To accommodate this deferral, window | 
| 121 // height and width changes needed by callers to this function pass their | |
| 122 // desired window changes in |size|. When scrolling is to be performed | |
| 123 // any scrolling change is given by |scrollDelta|. The ultimate amount of | |
| 124 // scrolling may be different from |scrollDelta| in order to accommodate | |
| 125 // changes in the scroller view layout. | |
| 126 // | |
| 127 // This function should be called when: 1) initially setting up a folder menu | |
| 128 // window, 2) responding to scrolling of the contents (which may affect the | |
| 129 // height of the window), 3) addition or removal of bookmark items (such as | |
| 130 // during cut/paste/delete/drag/drop operations). | |
| 131 - (void)adjustWindowLeft:(CGFloat)windowLeft | |
| 132 size:(NSSize)windowSize | |
| 133 scrollingBy:(CGFloat)scrollDelta; | |
| 65 | 134 | 
| 66 // Show or hide the scroll arrows at the top/bottom of the window. | 135 // Support function for adjustWindowLeft:size:scrollingBy: which initializes | 
| 
John Grabowski
2011/01/11 02:34:20
I believe C++ style guide says args which get modi
 
mrossetti
2011/01/11 04:12:18
Done.
 | |
| 67 - (void)showOrHideScrollArrows; | 136 // the layout adjustments by gathering current folder menu window and subviews | 
| 137 // positions and sizes. | |
| 138 - (void)gatherMetrics:(LayoutMetrics&)metrics; | |
| 139 | |
| 140 // Support function for adjustWindowLeft:size:scrollingBy: which calculates | |
| 141 // the changes which must be applied to the folder menu window and subviews | |
| 142 // positions and sizes. | |
| 143 - (void)adjustMetrics:(LayoutMetrics&)metrics; | |
| 
John Grabowski
2011/01/11 02:34:20
Normally I'd expect an "adjust metrics BECAUSE OF
 
mrossetti
2011/01/11 04:12:18
I added some comments to this and several other fu
 | |
| 144 | |
| 145 // Support function for adjustMetrics: which calculates the layout changes | |
| 146 // required to accommodate changes in the position and scrollability | |
| 147 // of the top of the folder menu window. | |
| 148 - (void)adjustMetricsForMenuTopChanges:(LayoutMetrics&)metrics; | |
| 149 | |
| 150 // Support function for adjustMetrics: which calculates the layout changes | |
| 151 // required to accommodate changes in the position and scrollability | |
| 152 // of the bottom of the folder menu window. | |
| 153 - (void)adjustMetricsForMenuBottomChanges:(LayoutMetrics&)metrics; | |
| 154 | |
| 155 // Support function for adjustWindowLeft:size:scrollingBy: which applies | |
| 156 // the layout adjustments to the folder menu window and subviews. | |
| 157 - (void)applyMetrics:(LayoutMetrics&)metrics; | |
| 158 | |
| 159 // This function is called when buttons are added or removed from the folder | |
| 160 // menu, and which may require a change in the layout of the folder menu | |
| 161 // window. Such layout changes may include horizontal placement, width, | |
| 162 // height, and scroller visibility changes. (This function calls through | |
| 163 // to -[adjustWindowLeft:size:scrollingBy:].) | |
| 164 // |buttonCount| should contain the updated count of menu buttons. | |
| 165 - (void)adjustWindowForButtonCount:(NSUInteger)buttonCount; | |
| 166 | |
| 167 // A helper function which takes the desired amount to scroll, given by | |
| 168 // |scrollDelta|, and calculates the actual scrolling change to be applied | |
| 169 // taking into account the layout of the folder menu window and any | |
| 170 // changes in it's scrollability. (For example, when scrolling down and the | |
| 171 // top-most menu item is coming into view we will only scroll enough for | |
| 172 // that item to be completely presented, which may be less than the | |
| 173 // scroll amount requested.) | |
| 174 - (CGFloat)determineFinalScrollDelta:(CGFloat)scrollDelta; | |
| 68 | 175 | 
| 69 // |point| is in the base coordinate system of the destination window; | 176 // |point| is in the base coordinate system of the destination window; | 
| 70 // it comes from an id<NSDraggingInfo>. |copy| is YES if a copy is to be | 177 // it comes from an id<NSDraggingInfo>. |copy| is YES if a copy is to be | 
| 71 // made and inserted into the new location while leaving the bookmark in | 178 // made and inserted into the new location while leaving the bookmark in | 
| 72 // the old location, otherwise move the bookmark by removing from its old | 179 // the old location, otherwise move the bookmark by removing from its old | 
| 73 // location and inserting into the new location. | 180 // location and inserting into the new location. | 
| 74 - (BOOL)dragBookmark:(const BookmarkNode*)sourceNode | 181 - (BOOL)dragBookmark:(const BookmarkNode*)sourceNode | 
| 75 to:(NSPoint)point | 182 to:(NSPoint)point | 
| 76 copy:(BOOL)copy; | 183 copy:(BOOL)copy; | 
| 77 | 184 | 
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 124 | 231 | 
| 125 parentController_.reset([parentController retain]); | 232 parentController_.reset([parentController retain]); | 
| 126 if (!parentController_) | 233 if (!parentController_) | 
| 127 [self setSubFolderGrowthToRight:YES]; | 234 [self setSubFolderGrowthToRight:YES]; | 
| 128 else | 235 else | 
| 129 [self setSubFolderGrowthToRight:[parentController | 236 [self setSubFolderGrowthToRight:[parentController | 
| 130 subFolderGrowthToRight]]; | 237 subFolderGrowthToRight]]; | 
| 131 barController_ = barController; // WEAK | 238 barController_ = barController; // WEAK | 
| 132 buttons_.reset([[NSMutableArray alloc] init]); | 239 buttons_.reset([[NSMutableArray alloc] init]); | 
| 133 folderTarget_.reset([[BookmarkFolderTarget alloc] initWithController:self]); | 240 folderTarget_.reset([[BookmarkFolderTarget alloc] initWithController:self]); | 
| 134 NSImage* image = app::mac::GetCachedImageWithName(@"menu_overflow_up.pdf"); | |
| 135 DCHECK(image); | |
| 136 verticalScrollArrowHeight_ = [image size].height; | |
| 137 [self configureWindow]; | 241 [self configureWindow]; | 
| 138 hoverState_.reset([[BookmarkBarFolderHoverState alloc] init]); | 242 hoverState_.reset([[BookmarkBarFolderHoverState alloc] init]); | 
| 139 } | 243 } | 
| 140 return self; | 244 return self; | 
| 141 } | 245 } | 
| 142 | 246 | 
| 143 - (void)dealloc { | 247 - (void)dealloc { | 
| 144 // The button is no longer part of the menu path. | 248 // The button is no longer part of the menu path. | 
| 145 [parentButton_ forceButtonBorderToStayOnAlways:NO]; | 249 [parentButton_ forceButtonBorderToStayOnAlways:NO]; | 
| 146 [parentButton_ setNeedsDisplay]; | 250 [parentButton_ setNeedsDisplay]; | 
| 147 | 251 | 
| 148 [self removeScrollTracking]; | 252 [self removeScrollTracking]; | 
| 149 [self endScroll]; | 253 [self endScroll]; | 
| 150 [hoverState_ draggingExited]; | 254 [hoverState_ draggingExited]; | 
| 151 | 255 | 
| 152 // Delegate pattern does not retain; make sure pointers to us are removed. | 256 // Delegate pattern does not retain; make sure pointers to us are removed. | 
| 153 for (BookmarkButton* button in buttons_.get()) { | 257 for (BookmarkButton* button in buttons_.get()) { | 
| 154 [button setDelegate:nil]; | 258 [button setDelegate:nil]; | 
| 155 [button setTarget:nil]; | 259 [button setTarget:nil]; | 
| 156 [button setAction:nil]; | 260 [button setAction:nil]; | 
| 157 } | 261 } | 
| 158 | 262 | 
| 159 // Note: we don't need to | 263 // Note: we don't need to | 
| 160 // [NSObject cancelPreviousPerformRequestsWithTarget:self]; | 264 // [NSObject cancelPreviousPerformRequestsWithTarget:self]; | 
| 161 // Because all of our performSelector: calls use withDelay: which | 265 // Because all of our performSelector: calls use withDelay: which | 
| 162 // retains us. | 266 // retains us. | 
| 163 [super dealloc]; | 267 [super dealloc]; | 
| 164 } | 268 } | 
| 165 | 269 | 
| 270 - (void)awakeFromNib { | |
| 271 NSRect windowFrame = [[self window] frame]; | |
| 272 NSRect scrollViewFrame = [scrollView_ frame]; | |
| 273 padding_ = NSWidth(windowFrame) - NSWidth(scrollViewFrame); | |
| 274 verticalScrollArrowHeight_ = NSHeight([scrollUpArrowView_ frame]); | |
| 275 } | |
| 276 | |
| 166 // Overriden from NSWindowController to call childFolderWillShow: before showing | 277 // Overriden from NSWindowController to call childFolderWillShow: before showing | 
| 167 // the window. | 278 // the window. | 
| 168 - (void)showWindow:(id)sender { | 279 - (void)showWindow:(id)sender { | 
| 169 [barController_ childFolderWillShow:self]; | 280 [barController_ childFolderWillShow:self]; | 
| 170 [super showWindow:sender]; | 281 [super showWindow:sender]; | 
| 171 } | 282 } | 
| 172 | 283 | 
| 173 - (BookmarkButton*)parentButton { | 284 - (BookmarkButton*)parentButton { | 
| 174 return parentButton_.get(); | 285 return parentButton_.get(); | 
| 175 } | 286 } | 
| (...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 269 urlString.c_str()]; | 380 urlString.c_str()]; | 
| 270 [button setToolTip:tooltip]; | 381 [button setToolTip:tooltip]; | 
| 271 } | 382 } | 
| 272 } else { | 383 } else { | 
| 273 [button setEnabled:NO]; | 384 [button setEnabled:NO]; | 
| 274 [button setBordered:NO]; | 385 [button setBordered:NO]; | 
| 275 } | 386 } | 
| 276 return button; | 387 return button; | 
| 277 } | 388 } | 
| 278 | 389 | 
| 279 // Exposed for testing. | |
| 280 - (NSView*)mainView { | |
| 281 return mainView_; | |
| 282 } | |
| 283 | |
| 284 - (id)folderTarget { | 390 - (id)folderTarget { | 
| 285 return folderTarget_.get(); | 391 return folderTarget_.get(); | 
| 286 } | 392 } | 
| 287 | 393 | 
| 288 | 394 | 
| 289 // Our parent controller is another BookmarkBarFolderController, so | 395 // Our parent controller is another BookmarkBarFolderController, so | 
| 290 // our window is to the right or left of it. We use a little overlap | 396 // our window is to the right or left of it. We use a little overlap | 
| 291 // since it looks much more menu-like than with none. If we would | 397 // since it looks much more menu-like than with none. If we would | 
| 292 // grow off the screen, switch growth to the other direction. Growth | 398 // grow off the screen, switch growth to the other direction. Growth | 
| 293 // direction sticks for folder windows which are descendents of us. | 399 // direction sticks for folder windows which are descendents of us. | 
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 347 // is bottom of button's parent view. | 453 // is bottom of button's parent view. | 
| 348 NSPoint buttonBottomLeftInScreen = | 454 NSPoint buttonBottomLeftInScreen = | 
| 349 [[parentButton_ window] | 455 [[parentButton_ window] | 
| 350 convertBaseToScreen:[parentButton_ | 456 convertBaseToScreen:[parentButton_ | 
| 351 convertPoint:NSZeroPoint toView:nil]]; | 457 convertPoint:NSZeroPoint toView:nil]]; | 
| 352 NSPoint bookmarkBarBottomLeftInScreen = | 458 NSPoint bookmarkBarBottomLeftInScreen = | 
| 353 [[parentButton_ window] | 459 [[parentButton_ window] | 
| 354 convertBaseToScreen:[[parentButton_ superview] | 460 convertBaseToScreen:[[parentButton_ superview] | 
| 355 convertPoint:NSZeroPoint toView:nil]]; | 461 convertPoint:NSZeroPoint toView:nil]]; | 
| 356 newWindowTopLeft = NSMakePoint(buttonBottomLeftInScreen.x, | 462 newWindowTopLeft = NSMakePoint(buttonBottomLeftInScreen.x, | 
| 357 bookmarkBarBottomLeftInScreen.y); | 463 bookmarkBarBottomLeftInScreen.y + | 
| 464 bookmarks::kBookmarkBarMenuOffset); | |
| 358 // Make sure the window is on-screen; if not, push left. It is | 465 // Make sure the window is on-screen; if not, push left. It is | 
| 359 // intentional that top level folders "push left" slightly | 466 // intentional that top level folders "push left" slightly | 
| 360 // different than subfolders. | 467 // different than subfolders. | 
| 361 NSRect screenFrame = [[[parentButton_ window] screen] frame]; | 468 NSRect screenFrame = [[[parentButton_ window] screen] frame]; | 
| 362 CGFloat spillOff = (newWindowTopLeft.x + windowWidth) - NSMaxX(screenFrame); | 469 CGFloat spillOff = (newWindowTopLeft.x + windowWidth) - NSMaxX(screenFrame); | 
| 363 if (spillOff > 0.0) { | 470 if (spillOff > 0.0) { | 
| 364 newWindowTopLeft.x = std::max(newWindowTopLeft.x - spillOff, | 471 newWindowTopLeft.x = std::max(newWindowTopLeft.x - spillOff, | 
| 365 NSMinX(screenFrame)); | 472 NSMinX(screenFrame)); | 
| 366 } | 473 } | 
| 367 } else { | 474 } else { | 
| 368 // Parent is a folder; grow right/left. | 475 // Parent is a folder; grow right/left. | 
| 369 newWindowTopLeft.x = [self childFolderWindowLeftForWidth:windowWidth]; | 476 newWindowTopLeft.x = [self childFolderWindowLeftForWidth:windowWidth]; | 
| 370 NSPoint top = NSMakePoint(0, (NSMaxY([parentButton_ frame]) + | 477 NSPoint topOfWindow = NSMakePoint(0, | 
| 371 bookmarks::kBookmarkVerticalPadding)); | 478 (NSMaxY([parentButton_ frame]) + | 
| 372 NSPoint topOfWindow = | 479 bookmarks::kBookmarkVerticalPadding)); | 
| 373 [[parentButton_ window] | 480 topOfWindow = [[parentButton_ window] | 
| 374 convertBaseToScreen:[[parentButton_ superview] | 481 convertBaseToScreen:[[parentButton_ superview] | 
| 375 convertPoint:top toView:nil]]; | 482 convertPoint:topOfWindow toView:nil]]; | 
| 376 newWindowTopLeft.y = topOfWindow.y; | 483 newWindowTopLeft.y = topOfWindow.y; | 
| 377 } | 484 } | 
| 378 return newWindowTopLeft; | 485 return newWindowTopLeft; | 
| 379 } | 486 } | 
| 380 | 487 | 
| 381 // Set our window level to the right spot so we're above the menubar, dock, etc. | 488 // Set our window level to the right spot so we're above the menubar, dock, etc. | 
| 382 // Factored out so we can override/noop in a unit test. | 489 // Factored out so we can override/noop in a unit test. | 
| 383 - (void)configureWindowLevel { | 490 - (void)configureWindowLevel { | 
| 384 [[self window] setLevel:NSPopUpMenuWindowLevel]; | 491 [[self window] setLevel:NSPopUpMenuWindowLevel]; | 
| 385 } | 492 } | 
| 386 | 493 | 
| 387 - (int)windowHeightForButtonCount:(int)buttonCount { | 494 - (int)menuHeightForButtonCount:(int)buttonCount { | 
| 495 // This does not take into account any padding which may be required at the | |
| 496 // top and/or bottom of the window. | |
| 388 return (buttonCount * bookmarks::kBookmarkButtonVerticalSpan) + | 497 return (buttonCount * bookmarks::kBookmarkButtonVerticalSpan) + | 
| 389 bookmarks::kBookmarkVerticalPadding; | 498 bookmarks::kBookmarkVerticalPadding; | 
| 390 } | 499 } | 
| 391 | 500 | 
| 392 - (void)adjustWindowForHeight:(int)windowHeight { | 501 - (void)adjustWindowLeft:(CGFloat)windowLeft | 
| 393 // Adjust all button widths to be consistent, determine the best size for | 502 size:(NSSize)windowSize | 
| 394 // the window, and set the window frame. | 503 scrollingBy:(CGFloat)scrollDelta { | 
| 395 CGFloat windowWidth = | 504 // Callers of this function should make adjustments to the vertical | 
| 396 [self adjustButtonWidths] + | 505 // attributes of the folder view only (height, scroll position). | 
| 397 (2 * bookmarks::kBookmarkSubMenuHorizontalPadding); | 506 // This function will then make appropriate layout adjustments in order | 
| 507 // to accommodate screen/dock margins, scroll-up and scroll-down arrow | |
| 508 // presentation, etc. | |
| 509 // The 4 views whose vertical height and origins may be adjusted | |
| 510 // by this function are: | |
| 511 // 1) window, 2) visible content view, 3) scroller view, 4) folder view. | |
| 512 | |
| 513 LayoutMetrics metrics(windowLeft, windowSize, scrollDelta); | |
| 
John Grabowski
2011/01/11 02:34:20
I like this a lot better
 
mrossetti
2011/01/11 04:12:18
Delighted!
 | |
| 514 [self gatherMetrics:metrics]; | |
| 515 [self adjustMetrics:metrics]; | |
| 516 [self applyMetrics:metrics]; | |
| 517 } | |
| 518 | |
| 519 - (void)gatherMetrics:(LayoutMetrics&)metrics { | |
| 520 NSWindow* window = [self window]; | |
| 521 metrics.windowFrame = [window frame]; | |
| 522 metrics.visibleFrame = [visibleView_ frame]; | |
| 523 metrics.scrollerFrame = [scrollView_ frame]; | |
| 524 metrics.scrollPoint = [scrollView_ documentVisibleRect].origin; | |
| 525 metrics.scrollPoint.y -= metrics.scrollDelta; | |
| 526 metrics.couldScrollUp = ![scrollUpArrowView_ isHidden]; | |
| 527 metrics.couldScrollDown = ![scrollDownArrowView_ isHidden]; | |
| 528 | |
| 529 metrics.deltaWindowHeight = 0.0; | |
| 530 metrics.deltaWindowY = 0.0; | |
| 531 metrics.deltaVisibleHeight = 0.0; | |
| 532 metrics.deltaVisibleY = 0.0; | |
| 533 metrics.deltaScrollerHeight = 0.0; | |
| 534 metrics.deltaScrollerY = 0.0; | |
| 535 | |
| 536 metrics.minimumY = NSMinY([[window screen] visibleFrame]) + | |
| 537 bookmarks::kScrollWindowVerticalMargin; | |
| 538 metrics.oldWindowY = NSMinY(metrics.windowFrame); | |
| 539 metrics.folderY = | |
| 540 metrics.scrollerFrame.origin.y + metrics.visibleFrame.origin.y + | |
| 541 metrics.oldWindowY - metrics.scrollPoint.y; | |
| 542 metrics.folderTop = metrics.folderY + NSHeight([folderView_ frame]); | |
| 543 } | |
| 544 | |
| 545 - (void)adjustMetrics:(LayoutMetrics&)metrics { | |
| 546 NSScreen* screen = [[self window] screen]; | |
| 547 CGFloat effectiveFolderY = metrics.folderY; | |
| 548 if (!metrics.couldScrollUp && !metrics.couldScrollDown) | |
| 549 effectiveFolderY -= metrics.windowSize.height; | |
| 550 metrics.canScrollUp = effectiveFolderY < metrics.minimumY; | |
| 551 CGFloat maximumY = | |
| 552 NSMaxY([screen frame]) - bookmarks::kScrollWindowVerticalMargin; | |
| 553 metrics.canScrollDown = metrics.folderTop > maximumY; | |
| 554 | |
| 555 // Accommodate changes in the bottom of the menu. | |
| 556 [self adjustMetricsForMenuTopChanges:metrics]; | |
| 557 | |
| 558 // Accommodate changes in the top of the menu. | |
| 559 [self adjustMetricsForMenuBottomChanges:metrics]; | |
| 560 | |
| 561 metrics.scrollerFrame.origin.y += metrics.deltaScrollerY; | |
| 562 metrics.scrollerFrame.size.height += metrics.deltaScrollerHeight; | |
| 563 metrics.visibleFrame.origin.y += metrics.deltaVisibleY; | |
| 564 metrics.visibleFrame.size.height += metrics.deltaVisibleHeight; | |
| 565 metrics.preScroll = metrics.canScrollUp && !metrics.couldScrollUp && | |
| 566 metrics.scrollDelta == 0.0 && metrics.deltaWindowHeight >= 0.0; | |
| 567 metrics.windowFrame.origin.y += metrics.deltaWindowY; | |
| 568 metrics.windowFrame.origin.x = metrics.windowLeft; | |
| 569 metrics.windowFrame.size.height += metrics.deltaWindowHeight; | |
| 570 metrics.windowFrame.size.width = metrics.windowSize.width; | |
| 571 } | |
| 572 | |
| 573 - (void)adjustMetricsForMenuTopChanges:(LayoutMetrics&)metrics { | |
| 574 if (metrics.canScrollUp) { | |
| 575 if (!metrics.couldScrollUp) { | |
| 576 // Couldn't -> Can | |
| 577 metrics.deltaWindowY = -metrics.oldWindowY; | |
| 578 metrics.deltaWindowHeight = -metrics.deltaWindowY; | |
| 579 metrics.deltaVisibleY = metrics.minimumY; | |
| 580 metrics.deltaVisibleHeight = -metrics.deltaVisibleY; | |
| 581 metrics.deltaScrollerY = verticalScrollArrowHeight_; | |
| 582 metrics.deltaScrollerHeight = -metrics.deltaScrollerY; | |
| 583 // Adjust the scroll delta if we've grown the window and it is | |
| 584 // now scroll-up-able, but don't adjust it factor if we've | |
| 585 // scrolled down and it wasn't scroll-up-able but now is. | |
| 586 if (metrics.canScrollDown == metrics.couldScrollDown) { | |
| 587 CGFloat deltaScroll = metrics.deltaWindowY + metrics.deltaScrollerY + | |
| 588 metrics.deltaVisibleY; | |
| 589 metrics.scrollPoint.y += deltaScroll + metrics.windowSize.height; | |
| 590 } | |
| 591 } else if (!metrics.canScrollDown && metrics.windowSize.height > 0.0) { | |
| 592 metrics.scrollPoint.y += metrics.windowSize.height; | |
| 593 } | |
| 594 } else { | |
| 595 if (metrics.couldScrollUp) { | |
| 596 // Could -> Can't | |
| 597 metrics.deltaWindowY = metrics.folderY - metrics.oldWindowY; | |
| 598 metrics.deltaWindowHeight = -metrics.deltaWindowY; | |
| 599 metrics.deltaVisibleY = -bookmarks::kScrollWindowVerticalMargin; | |
| 600 metrics.deltaVisibleHeight = -metrics.deltaVisibleY; | |
| 601 metrics.deltaScrollerY = -verticalScrollArrowHeight_; | |
| 602 metrics.deltaScrollerHeight = -metrics.deltaScrollerY; | |
| 603 // Adjust the scroll delta if we are no longer scroll-up-able | |
| 604 // and the scroll-down-able-ness hasn't changed. | |
| 605 if (metrics.canScrollDown == metrics.couldScrollDown) { | |
| 606 CGFloat deltaScroll = metrics.deltaWindowY + metrics.deltaScrollerY + | |
| 607 metrics.deltaVisibleY; | |
| 608 metrics.scrollPoint.y += deltaScroll; | |
| 609 } | |
| 610 } else { | |
| 611 // Couldn't -> Can't | |
| 612 // Check for menu height change by looking at the relative tops of the | |
| 613 // menu folder and the window folder, which previously would have been | |
| 614 // the same. | |
| 615 metrics.deltaWindowY = NSMaxY(metrics.windowFrame) - metrics.folderTop; | |
| 616 metrics.deltaWindowHeight = -metrics.deltaWindowY; | |
| 617 } | |
| 618 } | |
| 619 } | |
| 620 | |
| 621 - (void)adjustMetricsForMenuBottomChanges:(LayoutMetrics&)metrics { | |
| 622 if (metrics.canScrollDown == metrics.couldScrollDown) { | |
| 623 if (!metrics.canScrollDown) { | |
| 624 // Not scroll-down-able but the menu top has changed. | |
| 625 metrics.deltaWindowHeight += metrics.scrollDelta; | |
| 626 } | |
| 627 } else { | |
| 628 if (metrics.canScrollDown) { | |
| 629 // Couldn't -> Can | |
| 630 metrics.deltaWindowHeight += (NSMaxY([[[self window] screen] frame]) - | |
| 631 NSMaxY(metrics.windowFrame)); | |
| 632 metrics.deltaVisibleHeight -= bookmarks::kScrollWindowVerticalMargin; | |
| 633 metrics.deltaScrollerHeight -= verticalScrollArrowHeight_; | |
| 634 } else { | |
| 635 // Could -> Can't | |
| 636 metrics.deltaWindowHeight -= bookmarks::kScrollWindowVerticalMargin; | |
| 637 metrics.deltaVisibleHeight += bookmarks::kScrollWindowVerticalMargin; | |
| 638 metrics.deltaScrollerHeight += verticalScrollArrowHeight_; | |
| 639 } | |
| 640 } | |
| 641 } | |
| 642 | |
| 643 - (void)applyMetrics:(LayoutMetrics&)metrics { | |
| 644 // Hide or show the scroll arrows. | |
| 645 if (metrics.canScrollUp != metrics.couldScrollUp) | |
| 646 [scrollUpArrowView_ setHidden:metrics.couldScrollUp]; | |
| 647 if (metrics.canScrollDown != metrics.couldScrollDown) | |
| 648 [scrollDownArrowView_ setHidden:metrics.couldScrollDown]; | |
| 649 | |
| 650 // Adjust the geometry. The order is important because of sizer dependencies. | |
| 651 [scrollView_ setFrame:metrics.scrollerFrame]; | |
| 652 [visibleView_ setFrame:metrics.visibleFrame]; | |
| 653 // This little bit of trickery handles the one special case where | |
| 654 // the window is now scroll-up-able _and_ going to be resized -- scroll | |
| 655 // first in order to prevent flashing. | |
| 656 if (metrics.preScroll) | |
| 657 [[scrollView_ documentView] scrollPoint:metrics.scrollPoint]; | |
| 658 | |
| 659 [[self window] setFrame:metrics.windowFrame display:YES]; | |
| 660 | |
| 661 // In all other cases we defer scrolling until the window has been resized | |
| 662 // in order to prevent flashing. | |
| 663 if (!metrics.preScroll) | |
| 664 [[scrollView_ documentView] scrollPoint:metrics.scrollPoint]; | |
| 665 | |
| 666 if (metrics.canScrollUp != metrics.couldScrollUp || | |
| 667 metrics.canScrollDown != metrics.couldScrollDown || | |
| 668 metrics.scrollDelta != 0.0) { | |
| 669 if (metrics.canScrollUp || metrics.canScrollDown) | |
| 670 [self addOrUpdateScrollTracking]; | |
| 671 else | |
| 672 [self removeScrollTracking]; | |
| 673 } | |
| 674 } | |
| 675 | |
| 676 - (void)adjustWindowForButtonCount:(NSUInteger)buttonCount { | |
| 677 NSRect folderFrame = [folderView_ frame]; | |
| 678 CGFloat newMenuHeight = | |
| 679 (CGFloat)[self menuHeightForButtonCount:[buttons_ count]]; | |
| 680 CGFloat deltaMenuHeight = newMenuHeight - NSHeight(folderFrame); | |
| 681 // If the height has changed then also change the origin, and adjust the | |
| 682 // scroll (if scrolling). | |
| 683 if ([self canScrollUp]) { | |
| 684 NSPoint scrollPoint = [scrollView_ documentVisibleRect].origin; | |
| 685 scrollPoint.y += deltaMenuHeight; | |
| 686 [[scrollView_ documentView] scrollPoint:scrollPoint]; | |
| 687 } | |
| 688 folderFrame.size.height += deltaMenuHeight; | |
| 689 [folderView_ setFrameSize:folderFrame.size]; | |
| 690 CGFloat windowWidth = [self adjustButtonWidths] + padding_; | |
| 398 NSPoint newWindowTopLeft = [self windowTopLeftForWidth:windowWidth]; | 691 NSPoint newWindowTopLeft = [self windowTopLeftForWidth:windowWidth]; | 
| 399 NSSize windowSize = NSMakeSize(windowWidth, windowHeight); | 692 CGFloat left = newWindowTopLeft.x; | 
| 400 windowSize = [scrollView_ convertSize:windowSize toView:nil]; | 693 NSSize newSize = NSMakeSize(windowWidth, deltaMenuHeight); | 
| 401 NSWindow* window = [self window]; | 694 [self adjustWindowLeft:left size:newSize scrollingBy:0.0]; | 
| 402 // If the window is already visible then make sure its top remains stable. | |
| 403 BOOL windowAlreadyShowing = [window isVisible]; | |
| 404 CGFloat deltaY = windowHeight - NSHeight([mainView_ frame]); | |
| 405 if (windowAlreadyShowing) { | |
| 406 NSRect oldFrame = [window frame]; | |
| 407 newWindowTopLeft.y = oldFrame.origin.y + NSHeight(oldFrame); | |
| 408 } | |
| 409 NSRect windowFrame = NSMakeRect(newWindowTopLeft.x, | |
| 410 newWindowTopLeft.y - windowHeight, windowSize.width, windowHeight); | |
| 411 // Make the scrolled content be the right size (full size). | |
| 412 NSRect mainViewFrame = NSMakeRect(0, 0, NSWidth(windowFrame) - | |
| 413 bookmarks::kScrollViewContentWidthMargin, NSHeight(windowFrame)); | |
| 414 [mainView_ setFrame:mainViewFrame]; | |
| 415 // Make sure the window fits on the screen. If not, constrain. | |
| 416 // We'll scroll to allow the user to see all the content. | |
| 417 NSRect screenFrame = [[[self window] screen] frame]; | |
| 418 screenFrame = NSInsetRect(screenFrame, 0, kScrollWindowVerticalMargin); | |
| 419 BOOL wasScrollable = scrollable_; | |
| 420 if (!NSContainsRect(screenFrame, windowFrame)) { | |
| 421 scrollable_ = YES; | |
| 422 windowFrame = NSIntersectionRect(screenFrame, windowFrame); | |
| 423 } else { | |
| 424 scrollable_ = NO; | |
| 425 } | |
| 426 [window setFrame:windowFrame display:NO]; | |
| 427 if (wasScrollable != scrollable_) { | |
| 428 // If scrollability changed then rework visibility of the scroll arrows | |
| 429 // and the scroll offset of the menu view. | |
| 430 NSSize windowLocalSize = | |
| 431 [scrollView_ convertSize:windowFrame.size fromView:nil]; | |
| 432 CGFloat scrollPointY = NSHeight(mainViewFrame) - windowLocalSize.height + | |
| 433 bookmarks::kBookmarkVerticalPadding; | |
| 434 [mainView_ scrollPoint:NSMakePoint(0, scrollPointY)]; | |
| 435 [self showOrHideScrollArrows]; | |
| 436 [self addOrUpdateScrollTracking]; | |
| 437 } else if (scrollable_ && windowAlreadyShowing) { | |
| 438 // If the window was already showing and is still scrollable then make | |
| 439 // sure the main view moves upward, not downward so that the content | |
| 440 // at the bottom of the menu, not the top, appears to move. | |
| 441 // The edge case is when the menu is scrolled all the way to top (hence | |
| 442 // the test of scrollDownArrowShown_) - don't scroll then. | |
| 443 NSView* superView = [mainView_ superview]; | |
| 444 DCHECK([superView isKindOfClass:[NSClipView class]]); | |
| 445 NSClipView* clipView = static_cast<NSClipView*>(superView); | |
| 446 CGFloat scrollPointY = [clipView bounds].origin.y + | |
| 447 bookmarks::kBookmarkVerticalPadding; | |
| 448 if (scrollDownArrowShown_ || deltaY > 0.0) | |
| 449 scrollPointY += deltaY; | |
| 450 [mainView_ scrollPoint:NSMakePoint(0, scrollPointY)]; | |
| 451 } | |
| 452 [window display]; | |
| 453 } | 695 } | 
| 454 | 696 | 
| 455 // Determine window size and position. | 697 // Determine window size and position. | 
| 456 // Create buttons for all our nodes. | 698 // Create buttons for all our nodes. | 
| 457 // TODO(jrg): break up into more and smaller routines for easier unit testing. | 699 // TODO(jrg): break up into more and smaller routines for easier unit testing. | 
| 458 - (void)configureWindow { | 700 - (void)configureWindow { | 
| 459 const BookmarkNode* node = [parentButton_ bookmarkNode]; | 701 const BookmarkNode* node = [parentButton_ bookmarkNode]; | 
| 460 DCHECK(node); | 702 DCHECK(node); | 
| 461 int startingIndex = [[parentButton_ cell] startingChildIndex]; | 703 int startingIndex = [[parentButton_ cell] startingChildIndex]; | 
| 462 DCHECK_LE(startingIndex, node->GetChildCount()); | 704 DCHECK_LE(startingIndex, node->GetChildCount()); | 
| 463 // Must have at least 1 button (for "empty") | 705 // Must have at least 1 button (for "empty") | 
| 464 int buttons = std::max(node->GetChildCount() - startingIndex, 1); | 706 int buttons = std::max(node->GetChildCount() - startingIndex, 1); | 
| 465 | 707 | 
| 466 // Prelim height of the window. We'll trim later as needed. | 708 // Prelim height of the window. We'll trim later as needed. | 
| 467 int height = [self windowHeightForButtonCount:buttons]; | 709 int height = [self menuHeightForButtonCount:buttons]; | 
| 468 // We'll need this soon... | 710 // We'll need this soon... | 
| 469 [self window]; | 711 [self window]; | 
| 470 | 712 | 
| 471 // TODO(jrg): combine with frame code in bookmark_bar_controller.mm | 713 // TODO(jrg): combine with frame code in bookmark_bar_controller.mm | 
| 472 // http://crbug.com/35966 | 714 // http://crbug.com/35966 | 
| 473 NSRect buttonsOuterFrame = NSMakeRect( | 715 NSRect buttonsOuterFrame = NSMakeRect( | 
| 474 bookmarks::kBookmarkSubMenuHorizontalPadding, | 716 0, | 
| 475 (height - bookmarks::kBookmarkButtonVerticalSpan), | 717 (height - bookmarks::kBookmarkButtonVerticalSpan), | 
| 476 bookmarks::kDefaultBookmarkWidth, | 718 bookmarks::kDefaultBookmarkWidth, | 
| 477 bookmarks::kBookmarkButtonHeight); | 719 bookmarks::kBookmarkButtonHeight); | 
| 478 | 720 | 
| 479 // TODO(jrg): combine with addNodesToButtonList: code from | 721 // TODO(jrg): combine with addNodesToButtonList: code from | 
| 480 // bookmark_bar_controller.mm (but use y offset) | 722 // bookmark_bar_controller.mm (but use y offset) | 
| 481 // http://crbug.com/35966 | 723 // http://crbug.com/35966 | 
| 482 if (!node->GetChildCount()) { | 724 if (!node->GetChildCount()) { | 
| 483 // If no children we are the empty button. | 725 // If no children we are the empty button. | 
| 484 BookmarkButton* button = [self makeButtonForNode:nil | 726 BookmarkButton* button = [self makeButtonForNode:nil | 
| 485 frame:buttonsOuterFrame]; | 727 frame:buttonsOuterFrame]; | 
| 486 [buttons_ addObject:button]; | 728 [buttons_ addObject:button]; | 
| 487 [mainView_ addSubview:button]; | 729 [folderView_ addSubview:button]; | 
| 488 } else { | 730 } else { | 
| 489 for (int i = startingIndex; | 731 for (int i = startingIndex; | 
| 490 i < node->GetChildCount(); | 732 i < node->GetChildCount(); | 
| 491 i++) { | 733 i++) { | 
| 492 const BookmarkNode* child = node->GetChild(i); | 734 const BookmarkNode* child = node->GetChild(i); | 
| 493 BookmarkButton* button = [self makeButtonForNode:child | 735 BookmarkButton* button = [self makeButtonForNode:child | 
| 494 frame:buttonsOuterFrame]; | 736 frame:buttonsOuterFrame]; | 
| 495 [buttons_ addObject:button]; | 737 [buttons_ addObject:button]; | 
| 496 [mainView_ addSubview:button]; | 738 [folderView_ addSubview:button]; | 
| 497 buttonsOuterFrame.origin.y -= bookmarks::kBookmarkButtonVerticalSpan; | 739 buttonsOuterFrame.origin.y -= bookmarks::kBookmarkButtonVerticalSpan; | 
| 498 } | 740 } | 
| 499 } | 741 } | 
| 742 [self layOutWindowWithHeight:height]; | |
| 743 } | |
| 500 | 744 | 
| 501 [self adjustWindowForHeight:height]; | 745 - (void)layOutWindowWithHeight:(CGFloat)height { | 
| 502 // Finally pop me up. | 746 // Lay out the window by adjusting all button widths to be consistent, then | 
| 747 // base the window width on this ideal button width. | |
| 748 CGFloat buttonWidth = [self adjustButtonWidths]; | |
| 749 CGFloat windowWidth = buttonWidth + padding_; | |
| 750 NSPoint newWindowTopLeft = [self windowTopLeftForWidth:windowWidth]; | |
| 751 // Make sure as much of a submenu is exposed (which otherwise would be a | |
| 752 // problem if the parent button is close to the bottom of the screen). | |
| 753 if ([parentController_ isKindOfClass:[self class]]) { | |
| 754 newWindowTopLeft.y = MAX(newWindowTopLeft.y, | |
| 755 height + bookmarks::kScrollWindowVerticalMargin); | |
| 756 } | |
| 757 NSWindow* window = [self window]; | |
| 758 NSRect windowFrame = NSMakeRect(newWindowTopLeft.x, | |
| 759 newWindowTopLeft.y - height, | |
| 760 windowWidth, height); | |
| 761 [window setFrame:windowFrame display:NO]; | |
| 762 NSRect folderFrame = NSMakeRect(0, 0, windowWidth, height); | |
| 763 [folderView_ setFrame:folderFrame]; | |
| 764 NSSize newSize = NSMakeSize(windowWidth, 0.0); | |
| 765 [self adjustWindowLeft:newWindowTopLeft.x size:newSize scrollingBy:0.0]; | |
| 766 [window display]; | |
| 503 [self configureWindowLevel]; | 767 [self configureWindowLevel]; | 
| 504 } | 768 } | 
| 505 | 769 | 
| 506 // TODO(mrossetti): See if the following can be moved into view's viewWillDraw:. | 770 // TODO(mrossetti): See if the following can be moved into view's viewWillDraw:. | 
| 507 - (CGFloat)adjustButtonWidths { | 771 - (CGFloat)adjustButtonWidths { | 
| 508 CGFloat width = bookmarks::kBookmarkMenuButtonMinimumWidth; | 772 CGFloat width = bookmarks::kBookmarkMenuButtonMinimumWidth; | 
| 509 // Use the cell's size as the base for determining the desired width of the | 773 // Use the cell's size as the base for determining the desired width of the | 
| 510 // button rather than the button's current width. -[cell cellSize] always | 774 // button rather than the button's current width. -[cell cellSize] always | 
| 511 // returns the 'optimum' size of the cell based on the cell's contents even | 775 // returns the 'optimum' size of the cell based on the cell's contents even | 
| 512 // if it's less than the current button size. Relying on the button size | 776 // if it's less than the current button size. Relying on the button size | 
| 513 // would result in buttons that could only get wider but we want to handle | 777 // would result in buttons that could only get wider but we want to handle | 
| 514 // the case where the widest button gets removed from a folder menu. | 778 // the case where the widest button gets removed from a folder menu. | 
| 515 for (BookmarkButton* button in buttons_.get()) | 779 for (BookmarkButton* button in buttons_.get()) | 
| 516 width = std::max(width, [[button cell] cellSize].width); | 780 width = std::max(width, [[button cell] cellSize].width); | 
| 517 width = std::min(width, bookmarks::kBookmarkMenuButtonMaximumWidth); | 781 width = std::min(width, bookmarks::kBookmarkMenuButtonMaximumWidth); | 
| 518 // Things look and feel more menu-like if all the buttons are the | 782 // Things look and feel more menu-like if all the buttons are the | 
| 519 // full width of the window, especially if there are submenus. | 783 // full width of the window, especially if there are submenus. | 
| 520 for (BookmarkButton* button in buttons_.get()) { | 784 for (BookmarkButton* button in buttons_.get()) { | 
| 521 NSRect buttonFrame = [button frame]; | 785 NSRect buttonFrame = [button frame]; | 
| 522 buttonFrame.size.width = width; | 786 buttonFrame.size.width = width; | 
| 523 [button setFrame:buttonFrame]; | 787 [button setFrame:buttonFrame]; | 
| 524 } | 788 } | 
| 525 return width; | 789 return width; | 
| 526 } | 790 } | 
| 527 | 791 | 
| 528 - (BOOL)canScrollUp { | |
| 529 // If removal of an arrow would make things "finished", state as | |
| 530 // such. | |
| 531 CGFloat scrollY = [scrollView_ documentVisibleRect].origin.y; | |
| 532 if (scrollUpArrowShown_) | |
| 533 scrollY -= verticalScrollArrowHeight_; | |
| 534 | |
| 535 if (scrollY <= 0) | |
| 536 return NO; | |
| 537 return YES; | |
| 538 } | |
| 539 | |
| 540 - (BOOL)canScrollDown { | |
| 541 CGFloat arrowAdjustment = 0.0; | |
| 542 | |
| 543 // We do NOT adjust based on the scrollDOWN arrow. This keeps | |
| 544 // things from "jumping"; if removal of the down arrow (at the top | |
| 545 // of the window) would cause a scroll to end, we'll end. | |
| 546 if (scrollUpArrowShown_) | |
| 547 arrowAdjustment += verticalScrollArrowHeight_; | |
| 548 | |
| 549 NSPoint scrollPosition = [scrollView_ documentVisibleRect].origin; | |
| 550 NSRect documentRect = [[scrollView_ documentView] frame]; | |
| 551 | |
| 552 // If we are exactly the right height, return no. We need this | |
| 553 // extra conditional in the case where we've just scrolled/grown | |
| 554 // into position. | |
| 555 if (NSHeight([[self window] frame]) == NSHeight(documentRect)) | |
| 556 return NO; | |
| 557 | |
| 558 if ((scrollPosition.y + NSHeight([[self window] frame])) >= | |
| 559 (NSHeight(documentRect) + arrowAdjustment)) { | |
| 560 return NO; | |
| 561 } | |
| 562 return YES; | |
| 563 } | |
| 564 | |
| 565 - (void)showOrHideScrollArrows { | |
| 566 NSRect frame = [scrollView_ frame]; | |
| 567 CGFloat scrollDelta = 0.0; | |
| 568 BOOL canScrollDown = [self canScrollDown]; | |
| 569 BOOL canScrollUp = [self canScrollUp]; | |
| 570 | |
| 571 if (canScrollUp != scrollUpArrowShown_) { | |
| 572 if (scrollUpArrowShown_) { | |
| 573 frame.origin.y -= verticalScrollArrowHeight_; | |
| 574 frame.size.height += verticalScrollArrowHeight_; | |
| 575 scrollDelta = verticalScrollArrowHeight_; | |
| 576 } else { | |
| 577 frame.origin.y += verticalScrollArrowHeight_; | |
| 578 frame.size.height -= verticalScrollArrowHeight_; | |
| 579 scrollDelta = -verticalScrollArrowHeight_; | |
| 580 } | |
| 581 } | |
| 582 if (canScrollDown != scrollDownArrowShown_) { | |
| 583 if (scrollDownArrowShown_) { | |
| 584 frame.size.height += verticalScrollArrowHeight_; | |
| 585 } else { | |
| 586 frame.size.height -= verticalScrollArrowHeight_; | |
| 587 } | |
| 588 } | |
| 589 scrollUpArrowShown_ = canScrollUp; | |
| 590 scrollDownArrowShown_ = canScrollDown; | |
| 591 [scrollView_ setFrame:frame]; | |
| 592 | |
| 593 // Adjust scroll based on new frame. For example, if we make room | |
| 594 // for an arrow at the bottom, adjust the scroll so the topmost item | |
| 595 // is still fully visible. | |
| 596 if (scrollDelta) { | |
| 597 NSPoint scrollPosition = [scrollView_ documentVisibleRect].origin; | |
| 598 scrollPosition.y -= scrollDelta; | |
| 599 [[scrollView_ documentView] scrollPoint:scrollPosition]; | |
| 600 } | |
| 601 } | |
| 602 | |
| 603 - (BOOL)scrollable { | |
| 604 return scrollable_; | |
| 605 } | |
| 606 | |
| 607 // Start a "scroll up" timer. | 792 // Start a "scroll up" timer. | 
| 608 - (void)beginScrollWindowUp { | 793 - (void)beginScrollWindowUp { | 
| 609 [self addScrollTimerWithDelta:kBookmarkBarFolderScrollAmount]; | 794 [self addScrollTimerWithDelta:kBookmarkBarFolderScrollAmount]; | 
| 610 } | 795 } | 
| 611 | 796 | 
| 612 // Start a "scroll down" timer. | 797 // Start a "scroll down" timer. | 
| 613 - (void)beginScrollWindowDown { | 798 - (void)beginScrollWindowDown { | 
| 614 [self addScrollTimerWithDelta:-kBookmarkBarFolderScrollAmount]; | 799 [self addScrollTimerWithDelta:-kBookmarkBarFolderScrollAmount]; | 
| 615 } | 800 } | 
| 616 | 801 | 
| 617 // End a scrolling timer. Can be called excessively with no harm. | 802 // End a scrolling timer. Can be called excessively with no harm. | 
| 618 - (void)endScroll { | 803 - (void)endScroll { | 
| 619 if (scrollTimer_) { | 804 if (scrollTimer_) { | 
| 620 [scrollTimer_ invalidate]; | 805 [scrollTimer_ invalidate]; | 
| 621 scrollTimer_ = nil; | 806 scrollTimer_ = nil; | 
| 622 verticalScrollDelta_ = 0; | 807 verticalScrollDelta_ = 0; | 
| 623 } | 808 } | 
| 624 } | 809 } | 
| 625 | 810 | 
| 626 // Perform a single scroll of the specified amount. | 811 // Perform a single scroll of the specified amount. | 
| 627 // Scroll up: | |
| 628 // Scroll the documentView by the growth amount. | |
| 629 // If we cannot grow the window, simply scroll the documentView. | |
| 630 // If we can grow the window up without falling off the screen, do it. | |
| 631 // Scroll down: | |
| 632 // Never change the window size; only scroll the documentView. | |
| 633 - (void)performOneScroll:(CGFloat)delta { | 812 - (void)performOneScroll:(CGFloat)delta { | 
| 634 NSRect windowFrame = [[self window] frame]; | 813 CGFloat finalDelta = [self determineFinalScrollDelta:delta]; | 
| 635 NSRect screenFrame = [[[self window] screen] frame]; | 814 if (finalDelta > 0.0 || finalDelta < 0.0) { | 
| 815 if (buttonThatMouseIsIn_) | |
| 816 [buttonThatMouseIsIn_ toggleButtonBorderingWhileMouseInside]; | |
| 817 NSRect windowFrame = [[self window] frame]; | |
| 818 NSSize newSize = NSMakeSize(NSWidth(windowFrame), 0.0); | |
| 819 [self adjustWindowLeft:windowFrame.origin.x | |
| 820 size:newSize | |
| 821 scrollingBy:finalDelta]; | |
| 822 } | |
| 823 } | |
| 636 | 824 | 
| 637 // First scroll the "document" area. | 825 - (CGFloat)determineFinalScrollDelta:(CGFloat)delta { | 
| 638 NSPoint scrollPosition = [scrollView_ documentVisibleRect].origin; | 826 if (delta > 0.0 && ![scrollUpArrowView_ isHidden] || | 
| 639 scrollPosition.y -= delta; | 827 delta < 0.0 && ![scrollDownArrowView_ isHidden]) { | 
| 640 [[scrollView_ documentView] scrollPoint:scrollPosition]; | 828 NSWindow* window = [self window]; | 
| 829 NSRect windowFrame = [window frame]; | |
| 830 NSScreen* screen = [window screen]; | |
| 831 NSPoint scrollPosition = [scrollView_ documentVisibleRect].origin; | |
| 832 CGFloat scrollY = scrollPosition.y; | |
| 833 NSRect scrollerFrame = [scrollView_ frame]; | |
| 834 CGFloat scrollerY = NSMinY(scrollerFrame); | |
| 835 NSRect visibleFrame = [visibleView_ frame]; | |
| 836 CGFloat visibleY = NSMinY(visibleFrame); | |
| 837 CGFloat windowY = NSMinY(windowFrame); | |
| 838 CGFloat offset = scrollerY + visibleY + windowY; | |
| 641 | 839 | 
| 642 if (buttonThatMouseIsIn_) | 840 if (delta > 0.0) { | 
| 643 [buttonThatMouseIsIn_ toggleButtonBorderingWhileMouseInside]; | 841 // Scrolling up. | 
| 644 | 842 CGFloat minimumY = NSMinY([screen visibleFrame]) + | 
| 645 // We update the window size after shifting the scroll to avoid a race. | 843 bookmarks::kScrollWindowVerticalMargin; | 
| 646 CGFloat screenHeightMinusMargin = (NSHeight(screenFrame) - | 844 CGFloat maxUpDelta = scrollY - offset + minimumY; | 
| 647 (2 * kScrollWindowVerticalMargin)); | 845 delta = MIN(delta, maxUpDelta); | 
| 648 if (delta) { | 846 } else { | 
| 649 // If we can, grow the window (up). | 847 // Scrolling down. | 
| 650 if (NSHeight(windowFrame) < screenHeightMinusMargin) { | 848 NSRect screenFrame = [screen frame]; | 
| 651 CGFloat growAmount = delta; | 849 CGFloat topOfScreen = NSMaxY(screenFrame); | 
| 652 // Don't scroll more than enough to "finish". | 850 NSRect folderFrame = [folderView_ frame]; | 
| 653 if (scrollPosition.y < 0) | 851 CGFloat folderHeight = NSHeight(folderFrame); | 
| 654 growAmount += scrollPosition.y; | 852 CGFloat folderTop = folderHeight - scrollY + offset; | 
| 655 windowFrame.size.height += growAmount; | 853 CGFloat maxDownDelta = | 
| 656 windowFrame.size.height = std::min(NSHeight(windowFrame), | 854 topOfScreen - folderTop - bookmarks::kScrollWindowVerticalMargin; | 
| 657 screenHeightMinusMargin); | 855 delta = MAX(delta, maxDownDelta); | 
| 658 // Watch out for a finish that isn't the full height of the screen. | |
| 659 // We get here if using the scroll wheel to scroll by small amounts. | |
| 660 windowFrame.size.height = std::min(NSHeight(windowFrame), | |
| 661 NSHeight([mainView_ frame])); | |
| 662 // Don't allow scrolling to make the window smaller, ever. This | |
| 663 // conditional is important when processing scrollWheel events. | |
| 664 if (windowFrame.size.height > [[self window] frame].size.height) { | |
| 665 [[self window] setFrame:windowFrame display:YES]; | |
| 666 [self addOrUpdateScrollTracking]; | |
| 667 } | |
| 668 } | 856 } | 
| 857 } else { | |
| 858 delta = 0.0; | |
| 669 } | 859 } | 
| 670 | 860 return delta; | 
| 671 // If we're at either end, happiness. | |
| 672 if ((scrollPosition.y <= 0) || | |
| 673 ((scrollPosition.y + NSHeight(windowFrame) >= | |
| 674 NSHeight([mainView_ frame])) && | |
| 675 (windowFrame.size.height == screenHeightMinusMargin))) { | |
| 676 [self endScroll]; | |
| 677 | |
| 678 // If we can't scroll either up or down we are completely done. | |
| 679 // For example, perhaps we've scrolled a little and grown the | |
| 680 // window on-screen until there is now room for everything. | |
| 681 if (![self canScrollUp] && ![self canScrollDown]) { | |
| 682 scrollable_ = NO; | |
| 683 [self removeScrollTracking]; | |
| 684 } | |
| 685 } | |
| 686 | |
| 687 [self showOrHideScrollArrows]; | |
| 688 } | 861 } | 
| 689 | 862 | 
| 690 // Perform a scroll of the window on the screen. | 863 // Perform a scroll of the window on the screen. | 
| 691 // Called by a timer when scrolling. | 864 // Called by a timer when scrolling. | 
| 692 - (void)performScroll:(NSTimer*)timer { | 865 - (void)performScroll:(NSTimer*)timer { | 
| 693 DCHECK(verticalScrollDelta_); | 866 DCHECK(verticalScrollDelta_); | 
| 694 [self performOneScroll:verticalScrollDelta_]; | 867 [self performOneScroll:verticalScrollDelta_]; | 
| 695 } | 868 } | 
| 696 | 869 | 
| 697 | 870 | 
| (...skipping 11 matching lines...) Expand all Loading... | |
| 709 userInfo:nil | 882 userInfo:nil | 
| 710 repeats:YES]; | 883 repeats:YES]; | 
| 711 } | 884 } | 
| 712 | 885 | 
| 713 // Called as a result of our tracking area. Warning: on the main | 886 // Called as a result of our tracking area. Warning: on the main | 
| 714 // screen (of a single-screened machine), the minimum mouse y value is | 887 // screen (of a single-screened machine), the minimum mouse y value is | 
| 715 // 1, not 0. Also, we do not get events when the mouse is above the | 888 // 1, not 0. Also, we do not get events when the mouse is above the | 
| 716 // menubar (to be fixed by setting the proper window level; see | 889 // menubar (to be fixed by setting the proper window level; see | 
| 717 // initializer). | 890 // initializer). | 
| 718 - (void)mouseMoved:(NSEvent*)theEvent { | 891 - (void)mouseMoved:(NSEvent*)theEvent { | 
| 719 DCHECK([theEvent window] == [self window]); | 892 NSWindow* window = [theEvent window]; | 
| 893 DCHECK(window == [self window]); | |
| 720 | 894 | 
| 721 NSPoint eventScreenLocation = | 895 NSPoint eventScreenLocation = | 
| 722 [[theEvent window] convertBaseToScreen:[theEvent locationInWindow]]; | 896 [window convertBaseToScreen:[theEvent locationInWindow]]; | 
| 723 | 897 | 
| 724 // We use frame (not visibleFrame) since our bookmark folder is on | 898 // Base hot spot calculations on the positions of the scroll arrow views. | 
| 725 // TOP of the menubar. | 899 NSRect testRect = [scrollDownArrowView_ frame]; | 
| 726 NSRect visibleRect = [[[self window] screen] frame]; | 900 NSPoint testPoint = [visibleView_ convertPoint:testRect.origin | 
| 727 CGFloat closeToTopOfScreen = NSMaxY(visibleRect) - | 901 toView:nil]; | 
| 728 verticalScrollArrowHeight_; | 902 testPoint = [window convertBaseToScreen:testPoint]; | 
| 729 CGFloat closeToBottomOfScreen = NSMinY(visibleRect) + | 903 CGFloat closeToTopOfScreen = testPoint.y; | 
| 730 verticalScrollArrowHeight_; | |
| 731 | 904 | 
| 732 if (eventScreenLocation.y <= closeToBottomOfScreen) { | 905 testRect = [scrollUpArrowView_ frame]; | 
| 906 testPoint = [visibleView_ convertPoint:testRect.origin toView:nil]; | |
| 907 testPoint = [window convertBaseToScreen:testPoint]; | |
| 908 CGFloat closeToBottomOfScreen = testPoint.y + testRect.size.height; | |
| 909 if (eventScreenLocation.y <= closeToBottomOfScreen && | |
| 910 ![scrollUpArrowView_ isHidden]) { | |
| 733 [self beginScrollWindowUp]; | 911 [self beginScrollWindowUp]; | 
| 734 } else if (eventScreenLocation.y > closeToTopOfScreen) { | 912 } else if (eventScreenLocation.y > closeToTopOfScreen && | 
| 913 ![scrollDownArrowView_ isHidden]) { | |
| 735 [self beginScrollWindowDown]; | 914 [self beginScrollWindowDown]; | 
| 736 } else { | 915 } else { | 
| 737 [self endScroll]; | 916 [self endScroll]; | 
| 738 } | 917 } | 
| 739 } | 918 } | 
| 740 | 919 | 
| 741 - (void)mouseExited:(NSEvent*)theEvent { | 920 - (void)mouseExited:(NSEvent*)theEvent { | 
| 742 [self endScroll]; | 921 [self endScroll]; | 
| 743 } | 922 } | 
| 744 | 923 | 
| (...skipping 14 matching lines...) Expand all Loading... | |
| 759 } | 938 } | 
| 760 | 939 | 
| 761 // Remove the tracking area associated with scrolling. | 940 // Remove the tracking area associated with scrolling. | 
| 762 - (void)removeScrollTracking { | 941 - (void)removeScrollTracking { | 
| 763 if (scrollTrackingArea_.get()) { | 942 if (scrollTrackingArea_.get()) { | 
| 764 [[[self window] contentView] removeTrackingArea:scrollTrackingArea_]; | 943 [[[self window] contentView] removeTrackingArea:scrollTrackingArea_]; | 
| 765 } | 944 } | 
| 766 scrollTrackingArea_.reset(); | 945 scrollTrackingArea_.reset(); | 
| 767 } | 946 } | 
| 768 | 947 | 
| 769 // Delegate callback. | |
| 770 - (void)windowWillClose:(NSNotification*)notification { | |
| 771 // If a "hover open" is pending when the bookmark bar folder is | |
| 772 // closed, be sure it gets cancelled. | |
| 773 [NSObject cancelPreviousPerformRequestsWithTarget:self]; | |
| 774 | |
| 775 [barController_ childFolderWillClose:self]; | |
| 776 [self closeBookmarkFolder:self]; | |
| 777 [self autorelease]; | |
| 778 } | |
| 779 | |
| 780 // Close the old hover-open bookmark folder, and open a new one. We | 948 // Close the old hover-open bookmark folder, and open a new one. We | 
| 781 // do both in one step to allow for a delay in closing the old one. | 949 // do both in one step to allow for a delay in closing the old one. | 
| 782 // See comments above kDragHoverCloseDelay (bookmark_bar_controller.h) | 950 // See comments above kDragHoverCloseDelay (bookmark_bar_controller.h) | 
| 783 // for more details. | 951 // for more details. | 
| 784 - (void)openBookmarkFolderFromButtonAndCloseOldOne:(id)sender { | 952 - (void)openBookmarkFolderFromButtonAndCloseOldOne:(id)sender { | 
| 785 // If an old submenu exists, close it immediately. | 953 // If an old submenu exists, close it immediately. | 
| 786 [self closeBookmarkFolder:sender]; | 954 [self closeBookmarkFolder:sender]; | 
| 787 | 955 | 
| 788 // Open a new one if meaningful. | 956 // Open a new one if meaningful. | 
| 789 if ([sender isFolder]) | 957 if ([sender isFolder]) | 
| 790 [folderTarget_ openBookmarkFolderFromButton:sender]; | 958 [folderTarget_ openBookmarkFolderFromButton:sender]; | 
| 791 } | 959 } | 
| 792 | 960 | 
| 793 - (NSArray*)buttons { | 961 - (NSArray*)buttons { | 
| 794 return buttons_.get(); | 962 return buttons_.get(); | 
| 795 } | 963 } | 
| 796 | 964 | 
| 797 - (void)close { | 965 - (void)close { | 
| 798 [folderController_ close]; | 966 [folderController_ close]; | 
| 799 [super close]; | 967 [super close]; | 
| 800 } | 968 } | 
| 801 | 969 | 
| 802 - (void)scrollWheel:(NSEvent *)theEvent { | 970 - (void)scrollWheel:(NSEvent *)theEvent { | 
| 803 if (scrollable_) { | 971 if (![scrollUpArrowView_ isHidden] || ![scrollDownArrowView_ isHidden]) { | 
| 804 // We go negative since an NSScrollView has a flipped coordinate frame. | 972 // We go negative since an NSScrollView has a flipped coordinate frame. | 
| 805 CGFloat amt = kBookmarkBarFolderScrollWheelAmount * -[theEvent deltaY]; | 973 CGFloat amt = kBookmarkBarFolderScrollWheelAmount * -[theEvent deltaY]; | 
| 806 [self performOneScroll:amt]; | 974 [self performOneScroll:amt]; | 
| 807 } | 975 } | 
| 808 } | 976 } | 
| 809 | 977 | 
| 810 #pragma mark Actions Forwarded to Parent BookmarkBarController | 978 #pragma mark Actions Forwarded to Parent BookmarkBarController | 
| 811 | 979 | 
| 812 - (IBAction)openBookmark:(id)sender { | 980 - (IBAction)openBookmark:(id)sender { | 
| 813 [barController_ openBookmark:sender]; | 981 [barController_ openBookmark:sender]; | 
| (...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 919 // exist in the future). | 1087 // exist in the future). | 
| 920 // http://crbug.com/35966 | 1088 // http://crbug.com/35966 | 
| 921 - (int)indexForDragToPoint:(NSPoint)point { | 1089 - (int)indexForDragToPoint:(NSPoint)point { | 
| 922 // Identify which buttons we are between. For now, assume a button | 1090 // Identify which buttons we are between. For now, assume a button | 
| 923 // location is at the center point of its view, and that an exact | 1091 // location is at the center point of its view, and that an exact | 
| 924 // match means "place before". | 1092 // match means "place before". | 
| 925 // TODO(jrg): revisit position info based on UI team feedback. | 1093 // TODO(jrg): revisit position info based on UI team feedback. | 
| 926 // dropLocation is in bar local coordinates. | 1094 // dropLocation is in bar local coordinates. | 
| 927 // http://crbug.com/36276 | 1095 // http://crbug.com/36276 | 
| 928 NSPoint dropLocation = | 1096 NSPoint dropLocation = | 
| 929 [mainView_ convertPoint:point | 1097 [folderView_ convertPoint:point | 
| 930 fromView:[[self window] contentView]]; | 1098 fromView:[[self window] contentView]]; | 
| 931 BookmarkButton* buttonToTheTopOfDraggedButton = nil; | 1099 BookmarkButton* buttonToTheTopOfDraggedButton = nil; | 
| 932 // Buttons are laid out in this array from top to bottom (screen | 1100 // Buttons are laid out in this array from top to bottom (screen | 
| 933 // wise), which means "biggest y" --> "smallest y". | 1101 // wise), which means "biggest y" --> "smallest y". | 
| 934 for (BookmarkButton* button in buttons_.get()) { | 1102 for (BookmarkButton* button in buttons_.get()) { | 
| 935 CGFloat midpoint = NSMidY([button frame]); | 1103 CGFloat midpoint = NSMidY([button frame]); | 
| 936 if (dropLocation.y > midpoint) { | 1104 if (dropLocation.y > midpoint) { | 
| 937 break; | 1105 break; | 
| 938 } | 1106 } | 
| 939 buttonToTheTopOfDraggedButton = button; | 1107 buttonToTheTopOfDraggedButton = button; | 
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 993 else | 1161 else | 
| 994 [self bookmarkModel]->Move(sourceNode, destParent, destIndex); | 1162 [self bookmarkModel]->Move(sourceNode, destParent, destIndex); | 
| 995 wasCopiedOrMoved = YES; | 1163 wasCopiedOrMoved = YES; | 
| 996 // Movement of a node triggers observers (like us) to rebuild the | 1164 // Movement of a node triggers observers (like us) to rebuild the | 
| 997 // bar so we don't have to do so explicitly. | 1165 // bar so we don't have to do so explicitly. | 
| 998 } | 1166 } | 
| 999 | 1167 | 
| 1000 return wasCopiedOrMoved; | 1168 return wasCopiedOrMoved; | 
| 1001 } | 1169 } | 
| 1002 | 1170 | 
| 1171 #pragma mark NSWindowDelegate Functions | |
| 1172 | |
| 1173 - (void)windowWillClose:(NSNotification*)notification { | |
| 1174 // If a "hover open" is pending when the bookmark bar folder is | |
| 1175 // closed, be sure it gets cancelled. | |
| 1176 [NSObject cancelPreviousPerformRequestsWithTarget:self]; | |
| 1177 | |
| 1178 [self endScroll]; // Just in case we were scrolling. | |
| 1179 [barController_ childFolderWillClose:self]; | |
| 1180 [self closeBookmarkFolder:self]; | |
| 1181 [self autorelease]; | |
| 1182 } | |
| 1183 | |
| 1003 #pragma mark BookmarkButtonDelegate Protocol | 1184 #pragma mark BookmarkButtonDelegate Protocol | 
| 1004 | 1185 | 
| 1005 - (void)fillPasteboard:(NSPasteboard*)pboard | 1186 - (void)fillPasteboard:(NSPasteboard*)pboard | 
| 1006 forDragOfButton:(BookmarkButton*)button { | 1187 forDragOfButton:(BookmarkButton*)button { | 
| 1007 [[self folderTarget] fillPasteboard:pboard forDragOfButton:button]; | 1188 [[self folderTarget] fillPasteboard:pboard forDragOfButton:button]; | 
| 1008 | 1189 | 
| 1009 // Close our folder menu and submenus since we know we're going to be dragged. | 1190 // Close our folder menu and submenus since we know we're going to be dragged. | 
| 1010 [self closeBookmarkFolder:self]; | 1191 [self closeBookmarkFolder:self]; | 
| 1011 } | 1192 } | 
| 1012 | 1193 | 
| (...skipping 260 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1273 // which is where the new button will be located. | 1454 // which is where the new button will be located. | 
| 1274 newButtonFrame = [button frame]; | 1455 newButtonFrame = [button frame]; | 
| 1275 NSRect buttonFrame = [button frame]; | 1456 NSRect buttonFrame = [button frame]; | 
| 1276 buttonFrame.origin.y += bookmarks::kBookmarkButtonVerticalSpan; | 1457 buttonFrame.origin.y += bookmarks::kBookmarkButtonVerticalSpan; | 
| 1277 [button setFrame:buttonFrame]; | 1458 [button setFrame:buttonFrame]; | 
| 1278 } | 1459 } | 
| 1279 [[button cell] mouseExited:nil]; // De-highlight. | 1460 [[button cell] mouseExited:nil]; // De-highlight. | 
| 1280 BookmarkButton* newButton = [self makeButtonForNode:node | 1461 BookmarkButton* newButton = [self makeButtonForNode:node | 
| 1281 frame:newButtonFrame]; | 1462 frame:newButtonFrame]; | 
| 1282 [buttons_ insertObject:newButton atIndex:buttonIndex]; | 1463 [buttons_ insertObject:newButton atIndex:buttonIndex]; | 
| 1283 [mainView_ addSubview:newButton]; | 1464 [folderView_ addSubview:newButton]; | 
| 1284 | 1465 | 
| 1285 // Close any child folder(s) which may still be open. | 1466 // Close any child folder(s) which may still be open. | 
| 1286 [self closeBookmarkFolder:self]; | 1467 [self closeBookmarkFolder:self]; | 
| 1287 | 1468 | 
| 1288 // Prelim height of the window. We'll trim later as needed. | 1469 [self adjustWindowForButtonCount:[buttons_ count]]; | 
| 1289 int height = [self windowHeightForButtonCount:[buttons_ count]]; | |
| 1290 [self adjustWindowForHeight:height]; | |
| 1291 } | 1470 } | 
| 1292 | 1471 | 
| 1293 // More code which essentially duplicates that of BookmarkBarController. | 1472 // More code which essentially duplicates that of BookmarkBarController. | 
| 1294 // TODO(mrossetti,jrg): http://crbug.com/35966 | 1473 // TODO(mrossetti,jrg): http://crbug.com/35966 | 
| 1295 - (BOOL)addURLs:(NSArray*)urls withTitles:(NSArray*)titles at:(NSPoint)point { | 1474 - (BOOL)addURLs:(NSArray*)urls withTitles:(NSArray*)titles at:(NSPoint)point { | 
| 1296 DCHECK([urls count] == [titles count]); | 1475 DCHECK([urls count] == [titles count]); | 
| 1297 BOOL nodesWereAdded = NO; | 1476 BOOL nodesWereAdded = NO; | 
| 1298 // Figure out where these new bookmarks nodes are to be added. | 1477 // Figure out where these new bookmarks nodes are to be added. | 
| 1299 BookmarkButton* button = [self buttonForDroppingOnAtPoint:point]; | 1478 BookmarkButton* button = [self buttonForDroppingOnAtPoint:point]; | 
| 1300 BookmarkModel* bookmarkModel = [self bookmarkModel]; | 1479 BookmarkModel* bookmarkModel = [self bookmarkModel]; | 
| (...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1387 // http://crbug.com/54324 | 1566 // http://crbug.com/54324 | 
| 1388 for (NSButton* button in buttons_.get()) { | 1567 for (NSButton* button in buttons_.get()) { | 
| 1389 if ([button showsBorderOnlyWhileMouseInside]) { | 1568 if ([button showsBorderOnlyWhileMouseInside]) { | 
| 1390 [button setShowsBorderOnlyWhileMouseInside:NO]; | 1569 [button setShowsBorderOnlyWhileMouseInside:NO]; | 
| 1391 [button setShowsBorderOnlyWhileMouseInside:YES]; | 1570 [button setShowsBorderOnlyWhileMouseInside:YES]; | 
| 1392 } | 1571 } | 
| 1393 } | 1572 } | 
| 1394 | 1573 | 
| 1395 [oldButton setDelegate:nil]; | 1574 [oldButton setDelegate:nil]; | 
| 1396 [oldButton removeFromSuperview]; | 1575 [oldButton removeFromSuperview]; | 
| 1397 if (animate && !ignoreAnimations_) | |
| 1398 NSShowAnimationEffect(NSAnimationEffectDisappearingItemDefault, poofPoint, | |
| 1399 NSZeroSize, nil, nil, nil); | |
| 1400 [buttons_ removeObjectAtIndex:buttonIndex]; | 1576 [buttons_ removeObjectAtIndex:buttonIndex]; | 
| 1401 for (NSInteger i = 0; i < buttonIndex; ++i) { | 1577 for (NSInteger i = 0; i < buttonIndex; ++i) { | 
| 1402 BookmarkButton* button = [buttons_ objectAtIndex:i]; | 1578 BookmarkButton* button = [buttons_ objectAtIndex:i]; | 
| 1403 NSRect buttonFrame = [button frame]; | 1579 NSRect buttonFrame = [button frame]; | 
| 1404 buttonFrame.origin.y -= bookmarks::kBookmarkButtonVerticalSpan; | 1580 buttonFrame.origin.y -= bookmarks::kBookmarkButtonVerticalSpan; | 
| 1405 [button setFrame:buttonFrame]; | 1581 [button setFrame:buttonFrame]; | 
| 1406 } | 1582 } | 
| 1407 // Search for and adjust submenus, if necessary. | 1583 // Search for and adjust submenus, if necessary. | 
| 1408 NSInteger buttonCount = [buttons_ count]; | 1584 NSInteger buttonCount = [buttons_ count]; | 
| 1409 if (buttonCount) { | 1585 if (buttonCount) { | 
| 1410 BookmarkButton* subButton = [folderController_ parentButton]; | 1586 BookmarkButton* subButton = [folderController_ parentButton]; | 
| 1411 for (NSInteger i = buttonIndex; i < buttonCount; ++i) { | 1587 for (NSInteger i = buttonIndex; i < buttonCount; ++i) { | 
| 1412 BookmarkButton* aButton = [buttons_ objectAtIndex:i]; | 1588 BookmarkButton* aButton = [buttons_ objectAtIndex:i]; | 
| 1413 // If this button is showing its menu then we need to move the menu, too. | 1589 // If this button is showing its menu then we need to move the menu, too. | 
| 1414 if (aButton == subButton) | 1590 if (aButton == subButton) | 
| 1415 [folderController_ offsetFolderMenuWindow:NSMakeSize(0.0, | 1591 [folderController_ offsetFolderMenuWindow:NSMakeSize(0.0, | 
| 1416 bookmarks::kBookmarkBarHeight)]; | 1592 bookmarks::kBookmarkBarHeight)]; | 
| 1417 } | 1593 } | 
| 1418 } else { | 1594 } else { | 
| 1419 // If all nodes have been removed from this folder then add in the | 1595 // If all nodes have been removed from this folder then add in the | 
| 1420 // 'empty' placeholder button. | 1596 // 'empty' placeholder button. | 
| 1421 NSRect buttonFrame = | 1597 NSRect buttonFrame = | 
| 1422 NSMakeRect(bookmarks::kBookmarkSubMenuHorizontalPadding, | 1598 NSMakeRect(0, | 
| 1423 bookmarks::kBookmarkButtonHeight - | 1599 bookmarks::kBookmarkButtonHeight - | 
| 1424 (bookmarks::kBookmarkBarHeight - | 1600 (bookmarks::kBookmarkBarHeight - | 
| 1425 bookmarks::kBookmarkVerticalPadding), | 1601 bookmarks::kBookmarkVerticalPadding), | 
| 1426 bookmarks::kDefaultBookmarkWidth, | 1602 bookmarks::kDefaultBookmarkWidth, | 
| 1427 (bookmarks::kBookmarkBarHeight - | 1603 (bookmarks::kBookmarkBarHeight - | 
| 1428 2 * bookmarks::kBookmarkVerticalPadding)); | 1604 2 * bookmarks::kBookmarkVerticalPadding)); | 
| 1429 BookmarkButton* button = [self makeButtonForNode:nil | 1605 BookmarkButton* button = [self makeButtonForNode:nil | 
| 1430 frame:buttonFrame]; | 1606 frame:buttonFrame]; | 
| 1431 [buttons_ addObject:button]; | 1607 [buttons_ addObject:button]; | 
| 1432 [mainView_ addSubview:button]; | 1608 [folderView_ addSubview:button]; | 
| 1433 buttonCount = 1; | 1609 buttonCount = 1; | 
| 1434 } | 1610 } | 
| 1435 | 1611 | 
| 1436 // Propose a height for the window. We'll trim later as needed. | 1612 [self adjustWindowForButtonCount:buttonCount]; | 
| 1437 [self adjustWindowForHeight:[self windowHeightForButtonCount:buttonCount]]; | 1613 | 
| 1614 if (animate && !ignoreAnimations_) | |
| 1615 NSShowAnimationEffect(NSAnimationEffectDisappearingItemDefault, poofPoint, | |
| 1616 NSZeroSize, nil, nil, nil); | |
| 1438 } | 1617 } | 
| 1439 | 1618 | 
| 1440 - (id<BookmarkButtonControllerProtocol>)controllerForNode: | 1619 - (id<BookmarkButtonControllerProtocol>)controllerForNode: | 
| 1441 (const BookmarkNode*)node { | 1620 (const BookmarkNode*)node { | 
| 1442 // See if we are holding this node, otherwise see if it is in our | 1621 // See if we are holding this node, otherwise see if it is in our | 
| 1443 // hierarchy of visible folder menus. | 1622 // hierarchy of visible folder menus. | 
| 1444 if ([parentButton_ bookmarkNode] == node) | 1623 if ([parentButton_ bookmarkNode] == node) | 
| 1445 return self; | 1624 return self; | 
| 1446 return [folderController_ controllerForNode:node]; | 1625 return [folderController_ controllerForNode:node]; | 
| 1447 } | 1626 } | 
| 1448 | 1627 | 
| 1449 #pragma mark TestingAPI Only | 1628 #pragma mark TestingAPI Only | 
| 1450 | 1629 | 
| 1630 - (BOOL)canScrollUp { | |
| 1631 return ![scrollUpArrowView_ isHidden]; | |
| 1632 } | |
| 1633 | |
| 1634 - (BOOL)canScrollDown { | |
| 1635 return ![scrollDownArrowView_ isHidden]; | |
| 1636 } | |
| 1637 | |
| 1638 - (CGFloat)verticalScrollArrowHeight { | |
| 1639 return verticalScrollArrowHeight_; | |
| 1640 } | |
| 1641 | |
| 1642 - (NSView*)visibleView { | |
| 1643 return visibleView_; | |
| 1644 } | |
| 1645 | |
| 1646 - (NSView*)scrollView { | |
| 1647 return scrollView_; | |
| 1648 } | |
| 1649 | |
| 1650 - (NSView*)folderView { | |
| 1651 return folderView_; | |
| 1652 } | |
| 1653 | |
| 1451 - (void)setIgnoreAnimations:(BOOL)ignore { | 1654 - (void)setIgnoreAnimations:(BOOL)ignore { | 
| 1452 ignoreAnimations_ = ignore; | 1655 ignoreAnimations_ = ignore; | 
| 1453 } | 1656 } | 
| 1454 | 1657 | 
| 1455 - (BookmarkButton*)buttonThatMouseIsIn { | 1658 - (BookmarkButton*)buttonThatMouseIsIn { | 
| 1456 return buttonThatMouseIsIn_; | 1659 return buttonThatMouseIsIn_; | 
| 1457 } | 1660 } | 
| 1458 | 1661 | 
| 1459 @end // BookmarkBarFolderController | 1662 @end // BookmarkBarFolderController | 
| OLD | NEW |