BookmarkManager: Fix 'new folder text field size changes on clicking it' issue.
[chromium-blink-merge.git] / chrome / browser / ui / cocoa / bookmarks / bookmark_bar_folder_hover_state.mm
blob278bfd923a39c9a9135ad1ee4df2e3dc74ed9ce0
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"
7 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_controller.h"
9 @interface BookmarkBarFolderHoverState(Private)
10 - (void)setHoverState:(HoverState)state;
11 - (void)closeBookmarkFolderOnHoverButton:(BookmarkButton*)button;
12 - (void)openBookmarkFolderOnHoverButton:(BookmarkButton*)button;
13 @end
15 @implementation BookmarkBarFolderHoverState
17 - (id)init {
18   if ((self = [super init])) {
19     hoverState_ = kHoverStateClosed;
20   }
21   return self;
24 - (NSDragOperation)draggingEnteredButton:(BookmarkButton*)button {
25   if ([button isFolder]) {
26     if (hoverButton_ == button) {
27       // CASE A: hoverButton_ == button implies we've dragged over
28       // the same folder so no need to open or close anything new.
29     } else if (hoverButton_ &&
30                hoverButton_ != button) {
31       // CASE B: we have a hoverButton_ but it is different from the new button.
32       // This implies we've dragged over a new folder, so we'll close the old
33       // and open the new.
34       // Note that we only schedule the open or close if we have no other tasks
35       // currently pending.
37       // Since the new bookmark folder is not opened until the previous is
38       // closed, the NSDraggingDestination must provide continuous callbacks,
39       // even if the cursor isn't moving.
40       if (hoverState_ == kHoverStateOpen) {
41         // Close the old.
42         [self scheduleCloseBookmarkFolderOnHoverButton];
43       } else if (hoverState_ == kHoverStateClosed) {
44         // Open the new.
45         [self scheduleOpenBookmarkFolderOnHoverButton:button];
46       }
47     } else if (!hoverButton_) {
48       // CASE C: we don't have a current hoverButton_ but we have dragged onto
49       // a new folder so we open the new one.
50       [self scheduleOpenBookmarkFolderOnHoverButton:button];
51     }
52   } else if (!button) {
53     if (hoverButton_) {
54       // CASE D: We have a hoverButton_ but we've moved onto an area that
55       // requires no hover.  We close the hoverButton_ in this case.  This
56       // means cancelling if the open is pending (i.e. |kHoverStateOpening|)
57       // or closing if we don't alrealy have once in progress.
59       // Intiate close only if we have not already done so.
60       if (hoverState_ == kHoverStateOpening) {
61         // Cancel the pending open.
62         [self cancelPendingOpenBookmarkFolderOnHoverButton];
63       } else if (hoverState_ != kHoverStateClosing) {
64         // Schedule the close.
65         [self scheduleCloseBookmarkFolderOnHoverButton];
66       }
67     } else {
68       // CASE E: We have neither a hoverButton_ nor a new button that requires
69       // a hover.  In this case we do nothing.
70     }
71   }
73   return NSDragOperationMove;
76 - (void)draggingExited {
77   if (hoverButton_) {
78     if (hoverState_ == kHoverStateOpening) {
79       [self cancelPendingOpenBookmarkFolderOnHoverButton];
80     } else if (hoverState_ == kHoverStateClosing) {
81       [self cancelPendingCloseBookmarkFolderOnHoverButton];
82     }
83   }
86 // Schedule close of hover button.  Transition to kHoverStateClosing state.
87 - (void)scheduleCloseBookmarkFolderOnHoverButton {
88   DCHECK(hoverButton_);
89   [self setHoverState:kHoverStateClosing];
90   [self performSelector:@selector(closeBookmarkFolderOnHoverButton:)
91              withObject:hoverButton_
92              afterDelay:bookmarks::kDragHoverCloseDelay
93                 inModes:[NSArray arrayWithObject:NSRunLoopCommonModes]];
96 // Cancel pending hover close.  Transition to kHoverStateOpen state.
97 - (void)cancelPendingCloseBookmarkFolderOnHoverButton {
98   [self setHoverState:kHoverStateOpen];
99   [NSObject
100       cancelPreviousPerformRequestsWithTarget:self
101       selector:@selector(closeBookmarkFolderOnHoverButton:)
102       object:hoverButton_];
105 // Schedule open of hover button.  Transition to kHoverStateOpening state.
106 - (void)scheduleOpenBookmarkFolderOnHoverButton:(BookmarkButton*)button {
107   DCHECK(button);
108   hoverButton_.reset([button retain]);
109   [self setHoverState:kHoverStateOpening];
110   [self performSelector:@selector(openBookmarkFolderOnHoverButton:)
111              withObject:hoverButton_
112              afterDelay:bookmarks::kDragHoverOpenDelay
113                 inModes:[NSArray arrayWithObject:NSRunLoopCommonModes]];
116 // Cancel pending hover open.  Transition to kHoverStateClosed state.
117 - (void)cancelPendingOpenBookmarkFolderOnHoverButton {
118   [self setHoverState:kHoverStateClosed];
119   [NSObject
120       cancelPreviousPerformRequestsWithTarget:self
121       selector:@selector(openBookmarkFolderOnHoverButton:)
122       object:hoverButton_];
123   hoverButton_.reset();
126 // Hover button accessor.  For testing only.
127 - (BookmarkButton*)hoverButton {
128   return hoverButton_;
131 // Hover state accessor.  For testing only.
132 - (HoverState)hoverState {
133   return hoverState_;
136 // This method encodes the rules of our |hoverButton_| state machine.  Only
137 // specific state transitions are allowable (encoded in the DCHECK).
138 // Note that there is no state for simultaneously opening and closing.  A
139 // pending open must complete before scheduling a close, and vice versa.  And
140 // it is not possible to make a transition directly from open to closed, and
141 // vice versa.
142 - (void)setHoverState:(HoverState)state {
143   DCHECK(
144     (hoverState_ == kHoverStateClosed && state == kHoverStateOpening) ||
145     (hoverState_ == kHoverStateOpening && state == kHoverStateClosed) ||
146     (hoverState_ == kHoverStateOpening && state == kHoverStateOpen) ||
147     (hoverState_ == kHoverStateOpen && state == kHoverStateClosing) ||
148     (hoverState_ == kHoverStateClosing && state == kHoverStateOpen) ||
149     (hoverState_ == kHoverStateClosing && state == kHoverStateClosed)
150   ) << "bad transition: old = " << hoverState_ << " new = " << state;
152   hoverState_ = state;
155 // Called after a delay to close a previously hover-opened folder.
156 // Note: this method is not meant to be invoked directly, only through
157 // a delayed call to |scheduleCloseBookmarkFolderOnHoverButton:|.
158 - (void)closeBookmarkFolderOnHoverButton:(BookmarkButton*)button {
159   [NSObject
160       cancelPreviousPerformRequestsWithTarget:self
161       selector:@selector(closeBookmarkFolderOnHoverButton:)
162       object:hoverButton_];
163   [self setHoverState:kHoverStateClosed];
164   [[button target] closeBookmarkFolder:button];
165   hoverButton_.reset();
168 // Called after a delay to open a new hover folder.
169 // Note: this method is not meant to be invoked directly, only through
170 // a delayed call to |scheduleOpenBookmarkFolderOnHoverButton:|.
171 - (void)openBookmarkFolderOnHoverButton:(BookmarkButton*)button {
172   [self setHoverState:kHoverStateOpen];
173   [[button target] performSelector:@selector(openBookmarkFolderFromButton:)
174       withObject:button];
177 @end