summaryrefslogtreecommitdiffstats
path: root/chrome/browser/cocoa/bookmark_bar_folder_controller.mm
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/browser/cocoa/bookmark_bar_folder_controller.mm')
-rw-r--r--chrome/browser/cocoa/bookmark_bar_folder_controller.mm271
1 files changed, 235 insertions, 36 deletions
diff --git a/chrome/browser/cocoa/bookmark_bar_folder_controller.mm b/chrome/browser/cocoa/bookmark_bar_folder_controller.mm
index 29d7185..461d555 100644
--- a/chrome/browser/cocoa/bookmark_bar_folder_controller.mm
+++ b/chrome/browser/cocoa/bookmark_bar_folder_controller.mm
@@ -17,7 +17,6 @@
#import "chrome/browser/cocoa/browser_window_controller.h"
#import "chrome/browser/cocoa/event_utils.h"
-
namespace {
// Frequency of the scrolling timer in seconds.
@@ -57,6 +56,12 @@ const CGFloat kScrollWindowVerticalMargin = 0.0;
- (void)removeScrollTracking;
- (void)endScroll;
- (void)addScrollTimerWithDelta:(CGFloat)delta;
+
+// Determine the best button width (which will be the widest button or the
+// maximum allowable button width, whichever is less) and resize all buttons.
+// Return the new width (so that the window can be adjusted, if necessary).
+- (CGFloat)adjustButtonWidths;
+
@end
@implementation BookmarkBarFolderController
@@ -69,6 +74,10 @@ const CGFloat kScrollWindowVerticalMargin = 0.0;
ofType:@"nib"];
if ((self = [super initWithWindowNibPath:nibPath owner:self])) {
parentButton_.reset([button retain]);
+#if 1
+ // We want the button to remain bordered as part of the menu path.
+ [[button cell] setShowsBorderOnlyWhileMouseInside:NO];
+#endif
parentController_.reset([parentController retain]);
barController_ = barController; // WEAK
buttons_.reset([[NSMutableArray alloc] init]);
@@ -82,6 +91,11 @@ const CGFloat kScrollWindowVerticalMargin = 0.0;
}
- (void)dealloc {
+#if 1
+ // The button is no longer part of the menu path.
+ [[parentButton_ cell] setShowsBorderOnlyWhileMouseInside:YES];
+ [parentButton_ setNeedsDisplay];
+#endif
[self removeScrollTracking];
[self endScroll];
[hoverState_ draggingExited];
@@ -143,9 +157,13 @@ const CGFloat kScrollWindowVerticalMargin = 0.0;
// The "+2" is needed because, sometimes, Cocoa is off by a tad when
// returning the value it thinks it needs.
CGFloat desired = [cell cellSize].width + 2;
- frame.size.width = std::min(
- std::max(bookmarks::kBookmarkMenuButtonMinimumWidth, desired),
- bookmarks::kBookmarkMenuButtonMaximumWidth);
+ // The width is determined from the maximum of the proposed width
+ // (provided in |frame|) or the natural width of the title, then
+ // limited by the abolute minimum and maximum allowable widths.
+ frame.size.width =
+ std::min(std::max(bookmarks::kBookmarkMenuButtonMinimumWidth,
+ std::max(frame.size.width, desired)),
+ bookmarks::kBookmarkMenuButtonMaximumWidth);
BookmarkButton* button = [[[BookmarkButton alloc] initWithFrame:frame]
autorelease];
@@ -210,11 +228,13 @@ const CGFloat kScrollWindowVerticalMargin = 0.0;
bookmarkBarBottomLeftInScreen.y);
} else {
// Our parent controller is another BookmarkBarFolderController.
- // In this case, start ot the RIGHT of the parent button.
+ // In this case, start with a slight overlap on the RIGHT of the
+ // parent button, which looks much more menu-like than with none.
// Start to RIGHT of the button.
// TODO(jrg): If too far to right, pop left again.
// http://crbug.com/36225
- newWindowTopLeft.x = NSMaxX([[parentButton_ window] frame]);
+ newWindowTopLeft.x = NSMaxX([[parentButton_ window] frame]) -
+ bookmarks::kBookmarkMenuOverlap;
NSPoint top = NSMakePoint(0, (NSMaxY([parentButton_ frame]) +
bookmarks::kBookmarkVerticalPadding));
NSPoint topOfWindow =
@@ -254,7 +274,7 @@ const CGFloat kScrollWindowVerticalMargin = 0.0;
NSRect buttonsOuterFrame = NSMakeRect(
bookmarks::kBookmarkHorizontalPadding,
height - (bookmarks::kBookmarkBarHeight -
- bookmarks::kBookmarkHorizontalPadding),
+ bookmarks::kBookmarkVerticalPadding),
bookmarks::kDefaultBookmarkWidth,
(bookmarks::kBookmarkBarHeight -
2 * bookmarks::kBookmarkVerticalPadding));
@@ -281,24 +301,10 @@ const CGFloat kScrollWindowVerticalMargin = 0.0;
}
}
- // Now that we have all our buttons we can determine the real size
- // of our window.
- CGFloat width = 0.0;
- for (BookmarkButton* button in buttons_.get()) {
- width = std::max(width, NSWidth([button bounds]));
- }
- width = std::min(width, bookmarks::kBookmarkMenuButtonMaximumWidth);
-
- // Things look and feel more menu-like if all the buttons are the
- // full width of the window, especially if there are submenus.
- for (BookmarkButton* button in buttons_.get()) {
- NSRect buttonFrame = [button frame];
- buttonFrame.size.width = width;
- [button setFrame:buttonFrame];
- }
- width += (2 * bookmarks::kBookmarkVerticalPadding);
-
- // Finally, set our window size (make sure it fits on screen).
+ // Now that all buttons have been installed, adjust their sizes to be
+ // consistent, determine the best size for the window, and set the window.
+ CGFloat width =
+ [self adjustButtonWidths] + (2 * bookmarks::kBookmarkVerticalPadding);
NSRect windowFrame = NSMakeRect(newWindowTopLeft.x,
newWindowTopLeft.y - height,
width + kScrollViewContentWidthMargin,
@@ -341,6 +347,169 @@ const CGFloat kScrollWindowVerticalMargin = 0.0;
[self configureWindowLevel];
}
+- (void)offsetFolderMenuWindow:(NSSize)offset {
+ NSWindow* window = [self window];
+ NSRect windowFrame = [window frame];
+ windowFrame.origin.x -= offset.width;
+ windowFrame.origin.y += offset.height; // Yes, in the opposite direction!
+ [window setFrame:windowFrame display:YES];
+ [folderController_ offsetFolderMenuWindow:offset];
+}
+
+// TODO(mrossetti): See if the following can be moved into view's viewWillDraw:.
+- (CGFloat)adjustButtonWidths {
+ CGFloat width = 0.0;
+ for (BookmarkButton* button in buttons_.get()) {
+ width = std::max(width, NSWidth([button bounds]));
+ }
+ width = std::min(width, bookmarks::kBookmarkMenuButtonMaximumWidth);
+ // Things look and feel more menu-like if all the buttons are the
+ // full width of the window, especially if there are submenus.
+ for (BookmarkButton* button in buttons_.get()) {
+ NSRect buttonFrame = [button frame];
+ buttonFrame.size.width = width;
+ [button setFrame:buttonFrame];
+ }
+ return width;
+}
+
+#pragma mark BookmarkButtonControllerProtocol
+
+- (void)addButtonForNode:(const BookmarkNode*)node
+ atIndex:(NSInteger)buttonIndex {
+ if (buttonIndex == -1)
+ buttonIndex = [buttons_ count];
+
+ // Remember the last moved button's frame or the default.
+ NSRect buttonFrame = NSMakeRect(bookmarks::kBookmarkHorizontalPadding,
+ NSHeight([mainView_ frame]) + bookmarks::kBookmarkBarHeight +
+ bookmarks::kBookmarkVerticalPadding,
+ NSWidth([mainView_ frame]) - 2 * bookmarks::kBookmarkHorizontalPadding
+ - kScrollViewContentWidthMargin,
+ bookmarks::kBookmarkBarHeight - 2 * bookmarks::kBookmarkVerticalPadding);
+ BookmarkButton* button = nil; // Remember so it can be inavalidated.
+ for (NSInteger i = 0; i < buttonIndex; ++i) {
+ button = [buttons_ objectAtIndex:i];
+ buttonFrame = [button frame];
+ buttonFrame.origin.y += bookmarks::kBookmarkBarHeight;
+ [button setFrame:buttonFrame];
+ }
+ [[button cell] mouseExited:nil]; // De-highlight.
+ buttonFrame.origin.y -= bookmarks::kBookmarkBarHeight;
+ BookmarkButton* newButton = [self makeButtonForNode:node
+ frame:buttonFrame];
+ [buttons_ insertObject:newButton atIndex:buttonIndex];
+ [mainView_ addSubview:newButton];
+
+ // Close any child folder(s) which may still be open.
+ [self closeBookmarkFolder:self];
+
+ // Update all button widths in case more width is needed.
+ CGFloat windowWidth =
+ [self adjustButtonWidths] + (2 * bookmarks::kBookmarkVerticalPadding) +
+ kScrollViewContentWidthMargin;
+
+ // Update vertical metrics of the window and the main view (using sizers
+ // does not do what we want).
+ NSWindow* window = [self window];
+ NSRect frame = [mainView_ frame];
+ const CGFloat verticalDelta =
+ bookmarks::kBookmarkBarHeight - bookmarks::kBookmarkVerticalPadding;
+ frame.size.height += verticalDelta;
+ frame.size.width = windowWidth;
+ [mainView_ setFrame:frame];
+ frame = [window frame];
+ frame.origin.y -= bookmarks::kBookmarkBarHeight;
+ frame.size.height += bookmarks::kBookmarkBarHeight;
+ frame.size.width = windowWidth;
+ [window setFrame:frame display:YES];
+}
+
+- (void)moveButtonFromIndex:(NSInteger)fromIndex toIndex:(NSInteger)toIndex {
+ if (fromIndex != toIndex) {
+ if (toIndex == -1)
+ toIndex = [buttons_ count];
+ BookmarkButton* movedButton = [buttons_ objectAtIndex:fromIndex];
+ [buttons_ removeObjectAtIndex:fromIndex];
+ NSRect movedFrame = [movedButton frame];
+ NSPoint toOrigin = movedFrame.origin;
+ [movedButton setHidden:YES];
+ if (fromIndex < toIndex) {
+ BookmarkButton* targetButton = [buttons_ objectAtIndex:toIndex - 1];
+ toOrigin = [targetButton frame].origin;
+ for (NSInteger i = fromIndex; i < toIndex; ++i) {
+ BookmarkButton* button = [buttons_ objectAtIndex:i];
+ NSRect frame = [button frame];
+ frame.origin.y += bookmarks::kBookmarkBarHeight;
+ [button setFrameOrigin:frame.origin];
+ }
+ } else {
+ BookmarkButton* targetButton = [buttons_ objectAtIndex:toIndex];
+ toOrigin = [targetButton frame].origin;
+ for (NSInteger i = fromIndex - 1; i >= toIndex; --i) {
+ BookmarkButton* button = [buttons_ objectAtIndex:i];
+ NSRect buttonFrame = [button frame];
+ buttonFrame.origin.y -= bookmarks::kBookmarkBarHeight;
+ [button setFrameOrigin:buttonFrame.origin];
+ }
+ }
+ [buttons_ insertObject:movedButton atIndex:toIndex];
+ [movedButton setFrameOrigin:toOrigin];
+ [movedButton setHidden:NO];
+ }
+}
+
+// TODO(jrg): Refactor BookmarkBarFolder common code. http://crbug.com/35966
+- (void)removeButton:(NSInteger)buttonIndex animate:(BOOL)animate {
+ // TODO(mrossetti): Get disappearing animation to work. http://crbug.com/42360
+ NSWindow* window = [self window];
+ BookmarkButton* oldButton = [buttons_ objectAtIndex:buttonIndex];
+ NSRect poofFrame = [oldButton bounds];
+ NSPoint poofPoint = NSMakePoint(NSMidX(poofFrame), NSMidY(poofFrame));
+ poofPoint = [oldButton convertPoint:poofPoint toView:nil];
+ poofPoint = [[oldButton window] convertBaseToScreen:poofPoint];
+ [oldButton removeFromSuperview];
+ if (animate)
+ NSShowAnimationEffect(NSAnimationEffectDisappearingItemDefault, poofPoint,
+ NSZeroSize, nil, nil, nil);
+ [buttons_ removeObjectAtIndex:buttonIndex];
+ for (NSInteger i = 0; i < buttonIndex; ++i) {
+ BookmarkButton* button = [buttons_ objectAtIndex:i];
+ NSRect buttonFrame = [button frame];
+ buttonFrame.origin.y -= bookmarks::kBookmarkBarHeight;
+ [button setFrame:buttonFrame];
+ }
+ // Search for and adjust submenus, if necessary.
+ NSInteger buttonCount = [buttons_ count];
+ BookmarkButton* subButton = [folderController_ parentButton];
+ for (NSInteger i = buttonIndex; i < buttonCount; ++i) {
+ BookmarkButton* aButton = [buttons_ objectAtIndex:i];
+ // If this button is showing its menu then we need to move the menu, too.
+ if (aButton == subButton)
+ [folderController_ offsetFolderMenuWindow:NSMakeSize(0.0,
+ bookmarks::kBookmarkBarHeight)];
+ }
+
+ // Resize the window and the main view (using sizers does not work).
+ NSRect frame = [window frame];
+ frame.origin.y += bookmarks::kBookmarkBarHeight;
+ frame.size.height -= bookmarks::kBookmarkBarHeight;
+ [window setFrame:frame display:YES];
+ frame = [mainView_ frame];
+ frame.origin.y += bookmarks::kBookmarkBarHeight;
+ frame.size.height -= bookmarks::kBookmarkBarHeight;
+ [mainView_ setFrame:frame];
+}
+
+- (id<BookmarkButtonControllerProtocol>)controllerForNode:
+ (const BookmarkNode*)node {
+ // See if we are holding this node, otherwise see if it is in our
+ // hierarchy of visible folder menus.
+ if ([parentButton_ bookmarkNode] == node)
+ return self;
+ return [folderController_ controllerForNode:node];
+}
+
// Start a "scroll up" timer.
- (void)beginScrollWindowUp {
[self addScrollTimerWithDelta:kBookmarkBarFolderScrollAmount];
@@ -381,10 +550,12 @@ const CGFloat kScrollWindowVerticalMargin = 0.0;
// view that contains the button. It appears that a mouseExited:
// gets lost, so the button stays highlit forever. We accomodate
// here.
+#if 0
if (buttonThatMouseIsIn_) {
[[buttonThatMouseIsIn_ cell] setShowsBorderOnlyWhileMouseInside:NO];
[[buttonThatMouseIsIn_ cell] setShowsBorderOnlyWhileMouseInside:YES];
}
+#endif
// We update the window size after shifting the scroll to avoid a race.
CGFloat screenHeightMinusMargin = (NSHeight(screenFrame) -
@@ -540,6 +711,9 @@ const CGFloat kScrollWindowVerticalMargin = 0.0;
- (void)fillPasteboard:(NSPasteboard*)pboard
forDragOfButton:(BookmarkButton*)button {
[[self folderTarget] fillPasteboard:pboard forDragOfButton:button];
+
+ // Close our folder menu and submenus since we know we're going to be dragged.
+ [self closeBookmarkFolder:self];
}
// Find something like std::is_between<T>? I can't believe one doesn't exist.
@@ -588,6 +762,7 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) {
return nil;
}
+// TODO(jrg): Refactor BookmarkBarFolder common code. http://crbug.com/35966
// Most of the work (e.g. drop indicator) is taken care of in the
// folder_view. Here we handle hover open issues for subfolders.
// Caution: there are subtle differences between this one and
@@ -596,6 +771,19 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) {
NSPoint currentLocation = [info draggingLocation];
BookmarkButton* button = [self buttonForDroppingOnAtPoint:currentLocation];
+ // Don't allow drops that would result in cycles.
+ if (button) {
+ NSData* data = [[info draggingPasteboard]
+ dataForType:kBookmarkButtonDragType];
+ if (data && [info draggingSource]) {
+ BookmarkButton* sourceButton = nil;
+ [data getBytes:&sourceButton length:sizeof(sourceButton)];
+ const BookmarkNode* sourceNode = [sourceButton bookmarkNode];
+ const BookmarkNode* destNode = [button bookmarkNode];
+ if (destNode->HasAncestor(sourceNode))
+ button = nil;
+ }
+ }
// Delegate handling of dragging over a button to the |hoverState_| member.
return [hoverState_ draggingEnteredButton:button];
}
@@ -698,17 +886,19 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) {
destIndex += [[parentButton_ cell] startingChildIndex];
}
- if (copy)
- [self bookmarkModel]->Copy(sourceNode, destParent, destIndex);
- else
- [self bookmarkModel]->Move(sourceNode, destParent, destIndex);
-
- [self closeAllBookmarkFolders]; // For a hover open, if needed.
-
- // Movement of a node triggers observers (like us) to rebuild the
- // bar so we don't have to do so explicitly.
+ // Prevent cycles.
+ BOOL wasCopiedOrMoved = NO;
+ if (!destParent->HasAncestor(sourceNode)) {
+ if (copy)
+ [self bookmarkModel]->Copy(sourceNode, destParent, destIndex);
+ else
+ [self bookmarkModel]->Move(sourceNode, destParent, destIndex);
+ wasCopiedOrMoved = YES;
+ // Movement of a node triggers observers (like us) to rebuild the
+ // bar so we don't have to do so explicitly.
+ }
- return YES;
+ return wasCopiedOrMoved;
}
// Return YES if we should show the drop indicator, else NO.
@@ -844,14 +1034,22 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) {
}
}
+- (void)reconfigureMenu {
+ for (BookmarkButton* button in buttons_.get())
+ [button removeFromSuperview];
+ [buttons_ removeAllObjects];
+ [self configureWindow];
+}
+
#pragma mark Methods Forwarded to BookmarkBarController
- (IBAction)cutBookmark:(id)sender {
+ [self closeBookmarkFolder:self];
[barController_ cutBookmark:sender];
}
- (IBAction)copyBookmark:(id)sender {
- [barController_ cutBookmark:sender];
+ [barController_ copyBookmark:sender];
}
- (IBAction)pasteBookmark:(id)sender {
@@ -859,6 +1057,7 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) {
}
- (IBAction)deleteBookmark:(id)sender {
+ [self closeBookmarkFolder:self];
[barController_ deleteBookmark:sender];
}