Index: chrome/browser/ui/cocoa/bookmarks/bookmark_bar_folder_hover_state.mm |
diff --git a/chrome/browser/ui/cocoa/bookmarks/bookmark_bar_folder_hover_state.mm b/chrome/browser/ui/cocoa/bookmarks/bookmark_bar_folder_hover_state.mm |
new file mode 100644 |
index 0000000000000000000000000000000000000000..abd3f88c567a920568f8530288fa7aa2275f7d10 |
--- /dev/null |
+++ b/chrome/browser/ui/cocoa/bookmarks/bookmark_bar_folder_hover_state.mm |
@@ -0,0 +1,173 @@ |
+// Copyright (c) 2010 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_folder_hover_state.h" |
+#import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_controller.h" |
+ |
+@interface BookmarkBarFolderHoverState(Private) |
+- (void)setHoverState:(HoverState)state; |
+- (void)closeBookmarkFolderOnHoverButton:(BookmarkButton*)button; |
+- (void)openBookmarkFolderOnHoverButton:(BookmarkButton*)button; |
+@end |
+ |
+@implementation BookmarkBarFolderHoverState |
+ |
+- (id)init { |
+ if ((self = [super init])) { |
+ hoverState_ = kHoverStateClosed; |
+ } |
+ return self; |
+} |
+ |
+- (NSDragOperation)draggingEnteredButton:(BookmarkButton*)button { |
+ if ([button isFolder]) { |
+ if (hoverButton_ == button) { |
+ // CASE A: hoverButton_ == button implies we've dragged over |
+ // the same folder so no need to open or close anything new. |
+ } else if (hoverButton_ && |
+ hoverButton_ != button) { |
+ // CASE B: we have a hoverButton_ but it is different from the new button. |
+ // This implies we've dragged over a new folder, so we'll close the old |
+ // and open the new. |
+ // Note that we only schedule the open or close if we have no other tasks |
+ // currently pending. |
+ |
+ if (hoverState_ == kHoverStateOpen) { |
+ // Close the old. |
+ [self scheduleCloseBookmarkFolderOnHoverButton]; |
+ } else if (hoverState_ == kHoverStateClosed) { |
+ // Open the new. |
+ [self scheduleOpenBookmarkFolderOnHoverButton:button]; |
+ } |
+ } else if (!hoverButton_) { |
+ // CASE C: we don't have a current hoverButton_ but we have dragged onto |
+ // a new folder so we open the new one. |
+ [self scheduleOpenBookmarkFolderOnHoverButton:button]; |
+ } |
+ } else if (!button) { |
+ if (hoverButton_) { |
+ // CASE D: We have a hoverButton_ but we've moved onto an area that |
+ // requires no hover. We close the hoverButton_ in this case. This |
+ // means cancelling if the open is pending (i.e. |kHoverStateOpening|) |
+ // or closing if we don't alrealy have once in progress. |
+ |
+ // Intiate close only if we have not already done so. |
+ if (hoverState_ == kHoverStateOpening) { |
+ // Cancel the pending open. |
+ [self cancelPendingOpenBookmarkFolderOnHoverButton]; |
+ } else if (hoverState_ != kHoverStateClosing) { |
+ // Schedule the close. |
+ [self scheduleCloseBookmarkFolderOnHoverButton]; |
+ } |
+ } else { |
+ // CASE E: We have neither a hoverButton_ nor a new button that requires |
+ // a hover. In this case we do nothing. |
+ } |
+ } |
+ |
+ return NSDragOperationMove; |
+} |
+ |
+- (void)draggingExited { |
+ if (hoverButton_) { |
+ if (hoverState_ == kHoverStateOpening) { |
+ [self cancelPendingOpenBookmarkFolderOnHoverButton]; |
+ } else if (hoverState_ == kHoverStateClosing) { |
+ [self cancelPendingCloseBookmarkFolderOnHoverButton]; |
+ } |
+ } |
+} |
+ |
+// Schedule close of hover button. Transition to kHoverStateClosing state. |
+- (void)scheduleCloseBookmarkFolderOnHoverButton { |
+ DCHECK(hoverButton_); |
+ [self setHoverState:kHoverStateClosing]; |
+ [self performSelector:@selector(closeBookmarkFolderOnHoverButton:) |
+ withObject:hoverButton_ |
+ afterDelay:bookmarks::kDragHoverCloseDelay |
+ inModes:[NSArray arrayWithObject:NSRunLoopCommonModes]]; |
+} |
+ |
+// Cancel pending hover close. Transition to kHoverStateOpen state. |
+- (void)cancelPendingCloseBookmarkFolderOnHoverButton { |
+ [self setHoverState:kHoverStateOpen]; |
+ [NSObject |
+ cancelPreviousPerformRequestsWithTarget:self |
+ selector:@selector(closeBookmarkFolderOnHoverButton:) |
+ object:hoverButton_]; |
+} |
+ |
+// Schedule open of hover button. Transition to kHoverStateOpening state. |
+- (void)scheduleOpenBookmarkFolderOnHoverButton:(BookmarkButton*)button { |
+ DCHECK(button); |
+ hoverButton_.reset([button retain]); |
+ [self setHoverState:kHoverStateOpening]; |
+ [self performSelector:@selector(openBookmarkFolderOnHoverButton:) |
+ withObject:hoverButton_ |
+ afterDelay:bookmarks::kDragHoverOpenDelay |
+ inModes:[NSArray arrayWithObject:NSRunLoopCommonModes]]; |
+} |
+ |
+// Cancel pending hover open. Transition to kHoverStateClosed state. |
+- (void)cancelPendingOpenBookmarkFolderOnHoverButton { |
+ [self setHoverState:kHoverStateClosed]; |
+ [NSObject |
+ cancelPreviousPerformRequestsWithTarget:self |
+ selector:@selector(openBookmarkFolderOnHoverButton:) |
+ object:hoverButton_]; |
+ hoverButton_.reset(); |
+} |
+ |
+// Hover button accessor. For testing only. |
+- (BookmarkButton*)hoverButton { |
+ return hoverButton_; |
+} |
+ |
+// Hover state accessor. For testing only. |
+- (HoverState)hoverState { |
+ return hoverState_; |
+} |
+ |
+// This method encodes the rules of our |hoverButton_| state machine. Only |
+// specific state transitions are allowable (encoded in the DCHECK). |
+// Note that there is no state for simultaneously opening and closing. A |
+// pending open must complete before scheduling a close, and vice versa. And |
+// it is not possible to make a transition directly from open to closed, and |
+// vice versa. |
+- (void)setHoverState:(HoverState)state { |
+ DCHECK( |
+ (hoverState_ == kHoverStateClosed && state == kHoverStateOpening) || |
+ (hoverState_ == kHoverStateOpening && state == kHoverStateClosed) || |
+ (hoverState_ == kHoverStateOpening && state == kHoverStateOpen) || |
+ (hoverState_ == kHoverStateOpen && state == kHoverStateClosing) || |
+ (hoverState_ == kHoverStateClosing && state == kHoverStateOpen) || |
+ (hoverState_ == kHoverStateClosing && state == kHoverStateClosed) |
+ ) << "bad transition: old = " << hoverState_ << " new = " << state; |
+ |
+ hoverState_ = state; |
+} |
+ |
+// Called after a delay to close a previously hover-opened folder. |
+// Note: this method is not meant to be invoked directly, only through |
+// a delayed call to |scheduleCloseBookmarkFolderOnHoverButton:|. |
+- (void)closeBookmarkFolderOnHoverButton:(BookmarkButton*)button { |
+ [NSObject |
+ cancelPreviousPerformRequestsWithTarget:self |
+ selector:@selector(closeBookmarkFolderOnHoverButton:) |
+ object:hoverButton_]; |
+ [self setHoverState:kHoverStateClosed]; |
+ [[button target] closeBookmarkFolder:button]; |
+ hoverButton_.reset(); |
+} |
+ |
+// Called after a delay to open a new hover folder. |
+// Note: this method is not meant to be invoked directly, only through |
+// a delayed call to |scheduleOpenBookmarkFolderOnHoverButton:|. |
+- (void)openBookmarkFolderOnHoverButton:(BookmarkButton*)button { |
+ [self setHoverState:kHoverStateOpen]; |
+ [[button target] performSelector:@selector(openBookmarkFolderFromButton:) |
+ withObject:button]; |
+} |
+ |
+@end |