Views Omnibox: tolerate minor click-to-select-all dragging.
[chromium-blink-merge.git] / ui / app_list / cocoa / item_drag_controller.mm
blob1de425b97d5a2dfc3d3af94752fee2fa15a0c6bd
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"
11 // Scale to transform the grid cell when a drag starts. Note that 1.5 ensures
12 // that integers are used for the layer bounds when the grid cell dimensions
13 // are even.
14 const CGFloat kDraggingIconScale = 1.5;
16 const NSTimeInterval kAnimationDuration = 0.2;
19 @interface ItemDragController ()
21 - (void)animateTransformFrom:(CATransform3D)fromValue
22                  useDelegate:(BOOL)useDelegate;
24 - (void)clearAnimations;
26 @end
28 @implementation ItemDragController
30 - (id)initWithGridCellSize:(NSSize)size {
31   if ((self = [super init])) {
32     NSRect frameRect = NSMakeRect(0,
33                                   0,
34                                   size.width * kDraggingIconScale,
35                                   size.height * kDraggingIconScale);
36     base::scoped_nsobject<NSView> dragView(
37         [[NSView alloc] initWithFrame:frameRect]);
38     [dragView setWantsLayer:YES];
39     [dragView setHidden:YES];
41     dragLayer_.reset([[CALayer layer] retain]);
42     [dragLayer_ setFrame:NSRectToCGRect(frameRect)];
43     [[dragView layer] addSublayer:dragLayer_];
45     [self setView:dragView];
46   }
47   return self;
50 - (void)initiate:(AppsGridViewItem*)item
51     mouseDownLocation:(NSPoint)mouseDownLocation
52       currentLocation:(NSPoint)currentLocation
53             timestamp:(NSTimeInterval)eventTimestamp {
54   [self clearAnimations];
55   NSView* itemView = [item view];
56   NSPoint pointInGridCell = [itemView convertPoint:mouseDownLocation
57                                           fromView:nil];
58   mouseOffset_ = NSMakePoint(pointInGridCell.x - NSMidX([itemView bounds]),
59                              NSMidY([itemView bounds]) - pointInGridCell.y);
61   NSBitmapImageRep* imageRep = [item dragRepresentationForRestore:NO];
62   [dragLayer_ setContents:reinterpret_cast<id>([imageRep CGImage])];
63   [dragLayer_ setTransform:CATransform3DIdentity];
65   // Add a grow animation to the layer.
66   CATransform3D growFrom = CATransform3DScale(CATransform3DIdentity,
67                                               1.0 / kDraggingIconScale,
68                                               1.0 / kDraggingIconScale,
69                                               1.0);
70   [self animateTransformFrom:growFrom
71                  useDelegate:NO];
73   growStart_ = eventTimestamp;
74   [[self view] setHidden:NO];
77 - (void)update:(NSPoint)currentLocation
78      timestamp:(NSTimeInterval)eventTimestamp {
79   NSPoint pointInSuperview = [[[self view] superview]
80       convertPoint:currentLocation
81           fromView:nil];
82   NSRect rect = [[self view] bounds];
83   NSPoint anchor = NSMakePoint(NSMidX(rect), NSMidY(rect));
85   // If the grow animation is still in progress, make the point of the image
86   // that was clicked appear stuck to the mouse cursor.
87   CGFloat progress = (eventTimestamp - growStart_) / kAnimationDuration;
88   CGFloat currentIconScale = progress < 1.0 ?
89       1.0 + (kDraggingIconScale - 1.0) * progress :
90       kDraggingIconScale;
92   pointInSuperview.x -= (mouseOffset_.x * currentIconScale + anchor.x);
93   pointInSuperview.y -= (mouseOffset_.y * currentIconScale + anchor.y);
94   [[self view] setFrameOrigin:pointInSuperview];
97 - (void)complete:(AppsGridViewItem*)item
98     targetOrigin:(NSPoint)targetOrigin {
99   [self clearAnimations];
101   NSView* itemView = [item view];
102   NSBitmapImageRep* imageRep = [item dragRepresentationForRestore:YES];
104   [dragLayer_ setContents:reinterpret_cast<id>([imageRep CGImage])];
105   [dragLayer_ setTransform:CATransform3DScale(CATransform3DIdentity,
106                                               1.0 / kDraggingIconScale,
107                                               1.0 / kDraggingIconScale,
108                                               1.0)];
110   // Retain the button so it can be unhidden when the animation completes. Note
111   // that the |item| and corresponding button can differ from the |item| passed
112   // to initiate(), if it moved to a new page during the drag. At this point the
113   // destination page is known, so retain the button.
114   buttonToRestore_.reset([[item button] retain]);
116   // Add the shrink animation for the layer.
117   [self animateTransformFrom:CATransform3DIdentity
118                  useDelegate:YES];
119   shrinking_ = YES;
121   // Also animate the translation, on the view.
122   // TODO(tapted): This should be merged into the scale transform, instead of
123   // using a separate NSViewAnimation.
124   NSRect startRect = [[self view] frame];
126   // The final position needs to be adjusted since it shrinks from each side.
127   NSRect targetRect = NSMakeRect(
128       targetOrigin.x - NSMidX([itemView bounds]) * (kDraggingIconScale - 1),
129       targetOrigin.y - NSMidY([itemView bounds]) * (kDraggingIconScale - 1),
130       startRect.size.width,
131       startRect.size.height);
133   NSDictionary* animationDict = @{
134       NSViewAnimationTargetKey:     [self view],
135       NSViewAnimationStartFrameKey: [NSValue valueWithRect:startRect],
136       NSViewAnimationEndFrameKey:   [NSValue valueWithRect:targetRect]
137   };
139   base::scoped_nsobject<NSViewAnimation> translate([[NSViewAnimation alloc]
140       initWithViewAnimations:[NSArray arrayWithObject:animationDict]]);
141   [translate setDuration:kAnimationDuration];
142   [translate startAnimation];
145 - (void)animateTransformFrom:(CATransform3D)fromValue
146                  useDelegate:(BOOL)useDelegate {
147   CABasicAnimation* animation =
148       [CABasicAnimation animationWithKeyPath:@"transform"];
149   [animation setFromValue:[NSValue valueWithCATransform3D:fromValue]];
150   if (useDelegate)
151     [animation setDelegate:self];
153   [animation setDuration:kAnimationDuration];
154   [CATransaction begin];
155   [CATransaction setValue:[NSNumber numberWithFloat:kAnimationDuration]
156                    forKey:kCATransactionAnimationDuration];
157   [dragLayer_ addAnimation:animation
158                     forKey:@"transform"];
159   [CATransaction commit];
162 - (void)clearAnimations {
163   [dragLayer_ removeAllAnimations];
164   if (!shrinking_)
165     return;
167   DCHECK(buttonToRestore_);
168   [buttonToRestore_ setHidden:NO];
169   buttonToRestore_.reset();
170   shrinking_ = NO;
173 - (void)animationDidStop:(CAAnimation*)anim
174                 finished:(BOOL)finished {
175   if (!finished)
176     return;
178   DCHECK(shrinking_);
179   [self clearAnimations];
180   [dragLayer_ setContents:nil];
181   [[self view] setHidden:YES];
184 @end