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

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

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

Powered by Google App Engine
This is Rietveld 408576698