diff options
author | mrossetti@chromium.org <mrossetti@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-01-11 18:58:30 +0000 |
---|---|---|
committer | mrossetti@chromium.org <mrossetti@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-01-11 18:58:30 +0000 |
commit | be0aa82ab771df70f1548dd712701a04b9e23c18 (patch) | |
tree | cb5d3c9847f931f98d9ea35c642a2c5ddf68c858 /chrome/browser/ui/cocoa/bookmarks | |
parent | 539a1ac415e94892d0bc4a41205afeeb878c8f72 (diff) | |
download | chromium_src-be0aa82ab771df70f1548dd712701a04b9e23c18.zip chromium_src-be0aa82ab771df70f1548dd712701a04b9e23c18.tar.gz chromium_src-be0aa82ab771df70f1548dd712701a04b9e23c18.tar.bz2 |
Rework how bookmark bar folder menus and submenus are layed out when scrolling, adding and removing bookmarks and during drag and drop operations. Make a subfolder menu appear as much on screen as possible.
Changed to the BookmarkBarFolderWindow.xib: Added view (visibleView_) between the window's content view and the rest of the content. Added scroll-up arrow and scroll-down arrow views. Added connections to controller of all views within the window.
BUG=54701
TEST=Test submenu appearance by creating a folder menu with lots of items with the bottom-most item being a sub-folder with several bookmark items of its own. Place the browser window such that the bookmark button for the sub-folder will appear near the bottom of the screen. Pop up the folder menu then pop up the sub-menu and verify that the sub-menu is shown on screen and does not require scrolling.
In all following cases of adding or removing bookmarks, either by using the contextual menus (cut/copy/paste/delete) or by dragging and dropping, verify that the top of the folder menu and the menu items (bookmark buttons) above the location of the addition/removal remain in the same position vertically on the screen; menu items below the affected bookmark should move upward (for a removal) or downward (for an addition) and the scroll-up arrow (the one at the bottom of the menu) should hide or show as appropriate.
Verify that dragging or pasting a wide menu item into a narrow folder menu causes the menu to widen. Verify that cutting or deleting a wide menu item from a folder menu will cause the menu to narrow.
Perform the listed tests for each of the folder menu arrangements.
Folder menu arrangements
------------------------
1. Folder with no items (one showing 'empty')
2. Folder with just a few items and neither the scroll-up or scroll-down arrow showing.
3. Folder with enough items such that adding one more should cause the scroll-up arrow to show.
4. Folder with enough items and the scroll-up arrow showing such that removing one item should cause the scroll-up arrow to hide.
5. Folder with enough items that the scroll-up arrow shows but after fully scrolling the menu fits completely on screen without any scrolling arrows showing.
6. Folder with enough items that the scroll-up arrow shows and can be scrolled up such that both scroll-up and scroll-down arrows can show with plenty of extra items.
Tests
-----
1. Cut a bookmark menu item.
2. Paste an item.
3. Paste another item.
4. Drag an item to a different place within the same folder menu.
5. Drag an item to a different folder menu (one each of those listed above).
6. Cut the first item.
7. Cut the last item.
8. Paste to the last item.
For scrollable arrangements #5 and #6:
9. Scroll up only a little (such that the scroll-down arrow does not appear) and cut an item.
10. Scroll up only a little (such that the scroll-down arrow does not appear) and paste an item.
For scrollable arrangement #6:
11. Scroll up so that both the scroll-up and scroll-down arrows appear and cut an item.
12. Scroll up so that both the scroll-up and scroll-down arrows appear and paste an item.
Review URL: http://codereview.chromium.org/5694001
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@71059 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/ui/cocoa/bookmarks')
7 files changed, 672 insertions, 461 deletions
diff --git a/chrome/browser/ui/cocoa/bookmarks/bookmark_bar_constants.h b/chrome/browser/ui/cocoa/bookmarks/bookmark_bar_constants.h index b4e4bef..febd2a3 100644 --- a/chrome/browser/ui/cocoa/bookmarks/bookmark_bar_constants.h +++ b/chrome/browser/ui/cocoa/bookmarks/bookmark_bar_constants.h @@ -33,6 +33,10 @@ const int kNTPBookmarkBarPadding = // The height of buttons in the bookmark bar. const int kBookmarkButtonHeight = kBookmarkBarHeight + kVisualHeightOffset; +// The radius of the corner curves on the menu. Also used for sizing the shadow +// window behind the menu window at times when the menu can be scrolled. +const CGFloat kBookmarkBarMenuCornerRadius = 4.0; + } // namespace bookmarks #endif // CHROME_BROWSER_UI_COCOA_BOOKMARKS_BOOKMARK_BAR_CONSTANTS_H_ diff --git a/chrome/browser/ui/cocoa/bookmarks/bookmark_bar_controller.h b/chrome/browser/ui/cocoa/bookmarks/bookmark_bar_controller.h index 64591ba..f3f6976 100644 --- a/chrome/browser/ui/cocoa/bookmarks/bookmark_bar_controller.h +++ b/chrome/browser/ui/cocoa/bookmarks/bookmark_bar_controller.h @@ -54,9 +54,6 @@ const CGFloat kBookmarkVerticalPadding = 2.0; const CGFloat kBookmarkMenuButtonMinimumWidth = 100.0; const CGFloat kBookmarkMenuButtonMaximumWidth = 485.0; -// Horizontal separation between a menu button and both edges of its menu. -const CGFloat kBookmarkSubMenuHorizontalPadding = 5.0; - // TODO(mrossetti): Add constant (kBookmarkVerticalSeparation) for the gap // between buttons in a folder menu. Right now we're using // kBookmarkVerticalPadding, which is dual purpose and wrong. @@ -82,7 +79,17 @@ const CGFloat kScrollViewContentWidthMargin = 2; // Make subfolder menus overlap their parent menu a bit to give a better // perception of a menuing system. -const CGFloat kBookmarkMenuOverlap = 5.0; +const CGFloat kBookmarkMenuOverlap = 2.0; + +// When constraining a scrolling bookmark bar folder window to the +// screen, shrink the "constrain" by this much vertically. Currently +// this is 0.0 to avoid a problem with tracking areas leaving the +// window, but should probably be 8.0 or something. +const CGFloat kScrollWindowVerticalMargin = 6.0; + +// How far to offset a folder menu from the top of the bookmark bar. This +// is set just above the bar so that it become distinctive when drawn. +const CGFloat kBookmarkBarMenuOffset = 2.0; // Delay before opening a subfolder (and closing the previous one) // when hovering over a folder button. diff --git a/chrome/browser/ui/cocoa/bookmarks/bookmark_bar_folder_controller.h b/chrome/browser/ui/cocoa/bookmarks/bookmark_bar_folder_controller.h index 083efac..683c5ca 100644 --- a/chrome/browser/ui/cocoa/bookmarks/bookmark_bar_folder_controller.h +++ b/chrome/browser/ui/cocoa/bookmarks/bookmark_bar_folder_controller.h @@ -2,6 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#ifndef CHROME_BROWSER_UI_COCOA_BOOKMARKS_BOOKMARK_BAR_FOLDER_CONTROLLER_H_ +#define CHROME_BROWSER_UI_COCOA_BOOKMARKS_BOOKMARK_BAR_FOLDER_CONTROLLER_H_ +#pragma once + #import <Cocoa/Cocoa.h> #include "base/scoped_nsobject.h" @@ -11,6 +15,8 @@ @class BookmarkBarFolderView; @class BookmarkFolderTarget; @class BookmarkBarFolderHoverState; +@class BookmarkBarFolderWindow; +@class BookmarkBarFolderWindowContentView; // A controller for the pop-up windows from bookmark folder buttons // which look sort of like menus. @@ -60,20 +66,33 @@ // The scroll view that contains our main button view (below). IBOutlet NSScrollView* scrollView_; - // Are we scrollable? If no, the full contents of the folder are - // always visible. - BOOL scrollable_; - - BOOL scrollUpArrowShown_; - BOOL scrollDownArrowShown_; + // The view defining the visible area in which we draw our content. + IBOutlet BookmarkBarFolderWindowContentView* visibleView_; + + // The main view of this window (where the buttons go) within the scroller. + IBOutlet BookmarkBarFolderView* folderView_; + + // A window used to show the shadow behind the main window when it is + // scrollable. (A 'shadow' window is needed because the main window, when + // scrollable in either or both directions, will reach completely to the + // top and/or bottom edge of the screen in order to support mouse tracking + // during scrolling operations. In that case, though, the 'visible' + // window must be inset a bit from the edge of the screen for aesthetics; + // it will also be inset much more from the bottom of the screen when the + // Dock is showing. When scrollable, the main window would show a shadow + // incorrectly positioned, hence the 'shadow' window.) + IBOutlet BookmarkBarFolderWindow* shadowWindow_; + + // The up and down scroll arrow views. These arrows are hidden and shown + // as necessary (when scrolling is possible) and are contained in the nib + // as siblings to the scroll view. + IBOutlet NSView* scrollDownArrowView_; // Positioned at the top. + IBOutlet NSView* scrollUpArrowView_; // Positioned at the bottom. // YES if subfolders should grow to the right (the default). // Direction switches if we'd grow off the screen. BOOL subFolderGrowthToRight_; - // The main view of this window (where the buttons go). - IBOutlet BookmarkBarFolderView* mainView_; - // Weak; we keep track to work around a // setShowsBorderOnlyWhileMouseInside bug. BookmarkButton* buttonThatMouseIsIn_; @@ -105,6 +124,11 @@ // don't release it when done (we invalidate it). NSTimer* scrollTimer_; + // Precalculated sum of left and right edge padding of buttons in a + // folder menu window. This is calculated from the widths of the main + // folder menu window and the scroll view within. + CGFloat padding_; + // Amount to scroll by on each timer fire. Can be + or -. CGFloat verticalScrollDelta_; @@ -161,7 +185,6 @@ @end @interface BookmarkBarFolderController(TestingAPI) -- (NSView*)mainView; - (NSPoint)windowTopLeftForWidth:(int)windowWidth; - (NSArray*)buttons; - (BookmarkBarFolderController*)folderController; @@ -172,11 +195,15 @@ // Set to YES in order to prevent animations. - (void)setIgnoreAnimations:(BOOL)ignore; -// Return YES if we can scroll up or down. +// Return YES if the scroll-up or scroll-down arrows are showing. - (BOOL)canScrollUp; - (BOOL)canScrollDown; -// Return YES if the scrollable_ flag has been set. -- (BOOL)scrollable; +- (CGFloat)verticalScrollArrowHeight; +- (NSView*)visibleView; +- (NSView*)scrollView; +- (NSView*)folderView; - (BookmarkButton*)buttonForDroppingOnAtPoint:(NSPoint)point; @end + +#endif // CHROME_BROWSER_UI_COCOA_BOOKMARKS_BOOKMARK_BAR_FOLDER_CONTROLLER_H_ diff --git a/chrome/browser/ui/cocoa/bookmarks/bookmark_bar_folder_controller.mm b/chrome/browser/ui/cocoa/bookmarks/bookmark_bar_folder_controller.mm index 86420c2..adafe6e 100644 --- a/chrome/browser/ui/cocoa/bookmarks/bookmark_bar_folder_controller.mm +++ b/chrome/browser/ui/cocoa/bookmarks/bookmark_bar_folder_controller.mm @@ -4,7 +4,6 @@ #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_folder_controller.h" -#include "app/mac/nsimage_cache.h" #include "base/mac/mac_util.h" #include "base/sys_string_conversions.h" #include "chrome/browser/bookmarks/bookmark_model.h" @@ -15,10 +14,13 @@ #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_folder_button_cell.h" #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_folder_hover_state.h" #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_folder_view.h" +#import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_folder_window.h" #import "chrome/browser/ui/cocoa/bookmarks/bookmark_folder_target.h" #import "chrome/browser/ui/cocoa/browser_window_controller.h" #import "chrome/browser/ui/cocoa/event_utils.h" +using bookmarks::kBookmarkBarMenuCornerRadius; + namespace { // Frequency of the scrolling timer in seconds. @@ -29,16 +31,75 @@ const NSTimeInterval kBookmarkBarFolderScrollInterval = 0.1; const CGFloat kBookmarkBarFolderScrollAmount = 3 * bookmarks::kBookmarkButtonVerticalSpan; -// Amount to scroll for each scroll wheel delta. +// Amount to scroll for each scroll wheel roll. const CGFloat kBookmarkBarFolderScrollWheelAmount = 1 * bookmarks::kBookmarkButtonVerticalSpan; -// When constraining a scrolling bookmark bar folder window to the -// screen, shrink the "constrain" by this much vertically. Currently -// this is 0.0 to avoid a problem with tracking areas leaving the -// window, but should probably be 8.0 or something. -// TODO(jrg): http://crbug.com/36225 -const CGFloat kScrollWindowVerticalMargin = 0.0; +// Determining adjustments to the layout of the folder menu window in response +// to resizing and scrolling relies on many visual factors. The following +// struct is used to pass around these factors to the several support +// functions involved in the adjustment calculations and application. +struct LayoutMetrics { + // Metrics applied during the final layout adjustments to the window, + // the main visible content view, and the menu content view (i.e. the + // scroll view). + CGFloat windowLeft; + NSSize windowSize; + // The proposed and then final scrolling adjustment made to the scrollable + // area of the folder menu. This may be modified during the window layout + // primarily as a result of hiding or showing the scroll arrows. + CGFloat scrollDelta; + NSRect windowFrame; + NSRect visibleFrame; + NSRect scrollerFrame; + NSPoint scrollPoint; + // The difference between 'could' and 'can' in these next four data members + // is this: 'could' represents the previous condition for scrollability + // while 'can' represents what the new condition will be for scrollability. + BOOL couldScrollUp; + BOOL canScrollUp; + BOOL couldScrollDown; + BOOL canScrollDown; + // Determines the optimal time during folder menu layout when the contents + // of the button scroll area should be scrolled in order to prevent + // flickering. + BOOL preScroll; + + // Intermediate metrics used in determining window vertical layout changes. + CGFloat deltaWindowHeight; + CGFloat deltaWindowY; + CGFloat deltaVisibleHeight; + CGFloat deltaVisibleY; + CGFloat deltaScrollerHeight; + CGFloat deltaScrollerY; + + // Convenience metrics used in multiple functions (carried along here in + // order to eliminate the need to calculate in multiple places and + // reduce the possibility of bugs). + CGFloat minimumY; + CGFloat oldWindowY; + CGFloat folderY; + CGFloat folderTop; + + LayoutMetrics(CGFloat windowLeft, NSSize windowSize, CGFloat scrollDelta) : + windowLeft(windowLeft), + windowSize(windowSize), + scrollDelta(scrollDelta), + couldScrollUp(NO), + canScrollUp(NO), + couldScrollDown(NO), + canScrollDown(NO), + preScroll(NO), + deltaWindowHeight(0.0), + deltaWindowY(0.0), + deltaVisibleHeight(0.0), + deltaVisibleY(0.0), + deltaScrollerHeight(0.0), + deltaScrollerY(0.0), + oldWindowY(0.0), + folderY(0.0), + folderTop(0.0) {} +}; } // namespace @@ -49,22 +110,82 @@ const CGFloat kScrollWindowVerticalMargin = 0.0; - (void)endScroll; - (void)addScrollTimerWithDelta:(CGFloat)delta; +// Helper function to configureWindow which performs a basic layout of +// the window subviews, in particular the menu buttons and the window width. +- (void)layOutWindowWithHeight:(CGFloat)height; + // Determine the best button width (which will be the widest button or the // maximum allowable button width, whichever is less) and resize all buttons. -// Return the new width (so that the window can be adjusted, if necessary). +// Return the new width so that the window can be adjusted. - (CGFloat)adjustButtonWidths; // Returns the total menu height needed to display |buttonCount| buttons. // Does not do any fancy tricks like trimming the height to fit on the screen. -- (int)windowHeightForButtonCount:(int)buttonCount; - -// Adjust the height and horizontal position of the window such that the -// scroll arrows are shown as needed and the window appears completely -// on screen. -- (void)adjustWindowForHeight:(int)windowHeight; - -// Show or hide the scroll arrows at the top/bottom of the window. -- (void)showOrHideScrollArrows; +- (int)menuHeightForButtonCount:(int)buttonCount; + +// Adjust layout of the folder menu window components, showing/hiding the +// scroll up/down arrows, and resizing as necessary for a proper disaplay. +// In order to reduce window flicker, all layout changes are deferred until +// the final step of the adjustment. To accommodate this deferral, window +// height and width changes needed by callers to this function pass their +// desired window changes in |size|. When scrolling is to be performed +// any scrolling change is given by |scrollDelta|. The ultimate amount of +// scrolling may be different from |scrollDelta| in order to accommodate +// changes in the scroller view layout. These proposed window adjustments +// are passed to helper functions using a LayoutMetrics structure. +// +// This function should be called when: 1) initially setting up a folder menu +// window, 2) responding to scrolling of the contents (which may affect the +// height of the window), 3) addition or removal of bookmark items (such as +// during cut/paste/delete/drag/drop operations). +- (void)adjustWindowLeft:(CGFloat)windowLeft + size:(NSSize)windowSize + scrollingBy:(CGFloat)scrollDelta; + +// Support function for adjustWindowLeft:size:scrollingBy: which initializes +// the layout adjustments by gathering current folder menu window and subviews +// positions and sizes. This information is set in the |layoutMetrics| +// structure. +- (void)gatherMetrics:(LayoutMetrics*)layoutMetrics; + +// Support function for adjustWindowLeft:size:scrollingBy: which calculates +// the changes which must be applied to the folder menu window and subviews +// positions and sizes. |layoutMetrics| contains the proposed window size +// and scrolling along with the other current window and subview layout +// information. The values in |layoutMetrics| are then adjusted to +// accommodate scroll arrow presentation and window growth. +- (void)adjustMetrics:(LayoutMetrics*)layoutMetrics; + +// Support function for adjustMetrics: which calculates the layout changes +// required to accommodate changes in the position and scrollability +// of the top of the folder menu window. +- (void)adjustMetricsForMenuTopChanges:(LayoutMetrics*)layoutMetrics; + +// Support function for adjustMetrics: which calculates the layout changes +// required to accommodate changes in the position and scrollability +// of the bottom of the folder menu window. +- (void)adjustMetricsForMenuBottomChanges:(LayoutMetrics*)layoutMetrics; + +// Support function for adjustWindowLeft:size:scrollingBy: which applies +// the layout adjustments to the folder menu window and subviews. +- (void)applyMetrics:(LayoutMetrics*)layoutMetrics; + +// This function is called when buttons are added or removed from the folder +// menu, and which may require a change in the layout of the folder menu +// window. Such layout changes may include horizontal placement, width, +// height, and scroller visibility changes. (This function calls through +// to -[adjustWindowLeft:size:scrollingBy:].) +// |buttonCount| should contain the updated count of menu buttons. +- (void)adjustWindowForButtonCount:(NSUInteger)buttonCount; + +// A helper function which takes the desired amount to scroll, given by +// |scrollDelta|, and calculates the actual scrolling change to be applied +// taking into account the layout of the folder menu window and any +// changes in it's scrollability. (For example, when scrolling down and the +// top-most menu item is coming into view we will only scroll enough for +// that item to be completely presented, which may be less than the +// scroll amount requested.) +- (CGFloat)determineFinalScrollDelta:(CGFloat)scrollDelta; // |point| is in the base coordinate system of the destination window; // it comes from an id<NSDraggingInfo>. |copy| is YES if a copy is to be @@ -131,9 +252,6 @@ const CGFloat kScrollWindowVerticalMargin = 0.0; barController_ = barController; // WEAK buttons_.reset([[NSMutableArray alloc] init]); folderTarget_.reset([[BookmarkFolderTarget alloc] initWithController:self]); - NSImage* image = app::mac::GetCachedImageWithName(@"menu_overflow_up.pdf"); - DCHECK(image); - verticalScrollArrowHeight_ = [image size].height; [self configureWindow]; hoverState_.reset([[BookmarkBarFolderHoverState alloc] init]); } @@ -163,6 +281,13 @@ const CGFloat kScrollWindowVerticalMargin = 0.0; [super dealloc]; } +- (void)awakeFromNib { + NSRect windowFrame = [[self window] frame]; + NSRect scrollViewFrame = [scrollView_ frame]; + padding_ = NSWidth(windowFrame) - NSWidth(scrollViewFrame); + verticalScrollArrowHeight_ = NSHeight([scrollUpArrowView_ frame]); +} + // Overriden from NSWindowController to call childFolderWillShow: before showing // the window. - (void)showWindow:(id)sender { @@ -276,11 +401,6 @@ const CGFloat kScrollWindowVerticalMargin = 0.0; return button; } -// Exposed for testing. -- (NSView*)mainView { - return mainView_; -} - - (id)folderTarget { return folderTarget_.get(); } @@ -354,7 +474,8 @@ const CGFloat kScrollWindowVerticalMargin = 0.0; convertBaseToScreen:[[parentButton_ superview] convertPoint:NSZeroPoint toView:nil]]; newWindowTopLeft = NSMakePoint(buttonBottomLeftInScreen.x, - bookmarkBarBottomLeftInScreen.y); + bookmarkBarBottomLeftInScreen.y + + bookmarks::kBookmarkBarMenuOffset); // Make sure the window is on-screen; if not, push left. It is // intentional that top level folders "push left" slightly // different than subfolders. @@ -367,12 +488,12 @@ const CGFloat kScrollWindowVerticalMargin = 0.0; } else { // Parent is a folder; grow right/left. newWindowTopLeft.x = [self childFolderWindowLeftForWidth:windowWidth]; - NSPoint top = NSMakePoint(0, (NSMaxY([parentButton_ frame]) + - bookmarks::kBookmarkVerticalPadding)); - NSPoint topOfWindow = - [[parentButton_ window] - convertBaseToScreen:[[parentButton_ superview] - convertPoint:top toView:nil]]; + NSPoint topOfWindow = NSMakePoint(0, + (NSMaxY([parentButton_ frame]) + + bookmarks::kBookmarkVerticalPadding)); + topOfWindow = [[parentButton_ window] + convertBaseToScreen:[[parentButton_ superview] + convertPoint:topOfWindow toView:nil]]; newWindowTopLeft.y = topOfWindow.y; } return newWindowTopLeft; @@ -384,72 +505,212 @@ const CGFloat kScrollWindowVerticalMargin = 0.0; [[self window] setLevel:NSPopUpMenuWindowLevel]; } -- (int)windowHeightForButtonCount:(int)buttonCount { +- (int)menuHeightForButtonCount:(int)buttonCount { + // This does not take into account any padding which may be required at the + // top and/or bottom of the window. return (buttonCount * bookmarks::kBookmarkButtonVerticalSpan) + bookmarks::kBookmarkVerticalPadding; } -- (void)adjustWindowForHeight:(int)windowHeight { - // Adjust all button widths to be consistent, determine the best size for - // the window, and set the window frame. - CGFloat windowWidth = - [self adjustButtonWidths] + - (2 * bookmarks::kBookmarkSubMenuHorizontalPadding); - NSPoint newWindowTopLeft = [self windowTopLeftForWidth:windowWidth]; - NSSize windowSize = NSMakeSize(windowWidth, windowHeight); - windowSize = [scrollView_ convertSize:windowSize toView:nil]; +- (void)adjustWindowLeft:(CGFloat)windowLeft + size:(NSSize)windowSize + scrollingBy:(CGFloat)scrollDelta { + // Callers of this function should make adjustments to the vertical + // attributes of the folder view only (height, scroll position). + // This function will then make appropriate layout adjustments in order + // to accommodate screen/dock margins, scroll-up and scroll-down arrow + // presentation, etc. + // The 4 views whose vertical height and origins may be adjusted + // by this function are: + // 1) window, 2) visible content view, 3) scroller view, 4) folder view. + + LayoutMetrics layoutMetrics(windowLeft, windowSize, scrollDelta); + [self gatherMetrics:&layoutMetrics]; + [self adjustMetrics:&layoutMetrics]; + [self applyMetrics:&layoutMetrics]; +} + +- (void)gatherMetrics:(LayoutMetrics*)layoutMetrics { + LayoutMetrics& metrics(*layoutMetrics); NSWindow* window = [self window]; - // If the window is already visible then make sure its top remains stable. - BOOL windowAlreadyShowing = [window isVisible]; - CGFloat deltaY = windowHeight - NSHeight([mainView_ frame]); - if (windowAlreadyShowing) { - NSRect oldFrame = [window frame]; - newWindowTopLeft.y = oldFrame.origin.y + NSHeight(oldFrame); + metrics.windowFrame = [window frame]; + metrics.visibleFrame = [visibleView_ frame]; + metrics.scrollerFrame = [scrollView_ frame]; + metrics.scrollPoint = [scrollView_ documentVisibleRect].origin; + metrics.scrollPoint.y -= metrics.scrollDelta; + metrics.couldScrollUp = ![scrollUpArrowView_ isHidden]; + metrics.couldScrollDown = ![scrollDownArrowView_ isHidden]; + + metrics.deltaWindowHeight = 0.0; + metrics.deltaWindowY = 0.0; + metrics.deltaVisibleHeight = 0.0; + metrics.deltaVisibleY = 0.0; + metrics.deltaScrollerHeight = 0.0; + metrics.deltaScrollerY = 0.0; + + metrics.minimumY = NSMinY([[window screen] visibleFrame]) + + bookmarks::kScrollWindowVerticalMargin; + metrics.oldWindowY = NSMinY(metrics.windowFrame); + metrics.folderY = + metrics.scrollerFrame.origin.y + metrics.visibleFrame.origin.y + + metrics.oldWindowY - metrics.scrollPoint.y; + metrics.folderTop = metrics.folderY + NSHeight([folderView_ frame]); +} + +- (void)adjustMetrics:(LayoutMetrics*)layoutMetrics { + LayoutMetrics& metrics(*layoutMetrics); + NSScreen* screen = [[self window] screen]; + CGFloat effectiveFolderY = metrics.folderY; + if (!metrics.couldScrollUp && !metrics.couldScrollDown) + effectiveFolderY -= metrics.windowSize.height; + metrics.canScrollUp = effectiveFolderY < metrics.minimumY; + CGFloat maximumY = + NSMaxY([screen frame]) - bookmarks::kScrollWindowVerticalMargin; + metrics.canScrollDown = metrics.folderTop > maximumY; + + // Accommodate changes in the bottom of the menu. + [self adjustMetricsForMenuTopChanges:layoutMetrics]; + + // Accommodate changes in the top of the menu. + [self adjustMetricsForMenuBottomChanges:layoutMetrics]; + + metrics.scrollerFrame.origin.y += metrics.deltaScrollerY; + metrics.scrollerFrame.size.height += metrics.deltaScrollerHeight; + metrics.visibleFrame.origin.y += metrics.deltaVisibleY; + metrics.visibleFrame.size.height += metrics.deltaVisibleHeight; + metrics.preScroll = metrics.canScrollUp && !metrics.couldScrollUp && + metrics.scrollDelta == 0.0 && metrics.deltaWindowHeight >= 0.0; + metrics.windowFrame.origin.y += metrics.deltaWindowY; + metrics.windowFrame.origin.x = metrics.windowLeft; + metrics.windowFrame.size.height += metrics.deltaWindowHeight; + metrics.windowFrame.size.width = metrics.windowSize.width; +} + +- (void)adjustMetricsForMenuTopChanges:(LayoutMetrics*)layoutMetrics { + LayoutMetrics& metrics(*layoutMetrics); + if (metrics.canScrollUp) { + if (!metrics.couldScrollUp) { + // Couldn't -> Can + metrics.deltaWindowY = -metrics.oldWindowY; + metrics.deltaWindowHeight = -metrics.deltaWindowY; + metrics.deltaVisibleY = metrics.minimumY; + metrics.deltaVisibleHeight = -metrics.deltaVisibleY; + metrics.deltaScrollerY = verticalScrollArrowHeight_; + metrics.deltaScrollerHeight = -metrics.deltaScrollerY; + // Adjust the scroll delta if we've grown the window and it is + // now scroll-up-able, but don't adjust it factor if we've + // scrolled down and it wasn't scroll-up-able but now is. + if (metrics.canScrollDown == metrics.couldScrollDown) { + CGFloat deltaScroll = metrics.deltaWindowY + metrics.deltaScrollerY + + metrics.deltaVisibleY; + metrics.scrollPoint.y += deltaScroll + metrics.windowSize.height; + } + } else if (!metrics.canScrollDown && metrics.windowSize.height > 0.0) { + metrics.scrollPoint.y += metrics.windowSize.height; + } + } else { + if (metrics.couldScrollUp) { + // Could -> Can't + metrics.deltaWindowY = metrics.folderY - metrics.oldWindowY; + metrics.deltaWindowHeight = -metrics.deltaWindowY; + metrics.deltaVisibleY = -bookmarks::kScrollWindowVerticalMargin; + metrics.deltaVisibleHeight = -metrics.deltaVisibleY; + metrics.deltaScrollerY = -verticalScrollArrowHeight_; + metrics.deltaScrollerHeight = -metrics.deltaScrollerY; + // Adjust the scroll delta if we are no longer scroll-up-able + // and the scroll-down-able-ness hasn't changed. + if (metrics.canScrollDown == metrics.couldScrollDown) { + CGFloat deltaScroll = metrics.deltaWindowY + metrics.deltaScrollerY + + metrics.deltaVisibleY; + metrics.scrollPoint.y += deltaScroll; + } + } else { + // Couldn't -> Can't + // Check for menu height change by looking at the relative tops of the + // menu folder and the window folder, which previously would have been + // the same. + metrics.deltaWindowY = NSMaxY(metrics.windowFrame) - metrics.folderTop; + metrics.deltaWindowHeight = -metrics.deltaWindowY; + } } - NSRect windowFrame = NSMakeRect(newWindowTopLeft.x, - newWindowTopLeft.y - windowHeight, windowSize.width, windowHeight); - // Make the scrolled content be the right size (full size). - NSRect mainViewFrame = NSMakeRect(0, 0, NSWidth(windowFrame) - - bookmarks::kScrollViewContentWidthMargin, NSHeight(windowFrame)); - [mainView_ setFrame:mainViewFrame]; - // Make sure the window fits on the screen. If not, constrain. - // We'll scroll to allow the user to see all the content. - NSRect screenFrame = [[[self window] screen] frame]; - screenFrame = NSInsetRect(screenFrame, 0, kScrollWindowVerticalMargin); - BOOL wasScrollable = scrollable_; - if (!NSContainsRect(screenFrame, windowFrame)) { - scrollable_ = YES; - windowFrame = NSIntersectionRect(screenFrame, windowFrame); +} + +- (void)adjustMetricsForMenuBottomChanges:(LayoutMetrics*)layoutMetrics { + LayoutMetrics& metrics(*layoutMetrics); + if (metrics.canScrollDown == metrics.couldScrollDown) { + if (!metrics.canScrollDown) { + // Not scroll-down-able but the menu top has changed. + metrics.deltaWindowHeight += metrics.scrollDelta; + } } else { - scrollable_ = NO; + if (metrics.canScrollDown) { + // Couldn't -> Can + metrics.deltaWindowHeight += (NSMaxY([[[self window] screen] frame]) - + NSMaxY(metrics.windowFrame)); + metrics.deltaVisibleHeight -= bookmarks::kScrollWindowVerticalMargin; + metrics.deltaScrollerHeight -= verticalScrollArrowHeight_; + } else { + // Could -> Can't + metrics.deltaWindowHeight -= bookmarks::kScrollWindowVerticalMargin; + metrics.deltaVisibleHeight += bookmarks::kScrollWindowVerticalMargin; + metrics.deltaScrollerHeight += verticalScrollArrowHeight_; + } } - [window setFrame:windowFrame display:NO]; - if (wasScrollable != scrollable_) { - // If scrollability changed then rework visibility of the scroll arrows - // and the scroll offset of the menu view. - NSSize windowLocalSize = - [scrollView_ convertSize:windowFrame.size fromView:nil]; - CGFloat scrollPointY = NSHeight(mainViewFrame) - windowLocalSize.height + - bookmarks::kBookmarkVerticalPadding; - [mainView_ scrollPoint:NSMakePoint(0, scrollPointY)]; - [self showOrHideScrollArrows]; - [self addOrUpdateScrollTracking]; - } else if (scrollable_ && windowAlreadyShowing) { - // If the window was already showing and is still scrollable then make - // sure the main view moves upward, not downward so that the content - // at the bottom of the menu, not the top, appears to move. - // The edge case is when the menu is scrolled all the way to top (hence - // the test of scrollDownArrowShown_) - don't scroll then. - NSView* superView = [mainView_ superview]; - DCHECK([superView isKindOfClass:[NSClipView class]]); - NSClipView* clipView = static_cast<NSClipView*>(superView); - CGFloat scrollPointY = [clipView bounds].origin.y + - bookmarks::kBookmarkVerticalPadding; - if (scrollDownArrowShown_ || deltaY > 0.0) - scrollPointY += deltaY; - [mainView_ scrollPoint:NSMakePoint(0, scrollPointY)]; +} + +- (void)applyMetrics:(LayoutMetrics*)layoutMetrics { + LayoutMetrics& metrics(*layoutMetrics); + // Hide or show the scroll arrows. + if (metrics.canScrollUp != metrics.couldScrollUp) + [scrollUpArrowView_ setHidden:metrics.couldScrollUp]; + if (metrics.canScrollDown != metrics.couldScrollDown) + [scrollDownArrowView_ setHidden:metrics.couldScrollDown]; + + // Adjust the geometry. The order is important because of sizer dependencies. + [scrollView_ setFrame:metrics.scrollerFrame]; + [visibleView_ setFrame:metrics.visibleFrame]; + // This little bit of trickery handles the one special case where + // the window is now scroll-up-able _and_ going to be resized -- scroll + // first in order to prevent flashing. + if (metrics.preScroll) + [[scrollView_ documentView] scrollPoint:metrics.scrollPoint]; + + [[self window] setFrame:metrics.windowFrame display:YES]; + + // In all other cases we defer scrolling until the window has been resized + // in order to prevent flashing. + if (!metrics.preScroll) + [[scrollView_ documentView] scrollPoint:metrics.scrollPoint]; + + if (metrics.canScrollUp != metrics.couldScrollUp || + metrics.canScrollDown != metrics.couldScrollDown || + metrics.scrollDelta != 0.0) { + if (metrics.canScrollUp || metrics.canScrollDown) + [self addOrUpdateScrollTracking]; + else + [self removeScrollTracking]; } - [window display]; +} + +- (void)adjustWindowForButtonCount:(NSUInteger)buttonCount { + NSRect folderFrame = [folderView_ frame]; + CGFloat newMenuHeight = + (CGFloat)[self menuHeightForButtonCount:[buttons_ count]]; + CGFloat deltaMenuHeight = newMenuHeight - NSHeight(folderFrame); + // If the height has changed then also change the origin, and adjust the + // scroll (if scrolling). + if ([self canScrollUp]) { + NSPoint scrollPoint = [scrollView_ documentVisibleRect].origin; + scrollPoint.y += deltaMenuHeight; + [[scrollView_ documentView] scrollPoint:scrollPoint]; + } + folderFrame.size.height += deltaMenuHeight; + [folderView_ setFrameSize:folderFrame.size]; + CGFloat windowWidth = [self adjustButtonWidths] + padding_; + NSPoint newWindowTopLeft = [self windowTopLeftForWidth:windowWidth]; + CGFloat left = newWindowTopLeft.x; + NSSize newSize = NSMakeSize(windowWidth, deltaMenuHeight); + [self adjustWindowLeft:left size:newSize scrollingBy:0.0]; } // Determine window size and position. @@ -464,17 +725,17 @@ const CGFloat kScrollWindowVerticalMargin = 0.0; int buttons = std::max(node->GetChildCount() - startingIndex, 1); // Prelim height of the window. We'll trim later as needed. - int height = [self windowHeightForButtonCount:buttons]; + int height = [self menuHeightForButtonCount:buttons]; // We'll need this soon... [self window]; // TODO(jrg): combine with frame code in bookmark_bar_controller.mm // http://crbug.com/35966 NSRect buttonsOuterFrame = NSMakeRect( - bookmarks::kBookmarkSubMenuHorizontalPadding, - (height - bookmarks::kBookmarkButtonVerticalSpan), - bookmarks::kDefaultBookmarkWidth, - bookmarks::kBookmarkButtonHeight); + 0, + (height - bookmarks::kBookmarkButtonVerticalSpan), + bookmarks::kDefaultBookmarkWidth, + bookmarks::kBookmarkButtonHeight); // TODO(jrg): combine with addNodesToButtonList: code from // bookmark_bar_controller.mm (but use y offset) @@ -484,7 +745,7 @@ const CGFloat kScrollWindowVerticalMargin = 0.0; BookmarkButton* button = [self makeButtonForNode:nil frame:buttonsOuterFrame]; [buttons_ addObject:button]; - [mainView_ addSubview:button]; + [folderView_ addSubview:button]; } else { for (int i = startingIndex; i < node->GetChildCount(); @@ -493,13 +754,35 @@ const CGFloat kScrollWindowVerticalMargin = 0.0; BookmarkButton* button = [self makeButtonForNode:child frame:buttonsOuterFrame]; [buttons_ addObject:button]; - [mainView_ addSubview:button]; + [folderView_ addSubview:button]; buttonsOuterFrame.origin.y -= bookmarks::kBookmarkButtonVerticalSpan; } } + [self layOutWindowWithHeight:height]; +} - [self adjustWindowForHeight:height]; - // Finally pop me up. +- (void)layOutWindowWithHeight:(CGFloat)height { + // Lay out the window by adjusting all button widths to be consistent, then + // base the window width on this ideal button width. + CGFloat buttonWidth = [self adjustButtonWidths]; + CGFloat windowWidth = buttonWidth + padding_; + NSPoint newWindowTopLeft = [self windowTopLeftForWidth:windowWidth]; + // Make sure as much of a submenu is exposed (which otherwise would be a + // problem if the parent button is close to the bottom of the screen). + if ([parentController_ isKindOfClass:[self class]]) { + newWindowTopLeft.y = MAX(newWindowTopLeft.y, + height + bookmarks::kScrollWindowVerticalMargin); + } + NSWindow* window = [self window]; + NSRect windowFrame = NSMakeRect(newWindowTopLeft.x, + newWindowTopLeft.y - height, + windowWidth, height); + [window setFrame:windowFrame display:NO]; + NSRect folderFrame = NSMakeRect(0, 0, windowWidth, height); + [folderView_ setFrame:folderFrame]; + NSSize newSize = NSMakeSize(windowWidth, 0.0); + [self adjustWindowLeft:newWindowTopLeft.x size:newSize scrollingBy:0.0]; + [window display]; [self configureWindowLevel]; } @@ -525,85 +808,6 @@ const CGFloat kScrollWindowVerticalMargin = 0.0; return width; } -- (BOOL)canScrollUp { - // If removal of an arrow would make things "finished", state as - // such. - CGFloat scrollY = [scrollView_ documentVisibleRect].origin.y; - if (scrollUpArrowShown_) - scrollY -= verticalScrollArrowHeight_; - - if (scrollY <= 0) - return NO; - return YES; -} - -- (BOOL)canScrollDown { - CGFloat arrowAdjustment = 0.0; - - // We do NOT adjust based on the scrollDOWN arrow. This keeps - // things from "jumping"; if removal of the down arrow (at the top - // of the window) would cause a scroll to end, we'll end. - if (scrollUpArrowShown_) - arrowAdjustment += verticalScrollArrowHeight_; - - NSPoint scrollPosition = [scrollView_ documentVisibleRect].origin; - NSRect documentRect = [[scrollView_ documentView] frame]; - - // If we are exactly the right height, return no. We need this - // extra conditional in the case where we've just scrolled/grown - // into position. - if (NSHeight([[self window] frame]) == NSHeight(documentRect)) - return NO; - - if ((scrollPosition.y + NSHeight([[self window] frame])) >= - (NSHeight(documentRect) + arrowAdjustment)) { - return NO; - } - return YES; -} - -- (void)showOrHideScrollArrows { - NSRect frame = [scrollView_ frame]; - CGFloat scrollDelta = 0.0; - BOOL canScrollDown = [self canScrollDown]; - BOOL canScrollUp = [self canScrollUp]; - - if (canScrollUp != scrollUpArrowShown_) { - if (scrollUpArrowShown_) { - frame.origin.y -= verticalScrollArrowHeight_; - frame.size.height += verticalScrollArrowHeight_; - scrollDelta = verticalScrollArrowHeight_; - } else { - frame.origin.y += verticalScrollArrowHeight_; - frame.size.height -= verticalScrollArrowHeight_; - scrollDelta = -verticalScrollArrowHeight_; - } - } - if (canScrollDown != scrollDownArrowShown_) { - if (scrollDownArrowShown_) { - frame.size.height += verticalScrollArrowHeight_; - } else { - frame.size.height -= verticalScrollArrowHeight_; - } - } - scrollUpArrowShown_ = canScrollUp; - scrollDownArrowShown_ = canScrollDown; - [scrollView_ setFrame:frame]; - - // Adjust scroll based on new frame. For example, if we make room - // for an arrow at the bottom, adjust the scroll so the topmost item - // is still fully visible. - if (scrollDelta) { - NSPoint scrollPosition = [scrollView_ documentVisibleRect].origin; - scrollPosition.y -= scrollDelta; - [[scrollView_ documentView] scrollPoint:scrollPosition]; - } -} - -- (BOOL)scrollable { - return scrollable_; -} - // Start a "scroll up" timer. - (void)beginScrollWindowUp { [self addScrollTimerWithDelta:kBookmarkBarFolderScrollAmount]; @@ -624,67 +828,55 @@ const CGFloat kScrollWindowVerticalMargin = 0.0; } // Perform a single scroll of the specified amount. -// Scroll up: -// Scroll the documentView by the growth amount. -// If we cannot grow the window, simply scroll the documentView. -// If we can grow the window up without falling off the screen, do it. -// Scroll down: -// Never change the window size; only scroll the documentView. - (void)performOneScroll:(CGFloat)delta { - NSRect windowFrame = [[self window] frame]; - NSRect screenFrame = [[[self window] screen] frame]; - - // First scroll the "document" area. - NSPoint scrollPosition = [scrollView_ documentVisibleRect].origin; - scrollPosition.y -= delta; - [[scrollView_ documentView] scrollPoint:scrollPosition]; - - if (buttonThatMouseIsIn_) - [buttonThatMouseIsIn_ toggleButtonBorderingWhileMouseInside]; - - // We update the window size after shifting the scroll to avoid a race. - CGFloat screenHeightMinusMargin = (NSHeight(screenFrame) - - (2 * kScrollWindowVerticalMargin)); - if (delta) { - // If we can, grow the window (up). - if (NSHeight(windowFrame) < screenHeightMinusMargin) { - CGFloat growAmount = delta; - // Don't scroll more than enough to "finish". - if (scrollPosition.y < 0) - growAmount += scrollPosition.y; - windowFrame.size.height += growAmount; - windowFrame.size.height = std::min(NSHeight(windowFrame), - screenHeightMinusMargin); - // Watch out for a finish that isn't the full height of the screen. - // We get here if using the scroll wheel to scroll by small amounts. - windowFrame.size.height = std::min(NSHeight(windowFrame), - NSHeight([mainView_ frame])); - // Don't allow scrolling to make the window smaller, ever. This - // conditional is important when processing scrollWheel events. - if (windowFrame.size.height > [[self window] frame].size.height) { - [[self window] setFrame:windowFrame display:YES]; - [self addOrUpdateScrollTracking]; - } - } + CGFloat finalDelta = [self determineFinalScrollDelta:delta]; + if (finalDelta > 0.0 || finalDelta < 0.0) { + if (buttonThatMouseIsIn_) + [buttonThatMouseIsIn_ toggleButtonBorderingWhileMouseInside]; + NSRect windowFrame = [[self window] frame]; + NSSize newSize = NSMakeSize(NSWidth(windowFrame), 0.0); + [self adjustWindowLeft:windowFrame.origin.x + size:newSize + scrollingBy:finalDelta]; } +} - // If we're at either end, happiness. - if ((scrollPosition.y <= 0) || - ((scrollPosition.y + NSHeight(windowFrame) >= - NSHeight([mainView_ frame])) && - (windowFrame.size.height == screenHeightMinusMargin))) { - [self endScroll]; - - // If we can't scroll either up or down we are completely done. - // For example, perhaps we've scrolled a little and grown the - // window on-screen until there is now room for everything. - if (![self canScrollUp] && ![self canScrollDown]) { - scrollable_ = NO; - [self removeScrollTracking]; +- (CGFloat)determineFinalScrollDelta:(CGFloat)delta { + if (delta > 0.0 && ![scrollUpArrowView_ isHidden] || + delta < 0.0 && ![scrollDownArrowView_ isHidden]) { + NSWindow* window = [self window]; + NSRect windowFrame = [window frame]; + NSScreen* screen = [window screen]; + NSPoint scrollPosition = [scrollView_ documentVisibleRect].origin; + CGFloat scrollY = scrollPosition.y; + NSRect scrollerFrame = [scrollView_ frame]; + CGFloat scrollerY = NSMinY(scrollerFrame); + NSRect visibleFrame = [visibleView_ frame]; + CGFloat visibleY = NSMinY(visibleFrame); + CGFloat windowY = NSMinY(windowFrame); + CGFloat offset = scrollerY + visibleY + windowY; + + if (delta > 0.0) { + // Scrolling up. + CGFloat minimumY = NSMinY([screen visibleFrame]) + + bookmarks::kScrollWindowVerticalMargin; + CGFloat maxUpDelta = scrollY - offset + minimumY; + delta = MIN(delta, maxUpDelta); + } else { + // Scrolling down. + NSRect screenFrame = [screen frame]; + CGFloat topOfScreen = NSMaxY(screenFrame); + NSRect folderFrame = [folderView_ frame]; + CGFloat folderHeight = NSHeight(folderFrame); + CGFloat folderTop = folderHeight - scrollY + offset; + CGFloat maxDownDelta = + topOfScreen - folderTop - bookmarks::kScrollWindowVerticalMargin; + delta = MAX(delta, maxDownDelta); } + } else { + delta = 0.0; } - - [self showOrHideScrollArrows]; + return delta; } // Perform a scroll of the window on the screen. @@ -716,22 +908,28 @@ const CGFloat kScrollWindowVerticalMargin = 0.0; // menubar (to be fixed by setting the proper window level; see // initializer). - (void)mouseMoved:(NSEvent*)theEvent { - DCHECK([theEvent window] == [self window]); + NSWindow* window = [theEvent window]; + DCHECK(window == [self window]); NSPoint eventScreenLocation = - [[theEvent window] convertBaseToScreen:[theEvent locationInWindow]]; - - // We use frame (not visibleFrame) since our bookmark folder is on - // TOP of the menubar. - NSRect visibleRect = [[[self window] screen] frame]; - CGFloat closeToTopOfScreen = NSMaxY(visibleRect) - - verticalScrollArrowHeight_; - CGFloat closeToBottomOfScreen = NSMinY(visibleRect) + - verticalScrollArrowHeight_; - - if (eventScreenLocation.y <= closeToBottomOfScreen) { + [window convertBaseToScreen:[theEvent locationInWindow]]; + + // Base hot spot calculations on the positions of the scroll arrow views. + NSRect testRect = [scrollDownArrowView_ frame]; + NSPoint testPoint = [visibleView_ convertPoint:testRect.origin + toView:nil]; + testPoint = [window convertBaseToScreen:testPoint]; + CGFloat closeToTopOfScreen = testPoint.y; + + testRect = [scrollUpArrowView_ frame]; + testPoint = [visibleView_ convertPoint:testRect.origin toView:nil]; + testPoint = [window convertBaseToScreen:testPoint]; + CGFloat closeToBottomOfScreen = testPoint.y + testRect.size.height; + if (eventScreenLocation.y <= closeToBottomOfScreen && + ![scrollUpArrowView_ isHidden]) { [self beginScrollWindowUp]; - } else if (eventScreenLocation.y > closeToTopOfScreen) { + } else if (eventScreenLocation.y > closeToTopOfScreen && + ![scrollDownArrowView_ isHidden]) { [self beginScrollWindowDown]; } else { [self endScroll]; @@ -766,17 +964,6 @@ const CGFloat kScrollWindowVerticalMargin = 0.0; scrollTrackingArea_.reset(); } -// Delegate callback. -- (void)windowWillClose:(NSNotification*)notification { - // If a "hover open" is pending when the bookmark bar folder is - // closed, be sure it gets cancelled. - [NSObject cancelPreviousPerformRequestsWithTarget:self]; - - [barController_ childFolderWillClose:self]; - [self closeBookmarkFolder:self]; - [self autorelease]; -} - // Close the old hover-open bookmark folder, and open a new one. We // do both in one step to allow for a delay in closing the old one. // See comments above kDragHoverCloseDelay (bookmark_bar_controller.h) @@ -800,7 +987,7 @@ const CGFloat kScrollWindowVerticalMargin = 0.0; } - (void)scrollWheel:(NSEvent *)theEvent { - if (scrollable_) { + if (![scrollUpArrowView_ isHidden] || ![scrollDownArrowView_ isHidden]) { // We go negative since an NSScrollView has a flipped coordinate frame. CGFloat amt = kBookmarkBarFolderScrollWheelAmount * -[theEvent deltaY]; [self performOneScroll:amt]; @@ -926,7 +1113,7 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) { // dropLocation is in bar local coordinates. // http://crbug.com/36276 NSPoint dropLocation = - [mainView_ convertPoint:point + [folderView_ convertPoint:point fromView:[[self window] contentView]]; BookmarkButton* buttonToTheTopOfDraggedButton = nil; // Buttons are laid out in this array from top to bottom (screen @@ -1000,6 +1187,19 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) { return wasCopiedOrMoved; } +#pragma mark NSWindowDelegate Functions + +- (void)windowWillClose:(NSNotification*)notification { + // If a "hover open" is pending when the bookmark bar folder is + // closed, be sure it gets cancelled. + [NSObject cancelPreviousPerformRequestsWithTarget:self]; + + [self endScroll]; // Just in case we were scrolling. + [barController_ childFolderWillClose:self]; + [self closeBookmarkFolder:self]; + [self autorelease]; +} + #pragma mark BookmarkButtonDelegate Protocol - (void)fillPasteboard:(NSPasteboard*)pboard @@ -1280,14 +1480,12 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) { BookmarkButton* newButton = [self makeButtonForNode:node frame:newButtonFrame]; [buttons_ insertObject:newButton atIndex:buttonIndex]; - [mainView_ addSubview:newButton]; + [folderView_ addSubview:newButton]; // Close any child folder(s) which may still be open. [self closeBookmarkFolder:self]; - // Prelim height of the window. We'll trim later as needed. - int height = [self windowHeightForButtonCount:[buttons_ count]]; - [self adjustWindowForHeight:height]; + [self adjustWindowForButtonCount:[buttons_ count]]; } // More code which essentially duplicates that of BookmarkBarController. @@ -1394,9 +1592,6 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) { [oldButton setDelegate:nil]; [oldButton removeFromSuperview]; - if (animate && !ignoreAnimations_) - NSShowAnimationEffect(NSAnimationEffectDisappearingItemDefault, poofPoint, - NSZeroSize, nil, nil, nil); [buttons_ removeObjectAtIndex:buttonIndex]; for (NSInteger i = 0; i < buttonIndex; ++i) { BookmarkButton* button = [buttons_ objectAtIndex:i]; @@ -1419,22 +1614,25 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) { // If all nodes have been removed from this folder then add in the // 'empty' placeholder button. NSRect buttonFrame = - NSMakeRect(bookmarks::kBookmarkSubMenuHorizontalPadding, + NSMakeRect(0, bookmarks::kBookmarkButtonHeight - - (bookmarks::kBookmarkBarHeight - - bookmarks::kBookmarkVerticalPadding), + (bookmarks::kBookmarkBarHeight - + bookmarks::kBookmarkVerticalPadding), bookmarks::kDefaultBookmarkWidth, (bookmarks::kBookmarkBarHeight - - 2 * bookmarks::kBookmarkVerticalPadding)); + 2 * bookmarks::kBookmarkVerticalPadding)); BookmarkButton* button = [self makeButtonForNode:nil frame:buttonFrame]; [buttons_ addObject:button]; - [mainView_ addSubview:button]; + [folderView_ addSubview:button]; buttonCount = 1; } - // Propose a height for the window. We'll trim later as needed. - [self adjustWindowForHeight:[self windowHeightForButtonCount:buttonCount]]; + [self adjustWindowForButtonCount:buttonCount]; + + if (animate && !ignoreAnimations_) + NSShowAnimationEffect(NSAnimationEffectDisappearingItemDefault, poofPoint, + NSZeroSize, nil, nil, nil); } - (id<BookmarkButtonControllerProtocol>)controllerForNode: @@ -1448,6 +1646,30 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) { #pragma mark TestingAPI Only +- (BOOL)canScrollUp { + return ![scrollUpArrowView_ isHidden]; +} + +- (BOOL)canScrollDown { + return ![scrollDownArrowView_ isHidden]; +} + +- (CGFloat)verticalScrollArrowHeight { + return verticalScrollArrowHeight_; +} + +- (NSView*)visibleView { + return visibleView_; +} + +- (NSView*)scrollView { + return scrollView_; +} + +- (NSView*)folderView { + return folderView_; +} + - (void)setIgnoreAnimations:(BOOL)ignore { ignoreAnimations_ = ignore; } diff --git a/chrome/browser/ui/cocoa/bookmarks/bookmark_bar_folder_controller_unittest.mm b/chrome/browser/ui/cocoa/bookmarks/bookmark_bar_folder_controller_unittest.mm index cae8766..28f46de 100644 --- a/chrome/browser/ui/cocoa/bookmarks/bookmark_bar_folder_controller_unittest.mm +++ b/chrome/browser/ui/cocoa/bookmarks/bookmark_bar_folder_controller_unittest.mm @@ -64,28 +64,7 @@ } @end -// No window level and the ability to fake the "top left" point of the window. -// For testing. -@interface BookmarkBarFolderControllerLow : BookmarkBarFolderControllerNoLevel { - BOOL realTopLeft_; // Use the real windowTopLeft call? -} -@property (nonatomic) BOOL realTopLeft; -@end - - -@implementation BookmarkBarFolderControllerLow - -@synthesize realTopLeft = realTopLeft_; - -- (NSPoint)windowTopLeftForWidth:(int)width { - return realTopLeft_ ? [super windowTopLeftForWidth:width] : - NSMakePoint(200,200); -} - -@end - - -@interface BookmarkBarFolderControllerPong : BookmarkBarFolderControllerLow { +@interface BookmarkBarFolderControllerPong : BookmarkBarFolderController { BOOL childFolderWillShow_; BOOL childFolderWillClose_; } @@ -181,7 +160,9 @@ class BookmarkBarFolderControllerTest : public CocoaTest { [bar_ loaded:model]; // Make parent frame for bookmark bar then open it. NSRect frame = [[test_window() contentView] frame]; - frame = NSInsetRect(frame, 100, 200); + frame = NSMakeRect(frame.origin.x, + frame.size.height - bookmarks::kNTPBookmarkBarHeight, + frame.size.width, bookmarks::kNTPBookmarkBarHeight); NSView* fakeToolbarView = [[[NSView alloc] initWithFrame:frame] autorelease]; [[test_window() contentView] addSubview:fakeToolbarView]; @@ -235,9 +216,9 @@ TEST_F(BookmarkBarFolderControllerTest, InitCreateAndDelete) { } Class cellClass = [BookmarkBarFolderButtonCell class]; for (BookmarkButton* button in buttons) { - NSRect r = [[bbfc mainView] convertRect:[button frame] fromView:button]; + NSRect r = [[bbfc folderView] convertRect:[button frame] fromView:button]; // TODO(jrg): remove this adjustment. - NSRect bigger = NSInsetRect([[bbfc mainView] frame], -2, 0); + NSRect bigger = NSInsetRect([[bbfc folderView] frame], -2, 0); EXPECT_TRUE(NSContainsRect(bigger, r)); EXPECT_TRUE([[button cell] isKindOfClass:cellClass]); } @@ -269,13 +250,12 @@ TEST_F(BookmarkBarFolderControllerTest, BasicPosition) { EXPECT_TRUE(parentButton); // If parent is a BookmarkBarController, grow down. - scoped_nsobject<BookmarkBarFolderControllerLow> bbfc; - bbfc.reset([[BookmarkBarFolderControllerLow alloc] + scoped_nsobject<BookmarkBarFolderController> bbfc; + bbfc.reset([[BookmarkBarFolderController alloc] initWithParentButton:parentButton parentController:nil barController:bar_]); [bbfc window]; - [bbfc setRealTopLeft:YES]; NSPoint pt = [bbfc windowTopLeftForWidth:0]; // screen coords NSPoint buttonOriginInScreen = [[parentButton window] @@ -293,13 +273,12 @@ TEST_F(BookmarkBarFolderControllerTest, BasicPosition) { EXPECT_LT(shifted.x, pt.x); // If parent is a BookmarkBarFolderController, grow right. - scoped_nsobject<BookmarkBarFolderControllerLow> bbfc2; - bbfc2.reset([[BookmarkBarFolderControllerLow alloc] + scoped_nsobject<BookmarkBarFolderController> bbfc2; + bbfc2.reset([[BookmarkBarFolderController alloc] initWithParentButton:[[bbfc buttons] objectAtIndex:0] parentController:bbfc.get() barController:bar_]); [bbfc2 window]; - [bbfc2 setRealTopLeft:YES]; pt = [bbfc2 windowTopLeftForWidth:0]; // We're now overlapping the window a bit. EXPECT_EQ(pt.x, NSMaxX([[bbfc.get() window] frame]) - @@ -376,10 +355,10 @@ TEST_F(BookmarkBarFolderControllerTest, DropDestination) { EXPECT_TRUE(bbfc.get()); // Confirm "off the top" and "off the bottom" match no buttons. - NSPoint p = NSMakePoint(NSMidX([[bbfc mainView] frame]), 10000); + NSPoint p = NSMakePoint(NSMidX([[bbfc folderView] frame]), 10000); EXPECT_FALSE([bbfc buttonForDroppingOnAtPoint:p]); EXPECT_TRUE([bbfc shouldShowIndicatorShownForPoint:p]); - p = NSMakePoint(NSMidX([[bbfc mainView] frame]), -1); + p = NSMakePoint(NSMidX([[bbfc folderView] frame]), -1); EXPECT_FALSE([bbfc buttonForDroppingOnAtPoint:p]); EXPECT_TRUE([bbfc shouldShowIndicatorShownForPoint:p]); @@ -470,29 +449,51 @@ TEST_F(BookmarkBarFolderControllerTest, ChildFolderWidth) { // Simple scrolling tests. TEST_F(BookmarkBarFolderControllerTest, SimpleScroll) { scoped_nsobject<BookmarkBarFolderController> bbfc; - + NSRect screenFrame = [[NSScreen mainScreen] frame]; + CGFloat screenHeight = NSHeight(screenFrame); int nodecount = AddLotsOfNodes(); bbfc.reset(SimpleBookmarkBarFolderController()); EXPECT_TRUE(bbfc.get()); [bbfc showWindow:bbfc.get()]; + NSWindow* window = [bbfc window]; - // Make sure the window fits on the screen. - EXPECT_LT(NSHeight([[bbfc window] frame]), - NSHeight([[NSScreen mainScreen] frame])); + // The window should be shorter than the screen but reach exactly to the + // bottom of the screen since it's scrollable. + EXPECT_LT(NSHeight([window frame]), screenHeight); + EXPECT_CGFLOAT_EQ(0.0, [window frame].origin.y); - // Verify the logic used by the scroll arrow code. + // Initially, should show scroll-up but not scroll-down. EXPECT_TRUE([bbfc canScrollUp]); EXPECT_FALSE([bbfc canScrollDown]); - // Scroll it up. Make sure the window has gotten bigger each time. + // Scroll up a bit. Make sure the window has gotten bigger each time. // Also, for each scroll, make sure our hit test finds a new button // (to confirm the content area changed). NSView* savedHit = nil; - for (int i=0; i<3; i++) { - CGFloat height = NSHeight([[bbfc window] frame]); + NSView* scrollView = [bbfc scrollView]; + + // Find the next-to-last button showing at the bottom of the window and + // us its center for hit testing. + BookmarkButton* targetButton = nil; + NSPoint scrollPoint = [[bbfc scrollView] documentVisibleRect].origin; + for (BookmarkButton* button in [bbfc buttons]) { + NSRect buttonFrame = [button frame]; + buttonFrame.origin.y -= scrollPoint.y; + if (buttonFrame.origin.y < 0.0) + break; + targetButton = button; + } + NSPoint hitPoint = [targetButton frame].origin; + hitPoint.x += 50.0; + hitPoint.y += (bookmarks::kBookmarkButtonHeight / 2.0) - scrollPoint.y; + + for (int i = 0; i < 3; i++) { + CGFloat height = NSHeight([window frame]); [bbfc performOneScroll:60]; - EXPECT_GT(NSHeight([[bbfc window] frame]), height); - NSView* hit = [[[bbfc window] contentView] hitTest:NSMakePoint(22, 22)]; + EXPECT_GT(NSHeight([window frame]), height); + NSView* hit = [scrollView hitTest:hitPoint]; + // We should hit a bookmark button. + EXPECT_TRUE([[hit className] isEqualToString:@"BookmarkButton"]); EXPECT_NE(hit, savedHit); savedHit = hit; } @@ -500,11 +501,9 @@ TEST_F(BookmarkBarFolderControllerTest, SimpleScroll) { // Keep scrolling up; make sure we never get bigger than the screen. // Also confirm we never scroll the window off the screen. bool bothAtOnce = false; - NSRect screenFrame = [[NSScreen mainScreen] frame]; - for (int i = 0; i < nodecount; i++) { + while ([bbfc canScrollUp]) { [bbfc performOneScroll:60]; - EXPECT_TRUE(NSContainsRect(screenFrame, - [[bbfc window] frame])); + EXPECT_TRUE(NSContainsRect(screenFrame, [window frame])); // Make sure, sometime during our scroll, we have the ability to // scroll in either direction. if ([bbfc canScrollUp] && @@ -520,16 +519,23 @@ TEST_F(BookmarkBarFolderControllerTest, SimpleScroll) { // Now scroll down and make sure the window size does not change. // Also confirm we never scroll the window off the screen the other // way. - for (int i=0; i<nodecount+50; i++) { - CGFloat height = NSHeight([[bbfc window] frame]); + for (int i = 0; i < nodecount+50; ++i) { [bbfc performOneScroll:-60]; - EXPECT_EQ(height, NSHeight([[bbfc window] frame])); - EXPECT_TRUE(NSContainsRect(screenFrame, - [[bbfc window] frame])); + // Once we can no longer scroll down the window height changes. + if (![bbfc canScrollDown]) + break; + EXPECT_EQ(screenHeight, NSHeight([window frame])); + EXPECT_TRUE(NSContainsRect(screenFrame, [window frame])); } + // The final height should be offset from the top of the screen and still + // within the screen. + CGFloat height = screenHeight - bookmarks::kScrollWindowVerticalMargin; + EXPECT_CGFLOAT_EQ(height, NSHeight([window frame])); + EXPECT_TRUE(NSContainsRect(screenFrame, [window frame])); } -// Folder menu sizing and placementwhile deleting bookmarks and scrolling tests. +// Folder menu sizing and placement while deleting bookmarks +// and scrolling tests. TEST_F(BookmarkBarFolderControllerTest, MenuPlacementWhileScrollingDeleting) { scoped_nsobject<BookmarkBarFolderController> bbfc; AddLotsOfNodes(); @@ -579,7 +585,7 @@ TEST_F(BookmarkBarFolderControllerTest, MenuPlacementWhileScrollingDeleting) { button = [buttons objectAtIndex:buttonCounter + 3]; [folder deleteBookmark:button]; newTop = [menuWindow frame].origin.y + NSHeight([menuWindow frame]); - EXPECT_CGFLOAT_EQ(oldTop, newTop); + EXPECT_CGFLOAT_EQ(oldTop - bookmarks::kScrollWindowVerticalMargin, newTop); } @interface FakedDragInfo : NSObject { @@ -656,7 +662,7 @@ class BookmarkBarFolderControllerMenuTest : public CocoaTest { // Awkwardness to look like we've been installed. [parent_view_ addSubview:[bar view]]; NSRect frame = [[[bar view] superview] frame]; - frame.origin.y = 100; + frame.origin.y = 400; [[[bar view] superview] setFrame:frame]; // Make sure it's on in a window so viewDidMoveToWindow is called @@ -870,9 +876,9 @@ TEST_F(BookmarkBarFolderControllerMenuTest, DragMoveBarBookmarkToSubfolder) { NSRect newToSubwindowFrame = [toSubwindow frame]; NSRect expectedToSubwindowFrame = oldToSubwindowFrame; expectedToSubwindowFrame.origin.y -= - bookmarks::kBookmarkBarHeight + bookmarks::kVisualHeightOffset; + (bookmarks::kBookmarkButtonHeight + bookmarks::kVisualHeightOffset); expectedToSubwindowFrame.size.height += - bookmarks::kBookmarkBarHeight + bookmarks::kVisualHeightOffset; + (bookmarks::kBookmarkButtonHeight + bookmarks::kVisualHeightOffset); EXPECT_NSRECT_EQ(expectedToSubwindowFrame, newToSubwindowFrame); } @@ -1173,69 +1179,73 @@ TEST_F(BookmarkBarFolderControllerMenuTest, MenuSizingAndScrollArrows) { withObject:button]; BookmarkBarFolderController* folderController = [bar_ folderController]; EXPECT_TRUE(folderController); - NSWindow* folderMenu = [folderController window]; - EXPECT_TRUE(folderMenu); + NSWindow* folderWindow = [folderController window]; + EXPECT_TRUE(folderWindow); CGFloat expectedHeight = (CGFloat)bookmarks::kBookmarkButtonHeight + (2*bookmarks::kBookmarkVerticalPadding); - NSRect menuFrame = [folderMenu frame]; - CGFloat menuHeight = NSHeight(menuFrame); - EXPECT_CGFLOAT_EQ(expectedHeight, menuHeight); - EXPECT_FALSE([folderController scrollable]); + NSRect windowFrame = [folderWindow frame]; + CGFloat windowHeight = NSHeight(windowFrame); + EXPECT_CGFLOAT_EQ(expectedHeight, windowHeight); + EXPECT_FALSE([folderController canScrollUp]); + EXPECT_FALSE([folderController canScrollDown]); // Now add a real bookmark and reopen. model.AddURL(folder, folder->GetChildCount(), ASCIIToUTF16("a"), GURL("http://a.com/")); folderController = [bar_ folderController]; EXPECT_TRUE(folderController); - folderMenu = [folderController window]; - EXPECT_TRUE(folderMenu); - menuFrame = [folderMenu frame]; - menuHeight = NSHeight(menuFrame); + NSView* folderView = [folderController folderView]; + EXPECT_TRUE(folderView); + NSRect menuFrame = [folderView frame]; + NSView* visibleView = [folderController visibleView]; + NSRect visibleFrame = [visibleView frame]; + NSView* scrollView = [folderController scrollView]; + NSRect scrollFrame = [scrollView frame]; + + // Determine the margins between the scroll frame and the visible frame. + CGFloat widthDelta = NSWidth(visibleFrame) - NSWidth(scrollFrame); + + CGFloat menuHeight = NSHeight(menuFrame); EXPECT_CGFLOAT_EQ(expectedHeight, menuHeight); - CGFloat menuWidth = NSWidth(menuFrame); + CGFloat scrollerWidth = NSWidth(scrollFrame); button = [folderController buttonWithTitleEqualTo:@"a"]; CGFloat buttonWidth = NSWidth([button frame]); - CGFloat expectedWidth = - buttonWidth + (2 * bookmarks::kBookmarkSubMenuHorizontalPadding); - EXPECT_CGFLOAT_EQ(expectedWidth, menuWidth); + EXPECT_CGFLOAT_EQ(scrollerWidth, buttonWidth); + CGFloat visibleWidth = NSWidth(visibleFrame); + EXPECT_CGFLOAT_EQ(visibleWidth - widthDelta, buttonWidth); + EXPECT_LT(scrollerWidth, NSWidth([folderView frame])); // Add a wider bookmark and make sure the button widths match. - model.AddURL(folder, folder->GetChildCount(), - ASCIIToUTF16("A really, really long name"), + int reallyWideButtonNumber = folder->GetChildCount(); + model.AddURL(folder, reallyWideButtonNumber, + ASCIIToUTF16("A really, really, really, really, really, " + "really long name"), GURL("http://www.google.com/a")); - EXPECT_LT(menuWidth, NSWidth([folderMenu frame])); - EXPECT_LT(buttonWidth, NSWidth([button frame])); - buttonWidth = NSWidth([button frame]); - BookmarkButton* buttonB = - [folderController buttonWithTitleEqualTo:@"A really, really long name"]; - EXPECT_TRUE(buttonB); - CGFloat buttonWidthB = NSWidth([buttonB frame]); - EXPECT_CGFLOAT_EQ(buttonWidth, buttonWidthB); - // Add a bunch of bookmarks until the window grows no more, then check for - // a scroll down arrow. - CGFloat oldMenuHeight = 0.0; // It just has to be different for first run. - menuHeight = NSHeight([folderMenu frame]); + BookmarkButton* bigButton = + [folderController buttonWithTitleEqualTo: + @"A really, really, really, really, really, really long name"]; + EXPECT_TRUE(bigButton); + CGFloat buttonWidthB = NSWidth([bigButton frame]); + EXPECT_LT(buttonWidth, buttonWidthB); + // Add a bunch of bookmarks until the window becomes scrollable, then check + // for a scroll up arrow. NSUInteger tripWire = 0; // Prevent a runaway. - while (![folderController scrollable] && ++tripWire < 100) { + while (![folderController canScrollUp] && ++tripWire < 1000) { model.AddURL(folder, folder->GetChildCount(), ASCIIToUTF16("B"), GURL("http://b.com/")); - oldMenuHeight = menuHeight; - menuHeight = NSHeight([folderMenu frame]); } - EXPECT_TRUE([folderController scrollable]); EXPECT_TRUE([folderController canScrollUp]); // Remove one bookmark and make sure the scroll down arrow has been removed. // We'll remove the really long node so we can see if the buttons get resized. - menuWidth = NSWidth([folderMenu frame]); + scrollerWidth = NSWidth([folderView frame]); buttonWidth = NSWidth([button frame]); - model.Remove(folder, 1); - EXPECT_FALSE([folderController scrollable]); + model.Remove(folder, reallyWideButtonNumber); EXPECT_FALSE([folderController canScrollUp]); EXPECT_FALSE([folderController canScrollDown]); // Check the size. It should have reduced. - EXPECT_GT(menuWidth, NSWidth([folderMenu frame])); + EXPECT_GT(scrollerWidth, NSWidth([folderView frame])); EXPECT_GT(buttonWidth, NSWidth([button frame])); // Check button spacing. @@ -1362,7 +1372,7 @@ TEST_F(BookmarkBarFolderControllerMenuTest, DragBookmarkData) { // Verify the model. const std::string expectedA("1b 2f:[ O3f:[ O3f1b O3f2f ] 2f1b 2f2f:[ " "2f2f1b 2f2f2b 2f2f3b O4f:[ O4f1b O4f2f ] ] " - "2f3b ] 3b 4b "); + "2f3b ] 3b 4b "); actual = model_test_utils::ModelStringFromNode(root); EXPECT_EQ(expectedA, actual); diff --git a/chrome/browser/ui/cocoa/bookmarks/bookmark_bar_folder_window.h b/chrome/browser/ui/cocoa/bookmarks/bookmark_bar_folder_window.h index cd97d9e..7fe6608 100644 --- a/chrome/browser/ui/cocoa/bookmarks/bookmark_bar_folder_window.h +++ b/chrome/browser/ui/cocoa/bookmarks/bookmark_bar_folder_window.h @@ -13,17 +13,16 @@ // Window for a bookmark folder "menu". This menu pops up when you // click on a bookmark button that represents a folder of bookmarks. -// This window is borderless. +// This window is borderless but has a shadow, at least initially. +// Once the down scroll arrow is shown, the shadow is turned off and +// a secondary window is added which is sized to match the visible +// area of the menu and which provides the shadow. @interface BookmarkBarFolderWindow : NSWindow @end // Content view for the above window. "Stock" other than the drawing // of rounded corners. Only used in the nib. -@interface BookmarkBarFolderWindowContentView : NSView { - // Arrows to show ability to scroll up and down as needed. - scoped_nsobject<NSImage> arrowUpImage_; - scoped_nsobject<NSImage> arrowDownImage_; -} +@interface BookmarkBarFolderWindowContentView : NSView @end // Scroll view that contains the main view (where the buttons go). diff --git a/chrome/browser/ui/cocoa/bookmarks/bookmark_bar_folder_window.mm b/chrome/browser/ui/cocoa/bookmarks/bookmark_bar_folder_window.mm index 65cb664..c64de1e 100644 --- a/chrome/browser/ui/cocoa/bookmarks/bookmark_bar_folder_window.mm +++ b/chrome/browser/ui/cocoa/bookmarks/bookmark_bar_folder_window.mm @@ -5,13 +5,14 @@ #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_folder_window.h" #import "base/logging.h" -#include "app/mac/nsimage_cache.h" #import "base/scoped_nsobject.h" +#import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_constants.h" #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_folder_controller.h" #import "chrome/browser/ui/cocoa/image_utils.h" #import "third_party/GTM/AppKit/GTMNSColor+Luminance.h" #import "third_party/GTM/AppKit/GTMNSBezierPath+RoundRect.h" +using bookmarks::kBookmarkBarMenuCornerRadius; @implementation BookmarkBarFolderWindow @@ -32,89 +33,30 @@ @end -namespace { -// Corner radius for our bookmark bar folder window. -// Copied from bubble_view.mm. -const CGFloat kViewCornerRadius = 4.0; -} - @implementation BookmarkBarFolderWindowContentView -- (void)awakeFromNib { - arrowUpImage_.reset( - [app::mac::GetCachedImageWithName(@"menu_overflow_up.pdf") retain]); - arrowDownImage_.reset( - [app::mac::GetCachedImageWithName(@"menu_overflow_down.pdf") retain]); -} - -// Draw the arrows at the top and bottom of the folder window as a -// visual indication that scrolling is possible. We always draw the -// scrolling arrows; when not relevant (e.g. when not scrollable), the -// scroll view overlaps the window and the arrows aren't visible. -- (void)drawScrollArrows:(NSRect)rect { - NSRect visibleRect = [self bounds]; - - // On top - NSRect imageRect = NSZeroRect; - imageRect.size = [arrowUpImage_ size]; - NSRect drawRect = NSOffsetRect( - imageRect, - (NSWidth(visibleRect) - NSWidth(imageRect)) / 2, - NSHeight(visibleRect) - NSHeight(imageRect)); - [arrowUpImage_ drawInRect:drawRect - fromRect:imageRect - operation:NSCompositeSourceOver - fraction:1.0 - neverFlipped:YES]; - - // On bottom - imageRect = NSZeroRect; - imageRect.size = [arrowDownImage_ size]; - drawRect = NSOffsetRect(imageRect, - (NSWidth(visibleRect) - NSWidth(imageRect)) / 2, - 0); - [arrowDownImage_ drawInRect:drawRect - fromRect:imageRect - operation:NSCompositeSourceOver - fraction:1.0 - neverFlipped:YES]; -} - - (void)drawRect:(NSRect)rect { - NSRect bounds = [self bounds]; // Like NSMenus, only the bottom corners are rounded. NSBezierPath* bezier = - [NSBezierPath gtm_bezierPathWithRoundRect:bounds - topLeftCornerRadius:0 - topRightCornerRadius:0 - bottomLeftCornerRadius:kViewCornerRadius - bottomRightCornerRadius:kViewCornerRadius]; - [bezier closePath]; - - // TODO(jrg): share code with info_bubble_view.mm? Or bubble_view.mm? - NSColor* base_color = [NSColor colorWithCalibratedWhite:0.5 alpha:1.0]; - NSColor* startColor = - [base_color gtm_colorAdjustedFor:GTMColorationLightHighlight - faded:YES]; + [NSBezierPath gtm_bezierPathWithRoundRect:[self bounds] + topLeftCornerRadius:kBookmarkBarMenuCornerRadius + topRightCornerRadius:kBookmarkBarMenuCornerRadius + bottomLeftCornerRadius:kBookmarkBarMenuCornerRadius + bottomRightCornerRadius:kBookmarkBarMenuCornerRadius]; + NSColor* startColor = [NSColor colorWithCalibratedWhite:0.91 alpha:1.0]; NSColor* midColor = - [base_color gtm_colorAdjustedFor:GTMColorationLightMidtone - faded:YES]; + [startColor gtm_colorAdjustedFor:GTMColorationLightMidtone faded:YES]; NSColor* endColor = - [base_color gtm_colorAdjustedFor:GTMColorationLightShadow - faded:YES]; - NSColor* glowColor = - [base_color gtm_colorAdjustedFor:GTMColorationLightPenumbra - faded:YES]; + [startColor gtm_colorAdjustedFor:GTMColorationLightPenumbra faded:YES]; scoped_nsobject<NSGradient> gradient( [[NSGradient alloc] initWithColorsAndLocations:startColor, 0.0, midColor, 0.25, endColor, 0.5, - glowColor, 0.75, + midColor, 0.75, + startColor, 1.0, nil]); [gradient drawInBezierPath:bezier angle:0.0]; - - [self drawScrollArrows:rect]; } @end |