summaryrefslogtreecommitdiffstats
path: root/chrome/browser/ui/cocoa/bookmarks
diff options
context:
space:
mode:
authormrossetti@chromium.org <mrossetti@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-01-11 18:58:30 +0000
committermrossetti@chromium.org <mrossetti@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-01-11 18:58:30 +0000
commitbe0aa82ab771df70f1548dd712701a04b9e23c18 (patch)
treecb5d3c9847f931f98d9ea35c642a2c5ddf68c858 /chrome/browser/ui/cocoa/bookmarks
parent539a1ac415e94892d0bc4a41205afeeb878c8f72 (diff)
downloadchromium_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')
-rw-r--r--chrome/browser/ui/cocoa/bookmarks/bookmark_bar_constants.h4
-rw-r--r--chrome/browser/ui/cocoa/bookmarks/bookmark_bar_controller.h15
-rw-r--r--chrome/browser/ui/cocoa/bookmarks/bookmark_bar_folder_controller.h53
-rw-r--r--chrome/browser/ui/cocoa/bookmarks/bookmark_bar_folder_controller.mm770
-rw-r--r--chrome/browser/ui/cocoa/bookmarks/bookmark_bar_folder_controller_unittest.mm198
-rw-r--r--chrome/browser/ui/cocoa/bookmarks/bookmark_bar_folder_window.h11
-rw-r--r--chrome/browser/ui/cocoa/bookmarks/bookmark_bar_folder_window.mm82
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