Commit 27ed7650 authored by sky's avatar sky Committed by Commit bot

Revert of [Mac] Refactor bookmark bar controller (patchset #10 id:180001 of...

Revert of [Mac] Refactor bookmark bar controller (patchset #10 id:180001 of https://codereview.chromium.org/2751573002/ )

Reason for revert:
Reverting as likely caused msan failurs. See https://luci-logdog.appspot.com/v/?s=chromium%2Fbb%2Fchromium.memory%2FMac_ASan_64_Tests__1_%2F29660%2F%2B%2Frecipes%2Fsteps%2Fbrowser_tests%2F0%2Flogs%2FBookmarkFolderAppleScriptTest.DeleteBookmarkItems%2F0 for example:

==77706==ERROR: AddressSanitizer: heap-use-after-free on address 0x603000445b40 at pc 0x0001156ab4fe bp 0x7fff5c399fd0 sp 0x7fff5c399fc8
READ of size 8 at 0x603000445b40 thread T0
    #0 0x1156ab4fd in -[BookmarkBarController applyLayout:animated:] ??:0:0
    #1 0x1156a9db1 in -[BookmarkBarController rebuildLayoutWithAnimated:] ??:0:0
    #2 0x1156acf25 in -[BookmarkBarController nodeRemoved:parent:index:] ??:0:0
    #3 0x1127774c7 in bookmarks::BookmarkModel::RemoveAndDeleteNode(bookmarks::BookmarkNode*) ??:0:0
    #4 0x112776d40 in bookmarks::BookmarkModel::Remove(bookmarks::BookmarkNode const*) ??:0:0
    #5 0x10607a64c in (anonymous namespace)::BookmarkFolderAppleScriptTest_DeleteBookmarkItems_Test::RunTestOnMainThread() ??:0:0
    #6 0x10d1f6ac3 in content::BrowserTestBase::ProxyRunTestOnMainThreadLoop() ??:0:0
    #7 0x10bb87dc3 in ChromeBrowserMainParts::PreMainMessageLoopRunImpl() ??:0:0
    #8 0x10bb851e6 in ChromeBrowserMainParts::PreMainMessageLoopRun() ??:0:0
    #9 0x1077b02dd in content::BrowserMainLoop::PreMainMessageLoopRun() ??:0:0
    #10 0x1084ba8a3 in content::StartupTaskRunner::RunAllTasksNow() ??:0:0
    #11 0x1077abc3a in content::BrowserMainLoop::CreateStartupTasks() ??:0:0
    #12 0x1077b94ec in content::BrowserMainRunnerImpl::Initialize(content::MainFunctionParams const&) ??:0:0
    #13 0x1077a40b5 in content::BrowserMain(content::MainFunctionParams const&) ??:0:0
    #14 0x10b6a9604 in content::RunNamedProcessTypeMain(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, content::MainFunctionParams const&, content::ContentMainDelegate*) ??:0:0
    #15 0x10b6ab3c7 in content::ContentMainRunnerImpl::Run() ??:0:0
    #16 0x111afc88b in service_manager::Main(service_manager::MainParams const&) ??:0:0
    #17 0x10b6a8ff4 in content::ContentMain(content::ContentMainParams const&) ??:0:0
    #18 0x10d1f5c4f in content::BrowserTestBase::SetUp() ??:0:0
    #19 0x10ba09201 in InProcessBrowserTest::SetUp() ??:0:0
    #20 0x10ed76b2f in testing::Test::Run() ??:0:0
    #21 0x10ed78b43 in testing::TestInfo::Run() ??:0:0
    #22 0x10ed79e86 in testing::TestCase::Run() ??:0:0
    #23 0x10ed8cfb6 in testing::internal::UnitTestImpl::RunAllTests() ??:0:0
    #24 0x10ed8c588 in testing::UnitTest::Run() ??:0:0
    #25 0x10ba51d3e in base::TestSuite::Run() ??:0:0
    #26 0x10b6fc3ac in ChromeTestSuiteRunner::RunTestSuite(int, char**) ??:0:0
    #27 0x10d2c7991 in content::LaunchTests(content::TestLauncherDelegate*, int, int, char**) ??:0:0
    #28 0x10b6fc203 in main ??:0:0
    #29 0x7fff8a0125fc in start ??:0:0

Original issue's description:
> Yes, with RTL thrown in, since this is specifically designed to make it almost free.
>
> Two major things here:
> - The bar is no longer relaid-out directly in response to changes in view size, bookmark model etc. Instead, a new UI-direction-agnostic view model (BookmarkBarLayout) is created from the current state, and if it's different from before, it's applied to the view.
> - Removed a bunch of layout-related code that's no longer necessary
>
> BUG=648560
>
> Review-Url: https://codereview.chromium.org/2751573002
> Cr-Commit-Position: refs/heads/master@{#468364}
> Committed: https://chromium.googlesource.com/chromium/src/+/2644729cb7722a702a76cc2758d0ce372e1e6f92

TBR=ellyjones@chromium.org,avi@chromium.org,lgrey@chromium.org
# Skipping CQ checks because original CL landed less than 1 days ago.
NOPRESUBMIT=true
NOTREECHECKS=true
NOTRY=true
BUG=648560

Review-Url: https://codereview.chromium.org/2853123002
Cr-Commit-Position: refs/heads/master@{#468398}
parent 6c3d410e
...@@ -113,10 +113,9 @@ TEST_F(BookmarkBarBridgeTest, TestRedirect) { ...@@ -113,10 +113,9 @@ TEST_F(BookmarkBarBridgeTest, TestRedirect) {
bridge->BookmarkNodeFaviconChanged(NULL, NULL); bridge->BookmarkNodeFaviconChanged(NULL, NULL);
bridge->BookmarkNodeChildrenReordered(NULL, NULL); bridge->BookmarkNodeChildrenReordered(NULL, NULL);
bridge->BookmarkNodeRemoved(NULL, NULL, 0, NULL, std::set<GURL>()); bridge->BookmarkNodeRemoved(NULL, NULL, 0, NULL, std::set<GURL>());
// 7 calls above plus two Loaded() in init routing makes 9. // 7 calls above plus an initial Loaded() in init routine makes 8.
EXPECT_EQ(controller.get()->called_selectors_.size(), 9U); EXPECT_EQ(controller.get()->called_selectors_.size(), 8U);
std::vector<SEL> expected_selectors = { std::vector<SEL> expected_selectors = {
@selector(loaded:), // initial from init
@selector(loaded:), // initial from init @selector(loaded:), // initial from init
@selector(loaded:), @selector(loaded:),
@selector(nodeMoved:oldParent:oldIndex:newParent:newIndex:), @selector(nodeMoved:oldParent:oldIndex:newParent:newIndex:),
......
...@@ -10,7 +10,6 @@ ...@@ -10,7 +10,6 @@
#include <map> #include <map>
#include <memory> #include <memory>
#include <unordered_map>
#import "base/mac/cocoa_protocols.h" #import "base/mac/cocoa_protocols.h"
#include "base/mac/scoped_nsobject.h" #include "base/mac/scoped_nsobject.h"
...@@ -134,67 +133,6 @@ const NSTimeInterval kDragHoverOpenDelay = 0.7; ...@@ -134,67 +133,6 @@ const NSTimeInterval kDragHoverOpenDelay = 0.7;
// no opportunity for overlap. // no opportunity for overlap.
const NSTimeInterval kDragHoverCloseDelay = 0.4; const NSTimeInterval kDragHoverCloseDelay = 0.4;
enum BookmarkBarVisibleElementsMask {
kVisibleElementsMaskNone = 0,
kVisibleElementsMaskAppsButton = 1 << 0,
kVisibleElementsMaskManagedBookmarksButton = 1 << 1,
kVisibleElementsMaskSupervisedBookmarksButton = 1 << 2,
kVisibleElementsMaskOffTheSideButton = 1 << 3,
kVisibleElementsMaskOtherBookmarksButton = 1 << 4,
kVisibleElementsMaskNoItemTextField = 1 << 5,
kVisibleElementsMaskImportBookmarksButton = 1 << 6,
};
// Specifies the location and visibility of the various sub-elements
// of the bookmark bar. Allows calculating the layout and actually
// applying it to views to be decoupled. For example, applying
// the layout in an RTL context transforms all horizontal offsets
// transparently.
struct BookmarkBarLayout {
public:
BookmarkBarLayout();
~BookmarkBarLayout();
BookmarkBarLayout(BookmarkBarLayout&& other);
BookmarkBarLayout& operator=(BookmarkBarLayout&& other);
bool IsAppsButtonVisible() const {
return visible_elements & kVisibleElementsMaskAppsButton;
}
bool IsManagedBookmarksButtonVisible() const {
return visible_elements & kVisibleElementsMaskManagedBookmarksButton;
}
bool IsSupervisedBookmarksButtonVisible() const {
return visible_elements & kVisibleElementsMaskSupervisedBookmarksButton;
}
bool IsOffTheSideButtonVisible() const {
return visible_elements & kVisibleElementsMaskOffTheSideButton;
}
bool IsOtherBookmarksButtonVisible() const {
return visible_elements & kVisibleElementsMaskOtherBookmarksButton;
}
bool IsNoItemTextFieldVisible() const {
return visible_elements & kVisibleElementsMaskNoItemTextField;
}
bool IsImportBookmarksButtonVisible() const {
return visible_elements & kVisibleElementsMaskImportBookmarksButton;
}
size_t VisibleButtonCount() const { return button_offsets.size(); }
unsigned int visible_elements;
CGFloat apps_button_offset;
CGFloat managed_bookmarks_button_offset;
CGFloat supervised_bookmarks_button_offset;
CGFloat off_the_side_button_offset;
CGFloat other_bookmarks_button_offset;
CGFloat no_item_textfield_offset;
CGFloat no_item_textfield_width;
CGFloat import_bookmarks_button_offset;
CGFloat import_bookmarks_button_width;
CGFloat max_x;
std::unordered_map<int64_t, CGFloat> button_offsets;
};
} // namespace bookmarks } // namespace bookmarks
// The interface for the bookmark bar controller's delegate. Currently, the // The interface for the bookmark bar controller's delegate. Currently, the
...@@ -288,6 +226,9 @@ willAnimateFromState:(BookmarkBar::State)oldState ...@@ -288,6 +226,9 @@ willAnimateFromState:(BookmarkBar::State)oldState
buttonView_; // Contains 'no items' text fields. buttonView_; // Contains 'no items' text fields.
base::scoped_nsobject<BookmarkButton> offTheSideButton_; // aka the chevron. base::scoped_nsobject<BookmarkButton> offTheSideButton_; // aka the chevron.
NSRect originalNoItemsRect_; // Original, pre-resized field rect.
NSRect originalImportBookmarksRect_; // Original, pre-resized field rect.
// "Apps" button on the left side. // "Apps" button on the left side.
base::scoped_nsobject<BookmarkButton> appsPageShortcutButton_; base::scoped_nsobject<BookmarkButton> appsPageShortcutButton_;
...@@ -312,6 +253,15 @@ willAnimateFromState:(BookmarkBar::State)oldState ...@@ -312,6 +253,15 @@ willAnimateFromState:(BookmarkBar::State)oldState
// initial build. // initial build.
CGFloat savedFrameWidth_; CGFloat savedFrameWidth_;
// The number of buttons we display in the bookmark bar. This does
// not include the "off the side" chevron or the "Other Bookmarks"
// button. We use this number to determine if we need to display
// the chevron, and to know what to place in the chevron's menu.
// Since we create everything before doing layout we can't be sure
// that all bookmark buttons we create will be visible. Thus,
// [buttons_ count] isn't a definitive check.
int displayedButtonCount_;
// A state flag which tracks when the bar's folder menus should be shown. // A state flag which tracks when the bar's folder menus should be shown.
// An initial click in any of the folder buttons turns this on and // An initial click in any of the folder buttons turns this on and
// one of the following will turn it off: another click in the button, // one of the following will turn it off: another click in the button,
...@@ -477,29 +427,28 @@ willAnimateFromState:(BookmarkBar::State)oldState ...@@ -477,29 +427,28 @@ willAnimateFromState:(BookmarkBar::State)oldState
// These APIs should only be used by unit tests (or used internally). // These APIs should only be used by unit tests (or used internally).
@interface BookmarkBarController(InternalOrTestingAPI) @interface BookmarkBarController(InternalOrTestingAPI)
- (bookmarks::BookmarkBarLayout)layoutFromCurrentState;
- (void)applyLayout:(const bookmarks::BookmarkBarLayout&)layout
animated:(BOOL)animated;
- (void)openBookmarkFolder:(id)sender; - (void)openBookmarkFolder:(id)sender;
- (void)openOrCloseBookmarkFolderForOffTheSideButton; - (void)openOrCloseBookmarkFolderForOffTheSideButton;
- (BookmarkBarView*)buttonView; - (BookmarkBarView*)buttonView;
- (NSMutableArray*)buttons; - (NSMutableArray*)buttons;
- (BookmarkButton*)otherBookmarksButton; - (BOOL)offTheSideButtonIsHidden;
- (BookmarkButton*)managedBookmarksButton; - (BOOL)appsPageShortcutButtonIsHidden;
- (BookmarkButton*)supervisedBookmarksButton;
- (BookmarkButton*)otherBookmarksButton; - (BookmarkButton*)otherBookmarksButton;
- (BookmarkBarFolderController*)folderController; - (BookmarkBarFolderController*)folderController;
- (id)folderTarget; - (id)folderTarget;
- (int)displayedButtonCount;
- (void)openURL:(GURL)url disposition:(WindowOpenDisposition)disposition; - (void)openURL:(GURL)url disposition:(WindowOpenDisposition)disposition;
- (void)clearBookmarkBar; - (void)clearBookmarkBar;
- (BookmarkButtonCell*)cellForBookmarkNode:(const bookmarks::BookmarkNode*)node; - (BookmarkButtonCell*)cellForBookmarkNode:(const bookmarks::BookmarkNode*)node;
- (BookmarkButtonCell*)cellForCustomButtonWithText:(NSString*)text - (BookmarkButtonCell*)cellForCustomButtonWithText:(NSString*)text
image:(NSImage*)image; image:(NSImage*)image;
- (NSRect)frameForBookmarkButtonFromCell:(NSCell*)cell xOffset:(int*)xOffset;
- (void)checkForBookmarkButtonGrowth:(NSButton*)button; - (void)checkForBookmarkButtonGrowth:(NSButton*)button;
- (void)frameDidChange; - (void)frameDidChange;
- (void)updateTheme:(const ui::ThemeProvider*)themeProvider; - (void)updateTheme:(const ui::ThemeProvider*)themeProvider;
- (BookmarkButton*)buttonForDroppingOnAtPoint:(NSPoint)point; - (BookmarkButton*)buttonForDroppingOnAtPoint:(NSPoint)point;
- (BOOL)isEventAnExitEvent:(NSEvent*)event; - (BOOL)isEventAnExitEvent:(NSEvent*)event;
- (BOOL)shrinkOrHideView:(NSView*)view forMaxX:(CGFloat)maxViewX;
- (void)unhighlightBookmark:(const bookmarks::BookmarkNode*)node; - (void)unhighlightBookmark:(const bookmarks::BookmarkNode*)node;
@end @end
......
...@@ -43,7 +43,6 @@ ...@@ -43,7 +43,6 @@
#import "chrome/browser/ui/cocoa/bookmarks/bookmark_model_observer_for_cocoa.h" #import "chrome/browser/ui/cocoa/bookmarks/bookmark_model_observer_for_cocoa.h"
#import "chrome/browser/ui/cocoa/bookmarks/bookmark_name_folder_controller.h" #import "chrome/browser/ui/cocoa/bookmarks/bookmark_name_folder_controller.h"
#import "chrome/browser/ui/cocoa/browser_window_controller.h" #import "chrome/browser/ui/cocoa/browser_window_controller.h"
#import "chrome/browser/ui/cocoa/l10n_util.h"
#import "chrome/browser/ui/cocoa/menu_button.h" #import "chrome/browser/ui/cocoa/menu_button.h"
#import "chrome/browser/ui/cocoa/themed_window.h" #import "chrome/browser/ui/cocoa/themed_window.h"
#import "chrome/browser/ui/cocoa/toolbar/toolbar_controller.h" #import "chrome/browser/ui/cocoa/toolbar/toolbar_controller.h"
...@@ -79,7 +78,6 @@ ...@@ -79,7 +78,6 @@
#include "ui/resources/grit/ui_resources.h" #include "ui/resources/grit/ui_resources.h"
using base::UserMetricsAction; using base::UserMetricsAction;
using bookmarks::BookmarkBarLayout;
using bookmarks::BookmarkModel; using bookmarks::BookmarkModel;
using bookmarks::BookmarkNode; using bookmarks::BookmarkNode;
using bookmarks::BookmarkNodeData; using bookmarks::BookmarkNodeData;
...@@ -146,25 +144,6 @@ using content::WebContents; ...@@ -146,25 +144,6 @@ using content::WebContents;
// - The BWC should implement |-bookmarkBar:didChangeFromState:toState:| and // - The BWC should implement |-bookmarkBar:didChangeFromState:toState:| and
// |-bookmarkBar:willAnimateFromState:toState:| in order to inform the // |-bookmarkBar:willAnimateFromState:toState:| in order to inform the
// toolbar of required changes. // toolbar of required changes.
//
// Layout:
//
// Several events (initial load, changes to the bookmark model etc.) can
// require the bar layout to change. In most cases, this is accomplished
// by building a BookmarkBarLayout from the current state of the view,
// the bookmark model, and the managed bookmark service. If the calculated
// layout differs from the previous one, it's applied to the view
// via |applyLayout:animated:|. This is a cheap way to "coalesce" multiple
// potentially layout-changing events, since in practice, these events come
// in bursts and don't require a change.
//
// Temporary changes in layout during dragging are an exception to this,
// since the layout temporarily adjusts to the drag (for example, adding
// a placeholder space for a mid-drag button or removing the space previously
// taken up by a button which is being dragged off the bar). In this case,
// the original stored layout is maintained as the source of truth, and
// elements are laid out from a combination of the stored layout and the
// drag state. See |setDropInsertionPos:| for details.
namespace { namespace {
...@@ -172,11 +151,6 @@ namespace { ...@@ -172,11 +151,6 @@ namespace {
const NSTimeInterval kBookmarkBarAnimationDuration = 0.12; const NSTimeInterval kBookmarkBarAnimationDuration = 0.12;
const NSTimeInterval kDragAndDropAnimationDuration = 0.25; const NSTimeInterval kDragAndDropAnimationDuration = 0.25;
const int kMaxReusePoolSize = 10;
// Min width for no item text field and import bookmarks button.
const CGFloat kNoItemElementMinWidth = 30;
void RecordAppLaunch(Profile* profile, GURL url) { void RecordAppLaunch(Profile* profile, GURL url) {
const extensions::Extension* extension = const extensions::Extension* extension =
extensions::ExtensionRegistry::Get(profile)-> extensions::ExtensionRegistry::Get(profile)->
...@@ -188,71 +162,94 @@ void RecordAppLaunch(Profile* profile, GURL url) { ...@@ -188,71 +162,94 @@ void RecordAppLaunch(Profile* profile, GURL url) {
extension->GetType()); extension->GetType());
} }
const CGFloat kBookmarkButtonHeightMinusPadding =
bookmarks::kBookmarkButtonHeight - bookmarks::kBookmarkVerticalPadding * 2;
} // namespace } // namespace
namespace bookmarks { @interface BookmarkBarController ()
BookmarkBarLayout::BookmarkBarLayout() // Updates the sizes and positions of the subviews.
: visible_elements(0), - (void)layoutSubviews;
apps_button_offset(0),
managed_bookmarks_button_offset(0), // Moves to the given next state (from the current state), possibly animating.
supervised_bookmarks_button_offset(0), // If |animate| is NO, it will stop any running animation and jump to the given
off_the_side_button_offset(0), // state. If YES, it may either (depending on implementation) jump to the end of
other_bookmarks_button_offset(0), // the current animation and begin the next one, or stop the current animation
no_item_textfield_offset(0), // mid-flight and animate to the next state.
no_item_textfield_width(0), - (void)moveToState:(BookmarkBar::State)nextState
import_bookmarks_button_offset(0), withAnimation:(BOOL)animate;
import_bookmarks_button_width(0),
max_x(0){}; // Create buttons for all items in the given bookmark node tree.
BookmarkBarLayout::~BookmarkBarLayout(){}; // Modifies self->buttons_. Do not add more buttons than will fit on the view.
BookmarkBarLayout::BookmarkBarLayout(BookmarkBarLayout&& other) = default; - (void)addNodesToButtonList:(const BookmarkNode*)node;
BookmarkBarLayout& BookmarkBarLayout::operator=(BookmarkBarLayout&& other) =
default; // Create an autoreleased button appropriate for insertion into the bookmark
// bar. Update |xOffset| with the offset appropriate for the subsequent button.
bool operator==(const BookmarkBarLayout& lhs, const BookmarkBarLayout& rhs) { - (BookmarkButton*)buttonForNode:(const BookmarkNode*)node
return std::tie(lhs.visible_elements, lhs.apps_button_offset, xOffset:(int*)xOffset;
lhs.managed_bookmarks_button_offset,
lhs.supervised_bookmarks_button_offset, // Find a parent whose button is visible on the bookmark bar.
lhs.off_the_side_button_offset, - (BookmarkButton*)bookmarkButtonToPulseForNode:(const BookmarkNode*)node;
lhs.other_bookmarks_button_offset,
lhs.no_item_textfield_offset, lhs.no_item_textfield_width, // Puts stuff into the final state without animating, stopping a running
lhs.import_bookmarks_button_offset, // animation if necessary.
lhs.import_bookmarks_button_width, lhs.button_offsets, - (void)finalizeState;
lhs.max_x) ==
std::tie( // Stops any current animation in its tracks (midway).
rhs.visible_elements, rhs.apps_button_offset, - (void)stopCurrentAnimation;
rhs.managed_bookmarks_button_offset,
rhs.supervised_bookmarks_button_offset, // Show/hide the bookmark bar.
rhs.off_the_side_button_offset, rhs.other_bookmarks_button_offset, // if |animate| is YES, the changes are made using the animator; otherwise they
rhs.no_item_textfield_offset, rhs.no_item_textfield_width, // are made immediately.
rhs.import_bookmarks_button_offset, - (void)showBookmarkBarWithAnimation:(BOOL)animate;
rhs.import_bookmarks_button_width, rhs.button_offsets, rhs.max_x);
} // Handles animating the resize of the content view. Returns YES if it handled
// the animation, NO if not (and hence it should be done instantly).
bool operator!=(const BookmarkBarLayout& lhs, const BookmarkBarLayout& rhs) { - (BOOL)doBookmarkBarAnimation;
return !(lhs == rhs);
} // |point| is in the base coordinate system of the destination window;
// it comes from an id<NSDraggingInfo>. |copy| is YES if a copy is to be
} // namespace bookmarks // made and inserted into the new location while leaving the bookmark in
// the old location, otherwise move the bookmark by removing from its old
@implementation BookmarkBarController { // location and inserting into the new location.
BookmarkBarLayout layout_; - (BOOL)dragBookmark:(const BookmarkNode*)sourceNode
CGFloat originalNoItemTextFieldWidth_; to:(NSPoint)point
CGFloat originalImportBookmarksButtonWidth_; copy:(BOOL)copy;
CGFloat originalNoItemInterelementPadding_;
BOOL didCreateExtraButtons_; // Returns the index in the model for a drag to the location given by
// |point|. This is determined by finding the first button before the center
// Maps bookmark node IDs to instantiated buttons for ease of lookup. // of which |point| falls, scanning left to right. Note that, currently, only
std::unordered_map<int64_t, base::scoped_nsobject<BookmarkButton>> // the x-coordinate of |point| is considered. Though not currently implemented,
nodeIdToButtonMap_; // we may check for errors, in which case this would return negative value;
// callers should check for this.
// A place to stash bookmark buttons that have been removed from the bar - (int)indexForDragToPoint:(NSPoint)point;
// so that they can be reused instead of creating new ones.
base::scoped_nsobject<NSMutableArray> unusedButtonPool_; // Add or remove buttons to/from the bar until it is filled but not overflowed.
} - (void)redistributeButtonsOnBarAsNeeded;
// Determine the nature of the bookmark bar contents based on the number of
// buttons showing. If too many then show the off-the-side list, if none
// then show the no items label.
- (void)reconfigureBookmarkBar;
- (int)preferredHeight;
- (void)addButtonsToView;
- (BOOL)setManagedBookmarksButtonVisibility;
- (BOOL)setSupervisedBookmarksButtonVisibility;
- (BOOL)setOtherBookmarksButtonVisibility;
- (BOOL)setAppsPageShortcutButtonVisibility;
- (BookmarkButton*)createCustomBookmarkButtonForCell:(NSCell*)cell;
- (void)createManagedBookmarksButton;
- (void)createSupervisedBookmarksButton;
- (void)createOtherBookmarksButton;
- (void)createAppsPageShortcutButton;
- (void)openAppsPage:(id)sender;
- (void)centerNoItemsLabel;
- (void)positionRightSideButtons;
- (void)watchForExitEvent:(BOOL)watch;
- (void)resetAllButtonPositionsWithAnimation:(BOOL)animate;
@end
@implementation BookmarkBarController
@synthesize currentState = currentState_; @synthesize currentState = currentState_;
@synthesize lastState = lastState_; @synthesize lastState = lastState_;
...@@ -264,7 +261,8 @@ bool operator!=(const BookmarkBarLayout& lhs, const BookmarkBarLayout& rhs) { ...@@ -264,7 +261,8 @@ bool operator!=(const BookmarkBarLayout& lhs, const BookmarkBarLayout& rhs) {
- (id)initWithBrowser:(Browser*)browser - (id)initWithBrowser:(Browser*)browser
initialWidth:(CGFloat)initialWidth initialWidth:(CGFloat)initialWidth
delegate:(id<BookmarkBarControllerDelegate>)delegate { delegate:(id<BookmarkBarControllerDelegate>)delegate {
if ((self = [super initWithNibName:nil bundle:nil])) { if ((self = [super initWithNibName:@"BookmarkBar"
bundle:base::mac::FrameworkBundle()])) {
currentState_ = BookmarkBar::HIDDEN; currentState_ = BookmarkBar::HIDDEN;
lastState_ = BookmarkBar::HIDDEN; lastState_ = BookmarkBar::HIDDEN;
...@@ -275,12 +273,10 @@ bool operator!=(const BookmarkBarLayout& lhs, const BookmarkBarLayout& rhs) { ...@@ -275,12 +273,10 @@ bool operator!=(const BookmarkBarLayout& lhs, const BookmarkBarLayout& rhs) {
managedBookmarkService_ = managedBookmarkService_ =
ManagedBookmarkServiceFactory::GetForProfile(browser_->profile()); ManagedBookmarkServiceFactory::GetForProfile(browser_->profile());
buttons_.reset([[NSMutableArray alloc] init]); buttons_.reset([[NSMutableArray alloc] init]);
unusedButtonPool_.reset([[NSMutableArray alloc] init]);
delegate_ = delegate; delegate_ = delegate;
folderTarget_.reset( folderTarget_.reset(
[[BookmarkFolderTarget alloc] initWithController:self [[BookmarkFolderTarget alloc] initWithController:self
profile:browser_->profile()]); profile:browser_->profile()]);
didCreateExtraButtons_ = NO;
ResourceBundle& rb = ResourceBundle::GetSharedInstance(); ResourceBundle& rb = ResourceBundle::GetSharedInstance();
folderImage_.reset( folderImage_.reset(
...@@ -337,18 +333,21 @@ bool operator!=(const BookmarkBarLayout& lhs, const BookmarkBarLayout& rhs) { ...@@ -337,18 +333,21 @@ bool operator!=(const BookmarkBarLayout& lhs, const BookmarkBarLayout& rhs) {
buttonView_.reset([[BookmarkBarView alloc] buttonView_.reset([[BookmarkBarView alloc]
initWithController:self initWithController:self
frame:NSMakeRect(0, -2, 584, 144)]); frame:NSMakeRect(0, -2, 584, 144)]);
[buttonView_ setAutoresizingMask:NSViewWidthSizable | NSViewMaxXMargin]; [buttonView_ setAutoresizingMask:NSViewWidthSizable | NSViewMinYMargin |
NSViewMaxXMargin];
[[buttonView_ importBookmarksButton] setTarget:self]; [[buttonView_ importBookmarksButton] setTarget:self];
[[buttonView_ importBookmarksButton] setAction:@selector(importBookmarks:)]; [[buttonView_ importBookmarksButton] setAction:@selector(importBookmarks:)];
[self.view addSubview:buttonView_]; [self createOffTheSideButton];
[buttonView_ addSubview:offTheSideButton_];
// viewDidLoad became part of the API in 10.10. [self.view addSubview:buttonView_];
// viewDidLoad became part of the API in 10.10
if (!base::mac::IsAtLeastOS10_10()) if (!base::mac::IsAtLeastOS10_10())
[self viewDidLoad]; [self viewDidLoad];
} }
- (BookmarkButton*)findAncestorButtonOnBarForNode:(const BookmarkNode*)node { - (BookmarkButton*)bookmarkButtonToPulseForNode:(const BookmarkNode*)node {
// Find the closest parent that is visible on the bar. // Find the closest parent that is visible on the bar.
while (node) { while (node) {
// Check if we've reached one of the special buttons. Otherwise, if the next // Check if we've reached one of the special buttons. Otherwise, if the next
...@@ -383,7 +382,7 @@ bool operator!=(const BookmarkBarLayout& lhs, const BookmarkBarLayout& rhs) { ...@@ -383,7 +382,7 @@ bool operator!=(const BookmarkBarLayout& lhs, const BookmarkBarLayout& rhs) {
- (void)startPulsingBookmarkNode:(const BookmarkNode*)node { - (void)startPulsingBookmarkNode:(const BookmarkNode*)node {
[self stopPulsingBookmarkNode]; [self stopPulsingBookmarkNode];
pulsingButton_.reset([self findAncestorButtonOnBarForNode:node], pulsingButton_.reset([self bookmarkButtonToPulseForNode:node],
base::scoped_policy::RETAIN); base::scoped_policy::RETAIN);
if (!pulsingButton_) if (!pulsingButton_)
return; return;
...@@ -439,20 +438,12 @@ bool operator!=(const BookmarkBarLayout& lhs, const BookmarkBarLayout& rhs) { ...@@ -439,20 +438,12 @@ bool operator!=(const BookmarkBarLayout& lhs, const BookmarkBarLayout& rhs) {
[view performSelector:@selector(setController:) withObject:nil]; [view performSelector:@selector(setController:) withObject:nil];
// For safety, make sure the buttons can no longer call us. // For safety, make sure the buttons can no longer call us.
base::scoped_nsobject<NSMutableArray> buttons([buttons_ mutableCopy]); for (BookmarkButton* button in buttons_.get()) {
[buttons addObjectsFromArray:unusedButtonPool_];
if (didCreateExtraButtons_) {
[buttons addObjectsFromArray:@[
appsPageShortcutButton_, managedBookmarksButton_,
supervisedBookmarksButton_, otherBookmarksButton_, offTheSideButton_
]];
}
for (BookmarkButton* button in buttons.get()) {
[button setDelegate:nil]; [button setDelegate:nil];
[button setTarget:nil]; [button setTarget:nil];
[button setAction:nil]; [button setAction:nil];
} }
bridge_.reset(NULL); bridge_.reset(NULL);
[[NSNotificationCenter defaultCenter] removeObserver:self]; [[NSNotificationCenter defaultCenter] removeObserver:self];
[self watchForExitEvent:NO]; [self watchForExitEvent:NO];
...@@ -465,13 +456,20 @@ bool operator!=(const BookmarkBarLayout& lhs, const BookmarkBarLayout& rhs) { ...@@ -465,13 +456,20 @@ bool operator!=(const BookmarkBarLayout& lhs, const BookmarkBarLayout& rhs) {
// Remember the original sizes of the 'no items' and 'import bookmarks' // Remember the original sizes of the 'no items' and 'import bookmarks'
// fields to aid in resizing when the window frame changes. // fields to aid in resizing when the window frame changes.
NSRect noItemTextFieldFrame = [[buttonView_ noItemTextField] frame]; originalNoItemsRect_ = [[buttonView_ noItemTextfield] frame];
NSRect noItemButtonFrame = [[buttonView_ importBookmarksButton] frame]; originalImportBookmarksRect_ = [[buttonView_ importBookmarksButton] frame];
originalNoItemTextFieldWidth_ = NSWidth(noItemTextFieldFrame);
originalImportBookmarksButtonWidth_ = NSWidth(noItemButtonFrame); // Bookmark buttons start farther from the bookmark bar's left edge so
originalNoItemInterelementPadding_ = // adjust the positions of the noItems and importBookmarks textfields.
NSMinX(noItemButtonFrame) - NSMaxX(noItemTextFieldFrame); const CGFloat kBookmarksTextfieldOffsetX = 14;
originalNoItemsRect_.origin.x += kBookmarksTextfieldOffsetX;
[[buttonView_ noItemTextfield] setFrame:originalNoItemsRect_];
originalImportBookmarksRect_.origin.x += kBookmarksTextfieldOffsetX;
[[buttonView_ importBookmarksButton] setFrame:originalImportBookmarksRect_];
// When resized we may need to add new buttons, or remove them (if
// no longer visible), or add/remove the "off the side" menu.
[[self view] setPostsFrameChangedNotifications:YES]; [[self view] setPostsFrameChangedNotifications:YES];
[[NSNotificationCenter defaultCenter] [[NSNotificationCenter defaultCenter]
addObserver:self addObserver:self
...@@ -490,7 +488,6 @@ bool operator!=(const BookmarkBarLayout& lhs, const BookmarkBarLayout& rhs) { ...@@ -490,7 +488,6 @@ bool operator!=(const BookmarkBarLayout& lhs, const BookmarkBarLayout& rhs) {
selector:@selector(willEnterOrLeaveFullscreen:) selector:@selector(willEnterOrLeaveFullscreen:)
name:NSWindowWillExitFullScreenNotification name:NSWindowWillExitFullScreenNotification
object:nil]; object:nil];
[self layoutSubviews];
// Don't pass ourself along (as 'self') until our init is completely // Don't pass ourself along (as 'self') until our init is completely
// done. Thus, this call is (almost) last. // done. Thus, this call is (almost) last.
...@@ -556,7 +553,6 @@ bool operator!=(const BookmarkBarLayout& lhs, const BookmarkBarLayout& rhs) { ...@@ -556,7 +553,6 @@ bool operator!=(const BookmarkBarLayout& lhs, const BookmarkBarLayout& rhs) {
[[self view] autoresizingMask]); [[self view] autoresizingMask]);
[[self view] setFrame:frame]; [[self view] setFrame:frame];
[self layoutSubviews]; [self layoutSubviews];
[self frameDidChange];
} }
// Change the layout of the bookmark bar's subviews in response to a visibility // Change the layout of the bookmark bar's subviews in response to a visibility
...@@ -572,6 +568,7 @@ bool operator!=(const BookmarkBarLayout& lhs, const BookmarkBarLayout& rhs) { ...@@ -572,6 +568,7 @@ bool operator!=(const BookmarkBarLayout& lhs, const BookmarkBarLayout& rhs) {
padding = bookmarks::kNTPBookmarkBarPadding; padding = bookmarks::kNTPBookmarkBarPadding;
buttonViewFrame = buttonViewFrame =
NSInsetRect(buttonViewFrame, morph * padding, morph * padding); NSInsetRect(buttonViewFrame, morph * padding, morph * padding);
[buttonView_ setFrame:buttonViewFrame]; [buttonView_ setFrame:buttonViewFrame];
// Update bookmark button backgrounds. // Update bookmark button backgrounds.
...@@ -594,6 +591,19 @@ bool operator!=(const BookmarkBarLayout& lhs, const BookmarkBarLayout& rhs) { ...@@ -594,6 +591,19 @@ bool operator!=(const BookmarkBarLayout& lhs, const BookmarkBarLayout& rhs) {
[self showBookmarkBarWithAnimation:NO]; [self showBookmarkBarWithAnimation:NO];
} }
- (void)updateExtraButtonsVisibility {
if (!appsPageShortcutButton_.get() ||
!managedBookmarksButton_.get() ||
!supervisedBookmarksButton_.get()) {
return;
}
[self setAppsPageShortcutButtonVisibility];
[self setManagedBookmarksButtonVisibility];
[self setSupervisedBookmarksButtonVisibility];
[self resetAllButtonPositionsWithAnimation:NO];
[self reconfigureBookmarkBar];
}
- (void)updateHiddenState { - (void)updateHiddenState {
BOOL oldHidden = [[self view] isHidden]; BOOL oldHidden = [[self view] isHidden];
BOOL newHidden = ![self isVisible]; BOOL newHidden = ![self isVisible];
...@@ -818,7 +828,7 @@ bool operator!=(const BookmarkBarLayout& lhs, const BookmarkBarLayout& rhs) { ...@@ -818,7 +828,7 @@ bool operator!=(const BookmarkBarLayout& lhs, const BookmarkBarLayout& rhs) {
// Middle click on chevron should not open bookmarks under it, instead just // Middle click on chevron should not open bookmarks under it, instead just
// open its folder menu. // open its folder menu.
if (sender == offTheSideButton_.get()) { if (sender == offTheSideButton_.get()) {
[[sender cell] setStartingChildIndex:layout_.VisibleButtonCount()]; [[sender cell] setStartingChildIndex:displayedButtonCount_];
NSEvent* event = [NSApp currentEvent]; NSEvent* event = [NSApp currentEvent];
if ([event type] == NSOtherMouseUp) { if ([event type] == NSOtherMouseUp) {
[self openOrCloseBookmarkFolderForOffTheSideButton]; [self openOrCloseBookmarkFolderForOffTheSideButton];
...@@ -881,6 +891,55 @@ bool operator!=(const BookmarkBarLayout& lhs, const BookmarkBarLayout& rhs) { ...@@ -881,6 +891,55 @@ bool operator!=(const BookmarkBarLayout& lhs, const BookmarkBarLayout& rhs) {
BOOKMARK_LAUNCH_LOCATION_ATTACHED_BAR; BOOKMARK_LAUNCH_LOCATION_ATTACHED_BAR;
} }
// Position the right-side buttons including the off-the-side chevron.
- (void)positionRightSideButtons {
int maxX = NSMaxX([[self buttonView] bounds]) -
bookmarks::kBookmarkHorizontalPadding;
int right = maxX;
int ignored = 0;
NSRect frame = [self frameForBookmarkButtonFromCell:
[otherBookmarksButton_ cell] xOffset:&ignored];
if (![otherBookmarksButton_ isHidden]) {
right -= NSWidth(frame);
frame.origin.x = right;
} else {
frame.origin.x = maxX - NSWidth(frame);
}
[otherBookmarksButton_ setFrame:frame];
frame = [offTheSideButton_ frame];
frame.size.height = bookmarks::kBookmarkFolderButtonHeight;
right -= frame.size.width;
frame.origin.x = right;
[offTheSideButton_ setFrame:frame];
}
// Configure the off-the-side button (e.g. specify the node range,
// check if we should enable or disable it, etc).
- (void)configureOffTheSideButtonContentsAndVisibility {
[[offTheSideButton_ cell] setStartingChildIndex:displayedButtonCount_];
[[offTheSideButton_ cell]
setBookmarkNode:bookmarkModel_->bookmark_bar_node()];
int bookmarkChildren = bookmarkModel_->bookmark_bar_node()->child_count();
if (bookmarkChildren > displayedButtonCount_) {
[offTheSideButton_ setHidden:NO];
// Set the off the side button as needing re-display. This is needed to
// avoid the button being shown with a black background the first time
// it's displayed. See https://codereview.chromium.org/1630453002/ for
// more context.
[offTheSideButton_ setNeedsDisplay:YES];
} else {
// If we just deleted the last item in an off-the-side menu so the
// button will be going away, make sure the menu goes away.
if (folderController_ &&
([folderController_ parentButton] == offTheSideButton_))
[self closeAllBookmarkFolders];
// (And hide the button, too.)
[offTheSideButton_ setHidden:YES];
}
}
// Main menubar observation code, so we can know to close our fake menus if the // Main menubar observation code, so we can know to close our fake menus if the
// user clicks on the actual menubar, as multiple unconnected menus sharing // user clicks on the actual menubar, as multiple unconnected menus sharing
// the screen looks weird. // the screen looks weird.
...@@ -937,9 +996,19 @@ bool operator!=(const BookmarkBarLayout& lhs, const BookmarkBarLayout& rhs) { ...@@ -937,9 +996,19 @@ bool operator!=(const BookmarkBarLayout& lhs, const BookmarkBarLayout& rhs) {
} }
} }
// Show/hide the bookmark bar. // Keep the "no items" label centered in response to a frame size change.
// If |animate| is YES, the changes are made using the animator; otherwise they - (void)centerNoItemsLabel {
// are made immediately. // Note that this computation is done in the parent's coordinate system,
// which is unflipped. Also, we want the label to be a fixed distance from
// the bottom, so that it slides up properly (on animating to hidden).
// The textfield sits in the itemcontainer, so to center it we maintain
// equal vertical padding on the top and bottom.
int yoffset = (NSHeight([[buttonView_ noItemTextfield] frame]) -
NSHeight([[buttonView_ noItemContainer] frame])) / 2;
[[buttonView_ noItemContainer] setFrameOrigin:NSMakePoint(0, yoffset)];
}
// (Private)
- (void)showBookmarkBarWithAnimation:(BOOL)animate { - (void)showBookmarkBarWithAnimation:(BOOL)animate {
if (animate && stateAnimationsEnabled_) { if (animate && stateAnimationsEnabled_) {
// If |-doBookmarkBarAnimation| does the animation, we're done. // If |-doBookmarkBarAnimation| does the animation, we're done.
...@@ -966,8 +1035,7 @@ bool operator!=(const BookmarkBarLayout& lhs, const BookmarkBarLayout& rhs) { ...@@ -966,8 +1035,7 @@ bool operator!=(const BookmarkBarLayout& lhs, const BookmarkBarLayout& rhs) {
[self frameDidChange]; [self frameDidChange];
} }
// Handles animating the resize of the content view. Returns YES if it handled // (Private)
// the animation, NO if not (and hence it should be done instantly).
- (BOOL)doBookmarkBarAnimation { - (BOOL)doBookmarkBarAnimation {
BookmarkBarToolbarView* view = [self controlledView]; BookmarkBarToolbarView* view = [self controlledView];
if ([self isAnimatingFromState:BookmarkBar::HIDDEN if ([self isAnimatingFromState:BookmarkBar::HIDDEN
...@@ -1038,72 +1106,189 @@ bool operator!=(const BookmarkBarLayout& lhs, const BookmarkBarLayout& rhs) { ...@@ -1038,72 +1106,189 @@ bool operator!=(const BookmarkBarLayout& lhs, const BookmarkBarLayout& rhs) {
return std::min([cell cellSize].width, bookmarks::kDefaultBookmarkWidth); return std::min([cell cellSize].width, bookmarks::kDefaultBookmarkWidth);
} }
- (BookmarkButton*)buttonForNode:(const BookmarkNode*)node { // For the given root node of the bookmark bar, show or hide (as
BookmarkButton* button = nil; // appropriate) the "no items" container (text which says "bookmarks
int64_t nodeId = node->id(); // go here").
auto buttonIt = nodeIdToButtonMap_.find(nodeId); - (void)showOrHideNoItemContainerForNode:(const BookmarkNode*)node {
if (buttonIt != nodeIdToButtonMap_.end()) { BOOL hideNoItemWarning = !node->empty();
button = (*buttonIt).second.get(); [[buttonView_ noItemContainer] setHidden:hideNoItemWarning];
[self updateTitleAndTooltipForButton:button]; }
} else if ([unusedButtonPool_ count] > 0) {
button = [[[unusedButtonPool_ firstObject] retain] autorelease]; // TODO(jrg): write a "build bar" so there is a nice spot for things
[unusedButtonPool_ removeObjectAtIndex:0]; // like the contextual menu which is invoked when not over a
BOOL darkTheme = [[[self view] window] hasDarkTheme]; // bookmark. On Safari that menu has a "new folder" option.
[[button cell] - (void)addNodesToButtonList:(const BookmarkNode*)node {
setBookmarkNode:node [self showOrHideNoItemContainerForNode:node];
image:[self faviconForNode:node forADarkTheme:darkTheme]];
} else { CGFloat maxViewX = NSMaxX([[self view] bounds]);
int xOffset =
bookmarks::kBookmarkLeftMargin - bookmarks::kBookmarkHorizontalPadding;
// Draw the apps bookmark if needed.
if (![appsPageShortcutButton_ isHidden]) {
NSRect frame =
[self frameForBookmarkButtonFromCell:[appsPageShortcutButton_ cell]
xOffset:&xOffset];
[appsPageShortcutButton_ setFrame:frame];
}
// Draw the managed bookmark folder if needed.
if (![managedBookmarksButton_ isHidden]) {
xOffset += bookmarks::kBookmarkHorizontalPadding;
NSRect frame =
[self frameForBookmarkButtonFromCell:[managedBookmarksButton_ cell]
xOffset:&xOffset];
[managedBookmarksButton_ setFrame:frame];
}
// Draw the supervised bookmark folder if needed.
if (![supervisedBookmarksButton_ isHidden]) {
xOffset += bookmarks::kBookmarkHorizontalPadding;
NSRect frame =
[self frameForBookmarkButtonFromCell:[supervisedBookmarksButton_ cell]
xOffset:&xOffset];
[supervisedBookmarksButton_ setFrame:frame];
}
for (int i = 0; i < node->child_count(); i++) {
const BookmarkNode* child = node->GetChild(i);
BookmarkButton* button = [self buttonForNode:child xOffset:&xOffset];
if (NSMinX([button frame]) >= maxViewX) {
[button setDelegate:nil];
break;
}
[buttons_ addObject:button];
}
}
- (BookmarkButton*)buttonForNode:(const BookmarkNode*)node
xOffset:(int*)xOffset {
BookmarkButtonCell* cell = [self cellForBookmarkNode:node]; BookmarkButtonCell* cell = [self cellForBookmarkNode:node];
NSRect frame = NSMakeRect(0, bookmarks::kBookmarkVerticalPadding, 0, NSRect frame = [self frameForBookmarkButtonFromCell:cell xOffset:xOffset];
kBookmarkButtonHeightMinusPadding);
button = [[[BookmarkButton alloc] initWithFrame:frame] autorelease]; base::scoped_nsobject<BookmarkButton> button(
[[BookmarkButton alloc] initWithFrame:frame]);
DCHECK(button.get());
// [NSButton setCell:] warns to NOT use setCell: other than in the
// initializer of a control. However, we are using a basic
// NSButton whose initializer does not take an NSCell as an
// object. To honor the assumed semantics, we do nothing with
// NSButton between alloc/init and setCell:.
[button setCell:cell]; [button setCell:cell];
[buttonView_ addSubview:button];
[button setDelegate:self]; [button setDelegate:self];
// We cannot set the button cell's text color until it is placed in
// the button (e.g. the [button setCell:cell] call right above). We
// also cannot set the cell's text color until the view is added to
// the hierarchy. If that second part is now true, set the color.
// (If not we'll set the color on the 1st themeChanged:
// notification.)
const ui::ThemeProvider* themeProvider = [[[self view] window] themeProvider];
if (themeProvider) {
NSColor* color =
themeProvider->GetNSColor(ThemeProperties::COLOR_BOOKMARK_TEXT);
[cell setTextColor:color];
} }
DCHECK(button);
// Do setup.
nodeIdToButtonMap_.insert(
{nodeId, base::scoped_nsobject<BookmarkButton>([button retain])});
if (node->is_folder()) { if (node->is_folder()) {
[button setTarget:self]; [button setTarget:self];
[button setAction:@selector(openBookmarkFolderFromButton:)]; [button setAction:@selector(openBookmarkFolderFromButton:)];
[[button draggableButton] setActsOnMouseDown:YES]; [[button draggableButton] setActsOnMouseDown:YES];
// If it has a title, and it will be truncated, show full title in
// tooltip.
NSString* title = base::SysUTF16ToNSString(node->GetTitle());
if ([title length] &&
[[button cell] cellSize].width > bookmarks::kDefaultBookmarkWidth) {
[button setToolTip:title];
}
} else { } else {
// Make the button do something. // Make the button do something
[button setTarget:self]; [button setTarget:self];
[button setAction:@selector(openBookmark:)]; [button setAction:@selector(openBookmark:)];
[[button draggableButton] setActsOnMouseDown:NO]; if (node->is_url())
[button setToolTip:[BookmarkMenuCocoaController tooltipForNode:node]];
} }
[self updateTitleAndTooltipForButton:button]; return [[button.get() retain] autorelease];
return button;
} }
- (void)updateTitleAndTooltipForButton:(BookmarkButton*)button { // Add bookmark buttons to the view only if they are completely
const BookmarkNode* node = [button bookmarkNode]; // visible and don't overlap the "other bookmarks". Remove buttons
CGFloat buttonWidth = [self widthOfButtonForNode:node]; // which are clipped. Called when building the bookmark bar the first time.
NSString* buttonTitle = base::SysUTF16ToNSString(node->GetTitle()); - (void)addButtonsToView {
displayedButtonCount_ = 0;
if (NSWidth([button frame]) == buttonWidth && NSMutableArray* buttons = [self buttons];
[[button title] isEqualToString:buttonTitle]) for (NSButton* button in buttons) {
return; if (NSMaxX([button frame]) > (NSMinX([offTheSideButton_ frame]) -
bookmarks::kBookmarkHorizontalPadding))
break;
[buttonView_ addSubview:button];
++displayedButtonCount_;
}
NSUInteger removalCount =
[buttons count] - (NSUInteger)displayedButtonCount_;
if (removalCount > 0) {
NSRange removalRange = NSMakeRange(displayedButtonCount_, removalCount);
[buttons removeObjectsInRange:removalRange];
}
}
CGRect frame = [button frame]; // Shows or hides the Managed, Supervised, or Other Bookmarks button as
frame.size.width = buttonWidth; // appropriate, and returns whether it ended up visible.
[button setFrame:frame]; - (BOOL)setBookmarkButtonVisibility:(BookmarkButton*)button
[[button cell] setTitle:buttonTitle]; canShow:(BOOL)show
NSString* tooltip = nil; resetAllButtonPositions:(BOOL)resetButtons {
if (!button)
return NO;
// Folders show a tooltip iff the title is truncated. BOOL visible = ![button bookmarkNode]->empty() && show;
if (node->is_folder() && [buttonTitle length] > 0 && BOOL currentVisibility = ![button isHidden];
[[button cell] cellSize].width < buttonWidth) { if (currentVisibility != visible) {
tooltip = buttonTitle; [button setHidden:!visible];
} else if (node->is_url()) { if (resetButtons)
tooltip = [BookmarkMenuCocoaController tooltipForNode:node]; [self resetAllButtonPositionsWithAnimation:NO];
} }
return visible;
}
[button setToolTip:tooltip]; // Shows or hides the Managed Bookmarks button as appropriate, and returns
// whether it ended up visible.
- (BOOL)setManagedBookmarksButtonVisibility {
PrefService* prefs = browser_->profile()->GetPrefs();
BOOL prefIsSet =
prefs->GetBoolean(bookmarks::prefs::kShowManagedBookmarksInBookmarkBar);
return [self setBookmarkButtonVisibility:managedBookmarksButton_.get()
canShow:prefIsSet
resetAllButtonPositions:YES];
}
// Shows or hides the Supervised Bookmarks button as appropriate, and returns
// whether it ended up visible.
- (BOOL)setSupervisedBookmarksButtonVisibility {
return [self setBookmarkButtonVisibility:supervisedBookmarksButton_.get()
canShow:YES
resetAllButtonPositions:YES];
}
// Shows or hides the Other Bookmarks button as appropriate, and returns
// whether it ended up visible.
- (BOOL)setOtherBookmarksButtonVisibility {
return [self setBookmarkButtonVisibility:otherBookmarksButton_.get()
canShow:YES
resetAllButtonPositions:NO];
}
// Shows or hides the Apps button as appropriate, and returns whether it ended
// up visible.
- (BOOL)setAppsPageShortcutButtonVisibility {
if (!appsPageShortcutButton_.get())
return NO;
BOOL visible =
bookmarkModel_->loaded() &&
chrome::ShouldShowAppsShortcutInBookmarkBar(browser_->profile());
[appsPageShortcutButton_ setHidden:!visible];
return visible;
} }
// Creates a bookmark bar button that does not correspond to a regular bookmark // Creates a bookmark bar button that does not correspond to a regular bookmark
...@@ -1131,6 +1316,9 @@ bool operator!=(const BookmarkBarLayout& lhs, const BookmarkBarLayout& rhs) { ...@@ -1131,6 +1316,9 @@ bool operator!=(const BookmarkBarLayout& lhs, const BookmarkBarLayout& rhs) {
NSCell* cell = [managedBookmarksButton_ cell]; NSCell* cell = [managedBookmarksButton_ cell];
[cell setTitle:title]; [cell setTitle:title];
// Its visibility may have changed too.
[self setManagedBookmarksButtonVisibility];
return; return;
} }
...@@ -1139,17 +1327,16 @@ bool operator!=(const BookmarkBarLayout& lhs, const BookmarkBarLayout& rhs) { ...@@ -1139,17 +1327,16 @@ bool operator!=(const BookmarkBarLayout& lhs, const BookmarkBarLayout& rhs) {
managedBookmarksButton_.reset([self createCustomBookmarkButtonForCell:cell]); managedBookmarksButton_.reset([self createCustomBookmarkButtonForCell:cell]);
[managedBookmarksButton_ setAction:@selector(openBookmarkFolderFromButton:)]; [managedBookmarksButton_ setAction:@selector(openBookmarkFolderFromButton:)];
view_id_util::SetID(managedBookmarksButton_.get(), VIEW_ID_MANAGED_BOOKMARKS); view_id_util::SetID(managedBookmarksButton_.get(), VIEW_ID_MANAGED_BOOKMARKS);
NSRect frame = NSMakeRect(0, bookmarks::kBookmarkVerticalPadding,
[self widthForBookmarkButtonCell:cell],
kBookmarkButtonHeightMinusPadding);
[managedBookmarksButton_ setFrame:frame];
[buttonView_ addSubview:managedBookmarksButton_.get()]; [buttonView_ addSubview:managedBookmarksButton_.get()];
[self setManagedBookmarksButtonVisibility];
} }
// Creates the button for "Supervised Bookmarks", but does not position it. // Creates the button for "Supervised Bookmarks", but does not position it.
- (void)createSupervisedBookmarksButton { - (void)createSupervisedBookmarksButton {
if (supervisedBookmarksButton_.get()) { if (supervisedBookmarksButton_.get()) {
// The button's already there, but its visibility may have changed.
[self setSupervisedBookmarksButtonVisibility];
return; return;
} }
...@@ -1161,11 +1348,9 @@ bool operator!=(const BookmarkBarLayout& lhs, const BookmarkBarLayout& rhs) { ...@@ -1161,11 +1348,9 @@ bool operator!=(const BookmarkBarLayout& lhs, const BookmarkBarLayout& rhs) {
setAction:@selector(openBookmarkFolderFromButton:)]; setAction:@selector(openBookmarkFolderFromButton:)];
view_id_util::SetID(supervisedBookmarksButton_.get(), view_id_util::SetID(supervisedBookmarksButton_.get(),
VIEW_ID_SUPERVISED_BOOKMARKS); VIEW_ID_SUPERVISED_BOOKMARKS);
NSRect frame = NSMakeRect(0, bookmarks::kBookmarkVerticalPadding,
[self widthForBookmarkButtonCell:cell],
kBookmarkButtonHeightMinusPadding);
[supervisedBookmarksButton_ setFrame:frame];
[buttonView_ addSubview:supervisedBookmarksButton_.get()]; [buttonView_ addSubview:supervisedBookmarksButton_.get()];
[self setSupervisedBookmarksButtonVisibility];
} }
// Creates the button for "Other Bookmarks", but does not position it. // Creates the button for "Other Bookmarks", but does not position it.
...@@ -1173,23 +1358,27 @@ bool operator!=(const BookmarkBarLayout& lhs, const BookmarkBarLayout& rhs) { ...@@ -1173,23 +1358,27 @@ bool operator!=(const BookmarkBarLayout& lhs, const BookmarkBarLayout& rhs) {
// Can't create this until the model is loaded, but only need to // Can't create this until the model is loaded, but only need to
// create it once. // create it once.
if (otherBookmarksButton_.get()) { if (otherBookmarksButton_.get()) {
[self setOtherBookmarksButtonVisibility];
return; return;
} }
NSCell* cell = [self cellForBookmarkNode:bookmarkModel_->other_node()]; NSCell* cell = [self cellForBookmarkNode:bookmarkModel_->other_node()];
otherBookmarksButton_.reset([self createCustomBookmarkButtonForCell:cell]); otherBookmarksButton_.reset([self createCustomBookmarkButtonForCell:cell]);
// Peg at right; keep same height as bar.
[otherBookmarksButton_ setAutoresizingMask:(NSViewMinXMargin)];
[otherBookmarksButton_ setAction:@selector(openBookmarkFolderFromButton:)]; [otherBookmarksButton_ setAction:@selector(openBookmarkFolderFromButton:)];
NSRect frame = NSMakeRect(0, bookmarks::kBookmarkVerticalPadding,
[self widthForBookmarkButtonCell:cell],
kBookmarkButtonHeightMinusPadding);
[otherBookmarksButton_ setFrame:frame];
view_id_util::SetID(otherBookmarksButton_.get(), VIEW_ID_OTHER_BOOKMARKS); view_id_util::SetID(otherBookmarksButton_.get(), VIEW_ID_OTHER_BOOKMARKS);
[buttonView_ addSubview:otherBookmarksButton_.get()]; [buttonView_ addSubview:otherBookmarksButton_.get()];
[self setOtherBookmarksButtonVisibility];
} }
// Creates the button for "Apps", but does not position it. // Creates the button for "Apps", but does not position it.
- (void)createAppsPageShortcutButton { - (void)createAppsPageShortcutButton {
// Can't create this until the model is loaded, but only need to
// create it once.
if (appsPageShortcutButton_.get()) { if (appsPageShortcutButton_.get()) {
[self setAppsPageShortcutButtonVisibility];
return; return;
} }
...@@ -1199,29 +1388,20 @@ bool operator!=(const BookmarkBarLayout& lhs, const BookmarkBarLayout& rhs) { ...@@ -1199,29 +1388,20 @@ bool operator!=(const BookmarkBarLayout& lhs, const BookmarkBarLayout& rhs) {
IDR_BOOKMARK_BAR_APPS_SHORTCUT).ToNSImage(); IDR_BOOKMARK_BAR_APPS_SHORTCUT).ToNSImage();
NSCell* cell = [self cellForCustomButtonWithText:text NSCell* cell = [self cellForCustomButtonWithText:text
image:image]; image:image];
NSRect frame;
frame.origin.y = bookmarks::kBookmarkVerticalPadding;
frame.size = NSMakeSize([self widthForBookmarkButtonCell:cell],
kBookmarkButtonHeightMinusPadding);
appsPageShortcutButton_.reset([self createCustomBookmarkButtonForCell:cell]); appsPageShortcutButton_.reset([self createCustomBookmarkButtonForCell:cell]);
[appsPageShortcutButton_ setFrame:frame];
[[appsPageShortcutButton_ draggableButton] setActsOnMouseDown:NO]; [[appsPageShortcutButton_ draggableButton] setActsOnMouseDown:NO];
[appsPageShortcutButton_ setAction:@selector(openAppsPage:)]; [appsPageShortcutButton_ setAction:@selector(openAppsPage:)];
NSString* tooltip = NSString* tooltip =
l10n_util::GetNSString(IDS_BOOKMARK_BAR_APPS_SHORTCUT_TOOLTIP); l10n_util::GetNSString(IDS_BOOKMARK_BAR_APPS_SHORTCUT_TOOLTIP);
[appsPageShortcutButton_ setToolTip:tooltip]; [appsPageShortcutButton_ setToolTip:tooltip];
[buttonView_ addSubview:appsPageShortcutButton_.get()]; [buttonView_ addSubview:appsPageShortcutButton_.get()];
[self setAppsPageShortcutButtonVisibility];
} }
// Creates the "off-the-side" (chevron/overflow) button but
// does not position it.
- (void)createOffTheSideButton { - (void)createOffTheSideButton {
if (offTheSideButton_.get()) {
return;
}
DCHECK(bookmarkModel_->loaded());
offTheSideButton_.reset( offTheSideButton_.reset(
[[BookmarkButton alloc] initWithFrame:NSMakeRect(0, 0, 20, 24)]); [[BookmarkButton alloc] initWithFrame:NSMakeRect(586, 0, 20, 24)]);
id offTheSideCell = [BookmarkButtonCell offTheSideButtonCell]; id offTheSideCell = [BookmarkButtonCell offTheSideButtonCell];
[offTheSideCell setTag:kMaterialStandardButtonTypeWithLimitedClickFeedback]; [offTheSideCell setTag:kMaterialStandardButtonTypeWithLimitedClickFeedback];
[offTheSideCell setImagePosition:NSImageOnly]; [offTheSideCell setImagePosition:NSImageOnly];
...@@ -1229,8 +1409,6 @@ bool operator!=(const BookmarkBarLayout& lhs, const BookmarkBarLayout& rhs) { ...@@ -1229,8 +1409,6 @@ bool operator!=(const BookmarkBarLayout& lhs, const BookmarkBarLayout& rhs) {
[offTheSideCell setHighlightsBy:NSNoCellMask]; [offTheSideCell setHighlightsBy:NSNoCellMask];
[offTheSideCell setShowsBorderOnlyWhileMouseInside:YES]; [offTheSideCell setShowsBorderOnlyWhileMouseInside:YES];
[offTheSideCell setBezelStyle:NSShadowlessSquareBezelStyle]; [offTheSideCell setBezelStyle:NSShadowlessSquareBezelStyle];
[offTheSideCell setBookmarkNode:bookmarkModel_->bookmark_bar_node()];
[offTheSideButton_ setCell:offTheSideCell]; [offTheSideButton_ setCell:offTheSideCell];
[offTheSideButton_ setImage:[self offTheSideButtonImage:NO]]; [offTheSideButton_ setImage:[self offTheSideButtonImage:NO]];
[offTheSideButton_ setButtonType:NSMomentaryLightButton]; [offTheSideButton_ setButtonType:NSMomentaryLightButton];
...@@ -1240,22 +1418,6 @@ bool operator!=(const BookmarkBarLayout& lhs, const BookmarkBarLayout& rhs) { ...@@ -1240,22 +1418,6 @@ bool operator!=(const BookmarkBarLayout& lhs, const BookmarkBarLayout& rhs) {
[offTheSideButton_ setDelegate:self]; [offTheSideButton_ setDelegate:self];
[[offTheSideButton_ draggableButton] setDraggable:NO]; [[offTheSideButton_ draggableButton] setDraggable:NO];
[[offTheSideButton_ draggableButton] setActsOnMouseDown:YES]; [[offTheSideButton_ draggableButton] setActsOnMouseDown:YES];
[offTheSideButton_ setHidden:YES];
[buttonView_ addSubview:offTheSideButton_];
}
- (void)updateExtraButtonsVisibility {
[self rebuildLayoutWithAnimated:NO];
}
- (void)createExtraButtons {
DCHECK(!didCreateExtraButtons_);
[self createSupervisedBookmarksButton];
[self createManagedBookmarksButton];
[self createOtherBookmarksButton];
[self createAppsPageShortcutButton];
[self createOffTheSideButton];
didCreateExtraButtons_ = YES;
} }
- (void)openAppsPage:(id)sender { - (void)openAppsPage:(id)sender {
...@@ -1272,11 +1434,6 @@ bool operator!=(const BookmarkBarLayout& lhs, const BookmarkBarLayout& rhs) { ...@@ -1272,11 +1434,6 @@ bool operator!=(const BookmarkBarLayout& lhs, const BookmarkBarLayout& rhs) {
[contextMenuController_ cancelTracking]; [contextMenuController_ cancelTracking];
} }
// Moves to the given next state (from the current state), possibly animating.
// If |animate| is NO, it will stop any running animation and jump to the given
// state. If YES, it may either (depending on implementation) jump to the end of
// the current animation and begin the next one, or stop the current animation
// mid-flight and animate to the next state.
- (void)moveToState:(BookmarkBar::State)nextState - (void)moveToState:(BookmarkBar::State)nextState
withAnimation:(BOOL)animate { withAnimation:(BOOL)animate {
BOOL isAnimationRunning = [self isAnimationRunning]; BOOL isAnimationRunning = [self isAnimationRunning];
...@@ -1343,8 +1500,7 @@ bool operator!=(const BookmarkBarLayout& lhs, const BookmarkBarLayout& rhs) { ...@@ -1343,8 +1500,7 @@ bool operator!=(const BookmarkBarLayout& lhs, const BookmarkBarLayout& rhs) {
[self moveToState:newState withAnimation:animate]; [self moveToState:newState withAnimation:animate];
} }
// Jump to final state (detached, attached, hidden, etc.) without animating, // (Private)
// stopping a running animation if necessary.
- (void)finalizeState { - (void)finalizeState {
// We promise that our delegate that the variables will be finalized before // We promise that our delegate that the variables will be finalized before
// the call to |-bookmarkBar:didChangeFromState:toState:|. // the call to |-bookmarkBar:didChangeFromState:toState:|.
...@@ -1361,7 +1517,7 @@ bool operator!=(const BookmarkBarLayout& lhs, const BookmarkBarLayout& rhs) { ...@@ -1361,7 +1517,7 @@ bool operator!=(const BookmarkBarLayout& lhs, const BookmarkBarLayout& rhs) {
[self updateVisibility]; [self updateVisibility];
} }
// Stops any current animation in its tracks (midway). // (Private)
- (void)stopCurrentAnimation { - (void)stopCurrentAnimation {
[[self controlledView] stopAnimation]; [[self controlledView] stopAnimation];
} }
...@@ -1372,6 +1528,36 @@ bool operator!=(const BookmarkBarLayout& lhs, const BookmarkBarLayout& rhs) { ...@@ -1372,6 +1528,36 @@ bool operator!=(const BookmarkBarLayout& lhs, const BookmarkBarLayout& rhs) {
[self finalizeState]; [self finalizeState];
} }
- (void)reconfigureBookmarkBar {
[self setManagedBookmarksButtonVisibility];
[self setSupervisedBookmarksButtonVisibility];
[self redistributeButtonsOnBarAsNeeded];
[self positionRightSideButtons];
[self configureOffTheSideButtonContentsAndVisibility];
[self centerNoItemsLabel];
}
// Determine if the given |view| can completely fit within the constraint of
// maximum x, given by |maxViewX|, and, if not, narrow the view up to a minimum
// width. If the minimum width is not achievable then hide the view. Return YES
// if the view was hidden.
- (BOOL)shrinkOrHideView:(NSView*)view forMaxX:(CGFloat)maxViewX {
BOOL wasHidden = NO;
// See if the view needs to be narrowed.
NSRect frame = [view frame];
if (NSMaxX(frame) > maxViewX) {
// Resize if more than 30 pixels are showing, otherwise hide.
if (NSMinX(frame) + 30.0 < maxViewX) {
frame.size.width = maxViewX - NSMinX(frame);
[view setFrame:frame];
} else {
[view setHidden:YES];
wasHidden = YES;
}
}
return wasHidden;
}
// Bookmark button menu items that open a new window (e.g., open in new window, // Bookmark button menu items that open a new window (e.g., open in new window,
// open in incognito, edit, etc.) cause us to lose a mouse-exited event // open in incognito, edit, etc.) cause us to lose a mouse-exited event
// on the button, which leaves it in a hover state. // on the button, which leaves it in a hover state.
...@@ -1397,9 +1583,173 @@ bool operator!=(const BookmarkBarLayout& lhs, const BookmarkBarLayout& rhs) { ...@@ -1397,9 +1583,173 @@ bool operator!=(const BookmarkBarLayout& lhs, const BookmarkBarLayout& rhs) {
} }
} }
- (void)addButtonForNode:(const bookmarks::BookmarkNode*)node
atIndex:(NSInteger)buttonIndex { // Adjust the horizontal width, x position and the visibility of the "For quick
[self rebuildLayoutWithAnimated:NO]; // access" text field and "Import bookmarks..." button based on the current
// width of the containing |buttonView_| (which is affected by window width).
- (void)adjustNoItemContainerForMaxX:(CGFloat)maxViewX {
if (![[buttonView_ noItemContainer] isHidden]) {
// Reset initial frames for the two items, then adjust as necessary.
NSTextField* noItemTextfield = [buttonView_ noItemTextfield];
NSRect noItemsRect = originalNoItemsRect_;
NSRect importBookmarksRect = originalImportBookmarksRect_;
if (![appsPageShortcutButton_ isHidden]) {
float width = NSWidth([appsPageShortcutButton_ frame]);
noItemsRect.origin.x += width;
importBookmarksRect.origin.x += width;
}
if (![managedBookmarksButton_ isHidden]) {
float width = NSWidth([managedBookmarksButton_ frame]);
noItemsRect.origin.x += width;
importBookmarksRect.origin.x += width;
}
if (![supervisedBookmarksButton_ isHidden]) {
float width = NSWidth([supervisedBookmarksButton_ frame]);
noItemsRect.origin.x += width;
importBookmarksRect.origin.x += width;
}
[noItemTextfield setFrame:noItemsRect];
[noItemTextfield setHidden:NO];
NSButton* importBookmarksButton = [buttonView_ importBookmarksButton];
[importBookmarksButton setFrame:importBookmarksRect];
[importBookmarksButton setHidden:NO];
// Check each to see if they need to be shrunk or hidden.
if ([self shrinkOrHideView:importBookmarksButton forMaxX:maxViewX])
[self shrinkOrHideView:noItemTextfield forMaxX:maxViewX];
}
}
// Scans through all buttons from left to right, calculating from scratch where
// they should be based on the preceding widths, until it finds the one
// requested.
// Returns NSZeroRect if there is no such button in the bookmark bar.
// Enables you to work out where a button will end up when it is done animating.
- (NSRect)finalRectOfButton:(BookmarkButton*)wantedButton {
CGFloat left = bookmarks::kBookmarkLeftMargin;
NSRect buttonFrame = NSZeroRect;
// Draw the apps bookmark if needed.
if (![appsPageShortcutButton_ isHidden]) {
left = NSMaxX([appsPageShortcutButton_ frame]) +
bookmarks::kBookmarkHorizontalPadding;
}
// Draw the managed bookmarks folder if needed.
if (![managedBookmarksButton_ isHidden]) {
left = NSMaxX([managedBookmarksButton_ frame]) +
bookmarks::kBookmarkHorizontalPadding;
}
// Draw the supervised bookmarks folder if needed.
if (![supervisedBookmarksButton_ isHidden]) {
left = NSMaxX([supervisedBookmarksButton_ frame]) +
bookmarks::kBookmarkHorizontalPadding;
}
for (NSButton* button in buttons_.get()) {
// Hidden buttons get no space.
if ([button isHidden])
continue;
buttonFrame = [button frame];
buttonFrame.origin.x = left;
left += buttonFrame.size.width + bookmarks::kBookmarkHorizontalPadding;
if (button == wantedButton)
return buttonFrame;
}
return NSZeroRect;
}
// Calculates the final position of the last button in the bar.
// We can't just use [[self buttons] lastObject] frame] because the button
// may be animating currently.
- (NSRect)finalRectOfLastButton {
return [self finalRectOfButton:[[self buttons] lastObject]];
}
- (CGFloat)buttonViewMaxXWithOffTheSideButtonIsVisible:(BOOL)visible {
CGFloat maxViewX = NSMaxX([buttonView_ bounds]);
// If necessary, pull in the width to account for the Other Bookmarks button.
if ([self setOtherBookmarksButtonVisibility]) {
maxViewX = [otherBookmarksButton_ frame].origin.x -
bookmarks::kBookmarkRightMargin;
}
[self positionRightSideButtons];
// If we're already overflowing, then we need to account for the chevron.
if (visible) {
maxViewX =
[offTheSideButton_ frame].origin.x - bookmarks::kBookmarkRightMargin;
}
return maxViewX;
}
- (void)redistributeButtonsOnBarAsNeeded {
const BookmarkNode* node = bookmarkModel_->bookmark_bar_node();
NSInteger barCount = node->child_count();
// Determine the current maximum extent of the visible buttons.
[self positionRightSideButtons];
BOOL offTheSideButtonVisible = (barCount > displayedButtonCount_);
CGFloat maxViewX = [self buttonViewMaxXWithOffTheSideButtonIsVisible:
offTheSideButtonVisible];
// As a result of pasting or dragging, the bar may now have more buttons
// than will fit so remove any which overflow. They will be shown in
// the off-the-side folder.
while (displayedButtonCount_ > 0) {
BookmarkButton* button = [buttons_ lastObject];
if (NSMaxX([self finalRectOfLastButton]) < maxViewX)
break;
[buttons_ removeLastObject];
[button setDelegate:nil];
[button removeFromSuperview];
--displayedButtonCount_;
// Account for the fact that the chevron might now be visible.
if (!offTheSideButtonVisible) {
offTheSideButtonVisible = YES;
maxViewX = [self buttonViewMaxXWithOffTheSideButtonIsVisible:YES];
}
}
// As a result of cutting, deleting and dragging, the bar may now have room
// for more buttons.
int xOffset;
if (displayedButtonCount_ > 0) {
xOffset = NSMaxX([self finalRectOfLastButton]);
} else if (![managedBookmarksButton_ isHidden]) {
xOffset = NSMaxX([managedBookmarksButton_ frame]) +
bookmarks::kBookmarkHorizontalPadding;
} else if (![supervisedBookmarksButton_ isHidden]) {
xOffset = NSMaxX([supervisedBookmarksButton_ frame]) +
bookmarks::kBookmarkHorizontalPadding;
} else if (![appsPageShortcutButton_ isHidden]) {
xOffset = NSMaxX([appsPageShortcutButton_ frame]) +
bookmarks::kBookmarkHorizontalPadding;
} else {
xOffset = bookmarks::kBookmarkLeftMargin -
bookmarks::kBookmarkHorizontalPadding;
}
for (int i = displayedButtonCount_; i < barCount; ++i) {
const BookmarkNode* child = node->GetChild(i);
BookmarkButton* button = [self buttonForNode:child xOffset:&xOffset];
// If we're testing against the last possible button then account
// for the chevron no longer needing to be shown.
if (i == barCount - 1)
maxViewX = [self buttonViewMaxXWithOffTheSideButtonIsVisible:NO];
if (NSMaxX([button frame]) > maxViewX) {
[button setDelegate:nil];
break;
}
++displayedButtonCount_;
[buttons_ addObject:button];
[buttonView_ addSubview:button];
}
// While we're here, adjust the horizontal width and the visibility
// of the "For quick access" and "Import bookmarks..." text fields.
if (![buttons_ count])
[self adjustNoItemContainerForMaxX:maxViewX];
} }
#pragma mark Private Methods Exposed for Testing #pragma mark Private Methods Exposed for Testing
...@@ -1412,16 +1762,16 @@ bool operator!=(const BookmarkBarLayout& lhs, const BookmarkBarLayout& rhs) { ...@@ -1412,16 +1762,16 @@ bool operator!=(const BookmarkBarLayout& lhs, const BookmarkBarLayout& rhs) {
return buttons_.get(); return buttons_.get();
} }
- (BookmarkButton*)otherBookmarksButton { - (BOOL)offTheSideButtonIsHidden {
return otherBookmarksButton_.get(); return [offTheSideButton_ isHidden];
} }
- (BookmarkButton*)managedBookmarksButton { - (BOOL)appsPageShortcutButtonIsHidden {
return managedBookmarksButton_.get(); return [appsPageShortcutButton_ isHidden];
} }
- (BookmarkButton*)supervisedBookmarksButton { - (BookmarkButton*)otherBookmarksButton {
return supervisedBookmarksButton_.get(); return otherBookmarksButton_.get();
} }
- (BookmarkBarFolderController*)folderController { - (BookmarkBarFolderController*)folderController {
...@@ -1432,6 +1782,29 @@ bool operator!=(const BookmarkBarLayout& lhs, const BookmarkBarLayout& rhs) { ...@@ -1432,6 +1782,29 @@ bool operator!=(const BookmarkBarLayout& lhs, const BookmarkBarLayout& rhs) {
return folderTarget_.get(); return folderTarget_.get();
} }
- (int)displayedButtonCount {
return displayedButtonCount_;
}
// Delete all buttons (bookmarks, chevron, "other bookmarks") from the
// bookmark bar; reset knowledge of bookmarks.
- (void)clearBookmarkBar {
[self stopPulsingBookmarkNode];
for (BookmarkButton* button in buttons_.get()) {
[button setDelegate:nil];
[button removeFromSuperview];
}
[buttons_ removeAllObjects];
displayedButtonCount_ = 0;
// Make sure there are no stale pointers in the pasteboard. This
// can be important if a bookmark is deleted (via bookmark sync)
// while in the middle of a drag. The "drag completed" code
// (e.g. [BookmarkBarView performDragOperationForBookmarkButton:]) is
// careful enough to bail if there is no data found at "drop" time.
[[NSPasteboard pasteboardWithName:NSDragPboard] clearContents];
}
// Return an autoreleased NSCell suitable for a bookmark button. // Return an autoreleased NSCell suitable for a bookmark button.
// TODO(jrg): move much of the cell config into the BookmarkButtonCell class. // TODO(jrg): move much of the cell config into the BookmarkButtonCell class.
- (BookmarkButtonCell*)cellForBookmarkNode:(const BookmarkNode*)node { - (BookmarkButtonCell*)cellForBookmarkNode:(const BookmarkNode*)node {
...@@ -1469,9 +1842,59 @@ bool operator!=(const BookmarkBarLayout& lhs, const BookmarkBarLayout& rhs) { ...@@ -1469,9 +1842,59 @@ bool operator!=(const BookmarkBarLayout& lhs, const BookmarkBarLayout& rhs) {
return cell; return cell;
} }
// Returns a frame appropriate for the given bookmark cell, suitable
// for creating an NSButton that will contain it. |xOffset| is the X
// offset for the frame; it is increased to be an appropriate X offset
// for the next button.
- (NSRect)frameForBookmarkButtonFromCell:(NSCell*)cell
xOffset:(int*)xOffset {
DCHECK(xOffset);
NSRect bounds = [buttonView_ bounds];
bounds.size.height = bookmarks::kBookmarkButtonHeight;
NSRect frame = NSInsetRect(bounds,
bookmarks::kBookmarkHorizontalPadding,
bookmarks::kBookmarkVerticalPadding);
frame.size.width = [self widthForBookmarkButtonCell:cell];
// Add an X offset based on what we've already done
frame.origin.x += *xOffset;
// And up the X offset for next time.
*xOffset = NSMaxX(frame);
return frame;
}
// A bookmark button's contents changed. Check for growth
// (e.g. increase the width up to the maximum). If we grew, move
// other bookmark buttons over.
- (void)checkForBookmarkButtonGrowth:(NSButton*)changedButton {
NSRect frame = [changedButton frame];
CGFloat desiredSize = [self widthForBookmarkButtonCell:[changedButton cell]];
CGFloat delta = desiredSize - frame.size.width;
if (delta) {
frame.size.width = desiredSize;
[changedButton setFrame:frame];
for (NSButton* button in buttons_.get()) {
NSRect buttonFrame = [button frame];
if (buttonFrame.origin.x > frame.origin.x) {
buttonFrame.origin.x += delta;
[button setFrame:buttonFrame];
}
}
}
// We may have just crossed a threshold to enable the off-the-side
// button.
[self configureOffTheSideButtonContentsAndVisibility];
}
// Called when our controlled frame has changed size. // Called when our controlled frame has changed size.
- (void)frameDidChange { - (void)frameDidChange {
[self rebuildLayoutWithAnimated:NO]; if (!bookmarkModel_->loaded())
return;
[self updateTheme:[[[self view] window] themeProvider]];
[self reconfigureBookmarkBar];
} }
// Adapt appearance of buttons to the current theme. Called after // Adapt appearance of buttons to the current theme. Called after
...@@ -1566,14 +1989,11 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) { ...@@ -1566,14 +1989,11 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) {
// given array, or nil if none. We use this for distinguishing // given array, or nil if none. We use this for distinguishing
// between a hover-open candidate or drop-indicator draw. // between a hover-open candidate or drop-indicator draw.
// Helper for buttonForDroppingOnAtPoint:. // Helper for buttonForDroppingOnAtPoint:.
// Get UI review on "middle half" ness.
// http://crbug.com/36276
- (BookmarkButton*)buttonForDroppingOnAtPoint:(NSPoint)point - (BookmarkButton*)buttonForDroppingOnAtPoint:(NSPoint)point
fromArray:(NSArray*)array { fromArray:(NSArray*)array {
// Ensure buttons are scanned left to right. for (BookmarkButton* button in array) {
id<NSFastEnumeration> buttonsToCheck =
cocoa_l10n_util::ShouldDoExperimentalRTLLayout()
? [array reverseObjectEnumerator]
: [array objectEnumerator];
for (BookmarkButton* button in buttonsToCheck) {
// Hidden buttons can overlap valid visible buttons, just ignore. // Hidden buttons can overlap valid visible buttons, just ignore.
if ([button isHidden]) if ([button isHidden])
continue; continue;
...@@ -1629,7 +2049,7 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) { ...@@ -1629,7 +2049,7 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) {
// This is different than BookmarkBarFolderController. // This is different than BookmarkBarFolderController.
if (!button) { if (!button) {
NSMutableArray* array = [NSMutableArray array]; NSMutableArray* array = [NSMutableArray array];
if (layout_.IsOffTheSideButtonVisible()) if (![self offTheSideButtonIsHidden])
[array addObject:offTheSideButton_]; [array addObject:offTheSideButton_];
[array addObject:otherBookmarksButton_]; [array addObject:otherBookmarksButton_];
button = [self buttonForDroppingOnAtPoint:point button = [self buttonForDroppingOnAtPoint:point
...@@ -1638,38 +2058,33 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) { ...@@ -1638,38 +2058,33 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) {
return button; return button;
} }
// Returns the index in the model for a drag to the location given by
// |point|. This is determined by finding the first button before the center
// of which |point| falls, scanning front to back. Note that, currently, only
// the x-coordinate of |point| is considered. Though not currently implemented,
// we may check for errors, in which case this would return negative value;
// callers should check for this.
- (int)indexForDragToPoint:(NSPoint)point { - (int)indexForDragToPoint:(NSPoint)point {
// TODO(jrg): revisit position info based on UI team feedback.
// dropLocation is in bar local coordinates.
NSPoint dropLocation = NSPoint dropLocation =
[[self view] convertPoint:point [[self view] convertPoint:point
fromView:[[[self view] window] contentView]]; fromView:[[[self view] window] contentView]];
BookmarkButton* buttonToTheRightOfDraggedButton = nil;
BOOL isRTL = cocoa_l10n_util::ShouldDoExperimentalRTLLayout(); for (BookmarkButton* button in buttons_.get()) {
for (size_t i = 0; i < layout_.VisibleButtonCount(); i++) {
BookmarkButton* button = [buttons_ objectAtIndex:i];
CGFloat midpoint = NSMidX([button frame]); CGFloat midpoint = NSMidX([button frame]);
if (isRTL ? dropLocation.x >= midpoint : dropLocation.x <= midpoint) { if (dropLocation.x <= midpoint) {
return i; buttonToTheRightOfDraggedButton = button;
break;
} }
} }
// No buttons left to insert the dragged button before, so it if (buttonToTheRightOfDraggedButton) {
// goes at the end. const BookmarkNode* afterNode =
return layout_.VisibleButtonCount(); [buttonToTheRightOfDraggedButton bookmarkNode];
DCHECK(afterNode);
int index = afterNode->parent()->GetIndexOf(afterNode);
// Make sure we don't get confused by buttons which aren't visible.
return std::min(index, displayedButtonCount_);
}
// If nothing is to my right I am at the end!
return displayedButtonCount_;
} }
// Informs the bar that something representing the bookmark at
// |sourceNode| was dragged into the bar.
// |point| is in the base coordinate system of the destination window;
// it comes from an id<NSDraggingInfo>. |copy| is YES if a copy is to be
// made and inserted into the new location while leaving the bookmark in
// the old location, otherwise move the bookmark by removing from its old
// location and inserting into the new location.
// Returns whether the drag was allowed.
// TODO(mrossetti,jrg): Yet more duplicated code. // TODO(mrossetti,jrg): Yet more duplicated code.
// http://crbug.com/35966 // http://crbug.com/35966
- (BOOL)dragBookmark:(const BookmarkNode*)sourceNode - (BOOL)dragBookmark:(const BookmarkNode*)sourceNode
...@@ -1716,393 +2131,155 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) { ...@@ -1716,393 +2131,155 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) {
- (void)draggingEnded:(id<NSDraggingInfo>)info { - (void)draggingEnded:(id<NSDraggingInfo>)info {
[self closeFolderAndStopTrackingMenus]; [self closeFolderAndStopTrackingMenus];
layout_ = {}; // Force layout. [[BookmarkButton draggedButton] setHidden:NO];
[self rebuildLayoutWithAnimated:YES]; [self resetAllButtonPositionsWithAnimation:YES];
} }
// Set insertionPos_ and hasInsertionPos_, and make insertion space for a // Set insertionPos_ and hasInsertionPos_, and make insertion space for a
// hypothetical drop with the new button having a left edge of |where|. // hypothetical drop with the new button having a left edge of |where|.
// Gets called only by our view.
- (void)setDropInsertionPos:(CGFloat)where { - (void)setDropInsertionPos:(CGFloat)where {
if (hasInsertionPos_ && where == insertionPos_) { if (!hasInsertionPos_ || where != insertionPos_) {
return;
}
insertionPos_ = where; insertionPos_ = where;
hasInsertionPos_ = YES; hasInsertionPos_ = YES;
CGFloat left;
if (![supervisedBookmarksButton_ isHidden]) {
left = NSMaxX([supervisedBookmarksButton_ frame]) +
bookmarks::kBookmarkHorizontalPadding;
} else if (![managedBookmarksButton_ isHidden]) {
left = NSMaxX([managedBookmarksButton_ frame]) +
bookmarks::kBookmarkHorizontalPadding;
} else if (![appsPageShortcutButton_ isHidden]) {
left = NSMaxX([appsPageShortcutButton_ frame]) +
bookmarks::kBookmarkHorizontalPadding;
} else {
left = bookmarks::kBookmarkLeftMargin;
}
CGFloat paddingWidth = bookmarks::kDefaultBookmarkWidth; CGFloat paddingWidth = bookmarks::kDefaultBookmarkWidth;
BookmarkButton* draggedButton = [BookmarkButton draggedButton]; BookmarkButton* draggedButton = [BookmarkButton draggedButton];
BOOL draggedButtonIsOnBar = NO;
int64_t draggedButtonNodeId;
CGFloat draggedButtonOffset;
if (draggedButton) { if (draggedButton) {
paddingWidth = std::min(bookmarks::kDefaultBookmarkWidth, paddingWidth = std::min(bookmarks::kDefaultBookmarkWidth,
NSWidth([draggedButton frame])); NSWidth([draggedButton frame]));
draggedButtonNodeId = [draggedButton bookmarkNode]->id(); }
auto offsetIt = layout_.button_offsets.find(draggedButtonNodeId); // Put all the buttons where they belong, with all buttons to the right
if (offsetIt != layout_.button_offsets.end()) { // of the insertion point shuffling right to make space for it.
draggedButtonIsOnBar = YES;
draggedButtonOffset = (*offsetIt).second;
}
}
paddingWidth += bookmarks::kBookmarkHorizontalPadding;
BOOL isRTL = cocoa_l10n_util::ShouldDoExperimentalRTLLayout();
// If the button being dragged is not currently on the bar
// (for example, a drag from the URL bar, a link on the desktop,
// a button inside a menu, etc.), buttons in front of the drag position
// (to the right in LTR, to the left in RTL), should make room for it.
// Otherwise:
// If a given button was to the left of the dragged button's original
// position, but is to the right of the drag position, or
// if it was to the right of the dragged button's original position and
// is to the left of the drag position, make room.
// TODO(lgrey): Extract ScopedNSAnimationContextGroup
// from the tab strip controller and use it on all of these.
[NSAnimationContext beginGrouping]; [NSAnimationContext beginGrouping];
[[NSAnimationContext currentContext] [[NSAnimationContext currentContext]
setDuration:kDragAndDropAnimationDuration]; setDuration:kDragAndDropAnimationDuration];
for (NSButton* button in buttons_.get()) {
for (BookmarkButton* button in buttons_.get()) { // Hidden buttons get no space.
CGRect buttonFrame = [button frame]; if ([button isHidden])
int64_t nodeId = [button bookmarkNode]->id();
CGFloat offset = layout_.button_offsets[nodeId];
CGFloat buttonEdge = isRTL ? NSWidth([buttonView_ frame]) - offset : offset;
if (draggedButtonIsOnBar) {
if (nodeId == draggedButtonNodeId)
continue; continue;
BOOL wasBefore = offset < draggedButtonOffset; NSRect buttonFrame = [button frame];
BOOL isBefore = isRTL ? buttonEdge > where : buttonEdge < where; buttonFrame.origin.x = left;
if (isBefore && !wasBefore) { // Update "left" for next time around.
offset -= paddingWidth; left += buttonFrame.size.width;
} else if (!isBefore && wasBefore) { if (left > insertionPos_)
offset += paddingWidth; buttonFrame.origin.x += paddingWidth;
} left += bookmarks::kBookmarkHorizontalPadding;
} else { if (innerContentAnimationsEnabled_)
if (isRTL ? (where > buttonEdge) [[button animator] setFrame:buttonFrame];
: (where < offset + NSWidth(buttonFrame))) { else
offset += paddingWidth; [button setFrame:buttonFrame];
}
}
[self applyXOffset:offset
toButton:button
animated:innerContentAnimationsEnabled_];
} }
[NSAnimationContext endGrouping]; [NSAnimationContext endGrouping];
}
- (BookmarkBarLayout)layoutFromCurrentState {
BookmarkBarLayout layout = {};
const BookmarkNode* node = bookmarkModel_->bookmark_bar_node();
CGFloat viewWidth = NSWidth([buttonView_ frame]);
CGFloat xOffset = bookmarks::kBookmarkLeftMargin;
CGFloat maxX = viewWidth - bookmarks::kBookmarkHorizontalPadding;
layout.visible_elements = bookmarks::kVisibleElementsMaskNone;
layout.max_x = maxX;
// Lay out "extra" buttons (apps, managed, supervised, "Other").
if (chrome::ShouldShowAppsShortcutInBookmarkBar(browser_->profile())) {
layout.visible_elements |= bookmarks::kVisibleElementsMaskAppsButton;
layout.apps_button_offset = xOffset;
xOffset += NSWidth([appsPageShortcutButton_ frame]) +
bookmarks::kBookmarkHorizontalPadding;
}
if (!managedBookmarkService_->managed_node()->empty()) {
layout.visible_elements |=
bookmarks::kVisibleElementsMaskManagedBookmarksButton;
layout.managed_bookmarks_button_offset = xOffset;
xOffset += NSWidth([managedBookmarksButton_ frame]) +
bookmarks::kBookmarkHorizontalPadding;
}
if (!managedBookmarkService_->supervised_node()->empty()) {
layout.visible_elements |=
bookmarks::kVisibleElementsMaskSupervisedBookmarksButton;
layout.supervised_bookmarks_button_offset = xOffset;
xOffset += NSWidth([supervisedBookmarksButton_ frame]) +
bookmarks::kBookmarkHorizontalPadding;
}
if (!bookmarkModel_->other_node()->empty()) {
layout.visible_elements |=
bookmarks::kVisibleElementsMaskOtherBookmarksButton;
maxX -= NSWidth([otherBookmarksButton_ frame]);
layout.other_bookmarks_button_offset = maxX;
}
// Lay out empty state ("no items" label, import bookmarks button.)
if (node->empty()) {
CGFloat roomForTextField =
maxX - xOffset + bookmarks::kInitialNoItemTextFieldXOrigin;
if (roomForTextField >= kNoItemElementMinWidth) {
layout.visible_elements |= bookmarks::kVisibleElementsMaskNoItemTextField;
xOffset += bookmarks::kInitialNoItemTextFieldXOrigin;
layout.no_item_textfield_offset = xOffset;
layout.no_item_textfield_width =
std::min(maxX - xOffset, originalNoItemTextFieldWidth_);
xOffset +=
layout.no_item_textfield_width + originalNoItemInterelementPadding_;
// Does the "import bookmarks" button fit?
if (maxX - xOffset >= kNoItemElementMinWidth) {
layout.visible_elements |=
bookmarks::kVisibleElementsMaskImportBookmarksButton;
layout.import_bookmarks_button_offset = xOffset;
layout.import_bookmarks_button_width =
std::min(maxX - xOffset, originalImportBookmarksButtonWidth_);
} }
}
} else {
// Lay out bookmark buttons and "off-the-side" chevron.
CGFloat offTheSideButtonWidth = NSWidth([offTheSideButton_ frame]);
int buttonCount = node->child_count();
for (int i = 0; i < buttonCount; i++) {
const BookmarkNode* buttonNode = node->GetChild(i);
CGFloat widthOfButton = [self widthOfButtonForNode:buttonNode];
// If it's the last button, we just need to ensure that it can fit.
// If not, we need to be able to fit both it and the chevron.
CGFloat widthToCheck = i == buttonCount - 1
? widthOfButton
: widthOfButton + offTheSideButtonWidth;
if (xOffset + widthToCheck > maxX) {
layout.visible_elements |=
bookmarks::kVisibleElementsMaskOffTheSideButton;
layout.off_the_side_button_offset = maxX - offTheSideButtonWidth;
break;
}
layout.button_offsets.insert({buttonNode->id(), xOffset});
xOffset += widthOfButton + bookmarks::kBookmarkHorizontalPadding;
}
}
return layout;
} }
- (void)rebuildLayoutWithAnimated:(BOOL)animated { // Put all visible bookmark bar buttons in their normal locations, either with
if (!bookmarkModel_->loaded()) // or without animation according to the |animate| flag.
return; // This is generally useful, so is called from various places internally.
- (void)resetAllButtonPositionsWithAnimation:(BOOL)animate {
BookmarkBarLayout layout = [self layoutFromCurrentState]; // Position the apps bookmark if needed.
if (layout_ != layout) { CGFloat left = bookmarks::kBookmarkLeftMargin;
layout_ = std::move(layout); if (![appsPageShortcutButton_ isHidden]) {
[self applyLayout:layout_ animated:animated]; int xOffset = bookmarks::kBookmarkLeftMargin -
bookmarks::kBookmarkHorizontalPadding;
NSRect frame =
[self frameForBookmarkButtonFromCell:[appsPageShortcutButton_ cell]
xOffset:&xOffset];
[appsPageShortcutButton_ setFrame:frame];
left = xOffset + bookmarks::kBookmarkHorizontalPadding;
} }
}
- (void)applyLayout:(const BookmarkBarLayout&)layout animated:(BOOL)animated { // Position the managed bookmarks folder if needed.
if (!bookmarkModel_->loaded()) if (![managedBookmarksButton_ isHidden]) {
return; int xOffset = left;
NSRect frame =
if (folderController_) [self frameForBookmarkButtonFromCell:[managedBookmarksButton_ cell]
[self closeAllBookmarkFolders]; xOffset:&xOffset];
[self stopPulsingBookmarkNode]; [managedBookmarksButton_ setFrame:frame];
left = xOffset + bookmarks::kBookmarkHorizontalPadding;
// Hide or show "extra" buttons and position if necessary.
[self applyXOffset:layout.apps_button_offset
toButton:appsPageShortcutButton_
animated:NO];
[appsPageShortcutButton_ setHidden:!layout.IsAppsButtonVisible()];
[self applyXOffset:layout.managed_bookmarks_button_offset
toButton:managedBookmarksButton_
animated:NO];
[managedBookmarksButton_ setHidden:!layout.IsManagedBookmarksButtonVisible()];
[self applyXOffset:layout.supervised_bookmarks_button_offset
toButton:supervisedBookmarksButton_
animated:NO];
[supervisedBookmarksButton_
setHidden:!layout.IsSupervisedBookmarksButtonVisible()];
[self applyXOffset:layout.other_bookmarks_button_offset
toButton:otherBookmarksButton_
animated:NO];
[otherBookmarksButton_ setHidden:!layout.IsOtherBookmarksButtonVisible()];
[self applyXOffset:layout.off_the_side_button_offset
toButton:offTheSideButton_
animated:NO];
[offTheSideButton_ setHidden:!layout.IsOffTheSideButtonVisible()];
if (layout.IsOffTheSideButtonVisible()) {
[[offTheSideButton_ cell]
setStartingChildIndex:layout_.VisibleButtonCount()];
}
// Hide or show empty state and position if necessary.
[[buttonView_ noItemTextField] setHidden:!layout.IsNoItemTextFieldVisible()];
[[buttonView_ importBookmarksButton]
setHidden:!layout.IsImportBookmarksButtonVisible()];
if (layout.IsNoItemTextFieldVisible()) {
NSTextField* noItemTextField = [buttonView_ noItemTextField];
[noItemTextField setHidden:NO];
NSRect textFieldFrame = [noItemTextField frame];
textFieldFrame.size.width = layout.no_item_textfield_width;
if (cocoa_l10n_util::ShouldDoExperimentalRTLLayout()) {
textFieldFrame.origin.x = NSWidth([buttonView_ frame]) -
layout.no_item_textfield_offset -
layout.no_item_textfield_width;
} else {
textFieldFrame.origin.x = layout.no_item_textfield_offset;
} }
[noItemTextField setFrame:textFieldFrame];
if (layout.IsImportBookmarksButtonVisible()) { // Position the supervised bookmarks folder if needed.
NSButton* importBookmarksButton = [buttonView_ importBookmarksButton]; if (![supervisedBookmarksButton_ isHidden]) {
NSRect buttonFrame = [importBookmarksButton frame]; int xOffset = left;
buttonFrame.size.width = layout.import_bookmarks_button_width; NSRect frame =
if (cocoa_l10n_util::ShouldDoExperimentalRTLLayout()) { [self frameForBookmarkButtonFromCell:[supervisedBookmarksButton_ cell]
buttonFrame.origin.x = NSWidth([buttonView_ frame]) - xOffset:&xOffset];
layout.import_bookmarks_button_offset - [supervisedBookmarksButton_ setFrame:frame];
layout.import_bookmarks_button_width; left = xOffset + bookmarks::kBookmarkHorizontalPadding;
} else {
buttonFrame.origin.x = layout.import_bookmarks_button_offset;
}
[importBookmarksButton setFrame:buttonFrame];
}
}
// 1) Hide all buttons initially.
// 2) Show all buttons in the layout.
// 3) Remove any buttons that are still hidden (so: not in the layout)
// from the node ID -> button map and add them to the reuse pool if
// there's room.
for (BookmarkButton* button in buttons_.get()) {
[button setHidden:YES];
} }
[buttons_ removeAllObjects];
const BookmarkNode* parentNode = bookmarkModel_->bookmark_bar_node(); animate &= innerContentAnimationsEnabled_;
for (size_t i = 0; i < layout.VisibleButtonCount(); i++) {
const BookmarkNode* node = parentNode->GetChild(i);
DCHECK(node);
BookmarkButton* button = [self buttonForNode:node];
CGFloat offset = layout.button_offsets.at(node->id());
[self applyXOffset:offset
toButton:button
animated:animated && innerContentAnimationsEnabled_];
[buttons_ addObject:button];
[button setHidden:NO];
}
for (auto& item : nodeIdToButtonMap_) { for (NSButton* button in buttons_.get()) {
if ([item.second isHidden]) { // Hidden buttons get no space.
if ([unusedButtonPool_ count] < kMaxReusePoolSize) { if ([button isHidden])
BookmarkButton* button = item.second.get(); continue;
// Dragged buttons unhide themselves, so position it off-screen NSRect buttonFrame = [button frame];
// while it's in the reuse pool. buttonFrame.origin.x = left;
CGRect buttonFrame = [button frame]; left += buttonFrame.size.width + bookmarks::kBookmarkHorizontalPadding;
buttonFrame.origin.x = -10000; if (animate)
[[button animator] setFrame:buttonFrame];
else
[button setFrame:buttonFrame]; [button setFrame:buttonFrame];
[unusedButtonPool_ addObject:button];
} else {
[item.second setDelegate:nil];
[item.second removeFromSuperview];
}
nodeIdToButtonMap_.erase(item.first);
}
} }
const ui::ThemeProvider* themeProvider = [[[self view] window] themeProvider];
[self updateTheme:themeProvider];
}
- (void)applyXOffset:(CGFloat)offset
toButton:(BookmarkButton*)button
animated:(BOOL)animated {
CGRect frame = [button frame];
BOOL wasOffscreen = frame.origin.x < -200;
if (cocoa_l10n_util::ShouldDoExperimentalRTLLayout()) {
frame.origin.x = NSWidth([buttonView_ frame]) - offset - NSWidth(frame);
} else {
frame.origin.x = offset;
}
// Buttons fresh from the reuse pool are kept offscreen. Animation should
// be disabled for them so they don't slide in.
if (animated && !wasOffscreen) {
[[button animator] setFrame:frame];
} else {
[button setFrame:frame];
}
}
- (CGFloat)widthOfButtonForNode:(const BookmarkNode*)node {
// TODO(lgrey): Can we get this information without an actual image?
NSImage* image = [self faviconForNode:node forADarkTheme:NO];
CGFloat width = [BookmarkButtonCell cellWidthForNode:node image:image];
return std::min(width, bookmarks::kDefaultBookmarkWidth);
} }
// Clear insertion flag, remove insertion space and put all visible bookmark // Clear insertion flag, remove insertion space and put all visible bookmark
// bar buttons in their normal locations. // bar buttons in their normal locations.
// Gets called only by our view. // Gets called only by our view.
// TODO(lgrey): Is there a sane way to dedupe this with |setDropInsertionPos:|?
- (void)clearDropInsertionPos { - (void)clearDropInsertionPos {
if (!hasInsertionPos_) { if (hasInsertionPos_) {
return;
}
hasInsertionPos_ = NO; hasInsertionPos_ = NO;
BookmarkButton* draggedButton = [BookmarkButton draggedButton]; [self resetAllButtonPositionsWithAnimation:YES];
if (!draggedButton) {
[self applyLayout:layout_ animated:YES];
return;
}
int64_t draggedButtonNodeId = [draggedButton bookmarkNode]->id();
if (layout_.button_offsets.find(draggedButtonNodeId) ==
layout_.button_offsets.end()) {
[self applyLayout:layout_ animated:YES];
return;
}
// The dragged button came from the bar, but is being dragged outside
// of it now, so the rest of the buttons should be laid out as if it
// were removed.
CGFloat draggedButtonOffset = layout_.button_offsets[draggedButtonNodeId];
CGFloat spaceForDraggedButton =
NSWidth([draggedButton frame]) + bookmarks::kBookmarkHorizontalPadding;
[NSAnimationContext beginGrouping];
[[NSAnimationContext currentContext]
setDuration:kDragAndDropAnimationDuration];
for (BookmarkButton* button in buttons_.get()) {
int64_t nodeId = [button bookmarkNode]->id();
CGFloat offset = layout_.button_offsets[nodeId];
if (nodeId == draggedButtonNodeId)
continue;
if (offset > draggedButtonOffset) {
offset -= spaceForDraggedButton;
[self applyXOffset:offset
toButton:button
animated:innerContentAnimationsEnabled_];
} }
}
[NSAnimationContext endGrouping];
} }
#pragma mark Bridge Notification Handlers #pragma mark Bridge Notification Handlers
// TODO(jrg): for now this is brute force.
- (void)loaded:(BookmarkModel*)model { - (void)loaded:(BookmarkModel*)model {
DCHECK(model == bookmarkModel_); DCHECK(model == bookmarkModel_);
if (!model->loaded()) if (!model->loaded())
return; return;
// Make sure there are no stale pointers in the pasteboard. This
// can be important if a bookmark is deleted (via bookmark sync)
// while in the middle of a drag. The "drag completed" code
// (e.g. [BookmarkBarView performDragOperationForBookmarkButton:]) is
// careful enough to bail if there is no data found at "drop" time.
[[NSPasteboard pasteboardWithName:NSDragPboard] clearContents];
if (!didCreateExtraButtons_) {
[self createExtraButtons];
}
// If this is a rebuild request while we have a folder open, close it. // If this is a rebuild request while we have a folder open, close it.
// TODO(mrossetti): Eliminate the need for this because it causes the folder
// menu to disappear after a cut/copy/paste/delete change.
// See: http://crbug.com/36614
if (folderController_) if (folderController_)
[self closeAllBookmarkFolders]; [self closeAllBookmarkFolders];
[self rebuildLayoutWithAnimated:NO];
// Brute force nuke and build.
savedFrameWidth_ = NSWidth([[self view] frame]);
const BookmarkNode* node = model->bookmark_bar_node();
[self clearBookmarkBar];
[self createAppsPageShortcutButton];
[self createManagedBookmarksButton];
[self createSupervisedBookmarksButton];
[self addNodesToButtonList:node];
[self createOtherBookmarksButton];
[self updateTheme:[[[self view] window] themeProvider]];
[self positionRightSideButtons];
[self addButtonsToView];
[self configureOffTheSideButtonContentsAndVisibility];
[self reconfigureBookmarkBar];
} }
- (void)nodeAdded:(BookmarkModel*)model - (void)nodeAdded:(BookmarkModel*)model
parent:(const BookmarkNode*)newParent index:(int)newIndex { parent:(const BookmarkNode*)newParent index:(int)newIndex {
// If a context menu is open, close it. // If a context menu is open, close it.
...@@ -2112,7 +2289,11 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) { ...@@ -2112,7 +2289,11 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) {
id<BookmarkButtonControllerProtocol> newController = id<BookmarkButtonControllerProtocol> newController =
[self controllerForNode:newParent]; [self controllerForNode:newParent];
[newController addButtonForNode:newNode atIndex:newIndex]; [newController addButtonForNode:newNode atIndex:newIndex];
[self rebuildLayoutWithAnimated:NO]; // If we go from 0 --> 1 bookmarks we may need to hide the
// "bookmarks go here" text container.
[self showOrHideNoItemContainerForNode:model->bookmark_bar_node()];
// Cope with chevron or "Other Bookmarks" buttons possibly changing state.
[self reconfigureBookmarkBar];
} }
// TODO(jrg): for now this is brute force. // TODO(jrg): for now this is brute force.
...@@ -2135,7 +2316,11 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) { ...@@ -2135,7 +2316,11 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) {
[oldController removeButton:oldIndex animate:NO]; [oldController removeButton:oldIndex animate:NO];
[newController addButtonForNode:movedNode atIndex:newIndex]; [newController addButtonForNode:movedNode atIndex:newIndex];
} }
[self rebuildLayoutWithAnimated:NO]; // If the bar is one of the parents we may need to update the visibility
// of the "bookmarks go here" presentation.
[self showOrHideNoItemContainerForNode:model->bookmark_bar_node()];
// Cope with chevron or "Other Bookmarks" buttons possibly changing state.
[self reconfigureBookmarkBar];
} }
- (void)nodeRemoved:(BookmarkModel*)model - (void)nodeRemoved:(BookmarkModel*)model
...@@ -2148,9 +2333,17 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) { ...@@ -2148,9 +2333,17 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) {
id<BookmarkButtonControllerProtocol> parentController = id<BookmarkButtonControllerProtocol> parentController =
[self controllerForNode:oldParent]; [self controllerForNode:oldParent];
[parentController removeButton:index animate:YES]; [parentController removeButton:index animate:YES];
[self rebuildLayoutWithAnimated:NO]; // If we go from 1 --> 0 bookmarks we may need to show the
// "bookmarks go here" text container.
[self showOrHideNoItemContainerForNode:model->bookmark_bar_node()];
// If we deleted the only item on the "off the side" menu we no
// longer need to show it.
[self reconfigureBookmarkBar];
} }
// TODO(jrg): linear searching is bad.
// Need a BookmarkNode-->NSCell mapping.
//
// TODO(jrg): if the bookmark bar is open on launch, we see the // TODO(jrg): if the bookmark bar is open on launch, we see the
// buttons all placed, then "scooted over" as the favicons load. If // buttons all placed, then "scooted over" as the favicons load. If
// this looks bad I may need to change widthForBookmarkButtonCell to // this looks bad I may need to change widthForBookmarkButtonCell to
...@@ -2158,14 +2351,21 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) { ...@@ -2158,14 +2351,21 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) {
// favicons will eventually load. // favicons will eventually load.
- (void)nodeFaviconLoaded:(BookmarkModel*)model - (void)nodeFaviconLoaded:(BookmarkModel*)model
node:(const BookmarkNode*)node { node:(const BookmarkNode*)node {
auto buttonIt = nodeIdToButtonMap_.find(node->id()); for (BookmarkButton* button in buttons_.get()) {
if (buttonIt != nodeIdToButtonMap_.end()) { const BookmarkNode* cellnode = [button bookmarkNode];
BookmarkButton* button = (*buttonIt).second; if (cellnode == node) {
BOOL darkTheme = [[[self view] window] hasDarkTheme]; BOOL darkTheme = [[[self view] window] hasDarkTheme];
NSImage* theImage = [self faviconForNode:node forADarkTheme:darkTheme]; NSImage* theImage = [self faviconForNode:node forADarkTheme:darkTheme];
[[button cell] setBookmarkCellText:[button title] image:theImage]; [[button cell] setBookmarkCellText:[button title]
image:theImage];
// Adding an image means we might need more room for the
// bookmark. Test for it by growing the button (if needed)
// and shifting everything else over.
[self checkForBookmarkButtonGrowth:button];
return;
} }
[self rebuildLayoutWithAnimated:NO]; }
if (folderController_) if (folderController_)
[folderController_ faviconLoadedForNode:node]; [folderController_ faviconLoadedForNode:node];
} }
...@@ -2309,7 +2509,8 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) { ...@@ -2309,7 +2509,8 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) {
- (void)bookmarkDragDidEnd:(BookmarkButton*)button - (void)bookmarkDragDidEnd:(BookmarkButton*)button
operation:(NSDragOperation)operation { operation:(NSDragOperation)operation {
[self rebuildLayoutWithAnimated:YES]; [button setHidden:NO];
[self resetAllButtonPositionsWithAnimation:YES];
} }
...@@ -2469,50 +2670,50 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) { ...@@ -2469,50 +2670,50 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) {
CGFloat x = 0; CGFloat x = 0;
CGFloat halfHorizontalPadding = 0.5 * bookmarks::kBookmarkHorizontalPadding; CGFloat halfHorizontalPadding = 0.5 * bookmarks::kBookmarkHorizontalPadding;
int destIndex = [self indexForDragToPoint:point]; int destIndex = [self indexForDragToPoint:point];
int numButtons = layout_.VisibleButtonCount(); int numButtons = displayedButtonCount_;
CGFloat leadingOffset; CGFloat leftmostX;
if (layout_.IsSupervisedBookmarksButtonVisible()) { if (![supervisedBookmarksButton_ isHidden]) {
leadingOffset = leftmostX =
layout_.supervised_bookmarks_button_offset + halfHorizontalPadding; NSMaxX([supervisedBookmarksButton_ frame]) + halfHorizontalPadding;
} else if (layout_.IsManagedBookmarksButtonVisible()) { } else if (![managedBookmarksButton_ isHidden]) {
leadingOffset = leftmostX = NSMaxX([managedBookmarksButton_ frame]) + halfHorizontalPadding;
layout_.managed_bookmarks_button_offset + halfHorizontalPadding; } else if (![appsPageShortcutButton_ isHidden]) {
} else if (layout_.IsAppsButtonVisible()) { leftmostX = NSMaxX([appsPageShortcutButton_ frame]) + halfHorizontalPadding;
leadingOffset = layout_.apps_button_offset + halfHorizontalPadding;
} else { } else {
leadingOffset = bookmarks::kBookmarkLeftMargin - halfHorizontalPadding; leftmostX = bookmarks::kBookmarkLeftMargin - halfHorizontalPadding;
} }
// If it's a drop strictly between existing buttons ... // If it's a drop strictly between existing buttons ...
if (destIndex == 0) { if (destIndex == 0) {
x = leadingOffset; x = leftmostX;
} else if (destIndex > 0 && destIndex < numButtons) { } else if (destIndex > 0 && destIndex < numButtons) {
// ... put the indicator right between the buttons. // ... put the indicator right between the buttons.
int64_t nodeId = BookmarkButton* button =
bookmarkModel_->bookmark_bar_node()->GetChild(destIndex)->id(); [buttons_ objectAtIndex:static_cast<NSUInteger>(destIndex-1)];
x = layout_.button_offsets[nodeId] - halfHorizontalPadding; DCHECK(button);
NSRect buttonFrame = [button frame];
x = NSMaxX(buttonFrame) + halfHorizontalPadding;
// If it's a drop at the end (past the last button, if there are any) ... // If it's a drop at the end (past the last button, if there are any) ...
} else if (destIndex == numButtons) { } else if (destIndex == numButtons) {
// and if it's past the last button ... // and if it's past the last button ...
if (numButtons > 0) { if (numButtons > 0) {
// ... find the last button, and put the indicator after it. // ... find the last button, and put the indicator to its right.
const BookmarkNode* node = BookmarkButton* button =
bookmarkModel_->bookmark_bar_node()->GetChild(destIndex - 1); [buttons_ objectAtIndex:static_cast<NSUInteger>(destIndex - 1)];
int64_t nodeId = node->id(); DCHECK(button);
x = layout_.button_offsets[nodeId] + [self widthOfButtonForNode:node] + x = NSMaxX([button frame]) + halfHorizontalPadding;
halfHorizontalPadding;
// Otherwise, put it right at the beginning. // Otherwise, put it right at the beginning.
} else { } else {
x = leadingOffset; x = leftmostX;
} }
} else { } else {
NOTREACHED(); NOTREACHED();
} }
return cocoa_l10n_util::ShouldDoExperimentalRTLLayout() return x;
? NSWidth([buttonView_ frame]) - x
: x;
} }
- (void)childFolderWillShow:(id<BookmarkButtonControllerProtocol>)child { - (void)childFolderWillShow:(id<BookmarkButtonControllerProtocol>)child {
...@@ -2536,6 +2737,7 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) { ...@@ -2536,6 +2737,7 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) {
// Add a new folder controller as triggered by the given folder button. // Add a new folder controller as triggered by the given folder button.
- (void)addNewFolderControllerWithParentButton:(BookmarkButton*)parentButton { - (void)addNewFolderControllerWithParentButton:(BookmarkButton*)parentButton {
// If doing a close/open, make sure the fullscreen chrome doesn't // If doing a close/open, make sure the fullscreen chrome doesn't
// have a chance to begin animating away in the middle of things. // have a chance to begin animating away in the middle of things.
BrowserWindowController* browserController = BrowserWindowController* browserController =
...@@ -2571,6 +2773,34 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) { ...@@ -2571,6 +2773,34 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) {
browser_->profile()); browser_->profile());
} }
- (void)addButtonForNode:(const BookmarkNode*)node
atIndex:(NSInteger)buttonIndex {
int newOffset =
bookmarks::kBookmarkLeftMargin - bookmarks::kBookmarkHorizontalPadding;
if (buttonIndex == -1)
buttonIndex = [buttons_ count]; // New button goes at the end.
if (buttonIndex <= (NSInteger)[buttons_ count]) {
if (buttonIndex) {
BookmarkButton* targetButton = [buttons_ objectAtIndex:buttonIndex - 1];
NSRect targetFrame = [targetButton frame];
newOffset = targetFrame.origin.x + NSWidth(targetFrame) +
bookmarks::kBookmarkHorizontalPadding;
}
BookmarkButton* newButton = [self buttonForNode:node xOffset:&newOffset];
++displayedButtonCount_;
[buttons_ insertObject:newButton atIndex:buttonIndex];
[buttonView_ addSubview:newButton];
[self resetAllButtonPositionsWithAnimation:NO];
// See if any buttons need to be pushed off to or brought in from the side.
[self reconfigureBookmarkBar];
} else {
// A button from somewhere else (not the bar) is being moved to the
// off-the-side so insure it gets redrawn if its showing.
[self reconfigureBookmarkBar];
[folderController_ reconfigureMenu];
}
}
// TODO(mrossetti): Duplicate code with BookmarkBarFolderController. // TODO(mrossetti): Duplicate code with BookmarkBarFolderController.
// http://crbug.com/35966 // http://crbug.com/35966
- (BOOL)addURLs:(NSArray*)urls withTitles:(NSArray*)titles at:(NSPoint)point { - (BOOL)addURLs:(NSArray*)urls withTitles:(NSArray*)titles at:(NSPoint)point {
...@@ -2618,22 +2848,49 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) { ...@@ -2618,22 +2848,49 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) {
} }
- (void)moveButtonFromIndex:(NSInteger)fromIndex toIndex:(NSInteger)toIndex { - (void)moveButtonFromIndex:(NSInteger)fromIndex toIndex:(NSInteger)toIndex {
int buttonCount = layout_.VisibleButtonCount(); if (fromIndex != toIndex) {
BOOL isMoveWithinOffTheSideMenu = NSInteger buttonCount = (NSInteger)[buttons_ count];
(toIndex >= buttonCount) && (fromIndex >= buttonCount); if (toIndex == -1)
if (isMoveWithinOffTheSideMenu) { toIndex = buttonCount;
// See if we have a simple move within the bar, which will be the case if
// both button indexes are in the visible space.
if (fromIndex < buttonCount && toIndex < buttonCount) {
BookmarkButton* movedButton = [buttons_ objectAtIndex:fromIndex];
[buttons_ removeObjectAtIndex:fromIndex];
[buttons_ insertObject:movedButton atIndex:toIndex];
[movedButton setHidden:NO];
[self resetAllButtonPositionsWithAnimation:NO];
} else if (fromIndex < buttonCount) {
// A button is being removed from the bar and added to off-the-side.
// By now the node has already been inserted into the model so the
// button to be added is represented by |toIndex|. Things get
// complicated because the off-the-side is showing and must be redrawn
// while possibly re-laying out the bookmark bar.
[self removeButton:fromIndex animate:NO];
[self reconfigureBookmarkBar];
[folderController_ reconfigureMenu];
} else if (toIndex < buttonCount) {
// A button is being added to the bar and removed from off-the-side.
// By now the node has already been inserted into the model so the
// button to be added is represented by |toIndex|.
const BookmarkNode* node = bookmarkModel_->bookmark_bar_node();
const BookmarkNode* movedNode = node->GetChild(toIndex);
DCHECK(movedNode);
[self addButtonForNode:movedNode atIndex:toIndex];
[self reconfigureBookmarkBar];
} else {
// A button is being moved within the off-the-side.
fromIndex -= buttonCount; fromIndex -= buttonCount;
toIndex -= buttonCount; toIndex -= buttonCount;
[folderController_ moveButtonFromIndex:fromIndex toIndex:toIndex]; [folderController_ moveButtonFromIndex:fromIndex toIndex:toIndex];
} else { }
[self rebuildLayoutWithAnimated:NO];
} }
} }
- (void)removeButton:(NSInteger)buttonIndex animate:(BOOL)animate { - (void)removeButton:(NSInteger)buttonIndex animate:(BOOL)animate {
if ((size_t)buttonIndex < layout_.VisibleButtonCount()) { if (buttonIndex < (NSInteger)[buttons_ count]) {
// The button being removed is showing in the bar. // The button being removed is showing in the bar.
BookmarkButton* oldButton = buttons_[buttonIndex]; BookmarkButton* oldButton = [buttons_ objectAtIndex:buttonIndex];
if (oldButton == [folderController_ parentButton]) { if (oldButton == [folderController_ parentButton]) {
// If we are deleting a button whose folder is currently open, close it! // If we are deleting a button whose folder is currently open, close it!
[self closeAllBookmarkFolders]; [self closeAllBookmarkFolders];
...@@ -2644,12 +2901,17 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) { ...@@ -2644,12 +2901,17 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) {
NSShowAnimationEffect(NSAnimationEffectDisappearingItemDefault, poofPoint, NSShowAnimationEffect(NSAnimationEffectDisappearingItemDefault, poofPoint,
NSZeroSize, nil, nil, nil); NSZeroSize, nil, nil, nil);
} }
[self rebuildLayoutWithAnimated:YES]; [oldButton setDelegate:nil];
[oldButton removeFromSuperview];
[buttons_ removeObjectAtIndex:buttonIndex];
--displayedButtonCount_;
[self resetAllButtonPositionsWithAnimation:YES];
[self reconfigureBookmarkBar];
} else if (folderController_ && } else if (folderController_ &&
[folderController_ parentButton] == offTheSideButton_) { [folderController_ parentButton] == offTheSideButton_) {
// The button being removed is in the OTS (off-the-side) and the OTS // The button being removed is in the OTS (off-the-side) and the OTS
// menu is showing so we need to remove the button. // menu is showing so we need to remove the button.
NSInteger index = buttonIndex - layout_.VisibleButtonCount(); NSInteger index = buttonIndex - displayedButtonCount_;
[folderController_ removeButton:index animate:animate]; [folderController_ removeButton:index animate:animate];
} }
} }
...@@ -2663,9 +2925,4 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) { ...@@ -2663,9 +2925,4 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) {
return [folderController_ controllerForNode:node]; return [folderController_ controllerForNode:node];
} }
// For testing.
- (const BookmarkBarLayout&)currentLayout {
return layout_;
}
@end @end
...@@ -313,8 +313,14 @@ class BookmarkBarControllerTestBase : public CocoaProfileTest { ...@@ -313,8 +313,14 @@ class BookmarkBarControllerTestBase : public CocoaProfileTest {
} }
void InstallAndToggleBar(BookmarkBarController* bar) { void InstallAndToggleBar(BookmarkBarController* bar) {
// Loads the view. // In OSX 10.10, the owner of a nib file is retain/autoreleased during the
// initialization of the nib. Wrapping the nib loading in an
// autoreleasepool ensures that tests can control the destruction timing of
// the controller.
@autoreleasepool {
// Forces loading of the nib.
[[bar controlledView] setResizeDelegate:resizeDelegate_]; [[bar controlledView] setResizeDelegate:resizeDelegate_];
}
// Awkwardness to look like we've been installed. // Awkwardness to look like we've been installed.
for (NSView* subView in [parent_view_ subviews]) for (NSView* subView in [parent_view_ subviews])
[subView removeFromSuperview]; [subView removeFromSuperview];
...@@ -362,49 +368,6 @@ class BookmarkBarControllerTest : public BookmarkBarControllerTestBase { ...@@ -362,49 +368,6 @@ class BookmarkBarControllerTest : public BookmarkBarControllerTestBase {
BookmarkBarControllerNoOpen* noOpenBar() { BookmarkBarControllerNoOpen* noOpenBar() {
return (BookmarkBarControllerNoOpen*)bar_.get(); return (BookmarkBarControllerNoOpen*)bar_.get();
} }
// Verifies that the i-th button has the title of the i-th child node of
// |node|.
void VerifyButtonTitles(const BookmarkNode* node) {
ASSERT_LE((int)[[bar_ buttons] count], node->child_count());
int numButtons = [[bar_ buttons] count];
for (int i = 0; i < numButtons; i++) {
EXPECT_NSEQ([[[bar_ buttons] objectAtIndex:i] title],
base::SysUTF16ToNSString(node->GetChild(i)->GetTitle()));
}
}
// Verifies that |view|
// * With a blank layout applied:
// - Is hidden
//
// * With |layout| applied:
// - Is visible
// - Has x origin |expected_x_origin|
// If |check_exact_width| is true:
// - has width |expected_width|
// Otherwise:
// - has width > 0 (|expected_width| is ignored in this case)
void VerifyViewLayout(NSView* view,
bookmarks::BookmarkBarLayout& layout,
CGFloat expected_x_origin,
CGFloat expected_width,
bool check_exact_width) {
ASSERT_TRUE(view);
bookmarks::BookmarkBarLayout empty_layout = {};
[bar_ applyLayout:empty_layout animated:NO];
EXPECT_TRUE([view isHidden]);
[bar_ applyLayout:layout animated:NO];
EXPECT_CGFLOAT_EQ(NSMinX([view frame]), expected_x_origin);
CGFloat actual_width = NSWidth([view frame]);
if (check_exact_width) {
EXPECT_CGFLOAT_EQ(actual_width, expected_width);
} else {
EXPECT_GT(actual_width, 0);
}
}
}; };
TEST_F(BookmarkBarControllerTest, ShowWhenShowBookmarkBarTrue) { TEST_F(BookmarkBarControllerTest, ShowWhenShowBookmarkBarTrue) {
...@@ -454,6 +417,38 @@ TEST_F(BookmarkBarControllerTest, ShowOnNewTabPage) { ...@@ -454,6 +417,38 @@ TEST_F(BookmarkBarControllerTest, ShowOnNewTabPage) {
EXPECT_FALSE([[bar_ view] isHidden]); EXPECT_FALSE([[bar_ view] isHidden]);
EXPECT_GT([resizeDelegate_ height], 0); EXPECT_GT([resizeDelegate_ height], 0);
EXPECT_GT([[bar_ view] frame].size.height, 0); EXPECT_GT([[bar_ view] frame].size.height, 0);
// Make sure no buttons fall off the bar, either now or when resized
// bigger or smaller.
CGFloat sizes[] = { 300.0, -100.0, 200.0, -420.0 };
CGFloat previousX = 0.0;
for (unsigned x = 0; x < arraysize(sizes); x++) {
// Confirm the buttons moved from the last check (which may be
// init but that's fine).
CGFloat newX = [[bar_ offTheSideButton] frame].origin.x;
EXPECT_NE(previousX, newX);
previousX = newX;
// Confirm the buttons have a reasonable bounds. Recall that |-frame|
// returns rectangles in the superview's coordinates.
NSRect buttonViewFrame =
[[bar_ buttonView] convertRect:[[bar_ buttonView] frame]
fromView:[[bar_ buttonView] superview]];
EXPECT_EQ([bar_ buttonView], [[bar_ offTheSideButton] superview]);
EXPECT_TRUE(NSContainsRect(buttonViewFrame,
[[bar_ offTheSideButton] frame]));
EXPECT_EQ([bar_ buttonView], [[bar_ otherBookmarksButton] superview]);
EXPECT_TRUE(NSContainsRect(buttonViewFrame,
[[bar_ otherBookmarksButton] frame]));
// Now move them implicitly.
// We confirm FrameChangeNotification works in the next unit test;
// we simply assume it works here to resize or reposition the
// buttons above.
NSRect frame = [[bar_ view] frame];
frame.size.width += sizes[x];
[[bar_ view] setFrame:frame];
}
} }
// Test whether |-updateState:...| sets currentState as expected. Make // Test whether |-updateState:...| sets currentState as expected. Make
...@@ -526,393 +521,38 @@ TEST_F(BookmarkBarControllerTest, FrameChangeNotification) { ...@@ -526,393 +521,38 @@ TEST_F(BookmarkBarControllerTest, FrameChangeNotification) {
EXPECT_GT([bar toggles], 0); EXPECT_GT([bar toggles], 0);
} }
TEST_F(BookmarkBarControllerTest, ApplyLayoutNoItemTextField) { // Confirm our "no items" container goes away when we add the 1st
NSTextField* text_field = [[bar_ buttonView] noItemTextField]; // bookmark, and comes back when we delete the bookmark.
TEST_F(BookmarkBarControllerTest, NoItemContainerGoesAway) {
bookmarks::BookmarkBarLayout layout = {};
layout.visible_elements |= bookmarks::kVisibleElementsMaskNoItemTextField;
layout.no_item_textfield_offset = 10;
layout.no_item_textfield_width = 20;
VerifyViewLayout(text_field, layout, 10, 20, true);
}
TEST_F(BookmarkBarControllerTest, ApplyLayoutImportBookmarksButton) {
NSButton* button = [[bar_ buttonView] importBookmarksButton];
bookmarks::BookmarkBarLayout layout = {};
// This button never appears without the no item text field appearing, so
// show that too.
layout.visible_elements |= bookmarks::kVisibleElementsMaskNoItemTextField;
layout.no_item_textfield_offset = 10;
layout.no_item_textfield_width = 20;
layout.visible_elements |=
bookmarks::kVisibleElementsMaskImportBookmarksButton;
layout.import_bookmarks_button_offset = 30;
layout.import_bookmarks_button_width = 40;
VerifyViewLayout(button, layout, 30, 40, true);
}
TEST_F(BookmarkBarControllerTest, ApplyLayoutSupervisedButton) {
NSButton* button = [bar_ supervisedBookmarksButton];
bookmarks::BookmarkBarLayout layout = {};
layout.visible_elements |=
bookmarks::kVisibleElementsMaskSupervisedBookmarksButton;
layout.supervised_bookmarks_button_offset = 40;
VerifyViewLayout(button, layout, 40, CGFLOAT_MAX, false);
}
TEST_F(BookmarkBarControllerTest, ApplyLayoutManagedButton) {
NSButton* button = [bar_ managedBookmarksButton];
bookmarks::BookmarkBarLayout layout = {};
layout.visible_elements |=
bookmarks::kVisibleElementsMaskManagedBookmarksButton;
layout.managed_bookmarks_button_offset = 50;
VerifyViewLayout(button, layout, 50, CGFLOAT_MAX, false);
}
TEST_F(BookmarkBarControllerTest, ApplyLayoutOffTheSideButton) {
NSButton* button = [bar_ offTheSideButton];
bookmarks::BookmarkBarLayout layout = {};
layout.visible_elements |= bookmarks::kVisibleElementsMaskOffTheSideButton;
layout.off_the_side_button_offset = 100;
VerifyViewLayout(button, layout, 100, CGFLOAT_MAX, false);
}
TEST_F(BookmarkBarControllerTest, ApplyLayoutOtherBookmarksButton) {
NSButton* button = [bar_ otherBookmarksButton];
bookmarks::BookmarkBarLayout layout = {};
layout.visible_elements |=
bookmarks::kVisibleElementsMaskOtherBookmarksButton;
layout.other_bookmarks_button_offset = 110;
VerifyViewLayout(button, layout, 110, CGFLOAT_MAX, false);
}
TEST_F(BookmarkBarControllerTest, ApplyLayoutAppsButton) {
NSButton* button = [bar_ appsPageShortcutButton];
bookmarks::BookmarkBarLayout layout = {};
layout.visible_elements |= bookmarks::kVisibleElementsMaskAppsButton;
layout.apps_button_offset = 5;
VerifyViewLayout(button, layout, 5, CGFLOAT_MAX, false);
}
TEST_F(BookmarkBarControllerTest, ApplyLayoutBookmarkButtons) {
// Add some bookmarks
BookmarkModel* model = BookmarkModelFactory::GetForBrowserContext(profile());
GURL gurls[] = {
GURL("http://www.google.com/a"), GURL("http://www.google.com/b"),
GURL("http://www.google.com/c"), GURL("http://www.google.com/d")};
base::string16 titles[] = {ASCIIToUTF16("a"), ASCIIToUTF16("b"),
ASCIIToUTF16("c"), ASCIIToUTF16("d")};
for (size_t i = 0; i < arraysize(titles); i++)
bookmarks::AddIfNotBookmarked(model, gurls[i], titles[i]);
// Apply an empty layout. This should clear the buttons.
bookmarks::BookmarkBarLayout layout = {};
[bar_ applyLayout:layout animated:NO];
EXPECT_EQ([[bar_ buttons] count], 0U);
const BookmarkNode* parentNode = model->bookmark_bar_node();
EXPECT_EQ(parentNode->child_count(), 4);
// Ask the layout to show the first 2 nodes
for (int i = 0; i < 3; i++) {
const BookmarkNode* node = parentNode->GetChild(i);
layout.button_offsets[node->id()] =
bookmarks::kDefaultBookmarkWidth * (i + 1);
}
[bar_ applyLayout:layout animated:NO];
EXPECT_EQ([[bar_ buttons] count], 3U);
CGFloat lastMax = 0;
for (int i = 0; i < 3; i++) {
const BookmarkNode* node = parentNode->GetChild(i);
BookmarkButton* button = [[bar_ buttons] objectAtIndex:i];
EXPECT_EQ([button bookmarkNode], node);
EXPECT_CGFLOAT_EQ(NSMinX([button frame]),
bookmarks::kDefaultBookmarkWidth * (i + 1));
EXPECT_GT(NSWidth([button frame]), 0);
EXPECT_EQ([button superview], [bar_ buttonView]);
// Ensure buttons don't overlap.
EXPECT_GT(NSMinX([button frame]), lastMax);
lastMax = NSMaxX([button frame]);
}
VerifyButtonTitles(parentNode);
}
// TODO(lgrey): Add tests for RTL in a follow-up.
// crbug.com/648554
TEST_F(BookmarkBarControllerTest, LayoutNoBookmarks) {
// With no apps button, or managed/supervised buttons:
profile()->GetPrefs()->SetBoolean(
bookmarks::prefs::kShowAppsShortcutInBookmarkBar, false);
bookmarks::BookmarkBarLayout layout = [bar_ layoutFromCurrentState];
// Only the no item text field and import bookmarks button are showing.
EXPECT_EQ(
layout.visible_elements,
(unsigned int)(bookmarks::kVisibleElementsMaskImportBookmarksButton |
bookmarks::kVisibleElementsMaskNoItemTextField));
EXPECT_EQ(layout.VisibleButtonCount(), 0U);
// The import bookmarks button is after the no item text field and they don't
// overlap
EXPECT_GT(layout.import_bookmarks_button_offset,
layout.no_item_textfield_offset + layout.no_item_textfield_width);
// And they both have a width
EXPECT_GT(layout.no_item_textfield_width, 0);
EXPECT_GT(layout.import_bookmarks_button_width, 0);
// And start at the correct offset
EXPECT_EQ(layout.no_item_textfield_offset,
bookmarks::kBookmarkLeftMargin +
bookmarks::kInitialNoItemTextFieldXOrigin);
// With apps button:
profile()->GetPrefs()->SetBoolean(
bookmarks::prefs::kShowAppsShortcutInBookmarkBar, true);
layout = [bar_ layoutFromCurrentState];
// Only the apps button, no item text field and import bookmarks button are
// showing.
EXPECT_EQ(
layout.visible_elements,
(unsigned int)(bookmarks::kVisibleElementsMaskImportBookmarksButton |
bookmarks::kVisibleElementsMaskNoItemTextField |
bookmarks::kVisibleElementsMaskAppsButton));
EXPECT_EQ(layout.VisibleButtonCount(), 0U);
// The no item text field is after the apps button
EXPECT_GT(layout.no_item_textfield_offset, layout.apps_button_offset);
// And the apps button is at the correct offset
EXPECT_EQ(layout.apps_button_offset, bookmarks::kBookmarkLeftMargin);
// TODO(lgrey): It seems prohibitively difficult/maybe impossible
// to test the managed/supervised buttons at this time.
}
TEST_F(BookmarkBarControllerTest, LayoutManagedAppsButton) {
// By default the pref is not managed and the apps shortcut is shown.
sync_preferences::TestingPrefServiceSyncable* prefs =
profile()->GetTestingPrefService();
EXPECT_FALSE(prefs->IsManagedPreference(
bookmarks::prefs::kShowAppsShortcutInBookmarkBar));
bookmarks::BookmarkBarLayout layout = [bar_ layoutFromCurrentState];
EXPECT_TRUE(layout.IsAppsButtonVisible());
// Hide the apps shortcut by policy, via the managed pref.
prefs->SetManagedPref(bookmarks::prefs::kShowAppsShortcutInBookmarkBar,
base::MakeUnique<base::Value>(false));
layout = [bar_ layoutFromCurrentState];
EXPECT_FALSE(layout.IsAppsButtonVisible());
// And try showing it via policy too.
prefs->SetManagedPref(bookmarks::prefs::kShowAppsShortcutInBookmarkBar,
base::MakeUnique<base::Value>(true));
layout = [bar_ layoutFromCurrentState];
EXPECT_TRUE(layout.IsAppsButtonVisible());
}
TEST_F(BookmarkBarControllerTest, NoItemsResizing) {
bookmarks::BookmarkBarLayout layout = [bar_ layoutFromCurrentState];
EXPECT_TRUE(layout.IsNoItemTextFieldVisible() &&
layout.IsImportBookmarksButtonVisible());
CGFloat oldOffset = layout.import_bookmarks_button_offset;
CGFloat oldWidth = layout.import_bookmarks_button_width;
CGRect viewFrame = [[bar_ view] frame];
CGFloat originalViewWidth = NSWidth(viewFrame);
// Assert that the import bookmarks button fits.
EXPECT_GT(originalViewWidth, oldOffset + oldWidth);
// Resize the view to cut the import bookmarks button in half.
viewFrame.size.width = oldOffset + oldWidth * 0.5;
[[bar_ view] setFrame:viewFrame];
layout = [bar_ layoutFromCurrentState];
EXPECT_TRUE(layout.IsNoItemTextFieldVisible() &&
layout.IsImportBookmarksButtonVisible());
EXPECT_CGFLOAT_EQ(layout.import_bookmarks_button_offset, oldOffset);
EXPECT_LT(layout.import_bookmarks_button_width, oldWidth);
// Resize the view to cut off the import bookmarks button entirely.
viewFrame.size.width = layout.import_bookmarks_button_offset - 1;
[[bar_ view] setFrame:viewFrame];
layout = [bar_ layoutFromCurrentState];
EXPECT_FALSE(layout.IsImportBookmarksButtonVisible());
EXPECT_TRUE(layout.IsNoItemTextFieldVisible());
// Now, cut the text field in half
oldOffset = layout.no_item_textfield_offset;
oldWidth = layout.no_item_textfield_width;
viewFrame.size.width = oldOffset + oldWidth * 0.5;
[[bar_ view] setFrame:viewFrame];
layout = [bar_ layoutFromCurrentState];
EXPECT_TRUE(layout.IsNoItemTextFieldVisible());
EXPECT_CGFLOAT_EQ(layout.no_item_textfield_offset, oldOffset);
EXPECT_LT(layout.no_item_textfield_width, oldWidth);
// Make the view too small to fit either.
viewFrame.size.width = bookmarks::kBookmarkLeftMargin;
[[bar_ view] setFrame:viewFrame];
layout = [bar_ layoutFromCurrentState];
EXPECT_FALSE(layout.IsNoItemTextFieldVisible() ||
layout.IsImportBookmarksButtonVisible());
// Sanity check
viewFrame.size.width = originalViewWidth;
[[bar_ view] setFrame:viewFrame];
layout = [bar_ layoutFromCurrentState];
EXPECT_TRUE(layout.IsNoItemTextFieldVisible() &&
layout.IsImportBookmarksButtonVisible());
}
TEST_F(BookmarkBarControllerTest, LayoutOtherBookmarks) {
BookmarkModel* model = BookmarkModelFactory::GetForBrowserContext(profile()); BookmarkModel* model = BookmarkModelFactory::GetForBrowserContext(profile());
const BookmarkNode* otherBookmarks = model->other_node(); const BookmarkNode* bar = model->bookmark_bar_node();
model->AddURL(otherBookmarks, otherBookmarks->child_count(),
ASCIIToUTF16("TheOther"), GURL("http://www.other.com"));
bookmarks::BookmarkBarLayout layout = [bar_ layoutFromCurrentState];
EXPECT_TRUE(layout.IsOtherBookmarksButtonVisible());
EXPECT_EQ(layout.VisibleButtonCount(), 0U);
EXPECT_GT(layout.other_bookmarks_button_offset, 0);
CGFloat offsetFromMaxX =
NSWidth([[bar_ view] frame]) - layout.other_bookmarks_button_offset;
// Resize the view and ensure the button stays pinned to the right edge.
CGRect viewFrame = [[bar_ view] frame];
// Half of the original size
viewFrame.size.width = std::ceil(NSWidth(viewFrame) * 0.5);
[[bar_ view] setFrame:viewFrame];
layout = [bar_ layoutFromCurrentState];
EXPECT_TRUE(layout.IsOtherBookmarksButtonVisible());
EXPECT_GT(layout.other_bookmarks_button_offset, 0);
EXPECT_CGFLOAT_EQ(NSWidth(viewFrame) - layout.other_bookmarks_button_offset,
offsetFromMaxX);
// 150% of the original size
viewFrame.size.width = NSWidth(viewFrame) * 3;
[[bar_ view] setFrame:viewFrame];
layout = [bar_ layoutFromCurrentState];
EXPECT_TRUE(layout.IsOtherBookmarksButtonVisible());
EXPECT_CGFLOAT_EQ(NSWidth(viewFrame) - layout.other_bookmarks_button_offset,
offsetFromMaxX);
}
TEST_F(BookmarkBarControllerTest, LayoutBookmarks) {
// Apps button shows up by default, first test without it.
profile()->GetPrefs()->SetBoolean(
bookmarks::prefs::kShowAppsShortcutInBookmarkBar, false);
// Add some bookmarks.
BookmarkModel* model = BookmarkModelFactory::GetForBrowserContext(profile());
const BookmarkNode* barNode = model->bookmark_bar_node();
GURL gurls[] = {GURL("http://www.google.com/a"),
GURL("http://www.google.com/b"),
GURL("http://www.google.com/c")};
base::string16 titles[] = {ASCIIToUTF16("a"), ASCIIToUTF16("b"),
ASCIIToUTF16("c")};
for (size_t i = 0; i < arraysize(titles); i++)
bookmarks::AddIfNotBookmarked(model, gurls[i], titles[i]);
bookmarks::BookmarkBarLayout layout = [bar_ layoutFromCurrentState]; [bar_ loaded:model];
EXPECT_EQ(layout.VisibleButtonCount(), (size_t)barNode->child_count()); BookmarkBarView* view = [bar_ buttonView];
EXPECT_FALSE(layout.IsOffTheSideButtonVisible()); DCHECK(view);
EXPECT_EQ(layout.button_offsets.size(), (size_t)barNode->child_count()); NSView* noItemContainer = [view noItemContainer];
EXPECT_EQ(layout.button_offsets[barNode->GetChild(0)->id()], DCHECK(noItemContainer);
bookmarks::kBookmarkLeftMargin);
// Assert that the buttons are in order. EXPECT_FALSE([noItemContainer isHidden]);
CGFloat lastOffset = 0; const BookmarkNode* node = model->AddURL(bar, bar->child_count(),
for (int i = 0; i < barNode->child_count(); i++) { ASCIIToUTF16("title"),
CGFloat offset = layout.button_offsets[barNode->GetChild(i)->id()];
EXPECT_GT(offset, lastOffset);
lastOffset = offset;
}
}
TEST_F(BookmarkBarControllerTest, NoItemUIHiddenWithBookmarks) {
BookmarkModel* model = BookmarkModelFactory::GetForBrowserContext(profile());
const BookmarkNode* barNode = model->bookmark_bar_node();
bookmarks::BookmarkBarLayout layout = [bar_ layoutFromCurrentState];
EXPECT_TRUE(layout.IsNoItemTextFieldVisible() &&
layout.IsImportBookmarksButtonVisible());
const BookmarkNode* node =
model->AddURL(barNode, barNode->child_count(), ASCIIToUTF16("title"),
GURL("http://www.google.com")); GURL("http://www.google.com"));
layout = [bar_ layoutFromCurrentState]; EXPECT_TRUE([noItemContainer isHidden]);
EXPECT_FALSE(layout.IsNoItemTextFieldVisible() ||
layout.IsImportBookmarksButtonVisible());
model->Remove(node); model->Remove(node);
layout = [bar_ layoutFromCurrentState]; EXPECT_FALSE([noItemContainer isHidden]);
EXPECT_TRUE(layout.IsNoItemTextFieldVisible() &&
layout.IsImportBookmarksButtonVisible());
}
TEST_F(BookmarkBarControllerTest, LayoutBookmarksResize) { // Now try it using a bookmark from the Other Bookmarks.
BookmarkModel* model = BookmarkModelFactory::GetForBrowserContext(profile()); const BookmarkNode* otherBookmarks = model->other_node();
const BookmarkNode* barNode = model->bookmark_bar_node(); node = model->AddURL(otherBookmarks, otherBookmarks->child_count(),
for (int i = 0; i < 20; i++) { ASCIIToUTF16("TheOther"),
model->AddURL(barNode, barNode->child_count(), ASCIIToUTF16("title"), GURL("http://www.other.com"));
GURL("http://www.google.com")); EXPECT_FALSE([noItemContainer isHidden]);
} // Move it from Other Bookmarks to the bar.
bookmarks::BookmarkBarLayout layout = [bar_ layoutFromCurrentState]; model->Move(node, bar, 0);
EXPECT_LT(layout.VisibleButtonCount(), 20U); EXPECT_TRUE([noItemContainer isHidden]);
size_t originalButtonCount = layout.VisibleButtonCount(); // Move it back to Other Bookmarks from the bar.
EXPECT_TRUE(layout.IsOffTheSideButtonVisible()); model->Move(node, otherBookmarks, 0);
EXPECT_FALSE([noItemContainer isHidden]);
CGRect viewFrame = [[bar_ view] frame];
// Increase the width of the view. More buttons should be visible.
viewFrame.size.width += 200;
[[bar_ view] setFrame:viewFrame];
layout = [bar_ layoutFromCurrentState];
EXPECT_GT(layout.VisibleButtonCount(), originalButtonCount);
// Decrease the width of the view to below what it was originally.
// Less buttons should be visible.
viewFrame.size.width -= 400;
[[bar_ view] setFrame:viewFrame];
layout = [bar_ layoutFromCurrentState];
EXPECT_LT(layout.VisibleButtonCount(), originalButtonCount);
// NB: This part overlaps a little with |OffTheSideButtonHidden|
// but we just want to check the layout here, whereas that test
// ensures the folder is closed when the button goes away.
//
// Find the number of buttons at which the off-the-side button disappears.
EXPECT_TRUE(layout.IsOffTheSideButtonVisible());
bool crossoverPointFound = false;
// Bound the number of iterations in case the button never
// disappears.
const int maxToRemove = 20;
for (int i = 0; i < maxToRemove; i++) {
if (!layout.IsOffTheSideButtonVisible()) {
crossoverPointFound = true;
break;
}
model->Remove(barNode->GetChild(barNode->child_count() - 1));
layout = [bar_ layoutFromCurrentState];
}
// Either something is broken, or the test needs to be redone.
ASSERT_TRUE(crossoverPointFound);
EXPECT_FALSE(layout.IsOffTheSideButtonVisible());
// Add a button and ensure the off the side button appears again.
model->AddURL(barNode, barNode->child_count(), ASCIIToUTF16("title"),
GURL("http://www.google.com"));
layout = [bar_ layoutFromCurrentState];
EXPECT_TRUE(layout.IsOffTheSideButtonVisible());
// If the off-the-side button is visible, this means we have more
// buttons than we can show, so adding another shouldn't increase
// the button count.
size_t buttonCountBeforeAdding = layout.VisibleButtonCount();
model->AddURL(barNode, barNode->child_count(), ASCIIToUTF16("title"),
GURL("http://www.google.com"));
layout = [bar_ layoutFromCurrentState];
EXPECT_EQ(layout.VisibleButtonCount(), buttonCountBeforeAdding);
} }
// Confirm off the side button only enabled when reasonable. // Confirm off the side button only enabled when reasonable.
...@@ -920,12 +560,12 @@ TEST_F(BookmarkBarControllerTest, OffTheSideButtonHidden) { ...@@ -920,12 +560,12 @@ TEST_F(BookmarkBarControllerTest, OffTheSideButtonHidden) {
BookmarkModel* model = BookmarkModelFactory::GetForBrowserContext(profile()); BookmarkModel* model = BookmarkModelFactory::GetForBrowserContext(profile());
[bar_ loaded:model]; [bar_ loaded:model];
EXPECT_TRUE([[bar_ offTheSideButton] isHidden]); EXPECT_TRUE([bar_ offTheSideButtonIsHidden]);
for (int i = 0; i < 2; i++) { for (int i = 0; i < 2; i++) {
bookmarks::AddIfNotBookmarked( bookmarks::AddIfNotBookmarked(
model, GURL("http://www.foo.com"), ASCIIToUTF16("small")); model, GURL("http://www.foo.com"), ASCIIToUTF16("small"));
EXPECT_TRUE([[bar_ offTheSideButton] isHidden]); EXPECT_TRUE([bar_ offTheSideButtonIsHidden]);
} }
const BookmarkNode* parent = model->bookmark_bar_node(); const BookmarkNode* parent = model->bookmark_bar_node();
...@@ -934,12 +574,12 @@ TEST_F(BookmarkBarControllerTest, OffTheSideButtonHidden) { ...@@ -934,12 +574,12 @@ TEST_F(BookmarkBarControllerTest, OffTheSideButtonHidden) {
ASCIIToUTF16("super duper wide title"), ASCIIToUTF16("super duper wide title"),
GURL("http://superfriends.hall-of-justice.edu")); GURL("http://superfriends.hall-of-justice.edu"));
} }
EXPECT_FALSE([[bar_ offTheSideButton] isHidden]); EXPECT_FALSE([bar_ offTheSideButtonIsHidden]);
// Open the "off the side" and start deleting nodes. Make sure // Open the "off the side" and start deleting nodes. Make sure
// deletion of the last node in "off the side" causes the folder to // deletion of the last node in "off the side" causes the folder to
// close. // close.
EXPECT_FALSE([[bar_ offTheSideButton] isHidden]); EXPECT_FALSE([bar_ offTheSideButtonIsHidden]);
NSButton* offTheSideButton = [bar_ offTheSideButton]; NSButton* offTheSideButton = [bar_ offTheSideButton];
// Open "off the side" menu. // Open "off the side" menu.
[bar_ openOffTheSideFolderFromButton:offTheSideButton]; [bar_ openOffTheSideFolderFromButton:offTheSideButton];
...@@ -948,13 +588,13 @@ TEST_F(BookmarkBarControllerTest, OffTheSideButtonHidden) { ...@@ -948,13 +588,13 @@ TEST_F(BookmarkBarControllerTest, OffTheSideButtonHidden) {
[bbfc setIgnoreAnimations:YES]; [bbfc setIgnoreAnimations:YES];
while (!parent->empty()) { while (!parent->empty()) {
// We've completed the job so we're done. // We've completed the job so we're done.
if ([[bar_ offTheSideButton] isHidden]) if ([bar_ offTheSideButtonIsHidden])
break; break;
// Delete the last button. // Delete the last button.
model->Remove(parent->GetChild(parent->child_count() - 1)); model->Remove(parent->GetChild(parent->child_count() - 1));
// If last one make sure the menu is closed and the button is hidden. // If last one make sure the menu is closed and the button is hidden.
// Else make sure menu stays open. // Else make sure menu stays open.
if ([[bar_ offTheSideButton] isHidden]) { if ([bar_ offTheSideButtonIsHidden]) {
EXPECT_FALSE([bar_ folderController]); EXPECT_FALSE([bar_ folderController]);
} else { } else {
EXPECT_TRUE([bar_ folderController]); EXPECT_TRUE([bar_ folderController]);
...@@ -977,7 +617,7 @@ TEST_F(BookmarkBarControllerTest, DeleteFromOffTheSideWhileItIsOpen) { ...@@ -977,7 +617,7 @@ TEST_F(BookmarkBarControllerTest, DeleteFromOffTheSideWhileItIsOpen) {
model->AddURL(parent, parent->child_count(), ASCIIToUTF16(title.str()), model->AddURL(parent, parent->child_count(), ASCIIToUTF16(title.str()),
GURL("http://superfriends.hall-of-justice.edu")); GURL("http://superfriends.hall-of-justice.edu"));
} }
EXPECT_FALSE([[bar_ offTheSideButton] isHidden]); EXPECT_FALSE([bar_ offTheSideButtonIsHidden]);
// Open "off the side" menu. // Open "off the side" menu.
NSButton* offTheSideButton = [bar_ offTheSideButton]; NSButton* offTheSideButton = [bar_ offTheSideButton];
...@@ -1045,7 +685,14 @@ TEST_F(BookmarkBarControllerTest, OpenBookmark) { ...@@ -1045,7 +685,14 @@ TEST_F(BookmarkBarControllerTest, OpenBookmark) {
TEST_F(BookmarkBarControllerTest, TestAddRemoveAndClear) { TEST_F(BookmarkBarControllerTest, TestAddRemoveAndClear) {
BookmarkModel* model = BookmarkModelFactory::GetForBrowserContext(profile()); BookmarkModel* model = BookmarkModelFactory::GetForBrowserContext(profile());
NSView* buttonView = [bar_ buttonView];
EXPECT_EQ(0U, [[bar_ buttons] count]);
unsigned int initial_subview_count = [[buttonView subviews] count];
// Make sure a redundant call doesn't choke
[bar_ clearBookmarkBar];
EXPECT_EQ(0U, [[bar_ buttons] count]); EXPECT_EQ(0U, [[bar_ buttons] count]);
EXPECT_EQ(initial_subview_count, [[buttonView subviews] count]);
GURL gurl1("http://superfriends.hall-of-justice.edu"); GURL gurl1("http://superfriends.hall-of-justice.edu");
// Short titles increase the chances of this test succeeding if the view is // Short titles increase the chances of this test succeeding if the view is
...@@ -1055,24 +702,33 @@ TEST_F(BookmarkBarControllerTest, TestAddRemoveAndClear) { ...@@ -1055,24 +702,33 @@ TEST_F(BookmarkBarControllerTest, TestAddRemoveAndClear) {
base::string16 title1(ASCIIToUTF16("x")); base::string16 title1(ASCIIToUTF16("x"));
bookmarks::AddIfNotBookmarked(model, gurl1, title1); bookmarks::AddIfNotBookmarked(model, gurl1, title1);
EXPECT_EQ(1U, [[bar_ buttons] count]); EXPECT_EQ(1U, [[bar_ buttons] count]);
EXPECT_EQ(1+initial_subview_count, [[buttonView subviews] count]);
GURL gurl2("http://legion-of-doom.gov"); GURL gurl2("http://legion-of-doom.gov");
base::string16 title2(ASCIIToUTF16("y")); base::string16 title2(ASCIIToUTF16("y"));
bookmarks::AddIfNotBookmarked(model, gurl2, title2); bookmarks::AddIfNotBookmarked(model, gurl2, title2);
EXPECT_EQ(2U, [[bar_ buttons] count]); EXPECT_EQ(2U, [[bar_ buttons] count]);
EXPECT_EQ(2+initial_subview_count, [[buttonView subviews] count]);
for (int i = 0; i < 3; i++) { for (int i = 0; i < 3; i++) {
bookmarks::RemoveAllBookmarks(model, gurl2); bookmarks::RemoveAllBookmarks(model, gurl2);
EXPECT_EQ(1U, [[bar_ buttons] count]); EXPECT_EQ(1U, [[bar_ buttons] count]);
EXPECT_EQ(1+initial_subview_count, [[buttonView subviews] count]);
// and bring it back // and bring it back
bookmarks::AddIfNotBookmarked(model, gurl2, title2); bookmarks::AddIfNotBookmarked(model, gurl2, title2);
EXPECT_EQ(2U, [[bar_ buttons] count]); EXPECT_EQ(2U, [[bar_ buttons] count]);
EXPECT_EQ(2+initial_subview_count, [[buttonView subviews] count]);
} }
[bar_ clearBookmarkBar];
EXPECT_EQ(0U, [[bar_ buttons] count]);
EXPECT_EQ(initial_subview_count, [[buttonView subviews] count]);
// Explicit test of loaded: since this is a convenient spot // Explicit test of loaded: since this is a convenient spot
[bar_ loaded:model]; [bar_ loaded:model];
EXPECT_EQ(2U, [[bar_ buttons] count]); EXPECT_EQ(2U, [[bar_ buttons] count]);
EXPECT_EQ(2+initial_subview_count, [[buttonView subviews] count]);
} }
// Make sure we don't create too many buttons; we only really need // Make sure we don't create too many buttons; we only really need
...@@ -1100,7 +756,7 @@ TEST_F(BookmarkBarControllerTest, TestButtonLimits) { ...@@ -1100,7 +756,7 @@ TEST_F(BookmarkBarControllerTest, TestButtonLimits) {
// Add 10 more (to the front of the list so the on-screen buttons // Add 10 more (to the front of the list so the on-screen buttons
// would change) and make sure the count stays the same. // would change) and make sure the count stays the same.
for (int i=0; i<10; i++) { for (int i=0; i<10; i++) {
model->AddURL(parent, 0, // index is 0, so front, not end model->AddURL(parent, 0, /* index is 0, so front, not end */
ASCIIToUTF16("title"), GURL("http://www.google.com")); ASCIIToUTF16("title"), GURL("http://www.google.com"));
} }
...@@ -1112,6 +768,107 @@ TEST_F(BookmarkBarControllerTest, TestButtonLimits) { ...@@ -1112,6 +768,107 @@ TEST_F(BookmarkBarControllerTest, TestButtonLimits) {
EXPECT_GT(finalcount, count); EXPECT_GT(finalcount, count);
} }
// Make sure that each button we add marches to the right and does not
// overlap with the previous one.
TEST_F(BookmarkBarControllerTest, TestButtonMarch) {
base::scoped_nsobject<NSMutableArray> cells([[NSMutableArray alloc] init]);
CGFloat widths[] = { 10, 10, 100, 10, 500, 500, 80000, 60000, 1, 345 };
for (unsigned int i = 0; i < arraysize(widths); i++) {
NSCell* cell = [[CellWithDesiredSize alloc]
initTextCell:@"foo"
desiredSize:NSMakeSize(widths[i], 30)];
[cells addObject:cell];
[cell release];
}
int x_offset = 0;
CGFloat x_end = x_offset; // end of the previous button
for (unsigned int i = 0; i < arraysize(widths); i++) {
NSRect r = [bar_ frameForBookmarkButtonFromCell:[cells objectAtIndex:i]
xOffset:&x_offset];
EXPECT_GE(r.origin.x, x_end);
x_end = NSMaxX(r);
}
}
TEST_F(BookmarkBarControllerTest, CheckForGrowth) {
WithNoAnimation at_all; // Turn off Cocoa auto animation in this scope.
BookmarkModel* model = BookmarkModelFactory::GetForBrowserContext(profile());
GURL gurl1("http://www.google.com");
base::string16 title1(ASCIIToUTF16("x"));
bookmarks::AddIfNotBookmarked(model, gurl1, title1);
GURL gurl2("http://www.google.com/blah");
base::string16 title2(ASCIIToUTF16("y"));
bookmarks::AddIfNotBookmarked(model, gurl2, title2);
EXPECT_EQ(2U, [[bar_ buttons] count]);
CGFloat width_1 = [[[bar_ buttons] objectAtIndex:0] frame].size.width;
CGFloat x_2 = [[[bar_ buttons] objectAtIndex:1] frame].origin.x;
NSButton* first = [[bar_ buttons] objectAtIndex:0];
[[first cell] setTitle:@"This is a really big title; watch out mom!"];
[bar_ checkForBookmarkButtonGrowth:first];
// Make sure the 1st button is now wider, the 2nd one is moved over,
// and they don't overlap.
NSRect frame_1 = [[[bar_ buttons] objectAtIndex:0] frame];
NSRect frame_2 = [[[bar_ buttons] objectAtIndex:1] frame];
EXPECT_GT(frame_1.size.width, width_1);
EXPECT_GT(frame_2.origin.x, x_2);
EXPECT_GE(frame_2.origin.x, frame_1.origin.x + frame_1.size.width);
}
TEST_F(BookmarkBarControllerTest, DeleteBookmark) {
BookmarkModel* model = BookmarkModelFactory::GetForBrowserContext(profile());
const char* urls[] = { "https://secret.url.com",
"http://super.duper.web.site.for.doodz.gov",
"http://www.foo-bar-baz.com/" };
const BookmarkNode* parent = model->bookmark_bar_node();
for (unsigned int i = 0; i < arraysize(urls); i++) {
model->AddURL(parent, parent->child_count(),
ASCIIToUTF16("title"), GURL(urls[i]));
}
EXPECT_EQ(3, parent->child_count());
const BookmarkNode* middle_node = parent->GetChild(1);
model->Remove(middle_node);
EXPECT_EQ(2, parent->child_count());
EXPECT_EQ(parent->GetChild(0)->url(), GURL(urls[0]));
// node 2 moved into spot 1
EXPECT_EQ(parent->GetChild(1)->url(), GURL(urls[2]));
}
// TODO(jrg): write a test to confirm that nodeFaviconLoaded calls
// checkForBookmarkButtonGrowth:.
TEST_F(BookmarkBarControllerTest, Cell) {
BookmarkModel* model = BookmarkModelFactory::GetForBrowserContext(profile());
[bar_ loaded:model];
const BookmarkNode* parent = model->bookmark_bar_node();
model->AddURL(parent, parent->child_count(),
ASCIIToUTF16("supertitle"),
GURL("http://superfriends.hall-of-justice.edu"));
const BookmarkNode* node = parent->GetChild(0);
NSCell* cell = [bar_ cellForBookmarkNode:node];
EXPECT_TRUE(cell);
EXPECT_NSEQ(@"supertitle", [cell title]);
EXPECT_EQ(node, [[cell representedObject] pointerValue]);
EXPECT_TRUE([cell menu]);
// Empty cells still have a menu.
cell = [bar_ cellForBookmarkNode:nil];
EXPECT_TRUE([cell menu]);
// Even empty cells have a title (of "(empty)")
EXPECT_TRUE([cell title]);
// cell is autoreleased; no need to release here
}
// Test drawing, mostly to ensure nothing leaks or crashes. // Test drawing, mostly to ensure nothing leaks or crashes.
TEST_F(BookmarkBarControllerTest, Display) { TEST_F(BookmarkBarControllerTest, Display) {
[[bar_ view] display]; [[bar_ view] display];
...@@ -1141,10 +898,10 @@ TEST_F(BookmarkBarControllerTest, MiddleClick) { ...@@ -1141,10 +898,10 @@ TEST_F(BookmarkBarControllerTest, MiddleClick) {
ASCIIToUTF16("super duper wide title"), ASCIIToUTF16("super duper wide title"),
GURL("http://superfriends.hall-of-justice.edu")); GURL("http://superfriends.hall-of-justice.edu"));
} }
EXPECT_FALSE([bar_ offTheSideButtonIsHidden]);
NSButton* offTheSideButton = [bar_ offTheSideButton]; NSButton* offTheSideButton = [bar_ offTheSideButton];
EXPECT_TRUE(offTheSideButton); EXPECT_TRUE(offTheSideButton);
EXPECT_FALSE([offTheSideButton isHidden]);
[offTheSideButton otherMouseUp: [offTheSideButton otherMouseUp:
cocoa_test_event_utils::MouseEventWithType(NSOtherMouseUp, 0)]; cocoa_test_event_utils::MouseEventWithType(NSOtherMouseUp, 0)];
...@@ -1168,13 +925,18 @@ TEST_F(BookmarkBarControllerTest, MiddleClick) { ...@@ -1168,13 +925,18 @@ TEST_F(BookmarkBarControllerTest, MiddleClick) {
TEST_F(BookmarkBarControllerTest, DisplaysHelpMessageOnEmpty) { TEST_F(BookmarkBarControllerTest, DisplaysHelpMessageOnEmpty) {
BookmarkModel* model = BookmarkModelFactory::GetForBrowserContext(profile()); BookmarkModel* model = BookmarkModelFactory::GetForBrowserContext(profile());
[bar_ loaded:model]; [bar_ loaded:model];
EXPECT_FALSE([[[bar_ buttonView] noItemTextField] isHidden]); EXPECT_FALSE([[[bar_ buttonView] noItemContainer] isHidden]);
} }
TEST_F(BookmarkBarControllerTest, DisplaysImportBookmarksButtonOnEmpty) { TEST_F(BookmarkBarControllerTest, HidesHelpMessageWithBookmark) {
BookmarkModel* model = BookmarkModelFactory::GetForBrowserContext(profile()); BookmarkModel* model = BookmarkModelFactory::GetForBrowserContext(profile());
const BookmarkNode* parent = model->bookmark_bar_node();
model->AddURL(parent, parent->child_count(),
ASCIIToUTF16("title"), GURL("http://one.com"));
[bar_ loaded:model]; [bar_ loaded:model];
EXPECT_FALSE([[[bar_ buttonView] importBookmarksButton] isHidden]); EXPECT_TRUE([[[bar_ buttonView] noItemContainer] isHidden]);
} }
TEST_F(BookmarkBarControllerTest, BookmarkButtonSizing) { TEST_F(BookmarkBarControllerTest, BookmarkButtonSizing) {
...@@ -1509,7 +1271,7 @@ TEST_F(BookmarkBarControllerTest, TestFolderButtons) { ...@@ -1509,7 +1271,7 @@ TEST_F(BookmarkBarControllerTest, TestFolderButtons) {
TEST_F(BookmarkBarControllerTest, OffTheSideFolder) { TEST_F(BookmarkBarControllerTest, OffTheSideFolder) {
// It starts hidden. // It starts hidden.
EXPECT_TRUE([[bar_ offTheSideButton] isHidden]); EXPECT_TRUE([bar_ offTheSideButtonIsHidden]);
// Create some buttons. // Create some buttons.
BookmarkModel* model = BookmarkModelFactory::GetForBrowserContext(profile()); BookmarkModel* model = BookmarkModelFactory::GetForBrowserContext(profile());
...@@ -1527,7 +1289,7 @@ TEST_F(BookmarkBarControllerTest, OffTheSideFolder) { ...@@ -1527,7 +1289,7 @@ TEST_F(BookmarkBarControllerTest, OffTheSideFolder) {
GURL("http://framma-lamma.com")); GURL("http://framma-lamma.com"));
// Should no longer be hidden. // Should no longer be hidden.
EXPECT_FALSE([[bar_ offTheSideButton] isHidden]); EXPECT_FALSE([bar_ offTheSideButtonIsHidden]);
// Open it; make sure we have a folder controller. // Open it; make sure we have a folder controller.
EXPECT_FALSE([bar_ folderController]); EXPECT_FALSE([bar_ folderController]);
...@@ -1666,6 +1428,258 @@ TEST_F(BookmarkBarControllerTest, CloseFolderOnAnimate) { ...@@ -1666,6 +1428,258 @@ TEST_F(BookmarkBarControllerTest, CloseFolderOnAnimate) {
EXPECT_FALSE([bar_ isAnimationRunning]); EXPECT_FALSE([bar_ isAnimationRunning]);
} }
TEST_F(BookmarkBarControllerTest, MoveRemoveAddButtons) {
BookmarkModel* model = BookmarkModelFactory::GetForBrowserContext(profile());
const BookmarkNode* root = model->bookmark_bar_node();
const std::string model_string("1b 2f:[ 2f1b 2f2b ] 3b ");
bookmarks::test::AddNodesFromModelString(model, root, model_string);
// Validate initial model.
std::string actualModelString = bookmarks::test::ModelStringFromNode(root);
EXPECT_EQ(model_string, actualModelString);
// Remember how many buttons are showing.
int oldDisplayedButtons = [bar_ displayedButtonCount];
NSArray* buttons = [bar_ buttons];
// Move a button around a bit.
[bar_ moveButtonFromIndex:0 toIndex:2];
EXPECT_NSEQ(@"2f", [[buttons objectAtIndex:0] title]);
EXPECT_NSEQ(@"3b", [[buttons objectAtIndex:1] title]);
EXPECT_NSEQ(@"1b", [[buttons objectAtIndex:2] title]);
EXPECT_EQ(oldDisplayedButtons, [bar_ displayedButtonCount]);
[bar_ moveButtonFromIndex:2 toIndex:0];
EXPECT_NSEQ(@"1b", [[buttons objectAtIndex:0] title]);
EXPECT_NSEQ(@"2f", [[buttons objectAtIndex:1] title]);
EXPECT_NSEQ(@"3b", [[buttons objectAtIndex:2] title]);
EXPECT_EQ(oldDisplayedButtons, [bar_ displayedButtonCount]);
// Add a couple of buttons.
const BookmarkNode* parent = root->GetChild(1); // Purloin an existing node.
const BookmarkNode* node = parent->GetChild(0);
[bar_ addButtonForNode:node atIndex:0];
EXPECT_NSEQ(@"2f1b", [[buttons objectAtIndex:0] title]);
EXPECT_NSEQ(@"1b", [[buttons objectAtIndex:1] title]);
EXPECT_NSEQ(@"2f", [[buttons objectAtIndex:2] title]);
EXPECT_NSEQ(@"3b", [[buttons objectAtIndex:3] title]);
EXPECT_EQ(oldDisplayedButtons + 1, [bar_ displayedButtonCount]);
node = parent->GetChild(1);
[bar_ addButtonForNode:node atIndex:-1];
EXPECT_NSEQ(@"2f1b", [[buttons objectAtIndex:0] title]);
EXPECT_NSEQ(@"1b", [[buttons objectAtIndex:1] title]);
EXPECT_NSEQ(@"2f", [[buttons objectAtIndex:2] title]);
EXPECT_NSEQ(@"3b", [[buttons objectAtIndex:3] title]);
EXPECT_NSEQ(@"2f2b", [[buttons objectAtIndex:4] title]);
EXPECT_EQ(oldDisplayedButtons + 2, [bar_ displayedButtonCount]);
// Remove a couple of buttons.
[bar_ removeButton:4 animate:NO];
[bar_ removeButton:1 animate:NO];
EXPECT_NSEQ(@"2f1b", [[buttons objectAtIndex:0] title]);
EXPECT_NSEQ(@"2f", [[buttons objectAtIndex:1] title]);
EXPECT_NSEQ(@"3b", [[buttons objectAtIndex:2] title]);
EXPECT_EQ(oldDisplayedButtons, [bar_ displayedButtonCount]);
}
TEST_F(BookmarkBarControllerTest, ShrinkOrHideView) {
NSRect viewFrame = NSMakeRect(0.0, 0.0, 500.0, 50.0);
NSView* view = [[[NSView alloc] initWithFrame:viewFrame] autorelease];
EXPECT_FALSE([view isHidden]);
[bar_ shrinkOrHideView:view forMaxX:500.0];
EXPECT_EQ(500.0, NSWidth([view frame]));
EXPECT_FALSE([view isHidden]);
[bar_ shrinkOrHideView:view forMaxX:450.0];
EXPECT_EQ(450.0, NSWidth([view frame]));
EXPECT_FALSE([view isHidden]);
[bar_ shrinkOrHideView:view forMaxX:40.0];
EXPECT_EQ(40.0, NSWidth([view frame]));
EXPECT_FALSE([view isHidden]);
[bar_ shrinkOrHideView:view forMaxX:31.0];
EXPECT_EQ(31.0, NSWidth([view frame]));
EXPECT_FALSE([view isHidden]);
[bar_ shrinkOrHideView:view forMaxX:29.0];
EXPECT_TRUE([view isHidden]);
}
// Simulate coarse browser window width change and ensure that the bookmark
// buttons that should be visible are visible.
TEST_F(BookmarkBarControllerTest, RedistributeButtonsOnBarAsNeeded) {
// Hide the apps shortcut.
profile()->GetPrefs()->SetBoolean(
bookmarks::prefs::kShowAppsShortcutInBookmarkBar, false);
ASSERT_TRUE([bar_ appsPageShortcutButtonIsHidden]);
// Add three buttons to the bookmark bar.
BookmarkModel* model = BookmarkModelFactory::GetForBrowserContext(profile());
const BookmarkNode* root = model->bookmark_bar_node();
// Make long labels to test coarse resizes. After 16 digits, text eliding
// starts.
const std::string model_string(
"0000000000000000 1111111111111111 2222222222222222 ");
bookmarks::test::AddNodesFromModelString(model, root, model_string);
NSRect frame = [[bar_ view] frame];
frame.size.width = 400; // Typical minimum browser size.
[[bar_ view] setFrame:frame];
EXPECT_EQ(2, [bar_ displayedButtonCount]);
{
base::mac::ScopedNSAutoreleasePool pool;
frame.size.width = 800;
[[bar_ view] setFrame:frame];
EXPECT_EQ(3, [bar_ displayedButtonCount]);
const BookmarkNode* last = model->bookmark_bar_node()->GetChild(2);
EXPECT_TRUE(last);
[bar_ startPulsingBookmarkNode:last];
frame.size.width = 400;
[[bar_ view] setFrame:frame];
EXPECT_EQ(2, [bar_ displayedButtonCount]);
}
// Regression test for http://crbug.com/616051.
[bar_ stopPulsingBookmarkNode];
}
// Simiulate browser window width change and ensure that the bookmark buttons
// that should be visible are visible.
// Appears to fail on Mac 10.11 bot on the waterfall; http://crbug.com/612640.
TEST_F(BookmarkBarControllerTest, DISABLED_LastBookmarkResizeBehavior) {
// Hide the apps shortcut.
profile()->GetPrefs()->SetBoolean(
bookmarks::prefs::kShowAppsShortcutInBookmarkBar, false);
ASSERT_TRUE([bar_ appsPageShortcutButtonIsHidden]);
// Add three buttons to the bookmark bar.
BookmarkModel* model = BookmarkModelFactory::GetForBrowserContext(profile());
const BookmarkNode* root = model->bookmark_bar_node();
const std::string model_string("1b 2f:[ 2f1b 2f2b ] 3b ");
bookmarks::test::AddNodesFromModelString(model, root, model_string);
[bar_ frameDidChange];
// Step through simulated window resizings. In resizing from the first width
// to the second, the bookmark bar should transition from displaying one
// button to two. Of the next 5 widths, the third transitions the bar from
// displaying two buttons to three. The next width (200.0) resizes the bar to
// a large width that does not change the number of visible buttons, and the
// remaining widths step through all the previous resizings in reverse, which
// should correspond to the previous number of visible buttons.
//
// To recalibrate this test for new OS releases, etc., determine values for
// |view_widths[1]| and |view_widths[4]| which will cause a visible button
// transition from 1 -> 2 and 2 -> 3, respectively. With those two widths you
// can easily compute all widths from 0 through 6. |view_widths[7]| is always
// 200.0, and the remainder are the reverse of widths 0 through 6. When all
// three buttons are visible, be sure to sanity check with frames (i.e. under
// MD the first button should start at x=10, and there should be 16pt of space
// between the buttons).
//
// The default font changed between OSX Mavericks, OSX Yosemite, and
// OSX El Capitan, so this test requires different widths to trigger the
// appropriate results.
CGFloat view_widths_el_capitan[] =
{ 139.0, 140.0, 150.0, 151.0, 152.0, 153.0,
154.0, 200.0, 154.0, 153.0, 152.0, 151.0,
150.0, 140.0, 139.0 };
CGFloat view_widths_yosemite[] =
{ 140.0, 141.0, 150.0, 151.0, 152.0, 153.0,
154.0, 200.0, 154.0, 153.0, 152.0, 151.0,
150.0, 141.0, 140.0 };
CGFloat view_widths_rest[] =
{ 142.0, 143.0, 153.0, 154.0, 155.0, 156.0,
157.0, 200.0, 157.0, 156.0, 155.0, 154.0,
153.0, 143.0, 142.0 };
CGFloat* view_widths = NULL;
if (base::mac::IsOS10_11()) {
view_widths = view_widths_el_capitan;
} else if (base::mac::IsOS10_10()) {
view_widths = view_widths_yosemite;
} else {
view_widths = view_widths_rest;
}
BOOL off_the_side_button_is_hidden_results[] =
{ NO, NO, NO, NO, YES, YES, YES, YES, YES, YES, YES, NO, NO, NO, NO};
int displayed_button_count_results[] =
{ 1, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 1 };
for (unsigned int i = 0; i < arraysize(view_widths_yosemite); ++i) {
NSRect frame = [[bar_ view] frame];
frame.size.width = view_widths[i] + bookmarks::kBookmarkRightMargin;
[[bar_ view] setFrame:frame];
EXPECT_EQ(off_the_side_button_is_hidden_results[i],
[bar_ offTheSideButtonIsHidden]);
EXPECT_EQ(displayed_button_count_results[i], [bar_ displayedButtonCount]);
}
}
TEST_F(BookmarkBarControllerTest, BookmarksWithAppsPageShortcut) {
BookmarkModel* model = BookmarkModelFactory::GetForBrowserContext(profile());
const BookmarkNode* root = model->bookmark_bar_node();
const std::string model_string("1b 2f:[ 2f1b 2f2b ] 3b ");
bookmarks::test::AddNodesFromModelString(model, root, model_string);
[bar_ frameDidChange];
// Apps page shortcut button should be visible.
ASSERT_FALSE([bar_ appsPageShortcutButtonIsHidden]);
// Bookmarks should be to the right of the Apps page shortcut button.
CGFloat apps_button_right = NSMaxX([[bar_ appsPageShortcutButton] frame]);
CGFloat right = apps_button_right;
NSArray* buttons = [bar_ buttons];
for (size_t i = 0; i < [buttons count]; ++i) {
EXPECT_LE(right, NSMinX([[buttons objectAtIndex:i] frame]));
right = NSMaxX([[buttons objectAtIndex:i] frame]);
}
// Removing the Apps button should move every bookmark to the left.
profile()->GetPrefs()->SetBoolean(
bookmarks::prefs::kShowAppsShortcutInBookmarkBar, false);
ASSERT_TRUE([bar_ appsPageShortcutButtonIsHidden]);
EXPECT_GT(apps_button_right, NSMinX([[buttons objectAtIndex:0] frame]));
for (size_t i = 1; i < [buttons count]; ++i) {
EXPECT_LE(NSMaxX([[buttons objectAtIndex:i - 1] frame]),
NSMinX([[buttons objectAtIndex:i] frame]));
}
}
TEST_F(BookmarkBarControllerTest, BookmarksWithoutAppsPageShortcut) {
// The no item containers should be to the right of the Apps button.
ASSERT_FALSE([bar_ appsPageShortcutButtonIsHidden]);
CGFloat apps_button_right = NSMaxX([[bar_ appsPageShortcutButton] frame]);
EXPECT_LE(apps_button_right,
NSMinX([[[bar_ buttonView] noItemTextfield] frame]));
EXPECT_LE(NSMaxX([[[bar_ buttonView] noItemTextfield] frame]),
NSMinX([[[bar_ buttonView] importBookmarksButton] frame]));
// Removing the Apps button should move the no item containers to the left.
profile()->GetPrefs()->SetBoolean(
bookmarks::prefs::kShowAppsShortcutInBookmarkBar, false);
ASSERT_TRUE([bar_ appsPageShortcutButtonIsHidden]);
EXPECT_GT(apps_button_right,
NSMinX([[[bar_ buttonView] noItemTextfield] frame]));
EXPECT_LE(NSMaxX([[[bar_ buttonView] noItemTextfield] frame]),
NSMinX([[[bar_ buttonView] importBookmarksButton] frame]));
}
TEST_F(BookmarkBarControllerTest, ManagedShowAppsShortcutInBookmarksBar) {
// By default the pref is not managed and the apps shortcut is shown.
sync_preferences::TestingPrefServiceSyncable* prefs =
profile()->GetTestingPrefService();
EXPECT_FALSE(prefs->IsManagedPreference(
bookmarks::prefs::kShowAppsShortcutInBookmarkBar));
EXPECT_FALSE([bar_ appsPageShortcutButtonIsHidden]);
// Hide the apps shortcut by policy, via the managed pref.
prefs->SetManagedPref(bookmarks::prefs::kShowAppsShortcutInBookmarkBar,
base::MakeUnique<base::Value>(false));
EXPECT_TRUE([bar_ appsPageShortcutButtonIsHidden]);
// And try showing it via policy too.
prefs->SetManagedPref(bookmarks::prefs::kShowAppsShortcutInBookmarkBar,
base::MakeUnique<base::Value>(true));
EXPECT_FALSE([bar_ appsPageShortcutButtonIsHidden]);
}
class BookmarkBarControllerOpenAllTest : public BookmarkBarControllerTest { class BookmarkBarControllerOpenAllTest : public BookmarkBarControllerTest {
public: public:
void SetUp() override { void SetUp() override {
...@@ -1749,7 +1763,7 @@ class BookmarkBarControllerNotificationTest : public CocoaProfileTest { ...@@ -1749,7 +1763,7 @@ class BookmarkBarControllerNotificationTest : public CocoaProfileTest {
initialWidth:NSWidth(parent_frame) initialWidth:NSWidth(parent_frame)
delegate:nil]); delegate:nil]);
// Loads the view // Forces loading of the nib.
[[bar_ controlledView] setResizeDelegate:resizeDelegate_]; [[bar_ controlledView] setResizeDelegate:resizeDelegate_];
// Awkwardness to look like we've been installed. // Awkwardness to look like we've been installed.
[parent_view_ addSubview:[bar_ view]]; [parent_view_ addSubview:[bar_ view]];
...@@ -1827,13 +1841,13 @@ TEST_F(BookmarkBarControllerDragDropTest, DragMoveBarBookmarkToOffTheSide) { ...@@ -1827,13 +1841,13 @@ TEST_F(BookmarkBarControllerDragDropTest, DragMoveBarBookmarkToOffTheSide) {
std::string actualModelString = bookmarks::test::ModelStringFromNode(root); std::string actualModelString = bookmarks::test::ModelStringFromNode(root);
EXPECT_EQ(model_string, actualModelString); EXPECT_EQ(model_string, actualModelString);
// Ensure that the off-the-side button is showing. // Insure that the off-the-side is not showing.
bookmarks::BookmarkBarLayout layout = [bar_ layoutFromCurrentState]; ASSERT_FALSE([bar_ offTheSideButtonIsHidden]);
ASSERT_TRUE(layout.IsOffTheSideButtonVisible());
// Remember how many buttons are showing and are available. // Remember how many buttons are showing and are available.
int oldDisplayedButtons = layout.VisibleButtonCount(); int oldDisplayedButtons = [bar_ displayedButtonCount];
int oldChildCount = root->child_count(); int oldChildCount = root->child_count();
// Pop up the off-the-side menu. // Pop up the off-the-side menu.
BookmarkButton* otsButton = (BookmarkButton*)[bar_ offTheSideButton]; BookmarkButton* otsButton = (BookmarkButton*)[bar_ offTheSideButton];
ASSERT_TRUE(otsButton); ASSERT_TRUE(otsButton);
...@@ -1853,19 +1867,11 @@ TEST_F(BookmarkBarControllerDragDropTest, DragMoveBarBookmarkToOffTheSide) { ...@@ -1853,19 +1867,11 @@ TEST_F(BookmarkBarControllerDragDropTest, DragMoveBarBookmarkToOffTheSide) {
[otsController dragButton:draggedButton [otsController dragButton:draggedButton
to:[targetButton center] to:[targetButton center]
copy:YES]; copy:YES];
// Close
[[otsButton target] performSelector:@selector(openOffTheSideFolderFromButton:)
withObject:otsButton];
// Open
[[otsButton target] performSelector:@selector(openOffTheSideFolderFromButton:)
withObject:otsButton];
// There should still be the same number of buttons in the bar // There should still be the same number of buttons in the bar
// and off-the-side should have one more. // and off-the-side should have one more.
layout = [bar_ layoutFromCurrentState]; int newDisplayedButtons = [bar_ displayedButtonCount];
int newDisplayedButtons = layout.VisibleButtonCount();
int newChildCount = root->child_count(); int newChildCount = root->child_count();
int newOTSCount = (int)[[[bar_ folderController] buttons] count]; int newOTSCount = (int)[[otsController buttons] count];
EXPECT_EQ(oldDisplayedButtons, newDisplayedButtons); EXPECT_EQ(oldDisplayedButtons, newDisplayedButtons);
EXPECT_EQ(oldChildCount + 1, newChildCount); EXPECT_EQ(oldChildCount + 1, newChildCount);
EXPECT_EQ(oldOTSCount + 1, newOTSCount); EXPECT_EQ(oldOTSCount + 1, newOTSCount);
...@@ -1893,12 +1899,11 @@ TEST_F(BookmarkBarControllerDragDropTest, DragOffTheSideToOther) { ...@@ -1893,12 +1899,11 @@ TEST_F(BookmarkBarControllerDragDropTest, DragOffTheSideToOther) {
std::string actualOtherString = bookmarks::test::ModelStringFromNode(other); std::string actualOtherString = bookmarks::test::ModelStringFromNode(other);
EXPECT_EQ(other_string, actualOtherString); EXPECT_EQ(other_string, actualOtherString);
// Ensure that the off-the-side button is showing. // Insure that the off-the-side is showing.
bookmarks::BookmarkBarLayout layout = [bar_ layoutFromCurrentState]; ASSERT_FALSE([bar_ offTheSideButtonIsHidden]);
ASSERT_TRUE(layout.IsOffTheSideButtonVisible());
// Remember how many buttons are showing and are available. // Remember how many buttons are showing and are available.
int oldDisplayedButtons = layout.VisibleButtonCount(); int oldDisplayedButtons = [bar_ displayedButtonCount];
int oldRootCount = root->child_count(); int oldRootCount = root->child_count();
int oldOtherCount = other->child_count(); int oldOtherCount = other->child_count();
...@@ -2047,7 +2052,7 @@ TEST_F(BookmarkBarControllerDragDropTest, DropPositionIndicator) { ...@@ -2047,7 +2052,7 @@ TEST_F(BookmarkBarControllerDragDropTest, DropPositionIndicator) {
// Hide the apps shortcut. // Hide the apps shortcut.
profile()->GetPrefs()->SetBoolean( profile()->GetPrefs()->SetBoolean(
bookmarks::prefs::kShowAppsShortcutInBookmarkBar, false); bookmarks::prefs::kShowAppsShortcutInBookmarkBar, false);
ASSERT_TRUE([[bar_ appsPageShortcutButton] isHidden]); ASSERT_TRUE([bar_ appsPageShortcutButtonIsHidden]);
// Validate initial model. // Validate initial model.
std::string actualModel = bookmarks::test::ModelStringFromNode(root); std::string actualModel = bookmarks::test::ModelStringFromNode(root);
......
...@@ -790,11 +790,8 @@ TEST_F(BookmarkBarFolderControllerMenuTest, DragMoveBarBookmarkToFolder) { ...@@ -790,11 +790,8 @@ TEST_F(BookmarkBarFolderControllerMenuTest, DragMoveBarBookmarkToFolder) {
"4f2f1b 4f2f2b 4f2f3b ] 4f3f:[ 4f3f1b 4f3f2b 4f3f3b ] ] 5b "); "4f2f1b 4f2f2b 4f2f3b ] 4f3f:[ 4f3f1b 4f3f2b 4f3f3b ] ] 5b ");
EXPECT_EQ(expected_string, bookmarks::test::ModelStringFromNode(root)); EXPECT_EQ(expected_string, bookmarks::test::ModelStringFromNode(root));
// Reopen the window. // Verify the window still appears by looking for its controller.
[[toFolder target] performSelector:@selector(openBookmarkFolderFromButton:)
withObject:toFolder];
EXPECT_TRUE([bar_ folderController]); EXPECT_TRUE([bar_ folderController]);
folderController = [bar_ folderController];
// Gather the new frames. // Gather the new frames.
NSRect newToFolderFrame = [toFolder frame]; NSRect newToFolderFrame = [toFolder frame];
......
...@@ -11,10 +11,6 @@ ...@@ -11,10 +11,6 @@
@class BookmarkBarController; @class BookmarkBarController;
namespace bookmarks {
const CGFloat kInitialNoItemTextFieldXOrigin = 5;
}
// A simple custom NSView for the bookmark bar used to prevent clicking and // A simple custom NSView for the bookmark bar used to prevent clicking and
// dragging from moving the browser window. // dragging from moving the browser window.
@interface BookmarkBarView : NSView { @interface BookmarkBarView : NSView {
...@@ -23,11 +19,13 @@ const CGFloat kInitialNoItemTextFieldXOrigin = 5; ...@@ -23,11 +19,13 @@ const CGFloat kInitialNoItemTextFieldXOrigin = 5;
CGFloat dropIndicatorPosition_; // x position CGFloat dropIndicatorPosition_; // x position
BookmarkBarController* controller_; BookmarkBarController* controller_;
base::scoped_nsobject<NSTextField> noItemTextField_; base::scoped_nsobject<NSTextField> noItemTextfield_;
base::scoped_nsobject<NSButton> importBookmarksButton_; base::scoped_nsobject<NSButton> importBookmarksButton_;
base::scoped_nsobject<NSView> noItemContainer_;
} }
- (NSTextField*)noItemTextField; - (NSTextField*)noItemTextfield;
- (NSButton*)importBookmarksButton; - (NSButton*)importBookmarksButton;
- (NSView*)noItemContainer;
- (instancetype)initWithController:(BookmarkBarController*)controller - (instancetype)initWithController:(BookmarkBarController*)controller
frame:(NSRect)frame; frame:(NSRect)frame;
......
...@@ -28,10 +28,14 @@ using base::UserMetricsAction; ...@@ -28,10 +28,14 @@ using base::UserMetricsAction;
using bookmarks::BookmarkModel; using bookmarks::BookmarkModel;
using bookmarks::BookmarkNode; using bookmarks::BookmarkNode;
static const CGFloat kInitialElementYOrigin = 7; static const CGFloat kInitialContainerWidth = 596;
static const CGFloat kInitialContainerHeight = 41;
static const CGFloat kInitialElementYOrigin = 20;
static const CGFloat kInitialElementHeight = 14; static const CGFloat kInitialElementHeight = 14;
static const CGFloat kInitialTextFieldXOrigin = 5; static const CGFloat kInitialTextFieldXOrigin = 5;
// static const CGFloat kInitialTextFieldWidth = 167;
static const CGFloat kTextFieldTrailingPadding = 5; static const CGFloat kTextFieldTrailingPadding = 5;
// static const CGFloat kInitialButtonWidth = 199;
@interface BookmarkBarView (Private) @interface BookmarkBarView (Private)
- (void)themeDidChangeNotification:(NSNotification*)aNotification; - (void)themeDidChangeNotification:(NSNotification*)aNotification;
...@@ -76,40 +80,53 @@ static const CGFloat kTextFieldTrailingPadding = 5; ...@@ -76,40 +80,53 @@ static const CGFloat kTextFieldTrailingPadding = 5;
NSFont* smallSystemFont = NSFont* smallSystemFont =
[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]; [NSFont systemFontOfSize:[NSFont smallSystemFontSize]];
noItemContainer_.reset(
[[NSView alloc] initWithFrame:NSMakeRect(0, 0, kInitialContainerWidth,
kInitialContainerHeight)]);
[noItemContainer_ setAutoresizingMask:NSViewMaxXMargin];
[noItemContainer_ setAutoresizingMask:NSViewWidthSizable];
noItemTextField_.reset([[NSTextField alloc] noItemTextfield_.reset([[NSTextField alloc]
initWithFrame:NSMakeRect(kInitialTextFieldXOrigin, initWithFrame:NSMakeRect(kInitialTextFieldXOrigin,
kInitialElementYOrigin, CGFLOAT_MAX, kInitialElementYOrigin, CGFLOAT_MAX,
kInitialElementHeight)]); kInitialElementHeight)]);
[noItemTextField_ setFont:smallSystemFont]; [noItemTextfield_ setAutoresizingMask:NSViewWidthSizable];
[noItemTextField_ [noItemTextfield_ setFont:smallSystemFont];
[noItemTextfield_
setStringValue:l10n_util::GetNSString(IDS_BOOKMARKS_NO_ITEMS)]; setStringValue:l10n_util::GetNSString(IDS_BOOKMARKS_NO_ITEMS)];
[noItemTextField_ setEditable:NO]; [noItemTextfield_ setEditable:NO];
[noItemTextField_ setBordered:NO]; [noItemTextfield_ setBordered:NO];
[[noItemTextField_ cell] setLineBreakMode:NSLineBreakByTruncatingTail]; [[noItemTextfield_ cell] setLineBreakMode:NSLineBreakByTruncatingTail];
[noItemTextField_ setTextColor:[NSColor controlTextColor]]; [noItemTextfield_ setTextColor:[NSColor controlTextColor]];
[noItemTextField_ setBackgroundColor:[NSColor controlColor]]; [noItemTextfield_ setBackgroundColor:[NSColor controlColor]];
[noItemTextField_ setDrawsBackground:NO]; [noItemTextfield_ setDrawsBackground:NO];
[noItemTextField_ setTextColor:[NSColor controlTextColor]]; [noItemTextfield_ setTextColor:[NSColor controlTextColor]];
[noItemTextField_ setBackgroundColor:[NSColor controlColor]]; [noItemTextfield_ setBackgroundColor:[NSColor controlColor]];
[noItemTextField_ sizeToFit]; [noItemTextfield_ sizeToFit];
NSButton* importButton = [HyperlinkButtonCell NSButton* importButton = [HyperlinkButtonCell
buttonWithString:l10n_util::GetNSString(IDS_BOOKMARK_BAR_IMPORT_LINK)]; buttonWithString:l10n_util::GetNSString(IDS_BOOKMARK_BAR_IMPORT_LINK)];
importBookmarksButton_.reset([importButton retain]); importBookmarksButton_.reset([importButton retain]);
[importBookmarksButton_ [importBookmarksButton_
setFrame:NSMakeRect(NSMaxX([noItemTextField_ frame]) + setFrame:NSMakeRect(NSMaxX([noItemTextfield_ frame]) +
kTextFieldTrailingPadding, kTextFieldTrailingPadding,
kInitialElementYOrigin, CGFLOAT_MAX, kInitialElementYOrigin, CGFLOAT_MAX,
kInitialElementHeight)]; kInitialElementHeight)];
[importBookmarksButton_ setAutoresizingMask:NSViewMaxXMargin];
[importBookmarksButton_ setFont:smallSystemFont]; [importBookmarksButton_ setFont:smallSystemFont];
[importBookmarksButton_ sizeToFit]; [importBookmarksButton_ sizeToFit];
[noItemContainer_ addSubview:importBookmarksButton_];
[self addSubview:noItemTextField_]; [noItemContainer_ addSubview:noItemTextfield_];
[self addSubview:importBookmarksButton_]; NSRect containerFrame = [noItemContainer_ frame];
containerFrame.size.width = std::max(
NSWidth(containerFrame), NSMaxX([importBookmarksButton_ frame]));
[noItemContainer_ setFrame:containerFrame];
[self addSubview:noItemContainer_];
[self registerForNotificationsAndDraggedTypes]; [self registerForNotificationsAndDraggedTypes];
} }
return self; return self;
...@@ -157,7 +174,7 @@ static const CGFloat kTextFieldTrailingPadding = 5; ...@@ -157,7 +174,7 @@ static const CGFloat kTextFieldTrailingPadding = 5;
NSColor* color = NSColor* color =
themeProvider->GetNSColor(ThemeProperties::COLOR_BOOKMARK_TEXT); themeProvider->GetNSColor(ThemeProperties::COLOR_BOOKMARK_TEXT);
[noItemTextField_ setTextColor:color]; [noItemTextfield_ setTextColor:color];
} }
// Mouse down events on the bookmark bar should not allow dragging the parent // Mouse down events on the bookmark bar should not allow dragging the parent
...@@ -166,8 +183,12 @@ static const CGFloat kTextFieldTrailingPadding = 5; ...@@ -166,8 +183,12 @@ static const CGFloat kTextFieldTrailingPadding = 5;
return NO; return NO;
} }
- (NSTextField*)noItemTextField { - (NSTextField*)noItemTextfield {
return noItemTextField_; return noItemTextfield_;
}
- (NSView*)noItemContainer {
return noItemContainer_;
} }
- (NSButton*)importBookmarksButton { - (NSButton*)importBookmarksButton {
...@@ -236,6 +257,7 @@ static const CGFloat kTextFieldTrailingPadding = 5; ...@@ -236,6 +257,7 @@ static const CGFloat kTextFieldTrailingPadding = 5;
- (void)draggingEnded:(id<NSDraggingInfo>)info { - (void)draggingEnded:(id<NSDraggingInfo>)info {
[controller_ draggingEnded:info]; [controller_ draggingEnded:info];
[[BookmarkButton draggedButton] setHidden:NO];
if (dropIndicatorShown_) { if (dropIndicatorShown_) {
dropIndicatorShown_ = NO; dropIndicatorShown_ = NO;
[self dropIndicatorChanged]; [self dropIndicatorChanged];
......
...@@ -81,6 +81,7 @@ const CGFloat kKernAmount = 0.2; ...@@ -81,6 +81,7 @@ const CGFloat kKernAmount = 0.2;
- (NSDictionary*)titleTextAttributes; - (NSDictionary*)titleTextAttributes;
@end @end
@implementation BookmarkButtonCell @implementation BookmarkButtonCell
@synthesize startingChildIndex = startingChildIndex_; @synthesize startingChildIndex = startingChildIndex_;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment