Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1863)

Side by Side Diff: chrome/browser/cocoa/download_item_cell.mm

Issue 159060: Use a real download item.... (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: first round of review comments addressed Created 11 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
Property Changes:
Added: svn:eol-style
+ LF
OLDNEW
(Empty)
1 // Copyright (c) 2009 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/cocoa/download_item_cell.h"
6
7 #include "app/gfx/text_elider.h"
8 #include "app/l10n_util.h"
9 #include "base/mac_util.h"
10 #include "base/sys_string_conversions.h"
11 #import "chrome/browser/cocoa/download_item_cell.h"
12 #include "chrome/browser/download/download_item_model.h"
13 #include "chrome/browser/download/download_manager.h"
14 #import "third_party/GTM/AppKit/GTMTheme.h"
15
16 namespace {
17
18 // Distance from top border to icon
19 const CGFloat kImagePaddingTop = 1;
20
21 // Distance from left border to icon
22 const CGFloat kImagePaddingLeft = 1;
23
24 // Width of icon
25 const CGFloat kImageWidth = 32;
26
27 // Height of icon
28 const CGFloat kImageHeight = 32;
29
30 // x coordinate of download name string, in view coords
31 const CGFloat kTextPosLeft = kImagePaddingLeft + kImageWidth + 1;
32
33 // Distance from end of download name string to dropdown area
34 const CGFloat kTextPaddingRight = 3;
35
36 // y coordinate of download name string, in view coords, when status message
37 // is visible
38 const CGFloat kPrimaryTextPosTop = 5;
39
40 // y coordinate of download name string, in view coords, when status message
41 // is not visible
42 const CGFloat kPrimaryTextOnlyPosTop = 10;
43
44 // y coordinate of status message, in view coords
45 const CGFloat kSecondaryTextPosTop = 17;
46
47 // Width of dropdown area on the right
48 const CGFloat kDropdownAreaWidth = 18;
49
50 // Width of dropdown arrow
51 const CGFloat kDropdownArrowWidth = 5;
52
53 // Height of dropdown arrow
54 const CGFloat kDropdownArrowHeight = 3;
55
56 // Duration of the two-lines-to-one-line animation, in seconds
57 NSTimeInterval kHideStatusDuration = 0.3;
58
59 }
60
61 // This is a helper class to animate the fading out of the status text.
62 @interface HideSecondaryTitleAnimation : NSAnimation {
63 DownloadItemCell* cell_;
64 }
65 - (id)initWithDownloadItemCell:(DownloadItemCell*)cell;
66 @end
67
68 @interface DownloadItemCell(Private)
69 - (void)updateTrackingAreas:(id)sender;
70 - (void)hideSecondaryTitle;
71 - (void)animationProgressed:(NSAnimationProgress)progress;
72 @end
73
74 @implementation DownloadItemCell
75
76 @synthesize secondaryTitle = secondaryTitle_;
77 @synthesize secondaryFont = secondaryFont_;
78
79 - (void)setInitialState {
80 isStatusTextVisible_ = NO;
81 titleY_ = kPrimaryTextPosTop;
82 statusAlpha_ = 1.0;
83
84 [self setFont:[NSFont systemFontOfSize:
85 [NSFont systemFontSizeForControlSize:NSSmallControlSize]]];
86 [self setSecondaryFont:[NSFont systemFontOfSize:
87 [NSFont systemFontSizeForControlSize:NSMiniControlSize]]];
88
89 [self updateTrackingAreas:self];
90 [[NSNotificationCenter defaultCenter]
91 addObserver:self
92 selector:@selector(updateTrackingAreas:)
93 name:NSViewFrameDidChangeNotification
94 object:[self controlView]];
95 }
96
97 // For nib instantiations
98 - (id)initWithCoder:(NSCoder*)decoder {
99 if ((self = [super initWithCoder:decoder])) {
100 [self setInitialState];
101 }
102 return self;
103 }
104
105 // For programmatic instantiations
106 - (id)initTextCell:(NSString *)string {
107 if ((self = [super initTextCell:string])) {
108 [self setInitialState];
109 }
110 return self;
111 }
112
113 - (void)dealloc {
114 [[NSNotificationCenter defaultCenter] removeObserver:self];
115 [secondaryTitle_ release];
116 [secondaryFont_ release];
117 [super dealloc];
118 }
119
120 - (void)setStateFromDownload:(BaseDownloadItemModel*)downloadModel {
121 // Set name and icon of download.
122 downloadPath_ = downloadModel->download()->GetFileName();
123
124 // TODO(paulg): Use IconManager for loading icons on the file thread
125 // (crbug.com/16226).
126 NSString* extension = base::SysUTF8ToNSString(downloadPath_.Extension());
127 NSImage* icon = [[NSWorkspace sharedWorkspace] iconForFileType:extension];
128 [self setImage:icon];
129
130 std::wstring statusText = downloadModel->GetStatusText();
131 if (statusText.empty()) {
132 // Remove the status text label.
133 [self hideSecondaryTitle];
134 isStatusTextVisible_ = NO;
135 } else {
136 // Set status text.
137 NSString* statusString = base::SysWideToNSString(statusText);
138 [self setSecondaryTitle:statusString];
139 isStatusTextVisible_ = YES;
140 }
141 }
142
143 - (void)updateTrackingAreas:(id)sender {
144 if (trackingAreaButton_) {
145 [[self controlView] removeTrackingArea:trackingAreaButton_.get()];
146 trackingAreaButton_.reset(nil);
147 }
148 if (trackingAreaDropdown_) {
149 [[self controlView] removeTrackingArea:trackingAreaDropdown_.get()];
150 trackingAreaDropdown_.reset(nil);
151 }
152
153 // Use two distinct tracking rects for left and right parts.
154 NSRect bounds = [[self controlView] bounds];
155 NSRect buttonRect, dropdownRect;
156 NSDivideRect(bounds, &dropdownRect, &buttonRect,
157 kDropdownAreaWidth, NSMaxXEdge);
158
159 trackingAreaButton_.reset([[NSTrackingArea alloc]
160 initWithRect:buttonRect
161 options:(NSTrackingMouseEnteredAndExited |
162 NSTrackingActiveInActiveApp)
163 owner:self
164 userInfo:nil]);
165 [[self controlView] addTrackingArea:trackingAreaButton_.get()];
166
167 trackingAreaDropdown_.reset([[NSTrackingArea alloc]
168 initWithRect:dropdownRect
169 options:(NSTrackingMouseEnteredAndExited |
170 NSTrackingActiveInActiveApp)
171 owner:self
172 userInfo:nil]);
173 [[self controlView] addTrackingArea:trackingAreaDropdown_.get()];
174 }
175
176 - (void)setShowsBorderOnlyWhileMouseInside:(BOOL)showOnly {
177 // Override to make sure it doesn't do anything if it's called accidentally.
178 }
179
180 - (void)mouseEntered:(NSEvent*)theEvent {
181 mouseInsideCount_++;
182 if ([theEvent trackingArea] == trackingAreaButton_.get())
183 mousePosition_ = kDownloadItemMouseOverButtonPart;
184 else if ([theEvent trackingArea] == trackingAreaDropdown_.get())
185 mousePosition_ = kDownloadItemMouseOverButtonPart;
186 [[self controlView] setNeedsDisplay:YES];
187 }
188
189 - (void)mouseExited:(NSEvent *)theEvent {
190 mouseInsideCount_--;
191 if (mouseInsideCount_ == 0)
192 mousePosition_ = kDownloadItemMouseOutside;
193 [[self controlView] setNeedsDisplay:YES];
194 }
195
196 - (BOOL)isMouseInside {
197 return mousePosition_ != kDownloadItemMouseOutside;
198 }
199
200 - (BOOL)isMouseOverButtonPart {
201 return mousePosition_ == kDownloadItemMouseOverButtonPart;
202 }
203
204 - (BOOL)isButtonPartPressed {
205 return [self isHighlighted]
206 && mousePosition_ == kDownloadItemMouseOverButtonPart;
207 }
208
209 - (BOOL)isMouseOverDropdownPart {
210 return mousePosition_ == kDownloadItemMouseOverButtonPart;
211 }
212
213 - (BOOL)isDropdownPartPressed {
214 return [self isHighlighted]
215 && mousePosition_ == kDownloadItemMouseOverButtonPart;
216 }
217
218 - (NSBezierPath*)leftRoundedPath:(CGFloat)radius inRect:(NSRect)rect {
219
220 NSPoint topLeft = NSMakePoint(NSMinX(rect), NSMaxY(rect));
221 NSPoint topRight = NSMakePoint(NSMaxX(rect), NSMaxY(rect));
222 NSPoint bottomRight = NSMakePoint(NSMaxX(rect) , NSMinY(rect));
223
224 NSBezierPath* path = [NSBezierPath bezierPath];
225 [path moveToPoint:topRight];
226 [path appendBezierPathWithArcFromPoint:topLeft
227 toPoint:rect.origin
228 radius:radius];
229 [path appendBezierPathWithArcFromPoint:rect.origin
230 toPoint:bottomRight
231 radius:radius];
232 [path lineToPoint:bottomRight];
233 return path;
234 }
235
236 - (NSBezierPath*)rightRoundedPath:(CGFloat)radius inRect:(NSRect)rect {
237
238 NSPoint topLeft = NSMakePoint(NSMinX(rect), NSMaxY(rect));
239 NSPoint topRight = NSMakePoint(NSMaxX(rect), NSMaxY(rect));
240 NSPoint bottomRight = NSMakePoint(NSMaxX(rect), NSMinY(rect));
241
242 NSBezierPath* path = [NSBezierPath bezierPath];
243 [path moveToPoint:rect.origin];
244 [path appendBezierPathWithArcFromPoint:bottomRight
245 toPoint:topRight
246 radius:radius];
247 [path appendBezierPathWithArcFromPoint:topRight
248 toPoint:topLeft
249 radius:radius];
250 [path lineToPoint:topLeft];
251 [path closePath]; // Right path is closed
252 return path;
253 }
254
255 - (void)elideTitle:(int)availableWidth {
256 NSFont* font = [self font];
257 gfx::Font font_chr =
258 gfx::Font::CreateFont(base::SysNSStringToWide([font fontName]),
259 [font pointSize]);
260
261 NSString* titleString = base::SysWideToNSString(
262 ElideFilename(downloadPath_, font_chr, availableWidth));
263 [self setTitle:titleString];
264 }
265
266 - (void)drawWithFrame:(NSRect)cellFrame inView:(NSView*)controlView {
267
268 // Constants from Cole. Will kConstan them once the feedback loop
269 // is complete.
270 NSRect drawFrame = NSInsetRect(cellFrame, 0.5, 0.5);
271 NSRect innerFrame = NSInsetRect(cellFrame, 1, 1);
272
273 const float radius = 3.5;
274 NSWindow* window = [controlView window];
275 BOOL active = [window isKeyWindow] || [window isMainWindow];
276
277 GTMTheme* theme = [controlView gtm_theme];
278
279 NSRect buttonDrawRect, dropdownDrawRect;
280 NSDivideRect(drawFrame, &dropdownDrawRect, &buttonDrawRect,
281 kDropdownAreaWidth, NSMaxXEdge);
282
283 NSRect buttonInnerRect, dropdownInnerRect;
284 NSDivideRect(innerFrame, &dropdownInnerRect, &buttonInnerRect,
285 kDropdownAreaWidth, NSMaxXEdge);
286
287 NSBezierPath* buttonInnerPath = [self
288 leftRoundedPath:radius inRect:buttonDrawRect];
289 NSBezierPath* buttonOuterPath = [self
290 leftRoundedPath:(radius + 1)
291 inRect:NSInsetRect(buttonDrawRect, -1, -1)];
292
293 NSBezierPath* dropdownInnerPath = [self
294 rightRoundedPath:radius inRect:dropdownDrawRect];
295 NSBezierPath* dropdownOuterPath = [self
296 rightRoundedPath:(radius + 1)
297 inRect:NSInsetRect(dropdownDrawRect, -1, -1)];
298
299 // Stroke the borders and appropriate fill gradient. If we're borderless,
300 // the only time we want to draw the inner gradient is if we're highlighted.
301 if ([self isHighlighted] || [self isMouseInside]) {
302 [self drawBorderAndFillForTheme:theme
303 controlView:controlView
304 outerPath:buttonOuterPath
305 innerPath:buttonInnerPath
306 showHighlightGradient:[self isMouseOverButtonPart]
307 showClickedGradient:[self isButtonPartPressed]
308 active:active
309 cellFrame:cellFrame];
310
311 [self drawBorderAndFillForTheme: theme
312 controlView:controlView
313 outerPath:dropdownOuterPath
314 innerPath:dropdownInnerPath
315 showHighlightGradient:[self isMouseOverDropdownPart]
316 showClickedGradient:[self isDropdownPartPressed]
317 active:active
318 cellFrame:cellFrame];
319 }
320
321 [self drawInteriorWithFrame:innerFrame inView:controlView];
322 }
323
324 - (void)drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView*)controlView {
325 // Draw title
326 [self elideTitle:cellFrame.size.width -
327 (kTextPosLeft + kTextPaddingRight + kDropdownAreaWidth)];
328
329 NSColor* color = [self isButtonPartPressed]
330 ? [NSColor alternateSelectedControlTextColor] : [NSColor textColor];
331 NSString* primaryText = [self title];
332
333 NSDictionary* primaryTextAttributes = [NSDictionary
334 dictionaryWithObjectsAndKeys:
335 color, NSForegroundColorAttributeName,
336 [self font], NSFontAttributeName,
337 nil];
338 NSPoint primaryPos = NSMakePoint(
339 cellFrame.origin.x + kTextPosLeft,
340 titleY_);
341
342 [primaryText drawAtPoint:primaryPos withAttributes:primaryTextAttributes];
343
344 // Draw secondary title, if any
345 if ([self secondaryTitle] != nil && statusAlpha_ > 0) {
346 NSString* secondaryText = [self secondaryTitle];
347 NSColor* secondaryColor = [color colorWithAlphaComponent:statusAlpha_];
348 NSDictionary* secondaryTextAttributes = [NSDictionary
349 dictionaryWithObjectsAndKeys:
350 secondaryColor, NSForegroundColorAttributeName,
351 [self secondaryFont], NSFontAttributeName,
352 nil];
353 NSPoint secondaryPos = NSMakePoint(
354 cellFrame.origin.x + kTextPosLeft,
355 kSecondaryTextPosTop);
356 [secondaryText drawAtPoint:secondaryPos
357 withAttributes:secondaryTextAttributes];
358 }
359
360 // Draw icon
361 NSRect imageRect = NSZeroRect;
362 imageRect.size = [[self image] size];
363 [[self image] setFlipped:[controlView isFlipped]];
364 [[self image] drawInRect:[self imageRectForBounds:cellFrame]
365 fromRect:imageRect
366 operation:NSCompositeSourceOver
367 fraction:[self isEnabled] ? 1.0 : 0.5];
368
369 // Popup arrow. Put center of mass of the arrow in the center of the
370 // dropdown area.
371 CGFloat cx = NSMaxX(cellFrame) - kDropdownAreaWidth/2;
372 CGFloat cy = NSMidY(cellFrame);
373 NSPoint p1 = NSMakePoint(cx - kDropdownArrowWidth/2,
374 cy - kDropdownArrowHeight/3);
375 NSPoint p2 = NSMakePoint(cx + kDropdownArrowWidth/2,
376 cy - kDropdownArrowHeight/3);
377 NSPoint p3 = NSMakePoint(cx, cy + kDropdownArrowHeight*2/3);
378 NSBezierPath *triangle = [NSBezierPath bezierPath];
379 [triangle moveToPoint:p1];
380 [triangle lineToPoint:p2];
381 [triangle lineToPoint:p3];
382 [triangle closePath];
383
384 NSColor* fill = [self isDropdownPartPressed]
385 ? [NSColor alternateSelectedControlTextColor] : [NSColor textColor];
386 [fill setFill];
387 [triangle fill];
388 }
389
390 - (NSRect)imageRectForBounds:(NSRect)cellFrame {
391 return NSMakeRect(
392 kImagePaddingLeft, kImagePaddingTop, kImageWidth, kImageHeight);
393 }
394
395 - (void)hideSecondaryTitle {
396 if (isStatusTextVisible_) {
397 // No core animation -- text in CA layers is not subpixel antialiased :-/
398 hideStatusAnimation_.reset([[HideSecondaryTitleAnimation alloc]
399 initWithDownloadItemCell:self]);
400 [hideStatusAnimation_.get() setDelegate:self];
401 [hideStatusAnimation_.get() startAnimation];
402 } else {
403 // If the download is done so quickly that the status line is never visible,
404 // don't show an animation
405 [self animationProgressed:1.0];
406 }
407 }
408
409 - (void)animationProgressed:(NSAnimationProgress)progress {
410 titleY_ = progress*kPrimaryTextOnlyPosTop + (1 - progress)*kPrimaryTextPosTop;
411 statusAlpha_ = 1 - progress;
412 [[self controlView] setNeedsDisplay:YES];
413 }
414
415 - (void)animationDidEnd:(NSAnimation *)animation {
416 hideStatusAnimation_.reset();
417 }
418
419 @end
420
421 @implementation HideSecondaryTitleAnimation
422
423 - (id)initWithDownloadItemCell:(DownloadItemCell*)cell {
424 if ((self = [super initWithDuration:kHideStatusDuration
425 animationCurve:NSAnimationEaseIn])) {
426 cell_ = cell;
427 [self setAnimationBlockingMode:NSAnimationNonblocking];
428 }
429 return self;
430 }
431
432 - (void)setCurrentProgress:(NSAnimationProgress)progress {
433 [super setCurrentProgress:progress];
434 [cell_ animationProgressed:progress];
435 }
436
437 @end
OLDNEW
« no previous file with comments | « chrome/browser/cocoa/download_item_cell.h ('k') | chrome/browser/cocoa/download_item_controller.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698