- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / cocoa / history_overlay_controller.mm
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 #import "chrome/browser/ui/cocoa/history_overlay_controller.h"
6
7 #include "base/logging.h"
8 #import "chrome/browser/ui/cocoa/browser_window_controller.h"
9 #include "grit/theme_resources.h"
10 #include "ui/base/resource/resource_bundle.h"
11 #include "ui/gfx/image/image.h"
12
13 #import <QuartzCore/QuartzCore.h>
14
15 #include <cmath>
16
17 // Constants ///////////////////////////////////////////////////////////////////
18
19 // The radius of the circle drawn in the shield.
20 const CGFloat kShieldRadius = 70;
21
22 // The diameter of the circle and the width of its bounding box.
23 const CGFloat kShieldWidth = kShieldRadius * 2;
24
25 // The height of the shield.
26 const CGFloat kShieldHeight = 140;
27
28 // Additional height that is added to kShieldHeight when the gesture is
29 // considered complete.
30 const CGFloat kShieldHeightCompletionAdjust = 10;
31
32 // The amount of |gestureAmount| at which AppKit considers the gesture
33 // completed. This was derived more via art than science.
34 const CGFloat kGestureCompleteProgress = 0.3;
35
36 // HistoryOverlayView //////////////////////////////////////////////////////////
37
38 // The content view that draws the semicircle and the arrow.
39 @interface HistoryOverlayView : NSView {
40  @private
41   HistoryOverlayMode mode_;
42   CGFloat shieldAlpha_;
43 }
44 @property(nonatomic) CGFloat shieldAlpha;
45 - (id)initWithMode:(HistoryOverlayMode)mode
46              image:(NSImage*)image;
47 @end
48
49 @implementation HistoryOverlayView
50
51 @synthesize shieldAlpha = shieldAlpha_;
52
53 - (id)initWithMode:(HistoryOverlayMode)mode
54              image:(NSImage*)image {
55   NSRect frame = NSMakeRect(0, 0, kShieldWidth, kShieldHeight);
56   if ((self = [super initWithFrame:frame])) {
57     mode_ = mode;
58
59     // If going backward, the arrow needs to be in the right half of the circle,
60     // so offset the X position.
61     CGFloat offset = mode_ == kHistoryOverlayModeBack ? kShieldRadius : 0;
62     NSRect arrowRect = NSMakeRect(offset, 0, kShieldRadius, kShieldHeight);
63     arrowRect = NSInsetRect(arrowRect, 10, 0);  // Give a little padding.
64
65     base::scoped_nsobject<NSImageView> imageView(
66         [[NSImageView alloc] initWithFrame:arrowRect]);
67     [imageView setImage:image];
68     [imageView setAutoresizingMask:NSViewMinYMargin | NSViewMaxYMargin];
69     [self addSubview:imageView];
70   }
71   return self;
72 }
73
74 - (void)drawRect:(NSRect)dirtyRect {
75   NSBezierPath* path = [NSBezierPath bezierPathWithOvalInRect:self.bounds];
76   NSColor* fillColor = [NSColor colorWithCalibratedWhite:0 alpha:shieldAlpha_];
77   [fillColor set];
78   [path fill];
79 }
80
81 @end
82
83 // HistoryOverlayController ////////////////////////////////////////////////////
84
85 @implementation HistoryOverlayController
86
87 - (id)initForMode:(HistoryOverlayMode)mode {
88   if ((self = [super init])) {
89     mode_ = mode;
90     DCHECK(mode == kHistoryOverlayModeBack ||
91            mode == kHistoryOverlayModeForward);
92   }
93   return self;
94 }
95
96 - (void)dealloc {
97   [[BrowserWindowController
98       browserWindowControllerForView:[self view]] onOverlappedViewHidden];
99   [self.view removeFromSuperview];
100   [super dealloc];
101 }
102
103 - (void)loadView {
104   const gfx::Image& image =
105       ui::ResourceBundle::GetSharedInstance().GetNativeImageNamed(
106           mode_ == kHistoryOverlayModeBack ? IDR_SWIPE_BACK
107                                            : IDR_SWIPE_FORWARD);
108   contentView_.reset(
109       [[HistoryOverlayView alloc] initWithMode:mode_
110                                          image:image.ToNSImage()]);
111   self.view = contentView_;
112 }
113
114 - (void)setProgress:(CGFloat)gestureAmount {
115   NSRect parentFrame = [parent_ frame];
116   // Scale the gesture amount so that the progress is indicative of the gesture
117   // being completed.
118   gestureAmount = std::abs(gestureAmount) / kGestureCompleteProgress;
119
120   // When tracking the gesture, the height is constant and the alpha value
121   // changes from [0.25, 0.65].
122   CGFloat height = kShieldHeight;
123   CGFloat shieldAlpha = std::min(static_cast<CGFloat>(0.65),
124                                  std::max(gestureAmount,
125                                           static_cast<CGFloat>(0.25)));
126
127   // When the gesture is very likely to be completed (90% in this case), grow
128   // the semicircle's height and lock the alpha to 0.75.
129   if (gestureAmount > 0.9) {
130     height += kShieldHeightCompletionAdjust;
131     shieldAlpha = 0.75;
132   }
133
134   // Compute the new position based on the progress.
135   NSRect frame = self.view.frame;
136   frame.size.height = height;
137   frame.origin.y = (NSHeight(parentFrame) / 2) - (height / 2);
138
139   CGFloat width = std::min(kShieldRadius * gestureAmount, kShieldRadius);
140   if (mode_ == kHistoryOverlayModeForward)
141     frame.origin.x = NSMaxX(parentFrame) - width;
142   else if (mode_ == kHistoryOverlayModeBack)
143     frame.origin.x = NSMinX(parentFrame) - kShieldWidth + width;
144
145   self.view.frame = frame;
146   [contentView_ setShieldAlpha:shieldAlpha];
147   [contentView_ setNeedsDisplay:YES];
148 }
149
150 - (void)showPanelForView:(NSView*)view {
151   parent_.reset([view retain]);
152   [self setProgress:0];  // Set initial view position.
153   [[parent_ superview] addSubview:self.view
154                        positioned:NSWindowAbove
155                        relativeTo:parent_];
156   [[BrowserWindowController
157       browserWindowControllerForView:[self view]] onOverlappedViewShown];
158 }
159
160 - (void)dismiss {
161   const CGFloat kFadeOutDurationSeconds = 0.4;
162
163   NSView* overlay = self.view;
164
165   base::scoped_nsobject<CAAnimation> animation(
166       [[overlay animationForKey:@"alphaValue"] copy]);
167   [animation setDelegate:self];
168   [animation setDuration:kFadeOutDurationSeconds];
169   NSMutableDictionary* dictionary =
170       [NSMutableDictionary dictionaryWithCapacity:1];
171   [dictionary setObject:animation forKey:@"alphaValue"];
172   [overlay setAnimations:dictionary];
173   [[overlay animator] setAlphaValue:0.0];
174 }
175
176 - (void)animationDidStop:(CAAnimation*)theAnimation finished:(BOOL)finished {
177   // Destroy the CAAnimation and its strong reference to its delegate (this
178   // class).
179   [self.view setAnimations:nil];
180 }
181
182 @end