1 // Copyright 2013 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 "ui/app_list/cocoa/item_drag_controller.h"
7 #include "base/logging.h"
8 #import "ui/app_list/cocoa/apps_grid_view_item.h"
9 #include "ui/base/cocoa/window_size_constants.h"
13 // Scale to transform the grid cell when a drag starts. Note that 1.5 ensures
14 // that integers are used for the layer bounds when the grid cell dimensions
16 const CGFloat kDraggingIconScale = 1.5;
18 const NSTimeInterval kAnimationDuration = 0.2;
19 NSString* const kGrowAnimationKey = @"growAnimation";
23 @interface ItemDragController ()
25 - (void)animateTransformFrom:(CATransform3D)fromValue
26 useDelegate:(BOOL)useDelegate;
28 - (void)clearAnimations;
32 @implementation ItemDragController
34 - (id)initWithGridCellSize:(NSSize)size {
35 if ((self = [super init])) {
36 NSRect frameRect = NSMakeRect(0,
38 size.width * kDraggingIconScale,
39 size.height * kDraggingIconScale);
40 base::scoped_nsobject<NSView> dragView(
41 [[NSView alloc] initWithFrame:frameRect]);
42 [dragView setWantsLayer:YES];
43 [dragView setHidden:YES];
45 dragLayer_.reset([[CALayer layer] retain]);
46 [dragLayer_ setFrame:NSRectToCGRect(frameRect)];
47 [[dragView layer] addSublayer:dragLayer_];
49 [self setView:dragView];
54 - (void)initiate:(AppsGridViewItem*)item
55 mouseDownLocation:(NSPoint)mouseDownLocation
56 currentLocation:(NSPoint)currentLocation
57 timestamp:(NSTimeInterval)eventTimestamp {
58 [self clearAnimations];
59 NSView* itemView = [item view];
60 NSPoint pointInGridCell = [itemView convertPoint:mouseDownLocation
62 mouseOffset_ = NSMakePoint(pointInGridCell.x - NSMidX([itemView bounds]),
63 NSMidY([itemView bounds]) - pointInGridCell.y);
65 NSBitmapImageRep* imageRep = [item dragRepresentationForRestore:NO];
66 [dragLayer_ setContents:reinterpret_cast<id>([imageRep CGImage])];
67 [dragLayer_ setTransform:CATransform3DIdentity];
69 // Add a grow animation to the layer.
70 CATransform3D growFrom = CATransform3DScale(CATransform3DIdentity,
71 1.0 / kDraggingIconScale,
72 1.0 / kDraggingIconScale,
74 [self animateTransformFrom:growFrom
77 growStart_ = eventTimestamp;
78 [[self view] setHidden:NO];
81 - (void)update:(NSPoint)currentLocation
82 timestamp:(NSTimeInterval)eventTimestamp {
83 NSPoint pointInSuperview = [[[self view] superview]
84 convertPoint:currentLocation
86 NSRect rect = [[self view] bounds];
87 NSPoint anchor = NSMakePoint(NSMidX(rect), NSMidY(rect));
89 // If the grow animation is still in progress, make the point of the image
90 // that was clicked appear stuck to the mouse cursor.
91 CGFloat progress = (eventTimestamp - growStart_) / kAnimationDuration;
92 CGFloat currentIconScale = progress < 1.0 ?
93 1.0 + (kDraggingIconScale - 1.0) * progress :
96 pointInSuperview.x -= (mouseOffset_.x * currentIconScale + anchor.x);
97 pointInSuperview.y -= (mouseOffset_.y * currentIconScale + anchor.y);
98 [[self view] setFrameOrigin:pointInSuperview];
101 - (void)complete:(AppsGridViewItem*)item
102 targetOrigin:(NSPoint)targetOrigin {
103 [self clearAnimations];
105 NSView* itemView = [item view];
106 NSBitmapImageRep* imageRep = [item dragRepresentationForRestore:YES];
108 [dragLayer_ setContents:reinterpret_cast<id>([imageRep CGImage])];
109 [dragLayer_ setTransform:CATransform3DScale(CATransform3DIdentity,
110 1.0 / kDraggingIconScale,
111 1.0 / kDraggingIconScale,
114 // Retain the button so it can be unhidden when the animation completes. Note
115 // that the |item| and corresponding button can differ from the |item| passed
116 // to initiate(), if it moved to a new page during the drag. At this point the
117 // destination page is known, so retain the button.
118 buttonToRestore_.reset([[item button] retain]);
120 // Add the shrink animation for the layer.
121 [self animateTransformFrom:CATransform3DIdentity
125 // Also animate the translation, on the view.
126 // TODO(tapted): This should be merged into the scale transform, instead of
127 // using a separate NSViewAnimation.
128 NSRect startRect = [[self view] frame];
130 // The final position needs to be adjusted since it shrinks from each side.
131 NSRect targetRect = NSMakeRect(
132 targetOrigin.x - NSMidX([itemView bounds]) * (kDraggingIconScale - 1),
133 targetOrigin.y - NSMidY([itemView bounds]) * (kDraggingIconScale - 1),
134 startRect.size.width,
135 startRect.size.height);
137 NSDictionary* animationDict = @{
138 NSViewAnimationTargetKey: [self view],
139 NSViewAnimationStartFrameKey: [NSValue valueWithRect:startRect],
140 NSViewAnimationEndFrameKey: [NSValue valueWithRect:targetRect]
143 base::scoped_nsobject<NSViewAnimation> translate([[NSViewAnimation alloc]
144 initWithViewAnimations:[NSArray arrayWithObject:animationDict]]);
145 [translate setDuration:kAnimationDuration];
146 [translate startAnimation];
149 - (void)animateTransformFrom:(CATransform3D)fromValue
150 useDelegate:(BOOL)useDelegate {
151 CABasicAnimation* animation =
152 [CABasicAnimation animationWithKeyPath:@"transform"];
153 [animation setFromValue:[NSValue valueWithCATransform3D:fromValue]];
155 [animation setDelegate:self];
157 [animation setDuration:kAnimationDuration];
158 [CATransaction begin];
159 [CATransaction setValue:[NSNumber numberWithFloat:kAnimationDuration]
160 forKey:kCATransactionAnimationDuration];
161 [dragLayer_ addAnimation:animation
162 forKey:@"transform"];
163 [CATransaction commit];
166 - (void)clearAnimations {
167 [dragLayer_ removeAllAnimations];
171 DCHECK(buttonToRestore_);
172 [buttonToRestore_ setHidden:NO];
173 buttonToRestore_.reset();
177 - (void)animationDidStop:(CAAnimation*)anim
178 finished:(BOOL)finished {
183 [self clearAnimations];
184 [dragLayer_ setContents:nil];
185 [[self view] setHidden:YES];