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

Side by Side Diff: chrome/browser/ui/cocoa/bookmarks/bookmark_bar_folder_controller.mm

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/
Patch Set: '' Created 9 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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 // The proposed and then final scrolling adjustment made to the scrollable
49 // area of the folder menu. This may be modified during the window layout
50 // primarily as a result of hiding or showing the scroll arrows.
51 CGFloat scrollDelta;
52 NSRect windowFrame;
53 NSRect visibleFrame;
54 NSRect scrollerFrame;
55 NSPoint scrollPoint;
56 // The difference between 'could' and 'can' in these next four data members
57 // is this: 'could' represents the previous condition for scrollability
58 // while 'can' represents what the new condition will be for scrollability.
59 BOOL couldScrollUp;
60 BOOL canScrollUp;
61 BOOL couldScrollDown;
62 BOOL canScrollDown;
63 // Determines the optimal time during folder menu layout when the contents
64 // of the button scroll area should be scrolled in order to prevent
65 // flickering.
66 BOOL preScroll;
67
68 // Intermediate metrics used in determining window vertical layout changes.
69 CGFloat deltaWindowHeight;
70 CGFloat deltaWindowY;
71 CGFloat deltaVisibleHeight;
72 CGFloat deltaVisibleY;
73 CGFloat deltaScrollerHeight;
74 CGFloat deltaScrollerY;
75
76 // Convenience metrics used in multiple functions (carried along here in
77 // order to eliminate the need to calculate in multiple places and
78 // reduce the possibility of bugs).
79 CGFloat minimumY;
80 CGFloat oldWindowY;
81 CGFloat folderY;
82 CGFloat folderTop;
83
84 LayoutMetrics(CGFloat windowLeft, NSSize windowSize, CGFloat scrollDelta) :
85 windowLeft(windowLeft),
86 windowSize(windowSize),
87 scrollDelta(scrollDelta),
88 couldScrollUp(NO),
89 canScrollUp(NO),
90 couldScrollDown(NO),
91 canScrollDown(NO),
92 preScroll(NO),
93 deltaWindowHeight(0.0),
94 deltaWindowY(0.0),
95 deltaVisibleHeight(0.0),
96 deltaVisibleY(0.0),
97 deltaScrollerHeight(0.0),
98 deltaScrollerY(0.0),
99 oldWindowY(0.0),
100 folderY(0.0),
101 folderTop(0.0) {}
102 };
42 103
43 } // namespace 104 } // namespace
44 105
45 @interface BookmarkBarFolderController(Private) 106 @interface BookmarkBarFolderController(Private)
46 - (void)configureWindow; 107 - (void)configureWindow;
47 - (void)addOrUpdateScrollTracking; 108 - (void)addOrUpdateScrollTracking;
48 - (void)removeScrollTracking; 109 - (void)removeScrollTracking;
49 - (void)endScroll; 110 - (void)endScroll;
50 - (void)addScrollTimerWithDelta:(CGFloat)delta; 111 - (void)addScrollTimerWithDelta:(CGFloat)delta;
51 112
113 // Helper function to configureWindow which performs a basic layout of
114 // the window subviews, in particular the menu buttons and the window width.
115 - (void)layOutWindowWithHeight:(CGFloat)height;
116
52 // Determine the best button width (which will be the widest button or the 117 // 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. 118 // 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). 119 // Return the new width so that the window can be adjusted.
55 - (CGFloat)adjustButtonWidths; 120 - (CGFloat)adjustButtonWidths;
56 121
57 // Returns the total menu height needed to display |buttonCount| buttons. 122 // 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. 123 // Does not do any fancy tricks like trimming the height to fit on the screen.
59 - (int)windowHeightForButtonCount:(int)buttonCount; 124 - (int)menuHeightForButtonCount:(int)buttonCount;
60 125
61 // Adjust the height and horizontal position of the window such that the 126 // Adjust layout of the folder menu window components, showing/hiding the
62 // scroll arrows are shown as needed and the window appears completely 127 // scroll up/down arrows, and resizing as necessary for a proper disaplay.
63 // on screen. 128 // In order to reduce window flicker, all layout changes are deferred until
64 - (void)adjustWindowForHeight:(int)windowHeight; 129 // the final step of the adjustment. To accommodate this deferral, window
130 // height and width changes needed by callers to this function pass their
131 // desired window changes in |size|. When scrolling is to be performed
132 // any scrolling change is given by |scrollDelta|. The ultimate amount of
133 // scrolling may be different from |scrollDelta| in order to accommodate
134 // changes in the scroller view layout. These proposed window adjustments
135 // are passed to helper functions using a LayoutMetrics structure.
136 //
137 // This function should be called when: 1) initially setting up a folder menu
138 // window, 2) responding to scrolling of the contents (which may affect the
139 // height of the window), 3) addition or removal of bookmark items (such as
140 // during cut/paste/delete/drag/drop operations).
141 - (void)adjustWindowLeft:(CGFloat)windowLeft
142 size:(NSSize)windowSize
143 scrollingBy:(CGFloat)scrollDelta;
65 144
66 // Show or hide the scroll arrows at the top/bottom of the window. 145 // Support function for adjustWindowLeft:size:scrollingBy: which initializes
67 - (void)showOrHideScrollArrows; 146 // the layout adjustments by gathering current folder menu window and subviews
147 // positions and sizes. This information is set in the |layoutMetrics|
148 // structure.
149 - (void)gatherMetrics:(LayoutMetrics*)layoutMetrics;
150
151 // Support function for adjustWindowLeft:size:scrollingBy: which calculates
152 // the changes which must be applied to the folder menu window and subviews
153 // positions and sizes. |layoutMetrics| contains the proposed window size
154 // and scrolling along with the other current window and subview layout
155 // information. The values in |layoutMetrics| are then adjusted to
156 // accommodate scroll arrow presentation and window growth.
157 - (void)adjustMetrics:(LayoutMetrics*)layoutMetrics;
158
159 // Support function for adjustMetrics: which calculates the layout changes
160 // required to accommodate changes in the position and scrollability
161 // of the top of the folder menu window.
162 - (void)adjustMetricsForMenuTopChanges:(LayoutMetrics*)layoutMetrics;
163
164 // Support function for adjustMetrics: which calculates the layout changes
165 // required to accommodate changes in the position and scrollability
166 // of the bottom of the folder menu window.
167 - (void)adjustMetricsForMenuBottomChanges:(LayoutMetrics*)layoutMetrics;
168
169 // Support function for adjustWindowLeft:size:scrollingBy: which applies
170 // the layout adjustments to the folder menu window and subviews.
171 - (void)applyMetrics:(LayoutMetrics*)layoutMetrics;
172
173 // This function is called when buttons are added or removed from the folder
174 // menu, and which may require a change in the layout of the folder menu
175 // window. Such layout changes may include horizontal placement, width,
176 // height, and scroller visibility changes. (This function calls through
177 // to -[adjustWindowLeft:size:scrollingBy:].)
178 // |buttonCount| should contain the updated count of menu buttons.
179 - (void)adjustWindowForButtonCount:(NSUInteger)buttonCount;
180
181 // A helper function which takes the desired amount to scroll, given by
182 // |scrollDelta|, and calculates the actual scrolling change to be applied
183 // taking into account the layout of the folder menu window and any
184 // changes in it's scrollability. (For example, when scrolling down and the
185 // top-most menu item is coming into view we will only scroll enough for
186 // that item to be completely presented, which may be less than the
187 // scroll amount requested.)
188 - (CGFloat)determineFinalScrollDelta:(CGFloat)scrollDelta;
68 189
69 // |point| is in the base coordinate system of the destination window; 190 // |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 191 // 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 192 // 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 193 // the old location, otherwise move the bookmark by removing from its old
73 // location and inserting into the new location. 194 // location and inserting into the new location.
74 - (BOOL)dragBookmark:(const BookmarkNode*)sourceNode 195 - (BOOL)dragBookmark:(const BookmarkNode*)sourceNode
75 to:(NSPoint)point 196 to:(NSPoint)point
76 copy:(BOOL)copy; 197 copy:(BOOL)copy;
77 198
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after
124 245
125 parentController_.reset([parentController retain]); 246 parentController_.reset([parentController retain]);
126 if (!parentController_) 247 if (!parentController_)
127 [self setSubFolderGrowthToRight:YES]; 248 [self setSubFolderGrowthToRight:YES];
128 else 249 else
129 [self setSubFolderGrowthToRight:[parentController 250 [self setSubFolderGrowthToRight:[parentController
130 subFolderGrowthToRight]]; 251 subFolderGrowthToRight]];
131 barController_ = barController; // WEAK 252 barController_ = barController; // WEAK
132 buttons_.reset([[NSMutableArray alloc] init]); 253 buttons_.reset([[NSMutableArray alloc] init]);
133 folderTarget_.reset([[BookmarkFolderTarget alloc] initWithController:self]); 254 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]; 255 [self configureWindow];
138 hoverState_.reset([[BookmarkBarFolderHoverState alloc] init]); 256 hoverState_.reset([[BookmarkBarFolderHoverState alloc] init]);
139 } 257 }
140 return self; 258 return self;
141 } 259 }
142 260
143 - (void)dealloc { 261 - (void)dealloc {
144 // The button is no longer part of the menu path. 262 // The button is no longer part of the menu path.
145 [parentButton_ forceButtonBorderToStayOnAlways:NO]; 263 [parentButton_ forceButtonBorderToStayOnAlways:NO];
146 [parentButton_ setNeedsDisplay]; 264 [parentButton_ setNeedsDisplay];
147 265
148 [self removeScrollTracking]; 266 [self removeScrollTracking];
149 [self endScroll]; 267 [self endScroll];
150 [hoverState_ draggingExited]; 268 [hoverState_ draggingExited];
151 269
152 // Delegate pattern does not retain; make sure pointers to us are removed. 270 // Delegate pattern does not retain; make sure pointers to us are removed.
153 for (BookmarkButton* button in buttons_.get()) { 271 for (BookmarkButton* button in buttons_.get()) {
154 [button setDelegate:nil]; 272 [button setDelegate:nil];
155 [button setTarget:nil]; 273 [button setTarget:nil];
156 [button setAction:nil]; 274 [button setAction:nil];
157 } 275 }
158 276
159 // Note: we don't need to 277 // Note: we don't need to
160 // [NSObject cancelPreviousPerformRequestsWithTarget:self]; 278 // [NSObject cancelPreviousPerformRequestsWithTarget:self];
161 // Because all of our performSelector: calls use withDelay: which 279 // Because all of our performSelector: calls use withDelay: which
162 // retains us. 280 // retains us.
163 [super dealloc]; 281 [super dealloc];
164 } 282 }
165 283
284 - (void)awakeFromNib {
285 NSRect windowFrame = [[self window] frame];
286 NSRect scrollViewFrame = [scrollView_ frame];
287 padding_ = NSWidth(windowFrame) - NSWidth(scrollViewFrame);
288 verticalScrollArrowHeight_ = NSHeight([scrollUpArrowView_ frame]);
289 }
290
166 // Overriden from NSWindowController to call childFolderWillShow: before showing 291 // Overriden from NSWindowController to call childFolderWillShow: before showing
167 // the window. 292 // the window.
168 - (void)showWindow:(id)sender { 293 - (void)showWindow:(id)sender {
169 [barController_ childFolderWillShow:self]; 294 [barController_ childFolderWillShow:self];
170 [super showWindow:sender]; 295 [super showWindow:sender];
171 } 296 }
172 297
173 - (BookmarkButton*)parentButton { 298 - (BookmarkButton*)parentButton {
174 return parentButton_.get(); 299 return parentButton_.get();
175 } 300 }
(...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after
269 urlString.c_str()]; 394 urlString.c_str()];
270 [button setToolTip:tooltip]; 395 [button setToolTip:tooltip];
271 } 396 }
272 } else { 397 } else {
273 [button setEnabled:NO]; 398 [button setEnabled:NO];
274 [button setBordered:NO]; 399 [button setBordered:NO];
275 } 400 }
276 return button; 401 return button;
277 } 402 }
278 403
279 // Exposed for testing.
280 - (NSView*)mainView {
281 return mainView_;
282 }
283
284 - (id)folderTarget { 404 - (id)folderTarget {
285 return folderTarget_.get(); 405 return folderTarget_.get();
286 } 406 }
287 407
288 408
289 // Our parent controller is another BookmarkBarFolderController, so 409 // Our parent controller is another BookmarkBarFolderController, so
290 // our window is to the right or left of it. We use a little overlap 410 // 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 411 // 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 412 // grow off the screen, switch growth to the other direction. Growth
293 // direction sticks for folder windows which are descendents of us. 413 // direction sticks for folder windows which are descendents of us.
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
347 // is bottom of button's parent view. 467 // is bottom of button's parent view.
348 NSPoint buttonBottomLeftInScreen = 468 NSPoint buttonBottomLeftInScreen =
349 [[parentButton_ window] 469 [[parentButton_ window]
350 convertBaseToScreen:[parentButton_ 470 convertBaseToScreen:[parentButton_
351 convertPoint:NSZeroPoint toView:nil]]; 471 convertPoint:NSZeroPoint toView:nil]];
352 NSPoint bookmarkBarBottomLeftInScreen = 472 NSPoint bookmarkBarBottomLeftInScreen =
353 [[parentButton_ window] 473 [[parentButton_ window]
354 convertBaseToScreen:[[parentButton_ superview] 474 convertBaseToScreen:[[parentButton_ superview]
355 convertPoint:NSZeroPoint toView:nil]]; 475 convertPoint:NSZeroPoint toView:nil]];
356 newWindowTopLeft = NSMakePoint(buttonBottomLeftInScreen.x, 476 newWindowTopLeft = NSMakePoint(buttonBottomLeftInScreen.x,
357 bookmarkBarBottomLeftInScreen.y); 477 bookmarkBarBottomLeftInScreen.y +
478 bookmarks::kBookmarkBarMenuOffset);
358 // Make sure the window is on-screen; if not, push left. It is 479 // Make sure the window is on-screen; if not, push left. It is
359 // intentional that top level folders "push left" slightly 480 // intentional that top level folders "push left" slightly
360 // different than subfolders. 481 // different than subfolders.
361 NSRect screenFrame = [[[parentButton_ window] screen] frame]; 482 NSRect screenFrame = [[[parentButton_ window] screen] frame];
362 CGFloat spillOff = (newWindowTopLeft.x + windowWidth) - NSMaxX(screenFrame); 483 CGFloat spillOff = (newWindowTopLeft.x + windowWidth) - NSMaxX(screenFrame);
363 if (spillOff > 0.0) { 484 if (spillOff > 0.0) {
364 newWindowTopLeft.x = std::max(newWindowTopLeft.x - spillOff, 485 newWindowTopLeft.x = std::max(newWindowTopLeft.x - spillOff,
365 NSMinX(screenFrame)); 486 NSMinX(screenFrame));
366 } 487 }
367 } else { 488 } else {
368 // Parent is a folder; grow right/left. 489 // Parent is a folder; grow right/left.
369 newWindowTopLeft.x = [self childFolderWindowLeftForWidth:windowWidth]; 490 newWindowTopLeft.x = [self childFolderWindowLeftForWidth:windowWidth];
370 NSPoint top = NSMakePoint(0, (NSMaxY([parentButton_ frame]) + 491 NSPoint topOfWindow = NSMakePoint(0,
371 bookmarks::kBookmarkVerticalPadding)); 492 (NSMaxY([parentButton_ frame]) +
372 NSPoint topOfWindow = 493 bookmarks::kBookmarkVerticalPadding));
373 [[parentButton_ window] 494 topOfWindow = [[parentButton_ window]
374 convertBaseToScreen:[[parentButton_ superview] 495 convertBaseToScreen:[[parentButton_ superview]
375 convertPoint:top toView:nil]]; 496 convertPoint:topOfWindow toView:nil]];
376 newWindowTopLeft.y = topOfWindow.y; 497 newWindowTopLeft.y = topOfWindow.y;
377 } 498 }
378 return newWindowTopLeft; 499 return newWindowTopLeft;
379 } 500 }
380 501
381 // Set our window level to the right spot so we're above the menubar, dock, etc. 502 // 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. 503 // Factored out so we can override/noop in a unit test.
383 - (void)configureWindowLevel { 504 - (void)configureWindowLevel {
384 [[self window] setLevel:NSPopUpMenuWindowLevel]; 505 [[self window] setLevel:NSPopUpMenuWindowLevel];
385 } 506 }
386 507
387 - (int)windowHeightForButtonCount:(int)buttonCount { 508 - (int)menuHeightForButtonCount:(int)buttonCount {
509 // This does not take into account any padding which may be required at the
510 // top and/or bottom of the window.
388 return (buttonCount * bookmarks::kBookmarkButtonVerticalSpan) + 511 return (buttonCount * bookmarks::kBookmarkButtonVerticalSpan) +
389 bookmarks::kBookmarkVerticalPadding; 512 bookmarks::kBookmarkVerticalPadding;
390 } 513 }
391 514
392 - (void)adjustWindowForHeight:(int)windowHeight { 515 - (void)adjustWindowLeft:(CGFloat)windowLeft
393 // Adjust all button widths to be consistent, determine the best size for 516 size:(NSSize)windowSize
394 // the window, and set the window frame. 517 scrollingBy:(CGFloat)scrollDelta {
395 CGFloat windowWidth = 518 // Callers of this function should make adjustments to the vertical
396 [self adjustButtonWidths] + 519 // attributes of the folder view only (height, scroll position).
397 (2 * bookmarks::kBookmarkSubMenuHorizontalPadding); 520 // This function will then make appropriate layout adjustments in order
521 // to accommodate screen/dock margins, scroll-up and scroll-down arrow
522 // presentation, etc.
523 // The 4 views whose vertical height and origins may be adjusted
524 // by this function are:
525 // 1) window, 2) visible content view, 3) scroller view, 4) folder view.
526
527 LayoutMetrics layoutMetrics(windowLeft, windowSize, scrollDelta);
528 [self gatherMetrics:&layoutMetrics];
529 [self adjustMetrics:&layoutMetrics];
530 [self applyMetrics:&layoutMetrics];
531 }
532
533 - (void)gatherMetrics:(LayoutMetrics*)layoutMetrics {
534 LayoutMetrics& metrics(*layoutMetrics);
535 NSWindow* window = [self window];
536 metrics.windowFrame = [window frame];
537 metrics.visibleFrame = [visibleView_ frame];
538 metrics.scrollerFrame = [scrollView_ frame];
539 metrics.scrollPoint = [scrollView_ documentVisibleRect].origin;
540 metrics.scrollPoint.y -= metrics.scrollDelta;
541 metrics.couldScrollUp = ![scrollUpArrowView_ isHidden];
542 metrics.couldScrollDown = ![scrollDownArrowView_ isHidden];
543
544 metrics.deltaWindowHeight = 0.0;
545 metrics.deltaWindowY = 0.0;
546 metrics.deltaVisibleHeight = 0.0;
547 metrics.deltaVisibleY = 0.0;
548 metrics.deltaScrollerHeight = 0.0;
549 metrics.deltaScrollerY = 0.0;
550
551 metrics.minimumY = NSMinY([[window screen] visibleFrame]) +
552 bookmarks::kScrollWindowVerticalMargin;
553 metrics.oldWindowY = NSMinY(metrics.windowFrame);
554 metrics.folderY =
555 metrics.scrollerFrame.origin.y + metrics.visibleFrame.origin.y +
556 metrics.oldWindowY - metrics.scrollPoint.y;
557 metrics.folderTop = metrics.folderY + NSHeight([folderView_ frame]);
558 }
559
560 - (void)adjustMetrics:(LayoutMetrics*)layoutMetrics {
561 LayoutMetrics& metrics(*layoutMetrics);
562 NSScreen* screen = [[self window] screen];
563 CGFloat effectiveFolderY = metrics.folderY;
564 if (!metrics.couldScrollUp && !metrics.couldScrollDown)
565 effectiveFolderY -= metrics.windowSize.height;
566 metrics.canScrollUp = effectiveFolderY < metrics.minimumY;
567 CGFloat maximumY =
568 NSMaxY([screen frame]) - bookmarks::kScrollWindowVerticalMargin;
569 metrics.canScrollDown = metrics.folderTop > maximumY;
570
571 // Accommodate changes in the bottom of the menu.
572 [self adjustMetricsForMenuTopChanges:layoutMetrics];
573
574 // Accommodate changes in the top of the menu.
575 [self adjustMetricsForMenuBottomChanges:layoutMetrics];
576
577 metrics.scrollerFrame.origin.y += metrics.deltaScrollerY;
578 metrics.scrollerFrame.size.height += metrics.deltaScrollerHeight;
579 metrics.visibleFrame.origin.y += metrics.deltaVisibleY;
580 metrics.visibleFrame.size.height += metrics.deltaVisibleHeight;
581 metrics.preScroll = metrics.canScrollUp && !metrics.couldScrollUp &&
582 metrics.scrollDelta == 0.0 && metrics.deltaWindowHeight >= 0.0;
583 metrics.windowFrame.origin.y += metrics.deltaWindowY;
584 metrics.windowFrame.origin.x = metrics.windowLeft;
585 metrics.windowFrame.size.height += metrics.deltaWindowHeight;
586 metrics.windowFrame.size.width = metrics.windowSize.width;
587 }
588
589 - (void)adjustMetricsForMenuTopChanges:(LayoutMetrics*)layoutMetrics {
590 LayoutMetrics& metrics(*layoutMetrics);
591 if (metrics.canScrollUp) {
592 if (!metrics.couldScrollUp) {
593 // Couldn't -> Can
594 metrics.deltaWindowY = -metrics.oldWindowY;
595 metrics.deltaWindowHeight = -metrics.deltaWindowY;
596 metrics.deltaVisibleY = metrics.minimumY;
597 metrics.deltaVisibleHeight = -metrics.deltaVisibleY;
598 metrics.deltaScrollerY = verticalScrollArrowHeight_;
599 metrics.deltaScrollerHeight = -metrics.deltaScrollerY;
600 // Adjust the scroll delta if we've grown the window and it is
601 // now scroll-up-able, but don't adjust it factor if we've
602 // scrolled down and it wasn't scroll-up-able but now is.
603 if (metrics.canScrollDown == metrics.couldScrollDown) {
604 CGFloat deltaScroll = metrics.deltaWindowY + metrics.deltaScrollerY +
605 metrics.deltaVisibleY;
606 metrics.scrollPoint.y += deltaScroll + metrics.windowSize.height;
607 }
608 } else if (!metrics.canScrollDown && metrics.windowSize.height > 0.0) {
609 metrics.scrollPoint.y += metrics.windowSize.height;
610 }
611 } else {
612 if (metrics.couldScrollUp) {
613 // Could -> Can't
614 metrics.deltaWindowY = metrics.folderY - metrics.oldWindowY;
615 metrics.deltaWindowHeight = -metrics.deltaWindowY;
616 metrics.deltaVisibleY = -bookmarks::kScrollWindowVerticalMargin;
617 metrics.deltaVisibleHeight = -metrics.deltaVisibleY;
618 metrics.deltaScrollerY = -verticalScrollArrowHeight_;
619 metrics.deltaScrollerHeight = -metrics.deltaScrollerY;
620 // Adjust the scroll delta if we are no longer scroll-up-able
621 // and the scroll-down-able-ness hasn't changed.
622 if (metrics.canScrollDown == metrics.couldScrollDown) {
623 CGFloat deltaScroll = metrics.deltaWindowY + metrics.deltaScrollerY +
624 metrics.deltaVisibleY;
625 metrics.scrollPoint.y += deltaScroll;
626 }
627 } else {
628 // Couldn't -> Can't
629 // Check for menu height change by looking at the relative tops of the
630 // menu folder and the window folder, which previously would have been
631 // the same.
632 metrics.deltaWindowY = NSMaxY(metrics.windowFrame) - metrics.folderTop;
633 metrics.deltaWindowHeight = -metrics.deltaWindowY;
634 }
635 }
636 }
637
638 - (void)adjustMetricsForMenuBottomChanges:(LayoutMetrics*)layoutMetrics {
639 LayoutMetrics& metrics(*layoutMetrics);
640 if (metrics.canScrollDown == metrics.couldScrollDown) {
641 if (!metrics.canScrollDown) {
642 // Not scroll-down-able but the menu top has changed.
643 metrics.deltaWindowHeight += metrics.scrollDelta;
644 }
645 } else {
646 if (metrics.canScrollDown) {
647 // Couldn't -> Can
648 metrics.deltaWindowHeight += (NSMaxY([[[self window] screen] frame]) -
649 NSMaxY(metrics.windowFrame));
650 metrics.deltaVisibleHeight -= bookmarks::kScrollWindowVerticalMargin;
651 metrics.deltaScrollerHeight -= verticalScrollArrowHeight_;
652 } else {
653 // Could -> Can't
654 metrics.deltaWindowHeight -= bookmarks::kScrollWindowVerticalMargin;
655 metrics.deltaVisibleHeight += bookmarks::kScrollWindowVerticalMargin;
656 metrics.deltaScrollerHeight += verticalScrollArrowHeight_;
657 }
658 }
659 }
660
661 - (void)applyMetrics:(LayoutMetrics*)layoutMetrics {
662 LayoutMetrics& metrics(*layoutMetrics);
663 // Hide or show the scroll arrows.
664 if (metrics.canScrollUp != metrics.couldScrollUp)
665 [scrollUpArrowView_ setHidden:metrics.couldScrollUp];
666 if (metrics.canScrollDown != metrics.couldScrollDown)
667 [scrollDownArrowView_ setHidden:metrics.couldScrollDown];
668
669 // Adjust the geometry. The order is important because of sizer dependencies.
670 [scrollView_ setFrame:metrics.scrollerFrame];
671 [visibleView_ setFrame:metrics.visibleFrame];
672 // This little bit of trickery handles the one special case where
673 // the window is now scroll-up-able _and_ going to be resized -- scroll
674 // first in order to prevent flashing.
675 if (metrics.preScroll)
676 [[scrollView_ documentView] scrollPoint:metrics.scrollPoint];
677
678 [[self window] setFrame:metrics.windowFrame display:YES];
679
680 // In all other cases we defer scrolling until the window has been resized
681 // in order to prevent flashing.
682 if (!metrics.preScroll)
683 [[scrollView_ documentView] scrollPoint:metrics.scrollPoint];
684
685 if (metrics.canScrollUp != metrics.couldScrollUp ||
686 metrics.canScrollDown != metrics.couldScrollDown ||
687 metrics.scrollDelta != 0.0) {
688 if (metrics.canScrollUp || metrics.canScrollDown)
689 [self addOrUpdateScrollTracking];
690 else
691 [self removeScrollTracking];
692 }
693 }
694
695 - (void)adjustWindowForButtonCount:(NSUInteger)buttonCount {
696 NSRect folderFrame = [folderView_ frame];
697 CGFloat newMenuHeight =
698 (CGFloat)[self menuHeightForButtonCount:[buttons_ count]];
699 CGFloat deltaMenuHeight = newMenuHeight - NSHeight(folderFrame);
700 // If the height has changed then also change the origin, and adjust the
701 // scroll (if scrolling).
702 if ([self canScrollUp]) {
703 NSPoint scrollPoint = [scrollView_ documentVisibleRect].origin;
704 scrollPoint.y += deltaMenuHeight;
705 [[scrollView_ documentView] scrollPoint:scrollPoint];
706 }
707 folderFrame.size.height += deltaMenuHeight;
708 [folderView_ setFrameSize:folderFrame.size];
709 CGFloat windowWidth = [self adjustButtonWidths] + padding_;
398 NSPoint newWindowTopLeft = [self windowTopLeftForWidth:windowWidth]; 710 NSPoint newWindowTopLeft = [self windowTopLeftForWidth:windowWidth];
399 NSSize windowSize = NSMakeSize(windowWidth, windowHeight); 711 CGFloat left = newWindowTopLeft.x;
400 windowSize = [scrollView_ convertSize:windowSize toView:nil]; 712 NSSize newSize = NSMakeSize(windowWidth, deltaMenuHeight);
401 NSWindow* window = [self window]; 713 [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 } 714 }
454 715
455 // Determine window size and position. 716 // Determine window size and position.
456 // Create buttons for all our nodes. 717 // Create buttons for all our nodes.
457 // TODO(jrg): break up into more and smaller routines for easier unit testing. 718 // TODO(jrg): break up into more and smaller routines for easier unit testing.
458 - (void)configureWindow { 719 - (void)configureWindow {
459 const BookmarkNode* node = [parentButton_ bookmarkNode]; 720 const BookmarkNode* node = [parentButton_ bookmarkNode];
460 DCHECK(node); 721 DCHECK(node);
461 int startingIndex = [[parentButton_ cell] startingChildIndex]; 722 int startingIndex = [[parentButton_ cell] startingChildIndex];
462 DCHECK_LE(startingIndex, node->GetChildCount()); 723 DCHECK_LE(startingIndex, node->GetChildCount());
463 // Must have at least 1 button (for "empty") 724 // Must have at least 1 button (for "empty")
464 int buttons = std::max(node->GetChildCount() - startingIndex, 1); 725 int buttons = std::max(node->GetChildCount() - startingIndex, 1);
465 726
466 // Prelim height of the window. We'll trim later as needed. 727 // Prelim height of the window. We'll trim later as needed.
467 int height = [self windowHeightForButtonCount:buttons]; 728 int height = [self menuHeightForButtonCount:buttons];
468 // We'll need this soon... 729 // We'll need this soon...
469 [self window]; 730 [self window];
470 731
471 // TODO(jrg): combine with frame code in bookmark_bar_controller.mm 732 // TODO(jrg): combine with frame code in bookmark_bar_controller.mm
472 // http://crbug.com/35966 733 // http://crbug.com/35966
473 NSRect buttonsOuterFrame = NSMakeRect( 734 NSRect buttonsOuterFrame = NSMakeRect(
474 bookmarks::kBookmarkSubMenuHorizontalPadding, 735 0,
475 (height - bookmarks::kBookmarkButtonVerticalSpan), 736 (height - bookmarks::kBookmarkButtonVerticalSpan),
476 bookmarks::kDefaultBookmarkWidth, 737 bookmarks::kDefaultBookmarkWidth,
477 bookmarks::kBookmarkButtonHeight); 738 bookmarks::kBookmarkButtonHeight);
478 739
479 // TODO(jrg): combine with addNodesToButtonList: code from 740 // TODO(jrg): combine with addNodesToButtonList: code from
480 // bookmark_bar_controller.mm (but use y offset) 741 // bookmark_bar_controller.mm (but use y offset)
481 // http://crbug.com/35966 742 // http://crbug.com/35966
482 if (!node->GetChildCount()) { 743 if (!node->GetChildCount()) {
483 // If no children we are the empty button. 744 // If no children we are the empty button.
484 BookmarkButton* button = [self makeButtonForNode:nil 745 BookmarkButton* button = [self makeButtonForNode:nil
485 frame:buttonsOuterFrame]; 746 frame:buttonsOuterFrame];
486 [buttons_ addObject:button]; 747 [buttons_ addObject:button];
487 [mainView_ addSubview:button]; 748 [folderView_ addSubview:button];
488 } else { 749 } else {
489 for (int i = startingIndex; 750 for (int i = startingIndex;
490 i < node->GetChildCount(); 751 i < node->GetChildCount();
491 i++) { 752 i++) {
492 const BookmarkNode* child = node->GetChild(i); 753 const BookmarkNode* child = node->GetChild(i);
493 BookmarkButton* button = [self makeButtonForNode:child 754 BookmarkButton* button = [self makeButtonForNode:child
494 frame:buttonsOuterFrame]; 755 frame:buttonsOuterFrame];
495 [buttons_ addObject:button]; 756 [buttons_ addObject:button];
496 [mainView_ addSubview:button]; 757 [folderView_ addSubview:button];
497 buttonsOuterFrame.origin.y -= bookmarks::kBookmarkButtonVerticalSpan; 758 buttonsOuterFrame.origin.y -= bookmarks::kBookmarkButtonVerticalSpan;
498 } 759 }
499 } 760 }
761 [self layOutWindowWithHeight:height];
762 }
500 763
501 [self adjustWindowForHeight:height]; 764 - (void)layOutWindowWithHeight:(CGFloat)height {
502 // Finally pop me up. 765 // Lay out the window by adjusting all button widths to be consistent, then
766 // base the window width on this ideal button width.
767 CGFloat buttonWidth = [self adjustButtonWidths];
768 CGFloat windowWidth = buttonWidth + padding_;
769 NSPoint newWindowTopLeft = [self windowTopLeftForWidth:windowWidth];
770 // Make sure as much of a submenu is exposed (which otherwise would be a
771 // problem if the parent button is close to the bottom of the screen).
772 if ([parentController_ isKindOfClass:[self class]]) {
773 newWindowTopLeft.y = MAX(newWindowTopLeft.y,
774 height + bookmarks::kScrollWindowVerticalMargin);
775 }
776 NSWindow* window = [self window];
777 NSRect windowFrame = NSMakeRect(newWindowTopLeft.x,
778 newWindowTopLeft.y - height,
779 windowWidth, height);
780 [window setFrame:windowFrame display:NO];
781 NSRect folderFrame = NSMakeRect(0, 0, windowWidth, height);
782 [folderView_ setFrame:folderFrame];
783 NSSize newSize = NSMakeSize(windowWidth, 0.0);
784 [self adjustWindowLeft:newWindowTopLeft.x size:newSize scrollingBy:0.0];
785 [window display];
503 [self configureWindowLevel]; 786 [self configureWindowLevel];
504 } 787 }
505 788
506 // TODO(mrossetti): See if the following can be moved into view's viewWillDraw:. 789 // TODO(mrossetti): See if the following can be moved into view's viewWillDraw:.
507 - (CGFloat)adjustButtonWidths { 790 - (CGFloat)adjustButtonWidths {
508 CGFloat width = bookmarks::kBookmarkMenuButtonMinimumWidth; 791 CGFloat width = bookmarks::kBookmarkMenuButtonMinimumWidth;
509 // Use the cell's size as the base for determining the desired width of the 792 // 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 793 // 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 794 // 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 795 // 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 796 // 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. 797 // the case where the widest button gets removed from a folder menu.
515 for (BookmarkButton* button in buttons_.get()) 798 for (BookmarkButton* button in buttons_.get())
516 width = std::max(width, [[button cell] cellSize].width); 799 width = std::max(width, [[button cell] cellSize].width);
517 width = std::min(width, bookmarks::kBookmarkMenuButtonMaximumWidth); 800 width = std::min(width, bookmarks::kBookmarkMenuButtonMaximumWidth);
518 // Things look and feel more menu-like if all the buttons are the 801 // Things look and feel more menu-like if all the buttons are the
519 // full width of the window, especially if there are submenus. 802 // full width of the window, especially if there are submenus.
520 for (BookmarkButton* button in buttons_.get()) { 803 for (BookmarkButton* button in buttons_.get()) {
521 NSRect buttonFrame = [button frame]; 804 NSRect buttonFrame = [button frame];
522 buttonFrame.size.width = width; 805 buttonFrame.size.width = width;
523 [button setFrame:buttonFrame]; 806 [button setFrame:buttonFrame];
524 } 807 }
525 return width; 808 return width;
526 } 809 }
527 810
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. 811 // Start a "scroll up" timer.
608 - (void)beginScrollWindowUp { 812 - (void)beginScrollWindowUp {
609 [self addScrollTimerWithDelta:kBookmarkBarFolderScrollAmount]; 813 [self addScrollTimerWithDelta:kBookmarkBarFolderScrollAmount];
610 } 814 }
611 815
612 // Start a "scroll down" timer. 816 // Start a "scroll down" timer.
613 - (void)beginScrollWindowDown { 817 - (void)beginScrollWindowDown {
614 [self addScrollTimerWithDelta:-kBookmarkBarFolderScrollAmount]; 818 [self addScrollTimerWithDelta:-kBookmarkBarFolderScrollAmount];
615 } 819 }
616 820
617 // End a scrolling timer. Can be called excessively with no harm. 821 // End a scrolling timer. Can be called excessively with no harm.
618 - (void)endScroll { 822 - (void)endScroll {
619 if (scrollTimer_) { 823 if (scrollTimer_) {
620 [scrollTimer_ invalidate]; 824 [scrollTimer_ invalidate];
621 scrollTimer_ = nil; 825 scrollTimer_ = nil;
622 verticalScrollDelta_ = 0; 826 verticalScrollDelta_ = 0;
623 } 827 }
624 } 828 }
625 829
626 // Perform a single scroll of the specified amount. 830 // 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 { 831 - (void)performOneScroll:(CGFloat)delta {
634 NSRect windowFrame = [[self window] frame]; 832 CGFloat finalDelta = [self determineFinalScrollDelta:delta];
635 NSRect screenFrame = [[[self window] screen] frame]; 833 if (finalDelta > 0.0 || finalDelta < 0.0) {
834 if (buttonThatMouseIsIn_)
835 [buttonThatMouseIsIn_ toggleButtonBorderingWhileMouseInside];
836 NSRect windowFrame = [[self window] frame];
837 NSSize newSize = NSMakeSize(NSWidth(windowFrame), 0.0);
838 [self adjustWindowLeft:windowFrame.origin.x
839 size:newSize
840 scrollingBy:finalDelta];
841 }
842 }
636 843
637 // First scroll the "document" area. 844 - (CGFloat)determineFinalScrollDelta:(CGFloat)delta {
638 NSPoint scrollPosition = [scrollView_ documentVisibleRect].origin; 845 if (delta > 0.0 && ![scrollUpArrowView_ isHidden] ||
639 scrollPosition.y -= delta; 846 delta < 0.0 && ![scrollDownArrowView_ isHidden]) {
640 [[scrollView_ documentView] scrollPoint:scrollPosition]; 847 NSWindow* window = [self window];
848 NSRect windowFrame = [window frame];
849 NSScreen* screen = [window screen];
850 NSPoint scrollPosition = [scrollView_ documentVisibleRect].origin;
851 CGFloat scrollY = scrollPosition.y;
852 NSRect scrollerFrame = [scrollView_ frame];
853 CGFloat scrollerY = NSMinY(scrollerFrame);
854 NSRect visibleFrame = [visibleView_ frame];
855 CGFloat visibleY = NSMinY(visibleFrame);
856 CGFloat windowY = NSMinY(windowFrame);
857 CGFloat offset = scrollerY + visibleY + windowY;
641 858
642 if (buttonThatMouseIsIn_) 859 if (delta > 0.0) {
643 [buttonThatMouseIsIn_ toggleButtonBorderingWhileMouseInside]; 860 // Scrolling up.
644 861 CGFloat minimumY = NSMinY([screen visibleFrame]) +
645 // We update the window size after shifting the scroll to avoid a race. 862 bookmarks::kScrollWindowVerticalMargin;
646 CGFloat screenHeightMinusMargin = (NSHeight(screenFrame) - 863 CGFloat maxUpDelta = scrollY - offset + minimumY;
647 (2 * kScrollWindowVerticalMargin)); 864 delta = MIN(delta, maxUpDelta);
648 if (delta) { 865 } else {
649 // If we can, grow the window (up). 866 // Scrolling down.
650 if (NSHeight(windowFrame) < screenHeightMinusMargin) { 867 NSRect screenFrame = [screen frame];
651 CGFloat growAmount = delta; 868 CGFloat topOfScreen = NSMaxY(screenFrame);
652 // Don't scroll more than enough to "finish". 869 NSRect folderFrame = [folderView_ frame];
653 if (scrollPosition.y < 0) 870 CGFloat folderHeight = NSHeight(folderFrame);
654 growAmount += scrollPosition.y; 871 CGFloat folderTop = folderHeight - scrollY + offset;
655 windowFrame.size.height += growAmount; 872 CGFloat maxDownDelta =
656 windowFrame.size.height = std::min(NSHeight(windowFrame), 873 topOfScreen - folderTop - bookmarks::kScrollWindowVerticalMargin;
657 screenHeightMinusMargin); 874 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 } 875 }
876 } else {
877 delta = 0.0;
669 } 878 }
670 879 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 } 880 }
689 881
690 // Perform a scroll of the window on the screen. 882 // Perform a scroll of the window on the screen.
691 // Called by a timer when scrolling. 883 // Called by a timer when scrolling.
692 - (void)performScroll:(NSTimer*)timer { 884 - (void)performScroll:(NSTimer*)timer {
693 DCHECK(verticalScrollDelta_); 885 DCHECK(verticalScrollDelta_);
694 [self performOneScroll:verticalScrollDelta_]; 886 [self performOneScroll:verticalScrollDelta_];
695 } 887 }
696 888
697 889
(...skipping 11 matching lines...) Expand all
709 userInfo:nil 901 userInfo:nil
710 repeats:YES]; 902 repeats:YES];
711 } 903 }
712 904
713 // Called as a result of our tracking area. Warning: on the main 905 // 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 906 // 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 907 // 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 908 // menubar (to be fixed by setting the proper window level; see
717 // initializer). 909 // initializer).
718 - (void)mouseMoved:(NSEvent*)theEvent { 910 - (void)mouseMoved:(NSEvent*)theEvent {
719 DCHECK([theEvent window] == [self window]); 911 NSWindow* window = [theEvent window];
912 DCHECK(window == [self window]);
720 913
721 NSPoint eventScreenLocation = 914 NSPoint eventScreenLocation =
722 [[theEvent window] convertBaseToScreen:[theEvent locationInWindow]]; 915 [window convertBaseToScreen:[theEvent locationInWindow]];
723 916
724 // We use frame (not visibleFrame) since our bookmark folder is on 917 // Base hot spot calculations on the positions of the scroll arrow views.
725 // TOP of the menubar. 918 NSRect testRect = [scrollDownArrowView_ frame];
726 NSRect visibleRect = [[[self window] screen] frame]; 919 NSPoint testPoint = [visibleView_ convertPoint:testRect.origin
727 CGFloat closeToTopOfScreen = NSMaxY(visibleRect) - 920 toView:nil];
728 verticalScrollArrowHeight_; 921 testPoint = [window convertBaseToScreen:testPoint];
729 CGFloat closeToBottomOfScreen = NSMinY(visibleRect) + 922 CGFloat closeToTopOfScreen = testPoint.y;
730 verticalScrollArrowHeight_;
731 923
732 if (eventScreenLocation.y <= closeToBottomOfScreen) { 924 testRect = [scrollUpArrowView_ frame];
925 testPoint = [visibleView_ convertPoint:testRect.origin toView:nil];
926 testPoint = [window convertBaseToScreen:testPoint];
927 CGFloat closeToBottomOfScreen = testPoint.y + testRect.size.height;
928 if (eventScreenLocation.y <= closeToBottomOfScreen &&
929 ![scrollUpArrowView_ isHidden]) {
733 [self beginScrollWindowUp]; 930 [self beginScrollWindowUp];
734 } else if (eventScreenLocation.y > closeToTopOfScreen) { 931 } else if (eventScreenLocation.y > closeToTopOfScreen &&
932 ![scrollDownArrowView_ isHidden]) {
735 [self beginScrollWindowDown]; 933 [self beginScrollWindowDown];
736 } else { 934 } else {
737 [self endScroll]; 935 [self endScroll];
738 } 936 }
739 } 937 }
740 938
741 - (void)mouseExited:(NSEvent*)theEvent { 939 - (void)mouseExited:(NSEvent*)theEvent {
742 [self endScroll]; 940 [self endScroll];
743 } 941 }
744 942
(...skipping 14 matching lines...) Expand all
759 } 957 }
760 958
761 // Remove the tracking area associated with scrolling. 959 // Remove the tracking area associated with scrolling.
762 - (void)removeScrollTracking { 960 - (void)removeScrollTracking {
763 if (scrollTrackingArea_.get()) { 961 if (scrollTrackingArea_.get()) {
764 [[[self window] contentView] removeTrackingArea:scrollTrackingArea_]; 962 [[[self window] contentView] removeTrackingArea:scrollTrackingArea_];
765 } 963 }
766 scrollTrackingArea_.reset(); 964 scrollTrackingArea_.reset();
767 } 965 }
768 966
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 967 // 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. 968 // do both in one step to allow for a delay in closing the old one.
782 // See comments above kDragHoverCloseDelay (bookmark_bar_controller.h) 969 // See comments above kDragHoverCloseDelay (bookmark_bar_controller.h)
783 // for more details. 970 // for more details.
784 - (void)openBookmarkFolderFromButtonAndCloseOldOne:(id)sender { 971 - (void)openBookmarkFolderFromButtonAndCloseOldOne:(id)sender {
785 // If an old submenu exists, close it immediately. 972 // If an old submenu exists, close it immediately.
786 [self closeBookmarkFolder:sender]; 973 [self closeBookmarkFolder:sender];
787 974
788 // Open a new one if meaningful. 975 // Open a new one if meaningful.
789 if ([sender isFolder]) 976 if ([sender isFolder])
790 [folderTarget_ openBookmarkFolderFromButton:sender]; 977 [folderTarget_ openBookmarkFolderFromButton:sender];
791 } 978 }
792 979
793 - (NSArray*)buttons { 980 - (NSArray*)buttons {
794 return buttons_.get(); 981 return buttons_.get();
795 } 982 }
796 983
797 - (void)close { 984 - (void)close {
798 [folderController_ close]; 985 [folderController_ close];
799 [super close]; 986 [super close];
800 } 987 }
801 988
802 - (void)scrollWheel:(NSEvent *)theEvent { 989 - (void)scrollWheel:(NSEvent *)theEvent {
803 if (scrollable_) { 990 if (![scrollUpArrowView_ isHidden] || ![scrollDownArrowView_ isHidden]) {
804 // We go negative since an NSScrollView has a flipped coordinate frame. 991 // We go negative since an NSScrollView has a flipped coordinate frame.
805 CGFloat amt = kBookmarkBarFolderScrollWheelAmount * -[theEvent deltaY]; 992 CGFloat amt = kBookmarkBarFolderScrollWheelAmount * -[theEvent deltaY];
806 [self performOneScroll:amt]; 993 [self performOneScroll:amt];
807 } 994 }
808 } 995 }
809 996
810 #pragma mark Actions Forwarded to Parent BookmarkBarController 997 #pragma mark Actions Forwarded to Parent BookmarkBarController
811 998
812 - (IBAction)openBookmark:(id)sender { 999 - (IBAction)openBookmark:(id)sender {
813 [barController_ openBookmark:sender]; 1000 [barController_ openBookmark:sender];
(...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after
919 // exist in the future). 1106 // exist in the future).
920 // http://crbug.com/35966 1107 // http://crbug.com/35966
921 - (int)indexForDragToPoint:(NSPoint)point { 1108 - (int)indexForDragToPoint:(NSPoint)point {
922 // Identify which buttons we are between. For now, assume a button 1109 // 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 1110 // location is at the center point of its view, and that an exact
924 // match means "place before". 1111 // match means "place before".
925 // TODO(jrg): revisit position info based on UI team feedback. 1112 // TODO(jrg): revisit position info based on UI team feedback.
926 // dropLocation is in bar local coordinates. 1113 // dropLocation is in bar local coordinates.
927 // http://crbug.com/36276 1114 // http://crbug.com/36276
928 NSPoint dropLocation = 1115 NSPoint dropLocation =
929 [mainView_ convertPoint:point 1116 [folderView_ convertPoint:point
930 fromView:[[self window] contentView]]; 1117 fromView:[[self window] contentView]];
931 BookmarkButton* buttonToTheTopOfDraggedButton = nil; 1118 BookmarkButton* buttonToTheTopOfDraggedButton = nil;
932 // Buttons are laid out in this array from top to bottom (screen 1119 // Buttons are laid out in this array from top to bottom (screen
933 // wise), which means "biggest y" --> "smallest y". 1120 // wise), which means "biggest y" --> "smallest y".
934 for (BookmarkButton* button in buttons_.get()) { 1121 for (BookmarkButton* button in buttons_.get()) {
935 CGFloat midpoint = NSMidY([button frame]); 1122 CGFloat midpoint = NSMidY([button frame]);
936 if (dropLocation.y > midpoint) { 1123 if (dropLocation.y > midpoint) {
937 break; 1124 break;
938 } 1125 }
939 buttonToTheTopOfDraggedButton = button; 1126 buttonToTheTopOfDraggedButton = button;
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
993 else 1180 else
994 [self bookmarkModel]->Move(sourceNode, destParent, destIndex); 1181 [self bookmarkModel]->Move(sourceNode, destParent, destIndex);
995 wasCopiedOrMoved = YES; 1182 wasCopiedOrMoved = YES;
996 // Movement of a node triggers observers (like us) to rebuild the 1183 // Movement of a node triggers observers (like us) to rebuild the
997 // bar so we don't have to do so explicitly. 1184 // bar so we don't have to do so explicitly.
998 } 1185 }
999 1186
1000 return wasCopiedOrMoved; 1187 return wasCopiedOrMoved;
1001 } 1188 }
1002 1189
1190 #pragma mark NSWindowDelegate Functions
1191
1192 - (void)windowWillClose:(NSNotification*)notification {
1193 // If a "hover open" is pending when the bookmark bar folder is
1194 // closed, be sure it gets cancelled.
1195 [NSObject cancelPreviousPerformRequestsWithTarget:self];
1196
1197 [self endScroll]; // Just in case we were scrolling.
1198 [barController_ childFolderWillClose:self];
1199 [self closeBookmarkFolder:self];
1200 [self autorelease];
1201 }
1202
1003 #pragma mark BookmarkButtonDelegate Protocol 1203 #pragma mark BookmarkButtonDelegate Protocol
1004 1204
1005 - (void)fillPasteboard:(NSPasteboard*)pboard 1205 - (void)fillPasteboard:(NSPasteboard*)pboard
1006 forDragOfButton:(BookmarkButton*)button { 1206 forDragOfButton:(BookmarkButton*)button {
1007 [[self folderTarget] fillPasteboard:pboard forDragOfButton:button]; 1207 [[self folderTarget] fillPasteboard:pboard forDragOfButton:button];
1008 1208
1009 // Close our folder menu and submenus since we know we're going to be dragged. 1209 // Close our folder menu and submenus since we know we're going to be dragged.
1010 [self closeBookmarkFolder:self]; 1210 [self closeBookmarkFolder:self];
1011 } 1211 }
1012 1212
(...skipping 260 matching lines...) Expand 10 before | Expand all | Expand 10 after
1273 // which is where the new button will be located. 1473 // which is where the new button will be located.
1274 newButtonFrame = [button frame]; 1474 newButtonFrame = [button frame];
1275 NSRect buttonFrame = [button frame]; 1475 NSRect buttonFrame = [button frame];
1276 buttonFrame.origin.y += bookmarks::kBookmarkButtonVerticalSpan; 1476 buttonFrame.origin.y += bookmarks::kBookmarkButtonVerticalSpan;
1277 [button setFrame:buttonFrame]; 1477 [button setFrame:buttonFrame];
1278 } 1478 }
1279 [[button cell] mouseExited:nil]; // De-highlight. 1479 [[button cell] mouseExited:nil]; // De-highlight.
1280 BookmarkButton* newButton = [self makeButtonForNode:node 1480 BookmarkButton* newButton = [self makeButtonForNode:node
1281 frame:newButtonFrame]; 1481 frame:newButtonFrame];
1282 [buttons_ insertObject:newButton atIndex:buttonIndex]; 1482 [buttons_ insertObject:newButton atIndex:buttonIndex];
1283 [mainView_ addSubview:newButton]; 1483 [folderView_ addSubview:newButton];
1284 1484
1285 // Close any child folder(s) which may still be open. 1485 // Close any child folder(s) which may still be open.
1286 [self closeBookmarkFolder:self]; 1486 [self closeBookmarkFolder:self];
1287 1487
1288 // Prelim height of the window. We'll trim later as needed. 1488 [self adjustWindowForButtonCount:[buttons_ count]];
1289 int height = [self windowHeightForButtonCount:[buttons_ count]];
1290 [self adjustWindowForHeight:height];
1291 } 1489 }
1292 1490
1293 // More code which essentially duplicates that of BookmarkBarController. 1491 // More code which essentially duplicates that of BookmarkBarController.
1294 // TODO(mrossetti,jrg): http://crbug.com/35966 1492 // TODO(mrossetti,jrg): http://crbug.com/35966
1295 - (BOOL)addURLs:(NSArray*)urls withTitles:(NSArray*)titles at:(NSPoint)point { 1493 - (BOOL)addURLs:(NSArray*)urls withTitles:(NSArray*)titles at:(NSPoint)point {
1296 DCHECK([urls count] == [titles count]); 1494 DCHECK([urls count] == [titles count]);
1297 BOOL nodesWereAdded = NO; 1495 BOOL nodesWereAdded = NO;
1298 // Figure out where these new bookmarks nodes are to be added. 1496 // Figure out where these new bookmarks nodes are to be added.
1299 BookmarkButton* button = [self buttonForDroppingOnAtPoint:point]; 1497 BookmarkButton* button = [self buttonForDroppingOnAtPoint:point];
1300 BookmarkModel* bookmarkModel = [self bookmarkModel]; 1498 BookmarkModel* bookmarkModel = [self bookmarkModel];
(...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after
1387 // http://crbug.com/54324 1585 // http://crbug.com/54324
1388 for (NSButton* button in buttons_.get()) { 1586 for (NSButton* button in buttons_.get()) {
1389 if ([button showsBorderOnlyWhileMouseInside]) { 1587 if ([button showsBorderOnlyWhileMouseInside]) {
1390 [button setShowsBorderOnlyWhileMouseInside:NO]; 1588 [button setShowsBorderOnlyWhileMouseInside:NO];
1391 [button setShowsBorderOnlyWhileMouseInside:YES]; 1589 [button setShowsBorderOnlyWhileMouseInside:YES];
1392 } 1590 }
1393 } 1591 }
1394 1592
1395 [oldButton setDelegate:nil]; 1593 [oldButton setDelegate:nil];
1396 [oldButton removeFromSuperview]; 1594 [oldButton removeFromSuperview];
1397 if (animate && !ignoreAnimations_)
1398 NSShowAnimationEffect(NSAnimationEffectDisappearingItemDefault, poofPoint,
1399 NSZeroSize, nil, nil, nil);
1400 [buttons_ removeObjectAtIndex:buttonIndex]; 1595 [buttons_ removeObjectAtIndex:buttonIndex];
1401 for (NSInteger i = 0; i < buttonIndex; ++i) { 1596 for (NSInteger i = 0; i < buttonIndex; ++i) {
1402 BookmarkButton* button = [buttons_ objectAtIndex:i]; 1597 BookmarkButton* button = [buttons_ objectAtIndex:i];
1403 NSRect buttonFrame = [button frame]; 1598 NSRect buttonFrame = [button frame];
1404 buttonFrame.origin.y -= bookmarks::kBookmarkButtonVerticalSpan; 1599 buttonFrame.origin.y -= bookmarks::kBookmarkButtonVerticalSpan;
1405 [button setFrame:buttonFrame]; 1600 [button setFrame:buttonFrame];
1406 } 1601 }
1407 // Search for and adjust submenus, if necessary. 1602 // Search for and adjust submenus, if necessary.
1408 NSInteger buttonCount = [buttons_ count]; 1603 NSInteger buttonCount = [buttons_ count];
1409 if (buttonCount) { 1604 if (buttonCount) {
1410 BookmarkButton* subButton = [folderController_ parentButton]; 1605 BookmarkButton* subButton = [folderController_ parentButton];
1411 for (NSInteger i = buttonIndex; i < buttonCount; ++i) { 1606 for (NSInteger i = buttonIndex; i < buttonCount; ++i) {
1412 BookmarkButton* aButton = [buttons_ objectAtIndex:i]; 1607 BookmarkButton* aButton = [buttons_ objectAtIndex:i];
1413 // If this button is showing its menu then we need to move the menu, too. 1608 // If this button is showing its menu then we need to move the menu, too.
1414 if (aButton == subButton) 1609 if (aButton == subButton)
1415 [folderController_ offsetFolderMenuWindow:NSMakeSize(0.0, 1610 [folderController_ offsetFolderMenuWindow:NSMakeSize(0.0,
1416 bookmarks::kBookmarkBarHeight)]; 1611 bookmarks::kBookmarkBarHeight)];
1417 } 1612 }
1418 } else { 1613 } else {
1419 // If all nodes have been removed from this folder then add in the 1614 // If all nodes have been removed from this folder then add in the
1420 // 'empty' placeholder button. 1615 // 'empty' placeholder button.
1421 NSRect buttonFrame = 1616 NSRect buttonFrame =
1422 NSMakeRect(bookmarks::kBookmarkSubMenuHorizontalPadding, 1617 NSMakeRect(0,
1423 bookmarks::kBookmarkButtonHeight - 1618 bookmarks::kBookmarkButtonHeight -
1424 (bookmarks::kBookmarkBarHeight - 1619 (bookmarks::kBookmarkBarHeight -
1425 bookmarks::kBookmarkVerticalPadding), 1620 bookmarks::kBookmarkVerticalPadding),
1426 bookmarks::kDefaultBookmarkWidth, 1621 bookmarks::kDefaultBookmarkWidth,
1427 (bookmarks::kBookmarkBarHeight - 1622 (bookmarks::kBookmarkBarHeight -
1428 2 * bookmarks::kBookmarkVerticalPadding)); 1623 2 * bookmarks::kBookmarkVerticalPadding));
1429 BookmarkButton* button = [self makeButtonForNode:nil 1624 BookmarkButton* button = [self makeButtonForNode:nil
1430 frame:buttonFrame]; 1625 frame:buttonFrame];
1431 [buttons_ addObject:button]; 1626 [buttons_ addObject:button];
1432 [mainView_ addSubview:button]; 1627 [folderView_ addSubview:button];
1433 buttonCount = 1; 1628 buttonCount = 1;
1434 } 1629 }
1435 1630
1436 // Propose a height for the window. We'll trim later as needed. 1631 [self adjustWindowForButtonCount:buttonCount];
1437 [self adjustWindowForHeight:[self windowHeightForButtonCount:buttonCount]]; 1632
1633 if (animate && !ignoreAnimations_)
1634 NSShowAnimationEffect(NSAnimationEffectDisappearingItemDefault, poofPoint,
1635 NSZeroSize, nil, nil, nil);
1438 } 1636 }
1439 1637
1440 - (id<BookmarkButtonControllerProtocol>)controllerForNode: 1638 - (id<BookmarkButtonControllerProtocol>)controllerForNode:
1441 (const BookmarkNode*)node { 1639 (const BookmarkNode*)node {
1442 // See if we are holding this node, otherwise see if it is in our 1640 // See if we are holding this node, otherwise see if it is in our
1443 // hierarchy of visible folder menus. 1641 // hierarchy of visible folder menus.
1444 if ([parentButton_ bookmarkNode] == node) 1642 if ([parentButton_ bookmarkNode] == node)
1445 return self; 1643 return self;
1446 return [folderController_ controllerForNode:node]; 1644 return [folderController_ controllerForNode:node];
1447 } 1645 }
1448 1646
1449 #pragma mark TestingAPI Only 1647 #pragma mark TestingAPI Only
1450 1648
1649 - (BOOL)canScrollUp {
1650 return ![scrollUpArrowView_ isHidden];
1651 }
1652
1653 - (BOOL)canScrollDown {
1654 return ![scrollDownArrowView_ isHidden];
1655 }
1656
1657 - (CGFloat)verticalScrollArrowHeight {
1658 return verticalScrollArrowHeight_;
1659 }
1660
1661 - (NSView*)visibleView {
1662 return visibleView_;
1663 }
1664
1665 - (NSView*)scrollView {
1666 return scrollView_;
1667 }
1668
1669 - (NSView*)folderView {
1670 return folderView_;
1671 }
1672
1451 - (void)setIgnoreAnimations:(BOOL)ignore { 1673 - (void)setIgnoreAnimations:(BOOL)ignore {
1452 ignoreAnimations_ = ignore; 1674 ignoreAnimations_ = ignore;
1453 } 1675 }
1454 1676
1455 - (BookmarkButton*)buttonThatMouseIsIn { 1677 - (BookmarkButton*)buttonThatMouseIsIn {
1456 return buttonThatMouseIsIn_; 1678 return buttonThatMouseIsIn_;
1457 } 1679 }
1458 1680
1459 @end // BookmarkBarFolderController 1681 @end // BookmarkBarFolderController
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698