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

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

Issue 2853123002: Revert of [Mac] Refactor bookmark bar controller (Closed)
Patch Set: Created 3 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_controller.h" 5 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_controller.h"
6 6
7 #include <stddef.h> 7 #include <stddef.h>
8 8
9 #import "base/mac/bundle_locations.h" 9 #import "base/mac/bundle_locations.h"
10 #import "base/mac/foundation_util.h" 10 #import "base/mac/foundation_util.h"
(...skipping 25 matching lines...) Expand all
36 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_view_cocoa.h" 36 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_view_cocoa.h"
37 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_button.h" 37 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_button.h"
38 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_button_cell.h" 38 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_button_cell.h"
39 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_context_menu_cocoa_controlle r.h" 39 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_context_menu_cocoa_controlle r.h"
40 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_editor_controller.h" 40 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_editor_controller.h"
41 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_folder_target.h" 41 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_folder_target.h"
42 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_menu_cocoa_controller.h" 42 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_menu_cocoa_controller.h"
43 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_model_observer_for_cocoa.h" 43 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_model_observer_for_cocoa.h"
44 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_name_folder_controller.h" 44 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_name_folder_controller.h"
45 #import "chrome/browser/ui/cocoa/browser_window_controller.h" 45 #import "chrome/browser/ui/cocoa/browser_window_controller.h"
46 #import "chrome/browser/ui/cocoa/l10n_util.h"
47 #import "chrome/browser/ui/cocoa/menu_button.h" 46 #import "chrome/browser/ui/cocoa/menu_button.h"
48 #import "chrome/browser/ui/cocoa/themed_window.h" 47 #import "chrome/browser/ui/cocoa/themed_window.h"
49 #import "chrome/browser/ui/cocoa/toolbar/toolbar_controller.h" 48 #import "chrome/browser/ui/cocoa/toolbar/toolbar_controller.h"
50 #import "chrome/browser/ui/cocoa/view_id_util.h" 49 #import "chrome/browser/ui/cocoa/view_id_util.h"
51 #import "chrome/browser/ui/cocoa/view_resizer.h" 50 #import "chrome/browser/ui/cocoa/view_resizer.h"
52 #include "chrome/browser/ui/tabs/tab_strip_model.h" 51 #include "chrome/browser/ui/tabs/tab_strip_model.h"
53 #include "chrome/common/extensions/extension_constants.h" 52 #include "chrome/common/extensions/extension_constants.h"
54 #include "chrome/common/extensions/extension_metrics.h" 53 #include "chrome/common/extensions/extension_metrics.h"
55 #include "chrome/common/url_constants.h" 54 #include "chrome/common/url_constants.h"
56 #include "chrome/grit/generated_resources.h" 55 #include "chrome/grit/generated_resources.h"
(...skipping 15 matching lines...) Expand all
72 #include "ui/base/l10n/l10n_util_mac.h" 71 #include "ui/base/l10n/l10n_util_mac.h"
73 #include "ui/base/material_design/material_design_controller.h" 72 #include "ui/base/material_design/material_design_controller.h"
74 #include "ui/base/resource/resource_bundle.h" 73 #include "ui/base/resource/resource_bundle.h"
75 #include "ui/gfx/color_palette.h" 74 #include "ui/gfx/color_palette.h"
76 #include "ui/gfx/image/image.h" 75 #include "ui/gfx/image/image.h"
77 #include "ui/gfx/image/image_skia_util_mac.h" 76 #include "ui/gfx/image/image_skia_util_mac.h"
78 #include "ui/gfx/paint_vector_icon.h" 77 #include "ui/gfx/paint_vector_icon.h"
79 #include "ui/resources/grit/ui_resources.h" 78 #include "ui/resources/grit/ui_resources.h"
80 79
81 using base::UserMetricsAction; 80 using base::UserMetricsAction;
82 using bookmarks::BookmarkBarLayout;
83 using bookmarks::BookmarkModel; 81 using bookmarks::BookmarkModel;
84 using bookmarks::BookmarkNode; 82 using bookmarks::BookmarkNode;
85 using bookmarks::BookmarkNodeData; 83 using bookmarks::BookmarkNodeData;
86 using content::OpenURLParams; 84 using content::OpenURLParams;
87 using content::Referrer; 85 using content::Referrer;
88 using content::WebContents; 86 using content::WebContents;
89 87
90 // Bookmark bar state changing and animations 88 // Bookmark bar state changing and animations
91 // 89 //
92 // The bookmark bar has three real states: "showing" (a normal bar attached to 90 // The bookmark bar has three real states: "showing" (a normal bar attached to
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after
139 // Pointers to animation logic: 137 // Pointers to animation logic:
140 // - |-moveToState:withAnimation:| starts animations, deciding which ones we 138 // - |-moveToState:withAnimation:| starts animations, deciding which ones we
141 // know how to handle. 139 // know how to handle.
142 // - |-doBookmarkBarAnimation| has most of the actual logic. 140 // - |-doBookmarkBarAnimation| has most of the actual logic.
143 // - |-getDesiredToolbarHeightCompression| and |-toolbarDividerOpacity| contain 141 // - |-getDesiredToolbarHeightCompression| and |-toolbarDividerOpacity| contain
144 // related logic. 142 // related logic.
145 // - The BWC's |-layoutSubviews| needs to know how to position things. 143 // - The BWC's |-layoutSubviews| needs to know how to position things.
146 // - The BWC should implement |-bookmarkBar:didChangeFromState:toState:| and 144 // - The BWC should implement |-bookmarkBar:didChangeFromState:toState:| and
147 // |-bookmarkBar:willAnimateFromState:toState:| in order to inform the 145 // |-bookmarkBar:willAnimateFromState:toState:| in order to inform the
148 // toolbar of required changes. 146 // toolbar of required changes.
149 //
150 // Layout:
151 //
152 // Several events (initial load, changes to the bookmark model etc.) can
153 // require the bar layout to change. In most cases, this is accomplished
154 // by building a BookmarkBarLayout from the current state of the view,
155 // the bookmark model, and the managed bookmark service. If the calculated
156 // layout differs from the previous one, it's applied to the view
157 // via |applyLayout:animated:|. This is a cheap way to "coalesce" multiple
158 // potentially layout-changing events, since in practice, these events come
159 // in bursts and don't require a change.
160 //
161 // Temporary changes in layout during dragging are an exception to this,
162 // since the layout temporarily adjusts to the drag (for example, adding
163 // a placeholder space for a mid-drag button or removing the space previously
164 // taken up by a button which is being dragged off the bar). In this case,
165 // the original stored layout is maintained as the source of truth, and
166 // elements are laid out from a combination of the stored layout and the
167 // drag state. See |setDropInsertionPos:| for details.
168 147
169 namespace { 148 namespace {
170 149
171 // Duration of the bookmark bar animations. 150 // Duration of the bookmark bar animations.
172 const NSTimeInterval kBookmarkBarAnimationDuration = 0.12; 151 const NSTimeInterval kBookmarkBarAnimationDuration = 0.12;
173 const NSTimeInterval kDragAndDropAnimationDuration = 0.25; 152 const NSTimeInterval kDragAndDropAnimationDuration = 0.25;
174 153
175 const int kMaxReusePoolSize = 10;
176
177 // Min width for no item text field and import bookmarks button.
178 const CGFloat kNoItemElementMinWidth = 30;
179
180 void RecordAppLaunch(Profile* profile, GURL url) { 154 void RecordAppLaunch(Profile* profile, GURL url) {
181 const extensions::Extension* extension = 155 const extensions::Extension* extension =
182 extensions::ExtensionRegistry::Get(profile)-> 156 extensions::ExtensionRegistry::Get(profile)->
183 enabled_extensions().GetAppByURL(url); 157 enabled_extensions().GetAppByURL(url);
184 if (!extension) 158 if (!extension)
185 return; 159 return;
186 160
187 extensions::RecordAppLaunchType(extension_misc::APP_LAUNCH_BOOKMARK_BAR, 161 extensions::RecordAppLaunchType(extension_misc::APP_LAUNCH_BOOKMARK_BAR,
188 extension->GetType()); 162 extension->GetType());
189 } 163 }
190 164
191 const CGFloat kBookmarkButtonHeightMinusPadding =
192 bookmarks::kBookmarkButtonHeight - bookmarks::kBookmarkVerticalPadding * 2;
193
194 } // namespace 165 } // namespace
195 166
196 namespace bookmarks { 167 @interface BookmarkBarController ()
197 168
198 BookmarkBarLayout::BookmarkBarLayout() 169 // Updates the sizes and positions of the subviews.
199 : visible_elements(0), 170 - (void)layoutSubviews;
200 apps_button_offset(0),
201 managed_bookmarks_button_offset(0),
202 supervised_bookmarks_button_offset(0),
203 off_the_side_button_offset(0),
204 other_bookmarks_button_offset(0),
205 no_item_textfield_offset(0),
206 no_item_textfield_width(0),
207 import_bookmarks_button_offset(0),
208 import_bookmarks_button_width(0),
209 max_x(0){};
210 BookmarkBarLayout::~BookmarkBarLayout(){};
211 BookmarkBarLayout::BookmarkBarLayout(BookmarkBarLayout&& other) = default;
212 BookmarkBarLayout& BookmarkBarLayout::operator=(BookmarkBarLayout&& other) =
213 default;
214 171
215 bool operator==(const BookmarkBarLayout& lhs, const BookmarkBarLayout& rhs) { 172 // Moves to the given next state (from the current state), possibly animating.
216 return std::tie(lhs.visible_elements, lhs.apps_button_offset, 173 // If |animate| is NO, it will stop any running animation and jump to the given
217 lhs.managed_bookmarks_button_offset, 174 // state. If YES, it may either (depending on implementation) jump to the end of
218 lhs.supervised_bookmarks_button_offset, 175 // the current animation and begin the next one, or stop the current animation
219 lhs.off_the_side_button_offset, 176 // mid-flight and animate to the next state.
220 lhs.other_bookmarks_button_offset, 177 - (void)moveToState:(BookmarkBar::State)nextState
221 lhs.no_item_textfield_offset, lhs.no_item_textfield_width, 178 withAnimation:(BOOL)animate;
222 lhs.import_bookmarks_button_offset,
223 lhs.import_bookmarks_button_width, lhs.button_offsets,
224 lhs.max_x) ==
225 std::tie(
226 rhs.visible_elements, rhs.apps_button_offset,
227 rhs.managed_bookmarks_button_offset,
228 rhs.supervised_bookmarks_button_offset,
229 rhs.off_the_side_button_offset, rhs.other_bookmarks_button_offset,
230 rhs.no_item_textfield_offset, rhs.no_item_textfield_width,
231 rhs.import_bookmarks_button_offset,
232 rhs.import_bookmarks_button_width, rhs.button_offsets, rhs.max_x);
233 }
234 179
235 bool operator!=(const BookmarkBarLayout& lhs, const BookmarkBarLayout& rhs) { 180 // Create buttons for all items in the given bookmark node tree.
236 return !(lhs == rhs); 181 // Modifies self->buttons_. Do not add more buttons than will fit on the view.
237 } 182 - (void)addNodesToButtonList:(const BookmarkNode*)node;
238 183
239 } // namespace bookmarks 184 // Create an autoreleased button appropriate for insertion into the bookmark
185 // bar. Update |xOffset| with the offset appropriate for the subsequent button.
186 - (BookmarkButton*)buttonForNode:(const BookmarkNode*)node
187 xOffset:(int*)xOffset;
240 188
241 @implementation BookmarkBarController { 189 // Find a parent whose button is visible on the bookmark bar.
242 BookmarkBarLayout layout_; 190 - (BookmarkButton*)bookmarkButtonToPulseForNode:(const BookmarkNode*)node;
243 CGFloat originalNoItemTextFieldWidth_;
244 CGFloat originalImportBookmarksButtonWidth_;
245 CGFloat originalNoItemInterelementPadding_;
246 BOOL didCreateExtraButtons_;
247 191
248 // Maps bookmark node IDs to instantiated buttons for ease of lookup. 192 // Puts stuff into the final state without animating, stopping a running
249 std::unordered_map<int64_t, base::scoped_nsobject<BookmarkButton>> 193 // animation if necessary.
250 nodeIdToButtonMap_; 194 - (void)finalizeState;
251 195
252 // A place to stash bookmark buttons that have been removed from the bar 196 // Stops any current animation in its tracks (midway).
253 // so that they can be reused instead of creating new ones. 197 - (void)stopCurrentAnimation;
254 base::scoped_nsobject<NSMutableArray> unusedButtonPool_; 198
255 } 199 // Show/hide the bookmark bar.
200 // if |animate| is YES, the changes are made using the animator; otherwise they
201 // are made immediately.
202 - (void)showBookmarkBarWithAnimation:(BOOL)animate;
203
204 // Handles animating the resize of the content view. Returns YES if it handled
205 // the animation, NO if not (and hence it should be done instantly).
206 - (BOOL)doBookmarkBarAnimation;
207
208 // |point| is in the base coordinate system of the destination window;
209 // it comes from an id<NSDraggingInfo>. |copy| is YES if a copy is to be
210 // made and inserted into the new location while leaving the bookmark in
211 // the old location, otherwise move the bookmark by removing from its old
212 // location and inserting into the new location.
213 - (BOOL)dragBookmark:(const BookmarkNode*)sourceNode
214 to:(NSPoint)point
215 copy:(BOOL)copy;
216
217 // Returns the index in the model for a drag to the location given by
218 // |point|. This is determined by finding the first button before the center
219 // of which |point| falls, scanning left to right. Note that, currently, only
220 // the x-coordinate of |point| is considered. Though not currently implemented,
221 // we may check for errors, in which case this would return negative value;
222 // callers should check for this.
223 - (int)indexForDragToPoint:(NSPoint)point;
224
225 // Add or remove buttons to/from the bar until it is filled but not overflowed.
226 - (void)redistributeButtonsOnBarAsNeeded;
227
228 // Determine the nature of the bookmark bar contents based on the number of
229 // buttons showing. If too many then show the off-the-side list, if none
230 // then show the no items label.
231 - (void)reconfigureBookmarkBar;
232
233 - (int)preferredHeight;
234 - (void)addButtonsToView;
235 - (BOOL)setManagedBookmarksButtonVisibility;
236 - (BOOL)setSupervisedBookmarksButtonVisibility;
237 - (BOOL)setOtherBookmarksButtonVisibility;
238 - (BOOL)setAppsPageShortcutButtonVisibility;
239 - (BookmarkButton*)createCustomBookmarkButtonForCell:(NSCell*)cell;
240 - (void)createManagedBookmarksButton;
241 - (void)createSupervisedBookmarksButton;
242 - (void)createOtherBookmarksButton;
243 - (void)createAppsPageShortcutButton;
244 - (void)openAppsPage:(id)sender;
245 - (void)centerNoItemsLabel;
246 - (void)positionRightSideButtons;
247 - (void)watchForExitEvent:(BOOL)watch;
248 - (void)resetAllButtonPositionsWithAnimation:(BOOL)animate;
249
250 @end
251
252 @implementation BookmarkBarController
256 253
257 @synthesize currentState = currentState_; 254 @synthesize currentState = currentState_;
258 @synthesize lastState = lastState_; 255 @synthesize lastState = lastState_;
259 @synthesize isAnimationRunning = isAnimationRunning_; 256 @synthesize isAnimationRunning = isAnimationRunning_;
260 @synthesize delegate = delegate_; 257 @synthesize delegate = delegate_;
261 @synthesize stateAnimationsEnabled = stateAnimationsEnabled_; 258 @synthesize stateAnimationsEnabled = stateAnimationsEnabled_;
262 @synthesize innerContentAnimationsEnabled = innerContentAnimationsEnabled_; 259 @synthesize innerContentAnimationsEnabled = innerContentAnimationsEnabled_;
263 260
264 - (id)initWithBrowser:(Browser*)browser 261 - (id)initWithBrowser:(Browser*)browser
265 initialWidth:(CGFloat)initialWidth 262 initialWidth:(CGFloat)initialWidth
266 delegate:(id<BookmarkBarControllerDelegate>)delegate { 263 delegate:(id<BookmarkBarControllerDelegate>)delegate {
267 if ((self = [super initWithNibName:nil bundle:nil])) { 264 if ((self = [super initWithNibName:@"BookmarkBar"
265 bundle:base::mac::FrameworkBundle()])) {
268 currentState_ = BookmarkBar::HIDDEN; 266 currentState_ = BookmarkBar::HIDDEN;
269 lastState_ = BookmarkBar::HIDDEN; 267 lastState_ = BookmarkBar::HIDDEN;
270 268
271 browser_ = browser; 269 browser_ = browser;
272 initialWidth_ = initialWidth; 270 initialWidth_ = initialWidth;
273 bookmarkModel_ = 271 bookmarkModel_ =
274 BookmarkModelFactory::GetForBrowserContext(browser_->profile()); 272 BookmarkModelFactory::GetForBrowserContext(browser_->profile());
275 managedBookmarkService_ = 273 managedBookmarkService_ =
276 ManagedBookmarkServiceFactory::GetForProfile(browser_->profile()); 274 ManagedBookmarkServiceFactory::GetForProfile(browser_->profile());
277 buttons_.reset([[NSMutableArray alloc] init]); 275 buttons_.reset([[NSMutableArray alloc] init]);
278 unusedButtonPool_.reset([[NSMutableArray alloc] init]);
279 delegate_ = delegate; 276 delegate_ = delegate;
280 folderTarget_.reset( 277 folderTarget_.reset(
281 [[BookmarkFolderTarget alloc] initWithController:self 278 [[BookmarkFolderTarget alloc] initWithController:self
282 profile:browser_->profile()]); 279 profile:browser_->profile()]);
283 didCreateExtraButtons_ = NO;
284 280
285 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); 281 ResourceBundle& rb = ResourceBundle::GetSharedInstance();
286 folderImage_.reset( 282 folderImage_.reset(
287 rb.GetNativeImageNamed(IDR_BOOKMARK_BAR_FOLDER).CopyNSImage()); 283 rb.GetNativeImageNamed(IDR_BOOKMARK_BAR_FOLDER).CopyNSImage());
288 folderImageWhite_.reset( 284 folderImageWhite_.reset(
289 rb.GetNativeImageNamed(IDR_BOOKMARK_BAR_FOLDER_WHITE).CopyNSImage()); 285 rb.GetNativeImageNamed(IDR_BOOKMARK_BAR_FOLDER_WHITE).CopyNSImage());
290 286
291 const int kIconSize = 16; 287 const int kIconSize = 16;
292 defaultImage_.reset([NSImageFromImageSkia(gfx::CreateVectorIcon( 288 defaultImage_.reset([NSImageFromImageSkia(gfx::CreateVectorIcon(
293 omnibox::kHttpIcon, kIconSize, gfx::kChromeIconGrey)) retain]); 289 omnibox::kHttpIcon, kIconSize, gfx::kChromeIconGrey)) retain]);
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
330 initWithFrame:NSMakeRect(0, 0, initialWidth_, 0)] 326 initWithFrame:NSMakeRect(0, 0, initialWidth_, 0)]
331 autorelease]]; 327 autorelease]];
332 [[self view] setHidden:YES]; 328 [[self view] setHidden:YES];
333 [[self view] setAutoresizingMask:NSViewWidthSizable | NSViewMinYMargin]; 329 [[self view] setAutoresizingMask:NSViewWidthSizable | NSViewMinYMargin];
334 [[self controlledView] setController:self]; 330 [[self controlledView] setController:self];
335 [[self controlledView] setDelegate:self]; 331 [[self controlledView] setDelegate:self];
336 332
337 buttonView_.reset([[BookmarkBarView alloc] 333 buttonView_.reset([[BookmarkBarView alloc]
338 initWithController:self 334 initWithController:self
339 frame:NSMakeRect(0, -2, 584, 144)]); 335 frame:NSMakeRect(0, -2, 584, 144)]);
340 [buttonView_ setAutoresizingMask:NSViewWidthSizable | NSViewMaxXMargin]; 336 [buttonView_ setAutoresizingMask:NSViewWidthSizable | NSViewMinYMargin |
337 NSViewMaxXMargin];
341 [[buttonView_ importBookmarksButton] setTarget:self]; 338 [[buttonView_ importBookmarksButton] setTarget:self];
342 [[buttonView_ importBookmarksButton] setAction:@selector(importBookmarks:)]; 339 [[buttonView_ importBookmarksButton] setAction:@selector(importBookmarks:)];
343 340
341 [self createOffTheSideButton];
342 [buttonView_ addSubview:offTheSideButton_];
343
344 [self.view addSubview:buttonView_]; 344 [self.view addSubview:buttonView_];
345 345 // viewDidLoad became part of the API in 10.10
346 // viewDidLoad became part of the API in 10.10.
347 if (!base::mac::IsAtLeastOS10_10()) 346 if (!base::mac::IsAtLeastOS10_10())
348 [self viewDidLoad]; 347 [self viewDidLoad];
349 } 348 }
350 349
351 - (BookmarkButton*)findAncestorButtonOnBarForNode:(const BookmarkNode*)node { 350 - (BookmarkButton*)bookmarkButtonToPulseForNode:(const BookmarkNode*)node {
352 // Find the closest parent that is visible on the bar. 351 // Find the closest parent that is visible on the bar.
353 while (node) { 352 while (node) {
354 // Check if we've reached one of the special buttons. Otherwise, if the next 353 // Check if we've reached one of the special buttons. Otherwise, if the next
355 // parent is the boomark bar, find the corresponding button. 354 // parent is the boomark bar, find the corresponding button.
356 if ([managedBookmarksButton_ bookmarkNode] == node) 355 if ([managedBookmarksButton_ bookmarkNode] == node)
357 return managedBookmarksButton_; 356 return managedBookmarksButton_;
358 357
359 if ([supervisedBookmarksButton_ bookmarkNode] == node) 358 if ([supervisedBookmarksButton_ bookmarkNode] == node)
360 return supervisedBookmarksButton_; 359 return supervisedBookmarksButton_;
361 360
(...skipping 14 matching lines...) Expand all
376 375
377 node = node->parent(); 376 node = node->parent();
378 } 377 }
379 NOTREACHED(); 378 NOTREACHED();
380 return nil; 379 return nil;
381 } 380 }
382 381
383 - (void)startPulsingBookmarkNode:(const BookmarkNode*)node { 382 - (void)startPulsingBookmarkNode:(const BookmarkNode*)node {
384 [self stopPulsingBookmarkNode]; 383 [self stopPulsingBookmarkNode];
385 384
386 pulsingButton_.reset([self findAncestorButtonOnBarForNode:node], 385 pulsingButton_.reset([self bookmarkButtonToPulseForNode:node],
387 base::scoped_policy::RETAIN); 386 base::scoped_policy::RETAIN);
388 if (!pulsingButton_) 387 if (!pulsingButton_)
389 return; 388 return;
390 389
391 [pulsingButton_ setPulseIsStuckOn:YES]; 390 [pulsingButton_ setPulseIsStuckOn:YES];
392 pulsingBookmarkObserver_.reset( 391 pulsingBookmarkObserver_.reset(
393 new BookmarkModelObserverForCocoa(bookmarkModel_, ^() { 392 new BookmarkModelObserverForCocoa(bookmarkModel_, ^() {
394 // Stop pulsing if anything happened to the node. 393 // Stop pulsing if anything happened to the node.
395 [self stopPulsingBookmarkNode]; 394 [self stopPulsingBookmarkNode];
396 })); 395 }));
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
432 // Remove our view from its superview so it doesn't attempt to reference 431 // Remove our view from its superview so it doesn't attempt to reference
433 // it when the controller is gone. 432 // it when the controller is gone.
434 //TODO(dmaclach): Remove -- http://crbug.com/25845 433 //TODO(dmaclach): Remove -- http://crbug.com/25845
435 [view removeFromSuperview]; 434 [view removeFromSuperview];
436 435
437 // Be sure there is no dangling pointer. 436 // Be sure there is no dangling pointer.
438 if ([view respondsToSelector:@selector(setController:)]) 437 if ([view respondsToSelector:@selector(setController:)])
439 [view performSelector:@selector(setController:) withObject:nil]; 438 [view performSelector:@selector(setController:) withObject:nil];
440 439
441 // For safety, make sure the buttons can no longer call us. 440 // For safety, make sure the buttons can no longer call us.
442 base::scoped_nsobject<NSMutableArray> buttons([buttons_ mutableCopy]); 441 for (BookmarkButton* button in buttons_.get()) {
443 [buttons addObjectsFromArray:unusedButtonPool_];
444 if (didCreateExtraButtons_) {
445 [buttons addObjectsFromArray:@[
446 appsPageShortcutButton_, managedBookmarksButton_,
447 supervisedBookmarksButton_, otherBookmarksButton_, offTheSideButton_
448 ]];
449 }
450
451 for (BookmarkButton* button in buttons.get()) {
452 [button setDelegate:nil]; 442 [button setDelegate:nil];
453 [button setTarget:nil]; 443 [button setTarget:nil];
454 [button setAction:nil]; 444 [button setAction:nil];
455 } 445 }
446
456 bridge_.reset(NULL); 447 bridge_.reset(NULL);
457 [[NSNotificationCenter defaultCenter] removeObserver:self]; 448 [[NSNotificationCenter defaultCenter] removeObserver:self];
458 [self watchForExitEvent:NO]; 449 [self watchForExitEvent:NO];
459 browser_ = nullptr; 450 browser_ = nullptr;
460 } 451 }
461 452
462 - (void)viewDidLoad { 453 - (void)viewDidLoad {
463 // We are enabled by default. 454 // We are enabled by default.
464 barIsEnabled_ = YES; 455 barIsEnabled_ = YES;
465 456
466 // Remember the original sizes of the 'no items' and 'import bookmarks' 457 // Remember the original sizes of the 'no items' and 'import bookmarks'
467 // fields to aid in resizing when the window frame changes. 458 // fields to aid in resizing when the window frame changes.
468 NSRect noItemTextFieldFrame = [[buttonView_ noItemTextField] frame]; 459 originalNoItemsRect_ = [[buttonView_ noItemTextfield] frame];
469 NSRect noItemButtonFrame = [[buttonView_ importBookmarksButton] frame]; 460 originalImportBookmarksRect_ = [[buttonView_ importBookmarksButton] frame];
470 originalNoItemTextFieldWidth_ = NSWidth(noItemTextFieldFrame);
471 originalImportBookmarksButtonWidth_ = NSWidth(noItemButtonFrame);
472 originalNoItemInterelementPadding_ =
473 NSMinX(noItemButtonFrame) - NSMaxX(noItemTextFieldFrame);
474 461
462 // Bookmark buttons start farther from the bookmark bar's left edge so
463 // adjust the positions of the noItems and importBookmarks textfields.
464 const CGFloat kBookmarksTextfieldOffsetX = 14;
465 originalNoItemsRect_.origin.x += kBookmarksTextfieldOffsetX;
466 [[buttonView_ noItemTextfield] setFrame:originalNoItemsRect_];
467
468 originalImportBookmarksRect_.origin.x += kBookmarksTextfieldOffsetX;
469 [[buttonView_ importBookmarksButton] setFrame:originalImportBookmarksRect_];
470
471 // When resized we may need to add new buttons, or remove them (if
472 // no longer visible), or add/remove the "off the side" menu.
475 [[self view] setPostsFrameChangedNotifications:YES]; 473 [[self view] setPostsFrameChangedNotifications:YES];
476 [[NSNotificationCenter defaultCenter] 474 [[NSNotificationCenter defaultCenter]
477 addObserver:self 475 addObserver:self
478 selector:@selector(frameDidChange) 476 selector:@selector(frameDidChange)
479 name:NSViewFrameDidChangeNotification 477 name:NSViewFrameDidChangeNotification
480 object:[self view]]; 478 object:[self view]];
481 479
482 // Watch for things going to or from fullscreen. 480 // Watch for things going to or from fullscreen.
483 [[NSNotificationCenter defaultCenter] 481 [[NSNotificationCenter defaultCenter]
484 addObserver:self 482 addObserver:self
485 selector:@selector(willEnterOrLeaveFullscreen:) 483 selector:@selector(willEnterOrLeaveFullscreen:)
486 name:NSWindowWillEnterFullScreenNotification 484 name:NSWindowWillEnterFullScreenNotification
487 object:nil]; 485 object:nil];
488 [[NSNotificationCenter defaultCenter] 486 [[NSNotificationCenter defaultCenter]
489 addObserver:self 487 addObserver:self
490 selector:@selector(willEnterOrLeaveFullscreen:) 488 selector:@selector(willEnterOrLeaveFullscreen:)
491 name:NSWindowWillExitFullScreenNotification 489 name:NSWindowWillExitFullScreenNotification
492 object:nil]; 490 object:nil];
493 [self layoutSubviews];
494 491
495 // Don't pass ourself along (as 'self') until our init is completely 492 // Don't pass ourself along (as 'self') until our init is completely
496 // done. Thus, this call is (almost) last. 493 // done. Thus, this call is (almost) last.
497 bridge_.reset(new BookmarkBarBridge(browser_->profile(), self, 494 bridge_.reset(new BookmarkBarBridge(browser_->profile(), self,
498 bookmarkModel_)); 495 bookmarkModel_));
499 } 496 }
500 497
501 // Called by our main view (a BookmarkBarView) when it gets moved to a 498 // Called by our main view (a BookmarkBarView) when it gets moved to a
502 // window. We perform operations which need to know the relevant 499 // window. We perform operations which need to know the relevant
503 // window (e.g. watch for a window close) so they can't be performed 500 // window (e.g. watch for a window close) so they can't be performed
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
549 - (void)parentWindowDidResignMain:(NSNotification*)notification { 546 - (void)parentWindowDidResignMain:(NSNotification*)notification {
550 [self closeFolderAndStopTrackingMenus]; 547 [self closeFolderAndStopTrackingMenus];
551 } 548 }
552 549
553 - (void)layoutToFrame:(NSRect)frame { 550 - (void)layoutToFrame:(NSRect)frame {
554 // The view should be pinned to the top of the window with a flexible width. 551 // The view should be pinned to the top of the window with a flexible width.
555 DCHECK_EQ(NSViewWidthSizable | NSViewMinYMargin, 552 DCHECK_EQ(NSViewWidthSizable | NSViewMinYMargin,
556 [[self view] autoresizingMask]); 553 [[self view] autoresizingMask]);
557 [[self view] setFrame:frame]; 554 [[self view] setFrame:frame];
558 [self layoutSubviews]; 555 [self layoutSubviews];
559 [self frameDidChange];
560 } 556 }
561 557
562 // Change the layout of the bookmark bar's subviews in response to a visibility 558 // Change the layout of the bookmark bar's subviews in response to a visibility
563 // change (e.g., show or hide the bar) or style change (attached or floating). 559 // change (e.g., show or hide the bar) or style change (attached or floating).
564 - (void)layoutSubviews { 560 - (void)layoutSubviews {
565 NSRect frame = [[self view] frame]; 561 NSRect frame = [[self view] frame];
566 NSRect buttonViewFrame = NSMakeRect(0, 0, NSWidth(frame), NSHeight(frame)); 562 NSRect buttonViewFrame = NSMakeRect(0, 0, NSWidth(frame), NSHeight(frame));
567 563
568 // Add padding to the detached bookmark bar. 564 // Add padding to the detached bookmark bar.
569 // The state of our morph (if any); 1 is total bubble, 0 is the regular bar. 565 // The state of our morph (if any); 1 is total bubble, 0 is the regular bar.
570 CGFloat morph = [self detachedMorphProgress]; 566 CGFloat morph = [self detachedMorphProgress];
571 CGFloat padding = 0; 567 CGFloat padding = 0;
572 padding = bookmarks::kNTPBookmarkBarPadding; 568 padding = bookmarks::kNTPBookmarkBarPadding;
573 buttonViewFrame = 569 buttonViewFrame =
574 NSInsetRect(buttonViewFrame, morph * padding, morph * padding); 570 NSInsetRect(buttonViewFrame, morph * padding, morph * padding);
571
575 [buttonView_ setFrame:buttonViewFrame]; 572 [buttonView_ setFrame:buttonViewFrame];
576 573
577 // Update bookmark button backgrounds. 574 // Update bookmark button backgrounds.
578 if ([self isAnimationRunning]) { 575 if ([self isAnimationRunning]) {
579 for (NSButton* button in buttons_.get()) 576 for (NSButton* button in buttons_.get())
580 [button setNeedsDisplay:YES]; 577 [button setNeedsDisplay:YES];
581 // Update the apps and other buttons explicitly, since they are not in the 578 // Update the apps and other buttons explicitly, since they are not in the
582 // buttons_ array. 579 // buttons_ array.
583 [appsPageShortcutButton_ setNeedsDisplay:YES]; 580 [appsPageShortcutButton_ setNeedsDisplay:YES];
584 [managedBookmarksButton_ setNeedsDisplay:YES]; 581 [managedBookmarksButton_ setNeedsDisplay:YES];
585 [supervisedBookmarksButton_ setNeedsDisplay:YES]; 582 [supervisedBookmarksButton_ setNeedsDisplay:YES];
586 [otherBookmarksButton_ setNeedsDisplay:YES]; 583 [otherBookmarksButton_ setNeedsDisplay:YES];
587 } 584 }
588 } 585 }
589 586
590 // We don't change a preference; we only change visibility. Preference changing 587 // We don't change a preference; we only change visibility. Preference changing
591 // (global state) is handled in |chrome::ToggleBookmarkBarWhenVisible()|. We 588 // (global state) is handled in |chrome::ToggleBookmarkBarWhenVisible()|. We
592 // simply update based on what we're told. 589 // simply update based on what we're told.
593 - (void)updateVisibility { 590 - (void)updateVisibility {
594 [self showBookmarkBarWithAnimation:NO]; 591 [self showBookmarkBarWithAnimation:NO];
595 } 592 }
596 593
594 - (void)updateExtraButtonsVisibility {
595 if (!appsPageShortcutButton_.get() ||
596 !managedBookmarksButton_.get() ||
597 !supervisedBookmarksButton_.get()) {
598 return;
599 }
600 [self setAppsPageShortcutButtonVisibility];
601 [self setManagedBookmarksButtonVisibility];
602 [self setSupervisedBookmarksButtonVisibility];
603 [self resetAllButtonPositionsWithAnimation:NO];
604 [self reconfigureBookmarkBar];
605 }
606
597 - (void)updateHiddenState { 607 - (void)updateHiddenState {
598 BOOL oldHidden = [[self view] isHidden]; 608 BOOL oldHidden = [[self view] isHidden];
599 BOOL newHidden = ![self isVisible]; 609 BOOL newHidden = ![self isVisible];
600 if (oldHidden != newHidden) 610 if (oldHidden != newHidden)
601 [[self view] setHidden:newHidden]; 611 [[self view] setHidden:newHidden];
602 } 612 }
603 613
604 - (void)setBookmarkBarEnabled:(BOOL)enabled { 614 - (void)setBookmarkBarEnabled:(BOOL)enabled {
605 if (enabled != barIsEnabled_) { 615 if (enabled != barIsEnabled_) {
606 barIsEnabled_ = enabled; 616 barIsEnabled_ = enabled;
(...skipping 204 matching lines...) Expand 10 before | Expand all | Expand 10 after
811 DCHECK([[sender cell] isKindOfClass:[BookmarkButtonCell class]]); 821 DCHECK([[sender cell] isKindOfClass:[BookmarkButtonCell class]]);
812 822
813 // Only record the action if it's the initial folder being opened. 823 // Only record the action if it's the initial folder being opened.
814 if (!showFolderMenus_) 824 if (!showFolderMenus_)
815 RecordBookmarkFolderOpen([self bookmarkLaunchLocation]); 825 RecordBookmarkFolderOpen([self bookmarkLaunchLocation]);
816 showFolderMenus_ = !showFolderMenus_; 826 showFolderMenus_ = !showFolderMenus_;
817 827
818 // Middle click on chevron should not open bookmarks under it, instead just 828 // Middle click on chevron should not open bookmarks under it, instead just
819 // open its folder menu. 829 // open its folder menu.
820 if (sender == offTheSideButton_.get()) { 830 if (sender == offTheSideButton_.get()) {
821 [[sender cell] setStartingChildIndex:layout_.VisibleButtonCount()]; 831 [[sender cell] setStartingChildIndex:displayedButtonCount_];
822 NSEvent* event = [NSApp currentEvent]; 832 NSEvent* event = [NSApp currentEvent];
823 if ([event type] == NSOtherMouseUp) { 833 if ([event type] == NSOtherMouseUp) {
824 [self openOrCloseBookmarkFolderForOffTheSideButton]; 834 [self openOrCloseBookmarkFolderForOffTheSideButton];
825 return; 835 return;
826 } 836 }
827 } 837 }
828 // Toggle presentation of bar folder menus. 838 // Toggle presentation of bar folder menus.
829 [folderTarget_ openBookmarkFolderFromButton:sender]; 839 [folderTarget_ openBookmarkFolderFromButton:sender];
830 } 840 }
831 841
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after
874 - (void)themeDidChangeNotification:(NSNotification*)notification { 884 - (void)themeDidChangeNotification:(NSNotification*)notification {
875 [self updateTheme:[[[self view] window] themeProvider]]; 885 [self updateTheme:[[[self view] window] themeProvider]];
876 } 886 }
877 887
878 - (BookmarkLaunchLocation)bookmarkLaunchLocation { 888 - (BookmarkLaunchLocation)bookmarkLaunchLocation {
879 return currentState_ == BookmarkBar::DETACHED ? 889 return currentState_ == BookmarkBar::DETACHED ?
880 BOOKMARK_LAUNCH_LOCATION_DETACHED_BAR : 890 BOOKMARK_LAUNCH_LOCATION_DETACHED_BAR :
881 BOOKMARK_LAUNCH_LOCATION_ATTACHED_BAR; 891 BOOKMARK_LAUNCH_LOCATION_ATTACHED_BAR;
882 } 892 }
883 893
894 // Position the right-side buttons including the off-the-side chevron.
895 - (void)positionRightSideButtons {
896 int maxX = NSMaxX([[self buttonView] bounds]) -
897 bookmarks::kBookmarkHorizontalPadding;
898 int right = maxX;
899
900 int ignored = 0;
901 NSRect frame = [self frameForBookmarkButtonFromCell:
902 [otherBookmarksButton_ cell] xOffset:&ignored];
903 if (![otherBookmarksButton_ isHidden]) {
904 right -= NSWidth(frame);
905 frame.origin.x = right;
906 } else {
907 frame.origin.x = maxX - NSWidth(frame);
908 }
909 [otherBookmarksButton_ setFrame:frame];
910
911 frame = [offTheSideButton_ frame];
912 frame.size.height = bookmarks::kBookmarkFolderButtonHeight;
913 right -= frame.size.width;
914 frame.origin.x = right;
915 [offTheSideButton_ setFrame:frame];
916 }
917
918 // Configure the off-the-side button (e.g. specify the node range,
919 // check if we should enable or disable it, etc).
920 - (void)configureOffTheSideButtonContentsAndVisibility {
921 [[offTheSideButton_ cell] setStartingChildIndex:displayedButtonCount_];
922 [[offTheSideButton_ cell]
923 setBookmarkNode:bookmarkModel_->bookmark_bar_node()];
924 int bookmarkChildren = bookmarkModel_->bookmark_bar_node()->child_count();
925 if (bookmarkChildren > displayedButtonCount_) {
926 [offTheSideButton_ setHidden:NO];
927 // Set the off the side button as needing re-display. This is needed to
928 // avoid the button being shown with a black background the first time
929 // it's displayed. See https://codereview.chromium.org/1630453002/ for
930 // more context.
931 [offTheSideButton_ setNeedsDisplay:YES];
932 } else {
933 // If we just deleted the last item in an off-the-side menu so the
934 // button will be going away, make sure the menu goes away.
935 if (folderController_ &&
936 ([folderController_ parentButton] == offTheSideButton_))
937 [self closeAllBookmarkFolders];
938 // (And hide the button, too.)
939 [offTheSideButton_ setHidden:YES];
940 }
941 }
942
884 // Main menubar observation code, so we can know to close our fake menus if the 943 // Main menubar observation code, so we can know to close our fake menus if the
885 // user clicks on the actual menubar, as multiple unconnected menus sharing 944 // user clicks on the actual menubar, as multiple unconnected menus sharing
886 // the screen looks weird. 945 // the screen looks weird.
887 // Needed because the local event monitor doesn't see the click on the menubar. 946 // Needed because the local event monitor doesn't see the click on the menubar.
888 947
889 // Gets called when the menubar is clicked. 948 // Gets called when the menubar is clicked.
890 - (void)begunTracking:(NSNotification *)notification { 949 - (void)begunTracking:(NSNotification *)notification {
891 [self closeFolderAndStopTrackingMenus]; 950 [self closeFolderAndStopTrackingMenus];
892 } 951 }
893 952
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
930 } 989 }
931 } else { 990 } else {
932 if (exitEventTap_) { 991 if (exitEventTap_) {
933 [NSEvent removeMonitor:exitEventTap_]; 992 [NSEvent removeMonitor:exitEventTap_];
934 exitEventTap_ = nil; 993 exitEventTap_ = nil;
935 [self stopObservingMenubar]; 994 [self stopObservingMenubar];
936 } 995 }
937 } 996 }
938 } 997 }
939 998
940 // Show/hide the bookmark bar. 999 // Keep the "no items" label centered in response to a frame size change.
941 // If |animate| is YES, the changes are made using the animator; otherwise they 1000 - (void)centerNoItemsLabel {
942 // are made immediately. 1001 // Note that this computation is done in the parent's coordinate system,
1002 // which is unflipped. Also, we want the label to be a fixed distance from
1003 // the bottom, so that it slides up properly (on animating to hidden).
1004 // The textfield sits in the itemcontainer, so to center it we maintain
1005 // equal vertical padding on the top and bottom.
1006 int yoffset = (NSHeight([[buttonView_ noItemTextfield] frame]) -
1007 NSHeight([[buttonView_ noItemContainer] frame])) / 2;
1008 [[buttonView_ noItemContainer] setFrameOrigin:NSMakePoint(0, yoffset)];
1009 }
1010
1011 // (Private)
943 - (void)showBookmarkBarWithAnimation:(BOOL)animate { 1012 - (void)showBookmarkBarWithAnimation:(BOOL)animate {
944 if (animate && stateAnimationsEnabled_) { 1013 if (animate && stateAnimationsEnabled_) {
945 // If |-doBookmarkBarAnimation| does the animation, we're done. 1014 // If |-doBookmarkBarAnimation| does the animation, we're done.
946 if ([self doBookmarkBarAnimation]) 1015 if ([self doBookmarkBarAnimation])
947 return; 1016 return;
948 1017
949 // Else fall through and do the change instantly. 1018 // Else fall through and do the change instantly.
950 } 1019 }
951 1020
952 BookmarkBarToolbarView* view = [self controlledView]; 1021 BookmarkBarToolbarView* view = [self controlledView];
953 1022
954 // Set our height immediately via -[AnimatableView setHeight:]. 1023 // Set our height immediately via -[AnimatableView setHeight:].
955 [view setHeight:[self preferredHeight]]; 1024 [view setHeight:[self preferredHeight]];
956 1025
957 // Only show the divider if showing the normal bookmark bar. 1026 // Only show the divider if showing the normal bookmark bar.
958 BOOL showsDivider = [self isInState:BookmarkBar::SHOW]; 1027 BOOL showsDivider = [self isInState:BookmarkBar::SHOW];
959 [view setShowsDivider:showsDivider]; 1028 [view setShowsDivider:showsDivider];
960 1029
961 // Make sure we're shown. 1030 // Make sure we're shown.
962 [view setHidden:![self isVisible]]; 1031 [view setHidden:![self isVisible]];
963 1032
964 // Update everything else. 1033 // Update everything else.
965 [self layoutSubviews]; 1034 [self layoutSubviews];
966 [self frameDidChange]; 1035 [self frameDidChange];
967 } 1036 }
968 1037
969 // Handles animating the resize of the content view. Returns YES if it handled 1038 // (Private)
970 // the animation, NO if not (and hence it should be done instantly).
971 - (BOOL)doBookmarkBarAnimation { 1039 - (BOOL)doBookmarkBarAnimation {
972 BookmarkBarToolbarView* view = [self controlledView]; 1040 BookmarkBarToolbarView* view = [self controlledView];
973 if ([self isAnimatingFromState:BookmarkBar::HIDDEN 1041 if ([self isAnimatingFromState:BookmarkBar::HIDDEN
974 toState:BookmarkBar::SHOW]) { 1042 toState:BookmarkBar::SHOW]) {
975 [view setShowsDivider:YES]; 1043 [view setShowsDivider:YES];
976 [view setHidden:NO]; 1044 [view setHidden:NO];
977 // Height takes into account the extra height we have since the toolbar 1045 // Height takes into account the extra height we have since the toolbar
978 // only compresses when we're done. 1046 // only compresses when we're done.
979 [view animateToNewHeight:(chrome::kMinimumBookmarkBarHeight - 1047 [view animateToNewHeight:(chrome::kMinimumBookmarkBarHeight -
980 bookmarks::kBookmarkBarOverlap) 1048 bookmarks::kBookmarkBarOverlap)
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
1031 case BookmarkBar::HIDDEN: 1099 case BookmarkBar::HIDDEN:
1032 return 0; 1100 return 0;
1033 } 1101 }
1034 } 1102 }
1035 1103
1036 // Return an appropriate width for the given bookmark button cell. 1104 // Return an appropriate width for the given bookmark button cell.
1037 - (CGFloat)widthForBookmarkButtonCell:(NSCell*)cell { 1105 - (CGFloat)widthForBookmarkButtonCell:(NSCell*)cell {
1038 return std::min([cell cellSize].width, bookmarks::kDefaultBookmarkWidth); 1106 return std::min([cell cellSize].width, bookmarks::kDefaultBookmarkWidth);
1039 } 1107 }
1040 1108
1041 - (BookmarkButton*)buttonForNode:(const BookmarkNode*)node { 1109 // For the given root node of the bookmark bar, show or hide (as
1042 BookmarkButton* button = nil; 1110 // appropriate) the "no items" container (text which says "bookmarks
1043 int64_t nodeId = node->id(); 1111 // go here").
1044 auto buttonIt = nodeIdToButtonMap_.find(nodeId); 1112 - (void)showOrHideNoItemContainerForNode:(const BookmarkNode*)node {
1045 if (buttonIt != nodeIdToButtonMap_.end()) { 1113 BOOL hideNoItemWarning = !node->empty();
1046 button = (*buttonIt).second.get(); 1114 [[buttonView_ noItemContainer] setHidden:hideNoItemWarning];
1047 [self updateTitleAndTooltipForButton:button]; 1115 }
1048 } else if ([unusedButtonPool_ count] > 0) { 1116
1049 button = [[[unusedButtonPool_ firstObject] retain] autorelease]; 1117 // TODO(jrg): write a "build bar" so there is a nice spot for things
1050 [unusedButtonPool_ removeObjectAtIndex:0]; 1118 // like the contextual menu which is invoked when not over a
1051 BOOL darkTheme = [[[self view] window] hasDarkTheme]; 1119 // bookmark. On Safari that menu has a "new folder" option.
1052 [[button cell] 1120 - (void)addNodesToButtonList:(const BookmarkNode*)node {
1053 setBookmarkNode:node 1121 [self showOrHideNoItemContainerForNode:node];
1054 image:[self faviconForNode:node forADarkTheme:darkTheme]]; 1122
1055 } else { 1123 CGFloat maxViewX = NSMaxX([[self view] bounds]);
1056 BookmarkButtonCell* cell = [self cellForBookmarkNode:node]; 1124 int xOffset =
1057 NSRect frame = NSMakeRect(0, bookmarks::kBookmarkVerticalPadding, 0, 1125 bookmarks::kBookmarkLeftMargin - bookmarks::kBookmarkHorizontalPadding;
1058 kBookmarkButtonHeightMinusPadding); 1126
1059 button = [[[BookmarkButton alloc] initWithFrame:frame] autorelease]; 1127 // Draw the apps bookmark if needed.
1060 [button setCell:cell]; 1128 if (![appsPageShortcutButton_ isHidden]) {
1061 [buttonView_ addSubview:button]; 1129 NSRect frame =
1062 [button setDelegate:self]; 1130 [self frameForBookmarkButtonFromCell:[appsPageShortcutButton_ cell]
1131 xOffset:&xOffset];
1132 [appsPageShortcutButton_ setFrame:frame];
1063 } 1133 }
1064 DCHECK(button);
1065 1134
1066 // Do setup. 1135 // Draw the managed bookmark folder if needed.
1067 nodeIdToButtonMap_.insert( 1136 if (![managedBookmarksButton_ isHidden]) {
1068 {nodeId, base::scoped_nsobject<BookmarkButton>([button retain])}); 1137 xOffset += bookmarks::kBookmarkHorizontalPadding;
1138 NSRect frame =
1139 [self frameForBookmarkButtonFromCell:[managedBookmarksButton_ cell]
1140 xOffset:&xOffset];
1141 [managedBookmarksButton_ setFrame:frame];
1142 }
1143
1144 // Draw the supervised bookmark folder if needed.
1145 if (![supervisedBookmarksButton_ isHidden]) {
1146 xOffset += bookmarks::kBookmarkHorizontalPadding;
1147 NSRect frame =
1148 [self frameForBookmarkButtonFromCell:[supervisedBookmarksButton_ cell]
1149 xOffset:&xOffset];
1150 [supervisedBookmarksButton_ setFrame:frame];
1151 }
1152
1153 for (int i = 0; i < node->child_count(); i++) {
1154 const BookmarkNode* child = node->GetChild(i);
1155 BookmarkButton* button = [self buttonForNode:child xOffset:&xOffset];
1156 if (NSMinX([button frame]) >= maxViewX) {
1157 [button setDelegate:nil];
1158 break;
1159 }
1160 [buttons_ addObject:button];
1161 }
1162 }
1163
1164 - (BookmarkButton*)buttonForNode:(const BookmarkNode*)node
1165 xOffset:(int*)xOffset {
1166 BookmarkButtonCell* cell = [self cellForBookmarkNode:node];
1167 NSRect frame = [self frameForBookmarkButtonFromCell:cell xOffset:xOffset];
1168
1169 base::scoped_nsobject<BookmarkButton> button(
1170 [[BookmarkButton alloc] initWithFrame:frame]);
1171 DCHECK(button.get());
1172
1173 // [NSButton setCell:] warns to NOT use setCell: other than in the
1174 // initializer of a control. However, we are using a basic
1175 // NSButton whose initializer does not take an NSCell as an
1176 // object. To honor the assumed semantics, we do nothing with
1177 // NSButton between alloc/init and setCell:.
1178 [button setCell:cell];
1179 [button setDelegate:self];
1180
1181 // We cannot set the button cell's text color until it is placed in
1182 // the button (e.g. the [button setCell:cell] call right above). We
1183 // also cannot set the cell's text color until the view is added to
1184 // the hierarchy. If that second part is now true, set the color.
1185 // (If not we'll set the color on the 1st themeChanged:
1186 // notification.)
1187 const ui::ThemeProvider* themeProvider = [[[self view] window] themeProvider];
1188 if (themeProvider) {
1189 NSColor* color =
1190 themeProvider->GetNSColor(ThemeProperties::COLOR_BOOKMARK_TEXT);
1191 [cell setTextColor:color];
1192 }
1193
1069 if (node->is_folder()) { 1194 if (node->is_folder()) {
1070 [button setTarget:self]; 1195 [button setTarget:self];
1071 [button setAction:@selector(openBookmarkFolderFromButton:)]; 1196 [button setAction:@selector(openBookmarkFolderFromButton:)];
1072 [[button draggableButton] setActsOnMouseDown:YES]; 1197 [[button draggableButton] setActsOnMouseDown:YES];
1198 // If it has a title, and it will be truncated, show full title in
1199 // tooltip.
1200 NSString* title = base::SysUTF16ToNSString(node->GetTitle());
1201 if ([title length] &&
1202 [[button cell] cellSize].width > bookmarks::kDefaultBookmarkWidth) {
1203 [button setToolTip:title];
1204 }
1073 } else { 1205 } else {
1074 // Make the button do something. 1206 // Make the button do something
1075 [button setTarget:self]; 1207 [button setTarget:self];
1076 [button setAction:@selector(openBookmark:)]; 1208 [button setAction:@selector(openBookmark:)];
1077 [[button draggableButton] setActsOnMouseDown:NO]; 1209 if (node->is_url())
1210 [button setToolTip:[BookmarkMenuCocoaController tooltipForNode:node]];
1078 } 1211 }
1079 [self updateTitleAndTooltipForButton:button]; 1212 return [[button.get() retain] autorelease];
1080 return button;
1081 } 1213 }
1082 1214
1083 - (void)updateTitleAndTooltipForButton:(BookmarkButton*)button { 1215 // Add bookmark buttons to the view only if they are completely
1084 const BookmarkNode* node = [button bookmarkNode]; 1216 // visible and don't overlap the "other bookmarks". Remove buttons
1085 CGFloat buttonWidth = [self widthOfButtonForNode:node]; 1217 // which are clipped. Called when building the bookmark bar the first time.
1086 NSString* buttonTitle = base::SysUTF16ToNSString(node->GetTitle()); 1218 - (void)addButtonsToView {
1219 displayedButtonCount_ = 0;
1220 NSMutableArray* buttons = [self buttons];
1221 for (NSButton* button in buttons) {
1222 if (NSMaxX([button frame]) > (NSMinX([offTheSideButton_ frame]) -
1223 bookmarks::kBookmarkHorizontalPadding))
1224 break;
1225 [buttonView_ addSubview:button];
1226 ++displayedButtonCount_;
1227 }
1228 NSUInteger removalCount =
1229 [buttons count] - (NSUInteger)displayedButtonCount_;
1230 if (removalCount > 0) {
1231 NSRange removalRange = NSMakeRange(displayedButtonCount_, removalCount);
1232 [buttons removeObjectsInRange:removalRange];
1233 }
1234 }
1087 1235
1088 if (NSWidth([button frame]) == buttonWidth && 1236 // Shows or hides the Managed, Supervised, or Other Bookmarks button as
1089 [[button title] isEqualToString:buttonTitle]) 1237 // appropriate, and returns whether it ended up visible.
1090 return; 1238 - (BOOL)setBookmarkButtonVisibility:(BookmarkButton*)button
1239 canShow:(BOOL)show
1240 resetAllButtonPositions:(BOOL)resetButtons {
1241 if (!button)
1242 return NO;
1091 1243
1092 CGRect frame = [button frame]; 1244 BOOL visible = ![button bookmarkNode]->empty() && show;
1093 frame.size.width = buttonWidth; 1245 BOOL currentVisibility = ![button isHidden];
1094 [button setFrame:frame]; 1246 if (currentVisibility != visible) {
1095 [[button cell] setTitle:buttonTitle]; 1247 [button setHidden:!visible];
1096 NSString* tooltip = nil; 1248 if (resetButtons)
1249 [self resetAllButtonPositionsWithAnimation:NO];
1250 }
1251 return visible;
1252 }
1097 1253
1098 // Folders show a tooltip iff the title is truncated. 1254 // Shows or hides the Managed Bookmarks button as appropriate, and returns
1099 if (node->is_folder() && [buttonTitle length] > 0 && 1255 // whether it ended up visible.
1100 [[button cell] cellSize].width < buttonWidth) { 1256 - (BOOL)setManagedBookmarksButtonVisibility {
1101 tooltip = buttonTitle; 1257 PrefService* prefs = browser_->profile()->GetPrefs();
1102 } else if (node->is_url()) { 1258 BOOL prefIsSet =
1103 tooltip = [BookmarkMenuCocoaController tooltipForNode:node]; 1259 prefs->GetBoolean(bookmarks::prefs::kShowManagedBookmarksInBookmarkBar);
1104 } 1260 return [self setBookmarkButtonVisibility:managedBookmarksButton_.get()
1261 canShow:prefIsSet
1262 resetAllButtonPositions:YES];
1263 }
1105 1264
1106 [button setToolTip:tooltip]; 1265 // Shows or hides the Supervised Bookmarks button as appropriate, and returns
1266 // whether it ended up visible.
1267 - (BOOL)setSupervisedBookmarksButtonVisibility {
1268 return [self setBookmarkButtonVisibility:supervisedBookmarksButton_.get()
1269 canShow:YES
1270 resetAllButtonPositions:YES];
1271 }
1272
1273 // Shows or hides the Other Bookmarks button as appropriate, and returns
1274 // whether it ended up visible.
1275 - (BOOL)setOtherBookmarksButtonVisibility {
1276 return [self setBookmarkButtonVisibility:otherBookmarksButton_.get()
1277 canShow:YES
1278 resetAllButtonPositions:NO];
1279 }
1280
1281 // Shows or hides the Apps button as appropriate, and returns whether it ended
1282 // up visible.
1283 - (BOOL)setAppsPageShortcutButtonVisibility {
1284 if (!appsPageShortcutButton_.get())
1285 return NO;
1286
1287 BOOL visible =
1288 bookmarkModel_->loaded() &&
1289 chrome::ShouldShowAppsShortcutInBookmarkBar(browser_->profile());
1290 [appsPageShortcutButton_ setHidden:!visible];
1291 return visible;
1107 } 1292 }
1108 1293
1109 // Creates a bookmark bar button that does not correspond to a regular bookmark 1294 // Creates a bookmark bar button that does not correspond to a regular bookmark
1110 // or folder. It is used by the "Other Bookmarks" and the "Apps" buttons. 1295 // or folder. It is used by the "Other Bookmarks" and the "Apps" buttons.
1111 - (BookmarkButton*)createCustomBookmarkButtonForCell:(NSCell*)cell { 1296 - (BookmarkButton*)createCustomBookmarkButtonForCell:(NSCell*)cell {
1112 BookmarkButton* button = [[BookmarkButton alloc] init]; 1297 BookmarkButton* button = [[BookmarkButton alloc] init];
1113 [[button draggableButton] setDraggable:NO]; 1298 [[button draggableButton] setDraggable:NO];
1114 [[button draggableButton] setActsOnMouseDown:YES]; 1299 [[button draggableButton] setActsOnMouseDown:YES];
1115 [button setCell:cell]; 1300 [button setCell:cell];
1116 [button setDelegate:self]; 1301 [button setDelegate:self];
1117 [button setTarget:self]; 1302 [button setTarget:self];
1118 // Make sure this button, like all other BookmarkButtons, lives 1303 // Make sure this button, like all other BookmarkButtons, lives
1119 // until the end of the current event loop. 1304 // until the end of the current event loop.
1120 [[button retain] autorelease]; 1305 [[button retain] autorelease];
1121 return button; 1306 return button;
1122 } 1307 }
1123 1308
1124 // Creates the button for "Managed Bookmarks", but does not position it. 1309 // Creates the button for "Managed Bookmarks", but does not position it.
1125 - (void)createManagedBookmarksButton { 1310 - (void)createManagedBookmarksButton {
1126 if (managedBookmarksButton_.get()) { 1311 if (managedBookmarksButton_.get()) {
1127 // The node's title might have changed if the user signed in or out. 1312 // The node's title might have changed if the user signed in or out.
1128 // Make sure it's up to date now. 1313 // Make sure it's up to date now.
1129 const BookmarkNode* node = managedBookmarkService_->managed_node(); 1314 const BookmarkNode* node = managedBookmarkService_->managed_node();
1130 NSString* title = base::SysUTF16ToNSString(node->GetTitle()); 1315 NSString* title = base::SysUTF16ToNSString(node->GetTitle());
1131 NSCell* cell = [managedBookmarksButton_ cell]; 1316 NSCell* cell = [managedBookmarksButton_ cell];
1132 [cell setTitle:title]; 1317 [cell setTitle:title];
1133 1318
1319 // Its visibility may have changed too.
1320 [self setManagedBookmarksButtonVisibility];
1321
1134 return; 1322 return;
1135 } 1323 }
1136 1324
1137 NSCell* cell = 1325 NSCell* cell =
1138 [self cellForBookmarkNode:managedBookmarkService_->managed_node()]; 1326 [self cellForBookmarkNode:managedBookmarkService_->managed_node()];
1139 managedBookmarksButton_.reset([self createCustomBookmarkButtonForCell:cell]); 1327 managedBookmarksButton_.reset([self createCustomBookmarkButtonForCell:cell]);
1140 [managedBookmarksButton_ setAction:@selector(openBookmarkFolderFromButton:)]; 1328 [managedBookmarksButton_ setAction:@selector(openBookmarkFolderFromButton:)];
1141 view_id_util::SetID(managedBookmarksButton_.get(), VIEW_ID_MANAGED_BOOKMARKS); 1329 view_id_util::SetID(managedBookmarksButton_.get(), VIEW_ID_MANAGED_BOOKMARKS);
1142 NSRect frame = NSMakeRect(0, bookmarks::kBookmarkVerticalPadding,
1143 [self widthForBookmarkButtonCell:cell],
1144 kBookmarkButtonHeightMinusPadding);
1145 [managedBookmarksButton_ setFrame:frame];
1146 [buttonView_ addSubview:managedBookmarksButton_.get()]; 1330 [buttonView_ addSubview:managedBookmarksButton_.get()];
1147 1331
1332 [self setManagedBookmarksButtonVisibility];
1148 } 1333 }
1149 1334
1150 // Creates the button for "Supervised Bookmarks", but does not position it. 1335 // Creates the button for "Supervised Bookmarks", but does not position it.
1151 - (void)createSupervisedBookmarksButton { 1336 - (void)createSupervisedBookmarksButton {
1152 if (supervisedBookmarksButton_.get()) { 1337 if (supervisedBookmarksButton_.get()) {
1338 // The button's already there, but its visibility may have changed.
1339 [self setSupervisedBookmarksButtonVisibility];
1153 return; 1340 return;
1154 } 1341 }
1155 1342
1156 NSCell* cell = 1343 NSCell* cell =
1157 [self cellForBookmarkNode:managedBookmarkService_->supervised_node()]; 1344 [self cellForBookmarkNode:managedBookmarkService_->supervised_node()];
1158 supervisedBookmarksButton_.reset( 1345 supervisedBookmarksButton_.reset(
1159 [self createCustomBookmarkButtonForCell:cell]); 1346 [self createCustomBookmarkButtonForCell:cell]);
1160 [supervisedBookmarksButton_ 1347 [supervisedBookmarksButton_
1161 setAction:@selector(openBookmarkFolderFromButton:)]; 1348 setAction:@selector(openBookmarkFolderFromButton:)];
1162 view_id_util::SetID(supervisedBookmarksButton_.get(), 1349 view_id_util::SetID(supervisedBookmarksButton_.get(),
1163 VIEW_ID_SUPERVISED_BOOKMARKS); 1350 VIEW_ID_SUPERVISED_BOOKMARKS);
1164 NSRect frame = NSMakeRect(0, bookmarks::kBookmarkVerticalPadding,
1165 [self widthForBookmarkButtonCell:cell],
1166 kBookmarkButtonHeightMinusPadding);
1167 [supervisedBookmarksButton_ setFrame:frame];
1168 [buttonView_ addSubview:supervisedBookmarksButton_.get()]; 1351 [buttonView_ addSubview:supervisedBookmarksButton_.get()];
1352
1353 [self setSupervisedBookmarksButtonVisibility];
1169 } 1354 }
1170 1355
1171 // Creates the button for "Other Bookmarks", but does not position it. 1356 // Creates the button for "Other Bookmarks", but does not position it.
1172 - (void)createOtherBookmarksButton { 1357 - (void)createOtherBookmarksButton {
1173 // Can't create this until the model is loaded, but only need to 1358 // Can't create this until the model is loaded, but only need to
1174 // create it once. 1359 // create it once.
1175 if (otherBookmarksButton_.get()) { 1360 if (otherBookmarksButton_.get()) {
1361 [self setOtherBookmarksButtonVisibility];
1176 return; 1362 return;
1177 } 1363 }
1178 1364
1179 NSCell* cell = [self cellForBookmarkNode:bookmarkModel_->other_node()]; 1365 NSCell* cell = [self cellForBookmarkNode:bookmarkModel_->other_node()];
1180 otherBookmarksButton_.reset([self createCustomBookmarkButtonForCell:cell]); 1366 otherBookmarksButton_.reset([self createCustomBookmarkButtonForCell:cell]);
1367 // Peg at right; keep same height as bar.
1368 [otherBookmarksButton_ setAutoresizingMask:(NSViewMinXMargin)];
1181 [otherBookmarksButton_ setAction:@selector(openBookmarkFolderFromButton:)]; 1369 [otherBookmarksButton_ setAction:@selector(openBookmarkFolderFromButton:)];
1182 NSRect frame = NSMakeRect(0, bookmarks::kBookmarkVerticalPadding,
1183 [self widthForBookmarkButtonCell:cell],
1184 kBookmarkButtonHeightMinusPadding);
1185 [otherBookmarksButton_ setFrame:frame];
1186 view_id_util::SetID(otherBookmarksButton_.get(), VIEW_ID_OTHER_BOOKMARKS); 1370 view_id_util::SetID(otherBookmarksButton_.get(), VIEW_ID_OTHER_BOOKMARKS);
1187 [buttonView_ addSubview:otherBookmarksButton_.get()]; 1371 [buttonView_ addSubview:otherBookmarksButton_.get()];
1372
1373 [self setOtherBookmarksButtonVisibility];
1188 } 1374 }
1189 1375
1190 // Creates the button for "Apps", but does not position it. 1376 // Creates the button for "Apps", but does not position it.
1191 - (void)createAppsPageShortcutButton { 1377 - (void)createAppsPageShortcutButton {
1378 // Can't create this until the model is loaded, but only need to
1379 // create it once.
1192 if (appsPageShortcutButton_.get()) { 1380 if (appsPageShortcutButton_.get()) {
1381 [self setAppsPageShortcutButtonVisibility];
1193 return; 1382 return;
1194 } 1383 }
1195 1384
1196 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); 1385 ResourceBundle& rb = ResourceBundle::GetSharedInstance();
1197 NSString* text = l10n_util::GetNSString(IDS_BOOKMARK_BAR_APPS_SHORTCUT_NAME); 1386 NSString* text = l10n_util::GetNSString(IDS_BOOKMARK_BAR_APPS_SHORTCUT_NAME);
1198 NSImage* image = rb.GetNativeImageNamed( 1387 NSImage* image = rb.GetNativeImageNamed(
1199 IDR_BOOKMARK_BAR_APPS_SHORTCUT).ToNSImage(); 1388 IDR_BOOKMARK_BAR_APPS_SHORTCUT).ToNSImage();
1200 NSCell* cell = [self cellForCustomButtonWithText:text 1389 NSCell* cell = [self cellForCustomButtonWithText:text
1201 image:image]; 1390 image:image];
1202 NSRect frame;
1203 frame.origin.y = bookmarks::kBookmarkVerticalPadding;
1204 frame.size = NSMakeSize([self widthForBookmarkButtonCell:cell],
1205 kBookmarkButtonHeightMinusPadding);
1206 appsPageShortcutButton_.reset([self createCustomBookmarkButtonForCell:cell]); 1391 appsPageShortcutButton_.reset([self createCustomBookmarkButtonForCell:cell]);
1207 [appsPageShortcutButton_ setFrame:frame];
1208 [[appsPageShortcutButton_ draggableButton] setActsOnMouseDown:NO]; 1392 [[appsPageShortcutButton_ draggableButton] setActsOnMouseDown:NO];
1209 [appsPageShortcutButton_ setAction:@selector(openAppsPage:)]; 1393 [appsPageShortcutButton_ setAction:@selector(openAppsPage:)];
1210 NSString* tooltip = 1394 NSString* tooltip =
1211 l10n_util::GetNSString(IDS_BOOKMARK_BAR_APPS_SHORTCUT_TOOLTIP); 1395 l10n_util::GetNSString(IDS_BOOKMARK_BAR_APPS_SHORTCUT_TOOLTIP);
1212 [appsPageShortcutButton_ setToolTip:tooltip]; 1396 [appsPageShortcutButton_ setToolTip:tooltip];
1213 [buttonView_ addSubview:appsPageShortcutButton_.get()]; 1397 [buttonView_ addSubview:appsPageShortcutButton_.get()];
1398
1399 [self setAppsPageShortcutButtonVisibility];
1214 } 1400 }
1215 1401
1216 // Creates the "off-the-side" (chevron/overflow) button but
1217 // does not position it.
1218 - (void)createOffTheSideButton { 1402 - (void)createOffTheSideButton {
1219 if (offTheSideButton_.get()) {
1220 return;
1221 }
1222 DCHECK(bookmarkModel_->loaded());
1223 offTheSideButton_.reset( 1403 offTheSideButton_.reset(
1224 [[BookmarkButton alloc] initWithFrame:NSMakeRect(0, 0, 20, 24)]); 1404 [[BookmarkButton alloc] initWithFrame:NSMakeRect(586, 0, 20, 24)]);
1225 id offTheSideCell = [BookmarkButtonCell offTheSideButtonCell]; 1405 id offTheSideCell = [BookmarkButtonCell offTheSideButtonCell];
1226 [offTheSideCell setTag:kMaterialStandardButtonTypeWithLimitedClickFeedback]; 1406 [offTheSideCell setTag:kMaterialStandardButtonTypeWithLimitedClickFeedback];
1227 [offTheSideCell setImagePosition:NSImageOnly]; 1407 [offTheSideCell setImagePosition:NSImageOnly];
1228 1408
1229 [offTheSideCell setHighlightsBy:NSNoCellMask]; 1409 [offTheSideCell setHighlightsBy:NSNoCellMask];
1230 [offTheSideCell setShowsBorderOnlyWhileMouseInside:YES]; 1410 [offTheSideCell setShowsBorderOnlyWhileMouseInside:YES];
1231 [offTheSideCell setBezelStyle:NSShadowlessSquareBezelStyle]; 1411 [offTheSideCell setBezelStyle:NSShadowlessSquareBezelStyle];
1232 [offTheSideCell setBookmarkNode:bookmarkModel_->bookmark_bar_node()];
1233
1234 [offTheSideButton_ setCell:offTheSideCell]; 1412 [offTheSideButton_ setCell:offTheSideCell];
1235 [offTheSideButton_ setImage:[self offTheSideButtonImage:NO]]; 1413 [offTheSideButton_ setImage:[self offTheSideButtonImage:NO]];
1236 [offTheSideButton_ setButtonType:NSMomentaryLightButton]; 1414 [offTheSideButton_ setButtonType:NSMomentaryLightButton];
1237 1415
1238 [offTheSideButton_ setTarget:self]; 1416 [offTheSideButton_ setTarget:self];
1239 [offTheSideButton_ setAction:@selector(openOffTheSideFolderFromButton:)]; 1417 [offTheSideButton_ setAction:@selector(openOffTheSideFolderFromButton:)];
1240 [offTheSideButton_ setDelegate:self]; 1418 [offTheSideButton_ setDelegate:self];
1241 [[offTheSideButton_ draggableButton] setDraggable:NO]; 1419 [[offTheSideButton_ draggableButton] setDraggable:NO];
1242 [[offTheSideButton_ draggableButton] setActsOnMouseDown:YES]; 1420 [[offTheSideButton_ draggableButton] setActsOnMouseDown:YES];
1243 [offTheSideButton_ setHidden:YES];
1244 [buttonView_ addSubview:offTheSideButton_];
1245 }
1246
1247 - (void)updateExtraButtonsVisibility {
1248 [self rebuildLayoutWithAnimated:NO];
1249 }
1250
1251 - (void)createExtraButtons {
1252 DCHECK(!didCreateExtraButtons_);
1253 [self createSupervisedBookmarksButton];
1254 [self createManagedBookmarksButton];
1255 [self createOtherBookmarksButton];
1256 [self createAppsPageShortcutButton];
1257 [self createOffTheSideButton];
1258 didCreateExtraButtons_ = YES;
1259 } 1421 }
1260 1422
1261 - (void)openAppsPage:(id)sender { 1423 - (void)openAppsPage:(id)sender {
1262 WindowOpenDisposition disposition = 1424 WindowOpenDisposition disposition =
1263 ui::WindowOpenDispositionFromNSEvent([NSApp currentEvent]); 1425 ui::WindowOpenDispositionFromNSEvent([NSApp currentEvent]);
1264 [self openURL:GURL(chrome::kChromeUIAppsURL) disposition:disposition]; 1426 [self openURL:GURL(chrome::kChromeUIAppsURL) disposition:disposition];
1265 RecordBookmarkAppsPageOpen([self bookmarkLaunchLocation]); 1427 RecordBookmarkAppsPageOpen([self bookmarkLaunchLocation]);
1266 } 1428 }
1267 1429
1268 // To avoid problems with sync, changes that may impact the current 1430 // To avoid problems with sync, changes that may impact the current
1269 // bookmark (e.g. deletion) make sure context menus are closed. This 1431 // bookmark (e.g. deletion) make sure context menus are closed. This
1270 // prevents deleting a node which no longer exists. 1432 // prevents deleting a node which no longer exists.
1271 - (void)cancelMenuTracking { 1433 - (void)cancelMenuTracking {
1272 [contextMenuController_ cancelTracking]; 1434 [contextMenuController_ cancelTracking];
1273 } 1435 }
1274 1436
1275 // Moves to the given next state (from the current state), possibly animating.
1276 // If |animate| is NO, it will stop any running animation and jump to the given
1277 // state. If YES, it may either (depending on implementation) jump to the end of
1278 // the current animation and begin the next one, or stop the current animation
1279 // mid-flight and animate to the next state.
1280 - (void)moveToState:(BookmarkBar::State)nextState 1437 - (void)moveToState:(BookmarkBar::State)nextState
1281 withAnimation:(BOOL)animate { 1438 withAnimation:(BOOL)animate {
1282 BOOL isAnimationRunning = [self isAnimationRunning]; 1439 BOOL isAnimationRunning = [self isAnimationRunning];
1283 1440
1284 // No-op if the next state is the same as the "current" one, subject to the 1441 // No-op if the next state is the same as the "current" one, subject to the
1285 // following conditions: 1442 // following conditions:
1286 // - no animation is running; or 1443 // - no animation is running; or
1287 // - an animation is running and |animate| is YES ([*] if it's NO, we'd want 1444 // - an animation is running and |animate| is YES ([*] if it's NO, we'd want
1288 // to cancel the animation and jump to the final state). 1445 // to cancel the animation and jump to the final state).
1289 if ((nextState == currentState_) && (!isAnimationRunning || animate)) 1446 if ((nextState == currentState_) && (!isAnimationRunning || animate))
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after
1336 } 1493 }
1337 1494
1338 // N.B.: |-moveToState:...| will check if this should be a no-op or not. 1495 // N.B.: |-moveToState:...| will check if this should be a no-op or not.
1339 - (void)updateState:(BookmarkBar::State)newState 1496 - (void)updateState:(BookmarkBar::State)newState
1340 changeType:(BookmarkBar::AnimateChangeType)changeType { 1497 changeType:(BookmarkBar::AnimateChangeType)changeType {
1341 BOOL animate = changeType == BookmarkBar::ANIMATE_STATE_CHANGE && 1498 BOOL animate = changeType == BookmarkBar::ANIMATE_STATE_CHANGE &&
1342 stateAnimationsEnabled_; 1499 stateAnimationsEnabled_;
1343 [self moveToState:newState withAnimation:animate]; 1500 [self moveToState:newState withAnimation:animate];
1344 } 1501 }
1345 1502
1346 // Jump to final state (detached, attached, hidden, etc.) without animating, 1503 // (Private)
1347 // stopping a running animation if necessary.
1348 - (void)finalizeState { 1504 - (void)finalizeState {
1349 // We promise that our delegate that the variables will be finalized before 1505 // We promise that our delegate that the variables will be finalized before
1350 // the call to |-bookmarkBar:didChangeFromState:toState:|. 1506 // the call to |-bookmarkBar:didChangeFromState:toState:|.
1351 BookmarkBar::State oldState = lastState_; 1507 BookmarkBar::State oldState = lastState_;
1352 lastState_ = currentState_; 1508 lastState_ = currentState_;
1353 isAnimationRunning_ = NO; 1509 isAnimationRunning_ = NO;
1354 1510
1355 // Notify our delegate. 1511 // Notify our delegate.
1356 [delegate_ bookmarkBar:self 1512 [delegate_ bookmarkBar:self
1357 didChangeFromState:oldState 1513 didChangeFromState:oldState
1358 toState:currentState_]; 1514 toState:currentState_];
1359 1515
1360 // Update ourselves visually. 1516 // Update ourselves visually.
1361 [self updateVisibility]; 1517 [self updateVisibility];
1362 } 1518 }
1363 1519
1364 // Stops any current animation in its tracks (midway). 1520 // (Private)
1365 - (void)stopCurrentAnimation { 1521 - (void)stopCurrentAnimation {
1366 [[self controlledView] stopAnimation]; 1522 [[self controlledView] stopAnimation];
1367 } 1523 }
1368 1524
1369 // Delegate method for |AnimatableView| (a superclass of 1525 // Delegate method for |AnimatableView| (a superclass of
1370 // |BookmarkBarToolbarView|). 1526 // |BookmarkBarToolbarView|).
1371 - (void)animationDidEnd:(NSAnimation*)animation { 1527 - (void)animationDidEnd:(NSAnimation*)animation {
1372 [self finalizeState]; 1528 [self finalizeState];
1373 } 1529 }
1374 1530
1531 - (void)reconfigureBookmarkBar {
1532 [self setManagedBookmarksButtonVisibility];
1533 [self setSupervisedBookmarksButtonVisibility];
1534 [self redistributeButtonsOnBarAsNeeded];
1535 [self positionRightSideButtons];
1536 [self configureOffTheSideButtonContentsAndVisibility];
1537 [self centerNoItemsLabel];
1538 }
1539
1540 // Determine if the given |view| can completely fit within the constraint of
1541 // maximum x, given by |maxViewX|, and, if not, narrow the view up to a minimum
1542 // width. If the minimum width is not achievable then hide the view. Return YES
1543 // if the view was hidden.
1544 - (BOOL)shrinkOrHideView:(NSView*)view forMaxX:(CGFloat)maxViewX {
1545 BOOL wasHidden = NO;
1546 // See if the view needs to be narrowed.
1547 NSRect frame = [view frame];
1548 if (NSMaxX(frame) > maxViewX) {
1549 // Resize if more than 30 pixels are showing, otherwise hide.
1550 if (NSMinX(frame) + 30.0 < maxViewX) {
1551 frame.size.width = maxViewX - NSMinX(frame);
1552 [view setFrame:frame];
1553 } else {
1554 [view setHidden:YES];
1555 wasHidden = YES;
1556 }
1557 }
1558 return wasHidden;
1559 }
1560
1375 // Bookmark button menu items that open a new window (e.g., open in new window, 1561 // Bookmark button menu items that open a new window (e.g., open in new window,
1376 // open in incognito, edit, etc.) cause us to lose a mouse-exited event 1562 // open in incognito, edit, etc.) cause us to lose a mouse-exited event
1377 // on the button, which leaves it in a hover state. 1563 // on the button, which leaves it in a hover state.
1378 // Since the showsBorderOnlyWhileMouseInside uses a tracking area, simple 1564 // Since the showsBorderOnlyWhileMouseInside uses a tracking area, simple
1379 // tricks (e.g. sending an extra mouseExited: to the button) don't 1565 // tricks (e.g. sending an extra mouseExited: to the button) don't
1380 // fix the problem. 1566 // fix the problem.
1381 // http://crbug.com/129338 1567 // http://crbug.com/129338
1382 - (void)unhighlightBookmark:(const BookmarkNode*)node { 1568 - (void)unhighlightBookmark:(const BookmarkNode*)node {
1383 // Only relevant if context menu was opened from a button on the 1569 // Only relevant if context menu was opened from a button on the
1384 // bookmark bar. 1570 // bookmark bar.
1385 const BookmarkNode* parent = node->parent(); 1571 const BookmarkNode* parent = node->parent();
1386 BookmarkNode::Type parentType = parent->type(); 1572 BookmarkNode::Type parentType = parent->type();
1387 if (parentType == BookmarkNode::BOOKMARK_BAR) { 1573 if (parentType == BookmarkNode::BOOKMARK_BAR) {
1388 int index = parent->GetIndexOf(node); 1574 int index = parent->GetIndexOf(node);
1389 if ((index >= 0) && (static_cast<NSUInteger>(index) < [buttons_ count])) { 1575 if ((index >= 0) && (static_cast<NSUInteger>(index) < [buttons_ count])) {
1390 NSButton* button = 1576 NSButton* button =
1391 [buttons_ objectAtIndex:static_cast<NSUInteger>(index)]; 1577 [buttons_ objectAtIndex:static_cast<NSUInteger>(index)];
1392 if ([button showsBorderOnlyWhileMouseInside]) { 1578 if ([button showsBorderOnlyWhileMouseInside]) {
1393 [button setShowsBorderOnlyWhileMouseInside:NO]; 1579 [button setShowsBorderOnlyWhileMouseInside:NO];
1394 [button setShowsBorderOnlyWhileMouseInside:YES]; 1580 [button setShowsBorderOnlyWhileMouseInside:YES];
1395 } 1581 }
1396 } 1582 }
1397 } 1583 }
1398 } 1584 }
1399 1585
1400 - (void)addButtonForNode:(const bookmarks::BookmarkNode*)node 1586
1401 atIndex:(NSInteger)buttonIndex { 1587 // Adjust the horizontal width, x position and the visibility of the "For quick
1402 [self rebuildLayoutWithAnimated:NO]; 1588 // access" text field and "Import bookmarks..." button based on the current
1589 // width of the containing |buttonView_| (which is affected by window width).
1590 - (void)adjustNoItemContainerForMaxX:(CGFloat)maxViewX {
1591 if (![[buttonView_ noItemContainer] isHidden]) {
1592 // Reset initial frames for the two items, then adjust as necessary.
1593 NSTextField* noItemTextfield = [buttonView_ noItemTextfield];
1594 NSRect noItemsRect = originalNoItemsRect_;
1595 NSRect importBookmarksRect = originalImportBookmarksRect_;
1596 if (![appsPageShortcutButton_ isHidden]) {
1597 float width = NSWidth([appsPageShortcutButton_ frame]);
1598 noItemsRect.origin.x += width;
1599 importBookmarksRect.origin.x += width;
1600 }
1601 if (![managedBookmarksButton_ isHidden]) {
1602 float width = NSWidth([managedBookmarksButton_ frame]);
1603 noItemsRect.origin.x += width;
1604 importBookmarksRect.origin.x += width;
1605 }
1606 if (![supervisedBookmarksButton_ isHidden]) {
1607 float width = NSWidth([supervisedBookmarksButton_ frame]);
1608 noItemsRect.origin.x += width;
1609 importBookmarksRect.origin.x += width;
1610 }
1611 [noItemTextfield setFrame:noItemsRect];
1612 [noItemTextfield setHidden:NO];
1613 NSButton* importBookmarksButton = [buttonView_ importBookmarksButton];
1614 [importBookmarksButton setFrame:importBookmarksRect];
1615 [importBookmarksButton setHidden:NO];
1616 // Check each to see if they need to be shrunk or hidden.
1617 if ([self shrinkOrHideView:importBookmarksButton forMaxX:maxViewX])
1618 [self shrinkOrHideView:noItemTextfield forMaxX:maxViewX];
1619 }
1620 }
1621
1622 // Scans through all buttons from left to right, calculating from scratch where
1623 // they should be based on the preceding widths, until it finds the one
1624 // requested.
1625 // Returns NSZeroRect if there is no such button in the bookmark bar.
1626 // Enables you to work out where a button will end up when it is done animating.
1627 - (NSRect)finalRectOfButton:(BookmarkButton*)wantedButton {
1628 CGFloat left = bookmarks::kBookmarkLeftMargin;
1629 NSRect buttonFrame = NSZeroRect;
1630
1631 // Draw the apps bookmark if needed.
1632 if (![appsPageShortcutButton_ isHidden]) {
1633 left = NSMaxX([appsPageShortcutButton_ frame]) +
1634 bookmarks::kBookmarkHorizontalPadding;
1635 }
1636
1637 // Draw the managed bookmarks folder if needed.
1638 if (![managedBookmarksButton_ isHidden]) {
1639 left = NSMaxX([managedBookmarksButton_ frame]) +
1640 bookmarks::kBookmarkHorizontalPadding;
1641 }
1642
1643 // Draw the supervised bookmarks folder if needed.
1644 if (![supervisedBookmarksButton_ isHidden]) {
1645 left = NSMaxX([supervisedBookmarksButton_ frame]) +
1646 bookmarks::kBookmarkHorizontalPadding;
1647 }
1648
1649 for (NSButton* button in buttons_.get()) {
1650 // Hidden buttons get no space.
1651 if ([button isHidden])
1652 continue;
1653 buttonFrame = [button frame];
1654 buttonFrame.origin.x = left;
1655 left += buttonFrame.size.width + bookmarks::kBookmarkHorizontalPadding;
1656 if (button == wantedButton)
1657 return buttonFrame;
1658 }
1659 return NSZeroRect;
1660 }
1661
1662 // Calculates the final position of the last button in the bar.
1663 // We can't just use [[self buttons] lastObject] frame] because the button
1664 // may be animating currently.
1665 - (NSRect)finalRectOfLastButton {
1666 return [self finalRectOfButton:[[self buttons] lastObject]];
1667 }
1668
1669 - (CGFloat)buttonViewMaxXWithOffTheSideButtonIsVisible:(BOOL)visible {
1670 CGFloat maxViewX = NSMaxX([buttonView_ bounds]);
1671 // If necessary, pull in the width to account for the Other Bookmarks button.
1672 if ([self setOtherBookmarksButtonVisibility]) {
1673 maxViewX = [otherBookmarksButton_ frame].origin.x -
1674 bookmarks::kBookmarkRightMargin;
1675 }
1676
1677 [self positionRightSideButtons];
1678 // If we're already overflowing, then we need to account for the chevron.
1679 if (visible) {
1680 maxViewX =
1681 [offTheSideButton_ frame].origin.x - bookmarks::kBookmarkRightMargin;
1682 }
1683
1684 return maxViewX;
1685 }
1686
1687 - (void)redistributeButtonsOnBarAsNeeded {
1688 const BookmarkNode* node = bookmarkModel_->bookmark_bar_node();
1689 NSInteger barCount = node->child_count();
1690
1691 // Determine the current maximum extent of the visible buttons.
1692 [self positionRightSideButtons];
1693 BOOL offTheSideButtonVisible = (barCount > displayedButtonCount_);
1694 CGFloat maxViewX = [self buttonViewMaxXWithOffTheSideButtonIsVisible:
1695 offTheSideButtonVisible];
1696
1697 // As a result of pasting or dragging, the bar may now have more buttons
1698 // than will fit so remove any which overflow. They will be shown in
1699 // the off-the-side folder.
1700 while (displayedButtonCount_ > 0) {
1701 BookmarkButton* button = [buttons_ lastObject];
1702 if (NSMaxX([self finalRectOfLastButton]) < maxViewX)
1703 break;
1704 [buttons_ removeLastObject];
1705 [button setDelegate:nil];
1706 [button removeFromSuperview];
1707 --displayedButtonCount_;
1708 // Account for the fact that the chevron might now be visible.
1709 if (!offTheSideButtonVisible) {
1710 offTheSideButtonVisible = YES;
1711 maxViewX = [self buttonViewMaxXWithOffTheSideButtonIsVisible:YES];
1712 }
1713 }
1714
1715 // As a result of cutting, deleting and dragging, the bar may now have room
1716 // for more buttons.
1717 int xOffset;
1718 if (displayedButtonCount_ > 0) {
1719 xOffset = NSMaxX([self finalRectOfLastButton]);
1720 } else if (![managedBookmarksButton_ isHidden]) {
1721 xOffset = NSMaxX([managedBookmarksButton_ frame]) +
1722 bookmarks::kBookmarkHorizontalPadding;
1723 } else if (![supervisedBookmarksButton_ isHidden]) {
1724 xOffset = NSMaxX([supervisedBookmarksButton_ frame]) +
1725 bookmarks::kBookmarkHorizontalPadding;
1726 } else if (![appsPageShortcutButton_ isHidden]) {
1727 xOffset = NSMaxX([appsPageShortcutButton_ frame]) +
1728 bookmarks::kBookmarkHorizontalPadding;
1729 } else {
1730 xOffset = bookmarks::kBookmarkLeftMargin -
1731 bookmarks::kBookmarkHorizontalPadding;
1732 }
1733 for (int i = displayedButtonCount_; i < barCount; ++i) {
1734 const BookmarkNode* child = node->GetChild(i);
1735 BookmarkButton* button = [self buttonForNode:child xOffset:&xOffset];
1736 // If we're testing against the last possible button then account
1737 // for the chevron no longer needing to be shown.
1738 if (i == barCount - 1)
1739 maxViewX = [self buttonViewMaxXWithOffTheSideButtonIsVisible:NO];
1740 if (NSMaxX([button frame]) > maxViewX) {
1741 [button setDelegate:nil];
1742 break;
1743 }
1744 ++displayedButtonCount_;
1745 [buttons_ addObject:button];
1746 [buttonView_ addSubview:button];
1747 }
1748
1749 // While we're here, adjust the horizontal width and the visibility
1750 // of the "For quick access" and "Import bookmarks..." text fields.
1751 if (![buttons_ count])
1752 [self adjustNoItemContainerForMaxX:maxViewX];
1403 } 1753 }
1404 1754
1405 #pragma mark Private Methods Exposed for Testing 1755 #pragma mark Private Methods Exposed for Testing
1406 1756
1407 - (BookmarkBarView*)buttonView { 1757 - (BookmarkBarView*)buttonView {
1408 return buttonView_; 1758 return buttonView_;
1409 } 1759 }
1410 1760
1411 - (NSMutableArray*)buttons { 1761 - (NSMutableArray*)buttons {
1412 return buttons_.get(); 1762 return buttons_.get();
1413 } 1763 }
1414 1764
1765 - (BOOL)offTheSideButtonIsHidden {
1766 return [offTheSideButton_ isHidden];
1767 }
1768
1769 - (BOOL)appsPageShortcutButtonIsHidden {
1770 return [appsPageShortcutButton_ isHidden];
1771 }
1772
1415 - (BookmarkButton*)otherBookmarksButton { 1773 - (BookmarkButton*)otherBookmarksButton {
1416 return otherBookmarksButton_.get(); 1774 return otherBookmarksButton_.get();
1417 } 1775 }
1418 1776
1419 - (BookmarkButton*)managedBookmarksButton {
1420 return managedBookmarksButton_.get();
1421 }
1422
1423 - (BookmarkButton*)supervisedBookmarksButton {
1424 return supervisedBookmarksButton_.get();
1425 }
1426
1427 - (BookmarkBarFolderController*)folderController { 1777 - (BookmarkBarFolderController*)folderController {
1428 return folderController_; 1778 return folderController_;
1429 } 1779 }
1430 1780
1431 - (id)folderTarget { 1781 - (id)folderTarget {
1432 return folderTarget_.get(); 1782 return folderTarget_.get();
1433 } 1783 }
1434 1784
1785 - (int)displayedButtonCount {
1786 return displayedButtonCount_;
1787 }
1788
1789 // Delete all buttons (bookmarks, chevron, "other bookmarks") from the
1790 // bookmark bar; reset knowledge of bookmarks.
1791 - (void)clearBookmarkBar {
1792 [self stopPulsingBookmarkNode];
1793 for (BookmarkButton* button in buttons_.get()) {
1794 [button setDelegate:nil];
1795 [button removeFromSuperview];
1796 }
1797 [buttons_ removeAllObjects];
1798 displayedButtonCount_ = 0;
1799
1800 // Make sure there are no stale pointers in the pasteboard. This
1801 // can be important if a bookmark is deleted (via bookmark sync)
1802 // while in the middle of a drag. The "drag completed" code
1803 // (e.g. [BookmarkBarView performDragOperationForBookmarkButton:]) is
1804 // careful enough to bail if there is no data found at "drop" time.
1805 [[NSPasteboard pasteboardWithName:NSDragPboard] clearContents];
1806 }
1807
1435 // Return an autoreleased NSCell suitable for a bookmark button. 1808 // Return an autoreleased NSCell suitable for a bookmark button.
1436 // TODO(jrg): move much of the cell config into the BookmarkButtonCell class. 1809 // TODO(jrg): move much of the cell config into the BookmarkButtonCell class.
1437 - (BookmarkButtonCell*)cellForBookmarkNode:(const BookmarkNode*)node { 1810 - (BookmarkButtonCell*)cellForBookmarkNode:(const BookmarkNode*)node {
1438 BOOL darkTheme = [[[self view] window] hasDarkTheme]; 1811 BOOL darkTheme = [[[self view] window] hasDarkTheme];
1439 NSImage* image = node ? [self faviconForNode:node forADarkTheme:darkTheme] 1812 NSImage* image = node ? [self faviconForNode:node forADarkTheme:darkTheme]
1440 : nil; 1813 : nil;
1441 BookmarkButtonCell* cell = 1814 BookmarkButtonCell* cell =
1442 [BookmarkButtonCell buttonCellForNode:node 1815 [BookmarkButtonCell buttonCellForNode:node
1443 text:nil 1816 text:nil
1444 image:image 1817 image:image
(...skipping 17 matching lines...) Expand all
1462 menuController:contextMenuController_]; 1835 menuController:contextMenuController_];
1463 [cell setTag:kMaterialStandardButtonTypeWithLimitedClickFeedback]; 1836 [cell setTag:kMaterialStandardButtonTypeWithLimitedClickFeedback];
1464 [cell setHighlightsBy:NSNoCellMask]; 1837 [cell setHighlightsBy:NSNoCellMask];
1465 1838
1466 // Note: a quirk of setting a cell's text color is that it won't work 1839 // Note: a quirk of setting a cell's text color is that it won't work
1467 // until the cell is associated with a button, so we can't theme the cell yet. 1840 // until the cell is associated with a button, so we can't theme the cell yet.
1468 1841
1469 return cell; 1842 return cell;
1470 } 1843 }
1471 1844
1845 // Returns a frame appropriate for the given bookmark cell, suitable
1846 // for creating an NSButton that will contain it. |xOffset| is the X
1847 // offset for the frame; it is increased to be an appropriate X offset
1848 // for the next button.
1849 - (NSRect)frameForBookmarkButtonFromCell:(NSCell*)cell
1850 xOffset:(int*)xOffset {
1851 DCHECK(xOffset);
1852 NSRect bounds = [buttonView_ bounds];
1853 bounds.size.height = bookmarks::kBookmarkButtonHeight;
1854
1855 NSRect frame = NSInsetRect(bounds,
1856 bookmarks::kBookmarkHorizontalPadding,
1857 bookmarks::kBookmarkVerticalPadding);
1858 frame.size.width = [self widthForBookmarkButtonCell:cell];
1859
1860 // Add an X offset based on what we've already done
1861 frame.origin.x += *xOffset;
1862
1863 // And up the X offset for next time.
1864 *xOffset = NSMaxX(frame);
1865
1866 return frame;
1867 }
1868
1869 // A bookmark button's contents changed. Check for growth
1870 // (e.g. increase the width up to the maximum). If we grew, move
1871 // other bookmark buttons over.
1872 - (void)checkForBookmarkButtonGrowth:(NSButton*)changedButton {
1873 NSRect frame = [changedButton frame];
1874 CGFloat desiredSize = [self widthForBookmarkButtonCell:[changedButton cell]];
1875 CGFloat delta = desiredSize - frame.size.width;
1876 if (delta) {
1877 frame.size.width = desiredSize;
1878 [changedButton setFrame:frame];
1879 for (NSButton* button in buttons_.get()) {
1880 NSRect buttonFrame = [button frame];
1881 if (buttonFrame.origin.x > frame.origin.x) {
1882 buttonFrame.origin.x += delta;
1883 [button setFrame:buttonFrame];
1884 }
1885 }
1886 }
1887 // We may have just crossed a threshold to enable the off-the-side
1888 // button.
1889 [self configureOffTheSideButtonContentsAndVisibility];
1890 }
1891
1472 // Called when our controlled frame has changed size. 1892 // Called when our controlled frame has changed size.
1473 - (void)frameDidChange { 1893 - (void)frameDidChange {
1474 [self rebuildLayoutWithAnimated:NO]; 1894 if (!bookmarkModel_->loaded())
1895 return;
1896 [self updateTheme:[[[self view] window] themeProvider]];
1897 [self reconfigureBookmarkBar];
1475 } 1898 }
1476 1899
1477 // Adapt appearance of buttons to the current theme. Called after 1900 // Adapt appearance of buttons to the current theme. Called after
1478 // theme changes, or when our view is added to the view hierarchy. 1901 // theme changes, or when our view is added to the view hierarchy.
1479 // Oddly, the view pings us instead of us pinging our view. This is 1902 // Oddly, the view pings us instead of us pinging our view. This is
1480 // because our trigger is an [NSView viewWillMoveToWindow:], which the 1903 // because our trigger is an [NSView viewWillMoveToWindow:], which the
1481 // controller doesn't normally know about. Otherwise we don't have 1904 // controller doesn't normally know about. Otherwise we don't have
1482 // access to the theme before we know what window we will be on. 1905 // access to the theme before we know what window we will be on.
1483 - (void)updateTheme:(const ui::ThemeProvider*)themeProvider { 1906 - (void)updateTheme:(const ui::ThemeProvider*)themeProvider {
1484 if (!themeProvider) 1907 if (!themeProvider)
(...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after
1559 1982
1560 // Find something like std::is_between<T>? I can't believe one doesn't exist. 1983 // Find something like std::is_between<T>? I can't believe one doesn't exist.
1561 static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) { 1984 static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) {
1562 return ((value >= low) && (value <= high)); 1985 return ((value >= low) && (value <= high));
1563 } 1986 }
1564 1987
1565 // Return the proposed drop target for a hover open button from the 1988 // Return the proposed drop target for a hover open button from the
1566 // given array, or nil if none. We use this for distinguishing 1989 // given array, or nil if none. We use this for distinguishing
1567 // between a hover-open candidate or drop-indicator draw. 1990 // between a hover-open candidate or drop-indicator draw.
1568 // Helper for buttonForDroppingOnAtPoint:. 1991 // Helper for buttonForDroppingOnAtPoint:.
1992 // Get UI review on "middle half" ness.
1993 // http://crbug.com/36276
1569 - (BookmarkButton*)buttonForDroppingOnAtPoint:(NSPoint)point 1994 - (BookmarkButton*)buttonForDroppingOnAtPoint:(NSPoint)point
1570 fromArray:(NSArray*)array { 1995 fromArray:(NSArray*)array {
1571 // Ensure buttons are scanned left to right. 1996 for (BookmarkButton* button in array) {
1572 id<NSFastEnumeration> buttonsToCheck =
1573 cocoa_l10n_util::ShouldDoExperimentalRTLLayout()
1574 ? [array reverseObjectEnumerator]
1575 : [array objectEnumerator];
1576 for (BookmarkButton* button in buttonsToCheck) {
1577 // Hidden buttons can overlap valid visible buttons, just ignore. 1997 // Hidden buttons can overlap valid visible buttons, just ignore.
1578 if ([button isHidden]) 1998 if ([button isHidden])
1579 continue; 1999 continue;
1580 // Break early if we've gone too far. 2000 // Break early if we've gone too far.
1581 if ((NSMinX([button frame]) > point.x) || (![button superview])) 2001 if ((NSMinX([button frame]) > point.x) || (![button superview]))
1582 return nil; 2002 return nil;
1583 // Careful -- this only applies to the bar with horiz buttons. 2003 // Careful -- this only applies to the bar with horiz buttons.
1584 // Intentionally NOT using NSPointInRect() so that scrolling into 2004 // Intentionally NOT using NSPointInRect() so that scrolling into
1585 // a submenu doesn't cause it to be closed. 2005 // a submenu doesn't cause it to be closed.
1586 if (ValueInRangeInclusive(NSMinX([button frame]), 2006 if (ValueInRangeInclusive(NSMinX([button frame]),
(...skipping 27 matching lines...) Expand all
1614 - (BookmarkButton*)buttonForDroppingOnAtPoint:(NSPoint)point { 2034 - (BookmarkButton*)buttonForDroppingOnAtPoint:(NSPoint)point {
1615 point = [[self view] convertPoint:point 2035 point = [[self view] convertPoint:point
1616 fromView:[[[self view] window] contentView]]; 2036 fromView:[[[self view] window] contentView]];
1617 2037
1618 // If there's a hover button, return it if the point is within its bounds. 2038 // If there's a hover button, return it if the point is within its bounds.
1619 // Since the logic in -buttonForDroppingOnAtPoint:fromArray: only matches a 2039 // Since the logic in -buttonForDroppingOnAtPoint:fromArray: only matches a
1620 // button when the point is over the middle half, this is needed to prevent 2040 // button when the point is over the middle half, this is needed to prevent
1621 // the button's folder being closed if the mouse temporarily leaves the 2041 // the button's folder being closed if the mouse temporarily leaves the
1622 // middle half but is still within the button bounds. 2042 // middle half but is still within the button bounds.
1623 if (hoverButton_ && NSPointInRect(point, [hoverButton_ frame])) 2043 if (hoverButton_ && NSPointInRect(point, [hoverButton_ frame]))
1624 return hoverButton_.get(); 2044 return hoverButton_.get();
1625 2045
1626 BookmarkButton* button = [self buttonForDroppingOnAtPoint:point 2046 BookmarkButton* button = [self buttonForDroppingOnAtPoint:point
1627 fromArray:buttons_.get()]; 2047 fromArray:buttons_.get()];
1628 // One more chance -- try "Other Bookmarks" and "off the side" (if visible). 2048 // One more chance -- try "Other Bookmarks" and "off the side" (if visible).
1629 // This is different than BookmarkBarFolderController. 2049 // This is different than BookmarkBarFolderController.
1630 if (!button) { 2050 if (!button) {
1631 NSMutableArray* array = [NSMutableArray array]; 2051 NSMutableArray* array = [NSMutableArray array];
1632 if (layout_.IsOffTheSideButtonVisible()) 2052 if (![self offTheSideButtonIsHidden])
1633 [array addObject:offTheSideButton_]; 2053 [array addObject:offTheSideButton_];
1634 [array addObject:otherBookmarksButton_]; 2054 [array addObject:otherBookmarksButton_];
1635 button = [self buttonForDroppingOnAtPoint:point 2055 button = [self buttonForDroppingOnAtPoint:point
1636 fromArray:array]; 2056 fromArray:array];
1637 } 2057 }
1638 return button; 2058 return button;
1639 } 2059 }
1640 2060
1641 // Returns the index in the model for a drag to the location given by
1642 // |point|. This is determined by finding the first button before the center
1643 // of which |point| falls, scanning front to back. Note that, currently, only
1644 // the x-coordinate of |point| is considered. Though not currently implemented,
1645 // we may check for errors, in which case this would return negative value;
1646 // callers should check for this.
1647 - (int)indexForDragToPoint:(NSPoint)point { 2061 - (int)indexForDragToPoint:(NSPoint)point {
2062 // TODO(jrg): revisit position info based on UI team feedback.
2063 // dropLocation is in bar local coordinates.
1648 NSPoint dropLocation = 2064 NSPoint dropLocation =
1649 [[self view] convertPoint:point 2065 [[self view] convertPoint:point
1650 fromView:[[[self view] window] contentView]]; 2066 fromView:[[[self view] window] contentView]];
1651 2067 BookmarkButton* buttonToTheRightOfDraggedButton = nil;
1652 BOOL isRTL = cocoa_l10n_util::ShouldDoExperimentalRTLLayout(); 2068 for (BookmarkButton* button in buttons_.get()) {
1653 for (size_t i = 0; i < layout_.VisibleButtonCount(); i++) {
1654 BookmarkButton* button = [buttons_ objectAtIndex:i];
1655 CGFloat midpoint = NSMidX([button frame]); 2069 CGFloat midpoint = NSMidX([button frame]);
1656 if (isRTL ? dropLocation.x >= midpoint : dropLocation.x <= midpoint) { 2070 if (dropLocation.x <= midpoint) {
1657 return i; 2071 buttonToTheRightOfDraggedButton = button;
2072 break;
1658 } 2073 }
1659 } 2074 }
1660 // No buttons left to insert the dragged button before, so it 2075 if (buttonToTheRightOfDraggedButton) {
1661 // goes at the end. 2076 const BookmarkNode* afterNode =
1662 return layout_.VisibleButtonCount(); 2077 [buttonToTheRightOfDraggedButton bookmarkNode];
2078 DCHECK(afterNode);
2079 int index = afterNode->parent()->GetIndexOf(afterNode);
2080 // Make sure we don't get confused by buttons which aren't visible.
2081 return std::min(index, displayedButtonCount_);
2082 }
2083
2084 // If nothing is to my right I am at the end!
2085 return displayedButtonCount_;
1663 } 2086 }
1664 2087
1665 // Informs the bar that something representing the bookmark at
1666 // |sourceNode| was dragged into the bar.
1667 // |point| is in the base coordinate system of the destination window;
1668 // it comes from an id<NSDraggingInfo>. |copy| is YES if a copy is to be
1669 // made and inserted into the new location while leaving the bookmark in
1670 // the old location, otherwise move the bookmark by removing from its old
1671 // location and inserting into the new location.
1672 // Returns whether the drag was allowed.
1673 // TODO(mrossetti,jrg): Yet more duplicated code. 2088 // TODO(mrossetti,jrg): Yet more duplicated code.
1674 // http://crbug.com/35966 2089 // http://crbug.com/35966
1675 - (BOOL)dragBookmark:(const BookmarkNode*)sourceNode 2090 - (BOOL)dragBookmark:(const BookmarkNode*)sourceNode
1676 to:(NSPoint)point 2091 to:(NSPoint)point
1677 copy:(BOOL)copy { 2092 copy:(BOOL)copy {
1678 DCHECK(sourceNode); 2093 DCHECK(sourceNode);
1679 // Drop destination. 2094 // Drop destination.
1680 const BookmarkNode* destParent = NULL; 2095 const BookmarkNode* destParent = NULL;
1681 int destIndex = 0; 2096 int destIndex = 0;
1682 2097
(...skipping 26 matching lines...) Expand all
1709 [self closeFolderAndStopTrackingMenus]; 2124 [self closeFolderAndStopTrackingMenus];
1710 2125
1711 // Movement of a node triggers observers (like us) to rebuild the 2126 // Movement of a node triggers observers (like us) to rebuild the
1712 // bar so we don't have to do so explicitly. 2127 // bar so we don't have to do so explicitly.
1713 2128
1714 return YES; 2129 return YES;
1715 } 2130 }
1716 2131
1717 - (void)draggingEnded:(id<NSDraggingInfo>)info { 2132 - (void)draggingEnded:(id<NSDraggingInfo>)info {
1718 [self closeFolderAndStopTrackingMenus]; 2133 [self closeFolderAndStopTrackingMenus];
1719 layout_ = {}; // Force layout. 2134 [[BookmarkButton draggedButton] setHidden:NO];
1720 [self rebuildLayoutWithAnimated:YES]; 2135 [self resetAllButtonPositionsWithAnimation:YES];
1721 } 2136 }
1722 2137
1723 // Set insertionPos_ and hasInsertionPos_, and make insertion space for a 2138 // Set insertionPos_ and hasInsertionPos_, and make insertion space for a
1724 // hypothetical drop with the new button having a left edge of |where|. 2139 // hypothetical drop with the new button having a left edge of |where|.
2140 // Gets called only by our view.
1725 - (void)setDropInsertionPos:(CGFloat)where { 2141 - (void)setDropInsertionPos:(CGFloat)where {
1726 if (hasInsertionPos_ && where == insertionPos_) { 2142 if (!hasInsertionPos_ || where != insertionPos_) {
1727 return; 2143 insertionPos_ = where;
1728 } 2144 hasInsertionPos_ = YES;
1729 insertionPos_ = where; 2145 CGFloat left;
1730 hasInsertionPos_ = YES; 2146 if (![supervisedBookmarksButton_ isHidden]) {
1731 CGFloat paddingWidth = bookmarks::kDefaultBookmarkWidth; 2147 left = NSMaxX([supervisedBookmarksButton_ frame]) +
1732 BookmarkButton* draggedButton = [BookmarkButton draggedButton]; 2148 bookmarks::kBookmarkHorizontalPadding;
1733 BOOL draggedButtonIsOnBar = NO; 2149 } else if (![managedBookmarksButton_ isHidden]) {
1734 int64_t draggedButtonNodeId; 2150 left = NSMaxX([managedBookmarksButton_ frame]) +
1735 CGFloat draggedButtonOffset; 2151 bookmarks::kBookmarkHorizontalPadding;
1736 if (draggedButton) { 2152 } else if (![appsPageShortcutButton_ isHidden]) {
1737 paddingWidth = std::min(bookmarks::kDefaultBookmarkWidth, 2153 left = NSMaxX([appsPageShortcutButton_ frame]) +
1738 NSWidth([draggedButton frame])); 2154 bookmarks::kBookmarkHorizontalPadding;
1739 draggedButtonNodeId = [draggedButton bookmarkNode]->id(); 2155 } else {
1740 auto offsetIt = layout_.button_offsets.find(draggedButtonNodeId); 2156 left = bookmarks::kBookmarkLeftMargin;
1741 if (offsetIt != layout_.button_offsets.end()) {
1742 draggedButtonIsOnBar = YES;
1743 draggedButtonOffset = (*offsetIt).second;
1744 } 2157 }
1745 } 2158 CGFloat paddingWidth = bookmarks::kDefaultBookmarkWidth;
1746 paddingWidth += bookmarks::kBookmarkHorizontalPadding; 2159 BookmarkButton* draggedButton = [BookmarkButton draggedButton];
1747 BOOL isRTL = cocoa_l10n_util::ShouldDoExperimentalRTLLayout(); 2160 if (draggedButton) {
1748 2161 paddingWidth = std::min(bookmarks::kDefaultBookmarkWidth,
1749 // If the button being dragged is not currently on the bar 2162 NSWidth([draggedButton frame]));
1750 // (for example, a drag from the URL bar, a link on the desktop, 2163 }
1751 // a button inside a menu, etc.), buttons in front of the drag position 2164 // Put all the buttons where they belong, with all buttons to the right
1752 // (to the right in LTR, to the left in RTL), should make room for it. 2165 // of the insertion point shuffling right to make space for it.
1753 // Otherwise: 2166 [NSAnimationContext beginGrouping];
1754 // If a given button was to the left of the dragged button's original 2167 [[NSAnimationContext currentContext]
1755 // position, but is to the right of the drag position, or 2168 setDuration:kDragAndDropAnimationDuration];
1756 // if it was to the right of the dragged button's original position and 2169 for (NSButton* button in buttons_.get()) {
1757 // is to the left of the drag position, make room. 2170 // Hidden buttons get no space.
1758 // TODO(lgrey): Extract ScopedNSAnimationContextGroup 2171 if ([button isHidden])
1759 // from the tab strip controller and use it on all of these.
1760 [NSAnimationContext beginGrouping];
1761 [[NSAnimationContext currentContext]
1762 setDuration:kDragAndDropAnimationDuration];
1763
1764 for (BookmarkButton* button in buttons_.get()) {
1765 CGRect buttonFrame = [button frame];
1766 int64_t nodeId = [button bookmarkNode]->id();
1767 CGFloat offset = layout_.button_offsets[nodeId];
1768 CGFloat buttonEdge = isRTL ? NSWidth([buttonView_ frame]) - offset : offset;
1769
1770 if (draggedButtonIsOnBar) {
1771 if (nodeId == draggedButtonNodeId)
1772 continue; 2172 continue;
1773 BOOL wasBefore = offset < draggedButtonOffset; 2173 NSRect buttonFrame = [button frame];
1774 BOOL isBefore = isRTL ? buttonEdge > where : buttonEdge < where; 2174 buttonFrame.origin.x = left;
1775 if (isBefore && !wasBefore) { 2175 // Update "left" for next time around.
1776 offset -= paddingWidth; 2176 left += buttonFrame.size.width;
1777 } else if (!isBefore && wasBefore) { 2177 if (left > insertionPos_)
1778 offset += paddingWidth; 2178 buttonFrame.origin.x += paddingWidth;
1779 } 2179 left += bookmarks::kBookmarkHorizontalPadding;
1780 } else { 2180 if (innerContentAnimationsEnabled_)
1781 if (isRTL ? (where > buttonEdge) 2181 [[button animator] setFrame:buttonFrame];
1782 : (where < offset + NSWidth(buttonFrame))) { 2182 else
1783 offset += paddingWidth; 2183 [button setFrame:buttonFrame];
1784 }
1785 } 2184 }
1786 2185 [NSAnimationContext endGrouping];
1787 [self applyXOffset:offset
1788 toButton:button
1789 animated:innerContentAnimationsEnabled_];
1790 }
1791
1792 [NSAnimationContext endGrouping];
1793 }
1794
1795 - (BookmarkBarLayout)layoutFromCurrentState {
1796 BookmarkBarLayout layout = {};
1797
1798 const BookmarkNode* node = bookmarkModel_->bookmark_bar_node();
1799 CGFloat viewWidth = NSWidth([buttonView_ frame]);
1800 CGFloat xOffset = bookmarks::kBookmarkLeftMargin;
1801 CGFloat maxX = viewWidth - bookmarks::kBookmarkHorizontalPadding;
1802
1803 layout.visible_elements = bookmarks::kVisibleElementsMaskNone;
1804 layout.max_x = maxX;
1805
1806 // Lay out "extra" buttons (apps, managed, supervised, "Other").
1807 if (chrome::ShouldShowAppsShortcutInBookmarkBar(browser_->profile())) {
1808 layout.visible_elements |= bookmarks::kVisibleElementsMaskAppsButton;
1809 layout.apps_button_offset = xOffset;
1810 xOffset += NSWidth([appsPageShortcutButton_ frame]) +
1811 bookmarks::kBookmarkHorizontalPadding;
1812 }
1813 if (!managedBookmarkService_->managed_node()->empty()) {
1814 layout.visible_elements |=
1815 bookmarks::kVisibleElementsMaskManagedBookmarksButton;
1816 layout.managed_bookmarks_button_offset = xOffset;
1817 xOffset += NSWidth([managedBookmarksButton_ frame]) +
1818 bookmarks::kBookmarkHorizontalPadding;
1819 }
1820 if (!managedBookmarkService_->supervised_node()->empty()) {
1821 layout.visible_elements |=
1822 bookmarks::kVisibleElementsMaskSupervisedBookmarksButton;
1823 layout.supervised_bookmarks_button_offset = xOffset;
1824 xOffset += NSWidth([supervisedBookmarksButton_ frame]) +
1825 bookmarks::kBookmarkHorizontalPadding;
1826 }
1827 if (!bookmarkModel_->other_node()->empty()) {
1828 layout.visible_elements |=
1829 bookmarks::kVisibleElementsMaskOtherBookmarksButton;
1830 maxX -= NSWidth([otherBookmarksButton_ frame]);
1831 layout.other_bookmarks_button_offset = maxX;
1832 }
1833
1834 // Lay out empty state ("no items" label, import bookmarks button.)
1835 if (node->empty()) {
1836 CGFloat roomForTextField =
1837 maxX - xOffset + bookmarks::kInitialNoItemTextFieldXOrigin;
1838 if (roomForTextField >= kNoItemElementMinWidth) {
1839 layout.visible_elements |= bookmarks::kVisibleElementsMaskNoItemTextField;
1840 xOffset += bookmarks::kInitialNoItemTextFieldXOrigin;
1841 layout.no_item_textfield_offset = xOffset;
1842 layout.no_item_textfield_width =
1843 std::min(maxX - xOffset, originalNoItemTextFieldWidth_);
1844 xOffset +=
1845 layout.no_item_textfield_width + originalNoItemInterelementPadding_;
1846
1847 // Does the "import bookmarks" button fit?
1848 if (maxX - xOffset >= kNoItemElementMinWidth) {
1849 layout.visible_elements |=
1850 bookmarks::kVisibleElementsMaskImportBookmarksButton;
1851 layout.import_bookmarks_button_offset = xOffset;
1852 layout.import_bookmarks_button_width =
1853 std::min(maxX - xOffset, originalImportBookmarksButtonWidth_);
1854 }
1855 }
1856 } else {
1857 // Lay out bookmark buttons and "off-the-side" chevron.
1858 CGFloat offTheSideButtonWidth = NSWidth([offTheSideButton_ frame]);
1859 int buttonCount = node->child_count();
1860 for (int i = 0; i < buttonCount; i++) {
1861 const BookmarkNode* buttonNode = node->GetChild(i);
1862 CGFloat widthOfButton = [self widthOfButtonForNode:buttonNode];
1863 // If it's the last button, we just need to ensure that it can fit.
1864 // If not, we need to be able to fit both it and the chevron.
1865 CGFloat widthToCheck = i == buttonCount - 1
1866 ? widthOfButton
1867 : widthOfButton + offTheSideButtonWidth;
1868 if (xOffset + widthToCheck > maxX) {
1869 layout.visible_elements |=
1870 bookmarks::kVisibleElementsMaskOffTheSideButton;
1871 layout.off_the_side_button_offset = maxX - offTheSideButtonWidth;
1872 break;
1873 }
1874 layout.button_offsets.insert({buttonNode->id(), xOffset});
1875 xOffset += widthOfButton + bookmarks::kBookmarkHorizontalPadding;
1876 }
1877 }
1878 return layout;
1879 }
1880
1881 - (void)rebuildLayoutWithAnimated:(BOOL)animated {
1882 if (!bookmarkModel_->loaded())
1883 return;
1884
1885 BookmarkBarLayout layout = [self layoutFromCurrentState];
1886 if (layout_ != layout) {
1887 layout_ = std::move(layout);
1888 [self applyLayout:layout_ animated:animated];
1889 } 2186 }
1890 } 2187 }
1891 2188
1892 - (void)applyLayout:(const BookmarkBarLayout&)layout animated:(BOOL)animated { 2189 // Put all visible bookmark bar buttons in their normal locations, either with
1893 if (!bookmarkModel_->loaded()) 2190 // or without animation according to the |animate| flag.
1894 return; 2191 // This is generally useful, so is called from various places internally.
2192 - (void)resetAllButtonPositionsWithAnimation:(BOOL)animate {
1895 2193
1896 if (folderController_) 2194 // Position the apps bookmark if needed.
1897 [self closeAllBookmarkFolders]; 2195 CGFloat left = bookmarks::kBookmarkLeftMargin;
1898 [self stopPulsingBookmarkNode]; 2196 if (![appsPageShortcutButton_ isHidden]) {
1899 2197 int xOffset = bookmarks::kBookmarkLeftMargin -
1900 // Hide or show "extra" buttons and position if necessary. 2198 bookmarks::kBookmarkHorizontalPadding;
1901 [self applyXOffset:layout.apps_button_offset 2199 NSRect frame =
1902 toButton:appsPageShortcutButton_ 2200 [self frameForBookmarkButtonFromCell:[appsPageShortcutButton_ cell]
1903 animated:NO]; 2201 xOffset:&xOffset];
1904 [appsPageShortcutButton_ setHidden:!layout.IsAppsButtonVisible()]; 2202 [appsPageShortcutButton_ setFrame:frame];
1905 2203 left = xOffset + bookmarks::kBookmarkHorizontalPadding;
1906 [self applyXOffset:layout.managed_bookmarks_button_offset
1907 toButton:managedBookmarksButton_
1908 animated:NO];
1909 [managedBookmarksButton_ setHidden:!layout.IsManagedBookmarksButtonVisible()];
1910
1911 [self applyXOffset:layout.supervised_bookmarks_button_offset
1912 toButton:supervisedBookmarksButton_
1913 animated:NO];
1914 [supervisedBookmarksButton_
1915 setHidden:!layout.IsSupervisedBookmarksButtonVisible()];
1916
1917 [self applyXOffset:layout.other_bookmarks_button_offset
1918 toButton:otherBookmarksButton_
1919 animated:NO];
1920 [otherBookmarksButton_ setHidden:!layout.IsOtherBookmarksButtonVisible()];
1921
1922 [self applyXOffset:layout.off_the_side_button_offset
1923 toButton:offTheSideButton_
1924 animated:NO];
1925 [offTheSideButton_ setHidden:!layout.IsOffTheSideButtonVisible()];
1926 if (layout.IsOffTheSideButtonVisible()) {
1927 [[offTheSideButton_ cell]
1928 setStartingChildIndex:layout_.VisibleButtonCount()];
1929 } 2204 }
1930 2205
1931 // Hide or show empty state and position if necessary. 2206 // Position the managed bookmarks folder if needed.
1932 [[buttonView_ noItemTextField] setHidden:!layout.IsNoItemTextFieldVisible()]; 2207 if (![managedBookmarksButton_ isHidden]) {
1933 [[buttonView_ importBookmarksButton] 2208 int xOffset = left;
1934 setHidden:!layout.IsImportBookmarksButtonVisible()]; 2209 NSRect frame =
1935 2210 [self frameForBookmarkButtonFromCell:[managedBookmarksButton_ cell]
1936 if (layout.IsNoItemTextFieldVisible()) { 2211 xOffset:&xOffset];
1937 NSTextField* noItemTextField = [buttonView_ noItemTextField]; 2212 [managedBookmarksButton_ setFrame:frame];
1938 [noItemTextField setHidden:NO]; 2213 left = xOffset + bookmarks::kBookmarkHorizontalPadding;
1939 NSRect textFieldFrame = [noItemTextField frame];
1940 textFieldFrame.size.width = layout.no_item_textfield_width;
1941 if (cocoa_l10n_util::ShouldDoExperimentalRTLLayout()) {
1942 textFieldFrame.origin.x = NSWidth([buttonView_ frame]) -
1943 layout.no_item_textfield_offset -
1944 layout.no_item_textfield_width;
1945 } else {
1946 textFieldFrame.origin.x = layout.no_item_textfield_offset;
1947 }
1948 [noItemTextField setFrame:textFieldFrame];
1949
1950 if (layout.IsImportBookmarksButtonVisible()) {
1951 NSButton* importBookmarksButton = [buttonView_ importBookmarksButton];
1952 NSRect buttonFrame = [importBookmarksButton frame];
1953 buttonFrame.size.width = layout.import_bookmarks_button_width;
1954 if (cocoa_l10n_util::ShouldDoExperimentalRTLLayout()) {
1955 buttonFrame.origin.x = NSWidth([buttonView_ frame]) -
1956 layout.import_bookmarks_button_offset -
1957 layout.import_bookmarks_button_width;
1958 } else {
1959 buttonFrame.origin.x = layout.import_bookmarks_button_offset;
1960 }
1961 [importBookmarksButton setFrame:buttonFrame];
1962 }
1963 }
1964 // 1) Hide all buttons initially.
1965 // 2) Show all buttons in the layout.
1966 // 3) Remove any buttons that are still hidden (so: not in the layout)
1967 // from the node ID -> button map and add them to the reuse pool if
1968 // there's room.
1969 for (BookmarkButton* button in buttons_.get()) {
1970 [button setHidden:YES];
1971 }
1972 [buttons_ removeAllObjects];
1973
1974 const BookmarkNode* parentNode = bookmarkModel_->bookmark_bar_node();
1975 for (size_t i = 0; i < layout.VisibleButtonCount(); i++) {
1976 const BookmarkNode* node = parentNode->GetChild(i);
1977 DCHECK(node);
1978 BookmarkButton* button = [self buttonForNode:node];
1979 CGFloat offset = layout.button_offsets.at(node->id());
1980 [self applyXOffset:offset
1981 toButton:button
1982 animated:animated && innerContentAnimationsEnabled_];
1983 [buttons_ addObject:button];
1984 [button setHidden:NO];
1985 } 2214 }
1986 2215
1987 for (auto& item : nodeIdToButtonMap_) { 2216 // Position the supervised bookmarks folder if needed.
1988 if ([item.second isHidden]) { 2217 if (![supervisedBookmarksButton_ isHidden]) {
1989 if ([unusedButtonPool_ count] < kMaxReusePoolSize) { 2218 int xOffset = left;
1990 BookmarkButton* button = item.second.get(); 2219 NSRect frame =
1991 // Dragged buttons unhide themselves, so position it off-screen 2220 [self frameForBookmarkButtonFromCell:[supervisedBookmarksButton_ cell]
1992 // while it's in the reuse pool. 2221 xOffset:&xOffset];
1993 CGRect buttonFrame = [button frame]; 2222 [supervisedBookmarksButton_ setFrame:frame];
1994 buttonFrame.origin.x = -10000; 2223 left = xOffset + bookmarks::kBookmarkHorizontalPadding;
1995 [button setFrame:buttonFrame];
1996 [unusedButtonPool_ addObject:button];
1997 } else {
1998 [item.second setDelegate:nil];
1999 [item.second removeFromSuperview];
2000 }
2001
2002 nodeIdToButtonMap_.erase(item.first);
2003 }
2004 }
2005 const ui::ThemeProvider* themeProvider = [[[self view] window] themeProvider];
2006 [self updateTheme:themeProvider];
2007 }
2008
2009 - (void)applyXOffset:(CGFloat)offset
2010 toButton:(BookmarkButton*)button
2011 animated:(BOOL)animated {
2012 CGRect frame = [button frame];
2013 BOOL wasOffscreen = frame.origin.x < -200;
2014 if (cocoa_l10n_util::ShouldDoExperimentalRTLLayout()) {
2015 frame.origin.x = NSWidth([buttonView_ frame]) - offset - NSWidth(frame);
2016 } else {
2017 frame.origin.x = offset;
2018 } 2224 }
2019 2225
2020 // Buttons fresh from the reuse pool are kept offscreen. Animation should 2226 animate &= innerContentAnimationsEnabled_;
2021 // be disabled for them so they don't slide in. 2227
2022 if (animated && !wasOffscreen) { 2228 for (NSButton* button in buttons_.get()) {
2023 [[button animator] setFrame:frame]; 2229 // Hidden buttons get no space.
2024 } else { 2230 if ([button isHidden])
2025 [button setFrame:frame]; 2231 continue;
2232 NSRect buttonFrame = [button frame];
2233 buttonFrame.origin.x = left;
2234 left += buttonFrame.size.width + bookmarks::kBookmarkHorizontalPadding;
2235 if (animate)
2236 [[button animator] setFrame:buttonFrame];
2237 else
2238 [button setFrame:buttonFrame];
2026 } 2239 }
2027 } 2240 }
2028 2241
2029 - (CGFloat)widthOfButtonForNode:(const BookmarkNode*)node {
2030 // TODO(lgrey): Can we get this information without an actual image?
2031 NSImage* image = [self faviconForNode:node forADarkTheme:NO];
2032 CGFloat width = [BookmarkButtonCell cellWidthForNode:node image:image];
2033 return std::min(width, bookmarks::kDefaultBookmarkWidth);
2034 }
2035
2036 // Clear insertion flag, remove insertion space and put all visible bookmark 2242 // Clear insertion flag, remove insertion space and put all visible bookmark
2037 // bar buttons in their normal locations. 2243 // bar buttons in their normal locations.
2038 // Gets called only by our view. 2244 // Gets called only by our view.
2039 // TODO(lgrey): Is there a sane way to dedupe this with |setDropInsertionPos:|?
2040 - (void)clearDropInsertionPos { 2245 - (void)clearDropInsertionPos {
2041 if (!hasInsertionPos_) { 2246 if (hasInsertionPos_) {
2042 return; 2247 hasInsertionPos_ = NO;
2248 [self resetAllButtonPositionsWithAnimation:YES];
2043 } 2249 }
2044 hasInsertionPos_ = NO;
2045 BookmarkButton* draggedButton = [BookmarkButton draggedButton];
2046 if (!draggedButton) {
2047 [self applyLayout:layout_ animated:YES];
2048 return;
2049 }
2050 int64_t draggedButtonNodeId = [draggedButton bookmarkNode]->id();
2051 if (layout_.button_offsets.find(draggedButtonNodeId) ==
2052 layout_.button_offsets.end()) {
2053 [self applyLayout:layout_ animated:YES];
2054 return;
2055 }
2056 // The dragged button came from the bar, but is being dragged outside
2057 // of it now, so the rest of the buttons should be laid out as if it
2058 // were removed.
2059 CGFloat draggedButtonOffset = layout_.button_offsets[draggedButtonNodeId];
2060 CGFloat spaceForDraggedButton =
2061 NSWidth([draggedButton frame]) + bookmarks::kBookmarkHorizontalPadding;
2062 [NSAnimationContext beginGrouping];
2063 [[NSAnimationContext currentContext]
2064 setDuration:kDragAndDropAnimationDuration];
2065
2066 for (BookmarkButton* button in buttons_.get()) {
2067 int64_t nodeId = [button bookmarkNode]->id();
2068 CGFloat offset = layout_.button_offsets[nodeId];
2069
2070 if (nodeId == draggedButtonNodeId)
2071 continue;
2072
2073 if (offset > draggedButtonOffset) {
2074 offset -= spaceForDraggedButton;
2075 [self applyXOffset:offset
2076 toButton:button
2077 animated:innerContentAnimationsEnabled_];
2078 }
2079 }
2080
2081 [NSAnimationContext endGrouping];
2082 } 2250 }
2083 2251
2084 #pragma mark Bridge Notification Handlers 2252 #pragma mark Bridge Notification Handlers
2085 2253
2254 // TODO(jrg): for now this is brute force.
2086 - (void)loaded:(BookmarkModel*)model { 2255 - (void)loaded:(BookmarkModel*)model {
2087 DCHECK(model == bookmarkModel_); 2256 DCHECK(model == bookmarkModel_);
2088 if (!model->loaded()) 2257 if (!model->loaded())
2089 return; 2258 return;
2090 // Make sure there are no stale pointers in the pasteboard. This
2091 // can be important if a bookmark is deleted (via bookmark sync)
2092 // while in the middle of a drag. The "drag completed" code
2093 // (e.g. [BookmarkBarView performDragOperationForBookmarkButton:]) is
2094 // careful enough to bail if there is no data found at "drop" time.
2095 [[NSPasteboard pasteboardWithName:NSDragPboard] clearContents];
2096
2097 if (!didCreateExtraButtons_) {
2098 [self createExtraButtons];
2099 }
2100 2259
2101 // If this is a rebuild request while we have a folder open, close it. 2260 // If this is a rebuild request while we have a folder open, close it.
2261 // TODO(mrossetti): Eliminate the need for this because it causes the folder
2262 // menu to disappear after a cut/copy/paste/delete change.
2263 // See: http://crbug.com/36614
2102 if (folderController_) 2264 if (folderController_)
2103 [self closeAllBookmarkFolders]; 2265 [self closeAllBookmarkFolders];
2104 [self rebuildLayoutWithAnimated:NO]; 2266
2267 // Brute force nuke and build.
2268 savedFrameWidth_ = NSWidth([[self view] frame]);
2269 const BookmarkNode* node = model->bookmark_bar_node();
2270 [self clearBookmarkBar];
2271 [self createAppsPageShortcutButton];
2272 [self createManagedBookmarksButton];
2273 [self createSupervisedBookmarksButton];
2274 [self addNodesToButtonList:node];
2275 [self createOtherBookmarksButton];
2276 [self updateTheme:[[[self view] window] themeProvider]];
2277 [self positionRightSideButtons];
2278 [self addButtonsToView];
2279 [self configureOffTheSideButtonContentsAndVisibility];
2280 [self reconfigureBookmarkBar];
2105 } 2281 }
2282
2106 - (void)nodeAdded:(BookmarkModel*)model 2283 - (void)nodeAdded:(BookmarkModel*)model
2107 parent:(const BookmarkNode*)newParent index:(int)newIndex { 2284 parent:(const BookmarkNode*)newParent index:(int)newIndex {
2108 // If a context menu is open, close it. 2285 // If a context menu is open, close it.
2109 [self cancelMenuTracking]; 2286 [self cancelMenuTracking];
2110 2287
2111 const BookmarkNode* newNode = newParent->GetChild(newIndex); 2288 const BookmarkNode* newNode = newParent->GetChild(newIndex);
2112 id<BookmarkButtonControllerProtocol> newController = 2289 id<BookmarkButtonControllerProtocol> newController =
2113 [self controllerForNode:newParent]; 2290 [self controllerForNode:newParent];
2114 [newController addButtonForNode:newNode atIndex:newIndex]; 2291 [newController addButtonForNode:newNode atIndex:newIndex];
2115 [self rebuildLayoutWithAnimated:NO]; 2292 // If we go from 0 --> 1 bookmarks we may need to hide the
2293 // "bookmarks go here" text container.
2294 [self showOrHideNoItemContainerForNode:model->bookmark_bar_node()];
2295 // Cope with chevron or "Other Bookmarks" buttons possibly changing state.
2296 [self reconfigureBookmarkBar];
2116 } 2297 }
2117 2298
2118 // TODO(jrg): for now this is brute force. 2299 // TODO(jrg): for now this is brute force.
2119 - (void)nodeChanged:(BookmarkModel*)model 2300 - (void)nodeChanged:(BookmarkModel*)model
2120 node:(const BookmarkNode*)node { 2301 node:(const BookmarkNode*)node {
2121 [self loaded:model]; 2302 [self loaded:model];
2122 } 2303 }
2123 2304
2124 - (void)nodeMoved:(BookmarkModel*)model 2305 - (void)nodeMoved:(BookmarkModel*)model
2125 oldParent:(const BookmarkNode*)oldParent oldIndex:(int)oldIndex 2306 oldParent:(const BookmarkNode*)oldParent oldIndex:(int)oldIndex
2126 newParent:(const BookmarkNode*)newParent newIndex:(int)newIndex { 2307 newParent:(const BookmarkNode*)newParent newIndex:(int)newIndex {
2127 const BookmarkNode* movedNode = newParent->GetChild(newIndex); 2308 const BookmarkNode* movedNode = newParent->GetChild(newIndex);
2128 id<BookmarkButtonControllerProtocol> oldController = 2309 id<BookmarkButtonControllerProtocol> oldController =
2129 [self controllerForNode:oldParent]; 2310 [self controllerForNode:oldParent];
2130 id<BookmarkButtonControllerProtocol> newController = 2311 id<BookmarkButtonControllerProtocol> newController =
2131 [self controllerForNode:newParent]; 2312 [self controllerForNode:newParent];
2132 if (newController == oldController) { 2313 if (newController == oldController) {
2133 [oldController moveButtonFromIndex:oldIndex toIndex:newIndex]; 2314 [oldController moveButtonFromIndex:oldIndex toIndex:newIndex];
2134 } else { 2315 } else {
2135 [oldController removeButton:oldIndex animate:NO]; 2316 [oldController removeButton:oldIndex animate:NO];
2136 [newController addButtonForNode:movedNode atIndex:newIndex]; 2317 [newController addButtonForNode:movedNode atIndex:newIndex];
2137 } 2318 }
2138 [self rebuildLayoutWithAnimated:NO]; 2319 // If the bar is one of the parents we may need to update the visibility
2320 // of the "bookmarks go here" presentation.
2321 [self showOrHideNoItemContainerForNode:model->bookmark_bar_node()];
2322 // Cope with chevron or "Other Bookmarks" buttons possibly changing state.
2323 [self reconfigureBookmarkBar];
2139 } 2324 }
2140 2325
2141 - (void)nodeRemoved:(BookmarkModel*)model 2326 - (void)nodeRemoved:(BookmarkModel*)model
2142 parent:(const BookmarkNode*)oldParent index:(int)index { 2327 parent:(const BookmarkNode*)oldParent index:(int)index {
2143 // If a context menu is open, close it. 2328 // If a context menu is open, close it.
2144 [self cancelMenuTracking]; 2329 [self cancelMenuTracking];
2145 2330
2146 // Locate the parent node. The parent may not be showing, in which case 2331 // Locate the parent node. The parent may not be showing, in which case
2147 // we do nothing. 2332 // we do nothing.
2148 id<BookmarkButtonControllerProtocol> parentController = 2333 id<BookmarkButtonControllerProtocol> parentController =
2149 [self controllerForNode:oldParent]; 2334 [self controllerForNode:oldParent];
2150 [parentController removeButton:index animate:YES]; 2335 [parentController removeButton:index animate:YES];
2151 [self rebuildLayoutWithAnimated:NO]; 2336 // If we go from 1 --> 0 bookmarks we may need to show the
2337 // "bookmarks go here" text container.
2338 [self showOrHideNoItemContainerForNode:model->bookmark_bar_node()];
2339 // If we deleted the only item on the "off the side" menu we no
2340 // longer need to show it.
2341 [self reconfigureBookmarkBar];
2152 } 2342 }
2153 2343
2344 // TODO(jrg): linear searching is bad.
2345 // Need a BookmarkNode-->NSCell mapping.
2346 //
2154 // TODO(jrg): if the bookmark bar is open on launch, we see the 2347 // TODO(jrg): if the bookmark bar is open on launch, we see the
2155 // buttons all placed, then "scooted over" as the favicons load. If 2348 // buttons all placed, then "scooted over" as the favicons load. If
2156 // this looks bad I may need to change widthForBookmarkButtonCell to 2349 // this looks bad I may need to change widthForBookmarkButtonCell to
2157 // add space for an image even if not there on the assumption that 2350 // add space for an image even if not there on the assumption that
2158 // favicons will eventually load. 2351 // favicons will eventually load.
2159 - (void)nodeFaviconLoaded:(BookmarkModel*)model 2352 - (void)nodeFaviconLoaded:(BookmarkModel*)model
2160 node:(const BookmarkNode*)node { 2353 node:(const BookmarkNode*)node {
2161 auto buttonIt = nodeIdToButtonMap_.find(node->id()); 2354 for (BookmarkButton* button in buttons_.get()) {
2162 if (buttonIt != nodeIdToButtonMap_.end()) { 2355 const BookmarkNode* cellnode = [button bookmarkNode];
2163 BookmarkButton* button = (*buttonIt).second; 2356 if (cellnode == node) {
2164 BOOL darkTheme = [[[self view] window] hasDarkTheme]; 2357 BOOL darkTheme = [[[self view] window] hasDarkTheme];
2165 NSImage* theImage = [self faviconForNode:node forADarkTheme:darkTheme]; 2358 NSImage* theImage = [self faviconForNode:node forADarkTheme:darkTheme];
2166 [[button cell] setBookmarkCellText:[button title] image:theImage]; 2359 [[button cell] setBookmarkCellText:[button title]
2360 image:theImage];
2361 // Adding an image means we might need more room for the
2362 // bookmark. Test for it by growing the button (if needed)
2363 // and shifting everything else over.
2364 [self checkForBookmarkButtonGrowth:button];
2365 return;
2366 }
2167 } 2367 }
2168 [self rebuildLayoutWithAnimated:NO]; 2368
2169 if (folderController_) 2369 if (folderController_)
2170 [folderController_ faviconLoadedForNode:node]; 2370 [folderController_ faviconLoadedForNode:node];
2171 } 2371 }
2172 2372
2173 // TODO(jrg): for now this is brute force. 2373 // TODO(jrg): for now this is brute force.
2174 - (void)nodeChildrenReordered:(BookmarkModel*)model 2374 - (void)nodeChildrenReordered:(BookmarkModel*)model
2175 node:(const BookmarkNode*)node { 2375 node:(const BookmarkNode*)node {
2176 [self loaded:model]; 2376 [self loaded:model];
2177 } 2377 }
2178 2378
(...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after
2302 - (void)didDragBookmarkToTrash:(BookmarkButton*)button { 2502 - (void)didDragBookmarkToTrash:(BookmarkButton*)button {
2303 if ([self canDragBookmarkButtonToTrash:button]) { 2503 if ([self canDragBookmarkButtonToTrash:button]) {
2304 const BookmarkNode* node = [button bookmarkNode]; 2504 const BookmarkNode* node = [button bookmarkNode];
2305 if (node) 2505 if (node)
2306 bookmarkModel_->Remove(node); 2506 bookmarkModel_->Remove(node);
2307 } 2507 }
2308 } 2508 }
2309 2509
2310 - (void)bookmarkDragDidEnd:(BookmarkButton*)button 2510 - (void)bookmarkDragDidEnd:(BookmarkButton*)button
2311 operation:(NSDragOperation)operation { 2511 operation:(NSDragOperation)operation {
2312 [self rebuildLayoutWithAnimated:YES]; 2512 [button setHidden:NO];
2513 [self resetAllButtonPositionsWithAnimation:YES];
2313 } 2514 }
2314 2515
2315 2516
2316 #pragma mark BookmarkButtonControllerProtocol 2517 #pragma mark BookmarkButtonControllerProtocol
2317 2518
2318 // Close all bookmark folders. "Folder" here is the fake menu for 2519 // Close all bookmark folders. "Folder" here is the fake menu for
2319 // bookmark folders, not a button context menu. 2520 // bookmark folders, not a button context menu.
2320 - (void)closeAllBookmarkFolders { 2521 - (void)closeAllBookmarkFolders {
2321 [self watchForExitEvent:NO]; 2522 [self watchForExitEvent:NO];
2322 2523
(...skipping 139 matching lines...) Expand 10 before | Expand all | Expand 10 after
2462 // Return YES if we should show the drop indicator, else NO. 2663 // Return YES if we should show the drop indicator, else NO.
2463 - (BOOL)shouldShowIndicatorShownForPoint:(NSPoint)point { 2664 - (BOOL)shouldShowIndicatorShownForPoint:(NSPoint)point {
2464 return ![self buttonForDroppingOnAtPoint:point]; 2665 return ![self buttonForDroppingOnAtPoint:point];
2465 } 2666 }
2466 2667
2467 // Return the x position for a drop indicator. 2668 // Return the x position for a drop indicator.
2468 - (CGFloat)indicatorPosForDragToPoint:(NSPoint)point { 2669 - (CGFloat)indicatorPosForDragToPoint:(NSPoint)point {
2469 CGFloat x = 0; 2670 CGFloat x = 0;
2470 CGFloat halfHorizontalPadding = 0.5 * bookmarks::kBookmarkHorizontalPadding; 2671 CGFloat halfHorizontalPadding = 0.5 * bookmarks::kBookmarkHorizontalPadding;
2471 int destIndex = [self indexForDragToPoint:point]; 2672 int destIndex = [self indexForDragToPoint:point];
2472 int numButtons = layout_.VisibleButtonCount(); 2673 int numButtons = displayedButtonCount_;
2473 2674
2474 CGFloat leadingOffset; 2675 CGFloat leftmostX;
2475 if (layout_.IsSupervisedBookmarksButtonVisible()) { 2676 if (![supervisedBookmarksButton_ isHidden]) {
2476 leadingOffset = 2677 leftmostX =
2477 layout_.supervised_bookmarks_button_offset + halfHorizontalPadding; 2678 NSMaxX([supervisedBookmarksButton_ frame]) + halfHorizontalPadding;
2478 } else if (layout_.IsManagedBookmarksButtonVisible()) { 2679 } else if (![managedBookmarksButton_ isHidden]) {
2479 leadingOffset = 2680 leftmostX = NSMaxX([managedBookmarksButton_ frame]) + halfHorizontalPadding;
2480 layout_.managed_bookmarks_button_offset + halfHorizontalPadding; 2681 } else if (![appsPageShortcutButton_ isHidden]) {
2481 } else if (layout_.IsAppsButtonVisible()) { 2682 leftmostX = NSMaxX([appsPageShortcutButton_ frame]) + halfHorizontalPadding;
2482 leadingOffset = layout_.apps_button_offset + halfHorizontalPadding;
2483 } else { 2683 } else {
2484 leadingOffset = bookmarks::kBookmarkLeftMargin - halfHorizontalPadding; 2684 leftmostX = bookmarks::kBookmarkLeftMargin - halfHorizontalPadding;
2485 } 2685 }
2486 2686
2487 // If it's a drop strictly between existing buttons ... 2687 // If it's a drop strictly between existing buttons ...
2488 if (destIndex == 0) { 2688 if (destIndex == 0) {
2489 x = leadingOffset; 2689 x = leftmostX;
2490 } else if (destIndex > 0 && destIndex < numButtons) { 2690 } else if (destIndex > 0 && destIndex < numButtons) {
2491 // ... put the indicator right between the buttons. 2691 // ... put the indicator right between the buttons.
2492 int64_t nodeId = 2692 BookmarkButton* button =
2493 bookmarkModel_->bookmark_bar_node()->GetChild(destIndex)->id(); 2693 [buttons_ objectAtIndex:static_cast<NSUInteger>(destIndex-1)];
2494 x = layout_.button_offsets[nodeId] - halfHorizontalPadding; 2694 DCHECK(button);
2695 NSRect buttonFrame = [button frame];
2696 x = NSMaxX(buttonFrame) + halfHorizontalPadding;
2697
2495 // If it's a drop at the end (past the last button, if there are any) ... 2698 // If it's a drop at the end (past the last button, if there are any) ...
2496 } else if (destIndex == numButtons) { 2699 } else if (destIndex == numButtons) {
2497 // and if it's past the last button ... 2700 // and if it's past the last button ...
2498 if (numButtons > 0) { 2701 if (numButtons > 0) {
2499 // ... find the last button, and put the indicator after it. 2702 // ... find the last button, and put the indicator to its right.
2500 const BookmarkNode* node = 2703 BookmarkButton* button =
2501 bookmarkModel_->bookmark_bar_node()->GetChild(destIndex - 1); 2704 [buttons_ objectAtIndex:static_cast<NSUInteger>(destIndex - 1)];
2502 int64_t nodeId = node->id(); 2705 DCHECK(button);
2503 x = layout_.button_offsets[nodeId] + [self widthOfButtonForNode:node] + 2706 x = NSMaxX([button frame]) + halfHorizontalPadding;
2504 halfHorizontalPadding; 2707
2505 // Otherwise, put it right at the beginning. 2708 // Otherwise, put it right at the beginning.
2506 } else { 2709 } else {
2507 x = leadingOffset; 2710 x = leftmostX;
2508 } 2711 }
2509 } else { 2712 } else {
2510 NOTREACHED(); 2713 NOTREACHED();
2511 } 2714 }
2512 2715
2513 return cocoa_l10n_util::ShouldDoExperimentalRTLLayout() 2716 return x;
2514 ? NSWidth([buttonView_ frame]) - x
2515 : x;
2516 } 2717 }
2517 2718
2518 - (void)childFolderWillShow:(id<BookmarkButtonControllerProtocol>)child { 2719 - (void)childFolderWillShow:(id<BookmarkButtonControllerProtocol>)child {
2519 // If the bookmarkbar is not in detached mode, lock bar visibility, forcing 2720 // If the bookmarkbar is not in detached mode, lock bar visibility, forcing
2520 // the overlay to stay open when in fullscreen mode. 2721 // the overlay to stay open when in fullscreen mode.
2521 if (![self isInState:BookmarkBar::DETACHED] && 2722 if (![self isInState:BookmarkBar::DETACHED] &&
2522 ![self isAnimatingToState:BookmarkBar::DETACHED]) { 2723 ![self isAnimatingToState:BookmarkBar::DETACHED]) {
2523 BrowserWindowController* browserController = 2724 BrowserWindowController* browserController =
2524 [BrowserWindowController browserWindowControllerForView:[self view]]; 2725 [BrowserWindowController browserWindowControllerForView:[self view]];
2525 [browserController lockToolbarVisibilityForOwner:child withAnimation:NO]; 2726 [browserController lockToolbarVisibilityForOwner:child withAnimation:NO];
2526 } 2727 }
2527 } 2728 }
2528 2729
2529 - (void)childFolderWillClose:(id<BookmarkButtonControllerProtocol>)child { 2730 - (void)childFolderWillClose:(id<BookmarkButtonControllerProtocol>)child {
2530 // Release bar visibility, allowing the overlay to close if in fullscreen 2731 // Release bar visibility, allowing the overlay to close if in fullscreen
2531 // mode. 2732 // mode.
2532 BrowserWindowController* browserController = 2733 BrowserWindowController* browserController =
2533 [BrowserWindowController browserWindowControllerForView:[self view]]; 2734 [BrowserWindowController browserWindowControllerForView:[self view]];
2534 [browserController releaseToolbarVisibilityForOwner:child withAnimation:NO]; 2735 [browserController releaseToolbarVisibilityForOwner:child withAnimation:NO];
2535 } 2736 }
2536 2737
2537 // Add a new folder controller as triggered by the given folder button. 2738 // Add a new folder controller as triggered by the given folder button.
2538 - (void)addNewFolderControllerWithParentButton:(BookmarkButton*)parentButton { 2739 - (void)addNewFolderControllerWithParentButton:(BookmarkButton*)parentButton {
2740
2539 // If doing a close/open, make sure the fullscreen chrome doesn't 2741 // If doing a close/open, make sure the fullscreen chrome doesn't
2540 // have a chance to begin animating away in the middle of things. 2742 // have a chance to begin animating away in the middle of things.
2541 BrowserWindowController* browserController = 2743 BrowserWindowController* browserController =
2542 [BrowserWindowController browserWindowControllerForView:[self view]]; 2744 [BrowserWindowController browserWindowControllerForView:[self view]];
2543 // Confirm we're not re-locking with ourself as an owner before locking. 2745 // Confirm we're not re-locking with ourself as an owner before locking.
2544 DCHECK([browserController isToolbarVisibilityLockedForOwner:self] == NO); 2746 DCHECK([browserController isToolbarVisibilityLockedForOwner:self] == NO);
2545 [browserController lockToolbarVisibilityForOwner:self withAnimation:NO]; 2747 [browserController lockToolbarVisibilityForOwner:self withAnimation:NO];
2546 2748
2547 if (folderController_) 2749 if (folderController_)
2548 [self closeAllBookmarkFolders]; 2750 [self closeAllBookmarkFolders];
(...skipping 15 matching lines...) Expand all
2564 [browserController releaseToolbarVisibilityForOwner:self withAnimation:NO]; 2766 [browserController releaseToolbarVisibilityForOwner:self withAnimation:NO];
2565 } 2767 }
2566 2768
2567 - (void)openAll:(const BookmarkNode*)node 2769 - (void)openAll:(const BookmarkNode*)node
2568 disposition:(WindowOpenDisposition)disposition { 2770 disposition:(WindowOpenDisposition)disposition {
2569 [self closeFolderAndStopTrackingMenus]; 2771 [self closeFolderAndStopTrackingMenus];
2570 chrome::OpenAll([[self view] window], browser_, node, disposition, 2772 chrome::OpenAll([[self view] window], browser_, node, disposition,
2571 browser_->profile()); 2773 browser_->profile());
2572 } 2774 }
2573 2775
2776 - (void)addButtonForNode:(const BookmarkNode*)node
2777 atIndex:(NSInteger)buttonIndex {
2778 int newOffset =
2779 bookmarks::kBookmarkLeftMargin - bookmarks::kBookmarkHorizontalPadding;
2780 if (buttonIndex == -1)
2781 buttonIndex = [buttons_ count]; // New button goes at the end.
2782 if (buttonIndex <= (NSInteger)[buttons_ count]) {
2783 if (buttonIndex) {
2784 BookmarkButton* targetButton = [buttons_ objectAtIndex:buttonIndex - 1];
2785 NSRect targetFrame = [targetButton frame];
2786 newOffset = targetFrame.origin.x + NSWidth(targetFrame) +
2787 bookmarks::kBookmarkHorizontalPadding;
2788 }
2789 BookmarkButton* newButton = [self buttonForNode:node xOffset:&newOffset];
2790 ++displayedButtonCount_;
2791 [buttons_ insertObject:newButton atIndex:buttonIndex];
2792 [buttonView_ addSubview:newButton];
2793 [self resetAllButtonPositionsWithAnimation:NO];
2794 // See if any buttons need to be pushed off to or brought in from the side.
2795 [self reconfigureBookmarkBar];
2796 } else {
2797 // A button from somewhere else (not the bar) is being moved to the
2798 // off-the-side so insure it gets redrawn if its showing.
2799 [self reconfigureBookmarkBar];
2800 [folderController_ reconfigureMenu];
2801 }
2802 }
2803
2574 // TODO(mrossetti): Duplicate code with BookmarkBarFolderController. 2804 // TODO(mrossetti): Duplicate code with BookmarkBarFolderController.
2575 // http://crbug.com/35966 2805 // http://crbug.com/35966
2576 - (BOOL)addURLs:(NSArray*)urls withTitles:(NSArray*)titles at:(NSPoint)point { 2806 - (BOOL)addURLs:(NSArray*)urls withTitles:(NSArray*)titles at:(NSPoint)point {
2577 DCHECK([urls count] == [titles count]); 2807 DCHECK([urls count] == [titles count]);
2578 BOOL nodesWereAdded = NO; 2808 BOOL nodesWereAdded = NO;
2579 // Figure out where these new bookmarks nodes are to be added. 2809 // Figure out where these new bookmarks nodes are to be added.
2580 BookmarkButton* button = [self buttonForDroppingOnAtPoint:point]; 2810 BookmarkButton* button = [self buttonForDroppingOnAtPoint:point];
2581 const BookmarkNode* destParent = NULL; 2811 const BookmarkNode* destParent = NULL;
2582 int destIndex = 0; 2812 int destIndex = 0;
2583 if ([button isFolder]) { 2813 if ([button isFolder]) {
(...skipping 27 matching lines...) Expand all
2611 [titles objectAtIndex:i]), 2841 [titles objectAtIndex:i]),
2612 gurl); 2842 gurl);
2613 nodesWereAdded = YES; 2843 nodesWereAdded = YES;
2614 } 2844 }
2615 } 2845 }
2616 } 2846 }
2617 return nodesWereAdded; 2847 return nodesWereAdded;
2618 } 2848 }
2619 2849
2620 - (void)moveButtonFromIndex:(NSInteger)fromIndex toIndex:(NSInteger)toIndex { 2850 - (void)moveButtonFromIndex:(NSInteger)fromIndex toIndex:(NSInteger)toIndex {
2621 int buttonCount = layout_.VisibleButtonCount(); 2851 if (fromIndex != toIndex) {
2622 BOOL isMoveWithinOffTheSideMenu = 2852 NSInteger buttonCount = (NSInteger)[buttons_ count];
2623 (toIndex >= buttonCount) && (fromIndex >= buttonCount); 2853 if (toIndex == -1)
2624 if (isMoveWithinOffTheSideMenu) { 2854 toIndex = buttonCount;
2625 fromIndex -= buttonCount; 2855 // See if we have a simple move within the bar, which will be the case if
2626 toIndex -= buttonCount; 2856 // both button indexes are in the visible space.
2627 [folderController_ moveButtonFromIndex:fromIndex toIndex:toIndex]; 2857 if (fromIndex < buttonCount && toIndex < buttonCount) {
2628 } else { 2858 BookmarkButton* movedButton = [buttons_ objectAtIndex:fromIndex];
2629 [self rebuildLayoutWithAnimated:NO]; 2859 [buttons_ removeObjectAtIndex:fromIndex];
2860 [buttons_ insertObject:movedButton atIndex:toIndex];
2861 [movedButton setHidden:NO];
2862 [self resetAllButtonPositionsWithAnimation:NO];
2863 } else if (fromIndex < buttonCount) {
2864 // A button is being removed from the bar and added to off-the-side.
2865 // By now the node has already been inserted into the model so the
2866 // button to be added is represented by |toIndex|. Things get
2867 // complicated because the off-the-side is showing and must be redrawn
2868 // while possibly re-laying out the bookmark bar.
2869 [self removeButton:fromIndex animate:NO];
2870 [self reconfigureBookmarkBar];
2871 [folderController_ reconfigureMenu];
2872 } else if (toIndex < buttonCount) {
2873 // A button is being added to the bar and removed from off-the-side.
2874 // By now the node has already been inserted into the model so the
2875 // button to be added is represented by |toIndex|.
2876 const BookmarkNode* node = bookmarkModel_->bookmark_bar_node();
2877 const BookmarkNode* movedNode = node->GetChild(toIndex);
2878 DCHECK(movedNode);
2879 [self addButtonForNode:movedNode atIndex:toIndex];
2880 [self reconfigureBookmarkBar];
2881 } else {
2882 // A button is being moved within the off-the-side.
2883 fromIndex -= buttonCount;
2884 toIndex -= buttonCount;
2885 [folderController_ moveButtonFromIndex:fromIndex toIndex:toIndex];
2886 }
2630 } 2887 }
2631 } 2888 }
2632 2889
2633 - (void)removeButton:(NSInteger)buttonIndex animate:(BOOL)animate { 2890 - (void)removeButton:(NSInteger)buttonIndex animate:(BOOL)animate {
2634 if ((size_t)buttonIndex < layout_.VisibleButtonCount()) { 2891 if (buttonIndex < (NSInteger)[buttons_ count]) {
2635 // The button being removed is showing in the bar. 2892 // The button being removed is showing in the bar.
2636 BookmarkButton* oldButton = buttons_[buttonIndex]; 2893 BookmarkButton* oldButton = [buttons_ objectAtIndex:buttonIndex];
2637 if (oldButton == [folderController_ parentButton]) { 2894 if (oldButton == [folderController_ parentButton]) {
2638 // If we are deleting a button whose folder is currently open, close it! 2895 // If we are deleting a button whose folder is currently open, close it!
2639 [self closeAllBookmarkFolders]; 2896 [self closeAllBookmarkFolders];
2640 } 2897 }
2641 if (animate && innerContentAnimationsEnabled_ && [self isVisible] && 2898 if (animate && innerContentAnimationsEnabled_ && [self isVisible] &&
2642 [[self browserWindow] isMainWindow]) { 2899 [[self browserWindow] isMainWindow]) {
2643 NSPoint poofPoint = [oldButton screenLocationForRemoveAnimation]; 2900 NSPoint poofPoint = [oldButton screenLocationForRemoveAnimation];
2644 NSShowAnimationEffect(NSAnimationEffectDisappearingItemDefault, poofPoint, 2901 NSShowAnimationEffect(NSAnimationEffectDisappearingItemDefault, poofPoint,
2645 NSZeroSize, nil, nil, nil); 2902 NSZeroSize, nil, nil, nil);
2646 } 2903 }
2647 [self rebuildLayoutWithAnimated:YES]; 2904 [oldButton setDelegate:nil];
2905 [oldButton removeFromSuperview];
2906 [buttons_ removeObjectAtIndex:buttonIndex];
2907 --displayedButtonCount_;
2908 [self resetAllButtonPositionsWithAnimation:YES];
2909 [self reconfigureBookmarkBar];
2648 } else if (folderController_ && 2910 } else if (folderController_ &&
2649 [folderController_ parentButton] == offTheSideButton_) { 2911 [folderController_ parentButton] == offTheSideButton_) {
2650 // The button being removed is in the OTS (off-the-side) and the OTS 2912 // The button being removed is in the OTS (off-the-side) and the OTS
2651 // menu is showing so we need to remove the button. 2913 // menu is showing so we need to remove the button.
2652 NSInteger index = buttonIndex - layout_.VisibleButtonCount(); 2914 NSInteger index = buttonIndex - displayedButtonCount_;
2653 [folderController_ removeButton:index animate:animate]; 2915 [folderController_ removeButton:index animate:animate];
2654 } 2916 }
2655 } 2917 }
2656 2918
2657 - (id<BookmarkButtonControllerProtocol>)controllerForNode: 2919 - (id<BookmarkButtonControllerProtocol>)controllerForNode:
2658 (const BookmarkNode*)node { 2920 (const BookmarkNode*)node {
2659 // See if it's in the bar, then if it is in the hierarchy of visible 2921 // See if it's in the bar, then if it is in the hierarchy of visible
2660 // folder menus. 2922 // folder menus.
2661 if (bookmarkModel_->bookmark_bar_node() == node) 2923 if (bookmarkModel_->bookmark_bar_node() == node)
2662 return self; 2924 return self;
2663 return [folderController_ controllerForNode:node]; 2925 return [folderController_ controllerForNode:node];
2664 } 2926 }
2665 2927
2666 // For testing.
2667 - (const BookmarkBarLayout&)currentLayout {
2668 return layout_;
2669 }
2670
2671 @end 2928 @end
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698