Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 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. | |
| 4 | |
| 5 #import <Cocoa/Cocoa.h> | |
| 6 | |
| 7 #include "base/logging.h" // for NOTREACHED() | |
| 8 #include "base/mac/mac_util.h" | |
| 9 #include "base/sys_string_conversions.h" | |
| 10 #include "chrome/app/chrome_command_ids.h" | |
| 11 #include "chrome/browser/ui/browser.h" | |
| 12 #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h" | |
| 13 #import "chrome/browser/ui/cocoa/animatable_view.h" | |
| 14 #import "chrome/browser/ui/cocoa/browser_window_controller.h" | |
| 15 #include "chrome/browser/ui/cocoa/event_utils.h" | |
| 16 #import "chrome/browser/ui/cocoa/fullscreen_exit_bubble_controller.h" | |
| 17 #include "third_party/GTM/AppKit/GTMUILocalizerAndLayoutTweaker.h" | |
| 18 #import "third_party/GTM/AppKit/GTMNSAnimation+Duration.h" | |
| 19 #include "ui/base/models/accelerator_cocoa.h" | |
| 20 #include "ui/base/l10n/l10n_util_mac.h" | |
| 21 #include "chrome/app/chrome_command_ids.h" | |
| 22 #include "grit/generated_resources.h" | |
| 23 | |
| 24 namespace { | |
| 25 // Durations set to match the default SlideAnimation duration. | |
| 26 const float kAnimateOpenDuration = 0.12; | |
| 27 const float kAnimateCloseDuration = 0.12; | |
| 28 | |
| 29 // The baseline shift for text in the NSTextView. | |
| 30 const float kTextBaselineShift = -1.0; | |
| 31 } | |
| 32 | |
| 33 // This simple subclass of |NSTextView| just doesn't show the (text) cursor | |
| 34 // (|NSTextView| displays the cursor with full keyboard accessibility enabled). | |
| 35 /*@interface InfoBarTextView : NSTextView | |
| 36 - (void)fixupCursor; | |
| 37 @end | |
| 38 | |
| 39 @implementation InfoBarTextView | |
| 40 | |
| 41 // Never draw the insertion point (otherwise, it shows up without any user | |
| 42 // action if full keyboard accessibility is enabled). | |
| 43 - (BOOL)shouldDrawInsertionPoint { | |
| 44 return NO; | |
| 45 } | |
| 46 | |
| 47 - (NSRange)selectionRangeForProposedRange:(NSRange)proposedSelRange | |
| 48 granularity:(NSSelectionGranularity)granularity { | |
| 49 // Do not allow selections. | |
| 50 return NSMakeRange(0, 0); | |
| 51 } | |
| 52 | |
| 53 // Convince NSTextView to not show an I-Beam cursor when the cursor is over the | |
| 54 // text view but not over actual text. | |
| 55 // | |
| 56 // http://www.mail-archive.com/cocoa-dev@lists.apple.com/msg10791.html | |
| 57 // "NSTextView sets the cursor over itself dynamically, based on considerations | |
| 58 // including the text under the cursor. It does so in -mouseEntered:, | |
| 59 // -mouseMoved:, and -cursorUpdate:, so those would be points to consider | |
| 60 // overriding." | |
| 61 - (void)mouseMoved:(NSEvent*)e { | |
| 62 [super mouseMoved:e]; | |
| 63 [self fixupCursor]; | |
| 64 } | |
| 65 | |
| 66 - (void)mouseEntered:(NSEvent*)e { | |
| 67 [super mouseEntered:e]; | |
| 68 [self fixupCursor]; | |
| 69 } | |
| 70 | |
| 71 - (void)cursorUpdate:(NSEvent*)e { | |
| 72 [super cursorUpdate:e]; | |
| 73 [self fixupCursor]; | |
| 74 } | |
| 75 | |
| 76 - (void)fixupCursor { | |
| 77 if ([[NSCursor currentCursor] isEqual:[NSCursor IBeamCursor]]) | |
| 78 [[NSCursor arrowCursor] set]; | |
| 79 } | |
| 80 | |
| 81 @end*/ | |
| 82 | |
| 83 @interface FullscreenExitBubbleController (PrivateMethods) | |
| 84 // Sets |exitLabel_| based on |exitLabelPlaceholder_|, sets |exitLabelPlaceholde r_| to nil. | |
| 85 - (void)initializeLabel; | |
| 86 | |
| 87 // Sets the info bar message to the specified |message|, with a hypertext | |
| 88 // style link. |link| will be inserted into message at |linkOffset|. | |
| 89 - (void)setLabelToMessage:(NSString*)message | |
| 90 withLink:(NSString*)link | |
| 91 atOffset:(NSUInteger)linkOffset; | |
| 92 | |
| 93 - (void)showButtons:(BOOL)shown; | |
| 94 - (void)hideSoon; | |
| 95 | |
| 96 // Returns the Accelerator for the Toggle Fullscreen menu item. | |
| 97 + (ui::AcceleratorCocoa)toggleFullscreenAccelerator; | |
| 98 | |
| 99 // Returns a string representation fit for display of |+toggleFullscreenAccelera tor|. | |
| 100 + (NSString*)keyCommandString; | |
| 101 | |
| 102 + (NSString*)keyCombinationForAccelerator:(const ui::AcceleratorCocoa&)item; | |
| 103 @end | |
| 104 | |
| 105 @implementation FullscreenExitBubbleController | |
| 106 | |
| 107 - (id)initWithOwner:(BrowserWindowController*)owner browser:(Browser*)browser { | |
| 108 if ((self = [super initWithNibName:@"FullscreenExitBubble" | |
| 109 bundle:base::mac::MainAppBundle()])) { | |
| 110 browser_ = browser; | |
| 111 owner_ = owner; | |
| 112 } | |
| 113 return self; | |
| 114 } | |
| 115 | |
| 116 // All infobars have an icon, so we set up the icon in the base class | |
| 117 // awakeFromNib. | |
| 118 - (void)awakeFromNib { | |
| 119 [self initializeLabel]; | |
| 120 [self hideSoon]; | |
| 121 } | |
| 122 | |
| 123 // Called when someone clicks on the embedded link. | |
| 124 - (BOOL) textView:(NSTextView*)textView | |
| 125 clickedOnLink:(id)link | |
| 126 atIndex:(NSUInteger)charIndex { | |
| 127 // TODO there's probably a better way to invoke this. | |
| 128 browser_->ExecuteCommand(IDC_FULLSCREEN); | |
| 129 return YES; | |
| 130 } | |
| 131 | |
| 132 // Called when someone clicks on the ok button. | |
| 133 - (void)allow:(id)sender { | |
| 134 [self showButtons:NO]; | |
| 135 //browser_->OnAcceptFullscreenPermission(url_); | |
| 136 [self hideSoon]; | |
| 137 } | |
| 138 | |
| 139 - (void)hideTimerFire:(NSTimer*)timer { | |
| 140 // TODO why aren't we using animateClosed? | |
| 141 NSRect endFrame = [[self view] frame]; | |
| 142 endFrame.origin.y += endFrame.size.height; | |
| 143 float duration = 0.3; | |
| 144 NSDictionary* dict = [NSDictionary dictionaryWithObjectsAndKeys: | |
| 145 [self view], NSViewAnimationTargetKey, | |
| 146 [NSValue valueWithRect:endFrame], NSViewAnimationEndFrameKey, nil]; | |
| 147 | |
| 148 NSViewAnimation* animation = | |
| 149 [[NSViewAnimation alloc] | |
| 150 initWithViewAnimations:[NSArray arrayWithObjects:dict, nil]]; | |
| 151 [animation gtm_setDuration:duration | |
| 152 eventMask:NSLeftMouseUpMask]; | |
| 153 [animation setDelegate:self]; | |
| 154 [animation startAnimation]; | |
| 155 hideAnimation_ = animation; | |
| 156 } | |
| 157 | |
| 158 - (void)animationDidEnd:(NSAnimation*)animation { | |
| 159 if (animation == hideAnimation_) { | |
| 160 [hideAnimation_ autorelease]; | |
| 161 hideAnimation_ = nil; | |
| 162 } | |
| 163 | |
| 164 [owner_ setShowFloatingChrome:YES]; | |
| 165 } | |
| 166 | |
| 167 // Called when someone clicks on the cancel button. | |
| 168 - (void)deny:(id)sender { | |
| 169 //[owner_ setFullscreen:NO forURL:GURL() showButtons:NO]; | |
| 170 } | |
| 171 | |
| 172 - (AnimatableView*)animatableView { | |
| 173 return static_cast<AnimatableView*>([self view]); | |
| 174 } | |
| 175 | |
| 176 - (void)open { | |
| 177 // Simply reset the frame size to its opened size, forcing a relayout. | |
| 178 CGFloat finalHeight = [[self view] frame].size.height; | |
| 179 [[self animatableView] setHeight:finalHeight]; | |
| 180 } | |
| 181 | |
| 182 - (void)animateOpen { | |
| 183 // Force the frame size to be 0 and then start an animation. | |
| 184 NSRect frame = [[self view] frame]; | |
| 185 CGFloat finalHeight = frame.size.height; | |
| 186 frame.size.height = 0; | |
| 187 [[self view] setFrame:frame]; | |
| 188 [[self animatableView] animateToNewHeight:finalHeight | |
| 189 duration:kAnimateOpenDuration]; | |
| 190 } | |
| 191 | |
| 192 - (void)close { | |
| 193 // Stop any running animations. | |
| 194 [[self animatableView] stopAnimation]; | |
| 195 } | |
| 196 | |
| 197 - (void)animateClosed { | |
| 198 // Start animating closed. We will receive a notification when the animation | |
| 199 // is done, at which point we can remove our view from the hierarchy and | |
| 200 // notify the delegate that the infobar was closed. | |
| 201 [[self animatableView] animateToNewHeight:0 duration:kAnimateCloseDuration]; | |
| 202 | |
| 203 // The owner called this method to close the infobar, so there will | |
| 204 // be no need to forward future remove events to the owner. | |
| 205 owner_ = NULL; | |
| 206 } | |
| 207 | |
| 208 - (void)setLabelToMessage:(NSString*)message { | |
| 209 NSMutableDictionary* attributes = [NSMutableDictionary dictionary]; | |
| 210 NSFont* font = [NSFont labelFontOfSize: | |
| 211 [NSFont systemFontSizeForControlSize:NSRegularControlSize]]; | |
| 212 [attributes setObject:font | |
| 213 forKey:NSFontAttributeName]; | |
| 214 [attributes setObject:[NSCursor arrowCursor] | |
| 215 forKey:NSCursorAttributeName]; | |
| 216 [attributes setObject:[NSNumber numberWithFloat:kTextBaselineShift] | |
| 217 forKey:NSBaselineOffsetAttributeName]; | |
| 218 scoped_nsobject<NSAttributedString> attributedString( | |
| 219 [[NSAttributedString alloc] initWithString:message | |
| 220 attributes:attributes]); | |
| 221 [[exitLabel_.get() textStorage] setAttributedString:attributedString]; | |
| 222 } | |
| 223 | |
| 224 - (void)dealloc { | |
| 225 // TODO one or more of these things may be unnecessary. | |
| 226 [[self animatableView] stopAnimation]; | |
| 227 [hideAnimation_ stopAnimation]; | |
| 228 [hideTimer_ invalidate]; | |
| 229 [super dealloc]; | |
| 230 } | |
| 231 | |
| 232 @end | |
| 233 | |
| 234 @implementation FullscreenExitBubbleController (PrivateMethods) | |
| 235 | |
| 236 - (void)initializeLabel { | |
| 237 // Replace the label placeholder NSTextField with the real label NSTextView. | |
| 238 // The former doesn't show links in a nice way, but the latter can't be added | |
| 239 // in IB without a containing scroll view, so create the NSTextView | |
| 240 // programmatically. | |
| 241 NSRect frame = [exitLabelPlaceholder_ frame]; | |
| 242 frame.size.width = frame.size.height = 0; | |
| 243 exitLabel_.reset([[NSTextView alloc] | |
| 244 initWithFrame:[exitLabelPlaceholder_ frame]]); | |
| 245 //[exitLabel_.get() setAutoresizingMask:[exitLabelPlaceholder_ autoresizingMas k]]; | |
| 246 [[exitLabelPlaceholder_ superview] | |
| 247 replaceSubview:exitLabelPlaceholder_ with:exitLabel_.get()]; | |
| 248 exitLabelPlaceholder_ = nil; // Now released. | |
| 249 [exitLabel_.get() setDelegate:self]; | |
| 250 [exitLabel_.get() setEditable:NO]; | |
| 251 [exitLabel_.get() setDrawsBackground:NO]; | |
| 252 //[exitLabel_.get() setHorizontallyResizable:NO]; | |
| 253 [exitLabel_.get() setVerticallyResizable:NO]; | |
| 254 | |
| 255 NSMutableDictionary* linkAttributes = [NSMutableDictionary dictionary]; | |
| 256 [linkAttributes setObject:[NSCursor arrowCursor] | |
| 257 forKey:NSCursorAttributeName]; | |
| 258 NSFont* font = [NSFont labelFontOfSize: | |
| 259 [NSFont systemFontSizeForControlSize:NSRegularControlSize]]; | |
| 260 [linkAttributes setObject:font | |
| 261 forKey:NSFontAttributeName]; | |
| 262 [linkAttributes setObject:[NSColor whiteColor] | |
| 263 forKey:NSForegroundColorAttributeName]; | |
| 264 [linkAttributes setObject:[NSCursor pointingHandCursor] | |
| 265 forKey:NSCursorAttributeName]; | |
| 266 [linkAttributes setObject:[NSNumber numberWithInt:NSSingleUnderlineStyle] | |
| 267 forKey:NSUnderlineStyleAttributeName]; | |
| 268 [linkAttributes setObject:[NSString string] // dummy value | |
| 269 forKey:NSLinkAttributeName]; | |
| 270 | |
| 271 [exitLabel_.get() setLinkTextAttributes:linkAttributes]; | |
| 272 NSString *message = l10n_util::GetNSStringF(IDS_EXIT_FULLSCREEN_MODE, | |
| 273 base::SysNSStringToUTF16([[self class] keyCommandString])); | |
| 274 [self setLabelToMessage:@"" withLink:message atOffset:0]; | |
| 275 //[self view].frame = [exitLabel_ frame]; | |
| 276 } | |
| 277 | |
| 278 // This looks at the Main Menu and determines what the user has set as the | |
| 279 // key combination for quit. It then gets the modifiers and builds an object | |
| 280 // to hold the data. | |
| 281 + (ui::AcceleratorCocoa)toggleFullscreenAccelerator { | |
| 282 NSMenu* mainMenu = [NSApp mainMenu]; | |
| 283 // Get the application menu (i.e. Chromium). | |
| 284 for (NSMenuItem* menu in [mainMenu itemArray]) { | |
| 285 for (NSMenuItem* item in [[menu submenu] itemArray]) { | |
| 286 // Find the toggle presentation mode item. | |
| 287 if ([item tag] == IDC_PRESENTATION_MODE) { | |
| 288 return ui::AcceleratorCocoa([item keyEquivalent], | |
| 289 [item keyEquivalentModifierMask]); | |
| 290 } | |
| 291 } | |
| 292 } | |
| 293 // Default to Cmd+Shift+F. | |
| 294 return ui::AcceleratorCocoa(@"f", NSCommandKeyMask|NSShiftKeyMask); | |
| 295 } | |
| 296 | |
| 297 // This looks at the Main Menu and determines what the user has set as the | |
| 298 // key combination for quit. It then gets the modifiers and builds a string | |
| 299 // to display them. | |
| 300 + (NSString*)keyCommandString { | |
| 301 ui::AcceleratorCocoa accelerator = [[self class] toggleFullscreenAccelerator]; | |
| 302 return [[self class] keyCombinationForAccelerator:accelerator]; | |
| 303 } | |
| 304 | |
| 305 + (NSString*)keyCombinationForAccelerator:(const ui::AcceleratorCocoa&)item { | |
| 306 NSMutableString* string = [NSMutableString string]; | |
| 307 NSUInteger modifiers = item.modifiers(); | |
| 308 | |
| 309 if (modifiers & NSCommandKeyMask) | |
| 310 [string appendString:@"\u2318"]; | |
| 311 if (modifiers & NSControlKeyMask) | |
| 312 [string appendString:@"\u2303"]; | |
| 313 if (modifiers & NSAlternateKeyMask) | |
| 314 [string appendString:@"\u2325"]; | |
| 315 BOOL isUpperCase = [[NSCharacterSet uppercaseLetterCharacterSet] | |
| 316 characterIsMember:[item.characters() characterAtIndex:0]]; | |
| 317 if (modifiers & NSShiftKeyMask || isUpperCase) | |
| 318 [string appendString:@"\u21E7"]; | |
| 319 | |
| 320 [string appendString:[item.characters() uppercaseString]]; | |
| 321 return string; | |
| 322 } | |
| 323 | |
| 324 - (void)showButtons:(BOOL)show_buttons { | |
| 325 [exitLabel_ setHidden:show_buttons]; | |
| 326 [allowButton_ setHidden:!show_buttons]; | |
| 327 [denyButton_ setHidden:!show_buttons]; | |
| 328 } | |
| 329 | |
| 330 - (void)hideSoon { | |
| 331 // TODO probably don't need to retain? | |
| 332 hideTimer_.reset( | |
| 333 [[NSTimer scheduledTimerWithTimeInterval:2.3 | |
| 334 target:self | |
| 335 selector:@selector(hideTimerFire:) | |
| 336 userInfo:nil | |
| 337 repeats:NO] retain]); | |
| 338 } | |
| 339 | |
| 340 // TODO(joth): This method factors out some common functionality between the | |
| 341 // various derived infobar classes, however the class hierarchy itself could | |
| 342 // use refactoring to reduce this duplication. http://crbug.com/38924 | |
| 343 - (void)setLabelToMessage:(NSString*)message | |
| 344 withLink:(NSString*)link | |
| 345 atOffset:(NSUInteger)linkOffset { | |
| 346 if (linkOffset == std::wstring::npos) { | |
| 347 // linkOffset == std::wstring::npos means the link should be right-aligned, | |
| 348 // which is not supported on Mac (http://crbug.com/47728). | |
| 349 NOTIMPLEMENTED(); | |
| 350 linkOffset = [message length]; | |
| 351 } | |
| 352 // Create an attributes dictionary for the entire message. We have | |
| 353 // to explicitly set the control's font. We also override the cursor to give | |
| 354 // us the normal cursor rather than the text insertion cursor. | |
| 355 NSMutableDictionary* linkAttributes = [NSMutableDictionary dictionary]; | |
| 356 [linkAttributes setObject:[NSCursor arrowCursor] | |
| 357 forKey:NSCursorAttributeName]; | |
| 358 NSFont* font = [NSFont labelFontOfSize: | |
| 359 [NSFont systemFontSizeForControlSize:NSRegularControlSize]]; | |
| 360 [linkAttributes setObject:font | |
| 361 forKey:NSFontAttributeName]; | |
| 362 | |
| 363 // Create the attributed string for the main message text. | |
| 364 scoped_nsobject<NSMutableAttributedString> infoText( | |
| 365 [[NSMutableAttributedString alloc] initWithString:message]); | |
| 366 [infoText.get() addAttributes:linkAttributes | |
| 367 range:NSMakeRange(0, [infoText.get() length])]; | |
| 368 // Add additional attributes to style the link text appropriately as | |
| 369 // well as linkify it. | |
| 370 [linkAttributes setObject:[NSColor whiteColor] | |
| 371 forKey:NSForegroundColorAttributeName]; | |
| 372 [linkAttributes setObject:[NSNumber numberWithBool:YES] | |
| 373 forKey:NSUnderlineStyleAttributeName]; | |
| 374 [linkAttributes setObject:[NSCursor pointingHandCursor] | |
| 375 forKey:NSCursorAttributeName]; | |
| 376 [linkAttributes setObject:[NSNumber numberWithInt:NSSingleUnderlineStyle] | |
| 377 forKey:NSUnderlineStyleAttributeName]; | |
| 378 [linkAttributes setObject:[NSString string] // dummy value | |
| 379 forKey:NSLinkAttributeName]; | |
| 380 | |
| 381 // Insert the link text into the string at the appropriate offset. | |
| 382 scoped_nsobject<NSAttributedString> attributedString( | |
| 383 [[NSAttributedString alloc] initWithString:link | |
| 384 attributes:linkAttributes]); | |
| 385 [infoText.get() insertAttributedString:attributedString.get() | |
| 386 atIndex:linkOffset]; | |
| 387 // The entire text needs a baseline shift. | |
| 388 [infoText addAttribute:NSBaselineOffsetAttributeName | |
| 389 value:[NSNumber numberWithDouble:kTextBaselineShift] | |
| 390 range:NSMakeRange(0, [infoText length])]; | |
| 391 | |
| 392 // Update the label view with the new text. | |
| 393 [[exitLabel_.get() textStorage] setAttributedString:infoText]; | |
| 394 | |
| 395 [exitLabel_.get() sizeToFit]; | |
| 396 NSRect frame = [[self view] frame]; | |
| 397 NSLog(@"%f", frame.size.width); | |
| 398 frame.size.width = [link widthForHeight:0 attributes:linkAttributes]; | |
| 399 //frame.size.width = [exitLabel_ frame].size.width; | |
| 400 NSLog(@"%f", [exitLabel_ frame].size.width); | |
| 401 [[self view] setFrame:frame]; | |
| 402 NSLog(@"%f", [[self view] frame].size.width); | |
|
jeremya
2011/09/14 22:56:48
here
| |
| 403 } | |
| 404 | |
| 405 @end | |
| OLD | NEW |