Index: chrome/browser/ui/cocoa/bookmarks/bookmark_bar_controller.mm |
diff --git a/chrome/browser/ui/cocoa/bookmarks/bookmark_bar_controller.mm b/chrome/browser/ui/cocoa/bookmarks/bookmark_bar_controller.mm |
index f0fb4ca20654aeb48501eaf2461812b3fb69804f..2e375d052275f7d5e4c9b885a6daf1238300410a 100644 |
--- a/chrome/browser/ui/cocoa/bookmarks/bookmark_bar_controller.mm |
+++ b/chrome/browser/ui/cocoa/bookmarks/bookmark_bar_controller.mm |
@@ -43,6 +43,7 @@ |
#import "chrome/browser/ui/cocoa/bookmarks/bookmark_model_observer_for_cocoa.h" |
#import "chrome/browser/ui/cocoa/bookmarks/bookmark_name_folder_controller.h" |
#import "chrome/browser/ui/cocoa/browser_window_controller.h" |
+#import "chrome/browser/ui/cocoa/l10n_util.h" |
#import "chrome/browser/ui/cocoa/menu_button.h" |
#import "chrome/browser/ui/cocoa/themed_window.h" |
#import "chrome/browser/ui/cocoa/toolbar/toolbar_controller.h" |
@@ -78,6 +79,7 @@ |
#include "ui/resources/grit/ui_resources.h" |
using base::UserMetricsAction; |
+using bookmarks::BookmarkBarLayout; |
using bookmarks::BookmarkModel; |
using bookmarks::BookmarkNode; |
using bookmarks::BookmarkNodeData; |
@@ -144,6 +146,25 @@ using content::WebContents; |
// - The BWC should implement |-bookmarkBar:didChangeFromState:toState:| and |
// |-bookmarkBar:willAnimateFromState:toState:| in order to inform the |
// toolbar of required changes. |
+// |
+// Layout: |
+// |
+// Several events (initial load, changes to the bookmark model etc.) can |
+// require the bar layout to change. In most cases, this is accomplished |
+// by building a BookmarkBarLayout from the current state of the view, |
+// the bookmark model, and the managed bookmark service. If the calculated |
+// layout differs from the previous one, it's applied to the view |
+// via |applyLayout:animated:|. This is a cheap way to "coalesce" multiple |
+// potentially layout-changing events, since in practice, these events come |
+// in bursts and don't require a change. |
+// |
+// Temporary changes in layout during dragging are an exception to this, |
+// since the layout temporarily adjusts to the drag (for example, adding |
+// a placeholder space for a mid-drag button or removing the space previously |
+// taken up by a button which is being dragged off the bar). In this case, |
+// the original stored layout is maintained as the source of truth, and |
+// elements are laid out from a combination of the stored layout and the |
+// drag state. See |setDropInsertionPos:| for details. |
namespace { |
@@ -151,6 +172,11 @@ namespace { |
const NSTimeInterval kBookmarkBarAnimationDuration = 0.12; |
const NSTimeInterval kDragAndDropAnimationDuration = 0.25; |
+const int kMaxReusePoolSize = 10; |
+ |
+// Min width for no item text field and import bookmarks button. |
+const CGFloat kNoItemElementMinWidth = 30; |
+ |
void RecordAppLaunch(Profile* profile, GURL url) { |
const extensions::Extension* extension = |
extensions::ExtensionRegistry::Get(profile)-> |
@@ -162,94 +188,71 @@ void RecordAppLaunch(Profile* profile, GURL url) { |
extension->GetType()); |
} |
-} // namespace |
- |
-@interface BookmarkBarController () |
- |
-// Updates the sizes and positions of the subviews. |
-- (void)layoutSubviews; |
- |
-// Moves to the given next state (from the current state), possibly animating. |
-// If |animate| is NO, it will stop any running animation and jump to the given |
-// state. If YES, it may either (depending on implementation) jump to the end of |
-// the current animation and begin the next one, or stop the current animation |
-// mid-flight and animate to the next state. |
-- (void)moveToState:(BookmarkBar::State)nextState |
- withAnimation:(BOOL)animate; |
- |
-// Create buttons for all items in the given bookmark node tree. |
-// Modifies self->buttons_. Do not add more buttons than will fit on the view. |
-- (void)addNodesToButtonList:(const BookmarkNode*)node; |
- |
-// Create an autoreleased button appropriate for insertion into the bookmark |
-// bar. Update |xOffset| with the offset appropriate for the subsequent button. |
-- (BookmarkButton*)buttonForNode:(const BookmarkNode*)node |
- xOffset:(int*)xOffset; |
- |
-// Find a parent whose button is visible on the bookmark bar. |
-- (BookmarkButton*)bookmarkButtonToPulseForNode:(const BookmarkNode*)node; |
- |
-// Puts stuff into the final state without animating, stopping a running |
-// animation if necessary. |
-- (void)finalizeState; |
- |
-// Stops any current animation in its tracks (midway). |
-- (void)stopCurrentAnimation; |
- |
-// Show/hide the bookmark bar. |
-// if |animate| is YES, the changes are made using the animator; otherwise they |
-// are made immediately. |
-- (void)showBookmarkBarWithAnimation:(BOOL)animate; |
- |
-// Handles animating the resize of the content view. Returns YES if it handled |
-// the animation, NO if not (and hence it should be done instantly). |
-- (BOOL)doBookmarkBarAnimation; |
- |
-// |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 |
-// made and inserted into the new location while leaving the bookmark in |
-// the old location, otherwise move the bookmark by removing from its old |
-// location and inserting into the new location. |
-- (BOOL)dragBookmark:(const BookmarkNode*)sourceNode |
- to:(NSPoint)point |
- copy:(BOOL)copy; |
+const CGFloat kBookmarkButtonHeightMinusPadding = |
+ bookmarks::kBookmarkButtonHeight - bookmarks::kBookmarkVerticalPadding * 2; |
-// Returns the index in the model for a drag to the location given by |
-// |point|. This is determined by finding the first button before the center |
-// of which |point| falls, scanning left to right. Note that, currently, only |
-// the x-coordinate of |point| is considered. Though not currently implemented, |
-// we may check for errors, in which case this would return negative value; |
-// callers should check for this. |
-- (int)indexForDragToPoint:(NSPoint)point; |
- |
-// Add or remove buttons to/from the bar until it is filled but not overflowed. |
-- (void)redistributeButtonsOnBarAsNeeded; |
- |
-// Determine the nature of the bookmark bar contents based on the number of |
-// buttons showing. If too many then show the off-the-side list, if none |
-// then show the no items label. |
-- (void)reconfigureBookmarkBar; |
- |
-- (int)preferredHeight; |
-- (void)addButtonsToView; |
-- (BOOL)setManagedBookmarksButtonVisibility; |
-- (BOOL)setSupervisedBookmarksButtonVisibility; |
-- (BOOL)setOtherBookmarksButtonVisibility; |
-- (BOOL)setAppsPageShortcutButtonVisibility; |
-- (BookmarkButton*)createCustomBookmarkButtonForCell:(NSCell*)cell; |
-- (void)createManagedBookmarksButton; |
-- (void)createSupervisedBookmarksButton; |
-- (void)createOtherBookmarksButton; |
-- (void)createAppsPageShortcutButton; |
-- (void)openAppsPage:(id)sender; |
-- (void)centerNoItemsLabel; |
-- (void)positionRightSideButtons; |
-- (void)watchForExitEvent:(BOOL)watch; |
-- (void)resetAllButtonPositionsWithAnimation:(BOOL)animate; |
- |
-@end |
+} // namespace |
-@implementation BookmarkBarController |
+namespace bookmarks { |
+ |
+BookmarkBarLayout::BookmarkBarLayout() |
+ : visible_elements(0), |
+ apps_button_offset(0), |
+ managed_bookmarks_button_offset(0), |
+ supervised_bookmarks_button_offset(0), |
+ off_the_side_button_offset(0), |
+ other_bookmarks_button_offset(0), |
+ no_item_textfield_offset(0), |
+ no_item_textfield_width(0), |
+ import_bookmarks_button_offset(0), |
+ import_bookmarks_button_width(0), |
+ max_x(0){}; |
+BookmarkBarLayout::~BookmarkBarLayout(){}; |
+BookmarkBarLayout::BookmarkBarLayout(BookmarkBarLayout&& other) = default; |
+BookmarkBarLayout& BookmarkBarLayout::operator=(BookmarkBarLayout&& other) = |
+ default; |
+ |
+bool operator==(const BookmarkBarLayout& lhs, const BookmarkBarLayout& rhs) { |
+ return std::tie(lhs.visible_elements, lhs.apps_button_offset, |
+ lhs.managed_bookmarks_button_offset, |
+ lhs.supervised_bookmarks_button_offset, |
+ lhs.off_the_side_button_offset, |
+ lhs.other_bookmarks_button_offset, |
+ lhs.no_item_textfield_offset, lhs.no_item_textfield_width, |
+ lhs.import_bookmarks_button_offset, |
+ lhs.import_bookmarks_button_width, lhs.button_offsets, |
+ lhs.max_x) == |
+ std::tie( |
+ rhs.visible_elements, rhs.apps_button_offset, |
+ rhs.managed_bookmarks_button_offset, |
+ rhs.supervised_bookmarks_button_offset, |
+ rhs.off_the_side_button_offset, rhs.other_bookmarks_button_offset, |
+ rhs.no_item_textfield_offset, rhs.no_item_textfield_width, |
+ rhs.import_bookmarks_button_offset, |
+ rhs.import_bookmarks_button_width, rhs.button_offsets, rhs.max_x); |
+} |
+ |
+bool operator!=(const BookmarkBarLayout& lhs, const BookmarkBarLayout& rhs) { |
+ return !(lhs == rhs); |
+} |
+ |
+} // namespace bookmarks |
+ |
+@implementation BookmarkBarController { |
+ BookmarkBarLayout layout_; |
+ CGFloat originalNoItemTextFieldWidth_; |
+ CGFloat originalImportBookmarksButtonWidth_; |
+ CGFloat originalNoItemInterelementPadding_; |
+ BOOL didCreateExtraButtons_; |
+ |
+ // Maps bookmark node IDs to instantiated buttons for ease of lookup. |
+ std::unordered_map<int64_t, base::scoped_nsobject<BookmarkButton>> |
+ nodeIdToButtonMap_; |
+ |
+ // A place to stash bookmark buttons that have been removed from the bar |
+ // so that they can be reused instead of creating new ones. |
+ base::scoped_nsobject<NSMutableArray> unusedButtonPool_; |
+} |
@synthesize currentState = currentState_; |
@synthesize lastState = lastState_; |
@@ -261,8 +264,7 @@ void RecordAppLaunch(Profile* profile, GURL url) { |
- (id)initWithBrowser:(Browser*)browser |
initialWidth:(CGFloat)initialWidth |
delegate:(id<BookmarkBarControllerDelegate>)delegate { |
- if ((self = [super initWithNibName:@"BookmarkBar" |
- bundle:base::mac::FrameworkBundle()])) { |
+ if ((self = [super initWithNibName:nil bundle:nil])) { |
currentState_ = BookmarkBar::HIDDEN; |
lastState_ = BookmarkBar::HIDDEN; |
@@ -273,10 +275,12 @@ void RecordAppLaunch(Profile* profile, GURL url) { |
managedBookmarkService_ = |
ManagedBookmarkServiceFactory::GetForProfile(browser_->profile()); |
buttons_.reset([[NSMutableArray alloc] init]); |
+ unusedButtonPool_.reset([[NSMutableArray alloc] init]); |
delegate_ = delegate; |
folderTarget_.reset( |
[[BookmarkFolderTarget alloc] initWithController:self |
profile:browser_->profile()]); |
+ didCreateExtraButtons_ = NO; |
ResourceBundle& rb = ResourceBundle::GetSharedInstance(); |
folderImage_.reset( |
@@ -333,21 +337,18 @@ void RecordAppLaunch(Profile* profile, GURL url) { |
buttonView_.reset([[BookmarkBarView alloc] |
initWithController:self |
frame:NSMakeRect(0, -2, 584, 144)]); |
- [buttonView_ setAutoresizingMask:NSViewWidthSizable | NSViewMinYMargin | |
- NSViewMaxXMargin]; |
+ [buttonView_ setAutoresizingMask:NSViewWidthSizable | NSViewMaxXMargin]; |
[[buttonView_ importBookmarksButton] setTarget:self]; |
[[buttonView_ importBookmarksButton] setAction:@selector(importBookmarks:)]; |
- [self createOffTheSideButton]; |
- [buttonView_ addSubview:offTheSideButton_]; |
- |
[self.view addSubview:buttonView_]; |
- // viewDidLoad became part of the API in 10.10 |
+ |
+ // viewDidLoad became part of the API in 10.10. |
if (!base::mac::IsAtLeastOS10_10()) |
[self viewDidLoad]; |
} |
-- (BookmarkButton*)bookmarkButtonToPulseForNode:(const BookmarkNode*)node { |
+- (BookmarkButton*)findAncestorButtonOnBarForNode:(const BookmarkNode*)node { |
// Find the closest parent that is visible on the bar. |
while (node) { |
// Check if we've reached one of the special buttons. Otherwise, if the next |
@@ -382,7 +383,7 @@ void RecordAppLaunch(Profile* profile, GURL url) { |
- (void)startPulsingBookmarkNode:(const BookmarkNode*)node { |
[self stopPulsingBookmarkNode]; |
- pulsingButton_.reset([self bookmarkButtonToPulseForNode:node], |
+ pulsingButton_.reset([self findAncestorButtonOnBarForNode:node], |
base::scoped_policy::RETAIN); |
if (!pulsingButton_) |
return; |
@@ -438,12 +439,20 @@ void RecordAppLaunch(Profile* profile, GURL url) { |
[view performSelector:@selector(setController:) withObject:nil]; |
// For safety, make sure the buttons can no longer call us. |
- for (BookmarkButton* button in buttons_.get()) { |
+ base::scoped_nsobject<NSMutableArray> buttons([buttons_ mutableCopy]); |
+ [buttons addObjectsFromArray:unusedButtonPool_]; |
+ if (didCreateExtraButtons_) { |
+ [buttons addObjectsFromArray:@[ |
+ appsPageShortcutButton_, managedBookmarksButton_, |
+ supervisedBookmarksButton_, otherBookmarksButton_, offTheSideButton_ |
+ ]]; |
+ } |
+ |
+ for (BookmarkButton* button in buttons.get()) { |
[button setDelegate:nil]; |
[button setTarget:nil]; |
[button setAction:nil]; |
} |
- |
bridge_.reset(NULL); |
[[NSNotificationCenter defaultCenter] removeObserver:self]; |
[self watchForExitEvent:NO]; |
@@ -456,20 +465,13 @@ void RecordAppLaunch(Profile* profile, GURL url) { |
// Remember the original sizes of the 'no items' and 'import bookmarks' |
// fields to aid in resizing when the window frame changes. |
- originalNoItemsRect_ = [[buttonView_ noItemTextfield] frame]; |
- originalImportBookmarksRect_ = [[buttonView_ importBookmarksButton] frame]; |
- |
- // Bookmark buttons start farther from the bookmark bar's left edge so |
- // adjust the positions of the noItems and importBookmarks textfields. |
- const CGFloat kBookmarksTextfieldOffsetX = 14; |
- originalNoItemsRect_.origin.x += kBookmarksTextfieldOffsetX; |
- [[buttonView_ noItemTextfield] setFrame:originalNoItemsRect_]; |
+ NSRect noItemTextFieldFrame = [[buttonView_ noItemTextField] frame]; |
+ NSRect noItemButtonFrame = [[buttonView_ importBookmarksButton] frame]; |
+ originalNoItemTextFieldWidth_ = NSWidth(noItemTextFieldFrame); |
+ originalImportBookmarksButtonWidth_ = NSWidth(noItemButtonFrame); |
+ originalNoItemInterelementPadding_ = |
+ NSMinX(noItemButtonFrame) - NSMaxX(noItemTextFieldFrame); |
- originalImportBookmarksRect_.origin.x += kBookmarksTextfieldOffsetX; |
- [[buttonView_ importBookmarksButton] setFrame:originalImportBookmarksRect_]; |
- |
- // When resized we may need to add new buttons, or remove them (if |
- // no longer visible), or add/remove the "off the side" menu. |
[[self view] setPostsFrameChangedNotifications:YES]; |
[[NSNotificationCenter defaultCenter] |
addObserver:self |
@@ -488,6 +490,7 @@ void RecordAppLaunch(Profile* profile, GURL url) { |
selector:@selector(willEnterOrLeaveFullscreen:) |
name:NSWindowWillExitFullScreenNotification |
object:nil]; |
+ [self layoutSubviews]; |
// Don't pass ourself along (as 'self') until our init is completely |
// done. Thus, this call is (almost) last. |
@@ -553,6 +556,7 @@ void RecordAppLaunch(Profile* profile, GURL url) { |
[[self view] autoresizingMask]); |
[[self view] setFrame:frame]; |
[self layoutSubviews]; |
+ [self frameDidChange]; |
} |
// Change the layout of the bookmark bar's subviews in response to a visibility |
@@ -568,7 +572,6 @@ void RecordAppLaunch(Profile* profile, GURL url) { |
padding = bookmarks::kNTPBookmarkBarPadding; |
buttonViewFrame = |
NSInsetRect(buttonViewFrame, morph * padding, morph * padding); |
- |
[buttonView_ setFrame:buttonViewFrame]; |
// Update bookmark button backgrounds. |
@@ -591,19 +594,6 @@ void RecordAppLaunch(Profile* profile, GURL url) { |
[self showBookmarkBarWithAnimation:NO]; |
} |
-- (void)updateExtraButtonsVisibility { |
- if (!appsPageShortcutButton_.get() || |
- !managedBookmarksButton_.get() || |
- !supervisedBookmarksButton_.get()) { |
- return; |
- } |
- [self setAppsPageShortcutButtonVisibility]; |
- [self setManagedBookmarksButtonVisibility]; |
- [self setSupervisedBookmarksButtonVisibility]; |
- [self resetAllButtonPositionsWithAnimation:NO]; |
- [self reconfigureBookmarkBar]; |
-} |
- |
- (void)updateHiddenState { |
BOOL oldHidden = [[self view] isHidden]; |
BOOL newHidden = ![self isVisible]; |
@@ -828,7 +818,7 @@ void RecordAppLaunch(Profile* profile, GURL url) { |
// Middle click on chevron should not open bookmarks under it, instead just |
// open its folder menu. |
if (sender == offTheSideButton_.get()) { |
- [[sender cell] setStartingChildIndex:displayedButtonCount_]; |
+ [[sender cell] setStartingChildIndex:layout_.VisibleButtonCount()]; |
NSEvent* event = [NSApp currentEvent]; |
if ([event type] == NSOtherMouseUp) { |
[self openOrCloseBookmarkFolderForOffTheSideButton]; |
@@ -891,55 +881,6 @@ void RecordAppLaunch(Profile* profile, GURL url) { |
BOOKMARK_LAUNCH_LOCATION_ATTACHED_BAR; |
} |
-// Position the right-side buttons including the off-the-side chevron. |
-- (void)positionRightSideButtons { |
- int maxX = NSMaxX([[self buttonView] bounds]) - |
- bookmarks::kBookmarkHorizontalPadding; |
- int right = maxX; |
- |
- int ignored = 0; |
- NSRect frame = [self frameForBookmarkButtonFromCell: |
- [otherBookmarksButton_ cell] xOffset:&ignored]; |
- if (![otherBookmarksButton_ isHidden]) { |
- right -= NSWidth(frame); |
- frame.origin.x = right; |
- } else { |
- frame.origin.x = maxX - NSWidth(frame); |
- } |
- [otherBookmarksButton_ setFrame:frame]; |
- |
- frame = [offTheSideButton_ frame]; |
- frame.size.height = bookmarks::kBookmarkFolderButtonHeight; |
- right -= frame.size.width; |
- frame.origin.x = right; |
- [offTheSideButton_ setFrame:frame]; |
-} |
- |
-// Configure the off-the-side button (e.g. specify the node range, |
-// check if we should enable or disable it, etc). |
-- (void)configureOffTheSideButtonContentsAndVisibility { |
- [[offTheSideButton_ cell] setStartingChildIndex:displayedButtonCount_]; |
- [[offTheSideButton_ cell] |
- setBookmarkNode:bookmarkModel_->bookmark_bar_node()]; |
- int bookmarkChildren = bookmarkModel_->bookmark_bar_node()->child_count(); |
- if (bookmarkChildren > displayedButtonCount_) { |
- [offTheSideButton_ setHidden:NO]; |
- // Set the off the side button as needing re-display. This is needed to |
- // avoid the button being shown with a black background the first time |
- // it's displayed. See https://codereview.chromium.org/1630453002/ for |
- // more context. |
- [offTheSideButton_ setNeedsDisplay:YES]; |
- } else { |
- // If we just deleted the last item in an off-the-side menu so the |
- // button will be going away, make sure the menu goes away. |
- if (folderController_ && |
- ([folderController_ parentButton] == offTheSideButton_)) |
- [self closeAllBookmarkFolders]; |
- // (And hide the button, too.) |
- [offTheSideButton_ setHidden:YES]; |
- } |
-} |
- |
// Main menubar observation code, so we can know to close our fake menus if the |
// user clicks on the actual menubar, as multiple unconnected menus sharing |
// the screen looks weird. |
@@ -996,19 +937,9 @@ void RecordAppLaunch(Profile* profile, GURL url) { |
} |
} |
-// Keep the "no items" label centered in response to a frame size change. |
-- (void)centerNoItemsLabel { |
- // Note that this computation is done in the parent's coordinate system, |
- // which is unflipped. Also, we want the label to be a fixed distance from |
- // the bottom, so that it slides up properly (on animating to hidden). |
- // The textfield sits in the itemcontainer, so to center it we maintain |
- // equal vertical padding on the top and bottom. |
- int yoffset = (NSHeight([[buttonView_ noItemTextfield] frame]) - |
- NSHeight([[buttonView_ noItemContainer] frame])) / 2; |
- [[buttonView_ noItemContainer] setFrameOrigin:NSMakePoint(0, yoffset)]; |
-} |
- |
-// (Private) |
+// Show/hide the bookmark bar. |
+// If |animate| is YES, the changes are made using the animator; otherwise they |
+// are made immediately. |
- (void)showBookmarkBarWithAnimation:(BOOL)animate { |
if (animate && stateAnimationsEnabled_) { |
// If |-doBookmarkBarAnimation| does the animation, we're done. |
@@ -1035,7 +966,8 @@ void RecordAppLaunch(Profile* profile, GURL url) { |
[self frameDidChange]; |
} |
-// (Private) |
+// Handles animating the resize of the content view. Returns YES if it handled |
+// the animation, NO if not (and hence it should be done instantly). |
- (BOOL)doBookmarkBarAnimation { |
BookmarkBarToolbarView* view = [self controlledView]; |
if ([self isAnimatingFromState:BookmarkBar::HIDDEN |
@@ -1106,189 +1038,72 @@ void RecordAppLaunch(Profile* profile, GURL url) { |
return std::min([cell cellSize].width, bookmarks::kDefaultBookmarkWidth); |
} |
-// For the given root node of the bookmark bar, show or hide (as |
-// appropriate) the "no items" container (text which says "bookmarks |
-// go here"). |
-- (void)showOrHideNoItemContainerForNode:(const BookmarkNode*)node { |
- BOOL hideNoItemWarning = !node->empty(); |
- [[buttonView_ noItemContainer] setHidden:hideNoItemWarning]; |
-} |
- |
-// TODO(jrg): write a "build bar" so there is a nice spot for things |
-// like the contextual menu which is invoked when not over a |
-// bookmark. On Safari that menu has a "new folder" option. |
-- (void)addNodesToButtonList:(const BookmarkNode*)node { |
- [self showOrHideNoItemContainerForNode:node]; |
- |
- CGFloat maxViewX = NSMaxX([[self view] bounds]); |
- int xOffset = |
- bookmarks::kBookmarkLeftMargin - bookmarks::kBookmarkHorizontalPadding; |
- |
- // Draw the apps bookmark if needed. |
- if (![appsPageShortcutButton_ isHidden]) { |
- NSRect frame = |
- [self frameForBookmarkButtonFromCell:[appsPageShortcutButton_ cell] |
- xOffset:&xOffset]; |
- [appsPageShortcutButton_ setFrame:frame]; |
- } |
- |
- // Draw the managed bookmark folder if needed. |
- if (![managedBookmarksButton_ isHidden]) { |
- xOffset += bookmarks::kBookmarkHorizontalPadding; |
- NSRect frame = |
- [self frameForBookmarkButtonFromCell:[managedBookmarksButton_ cell] |
- xOffset:&xOffset]; |
- [managedBookmarksButton_ setFrame:frame]; |
- } |
- |
- // Draw the supervised bookmark folder if needed. |
- if (![supervisedBookmarksButton_ isHidden]) { |
- xOffset += bookmarks::kBookmarkHorizontalPadding; |
- NSRect frame = |
- [self frameForBookmarkButtonFromCell:[supervisedBookmarksButton_ cell] |
- xOffset:&xOffset]; |
- [supervisedBookmarksButton_ setFrame:frame]; |
- } |
- |
- for (int i = 0; i < node->child_count(); i++) { |
- const BookmarkNode* child = node->GetChild(i); |
- BookmarkButton* button = [self buttonForNode:child xOffset:&xOffset]; |
- if (NSMinX([button frame]) >= maxViewX) { |
- [button setDelegate:nil]; |
- break; |
- } |
- [buttons_ addObject:button]; |
- } |
-} |
- |
-- (BookmarkButton*)buttonForNode:(const BookmarkNode*)node |
- xOffset:(int*)xOffset { |
- BookmarkButtonCell* cell = [self cellForBookmarkNode:node]; |
- NSRect frame = [self frameForBookmarkButtonFromCell:cell xOffset:xOffset]; |
- |
- base::scoped_nsobject<BookmarkButton> button( |
- [[BookmarkButton alloc] initWithFrame:frame]); |
- DCHECK(button.get()); |
- |
- // [NSButton setCell:] warns to NOT use setCell: other than in the |
- // initializer of a control. However, we are using a basic |
- // NSButton whose initializer does not take an NSCell as an |
- // object. To honor the assumed semantics, we do nothing with |
- // NSButton between alloc/init and setCell:. |
- [button setCell:cell]; |
- [button setDelegate:self]; |
- |
- // We cannot set the button cell's text color until it is placed in |
- // the button (e.g. the [button setCell:cell] call right above). We |
- // also cannot set the cell's text color until the view is added to |
- // the hierarchy. If that second part is now true, set the color. |
- // (If not we'll set the color on the 1st themeChanged: |
- // notification.) |
- const ui::ThemeProvider* themeProvider = [[[self view] window] themeProvider]; |
- if (themeProvider) { |
- NSColor* color = |
- themeProvider->GetNSColor(ThemeProperties::COLOR_BOOKMARK_TEXT); |
- [cell setTextColor:color]; |
+- (BookmarkButton*)buttonForNode:(const BookmarkNode*)node { |
+ BookmarkButton* button = nil; |
+ int64_t nodeId = node->id(); |
+ auto buttonIt = nodeIdToButtonMap_.find(nodeId); |
+ if (buttonIt != nodeIdToButtonMap_.end()) { |
+ button = (*buttonIt).second.get(); |
+ [self updateTitleAndTooltipForButton:button]; |
+ } else if ([unusedButtonPool_ count] > 0) { |
+ button = [[[unusedButtonPool_ firstObject] retain] autorelease]; |
+ [unusedButtonPool_ removeObjectAtIndex:0]; |
+ BOOL darkTheme = [[[self view] window] hasDarkTheme]; |
+ [[button cell] |
+ setBookmarkNode:node |
+ image:[self faviconForNode:node forADarkTheme:darkTheme]]; |
+ } else { |
+ BookmarkButtonCell* cell = [self cellForBookmarkNode:node]; |
+ NSRect frame = NSMakeRect(0, bookmarks::kBookmarkVerticalPadding, 0, |
+ kBookmarkButtonHeightMinusPadding); |
+ button = [[[BookmarkButton alloc] initWithFrame:frame] autorelease]; |
+ [button setCell:cell]; |
+ [buttonView_ addSubview:button]; |
+ [button setDelegate:self]; |
} |
+ DCHECK(button); |
+ // Do setup. |
+ nodeIdToButtonMap_.insert( |
+ {nodeId, base::scoped_nsobject<BookmarkButton>([button retain])}); |
if (node->is_folder()) { |
[button setTarget:self]; |
[button setAction:@selector(openBookmarkFolderFromButton:)]; |
[[button draggableButton] setActsOnMouseDown:YES]; |
- // If it has a title, and it will be truncated, show full title in |
- // tooltip. |
- NSString* title = base::SysUTF16ToNSString(node->GetTitle()); |
- if ([title length] && |
- [[button cell] cellSize].width > bookmarks::kDefaultBookmarkWidth) { |
- [button setToolTip:title]; |
- } |
} else { |
- // Make the button do something |
+ // Make the button do something. |
[button setTarget:self]; |
[button setAction:@selector(openBookmark:)]; |
- if (node->is_url()) |
- [button setToolTip:[BookmarkMenuCocoaController tooltipForNode:node]]; |
- } |
- return [[button.get() retain] autorelease]; |
-} |
- |
-// Add bookmark buttons to the view only if they are completely |
-// visible and don't overlap the "other bookmarks". Remove buttons |
-// which are clipped. Called when building the bookmark bar the first time. |
-- (void)addButtonsToView { |
- displayedButtonCount_ = 0; |
- NSMutableArray* buttons = [self buttons]; |
- for (NSButton* button in buttons) { |
- if (NSMaxX([button frame]) > (NSMinX([offTheSideButton_ frame]) - |
- bookmarks::kBookmarkHorizontalPadding)) |
- break; |
- [buttonView_ addSubview:button]; |
- ++displayedButtonCount_; |
+ [[button draggableButton] setActsOnMouseDown:NO]; |
} |
- NSUInteger removalCount = |
- [buttons count] - (NSUInteger)displayedButtonCount_; |
- if (removalCount > 0) { |
- NSRange removalRange = NSMakeRange(displayedButtonCount_, removalCount); |
- [buttons removeObjectsInRange:removalRange]; |
- } |
-} |
- |
-// Shows or hides the Managed, Supervised, or Other Bookmarks button as |
-// appropriate, and returns whether it ended up visible. |
-- (BOOL)setBookmarkButtonVisibility:(BookmarkButton*)button |
- canShow:(BOOL)show |
- resetAllButtonPositions:(BOOL)resetButtons { |
- if (!button) |
- return NO; |
- |
- BOOL visible = ![button bookmarkNode]->empty() && show; |
- BOOL currentVisibility = ![button isHidden]; |
- if (currentVisibility != visible) { |
- [button setHidden:!visible]; |
- if (resetButtons) |
- [self resetAllButtonPositionsWithAnimation:NO]; |
- } |
- return visible; |
+ [self updateTitleAndTooltipForButton:button]; |
+ return button; |
} |
-// Shows or hides the Managed Bookmarks button as appropriate, and returns |
-// whether it ended up visible. |
-- (BOOL)setManagedBookmarksButtonVisibility { |
- PrefService* prefs = browser_->profile()->GetPrefs(); |
- BOOL prefIsSet = |
- prefs->GetBoolean(bookmarks::prefs::kShowManagedBookmarksInBookmarkBar); |
- return [self setBookmarkButtonVisibility:managedBookmarksButton_.get() |
- canShow:prefIsSet |
- resetAllButtonPositions:YES]; |
-} |
+- (void)updateTitleAndTooltipForButton:(BookmarkButton*)button { |
+ const BookmarkNode* node = [button bookmarkNode]; |
+ CGFloat buttonWidth = [self widthOfButtonForNode:node]; |
+ NSString* buttonTitle = base::SysUTF16ToNSString(node->GetTitle()); |
-// Shows or hides the Supervised Bookmarks button as appropriate, and returns |
-// whether it ended up visible. |
-- (BOOL)setSupervisedBookmarksButtonVisibility { |
- return [self setBookmarkButtonVisibility:supervisedBookmarksButton_.get() |
- canShow:YES |
- resetAllButtonPositions:YES]; |
-} |
+ if (NSWidth([button frame]) == buttonWidth && |
+ [[button title] isEqualToString:buttonTitle]) |
+ return; |
-// Shows or hides the Other Bookmarks button as appropriate, and returns |
-// whether it ended up visible. |
-- (BOOL)setOtherBookmarksButtonVisibility { |
- return [self setBookmarkButtonVisibility:otherBookmarksButton_.get() |
- canShow:YES |
- resetAllButtonPositions:NO]; |
-} |
+ CGRect frame = [button frame]; |
+ frame.size.width = buttonWidth; |
+ [button setFrame:frame]; |
+ [[button cell] setTitle:buttonTitle]; |
+ NSString* tooltip = nil; |
-// Shows or hides the Apps button as appropriate, and returns whether it ended |
-// up visible. |
-- (BOOL)setAppsPageShortcutButtonVisibility { |
- if (!appsPageShortcutButton_.get()) |
- return NO; |
+ // Folders show a tooltip iff the title is truncated. |
+ if (node->is_folder() && [buttonTitle length] > 0 && |
+ [[button cell] cellSize].width < buttonWidth) { |
+ tooltip = buttonTitle; |
+ } else if (node->is_url()) { |
+ tooltip = [BookmarkMenuCocoaController tooltipForNode:node]; |
+ } |
- BOOL visible = |
- bookmarkModel_->loaded() && |
- chrome::ShouldShowAppsShortcutInBookmarkBar(browser_->profile()); |
- [appsPageShortcutButton_ setHidden:!visible]; |
- return visible; |
+ [button setToolTip:tooltip]; |
} |
// Creates a bookmark bar button that does not correspond to a regular bookmark |
@@ -1316,9 +1131,6 @@ void RecordAppLaunch(Profile* profile, GURL url) { |
NSCell* cell = [managedBookmarksButton_ cell]; |
[cell setTitle:title]; |
- // Its visibility may have changed too. |
- [self setManagedBookmarksButtonVisibility]; |
- |
return; |
} |
@@ -1327,16 +1139,17 @@ void RecordAppLaunch(Profile* profile, GURL url) { |
managedBookmarksButton_.reset([self createCustomBookmarkButtonForCell:cell]); |
[managedBookmarksButton_ setAction:@selector(openBookmarkFolderFromButton:)]; |
view_id_util::SetID(managedBookmarksButton_.get(), VIEW_ID_MANAGED_BOOKMARKS); |
+ NSRect frame = NSMakeRect(0, bookmarks::kBookmarkVerticalPadding, |
+ [self widthForBookmarkButtonCell:cell], |
+ kBookmarkButtonHeightMinusPadding); |
+ [managedBookmarksButton_ setFrame:frame]; |
[buttonView_ addSubview:managedBookmarksButton_.get()]; |
- [self setManagedBookmarksButtonVisibility]; |
} |
// Creates the button for "Supervised Bookmarks", but does not position it. |
- (void)createSupervisedBookmarksButton { |
if (supervisedBookmarksButton_.get()) { |
- // The button's already there, but its visibility may have changed. |
- [self setSupervisedBookmarksButtonVisibility]; |
return; |
} |
@@ -1348,9 +1161,11 @@ void RecordAppLaunch(Profile* profile, GURL url) { |
setAction:@selector(openBookmarkFolderFromButton:)]; |
view_id_util::SetID(supervisedBookmarksButton_.get(), |
VIEW_ID_SUPERVISED_BOOKMARKS); |
+ NSRect frame = NSMakeRect(0, bookmarks::kBookmarkVerticalPadding, |
+ [self widthForBookmarkButtonCell:cell], |
+ kBookmarkButtonHeightMinusPadding); |
+ [supervisedBookmarksButton_ setFrame:frame]; |
[buttonView_ addSubview:supervisedBookmarksButton_.get()]; |
- |
- [self setSupervisedBookmarksButtonVisibility]; |
} |
// Creates the button for "Other Bookmarks", but does not position it. |
@@ -1358,27 +1173,23 @@ void RecordAppLaunch(Profile* profile, GURL url) { |
// Can't create this until the model is loaded, but only need to |
// create it once. |
if (otherBookmarksButton_.get()) { |
- [self setOtherBookmarksButtonVisibility]; |
return; |
} |
NSCell* cell = [self cellForBookmarkNode:bookmarkModel_->other_node()]; |
otherBookmarksButton_.reset([self createCustomBookmarkButtonForCell:cell]); |
- // Peg at right; keep same height as bar. |
- [otherBookmarksButton_ setAutoresizingMask:(NSViewMinXMargin)]; |
[otherBookmarksButton_ setAction:@selector(openBookmarkFolderFromButton:)]; |
+ NSRect frame = NSMakeRect(0, bookmarks::kBookmarkVerticalPadding, |
+ [self widthForBookmarkButtonCell:cell], |
+ kBookmarkButtonHeightMinusPadding); |
+ [otherBookmarksButton_ setFrame:frame]; |
view_id_util::SetID(otherBookmarksButton_.get(), VIEW_ID_OTHER_BOOKMARKS); |
[buttonView_ addSubview:otherBookmarksButton_.get()]; |
- |
- [self setOtherBookmarksButtonVisibility]; |
} |
// Creates the button for "Apps", but does not position it. |
- (void)createAppsPageShortcutButton { |
- // Can't create this until the model is loaded, but only need to |
- // create it once. |
if (appsPageShortcutButton_.get()) { |
- [self setAppsPageShortcutButtonVisibility]; |
return; |
} |
@@ -1388,20 +1199,29 @@ void RecordAppLaunch(Profile* profile, GURL url) { |
IDR_BOOKMARK_BAR_APPS_SHORTCUT).ToNSImage(); |
NSCell* cell = [self cellForCustomButtonWithText:text |
image:image]; |
+ NSRect frame; |
+ frame.origin.y = bookmarks::kBookmarkVerticalPadding; |
+ frame.size = NSMakeSize([self widthForBookmarkButtonCell:cell], |
+ kBookmarkButtonHeightMinusPadding); |
appsPageShortcutButton_.reset([self createCustomBookmarkButtonForCell:cell]); |
+ [appsPageShortcutButton_ setFrame:frame]; |
[[appsPageShortcutButton_ draggableButton] setActsOnMouseDown:NO]; |
[appsPageShortcutButton_ setAction:@selector(openAppsPage:)]; |
NSString* tooltip = |
l10n_util::GetNSString(IDS_BOOKMARK_BAR_APPS_SHORTCUT_TOOLTIP); |
[appsPageShortcutButton_ setToolTip:tooltip]; |
[buttonView_ addSubview:appsPageShortcutButton_.get()]; |
- |
- [self setAppsPageShortcutButtonVisibility]; |
} |
+// Creates the "off-the-side" (chevron/overflow) button but |
+// does not position it. |
- (void)createOffTheSideButton { |
+ if (offTheSideButton_.get()) { |
+ return; |
+ } |
+ DCHECK(bookmarkModel_->loaded()); |
offTheSideButton_.reset( |
- [[BookmarkButton alloc] initWithFrame:NSMakeRect(586, 0, 20, 24)]); |
+ [[BookmarkButton alloc] initWithFrame:NSMakeRect(0, 0, 20, 24)]); |
id offTheSideCell = [BookmarkButtonCell offTheSideButtonCell]; |
[offTheSideCell setTag:kMaterialStandardButtonTypeWithLimitedClickFeedback]; |
[offTheSideCell setImagePosition:NSImageOnly]; |
@@ -1409,6 +1229,8 @@ void RecordAppLaunch(Profile* profile, GURL url) { |
[offTheSideCell setHighlightsBy:NSNoCellMask]; |
[offTheSideCell setShowsBorderOnlyWhileMouseInside:YES]; |
[offTheSideCell setBezelStyle:NSShadowlessSquareBezelStyle]; |
+ [offTheSideCell setBookmarkNode:bookmarkModel_->bookmark_bar_node()]; |
+ |
[offTheSideButton_ setCell:offTheSideCell]; |
[offTheSideButton_ setImage:[self offTheSideButtonImage:NO]]; |
[offTheSideButton_ setButtonType:NSMomentaryLightButton]; |
@@ -1418,6 +1240,22 @@ void RecordAppLaunch(Profile* profile, GURL url) { |
[offTheSideButton_ setDelegate:self]; |
[[offTheSideButton_ draggableButton] setDraggable:NO]; |
[[offTheSideButton_ draggableButton] setActsOnMouseDown:YES]; |
+ [offTheSideButton_ setHidden:YES]; |
+ [buttonView_ addSubview:offTheSideButton_]; |
+} |
+ |
+- (void)updateExtraButtonsVisibility { |
+ [self rebuildLayoutWithAnimated:NO]; |
+} |
+ |
+- (void)createExtraButtons { |
+ DCHECK(!didCreateExtraButtons_); |
+ [self createSupervisedBookmarksButton]; |
+ [self createManagedBookmarksButton]; |
+ [self createOtherBookmarksButton]; |
+ [self createAppsPageShortcutButton]; |
+ [self createOffTheSideButton]; |
+ didCreateExtraButtons_ = YES; |
} |
- (void)openAppsPage:(id)sender { |
@@ -1434,6 +1272,11 @@ void RecordAppLaunch(Profile* profile, GURL url) { |
[contextMenuController_ cancelTracking]; |
} |
+// Moves to the given next state (from the current state), possibly animating. |
+// If |animate| is NO, it will stop any running animation and jump to the given |
+// state. If YES, it may either (depending on implementation) jump to the end of |
+// the current animation and begin the next one, or stop the current animation |
+// mid-flight and animate to the next state. |
- (void)moveToState:(BookmarkBar::State)nextState |
withAnimation:(BOOL)animate { |
BOOL isAnimationRunning = [self isAnimationRunning]; |
@@ -1500,7 +1343,8 @@ void RecordAppLaunch(Profile* profile, GURL url) { |
[self moveToState:newState withAnimation:animate]; |
} |
-// (Private) |
+// Jump to final state (detached, attached, hidden, etc.) without animating, |
+// stopping a running animation if necessary. |
- (void)finalizeState { |
// We promise that our delegate that the variables will be finalized before |
// the call to |-bookmarkBar:didChangeFromState:toState:|. |
@@ -1517,7 +1361,7 @@ void RecordAppLaunch(Profile* profile, GURL url) { |
[self updateVisibility]; |
} |
-// (Private) |
+// Stops any current animation in its tracks (midway). |
- (void)stopCurrentAnimation { |
[[self controlledView] stopAnimation]; |
} |
@@ -1528,36 +1372,6 @@ void RecordAppLaunch(Profile* profile, GURL url) { |
[self finalizeState]; |
} |
-- (void)reconfigureBookmarkBar { |
- [self setManagedBookmarksButtonVisibility]; |
- [self setSupervisedBookmarksButtonVisibility]; |
- [self redistributeButtonsOnBarAsNeeded]; |
- [self positionRightSideButtons]; |
- [self configureOffTheSideButtonContentsAndVisibility]; |
- [self centerNoItemsLabel]; |
-} |
- |
-// Determine if the given |view| can completely fit within the constraint of |
-// maximum x, given by |maxViewX|, and, if not, narrow the view up to a minimum |
-// width. If the minimum width is not achievable then hide the view. Return YES |
-// if the view was hidden. |
-- (BOOL)shrinkOrHideView:(NSView*)view forMaxX:(CGFloat)maxViewX { |
- BOOL wasHidden = NO; |
- // See if the view needs to be narrowed. |
- NSRect frame = [view frame]; |
- if (NSMaxX(frame) > maxViewX) { |
- // Resize if more than 30 pixels are showing, otherwise hide. |
- if (NSMinX(frame) + 30.0 < maxViewX) { |
- frame.size.width = maxViewX - NSMinX(frame); |
- [view setFrame:frame]; |
- } else { |
- [view setHidden:YES]; |
- wasHidden = YES; |
- } |
- } |
- return wasHidden; |
-} |
- |
// Bookmark button menu items that open a new window (e.g., open in new window, |
// open in incognito, edit, etc.) cause us to lose a mouse-exited event |
// on the button, which leaves it in a hover state. |
@@ -1583,173 +1397,9 @@ void RecordAppLaunch(Profile* profile, GURL url) { |
} |
} |
- |
-// Adjust the horizontal width, x position and the visibility of the "For quick |
-// access" text field and "Import bookmarks..." button based on the current |
-// width of the containing |buttonView_| (which is affected by window width). |
-- (void)adjustNoItemContainerForMaxX:(CGFloat)maxViewX { |
- if (![[buttonView_ noItemContainer] isHidden]) { |
- // Reset initial frames for the two items, then adjust as necessary. |
- NSTextField* noItemTextfield = [buttonView_ noItemTextfield]; |
- NSRect noItemsRect = originalNoItemsRect_; |
- NSRect importBookmarksRect = originalImportBookmarksRect_; |
- if (![appsPageShortcutButton_ isHidden]) { |
- float width = NSWidth([appsPageShortcutButton_ frame]); |
- noItemsRect.origin.x += width; |
- importBookmarksRect.origin.x += width; |
- } |
- if (![managedBookmarksButton_ isHidden]) { |
- float width = NSWidth([managedBookmarksButton_ frame]); |
- noItemsRect.origin.x += width; |
- importBookmarksRect.origin.x += width; |
- } |
- if (![supervisedBookmarksButton_ isHidden]) { |
- float width = NSWidth([supervisedBookmarksButton_ frame]); |
- noItemsRect.origin.x += width; |
- importBookmarksRect.origin.x += width; |
- } |
- [noItemTextfield setFrame:noItemsRect]; |
- [noItemTextfield setHidden:NO]; |
- NSButton* importBookmarksButton = [buttonView_ importBookmarksButton]; |
- [importBookmarksButton setFrame:importBookmarksRect]; |
- [importBookmarksButton setHidden:NO]; |
- // Check each to see if they need to be shrunk or hidden. |
- if ([self shrinkOrHideView:importBookmarksButton forMaxX:maxViewX]) |
- [self shrinkOrHideView:noItemTextfield forMaxX:maxViewX]; |
- } |
-} |
- |
-// Scans through all buttons from left to right, calculating from scratch where |
-// they should be based on the preceding widths, until it finds the one |
-// requested. |
-// Returns NSZeroRect if there is no such button in the bookmark bar. |
-// Enables you to work out where a button will end up when it is done animating. |
-- (NSRect)finalRectOfButton:(BookmarkButton*)wantedButton { |
- CGFloat left = bookmarks::kBookmarkLeftMargin; |
- NSRect buttonFrame = NSZeroRect; |
- |
- // Draw the apps bookmark if needed. |
- if (![appsPageShortcutButton_ isHidden]) { |
- left = NSMaxX([appsPageShortcutButton_ frame]) + |
- bookmarks::kBookmarkHorizontalPadding; |
- } |
- |
- // Draw the managed bookmarks folder if needed. |
- if (![managedBookmarksButton_ isHidden]) { |
- left = NSMaxX([managedBookmarksButton_ frame]) + |
- bookmarks::kBookmarkHorizontalPadding; |
- } |
- |
- // Draw the supervised bookmarks folder if needed. |
- if (![supervisedBookmarksButton_ isHidden]) { |
- left = NSMaxX([supervisedBookmarksButton_ frame]) + |
- bookmarks::kBookmarkHorizontalPadding; |
- } |
- |
- for (NSButton* button in buttons_.get()) { |
- // Hidden buttons get no space. |
- if ([button isHidden]) |
- continue; |
- buttonFrame = [button frame]; |
- buttonFrame.origin.x = left; |
- left += buttonFrame.size.width + bookmarks::kBookmarkHorizontalPadding; |
- if (button == wantedButton) |
- return buttonFrame; |
- } |
- return NSZeroRect; |
-} |
- |
-// Calculates the final position of the last button in the bar. |
-// We can't just use [[self buttons] lastObject] frame] because the button |
-// may be animating currently. |
-- (NSRect)finalRectOfLastButton { |
- return [self finalRectOfButton:[[self buttons] lastObject]]; |
-} |
- |
-- (CGFloat)buttonViewMaxXWithOffTheSideButtonIsVisible:(BOOL)visible { |
- CGFloat maxViewX = NSMaxX([buttonView_ bounds]); |
- // If necessary, pull in the width to account for the Other Bookmarks button. |
- if ([self setOtherBookmarksButtonVisibility]) { |
- maxViewX = [otherBookmarksButton_ frame].origin.x - |
- bookmarks::kBookmarkRightMargin; |
- } |
- |
- [self positionRightSideButtons]; |
- // If we're already overflowing, then we need to account for the chevron. |
- if (visible) { |
- maxViewX = |
- [offTheSideButton_ frame].origin.x - bookmarks::kBookmarkRightMargin; |
- } |
- |
- return maxViewX; |
-} |
- |
-- (void)redistributeButtonsOnBarAsNeeded { |
- const BookmarkNode* node = bookmarkModel_->bookmark_bar_node(); |
- NSInteger barCount = node->child_count(); |
- |
- // Determine the current maximum extent of the visible buttons. |
- [self positionRightSideButtons]; |
- BOOL offTheSideButtonVisible = (barCount > displayedButtonCount_); |
- CGFloat maxViewX = [self buttonViewMaxXWithOffTheSideButtonIsVisible: |
- offTheSideButtonVisible]; |
- |
- // As a result of pasting or dragging, the bar may now have more buttons |
- // than will fit so remove any which overflow. They will be shown in |
- // the off-the-side folder. |
- while (displayedButtonCount_ > 0) { |
- BookmarkButton* button = [buttons_ lastObject]; |
- if (NSMaxX([self finalRectOfLastButton]) < maxViewX) |
- break; |
- [buttons_ removeLastObject]; |
- [button setDelegate:nil]; |
- [button removeFromSuperview]; |
- --displayedButtonCount_; |
- // Account for the fact that the chevron might now be visible. |
- if (!offTheSideButtonVisible) { |
- offTheSideButtonVisible = YES; |
- maxViewX = [self buttonViewMaxXWithOffTheSideButtonIsVisible:YES]; |
- } |
- } |
- |
- // As a result of cutting, deleting and dragging, the bar may now have room |
- // for more buttons. |
- int xOffset; |
- if (displayedButtonCount_ > 0) { |
- xOffset = NSMaxX([self finalRectOfLastButton]); |
- } else if (![managedBookmarksButton_ isHidden]) { |
- xOffset = NSMaxX([managedBookmarksButton_ frame]) + |
- bookmarks::kBookmarkHorizontalPadding; |
- } else if (![supervisedBookmarksButton_ isHidden]) { |
- xOffset = NSMaxX([supervisedBookmarksButton_ frame]) + |
- bookmarks::kBookmarkHorizontalPadding; |
- } else if (![appsPageShortcutButton_ isHidden]) { |
- xOffset = NSMaxX([appsPageShortcutButton_ frame]) + |
- bookmarks::kBookmarkHorizontalPadding; |
- } else { |
- xOffset = bookmarks::kBookmarkLeftMargin - |
- bookmarks::kBookmarkHorizontalPadding; |
- } |
- for (int i = displayedButtonCount_; i < barCount; ++i) { |
- const BookmarkNode* child = node->GetChild(i); |
- BookmarkButton* button = [self buttonForNode:child xOffset:&xOffset]; |
- // If we're testing against the last possible button then account |
- // for the chevron no longer needing to be shown. |
- if (i == barCount - 1) |
- maxViewX = [self buttonViewMaxXWithOffTheSideButtonIsVisible:NO]; |
- if (NSMaxX([button frame]) > maxViewX) { |
- [button setDelegate:nil]; |
- break; |
- } |
- ++displayedButtonCount_; |
- [buttons_ addObject:button]; |
- [buttonView_ addSubview:button]; |
- } |
- |
- // While we're here, adjust the horizontal width and the visibility |
- // of the "For quick access" and "Import bookmarks..." text fields. |
- if (![buttons_ count]) |
- [self adjustNoItemContainerForMaxX:maxViewX]; |
+- (void)addButtonForNode:(const bookmarks::BookmarkNode*)node |
+ atIndex:(NSInteger)buttonIndex { |
+ [self rebuildLayoutWithAnimated:NO]; |
} |
#pragma mark Private Methods Exposed for Testing |
@@ -1762,16 +1412,16 @@ void RecordAppLaunch(Profile* profile, GURL url) { |
return buttons_.get(); |
} |
-- (BOOL)offTheSideButtonIsHidden { |
- return [offTheSideButton_ isHidden]; |
+- (BookmarkButton*)otherBookmarksButton { |
+ return otherBookmarksButton_.get(); |
} |
-- (BOOL)appsPageShortcutButtonIsHidden { |
- return [appsPageShortcutButton_ isHidden]; |
+- (BookmarkButton*)managedBookmarksButton { |
+ return managedBookmarksButton_.get(); |
} |
-- (BookmarkButton*)otherBookmarksButton { |
- return otherBookmarksButton_.get(); |
+- (BookmarkButton*)supervisedBookmarksButton { |
+ return supervisedBookmarksButton_.get(); |
} |
- (BookmarkBarFolderController*)folderController { |
@@ -1782,29 +1432,6 @@ void RecordAppLaunch(Profile* profile, GURL url) { |
return folderTarget_.get(); |
} |
-- (int)displayedButtonCount { |
- return displayedButtonCount_; |
-} |
- |
-// Delete all buttons (bookmarks, chevron, "other bookmarks") from the |
-// bookmark bar; reset knowledge of bookmarks. |
-- (void)clearBookmarkBar { |
- [self stopPulsingBookmarkNode]; |
- for (BookmarkButton* button in buttons_.get()) { |
- [button setDelegate:nil]; |
- [button removeFromSuperview]; |
- } |
- [buttons_ removeAllObjects]; |
- displayedButtonCount_ = 0; |
- |
- // Make sure there are no stale pointers in the pasteboard. This |
- // can be important if a bookmark is deleted (via bookmark sync) |
- // while in the middle of a drag. The "drag completed" code |
- // (e.g. [BookmarkBarView performDragOperationForBookmarkButton:]) is |
- // careful enough to bail if there is no data found at "drop" time. |
- [[NSPasteboard pasteboardWithName:NSDragPboard] clearContents]; |
-} |
- |
// Return an autoreleased NSCell suitable for a bookmark button. |
// TODO(jrg): move much of the cell config into the BookmarkButtonCell class. |
- (BookmarkButtonCell*)cellForBookmarkNode:(const BookmarkNode*)node { |
@@ -1842,59 +1469,9 @@ void RecordAppLaunch(Profile* profile, GURL url) { |
return cell; |
} |
-// Returns a frame appropriate for the given bookmark cell, suitable |
-// for creating an NSButton that will contain it. |xOffset| is the X |
-// offset for the frame; it is increased to be an appropriate X offset |
-// for the next button. |
-- (NSRect)frameForBookmarkButtonFromCell:(NSCell*)cell |
- xOffset:(int*)xOffset { |
- DCHECK(xOffset); |
- NSRect bounds = [buttonView_ bounds]; |
- bounds.size.height = bookmarks::kBookmarkButtonHeight; |
- |
- NSRect frame = NSInsetRect(bounds, |
- bookmarks::kBookmarkHorizontalPadding, |
- bookmarks::kBookmarkVerticalPadding); |
- frame.size.width = [self widthForBookmarkButtonCell:cell]; |
- |
- // Add an X offset based on what we've already done |
- frame.origin.x += *xOffset; |
- |
- // And up the X offset for next time. |
- *xOffset = NSMaxX(frame); |
- |
- return frame; |
-} |
- |
-// A bookmark button's contents changed. Check for growth |
-// (e.g. increase the width up to the maximum). If we grew, move |
-// other bookmark buttons over. |
-- (void)checkForBookmarkButtonGrowth:(NSButton*)changedButton { |
- NSRect frame = [changedButton frame]; |
- CGFloat desiredSize = [self widthForBookmarkButtonCell:[changedButton cell]]; |
- CGFloat delta = desiredSize - frame.size.width; |
- if (delta) { |
- frame.size.width = desiredSize; |
- [changedButton setFrame:frame]; |
- for (NSButton* button in buttons_.get()) { |
- NSRect buttonFrame = [button frame]; |
- if (buttonFrame.origin.x > frame.origin.x) { |
- buttonFrame.origin.x += delta; |
- [button setFrame:buttonFrame]; |
- } |
- } |
- } |
- // We may have just crossed a threshold to enable the off-the-side |
- // button. |
- [self configureOffTheSideButtonContentsAndVisibility]; |
-} |
- |
// Called when our controlled frame has changed size. |
- (void)frameDidChange { |
- if (!bookmarkModel_->loaded()) |
- return; |
- [self updateTheme:[[[self view] window] themeProvider]]; |
- [self reconfigureBookmarkBar]; |
+ [self rebuildLayoutWithAnimated:NO]; |
} |
// Adapt appearance of buttons to the current theme. Called after |
@@ -1989,11 +1566,14 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) { |
// given array, or nil if none. We use this for distinguishing |
// between a hover-open candidate or drop-indicator draw. |
// Helper for buttonForDroppingOnAtPoint:. |
-// Get UI review on "middle half" ness. |
-// http://crbug.com/36276 |
- (BookmarkButton*)buttonForDroppingOnAtPoint:(NSPoint)point |
fromArray:(NSArray*)array { |
- for (BookmarkButton* button in array) { |
+ // Ensure buttons are scanned left to right. |
+ id<NSFastEnumeration> buttonsToCheck = |
Avi (use Gerrit)
2017/04/27 18:00:29
NSEnumerator*
|
+ cocoa_l10n_util::ShouldDoExperimentalRTLLayout() |
+ ? [array reverseObjectEnumerator] |
+ : [array objectEnumerator]; |
+ for (BookmarkButton* button in buttonsToCheck) { |
// Hidden buttons can overlap valid visible buttons, just ignore. |
if ([button isHidden]) |
continue; |
@@ -2041,7 +1621,7 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) { |
// the button's folder being closed if the mouse temporarily leaves the |
// middle half but is still within the button bounds. |
if (hoverButton_ && NSPointInRect(point, [hoverButton_ frame])) |
- return hoverButton_.get(); |
+ return hoverButton_.get(); |
BookmarkButton* button = [self buttonForDroppingOnAtPoint:point |
fromArray:buttons_.get()]; |
@@ -2049,7 +1629,7 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) { |
// This is different than BookmarkBarFolderController. |
if (!button) { |
NSMutableArray* array = [NSMutableArray array]; |
- if (![self offTheSideButtonIsHidden]) |
+ if (layout_.IsOffTheSideButtonVisible()) |
[array addObject:offTheSideButton_]; |
[array addObject:otherBookmarksButton_]; |
button = [self buttonForDroppingOnAtPoint:point |
@@ -2058,33 +1638,38 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) { |
return button; |
} |
+// Returns the index in the model for a drag to the location given by |
+// |point|. This is determined by finding the first button before the center |
+// of which |point| falls, scanning front to back. Note that, currently, only |
+// the x-coordinate of |point| is considered. Though not currently implemented, |
+// we may check for errors, in which case this would return negative value; |
+// callers should check for this. |
- (int)indexForDragToPoint:(NSPoint)point { |
- // TODO(jrg): revisit position info based on UI team feedback. |
- // dropLocation is in bar local coordinates. |
NSPoint dropLocation = |
[[self view] convertPoint:point |
fromView:[[[self view] window] contentView]]; |
- BookmarkButton* buttonToTheRightOfDraggedButton = nil; |
- for (BookmarkButton* button in buttons_.get()) { |
+ |
+ BOOL isRTL = cocoa_l10n_util::ShouldDoExperimentalRTLLayout(); |
+ for (size_t i = 0; i < layout_.VisibleButtonCount(); i++) { |
+ BookmarkButton* button = [buttons_ objectAtIndex:i]; |
CGFloat midpoint = NSMidX([button frame]); |
- if (dropLocation.x <= midpoint) { |
- buttonToTheRightOfDraggedButton = button; |
- break; |
+ if (isRTL ? dropLocation.x >= midpoint : dropLocation.x <= midpoint) { |
+ return i; |
} |
} |
- if (buttonToTheRightOfDraggedButton) { |
- const BookmarkNode* afterNode = |
- [buttonToTheRightOfDraggedButton bookmarkNode]; |
- DCHECK(afterNode); |
- int index = afterNode->parent()->GetIndexOf(afterNode); |
- // Make sure we don't get confused by buttons which aren't visible. |
- return std::min(index, displayedButtonCount_); |
- } |
- |
- // If nothing is to my right I am at the end! |
- return displayedButtonCount_; |
+ // No buttons left to insert the dragged button before, so it |
+ // goes at the end. |
+ return layout_.VisibleButtonCount(); |
} |
+// Informs the bar that something representing the bookmark at |
+// |sourceNode| was dragged into the bar. |
+// |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 |
+// made and inserted into the new location while leaving the bookmark in |
+// the old location, otherwise move the bookmark by removing from its old |
+// location and inserting into the new location. |
+// Returns whether the drag was allowed. |
// TODO(mrossetti,jrg): Yet more duplicated code. |
// http://crbug.com/35966 |
- (BOOL)dragBookmark:(const BookmarkNode*)sourceNode |
@@ -2131,155 +1716,393 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) { |
- (void)draggingEnded:(id<NSDraggingInfo>)info { |
[self closeFolderAndStopTrackingMenus]; |
- [[BookmarkButton draggedButton] setHidden:NO]; |
- [self resetAllButtonPositionsWithAnimation:YES]; |
+ layout_ = {}; // Force layout. |
+ [self rebuildLayoutWithAnimated:YES]; |
} |
// Set insertionPos_ and hasInsertionPos_, and make insertion space for a |
// hypothetical drop with the new button having a left edge of |where|. |
-// Gets called only by our view. |
- (void)setDropInsertionPos:(CGFloat)where { |
- if (!hasInsertionPos_ || where != insertionPos_) { |
- insertionPos_ = where; |
- hasInsertionPos_ = YES; |
- CGFloat left; |
- if (![supervisedBookmarksButton_ isHidden]) { |
- left = NSMaxX([supervisedBookmarksButton_ frame]) + |
- bookmarks::kBookmarkHorizontalPadding; |
- } else if (![managedBookmarksButton_ isHidden]) { |
- left = NSMaxX([managedBookmarksButton_ frame]) + |
- bookmarks::kBookmarkHorizontalPadding; |
- } else if (![appsPageShortcutButton_ isHidden]) { |
- left = NSMaxX([appsPageShortcutButton_ frame]) + |
- bookmarks::kBookmarkHorizontalPadding; |
+ if (hasInsertionPos_ && where == insertionPos_) { |
+ return; |
+ } |
+ insertionPos_ = where; |
+ hasInsertionPos_ = YES; |
+ CGFloat paddingWidth = bookmarks::kDefaultBookmarkWidth; |
+ BookmarkButton* draggedButton = [BookmarkButton draggedButton]; |
+ BOOL draggedButtonIsOnBar = NO; |
+ int64_t draggedButtonNodeId; |
+ CGFloat draggedButtonOffset; |
+ if (draggedButton) { |
+ paddingWidth = std::min(bookmarks::kDefaultBookmarkWidth, |
+ NSWidth([draggedButton frame])); |
+ draggedButtonNodeId = [draggedButton bookmarkNode]->id(); |
+ auto offsetIt = layout_.button_offsets.find(draggedButtonNodeId); |
+ if (offsetIt != layout_.button_offsets.end()) { |
+ draggedButtonIsOnBar = YES; |
+ draggedButtonOffset = (*offsetIt).second; |
+ } |
+ } |
+ paddingWidth += bookmarks::kBookmarkHorizontalPadding; |
+ BOOL isRTL = cocoa_l10n_util::ShouldDoExperimentalRTLLayout(); |
+ |
+ // If the button being dragged is not currently on the bar |
+ // (for example, a drag from the URL bar, a link on the desktop, |
+ // a button inside a menu, etc.), buttons in front of the drag position |
+ // (to the right in LTR, to the left in RTL), should make room for it. |
+ // Otherwise: |
+ // If a given button was to the left of the dragged button's original |
+ // position, but is to the right of the drag position, or |
+ // if it was to the right of the dragged button's original position and |
+ // is to the left of the drag position, make room. |
+ // TODO(lgrey): Extract ScopedNSAnimationContextGroup |
+ // from the tab strip controller and use it on all of these. |
+ [NSAnimationContext beginGrouping]; |
+ [[NSAnimationContext currentContext] |
+ setDuration:kDragAndDropAnimationDuration]; |
+ |
+ for (BookmarkButton* button in buttons_.get()) { |
+ CGRect buttonFrame = [button frame]; |
+ int64_t nodeId = [button bookmarkNode]->id(); |
+ CGFloat offset = layout_.button_offsets[nodeId]; |
+ CGFloat buttonEdge = isRTL ? NSWidth([buttonView_ frame]) - offset : offset; |
+ |
+ if (draggedButtonIsOnBar) { |
+ if (nodeId == draggedButtonNodeId) |
+ continue; |
+ BOOL wasBefore = offset < draggedButtonOffset; |
+ BOOL isBefore = isRTL ? buttonEdge > where : buttonEdge < where; |
+ if (isBefore && !wasBefore) { |
+ offset -= paddingWidth; |
+ } else if (!isBefore && wasBefore) { |
+ offset += paddingWidth; |
+ } |
} else { |
- left = bookmarks::kBookmarkLeftMargin; |
+ if (isRTL ? (where > buttonEdge) |
+ : (where < offset + NSWidth(buttonFrame))) { |
+ offset += paddingWidth; |
+ } |
} |
- CGFloat paddingWidth = bookmarks::kDefaultBookmarkWidth; |
- BookmarkButton* draggedButton = [BookmarkButton draggedButton]; |
- if (draggedButton) { |
- paddingWidth = std::min(bookmarks::kDefaultBookmarkWidth, |
- NSWidth([draggedButton frame])); |
+ |
+ [self applyXOffset:offset |
+ toButton:button |
+ animated:innerContentAnimationsEnabled_]; |
+ } |
+ |
+ [NSAnimationContext endGrouping]; |
+} |
+ |
+- (BookmarkBarLayout)layoutFromCurrentState { |
+ BookmarkBarLayout layout = {}; |
+ |
+ const BookmarkNode* node = bookmarkModel_->bookmark_bar_node(); |
+ CGFloat viewWidth = NSWidth([buttonView_ frame]); |
+ CGFloat xOffset = bookmarks::kBookmarkLeftMargin; |
+ CGFloat maxX = viewWidth - bookmarks::kBookmarkHorizontalPadding; |
+ |
+ layout.visible_elements = bookmarks::kVisibleElementsMaskNone; |
+ layout.max_x = maxX; |
+ |
+ // Lay out "extra" buttons (apps, managed, supervised, "Other"). |
+ if (chrome::ShouldShowAppsShortcutInBookmarkBar(browser_->profile())) { |
+ layout.visible_elements |= bookmarks::kVisibleElementsMaskAppsButton; |
+ layout.apps_button_offset = xOffset; |
+ xOffset += NSWidth([appsPageShortcutButton_ frame]) + |
+ bookmarks::kBookmarkHorizontalPadding; |
+ } |
+ if (!managedBookmarkService_->managed_node()->empty()) { |
+ layout.visible_elements |= |
+ bookmarks::kVisibleElementsMaskManagedBookmarksButton; |
+ layout.managed_bookmarks_button_offset = xOffset; |
+ xOffset += NSWidth([managedBookmarksButton_ frame]) + |
+ bookmarks::kBookmarkHorizontalPadding; |
+ } |
+ if (!managedBookmarkService_->supervised_node()->empty()) { |
+ layout.visible_elements |= |
+ bookmarks::kVisibleElementsMaskSupervisedBookmarksButton; |
+ layout.supervised_bookmarks_button_offset = xOffset; |
+ xOffset += NSWidth([supervisedBookmarksButton_ frame]) + |
+ bookmarks::kBookmarkHorizontalPadding; |
+ } |
+ if (!bookmarkModel_->other_node()->empty()) { |
+ layout.visible_elements |= |
+ bookmarks::kVisibleElementsMaskOtherBookmarksButton; |
+ maxX -= NSWidth([otherBookmarksButton_ frame]); |
+ layout.other_bookmarks_button_offset = maxX; |
+ } |
+ |
+ // Lay out empty state ("no items" label, import bookmarks button.) |
+ if (node->empty()) { |
+ CGFloat roomForTextField = |
+ maxX - xOffset + bookmarks::kInitialNoItemTextFieldXOrigin; |
+ if (roomForTextField >= kNoItemElementMinWidth) { |
+ layout.visible_elements |= bookmarks::kVisibleElementsMaskNoItemTextField; |
+ xOffset += bookmarks::kInitialNoItemTextFieldXOrigin; |
+ layout.no_item_textfield_offset = xOffset; |
+ layout.no_item_textfield_width = |
+ std::min(maxX - xOffset, originalNoItemTextFieldWidth_); |
+ xOffset += |
+ layout.no_item_textfield_width + originalNoItemInterelementPadding_; |
+ |
+ // Does the "import bookmarks" button fit? |
+ if (maxX - xOffset >= kNoItemElementMinWidth) { |
+ layout.visible_elements |= |
+ bookmarks::kVisibleElementsMaskImportBookmarksButton; |
+ layout.import_bookmarks_button_offset = xOffset; |
+ layout.import_bookmarks_button_width = |
+ std::min(maxX - xOffset, originalImportBookmarksButtonWidth_); |
+ } |
} |
- // Put all the buttons where they belong, with all buttons to the right |
- // of the insertion point shuffling right to make space for it. |
- [NSAnimationContext beginGrouping]; |
- [[NSAnimationContext currentContext] |
- setDuration:kDragAndDropAnimationDuration]; |
- for (NSButton* button in buttons_.get()) { |
- // Hidden buttons get no space. |
- if ([button isHidden]) |
- continue; |
- NSRect buttonFrame = [button frame]; |
- buttonFrame.origin.x = left; |
- // Update "left" for next time around. |
- left += buttonFrame.size.width; |
- if (left > insertionPos_) |
- buttonFrame.origin.x += paddingWidth; |
- left += bookmarks::kBookmarkHorizontalPadding; |
- if (innerContentAnimationsEnabled_) |
- [[button animator] setFrame:buttonFrame]; |
- else |
- [button setFrame:buttonFrame]; |
+ } else { |
+ // Lay out bookmark buttons and "off-the-side" chevron. |
+ CGFloat offTheSideButtonWidth = NSWidth([offTheSideButton_ frame]); |
+ int buttonCount = node->child_count(); |
+ for (int i = 0; i < buttonCount; i++) { |
+ const BookmarkNode* buttonNode = node->GetChild(i); |
+ CGFloat widthOfButton = [self widthOfButtonForNode:buttonNode]; |
+ // If it's the last button, we just need to ensure that it can fit. |
+ // If not, we need to be able to fit both it and the chevron. |
+ CGFloat widthToCheck = i == buttonCount - 1 |
+ ? widthOfButton |
+ : widthOfButton + offTheSideButtonWidth; |
+ if (xOffset + widthToCheck > maxX) { |
+ layout.visible_elements |= |
+ bookmarks::kVisibleElementsMaskOffTheSideButton; |
+ layout.off_the_side_button_offset = maxX - offTheSideButtonWidth; |
+ break; |
+ } |
+ layout.button_offsets.insert({buttonNode->id(), xOffset}); |
+ xOffset += widthOfButton + bookmarks::kBookmarkHorizontalPadding; |
} |
- [NSAnimationContext endGrouping]; |
} |
+ return layout; |
} |
-// Put all visible bookmark bar buttons in their normal locations, either with |
-// or without animation according to the |animate| flag. |
-// This is generally useful, so is called from various places internally. |
-- (void)resetAllButtonPositionsWithAnimation:(BOOL)animate { |
+- (void)rebuildLayoutWithAnimated:(BOOL)animated { |
+ if (!bookmarkModel_->loaded()) |
+ return; |
+ |
+ BookmarkBarLayout layout = [self layoutFromCurrentState]; |
+ if (layout_ != layout) { |
+ layout_ = std::move(layout); |
+ [self applyLayout:layout_ animated:animated]; |
+ } |
+} |
+ |
+- (void)applyLayout:(const BookmarkBarLayout&)layout animated:(BOOL)animated { |
+ if (!bookmarkModel_->loaded()) |
+ return; |
+ |
+ if (folderController_) |
+ [self closeAllBookmarkFolders]; |
+ [self stopPulsingBookmarkNode]; |
+ |
+ // Hide or show "extra" buttons and position if necessary. |
+ [self applyXOffset:layout.apps_button_offset |
+ toButton:appsPageShortcutButton_ |
+ animated:NO]; |
+ [appsPageShortcutButton_ setHidden:!layout.IsAppsButtonVisible()]; |
+ |
+ [self applyXOffset:layout.managed_bookmarks_button_offset |
+ toButton:managedBookmarksButton_ |
+ animated:NO]; |
+ [managedBookmarksButton_ setHidden:!layout.IsManagedBookmarksButtonVisible()]; |
- // Position the apps bookmark if needed. |
- CGFloat left = bookmarks::kBookmarkLeftMargin; |
- if (![appsPageShortcutButton_ isHidden]) { |
- int xOffset = bookmarks::kBookmarkLeftMargin - |
- bookmarks::kBookmarkHorizontalPadding; |
- NSRect frame = |
- [self frameForBookmarkButtonFromCell:[appsPageShortcutButton_ cell] |
- xOffset:&xOffset]; |
- [appsPageShortcutButton_ setFrame:frame]; |
- left = xOffset + bookmarks::kBookmarkHorizontalPadding; |
+ [self applyXOffset:layout.supervised_bookmarks_button_offset |
+ toButton:supervisedBookmarksButton_ |
+ animated:NO]; |
+ [supervisedBookmarksButton_ |
+ setHidden:!layout.IsSupervisedBookmarksButtonVisible()]; |
+ |
+ [self applyXOffset:layout.other_bookmarks_button_offset |
+ toButton:otherBookmarksButton_ |
+ animated:NO]; |
+ [otherBookmarksButton_ setHidden:!layout.IsOtherBookmarksButtonVisible()]; |
+ |
+ [self applyXOffset:layout.off_the_side_button_offset |
+ toButton:offTheSideButton_ |
+ animated:NO]; |
+ [offTheSideButton_ setHidden:!layout.IsOffTheSideButtonVisible()]; |
+ if (layout.IsOffTheSideButtonVisible()) { |
+ [[offTheSideButton_ cell] |
+ setStartingChildIndex:layout_.VisibleButtonCount()]; |
+ } |
+ |
+ // Hide or show empty state and position if necessary. |
+ [[buttonView_ noItemTextField] setHidden:!layout.IsNoItemTextFieldVisible()]; |
+ [[buttonView_ importBookmarksButton] |
+ setHidden:!layout.IsImportBookmarksButtonVisible()]; |
+ |
+ if (layout.IsNoItemTextFieldVisible()) { |
+ NSTextField* noItemTextField = [buttonView_ noItemTextField]; |
+ [noItemTextField setHidden:NO]; |
+ NSRect textFieldFrame = [noItemTextField frame]; |
+ textFieldFrame.size.width = layout.no_item_textfield_width; |
+ if (cocoa_l10n_util::ShouldDoExperimentalRTLLayout()) { |
+ textFieldFrame.origin.x = NSWidth([buttonView_ frame]) - |
+ layout.no_item_textfield_offset - |
+ layout.no_item_textfield_width; |
+ } else { |
+ textFieldFrame.origin.x = layout.no_item_textfield_offset; |
+ } |
+ [noItemTextField setFrame:textFieldFrame]; |
+ |
+ if (layout.IsImportBookmarksButtonVisible()) { |
+ NSButton* importBookmarksButton = [buttonView_ importBookmarksButton]; |
+ NSRect buttonFrame = [importBookmarksButton frame]; |
+ buttonFrame.size.width = layout.import_bookmarks_button_width; |
+ if (cocoa_l10n_util::ShouldDoExperimentalRTLLayout()) { |
+ buttonFrame.origin.x = NSWidth([buttonView_ frame]) - |
+ layout.import_bookmarks_button_offset - |
+ layout.import_bookmarks_button_width; |
+ } else { |
+ buttonFrame.origin.x = layout.import_bookmarks_button_offset; |
+ } |
+ [importBookmarksButton setFrame:buttonFrame]; |
+ } |
+ } |
+ // 1) Hide all buttons initially. |
+ // 2) Show all buttons in the layout. |
+ // 3) Remove any buttons that are still hidden (so: not in the layout) |
+ // from the node ID -> button map and add them to the reuse pool if |
+ // there's room. |
+ for (BookmarkButton* button in buttons_.get()) { |
+ [button setHidden:YES]; |
} |
+ [buttons_ removeAllObjects]; |
- // Position the managed bookmarks folder if needed. |
- if (![managedBookmarksButton_ isHidden]) { |
- int xOffset = left; |
- NSRect frame = |
- [self frameForBookmarkButtonFromCell:[managedBookmarksButton_ cell] |
- xOffset:&xOffset]; |
- [managedBookmarksButton_ setFrame:frame]; |
- left = xOffset + bookmarks::kBookmarkHorizontalPadding; |
+ const BookmarkNode* parentNode = bookmarkModel_->bookmark_bar_node(); |
+ for (size_t i = 0; i < layout.VisibleButtonCount(); i++) { |
+ const BookmarkNode* node = parentNode->GetChild(i); |
+ DCHECK(node); |
+ BookmarkButton* button = [self buttonForNode:node]; |
+ CGFloat offset = layout.button_offsets.at(node->id()); |
+ [self applyXOffset:offset |
+ toButton:button |
+ animated:animated && innerContentAnimationsEnabled_]; |
+ [buttons_ addObject:button]; |
+ [button setHidden:NO]; |
} |
- // Position the supervised bookmarks folder if needed. |
- if (![supervisedBookmarksButton_ isHidden]) { |
- int xOffset = left; |
- NSRect frame = |
- [self frameForBookmarkButtonFromCell:[supervisedBookmarksButton_ cell] |
- xOffset:&xOffset]; |
- [supervisedBookmarksButton_ setFrame:frame]; |
- left = xOffset + bookmarks::kBookmarkHorizontalPadding; |
+ for (auto& item : nodeIdToButtonMap_) { |
+ if ([item.second isHidden]) { |
+ if ([unusedButtonPool_ count] < kMaxReusePoolSize) { |
+ BookmarkButton* button = item.second.get(); |
+ // Dragged buttons unhide themselves, so position it off-screen |
+ // while it's in the reuse pool. |
+ CGRect buttonFrame = [button frame]; |
+ buttonFrame.origin.x = -10000; |
+ [button setFrame:buttonFrame]; |
+ [unusedButtonPool_ addObject:button]; |
+ } else { |
+ [item.second setDelegate:nil]; |
+ [item.second removeFromSuperview]; |
+ } |
+ |
+ nodeIdToButtonMap_.erase(item.first); |
+ } |
} |
+ const ui::ThemeProvider* themeProvider = [[[self view] window] themeProvider]; |
+ [self updateTheme:themeProvider]; |
+} |
- animate &= innerContentAnimationsEnabled_; |
+- (void)applyXOffset:(CGFloat)offset |
+ toButton:(BookmarkButton*)button |
+ animated:(BOOL)animated { |
+ CGRect frame = [button frame]; |
+ BOOL wasOffscreen = frame.origin.x < -200; |
+ if (cocoa_l10n_util::ShouldDoExperimentalRTLLayout()) { |
+ frame.origin.x = NSWidth([buttonView_ frame]) - offset - NSWidth(frame); |
+ } else { |
+ frame.origin.x = offset; |
+ } |
- for (NSButton* button in buttons_.get()) { |
- // Hidden buttons get no space. |
- if ([button isHidden]) |
- continue; |
- NSRect buttonFrame = [button frame]; |
- buttonFrame.origin.x = left; |
- left += buttonFrame.size.width + bookmarks::kBookmarkHorizontalPadding; |
- if (animate) |
- [[button animator] setFrame:buttonFrame]; |
- else |
- [button setFrame:buttonFrame]; |
+ // Buttons fresh from the reuse pool are kept offscreen. Animation should |
+ // be disabled for them so they don't slide in. |
+ if (animated && !wasOffscreen) { |
+ [[button animator] setFrame:frame]; |
+ } else { |
+ [button setFrame:frame]; |
} |
} |
+- (CGFloat)widthOfButtonForNode:(const BookmarkNode*)node { |
+ // TODO(lgrey): Can we get this information without an actual image? |
+ NSImage* image = [self faviconForNode:node forADarkTheme:NO]; |
+ CGFloat width = [BookmarkButtonCell cellWidthForNode:node image:image]; |
+ return std::min(width, bookmarks::kDefaultBookmarkWidth); |
+} |
+ |
// Clear insertion flag, remove insertion space and put all visible bookmark |
// bar buttons in their normal locations. |
// Gets called only by our view. |
+// TODO(lgrey): Is there a sane way to dedupe this with |setDropInsertionPos:|? |
- (void)clearDropInsertionPos { |
- if (hasInsertionPos_) { |
- hasInsertionPos_ = NO; |
- [self resetAllButtonPositionsWithAnimation:YES]; |
+ if (!hasInsertionPos_) { |
+ return; |
+ } |
+ hasInsertionPos_ = NO; |
+ BookmarkButton* draggedButton = [BookmarkButton draggedButton]; |
+ if (!draggedButton) { |
+ [self applyLayout:layout_ animated:YES]; |
+ return; |
+ } |
+ int64_t draggedButtonNodeId = [draggedButton bookmarkNode]->id(); |
+ if (layout_.button_offsets.find(draggedButtonNodeId) == |
+ layout_.button_offsets.end()) { |
+ [self applyLayout:layout_ animated:YES]; |
+ return; |
} |
+ // The dragged button came from the bar, but is being dragged outside |
+ // of it now, so the rest of the buttons should be laid out as if it |
+ // were removed. |
+ CGFloat draggedButtonOffset = layout_.button_offsets[draggedButtonNodeId]; |
+ CGFloat spaceForDraggedButton = |
+ NSWidth([draggedButton frame]) + bookmarks::kBookmarkHorizontalPadding; |
+ [NSAnimationContext beginGrouping]; |
+ [[NSAnimationContext currentContext] |
+ setDuration:kDragAndDropAnimationDuration]; |
+ |
+ for (BookmarkButton* button in buttons_.get()) { |
+ int64_t nodeId = [button bookmarkNode]->id(); |
+ CGFloat offset = layout_.button_offsets[nodeId]; |
+ |
+ if (nodeId == draggedButtonNodeId) |
+ continue; |
+ |
+ if (offset > draggedButtonOffset) { |
+ offset -= spaceForDraggedButton; |
+ [self applyXOffset:offset |
+ toButton:button |
+ animated:innerContentAnimationsEnabled_]; |
+ } |
+ } |
+ |
+ [NSAnimationContext endGrouping]; |
} |
#pragma mark Bridge Notification Handlers |
-// TODO(jrg): for now this is brute force. |
- (void)loaded:(BookmarkModel*)model { |
DCHECK(model == bookmarkModel_); |
if (!model->loaded()) |
return; |
+ // Make sure there are no stale pointers in the pasteboard. This |
+ // can be important if a bookmark is deleted (via bookmark sync) |
+ // while in the middle of a drag. The "drag completed" code |
+ // (e.g. [BookmarkBarView performDragOperationForBookmarkButton:]) is |
+ // careful enough to bail if there is no data found at "drop" time. |
+ [[NSPasteboard pasteboardWithName:NSDragPboard] clearContents]; |
+ |
+ if (!didCreateExtraButtons_) { |
+ [self createExtraButtons]; |
+ } |
// If this is a rebuild request while we have a folder open, close it. |
- // TODO(mrossetti): Eliminate the need for this because it causes the folder |
- // menu to disappear after a cut/copy/paste/delete change. |
- // See: http://crbug.com/36614 |
if (folderController_) |
[self closeAllBookmarkFolders]; |
- |
- // Brute force nuke and build. |
- savedFrameWidth_ = NSWidth([[self view] frame]); |
- const BookmarkNode* node = model->bookmark_bar_node(); |
- [self clearBookmarkBar]; |
- [self createAppsPageShortcutButton]; |
- [self createManagedBookmarksButton]; |
- [self createSupervisedBookmarksButton]; |
- [self addNodesToButtonList:node]; |
- [self createOtherBookmarksButton]; |
- [self updateTheme:[[[self view] window] themeProvider]]; |
- [self positionRightSideButtons]; |
- [self addButtonsToView]; |
- [self configureOffTheSideButtonContentsAndVisibility]; |
- [self reconfigureBookmarkBar]; |
+ [self rebuildLayoutWithAnimated:NO]; |
} |
- |
- (void)nodeAdded:(BookmarkModel*)model |
parent:(const BookmarkNode*)newParent index:(int)newIndex { |
// If a context menu is open, close it. |
@@ -2289,11 +2112,7 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) { |
id<BookmarkButtonControllerProtocol> newController = |
[self controllerForNode:newParent]; |
[newController addButtonForNode:newNode atIndex:newIndex]; |
- // If we go from 0 --> 1 bookmarks we may need to hide the |
- // "bookmarks go here" text container. |
- [self showOrHideNoItemContainerForNode:model->bookmark_bar_node()]; |
- // Cope with chevron or "Other Bookmarks" buttons possibly changing state. |
- [self reconfigureBookmarkBar]; |
+ [self rebuildLayoutWithAnimated:NO]; |
} |
// TODO(jrg): for now this is brute force. |
@@ -2316,11 +2135,7 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) { |
[oldController removeButton:oldIndex animate:NO]; |
[newController addButtonForNode:movedNode atIndex:newIndex]; |
} |
- // If the bar is one of the parents we may need to update the visibility |
- // of the "bookmarks go here" presentation. |
- [self showOrHideNoItemContainerForNode:model->bookmark_bar_node()]; |
- // Cope with chevron or "Other Bookmarks" buttons possibly changing state. |
- [self reconfigureBookmarkBar]; |
+ [self rebuildLayoutWithAnimated:NO]; |
} |
- (void)nodeRemoved:(BookmarkModel*)model |
@@ -2333,17 +2148,9 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) { |
id<BookmarkButtonControllerProtocol> parentController = |
[self controllerForNode:oldParent]; |
[parentController removeButton:index animate:YES]; |
- // If we go from 1 --> 0 bookmarks we may need to show the |
- // "bookmarks go here" text container. |
- [self showOrHideNoItemContainerForNode:model->bookmark_bar_node()]; |
- // If we deleted the only item on the "off the side" menu we no |
- // longer need to show it. |
- [self reconfigureBookmarkBar]; |
+ [self rebuildLayoutWithAnimated:NO]; |
} |
-// TODO(jrg): linear searching is bad. |
-// Need a BookmarkNode-->NSCell mapping. |
-// |
// TODO(jrg): if the bookmark bar is open on launch, we see the |
// buttons all placed, then "scooted over" as the favicons load. If |
// this looks bad I may need to change widthForBookmarkButtonCell to |
@@ -2351,21 +2158,14 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) { |
// favicons will eventually load. |
- (void)nodeFaviconLoaded:(BookmarkModel*)model |
node:(const BookmarkNode*)node { |
- for (BookmarkButton* button in buttons_.get()) { |
- const BookmarkNode* cellnode = [button bookmarkNode]; |
- if (cellnode == node) { |
- BOOL darkTheme = [[[self view] window] hasDarkTheme]; |
- NSImage* theImage = [self faviconForNode:node forADarkTheme:darkTheme]; |
- [[button cell] setBookmarkCellText:[button title] |
- image:theImage]; |
- // Adding an image means we might need more room for the |
- // bookmark. Test for it by growing the button (if needed) |
- // and shifting everything else over. |
- [self checkForBookmarkButtonGrowth:button]; |
- return; |
- } |
- } |
- |
+ auto buttonIt = nodeIdToButtonMap_.find(node->id()); |
+ if (buttonIt != nodeIdToButtonMap_.end()) { |
+ BookmarkButton* button = (*buttonIt).second; |
+ BOOL darkTheme = [[[self view] window] hasDarkTheme]; |
+ NSImage* theImage = [self faviconForNode:node forADarkTheme:darkTheme]; |
+ [[button cell] setBookmarkCellText:[button title] image:theImage]; |
+ } |
+ [self rebuildLayoutWithAnimated:NO]; |
if (folderController_) |
[folderController_ faviconLoadedForNode:node]; |
} |
@@ -2509,8 +2309,7 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) { |
- (void)bookmarkDragDidEnd:(BookmarkButton*)button |
operation:(NSDragOperation)operation { |
- [button setHidden:NO]; |
- [self resetAllButtonPositionsWithAnimation:YES]; |
+ [self rebuildLayoutWithAnimated:YES]; |
} |
@@ -2670,50 +2469,50 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) { |
CGFloat x = 0; |
CGFloat halfHorizontalPadding = 0.5 * bookmarks::kBookmarkHorizontalPadding; |
int destIndex = [self indexForDragToPoint:point]; |
- int numButtons = displayedButtonCount_; |
- |
- CGFloat leftmostX; |
- if (![supervisedBookmarksButton_ isHidden]) { |
- leftmostX = |
- NSMaxX([supervisedBookmarksButton_ frame]) + halfHorizontalPadding; |
- } else if (![managedBookmarksButton_ isHidden]) { |
- leftmostX = NSMaxX([managedBookmarksButton_ frame]) + halfHorizontalPadding; |
- } else if (![appsPageShortcutButton_ isHidden]) { |
- leftmostX = NSMaxX([appsPageShortcutButton_ frame]) + halfHorizontalPadding; |
+ int numButtons = layout_.VisibleButtonCount(); |
+ |
+ CGFloat leadingOffset; |
+ if (layout_.IsSupervisedBookmarksButtonVisible()) { |
+ leadingOffset = |
+ layout_.supervised_bookmarks_button_offset + halfHorizontalPadding; |
+ } else if (layout_.IsManagedBookmarksButtonVisible()) { |
+ leadingOffset = |
+ layout_.managed_bookmarks_button_offset + halfHorizontalPadding; |
+ } else if (layout_.IsAppsButtonVisible()) { |
+ leadingOffset = layout_.apps_button_offset + halfHorizontalPadding; |
} else { |
- leftmostX = bookmarks::kBookmarkLeftMargin - halfHorizontalPadding; |
+ leadingOffset = bookmarks::kBookmarkLeftMargin - halfHorizontalPadding; |
} |
// If it's a drop strictly between existing buttons ... |
if (destIndex == 0) { |
- x = leftmostX; |
+ x = leadingOffset; |
} else if (destIndex > 0 && destIndex < numButtons) { |
// ... put the indicator right between the buttons. |
- BookmarkButton* button = |
- [buttons_ objectAtIndex:static_cast<NSUInteger>(destIndex-1)]; |
- DCHECK(button); |
- NSRect buttonFrame = [button frame]; |
- x = NSMaxX(buttonFrame) + halfHorizontalPadding; |
- |
+ int64_t nodeId = |
+ bookmarkModel_->bookmark_bar_node()->GetChild(destIndex)->id(); |
+ x = layout_.button_offsets[nodeId] - halfHorizontalPadding; |
// If it's a drop at the end (past the last button, if there are any) ... |
} else if (destIndex == numButtons) { |
// and if it's past the last button ... |
if (numButtons > 0) { |
- // ... find the last button, and put the indicator to its right. |
- BookmarkButton* button = |
- [buttons_ objectAtIndex:static_cast<NSUInteger>(destIndex - 1)]; |
- DCHECK(button); |
- x = NSMaxX([button frame]) + halfHorizontalPadding; |
- |
+ // ... find the last button, and put the indicator after it. |
+ const BookmarkNode* node = |
+ bookmarkModel_->bookmark_bar_node()->GetChild(destIndex - 1); |
+ int64_t nodeId = node->id(); |
+ x = layout_.button_offsets[nodeId] + [self widthOfButtonForNode:node] + |
+ halfHorizontalPadding; |
// Otherwise, put it right at the beginning. |
} else { |
- x = leftmostX; |
+ x = leadingOffset; |
} |
} else { |
NOTREACHED(); |
} |
- return x; |
+ return cocoa_l10n_util::ShouldDoExperimentalRTLLayout() |
+ ? NSWidth([buttonView_ frame]) - x |
+ : x; |
} |
- (void)childFolderWillShow:(id<BookmarkButtonControllerProtocol>)child { |
@@ -2737,7 +2536,6 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) { |
// Add a new folder controller as triggered by the given folder button. |
- (void)addNewFolderControllerWithParentButton:(BookmarkButton*)parentButton { |
- |
// If doing a close/open, make sure the fullscreen chrome doesn't |
// have a chance to begin animating away in the middle of things. |
BrowserWindowController* browserController = |
@@ -2773,34 +2571,6 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) { |
browser_->profile()); |
} |
-- (void)addButtonForNode:(const BookmarkNode*)node |
- atIndex:(NSInteger)buttonIndex { |
- int newOffset = |
- bookmarks::kBookmarkLeftMargin - bookmarks::kBookmarkHorizontalPadding; |
- if (buttonIndex == -1) |
- buttonIndex = [buttons_ count]; // New button goes at the end. |
- if (buttonIndex <= (NSInteger)[buttons_ count]) { |
- if (buttonIndex) { |
- BookmarkButton* targetButton = [buttons_ objectAtIndex:buttonIndex - 1]; |
- NSRect targetFrame = [targetButton frame]; |
- newOffset = targetFrame.origin.x + NSWidth(targetFrame) + |
- bookmarks::kBookmarkHorizontalPadding; |
- } |
- BookmarkButton* newButton = [self buttonForNode:node xOffset:&newOffset]; |
- ++displayedButtonCount_; |
- [buttons_ insertObject:newButton atIndex:buttonIndex]; |
- [buttonView_ addSubview:newButton]; |
- [self resetAllButtonPositionsWithAnimation:NO]; |
- // See if any buttons need to be pushed off to or brought in from the side. |
- [self reconfigureBookmarkBar]; |
- } else { |
- // A button from somewhere else (not the bar) is being moved to the |
- // off-the-side so insure it gets redrawn if its showing. |
- [self reconfigureBookmarkBar]; |
- [folderController_ reconfigureMenu]; |
- } |
-} |
- |
// TODO(mrossetti): Duplicate code with BookmarkBarFolderController. |
// http://crbug.com/35966 |
- (BOOL)addURLs:(NSArray*)urls withTitles:(NSArray*)titles at:(NSPoint)point { |
@@ -2848,49 +2618,22 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) { |
} |
- (void)moveButtonFromIndex:(NSInteger)fromIndex toIndex:(NSInteger)toIndex { |
- if (fromIndex != toIndex) { |
- NSInteger buttonCount = (NSInteger)[buttons_ count]; |
- if (toIndex == -1) |
- toIndex = buttonCount; |
- // See if we have a simple move within the bar, which will be the case if |
- // both button indexes are in the visible space. |
- if (fromIndex < buttonCount && toIndex < buttonCount) { |
- BookmarkButton* movedButton = [buttons_ objectAtIndex:fromIndex]; |
- [buttons_ removeObjectAtIndex:fromIndex]; |
- [buttons_ insertObject:movedButton atIndex:toIndex]; |
- [movedButton setHidden:NO]; |
- [self resetAllButtonPositionsWithAnimation:NO]; |
- } else if (fromIndex < buttonCount) { |
- // A button is being removed from the bar and added to off-the-side. |
- // By now the node has already been inserted into the model so the |
- // button to be added is represented by |toIndex|. Things get |
- // complicated because the off-the-side is showing and must be redrawn |
- // while possibly re-laying out the bookmark bar. |
- [self removeButton:fromIndex animate:NO]; |
- [self reconfigureBookmarkBar]; |
- [folderController_ reconfigureMenu]; |
- } else if (toIndex < buttonCount) { |
- // A button is being added to the bar and removed from off-the-side. |
- // By now the node has already been inserted into the model so the |
- // button to be added is represented by |toIndex|. |
- const BookmarkNode* node = bookmarkModel_->bookmark_bar_node(); |
- const BookmarkNode* movedNode = node->GetChild(toIndex); |
- DCHECK(movedNode); |
- [self addButtonForNode:movedNode atIndex:toIndex]; |
- [self reconfigureBookmarkBar]; |
- } else { |
- // A button is being moved within the off-the-side. |
- fromIndex -= buttonCount; |
- toIndex -= buttonCount; |
- [folderController_ moveButtonFromIndex:fromIndex toIndex:toIndex]; |
- } |
+ int buttonCount = layout_.VisibleButtonCount(); |
+ BOOL isMoveWithinOffTheSideMenu = |
+ (toIndex >= buttonCount) && (fromIndex >= buttonCount); |
+ if (isMoveWithinOffTheSideMenu) { |
+ fromIndex -= buttonCount; |
+ toIndex -= buttonCount; |
+ [folderController_ moveButtonFromIndex:fromIndex toIndex:toIndex]; |
+ } else { |
+ [self rebuildLayoutWithAnimated:NO]; |
} |
} |
- (void)removeButton:(NSInteger)buttonIndex animate:(BOOL)animate { |
- if (buttonIndex < (NSInteger)[buttons_ count]) { |
+ if ((size_t)buttonIndex < layout_.VisibleButtonCount()) { |
// The button being removed is showing in the bar. |
- BookmarkButton* oldButton = [buttons_ objectAtIndex:buttonIndex]; |
+ BookmarkButton* oldButton = buttons_[buttonIndex]; |
if (oldButton == [folderController_ parentButton]) { |
// If we are deleting a button whose folder is currently open, close it! |
[self closeAllBookmarkFolders]; |
@@ -2901,17 +2644,12 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) { |
NSShowAnimationEffect(NSAnimationEffectDisappearingItemDefault, poofPoint, |
NSZeroSize, nil, nil, nil); |
} |
- [oldButton setDelegate:nil]; |
- [oldButton removeFromSuperview]; |
- [buttons_ removeObjectAtIndex:buttonIndex]; |
- --displayedButtonCount_; |
- [self resetAllButtonPositionsWithAnimation:YES]; |
- [self reconfigureBookmarkBar]; |
+ [self rebuildLayoutWithAnimated:YES]; |
} else if (folderController_ && |
[folderController_ parentButton] == offTheSideButton_) { |
// The button being removed is in the OTS (off-the-side) and the OTS |
// menu is showing so we need to remove the button. |
- NSInteger index = buttonIndex - displayedButtonCount_; |
+ NSInteger index = buttonIndex - layout_.VisibleButtonCount(); |
[folderController_ removeButton:index animate:animate]; |
} |
} |
@@ -2925,4 +2663,9 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) { |
return [folderController_ controllerForNode:node]; |
} |
+// For testing. |
+- (const BookmarkBarLayout&)currentLayout { |
+ return layout_; |
+} |
+ |
@end |