Chromium Code Reviews| Index: chrome/browser/ui/cocoa/fullscreen_exit_bubble_controller.mm |
| diff --git a/chrome/browser/ui/cocoa/fullscreen_exit_bubble_controller.mm b/chrome/browser/ui/cocoa/fullscreen_exit_bubble_controller.mm |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..606295b1578b7e2eecc195dcba5c7c68bec21bc9 |
| --- /dev/null |
| +++ b/chrome/browser/ui/cocoa/fullscreen_exit_bubble_controller.mm |
| @@ -0,0 +1,405 @@ |
| +// Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#import <Cocoa/Cocoa.h> |
| + |
| +#include "base/logging.h" // for NOTREACHED() |
| +#include "base/mac/mac_util.h" |
| +#include "base/sys_string_conversions.h" |
| +#include "chrome/app/chrome_command_ids.h" |
| +#include "chrome/browser/ui/browser.h" |
| +#include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h" |
| +#import "chrome/browser/ui/cocoa/animatable_view.h" |
| +#import "chrome/browser/ui/cocoa/browser_window_controller.h" |
| +#include "chrome/browser/ui/cocoa/event_utils.h" |
| +#import "chrome/browser/ui/cocoa/fullscreen_exit_bubble_controller.h" |
| +#include "third_party/GTM/AppKit/GTMUILocalizerAndLayoutTweaker.h" |
| +#import "third_party/GTM/AppKit/GTMNSAnimation+Duration.h" |
| +#include "ui/base/models/accelerator_cocoa.h" |
| +#include "ui/base/l10n/l10n_util_mac.h" |
| +#include "chrome/app/chrome_command_ids.h" |
| +#include "grit/generated_resources.h" |
| + |
| +namespace { |
| +// Durations set to match the default SlideAnimation duration. |
| +const float kAnimateOpenDuration = 0.12; |
| +const float kAnimateCloseDuration = 0.12; |
| + |
| +// The baseline shift for text in the NSTextView. |
| +const float kTextBaselineShift = -1.0; |
| +} |
| + |
| +// This simple subclass of |NSTextView| just doesn't show the (text) cursor |
| +// (|NSTextView| displays the cursor with full keyboard accessibility enabled). |
| +/*@interface InfoBarTextView : NSTextView |
| +- (void)fixupCursor; |
| +@end |
| + |
| +@implementation InfoBarTextView |
| + |
| +// Never draw the insertion point (otherwise, it shows up without any user |
| +// action if full keyboard accessibility is enabled). |
| +- (BOOL)shouldDrawInsertionPoint { |
| + return NO; |
| +} |
| + |
| +- (NSRange)selectionRangeForProposedRange:(NSRange)proposedSelRange |
| + granularity:(NSSelectionGranularity)granularity { |
| + // Do not allow selections. |
| + return NSMakeRange(0, 0); |
| +} |
| + |
| +// Convince NSTextView to not show an I-Beam cursor when the cursor is over the |
| +// text view but not over actual text. |
| +// |
| +// http://www.mail-archive.com/cocoa-dev@lists.apple.com/msg10791.html |
| +// "NSTextView sets the cursor over itself dynamically, based on considerations |
| +// including the text under the cursor. It does so in -mouseEntered:, |
| +// -mouseMoved:, and -cursorUpdate:, so those would be points to consider |
| +// overriding." |
| +- (void)mouseMoved:(NSEvent*)e { |
| + [super mouseMoved:e]; |
| + [self fixupCursor]; |
| +} |
| + |
| +- (void)mouseEntered:(NSEvent*)e { |
| + [super mouseEntered:e]; |
| + [self fixupCursor]; |
| +} |
| + |
| +- (void)cursorUpdate:(NSEvent*)e { |
| + [super cursorUpdate:e]; |
| + [self fixupCursor]; |
| +} |
| + |
| +- (void)fixupCursor { |
| + if ([[NSCursor currentCursor] isEqual:[NSCursor IBeamCursor]]) |
| + [[NSCursor arrowCursor] set]; |
| +} |
| + |
| +@end*/ |
| + |
| +@interface FullscreenExitBubbleController (PrivateMethods) |
| +// Sets |exitLabel_| based on |exitLabelPlaceholder_|, sets |exitLabelPlaceholder_| to nil. |
| +- (void)initializeLabel; |
| + |
| +// Sets the info bar message to the specified |message|, with a hypertext |
| +// style link. |link| will be inserted into message at |linkOffset|. |
| +- (void)setLabelToMessage:(NSString*)message |
| + withLink:(NSString*)link |
| + atOffset:(NSUInteger)linkOffset; |
| + |
| +- (void)showButtons:(BOOL)shown; |
| +- (void)hideSoon; |
| + |
| +// Returns the Accelerator for the Toggle Fullscreen menu item. |
| ++ (ui::AcceleratorCocoa)toggleFullscreenAccelerator; |
| + |
| +// Returns a string representation fit for display of |+toggleFullscreenAccelerator|. |
| ++ (NSString*)keyCommandString; |
| + |
| ++ (NSString*)keyCombinationForAccelerator:(const ui::AcceleratorCocoa&)item; |
| +@end |
| + |
| +@implementation FullscreenExitBubbleController |
| + |
| +- (id)initWithOwner:(BrowserWindowController*)owner browser:(Browser*)browser { |
| + if ((self = [super initWithNibName:@"FullscreenExitBubble" |
| + bundle:base::mac::MainAppBundle()])) { |
| + browser_ = browser; |
| + owner_ = owner; |
| + } |
| + return self; |
| +} |
| + |
| +// All infobars have an icon, so we set up the icon in the base class |
| +// awakeFromNib. |
| +- (void)awakeFromNib { |
| + [self initializeLabel]; |
| + [self hideSoon]; |
| +} |
| + |
| +// Called when someone clicks on the embedded link. |
| +- (BOOL) textView:(NSTextView*)textView |
| + clickedOnLink:(id)link |
| + atIndex:(NSUInteger)charIndex { |
| + // TODO there's probably a better way to invoke this. |
| + browser_->ExecuteCommand(IDC_FULLSCREEN); |
| + return YES; |
| +} |
| + |
| +// Called when someone clicks on the ok button. |
| +- (void)allow:(id)sender { |
| + [self showButtons:NO]; |
| + //browser_->OnAcceptFullscreenPermission(url_); |
| + [self hideSoon]; |
| +} |
| + |
| +- (void)hideTimerFire:(NSTimer*)timer { |
| + // TODO why aren't we using animateClosed? |
| + NSRect endFrame = [[self view] frame]; |
| + endFrame.origin.y += endFrame.size.height; |
| + float duration = 0.3; |
| + NSDictionary* dict = [NSDictionary dictionaryWithObjectsAndKeys: |
| + [self view], NSViewAnimationTargetKey, |
| + [NSValue valueWithRect:endFrame], NSViewAnimationEndFrameKey, nil]; |
| + |
| + NSViewAnimation* animation = |
| + [[NSViewAnimation alloc] |
| + initWithViewAnimations:[NSArray arrayWithObjects:dict, nil]]; |
| + [animation gtm_setDuration:duration |
| + eventMask:NSLeftMouseUpMask]; |
| + [animation setDelegate:self]; |
| + [animation startAnimation]; |
| + hideAnimation_ = animation; |
| +} |
| + |
| +- (void)animationDidEnd:(NSAnimation*)animation { |
| + if (animation == hideAnimation_) { |
| + [hideAnimation_ autorelease]; |
| + hideAnimation_ = nil; |
| + } |
| + |
| + [owner_ setShowFloatingChrome:YES]; |
| +} |
| + |
| +// Called when someone clicks on the cancel button. |
| +- (void)deny:(id)sender { |
| + //[owner_ setFullscreen:NO forURL:GURL() showButtons:NO]; |
| +} |
| + |
| +- (AnimatableView*)animatableView { |
| + return static_cast<AnimatableView*>([self view]); |
| +} |
| + |
| +- (void)open { |
| + // Simply reset the frame size to its opened size, forcing a relayout. |
| + CGFloat finalHeight = [[self view] frame].size.height; |
| + [[self animatableView] setHeight:finalHeight]; |
| +} |
| + |
| +- (void)animateOpen { |
| + // Force the frame size to be 0 and then start an animation. |
| + NSRect frame = [[self view] frame]; |
| + CGFloat finalHeight = frame.size.height; |
| + frame.size.height = 0; |
| + [[self view] setFrame:frame]; |
| + [[self animatableView] animateToNewHeight:finalHeight |
| + duration:kAnimateOpenDuration]; |
| +} |
| + |
| +- (void)close { |
| + // Stop any running animations. |
| + [[self animatableView] stopAnimation]; |
| +} |
| + |
| +- (void)animateClosed { |
| + // Start animating closed. We will receive a notification when the animation |
| + // is done, at which point we can remove our view from the hierarchy and |
| + // notify the delegate that the infobar was closed. |
| + [[self animatableView] animateToNewHeight:0 duration:kAnimateCloseDuration]; |
| + |
| + // The owner called this method to close the infobar, so there will |
| + // be no need to forward future remove events to the owner. |
| + owner_ = NULL; |
| +} |
| + |
| +- (void)setLabelToMessage:(NSString*)message { |
| + NSMutableDictionary* attributes = [NSMutableDictionary dictionary]; |
| + NSFont* font = [NSFont labelFontOfSize: |
| + [NSFont systemFontSizeForControlSize:NSRegularControlSize]]; |
| + [attributes setObject:font |
| + forKey:NSFontAttributeName]; |
| + [attributes setObject:[NSCursor arrowCursor] |
| + forKey:NSCursorAttributeName]; |
| + [attributes setObject:[NSNumber numberWithFloat:kTextBaselineShift] |
| + forKey:NSBaselineOffsetAttributeName]; |
| + scoped_nsobject<NSAttributedString> attributedString( |
| + [[NSAttributedString alloc] initWithString:message |
| + attributes:attributes]); |
| + [[exitLabel_.get() textStorage] setAttributedString:attributedString]; |
| +} |
| + |
| +- (void)dealloc { |
| + // TODO one or more of these things may be unnecessary. |
| + [[self animatableView] stopAnimation]; |
| + [hideAnimation_ stopAnimation]; |
| + [hideTimer_ invalidate]; |
| + [super dealloc]; |
| +} |
| + |
| +@end |
| + |
| +@implementation FullscreenExitBubbleController (PrivateMethods) |
| + |
| +- (void)initializeLabel { |
| + // Replace the label placeholder NSTextField with the real label NSTextView. |
| + // The former doesn't show links in a nice way, but the latter can't be added |
| + // in IB without a containing scroll view, so create the NSTextView |
| + // programmatically. |
| + NSRect frame = [exitLabelPlaceholder_ frame]; |
| + frame.size.width = frame.size.height = 0; |
| + exitLabel_.reset([[NSTextView alloc] |
| + initWithFrame:[exitLabelPlaceholder_ frame]]); |
| + //[exitLabel_.get() setAutoresizingMask:[exitLabelPlaceholder_ autoresizingMask]]; |
| + [[exitLabelPlaceholder_ superview] |
| + replaceSubview:exitLabelPlaceholder_ with:exitLabel_.get()]; |
| + exitLabelPlaceholder_ = nil; // Now released. |
| + [exitLabel_.get() setDelegate:self]; |
| + [exitLabel_.get() setEditable:NO]; |
| + [exitLabel_.get() setDrawsBackground:NO]; |
| + //[exitLabel_.get() setHorizontallyResizable:NO]; |
| + [exitLabel_.get() setVerticallyResizable:NO]; |
| + |
| + NSMutableDictionary* linkAttributes = [NSMutableDictionary dictionary]; |
| + [linkAttributes setObject:[NSCursor arrowCursor] |
| + forKey:NSCursorAttributeName]; |
| + NSFont* font = [NSFont labelFontOfSize: |
| + [NSFont systemFontSizeForControlSize:NSRegularControlSize]]; |
| + [linkAttributes setObject:font |
| + forKey:NSFontAttributeName]; |
| + [linkAttributes setObject:[NSColor whiteColor] |
| + forKey:NSForegroundColorAttributeName]; |
| + [linkAttributes setObject:[NSCursor pointingHandCursor] |
| + forKey:NSCursorAttributeName]; |
| + [linkAttributes setObject:[NSNumber numberWithInt:NSSingleUnderlineStyle] |
| + forKey:NSUnderlineStyleAttributeName]; |
| + [linkAttributes setObject:[NSString string] // dummy value |
| + forKey:NSLinkAttributeName]; |
| + |
| + [exitLabel_.get() setLinkTextAttributes:linkAttributes]; |
| + NSString *message = l10n_util::GetNSStringF(IDS_EXIT_FULLSCREEN_MODE, |
| + base::SysNSStringToUTF16([[self class] keyCommandString])); |
| + [self setLabelToMessage:@"" withLink:message atOffset:0]; |
| + //[self view].frame = [exitLabel_ frame]; |
| +} |
| + |
| +// This looks at the Main Menu and determines what the user has set as the |
| +// key combination for quit. It then gets the modifiers and builds an object |
| +// to hold the data. |
| ++ (ui::AcceleratorCocoa)toggleFullscreenAccelerator { |
| + NSMenu* mainMenu = [NSApp mainMenu]; |
| + // Get the application menu (i.e. Chromium). |
| + for (NSMenuItem* menu in [mainMenu itemArray]) { |
| + for (NSMenuItem* item in [[menu submenu] itemArray]) { |
| + // Find the toggle presentation mode item. |
| + if ([item tag] == IDC_PRESENTATION_MODE) { |
| + return ui::AcceleratorCocoa([item keyEquivalent], |
| + [item keyEquivalentModifierMask]); |
| + } |
| + } |
| + } |
| + // Default to Cmd+Shift+F. |
| + return ui::AcceleratorCocoa(@"f", NSCommandKeyMask|NSShiftKeyMask); |
| +} |
| + |
| +// This looks at the Main Menu and determines what the user has set as the |
| +// key combination for quit. It then gets the modifiers and builds a string |
| +// to display them. |
| ++ (NSString*)keyCommandString { |
| + ui::AcceleratorCocoa accelerator = [[self class] toggleFullscreenAccelerator]; |
| + return [[self class] keyCombinationForAccelerator:accelerator]; |
| +} |
| + |
| ++ (NSString*)keyCombinationForAccelerator:(const ui::AcceleratorCocoa&)item { |
| + NSMutableString* string = [NSMutableString string]; |
| + NSUInteger modifiers = item.modifiers(); |
| + |
| + if (modifiers & NSCommandKeyMask) |
| + [string appendString:@"\u2318"]; |
| + if (modifiers & NSControlKeyMask) |
| + [string appendString:@"\u2303"]; |
| + if (modifiers & NSAlternateKeyMask) |
| + [string appendString:@"\u2325"]; |
| + BOOL isUpperCase = [[NSCharacterSet uppercaseLetterCharacterSet] |
| + characterIsMember:[item.characters() characterAtIndex:0]]; |
| + if (modifiers & NSShiftKeyMask || isUpperCase) |
| + [string appendString:@"\u21E7"]; |
| + |
| + [string appendString:[item.characters() uppercaseString]]; |
| + return string; |
| +} |
| + |
| +- (void)showButtons:(BOOL)show_buttons { |
| + [exitLabel_ setHidden:show_buttons]; |
| + [allowButton_ setHidden:!show_buttons]; |
| + [denyButton_ setHidden:!show_buttons]; |
| +} |
| + |
| +- (void)hideSoon { |
| + // TODO probably don't need to retain? |
| + hideTimer_.reset( |
| + [[NSTimer scheduledTimerWithTimeInterval:2.3 |
| + target:self |
| + selector:@selector(hideTimerFire:) |
| + userInfo:nil |
| + repeats:NO] retain]); |
| +} |
| + |
| +// TODO(joth): This method factors out some common functionality between the |
| +// various derived infobar classes, however the class hierarchy itself could |
| +// use refactoring to reduce this duplication. http://crbug.com/38924 |
| +- (void)setLabelToMessage:(NSString*)message |
| + withLink:(NSString*)link |
| + atOffset:(NSUInteger)linkOffset { |
| + if (linkOffset == std::wstring::npos) { |
| + // linkOffset == std::wstring::npos means the link should be right-aligned, |
| + // which is not supported on Mac (http://crbug.com/47728). |
| + NOTIMPLEMENTED(); |
| + linkOffset = [message length]; |
| + } |
| + // Create an attributes dictionary for the entire message. We have |
| + // to explicitly set the control's font. We also override the cursor to give |
| + // us the normal cursor rather than the text insertion cursor. |
| + NSMutableDictionary* linkAttributes = [NSMutableDictionary dictionary]; |
| + [linkAttributes setObject:[NSCursor arrowCursor] |
| + forKey:NSCursorAttributeName]; |
| + NSFont* font = [NSFont labelFontOfSize: |
| + [NSFont systemFontSizeForControlSize:NSRegularControlSize]]; |
| + [linkAttributes setObject:font |
| + forKey:NSFontAttributeName]; |
| + |
| + // Create the attributed string for the main message text. |
| + scoped_nsobject<NSMutableAttributedString> infoText( |
| + [[NSMutableAttributedString alloc] initWithString:message]); |
| + [infoText.get() addAttributes:linkAttributes |
| + range:NSMakeRange(0, [infoText.get() length])]; |
| + // Add additional attributes to style the link text appropriately as |
| + // well as linkify it. |
| + [linkAttributes setObject:[NSColor whiteColor] |
| + forKey:NSForegroundColorAttributeName]; |
| + [linkAttributes setObject:[NSNumber numberWithBool:YES] |
| + forKey:NSUnderlineStyleAttributeName]; |
| + [linkAttributes setObject:[NSCursor pointingHandCursor] |
| + forKey:NSCursorAttributeName]; |
| + [linkAttributes setObject:[NSNumber numberWithInt:NSSingleUnderlineStyle] |
| + forKey:NSUnderlineStyleAttributeName]; |
| + [linkAttributes setObject:[NSString string] // dummy value |
| + forKey:NSLinkAttributeName]; |
| + |
| + // Insert the link text into the string at the appropriate offset. |
| + scoped_nsobject<NSAttributedString> attributedString( |
| + [[NSAttributedString alloc] initWithString:link |
| + attributes:linkAttributes]); |
| + [infoText.get() insertAttributedString:attributedString.get() |
| + atIndex:linkOffset]; |
| + // The entire text needs a baseline shift. |
| + [infoText addAttribute:NSBaselineOffsetAttributeName |
| + value:[NSNumber numberWithDouble:kTextBaselineShift] |
| + range:NSMakeRange(0, [infoText length])]; |
| + |
| + // Update the label view with the new text. |
| + [[exitLabel_.get() textStorage] setAttributedString:infoText]; |
| + |
| + [exitLabel_.get() sizeToFit]; |
| + NSRect frame = [[self view] frame]; |
| + NSLog(@"%f", frame.size.width); |
| + frame.size.width = [link widthForHeight:0 attributes:linkAttributes]; |
| + //frame.size.width = [exitLabel_ frame].size.width; |
| + NSLog(@"%f", [exitLabel_ frame].size.width); |
| + [[self view] setFrame:frame]; |
| + NSLog(@"%f", [[self view] frame].size.width); |
|
jeremya
2011/09/14 22:56:48
here
|
| +} |
| + |
| +@end |