1 // Copyright (c) 2012 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 #ifndef CHROME_BROWSER_UI_COCOA_BOOKMARKS_BOOKMARK_BAR_CONTROLLER_H_
6 #define CHROME_BROWSER_UI_COCOA_BOOKMARKS_BOOKMARK_BAR_CONTROLLER_H_
8 #import <Cocoa/Cocoa.h>
11 #import "base/mac/cocoa_protocols.h"
12 #include "base/mac/scoped_nsobject.h"
13 #include "base/memory/scoped_ptr.h"
14 #include "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_bridge.h"
15 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_constants.h"
16 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_state.h"
17 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_toolbar_view.h"
18 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_button.h"
19 #import "chrome/browser/ui/cocoa/has_weak_browser_pointer.h"
20 #include "chrome/browser/ui/cocoa/tabs/tab_strip_model_observer_bridge.h"
21 #include "ui/base/window_open_disposition.h"
23 @
class BookmarkBarController
;
24 @
class BookmarkBarFolderController
;
25 @
class BookmarkBarView
;
26 @
class BookmarkButtonCell
;
27 @
class BookmarkContextMenuCocoaController
;
28 @
class BookmarkFolderTarget
;
29 class BookmarkModelObserverForCocoa
;
40 class ManagedBookmarkService
;
42 // Magic numbers from Cole
43 // TODO(jrg): create an objc-friendly version of bookmark_bar_constants.h?
45 // Used as a maximum width for buttons on the bar.
46 const CGFloat kDefaultBookmarkWidth
= 150.0;
48 // Horizontal frame inset for buttons in the bookmark bar.
49 const CGFloat kBookmarkHorizontalPadding
= 1.0;
51 // Vertical frame inset for buttons in the bookmark bar.
52 const CGFloat kBookmarkVerticalPadding
= 2.0;
54 // Left margin before the first button in the bookmark bar.
55 const CGFloat kBookmarkLeftMargin
= 2.0;
57 // Right margin before the last button in the bookmark bar.
58 const CGFloat kBookmarkRightMargin
= 2.0;
60 // Used as a min/max width for buttons on menus (not on the bar).
61 const CGFloat kBookmarkMenuButtonMinimumWidth
= 100.0;
62 const CGFloat kBookmarkMenuButtonMaximumWidth
= 485.0;
64 // The minimum separation between a folder menu and the edge of the screen.
65 // If the menu gets closer to the edge of the screen (either right or left)
66 // then it is pops up in the opposite direction.
67 // (See -[BookmarkBarFolderController childFolderWindowLeftForWidth:]).
68 const CGFloat kBookmarkHorizontalScreenPadding
= 8.0;
70 // Our NSScrollView is supposed to be just barely big enough to fit its
71 // contentView. It is actually a hair too small.
72 // This turns on horizontal scrolling which, although slight, is awkward.
73 // Make sure our window (and NSScrollView) are wider than its documentView
74 // by at least this much.
75 const CGFloat kScrollViewContentWidthMargin
= 2;
77 // Make subfolder menus overlap their parent menu a bit to give a better
78 // perception of a menuing system.
79 const CGFloat kBookmarkMenuOverlap
= 2.0;
81 // When constraining a scrolling bookmark bar folder window to the
82 // screen, shrink the "constrain" by this much vertically. Currently
83 // this is 0.0 to avoid a problem with tracking areas leaving the
84 // window, but should probably be 8.0 or something.
85 const CGFloat kScrollWindowVerticalMargin
= 6.0;
87 // How far to offset a folder menu from the top of the bookmark bar. This
88 // is set just above the bar so that it become distinctive when drawn.
89 const CGFloat kBookmarkBarMenuOffset
= 2.0;
91 // How far to offset a folder menu's left edge horizontally in relation to
92 // the left edge of the button from which it springs. Because of drawing
93 // differences, simply aligning the |frame| of each does not render the
94 // pproper result, so we have to offset.
95 const CGFloat kBookmarkBarButtonOffset
= 2.0;
97 // Delay before opening a subfolder (and closing the previous one)
98 // when hovering over a folder button.
99 const NSTimeInterval kHoverOpenDelay
= 0.3;
101 // Delay on hover before a submenu opens when dragging.
102 // Experimentally a drag hover open delay needs to be bigger than a
103 // normal (non-drag) menu hover open such as used in the bookmark folder.
104 // TODO(jrg): confirm feel of this constant with ui-team.
105 // http://crbug.com/36276
106 const NSTimeInterval kDragHoverOpenDelay
= 0.7;
108 // Notes on use of kDragHoverCloseDelay in
109 // -[BookmarkBarFolderController draggingEntered:].
111 // We have an implicit delay on stop-hover-open before a submenu
112 // closes. This cannot be zero since it's nice to move the mouse in a
113 // direct line from "current position" to "position of item in
114 // submenu". However, by doing so, it's possible to overlap a
115 // different button on the current menu. Example:
122 // If you hover over the F in Folder2 to open the sub, and then want to
123 // select Sub3, a direct line movement of the mouse may cross over
124 // Folder3. Without this delay, that'll cause Sub to be closed before
125 // you get there, since a "hover over" of Folder3 gets activated.
126 // It's subtle but without the delay it feels broken.
128 // This is only really a problem with vertical menu --> vertical menu
129 // movement; the bookmark bar (horizontal menu, sort of) seems fine,
130 // perhaps because mouse move direction is purely vertical so there is
131 // no opportunity for overlap.
132 const NSTimeInterval kDragHoverCloseDelay
= 0.4;
134 } // namespace bookmarks
136 // The interface for the bookmark bar controller's delegate. Currently, the
137 // delegate is the BWC and is responsible for ensuring that the toolbar is
138 // displayed correctly (as specified by |-getDesiredToolbarHeightCompression|
139 // and |-toolbarDividerOpacity|) at the beginning and at the end of an animation
140 // (or after a state change).
141 @protocol BookmarkBarControllerDelegate
143 // Sent when the state has changed (after any animation), but before the final
145 - (void)bookmarkBar
:(BookmarkBarController
*)controller
146 didChangeFromState
:(BookmarkBar::State
)oldState
147 toState
:(BookmarkBar::State
)newState
;
149 // Sent before the animation begins.
150 - (void)bookmarkBar
:(BookmarkBarController
*)controller
151 willAnimateFromState
:(BookmarkBar::State
)oldState
152 toState
:(BookmarkBar::State
)newState
;
156 // A controller for the bookmark bar in the browser window. Handles showing
157 // and hiding based on the preference in the given profile.
158 @interface BookmarkBarController
159 : NSViewController
<BookmarkBarState
,
160 BookmarkBarToolbarViewController
,
161 BookmarkButtonDelegate
,
162 BookmarkButtonControllerProtocol
,
163 NSDraggingDestination
,
164 HasWeakBrowserPointer
> {
166 // The state of the bookmark bar. If an animation is running, this is set to
167 // the "destination" and |lastState_| is set to the "original" state.
168 BookmarkBar::State currentState_
;
170 // The "original" state of the bookmark bar if an animation is running.
171 BookmarkBar::State lastState_
;
173 // YES if an animation is running.
174 BOOL isAnimationRunning_
;
176 Browser
* browser_
; // weak; owned by its window
177 bookmarks::BookmarkModel
* bookmarkModel_
; // weak; part of the profile owned
178 // by the top-level Browser object.
179 bookmarks::ManagedBookmarkService
* managedBookmarkService_
;
181 // Our initial view width, which is applied in awakeFromNib.
182 CGFloat initialWidth_
;
184 // BookmarkNodes have a 64bit id. NSMenuItems have a 32bit tag used
185 // to represent the bookmark node they refer to. This map provides
186 // a mapping from one to the other, so we can properly identify the
187 // node from the item. When adding items in, we start with seedId_.
189 std::map
<int32
,int64
> menuTagMap_
;
191 // Our bookmark buttons, ordered from L-->R.
192 base::scoped_nsobject
<NSMutableArray
> buttons_
;
194 // The folder image so we can use one copy for all buttons
195 base::scoped_nsobject
<NSImage
> folderImage_
;
197 // The default image, so we can use one copy for all buttons.
198 base::scoped_nsobject
<NSImage
> defaultImage_
;
200 // If the bar is disabled, we hide it and ignore show/hide commands.
201 // Set when using fullscreen mode.
204 // Bridge from Chrome-style C++ notifications (e.g. derived from
205 // BookmarkModelObserver)
206 scoped_ptr
<BookmarkBarBridge
> bridge_
;
208 // Delegate that is informed about state changes in the bookmark bar.
209 id
<BookmarkBarControllerDelegate
> delegate_
; // weak
211 // Delegate that can resize us.
212 id
<ViewResizer
> resizeDelegate_
; // weak
214 // Logic for dealing with a click on a bookmark folder button.
215 base::scoped_nsobject
<BookmarkFolderTarget
> folderTarget_
;
217 // A controller for a pop-up bookmark folder window (custom menu).
218 // This is not a scoped_nsobject because it owns itself (when its
219 // window closes the controller gets autoreleased).
220 BookmarkBarFolderController
* folderController_
;
222 // The event tap that allows monitoring of all events, to properly close with
223 // a click outside the bounds of the window.
226 IBOutlet BookmarkBarView
* buttonView_
; // Contains 'no items' text fields.
227 IBOutlet BookmarkButton
* offTheSideButton_
; // aka the chevron.
229 NSRect originalNoItemsRect_
; // Original, pre-resized field rect.
230 NSRect originalImportBookmarksRect_
; // Original, pre-resized field rect.
232 // "Apps" button on the left side.
233 base::scoped_nsobject
<BookmarkButton
> appsPageShortcutButton_
;
235 // "Managed bookmarks" button on the left side, next to the apps button.
236 base::scoped_nsobject
<BookmarkButton
> managedBookmarksButton_
;
238 // "Supervised bookmarks" button on the left side, next to the apps button.
239 base::scoped_nsobject
<BookmarkButton
> supervisedBookmarksButton_
;
241 // "Other bookmarks" button on the right side.
242 base::scoped_nsobject
<BookmarkButton
> otherBookmarksButton_
;
244 // When doing a drag, this is folder button "hovered over" which we
245 // may want to open after a short delay. There are cases where a
246 // mouse-enter can open a folder (e.g. if the menus are "active")
247 // but that doesn't use this variable or need a delay so "hover" is
249 base::scoped_nsobject
<BookmarkButton
> hoverButton_
;
251 // We save the view width when we add bookmark buttons. This lets
252 // us avoid a rebuild until we've grown the window bigger than our
254 CGFloat savedFrameWidth_
;
256 // The number of buttons we display in the bookmark bar. This does
257 // not include the "off the side" chevron or the "Other Bookmarks"
258 // button. We use this number to determine if we need to display
259 // the chevron, and to know what to place in the chevron's menu.
260 // Since we create everything before doing layout we can't be sure
261 // that all bookmark buttons we create will be visible. Thus,
262 // [buttons_ count] isn't a definitive check.
263 int displayedButtonCount_
;
265 // A state flag which tracks when the bar's folder menus should be shown.
266 // An initial click in any of the folder buttons turns this on and
267 // one of the following will turn it off: another click in the button,
268 // the window losing focus, a click somewhere other than in the bar
270 BOOL showFolderMenus_
;
272 // If YES then state changes (for example, from hidden to shown) are animated.
273 // This is turned off for unit tests.
274 BOOL stateAnimationsEnabled_
;
276 // If YES then changes inside the bookmark bar (for example, removing a
277 // bookmark) are animated. This is turned off for unit tests.
278 BOOL innerContentAnimationsEnabled_
;
280 // YES if there is a possible drop about to happen in the bar.
281 BOOL hasInsertionPos_
;
283 // The x point on the bar where the left edge of the new item will end
284 // up if it is dropped.
285 CGFloat insertionPos_
;
287 // Controller responsible for all bookmark context menus.
288 base::scoped_nsobject
<BookmarkContextMenuCocoaController
>
289 contextMenuController_
;
291 // Weak pointer to the pulsed button for the currently pulsing node. We need
292 // to store this as it may not be possible to determine the pulsing button if
293 // the pulsing node is deleted. Nil if there is no pulsing node.
294 BookmarkButton
* pulsingButton_
;
296 // Specifically watch the currently pulsing node. This lets us stop pulsing
297 // when anything happens to the node. Null if there is no pulsing node.
298 scoped_ptr
<BookmarkModelObserverForCocoa
> pulsingBookmarkObserver_
;
301 @
property(readonly
, nonatomic
) BookmarkBar::State currentState
;
302 @
property(readonly
, nonatomic
) BookmarkBar::State lastState
;
303 @
property(readonly
, nonatomic
) BOOL isAnimationRunning
;
304 @
property(assign
, nonatomic
) id
<BookmarkBarControllerDelegate
> delegate
;
305 @
property(assign
, nonatomic
) BOOL stateAnimationsEnabled
;
306 @
property(assign
, nonatomic
) BOOL innerContentAnimationsEnabled
;
308 // Initializes the bookmark bar controller with the given browser
309 // profile and delegates.
310 - (id
)initWithBrowser
:(Browser
*)browser
311 initialWidth
:(CGFloat
)initialWidth
312 delegate
:(id
<BookmarkBarControllerDelegate
>)delegate
313 resizeDelegate
:(id
<ViewResizer
>)resizeDelegate
;
315 // The Browser corresponding to this BookmarkBarController.
318 // The controller for all bookmark bar context menus.
319 - (BookmarkContextMenuCocoaController
*)menuController
;
321 // Pulses the given bookmark node, or the closest parent node that is visible.
322 - (void)startPulsingBookmarkNode
:(const bookmarks::BookmarkNode
*)node
;
324 // Stops pulsing any bookmark nodes.
325 - (void)stopPulsingBookmarkNode
;
327 // Updates the bookmark bar (from its current, possibly in-transition) state to
329 - (void)updateState
:(BookmarkBar::State
)newState
330 changeType
:(BookmarkBar::AnimateChangeType
)changeType
;
332 // Update the visible state of the bookmark bar.
333 - (void)updateVisibility
;
335 // Update the visible state of the extra buttons on the bookmark bar: the
336 // apps shortcut, the managed bookmarks folder, and the supervised bookmarks
338 - (void)updateExtraButtonsVisibility
;
340 // Hides or shows the bookmark bar depending on the current state.
341 - (void)updateHiddenState
;
343 // Turn on or off the bookmark bar and prevent or reallow its appearance. On
344 // disable, toggle off if shown. On enable, show only if needed. App and popup
345 // windows do not show a bookmark bar.
346 - (void)setBookmarkBarEnabled
:(BOOL
)enabled
;
348 // Returns the amount by which the toolbar above should be compressed.
349 - (CGFloat
)getDesiredToolbarHeightCompression
;
351 // Gets the appropriate opacity for the toolbar's divider; 0 means that it
352 // shouldn't be shown.
353 - (CGFloat
)toolbarDividerOpacity
;
355 // Updates the sizes and positions of the subviews.
356 // TODO(viettrungluu): I'm not convinced this should be public, but I currently
357 // need it for animations. Try not to propagate its use.
358 - (void)layoutSubviews
;
360 // Called by our view when it is moved to a window.
361 - (void)viewDidMoveToWindow
;
363 // Provide a favicon for a bookmark node. May return nil.
364 - (NSImage
*)faviconForNode
:(const bookmarks::BookmarkNode
*)node
;
366 // Used for situations where the bookmark bar folder menus should no longer
367 // be actively popping up. Called when the window loses focus, a click has
368 // occured outside the menus or a bookmark has been activated. (Note that this
369 // differs from the behavior of the -[BookmarkButtonControllerProtocol
370 // closeAllBookmarkFolders] method in that the latter does not terminate menu
371 // tracking since it may be being called in response to actions (such as
372 // dragging) where a 'stale' menu presentation should first be collapsed before
373 // presenting a new menu.)
374 - (void)closeFolderAndStopTrackingMenus
;
376 // Checks if operations such as edit or delete are allowed.
377 - (BOOL
)canEditBookmark
:(const bookmarks::BookmarkNode
*)node
;
379 // Checks if bookmark editing is enabled at all.
380 - (BOOL
)canEditBookmarks
;
382 // Actions for manipulating bookmarks.
383 // Open a normal bookmark or folder from a button, ...
384 - (IBAction
)openBookmark
:(id
)sender
;
385 - (IBAction
)openBookmarkFolderFromButton
:(id
)sender
;
386 // From the "off the side" button, ...
387 - (IBAction
)openOffTheSideFolderFromButton
:(id
)sender
;
388 // Import bookmarks from another browser.
389 - (IBAction
)importBookmarks
:(id
)sender
;
392 // Redirects from BookmarkBarBridge, the C++ object which glues us to
393 // the rest of Chromium. Internal to BookmarkBarController.
394 @interface
BookmarkBarController(BridgeRedirect
)
395 - (void)loaded
:(bookmarks::BookmarkModel
*)model
;
396 - (void)beingDeleted
:(bookmarks::BookmarkModel
*)model
;
397 - (void)nodeAdded
:(bookmarks::BookmarkModel
*)model
398 parent
:(const bookmarks::BookmarkNode
*)oldParent index
:(int)index
;
399 - (void)nodeChanged
:(bookmarks::BookmarkModel
*)model
400 node
:(const bookmarks::BookmarkNode
*)node
;
401 - (void)nodeMoved
:(bookmarks::BookmarkModel
*)model
402 oldParent
:(const bookmarks::BookmarkNode
*)oldParent
403 oldIndex
:(int)oldIndex
404 newParent
:(const bookmarks::BookmarkNode
*)newParent
405 newIndex
:(int)newIndex
;
406 - (void)nodeRemoved
:(bookmarks::BookmarkModel
*)model
407 parent
:(const bookmarks::BookmarkNode
*)oldParent
409 - (void)nodeFaviconLoaded
:(bookmarks::BookmarkModel
*)model
410 node
:(const bookmarks::BookmarkNode
*)node
;
411 - (void)nodeChildrenReordered
:(bookmarks::BookmarkModel
*)model
412 node
:(const bookmarks::BookmarkNode
*)node
;
415 // These APIs should only be used by unit tests (or used internally).
416 @interface
BookmarkBarController(InternalOrTestingAPI
)
417 - (void)openBookmarkFolder
:(id
)sender
;
418 - (void)openOrCloseBookmarkFolderForOffTheSideButton
;
419 - (BookmarkBarView
*)buttonView
;
420 - (NSMutableArray
*)buttons
;
421 - (NSButton
*)offTheSideButton
;
422 - (NSButton
*)appsPageShortcutButton
;
423 - (BOOL
)offTheSideButtonIsHidden
;
424 - (BOOL
)appsPageShortcutButtonIsHidden
;
425 - (BookmarkButton
*)otherBookmarksButton
;
426 - (BookmarkBarFolderController
*)folderController
;
428 - (int)displayedButtonCount
;
429 - (void)openURL
:(GURL
)url disposition
:(WindowOpenDisposition
)disposition
;
430 - (void)clearBookmarkBar
;
431 - (BookmarkButtonCell
*)cellForBookmarkNode
:(const bookmarks::BookmarkNode
*)node
;
432 - (BookmarkButtonCell
*)cellForCustomButtonWithText
:(NSString
*)text
433 image
:(NSImage
*)image
;
434 - (NSRect
)frameForBookmarkButtonFromCell
:(NSCell
*)cell xOffset
:(int*)xOffset
;
435 - (void)checkForBookmarkButtonGrowth
:(NSButton
*)button
;
436 - (void)frameDidChange
;
437 - (int64
)nodeIdFromMenuTag
:(int32
)tag
;
438 - (int32
)menuTagFromNodeId
:(int64
)menuid
;
439 - (void)updateTheme
:(ui::ThemeProvider
*)themeProvider
;
440 - (BookmarkButton
*)buttonForDroppingOnAtPoint
:(NSPoint
)point
;
441 - (BOOL
)isEventAnExitEvent
:(NSEvent
*)event
;
442 - (BOOL
)shrinkOrHideView
:(NSView
*)view forMaxX
:(CGFloat
)maxViewX
;
443 - (void)unhighlightBookmark
:(const bookmarks::BookmarkNode
*)node
;
445 // The following are for testing purposes only and are not used internally.
446 - (NSMenu
*)menuForFolderNode
:(const bookmarks::BookmarkNode
*)node
;
449 #endif // CHROME_BROWSER_UI_COCOA_BOOKMARKS_BOOKMARK_BAR_CONTROLLER_H_