blob: 5470084a14ea8d87d7c51e30cf88def87bb9df9b [file] [log] [blame]
// Copyright (c) 2011 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