File: item_drag_controller.mm

package info (click to toggle)
chromium-browser 41.0.2272.118-1
  • links: PTS, VCS
  • area: main
  • in suites: jessie-kfreebsd
  • size: 2,189,132 kB
  • sloc: cpp: 9,691,462; ansic: 3,341,451; python: 712,689; asm: 518,779; xml: 208,926; java: 169,820; sh: 119,353; perl: 68,907; makefile: 28,311; yacc: 13,305; objc: 11,385; tcl: 3,186; cs: 2,225; sql: 2,217; lex: 2,215; lisp: 1,349; pascal: 1,256; awk: 407; ruby: 155; sed: 53; php: 14; exp: 11
file content (184 lines) | stat: -rw-r--r-- 6,559 bytes parent folder | download | duplicates (2)
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