Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / ui / cocoa / history_overlay_controller.mm
blobe78a9e3ba5f17621d4e86c225be7263e2b3b18c2
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 #import "chrome/browser/ui/cocoa/history_overlay_controller.h"
7 #include "base/logging.h"
8 #include "base/mac/scoped_cftyperef.h"
9 #import "chrome/browser/ui/cocoa/browser_window_controller.h"
10 #include "grit/theme_resources.h"
11 #include "ui/base/resource/resource_bundle.h"
12 #include "ui/gfx/image/image.h"
14 #import <QuartzCore/QuartzCore.h>
16 #include <cmath>
18 // Constants ///////////////////////////////////////////////////////////////////
20 // The radius of the circle drawn in the shield.
21 const CGFloat kShieldRadius = 70;
23 // The diameter of the circle and the width of its bounding box.
24 const CGFloat kShieldWidth = kShieldRadius * 2;
26 // The height of the shield.
27 const CGFloat kShieldHeight = 140;
29 // Additional height that is added to kShieldHeight when the gesture is
30 // considered complete.
31 const CGFloat kShieldHeightCompletionAdjust = 10;
33 // HistoryOverlayView //////////////////////////////////////////////////////////
35 // The content view that draws the semicircle and the arrow.
36 @interface HistoryOverlayView : NSView {
37  @private
38   HistoryOverlayMode mode_;
39   CGFloat shieldAlpha_;
40   base::scoped_nsobject<CAShapeLayer> shapeLayer_;
42 @property(nonatomic) CGFloat shieldAlpha;
43 - (id)initWithMode:(HistoryOverlayMode)mode
44              image:(NSImage*)image;
45 @end
47 @implementation HistoryOverlayView
49 @synthesize shieldAlpha = shieldAlpha_;
51 - (id)initWithMode:(HistoryOverlayMode)mode
52              image:(NSImage*)image {
53   NSRect frame = NSMakeRect(0, 0, kShieldWidth, kShieldHeight);
54   if ((self = [super initWithFrame:frame])) {
55     mode_ = mode;
56     shieldAlpha_ = 1.0;  // CAShapeLayer's fillColor defaults to opaque black.
58     // A layer-hosting view.
59     shapeLayer_.reset([[CAShapeLayer alloc] init]);
60     [self setLayer:shapeLayer_];
61     [self setWantsLayer:YES];
63     // If going backward, the arrow needs to be in the right half of the circle,
64     // so offset the X position.
65     CGFloat offset = mode_ == kHistoryOverlayModeBack ? kShieldRadius : 0;
66     NSRect arrowRect = NSMakeRect(offset, 0, kShieldRadius, kShieldHeight);
67     arrowRect = NSInsetRect(arrowRect, 10, 0);  // Give a little padding.
69     base::scoped_nsobject<NSImageView> imageView(
70         [[NSImageView alloc] initWithFrame:arrowRect]);
71     [imageView setImage:image];
72     [imageView setAutoresizingMask:NSViewMinYMargin | NSViewMaxYMargin];
73     [self addSubview:imageView];
74   }
75   return self;
78 - (void)setFrameSize:(CGSize)newSize {
79   NSSize oldSize = [self frame].size;
80   [super setFrameSize:newSize];
82   if (![shapeLayer_ path] || !NSEqualSizes(oldSize, newSize))  {
83     base::ScopedCFTypeRef<CGMutablePathRef> oval(CGPathCreateMutable());
84     CGRect ovalRect = CGRectMake(0, 0, newSize.width, newSize.height);
85     CGPathAddEllipseInRect(oval, nullptr, ovalRect);
86     [shapeLayer_ setPath:oval];
87   }
90 - (void)setShieldAlpha:(CGFloat)shieldAlpha {
91   if (shieldAlpha != shieldAlpha_) {
92     shieldAlpha_ = shieldAlpha;
93     base::ScopedCFTypeRef<CGColorRef> fillColor(
94         CGColorCreateGenericGray(0, shieldAlpha));
95     [shapeLayer_ setFillColor:fillColor];
96   }
99 @end
101 // HistoryOverlayController ////////////////////////////////////////////////////
103 @implementation HistoryOverlayController
105 - (id)initForMode:(HistoryOverlayMode)mode {
106   if ((self = [super init])) {
107     mode_ = mode;
108     DCHECK(mode == kHistoryOverlayModeBack ||
109            mode == kHistoryOverlayModeForward);
110   }
111   return self;
114 - (void)loadView {
115   const gfx::Image& image =
116       ui::ResourceBundle::GetSharedInstance().GetNativeImageNamed(
117           mode_ == kHistoryOverlayModeBack ? IDR_SWIPE_BACK
118                                            : IDR_SWIPE_FORWARD);
119   contentView_.reset(
120       [[HistoryOverlayView alloc] initWithMode:mode_
121                                          image:image.ToNSImage()]);
122   self.view = contentView_;
125 - (void)setProgress:(CGFloat)gestureAmount finished:(BOOL)finished {
126   NSRect parentFrame = [parent_ frame];
127   // When tracking the gesture, the height is constant and the alpha value
128   // changes from [0.25, 0.65].
129   CGFloat height = kShieldHeight;
130   CGFloat shieldAlpha = std::min(static_cast<CGFloat>(0.65),
131                                  std::max(gestureAmount,
132                                           static_cast<CGFloat>(0.25)));
134   // When the gesture is very likely to be completed (90% in this case), grow
135   // the semicircle's height and lock the alpha to 0.75.
136   if (finished) {
137     height += kShieldHeightCompletionAdjust;
138     shieldAlpha = 0.75;
139   }
141   // Compute the new position based on the progress.
142   NSRect frame = self.view.frame;
143   frame.size.height = height;
144   frame.origin.y = (NSHeight(parentFrame) / 2) - (height / 2);
146   CGFloat width = std::min(kShieldRadius * gestureAmount, kShieldRadius);
147   if (mode_ == kHistoryOverlayModeForward)
148     frame.origin.x = NSMaxX(parentFrame) - width;
149   else if (mode_ == kHistoryOverlayModeBack)
150     frame.origin.x = NSMinX(parentFrame) - kShieldWidth + width;
152   self.view.frame = frame;
153   [contentView_ setShieldAlpha:shieldAlpha];
156 - (void)showPanelForView:(NSView*)view {
157   parent_.reset([view retain]);
158   [self setProgress:0 finished:NO];  // Set initial view position.
159   [parent_ addSubview:self.view];
162 - (void)dismiss {
163   const CGFloat kFadeOutDurationSeconds = 0.4;
165   [NSAnimationContext beginGrouping];
166   [NSAnimationContext currentContext].duration = kFadeOutDurationSeconds;
167   [[self.view animator] removeFromSuperview];
168   [NSAnimationContext endGrouping];
171 @end