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