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