Index: chrome/browser/ui/cocoa/history_overlay_controller.mm |
diff --git a/chrome/browser/ui/cocoa/history_overlay_controller.mm b/chrome/browser/ui/cocoa/history_overlay_controller.mm |
new file mode 100644 |
index 0000000000000000000000000000000000000000..ae4ce6b63c20b10cd1fb1eeb25b19f6406cfa4ac |
--- /dev/null |
+++ b/chrome/browser/ui/cocoa/history_overlay_controller.mm |
@@ -0,0 +1,180 @@ |
+// 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 "chrome/browser/ui/cocoa/history_overlay_controller.h" |
+ |
+#import <QuartzCore/QuartzCore.h> |
+ |
+#include <cmath> |
+ |
+// OverlayFrameView //////////////////////////////////////////////////////////// |
+ |
+// The content view of the window that draws a custom frame. |
+@interface OverlayFrameView : NSView { |
+ @private |
+ NSTextField* message_; // Weak, owned by the view hierarchy. |
+} |
+- (void)setMessageText:(NSString*)text; |
+@end |
+ |
+// The content view of the window that draws a custom frame. |
+@implementation OverlayFrameView |
+ |
+- (id)initWithFrame:(NSRect)frameRect { |
+ if ((self = [super initWithFrame:frameRect])) { |
+ scoped_nsobject<NSTextField> message( |
+ // The frame will be fixed up when |-setMessageText:| is called. |
+ [[NSTextField alloc] initWithFrame:NSZeroRect]); |
+ message_ = message.get(); |
+ [message_ setEditable:NO]; |
+ [message_ setSelectable:NO]; |
+ [message_ setBezeled:NO]; |
+ [message_ setDrawsBackground:NO]; |
+ [message_ setFont:[NSFont boldSystemFontOfSize:72]]; |
+ [message_ setTextColor:[NSColor whiteColor]]; |
+ [self addSubview:message_]; |
+ } |
+ return self; |
+} |
+ |
+- (void)drawRect:(NSRect)dirtyRect { |
+ const CGFloat kCornerRadius = 5.0; |
+ NSBezierPath* path = [NSBezierPath bezierPathWithRoundedRect:[self bounds] |
+ xRadius:kCornerRadius |
+ yRadius:kCornerRadius]; |
+ |
+ NSColor* fillColor = [NSColor colorWithCalibratedWhite:0.2 alpha:0.85]; |
+ [fillColor set]; |
+ [path fill]; |
+} |
+ |
+- (void)setMessageText:(NSString*)text { |
+ const CGFloat kHorizontalPadding = 30; // In view coordinates. |
+ |
+ // Style the string. |
+ scoped_nsobject<NSMutableAttributedString> attrString( |
+ [[NSMutableAttributedString alloc] initWithString:text]); |
+ scoped_nsobject<NSShadow> textShadow([[NSShadow alloc] init]); |
+ [textShadow.get() setShadowColor:[NSColor colorWithCalibratedWhite:0 |
+ alpha:0.6]]; |
+ [textShadow.get() setShadowOffset:NSMakeSize(0, -1)]; |
+ [textShadow setShadowBlurRadius:1.0]; |
+ [attrString addAttribute:NSShadowAttributeName |
+ value:textShadow |
+ range:NSMakeRange(0, [text length])]; |
+ [message_ setAttributedStringValue:attrString]; |
+ |
+ // Fix up the frame of the string. |
+ [message_ sizeToFit]; |
+ NSRect messageFrame = [message_ frame]; |
+ NSRect frameInViewSpace = |
+ [message_ convertRect:[[self window] frame] fromView:nil]; |
+ |
+ if (NSWidth(messageFrame) > NSWidth(frameInViewSpace)) |
+ frameInViewSpace.size.width = NSWidth(messageFrame) + kHorizontalPadding; |
+ |
+ messageFrame.origin.x = |
+ (NSWidth(frameInViewSpace) - NSWidth(messageFrame)) / 2; |
+ messageFrame.origin.y = |
+ (NSHeight(frameInViewSpace) - NSHeight(messageFrame)) / 2; |
+ |
+ [[self window] setFrame:[message_ convertRect:frameInViewSpace toView:nil] |
+ display:YES]; |
+ [message_ setFrame:messageFrame]; |
+} |
+ |
+@end |
+ |
+// HistoryOverlayController //////////////////////////////////////////////////// |
+ |
+@implementation HistoryOverlayController |
+ |
+- (id)initForMode:(HistoryOverlayMode)mode { |
+ const NSRect kWindowFrame = NSMakeRect(0, 0, 120, 70); |
+ scoped_nsobject<NSWindow> window( |
+ [[NSWindow alloc] initWithContentRect:kWindowFrame |
+ styleMask:NSBorderlessWindowMask |
+ backing:NSBackingStoreBuffered |
+ defer:NO]); |
+ if ((self = [super initWithWindow:window])) { |
+ mode_ = mode; |
+ |
+ [window setDelegate:self]; |
+ [window setBackgroundColor:[NSColor clearColor]]; |
+ [window setOpaque:NO]; |
+ [window setHasShadow:NO]; |
+ |
+ // Create the content view. Take the frame from the existing content view. |
+ NSRect frame = [[window contentView] frame]; |
+ scoped_nsobject<OverlayFrameView> frameView( |
+ [[OverlayFrameView alloc] initWithFrame:frame]); |
+ contentView_ = frameView.get(); |
+ [window setContentView:contentView_]; |
+ |
+ const unichar kBackArrowCharacter = 0x2190; |
+ const unichar kForwardArrowCharacter = 0x2192; |
+ |
+ unichar commandChar = mode_ == kHistoryOverlayModeForward ? |
+ kForwardArrowCharacter : kBackArrowCharacter; |
+ NSString* text = |
+ [NSString stringWithCharacters:&commandChar length:1]; |
+ [contentView_ setMessageText:text]; |
+ } |
+ return self; |
+} |
+ |
+- (void)setProgress:(CGFloat)gestureAmount { |
+ const CGFloat kVerticalPositionRatio = 0.65; |
+ NSRect windowFrame = [parent_ frame]; |
+ CGFloat minX = NSMinX(windowFrame); |
+ CGFloat maxX = NSMaxX(windowFrame) - NSWidth([[self window] frame]); |
+ CGFloat x = 0; |
+ if (mode_ == kHistoryOverlayModeForward) |
+ x = maxX + gestureAmount * (maxX - minX); |
+ else if (mode_ == kHistoryOverlayModeBack) |
+ x = minX + gestureAmount * (maxX - minX); |
+ NSPoint p = [parent_ frame].origin; |
+ p.x = x; |
+ p.y += (NSHeight(windowFrame) - NSHeight([[self window] frame])) * |
+ kVerticalPositionRatio; |
+ [[self window] setFrameOrigin:p]; |
+ |
+ CGFloat alpha = |
+ -(std::abs(gestureAmount) - 1) * (std::abs(gestureAmount) - 1) + 1; |
+ [[self window] setAlphaValue:alpha]; |
+} |
+ |
+- (void)dismiss { |
+ const CGFloat kFadeOutDurationSeconds = 0.2; |
+ |
+ NSWindow* overlayWindow = [self window]; |
+ |
+ scoped_nsobject<CAAnimation> animation( |
+ [[overlayWindow animationForKey:@"alphaValue"] copy]); |
+ [animation setDelegate:self]; |
+ [animation setDuration:kFadeOutDurationSeconds]; |
+ NSMutableDictionary* dictionary = |
+ [NSMutableDictionary dictionaryWithCapacity:1]; |
+ [dictionary setObject:animation forKey:@"alphaValue"]; |
+ [overlayWindow setAnimations:dictionary]; |
+ [[overlayWindow animator] setAlphaValue:0.0]; |
+} |
+ |
+- (void)windowWillClose:(NSNotification*)notification { |
+ // Release all animations because CAAnimation retains its delegate (self), |
+ // which will cause a retain cycle. Break it! |
+ [[self window] setAnimations:[NSDictionary dictionary]]; |
+} |
+ |
+- (void)showPanelForWindow:(NSWindow*)window { |
+ parent_.reset([window retain]); |
+ [self setProgress:0]; // Set initial window position. |
+ [self showWindow:self]; |
+} |
+ |
+- (void)animationDidStop:(CAAnimation*)theAnimation finished:(BOOL)finished { |
+ [self close]; |
+} |
+ |
+@end |