1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_folder_hover_state.h"
6 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_controller.h"
8 @interface BookmarkBarFolderHoverState(Private)
9 - (void)setHoverState:(HoverState)state;
10 - (void)closeBookmarkFolderOnHoverButton:(BookmarkButton*)button;
11 - (void)openBookmarkFolderOnHoverButton:(BookmarkButton*)button;
14 @implementation BookmarkBarFolderHoverState
17 if ((self = [super init])) {
18 hoverState_ = kHoverStateClosed;
23 - (NSDragOperation)draggingEnteredButton:(BookmarkButton*)button {
24 if ([button isFolder]) {
25 if (hoverButton_ == button) {
26 // CASE A: hoverButton_ == button implies we've dragged over
27 // the same folder so no need to open or close anything new.
28 } else if (hoverButton_ &&
29 hoverButton_ != button) {
30 // CASE B: we have a hoverButton_ but it is different from the new button.
31 // This implies we've dragged over a new folder, so we'll close the old
33 // Note that we only schedule the open or close if we have no other tasks
36 if (hoverState_ == kHoverStateOpen) {
38 [self scheduleCloseBookmarkFolderOnHoverButton];
39 } else if (hoverState_ == kHoverStateClosed) {
41 [self scheduleOpenBookmarkFolderOnHoverButton:button];
43 } else if (!hoverButton_) {
44 // CASE C: we don't have a current hoverButton_ but we have dragged onto
45 // a new folder so we open the new one.
46 [self scheduleOpenBookmarkFolderOnHoverButton:button];
50 // CASE D: We have a hoverButton_ but we've moved onto an area that
51 // requires no hover. We close the hoverButton_ in this case. This
52 // means cancelling if the open is pending (i.e. |kHoverStateOpening|)
53 // or closing if we don't alrealy have once in progress.
55 // Intiate close only if we have not already done so.
56 if (hoverState_ == kHoverStateOpening) {
57 // Cancel the pending open.
58 [self cancelPendingOpenBookmarkFolderOnHoverButton];
59 } else if (hoverState_ != kHoverStateClosing) {
60 // Schedule the close.
61 [self scheduleCloseBookmarkFolderOnHoverButton];
64 // CASE E: We have neither a hoverButton_ nor a new button that requires
65 // a hover. In this case we do nothing.
69 return NSDragOperationMove;
72 - (void)draggingExited {
74 if (hoverState_ == kHoverStateOpening) {
75 [self cancelPendingOpenBookmarkFolderOnHoverButton];
76 } else if (hoverState_ == kHoverStateClosing) {
77 [self cancelPendingCloseBookmarkFolderOnHoverButton];
82 // Schedule close of hover button. Transition to kHoverStateClosing state.
83 - (void)scheduleCloseBookmarkFolderOnHoverButton {
85 [self setHoverState:kHoverStateClosing];
86 [self performSelector:@selector(closeBookmarkFolderOnHoverButton:)
87 withObject:hoverButton_
88 afterDelay:bookmarks::kDragHoverCloseDelay
89 inModes:[NSArray arrayWithObject:NSRunLoopCommonModes]];
92 // Cancel pending hover close. Transition to kHoverStateOpen state.
93 - (void)cancelPendingCloseBookmarkFolderOnHoverButton {
94 [self setHoverState:kHoverStateOpen];
96 cancelPreviousPerformRequestsWithTarget:self
97 selector:@selector(closeBookmarkFolderOnHoverButton:)
101 // Schedule open of hover button. Transition to kHoverStateOpening state.
102 - (void)scheduleOpenBookmarkFolderOnHoverButton:(BookmarkButton*)button {
104 hoverButton_.reset([button retain]);
105 [self setHoverState:kHoverStateOpening];
106 [self performSelector:@selector(openBookmarkFolderOnHoverButton:)
107 withObject:hoverButton_
108 afterDelay:bookmarks::kDragHoverOpenDelay
109 inModes:[NSArray arrayWithObject:NSRunLoopCommonModes]];
112 // Cancel pending hover open. Transition to kHoverStateClosed state.
113 - (void)cancelPendingOpenBookmarkFolderOnHoverButton {
114 [self setHoverState:kHoverStateClosed];
116 cancelPreviousPerformRequestsWithTarget:self
117 selector:@selector(openBookmarkFolderOnHoverButton:)
118 object:hoverButton_];
119 hoverButton_.reset();
122 // Hover button accessor. For testing only.
123 - (BookmarkButton*)hoverButton {
127 // Hover state accessor. For testing only.
128 - (HoverState)hoverState {
132 // This method encodes the rules of our |hoverButton_| state machine. Only
133 // specific state transitions are allowable (encoded in the DCHECK).
134 // Note that there is no state for simultaneously opening and closing. A
135 // pending open must complete before scheduling a close, and vice versa. And
136 // it is not possible to make a transition directly from open to closed, and
138 - (void)setHoverState:(HoverState)state {
140 (hoverState_ == kHoverStateClosed && state == kHoverStateOpening) ||
141 (hoverState_ == kHoverStateOpening && state == kHoverStateClosed) ||
142 (hoverState_ == kHoverStateOpening && state == kHoverStateOpen) ||
143 (hoverState_ == kHoverStateOpen && state == kHoverStateClosing) ||
144 (hoverState_ == kHoverStateClosing && state == kHoverStateOpen) ||
145 (hoverState_ == kHoverStateClosing && state == kHoverStateClosed)
146 ) << "bad transition: old = " << hoverState_ << " new = " << state;
151 // Called after a delay to close a previously hover-opened folder.
152 // Note: this method is not meant to be invoked directly, only through
153 // a delayed call to |scheduleCloseBookmarkFolderOnHoverButton:|.
154 - (void)closeBookmarkFolderOnHoverButton:(BookmarkButton*)button {
156 cancelPreviousPerformRequestsWithTarget:self
157 selector:@selector(closeBookmarkFolderOnHoverButton:)
158 object:hoverButton_];
159 [self setHoverState:kHoverStateClosed];
160 [[button target] closeBookmarkFolder:button];
161 hoverButton_.reset();
164 // Called after a delay to open a new hover folder.
165 // Note: this method is not meant to be invoked directly, only through
166 // a delayed call to |scheduleOpenBookmarkFolderOnHoverButton:|.
167 - (void)openBookmarkFolderOnHoverButton:(BookmarkButton*)button {
168 [self setHoverState:kHoverStateOpen];
169 [[button target] performSelector:@selector(openBookmarkFolderFromButton:)