API  1.0.0
CPProgressIndicator.j
Go to the documentation of this file.
1 /*
2  * CPProgressIndicator.j
3  * AppKit
4  *
5  * Created by Francisco Tolmasky.
6  * Copyright 2008, 280 North, Inc.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21  */
22 
23 
24 
25 @typedef CPProgressIndicatorStyle
26 /*
27  @global
28  @group CPProgressIndicatorStyle
29 */
31 /*
32  @global
33  @group CPProgressIndicatorStyle
34 */
36 /*
37  @global
38  @group CPProgressIndicatorStyle
39 */
41 
43 
51 @implementation CPProgressIndicator : CPView
52 {
53  double _minValue;
54  double _maxValue;
55 
56  double _doubleValue;
57 
58  CPControlSize _controlSize;
59 
60  BOOL _indeterminate;
61  CPProgressIndicatorStyle _style;
62 
63  BOOL _isAnimating;
64 
65  BOOL _isDisplayedWhenStoppedSet;
66  BOOL _isDisplayedWhenStopped;
67 }
68 
69 + (CPString)defaultThemeClass
70 {
71  return @"progress-indicator";
72 }
73 
74 + (CPDictionary)themeAttributes
75 {
76  return @{
77  @"indeterminate-bar-color": [CPNull null],
78  @"bar-color": [CPNull null],
79  @"default-height": 20,
80  @"bezel-color": [CPNull null],
81  @"spinning-mini-gif": [CPNull null],
82  @"spinning-small-gif": [CPNull null],
83  @"spinning-regular-gif": [CPNull null],
84  @"circular-border-color": [CPNull null],
85  @"circular-border-size": 1,
86  @"circular-color": [CPNull null]
87  };
88 }
89 
90 + (Class)_binderClassForBinding:(CPString)aBinding
91 {
92  if (aBinding === CPValueBinding || aBinding === @"isIndeterminate")
93  return [_CPProgressIndicatorBinder class];
94 
95  return [super _binderClassForBinding:aBinding];
96 }
97 
98 - (id)initWithFrame:(CGRect)aFrame
99 {
100  self = [super initWithFrame:aFrame];
101 
102  if (self)
103  {
104  _minValue = 0.0;
105  _maxValue = 100.0;
106 
107  _doubleValue = 0.0;
108 
110  _isDisplayedWhenStoppedSet = NO;
111 
112  _controlSize = CPRegularControlSize;
113 
114  [self setNeedsLayout];
115  }
116 
117  return self;
118 }
119 
120 /*
121  @ignore
122 */
123 - (void)setUsesThreadedAnimation:(BOOL)aFlag
124 {
125 }
126 
131 - (void)startAnimation:(id)aSender
132 {
133  _isAnimating = YES;
134 
135  [self _hideOrDisplay];
136 }
137 
142 - (void)stopAnimation:(id)aSender
143 {
144  _isAnimating = NO;
145 
146  [self _hideOrDisplay];
147 }
148 
152 - (BOOL)usesThreadedAnimation
153 {
154  return NO;
155 }
156 
157 // Advancing the Progress Bar
162 - (void)incrementBy:(double)aValue
163 {
164  [self setDoubleValue:_doubleValue + aValue];
165 }
166 
170 - (void)setDoubleValue:(double)aValue
171 {
172  _doubleValue = MIN(MAX(aValue, _minValue), _maxValue);
173 
174  [self drawBar];
175 }
176 
180 - (double)doubleValue
181 {
182  return _doubleValue;
183 }
184 
189 - (void)setMinValue:(double)aValue
190 {
191  _minValue = aValue;
192 }
193 
197 - (double)minValue
198 {
199  return _minValue;
200 }
201 
206 - (void)setMaxValue:(double)aValue
207 {
208  _maxValue = aValue;
209 }
210 
214 - (double)maxValue
215 {
216  return _maxValue;
217 }
218 
219 // Setting the Appearance
224 - (void)setControlSize:(CPControlSize)aControlSize
225 {
226  if (_controlSize == aControlSize)
227  return;
228 
229  _controlSize = aControlSize;
230 
231  [self updateBackgroundColor];
232 }
233 
237 - (CPControlSize)controlSize
238 {
239  return _controlSize;
240 }
241 
242 /*
243  Not yet implemented
244 */
245 - (void)setControlTint:(CPControlTint)aControlTint
246 {
247 }
248 
249 /*
250  Not yet implemented.
251 */
252 - (CPControlTint)controlTint
253 {
254  return 0;
255 }
256 
257 /*
258  Not yet implemented.
259 */
260 - (void)setBezeled:(BOOL)isBezeled
261 {
262 }
263 
264 /*
265  Not yet implemented.
266 */
267 - (BOOL)isBezeled
268 {
269  return YES;
270 }
271 
276 - (void)setIndeterminate:(BOOL)indeterminate
277 {
278  if (_indeterminate == indeterminate)
279  return;
280 
281  _indeterminate = indeterminate;
282 
283  [self updateBackgroundColor];
284 }
285 
289 - (BOOL)isIndeterminate
290 {
291  return _indeterminate;
292 }
293 
298 - (void)setStyle:(CPProgressIndicatorStyle)aStyle
299 {
300  if (_style == aStyle)
301  return;
302 
303  _style = aStyle;
304 
305  [self setTheme:(_style === CPProgressIndicatorHUDBarStyle) ? [CPTheme defaultHudTheme] : [CPTheme defaultTheme]];
306 
307  [self updateBackgroundColor];
308 }
309 
313 - (void)sizeToFit
314 {
315  if (_style == CPProgressIndicatorSpinningStyle)
316  [self setFrameSize:[[CPProgressIndicatorSpinningStyleColors[_controlSize] patternImage] size]];
317  else
318  [self setFrameSize:CGSizeMake(CGRectGetWidth([self frame]), [self valueForThemeAttribute:@"default-height"])];
319 }
320 
326 - (void)setDisplayedWhenStopped:(BOOL)isDisplayedWhenStopped
327 {
328  if (_isDisplayedWhenStoppedSet && _isDisplayedWhenStopped == isDisplayedWhenStopped)
329  return;
330 
331  _isDisplayedWhenStoppedSet = YES;
332 
333  _isDisplayedWhenStopped = isDisplayedWhenStopped;
334 
335  [self _hideOrDisplay];
336 }
337 
341 - (BOOL)isDisplayedWhenStopped
342 {
343  if (_isDisplayedWhenStoppedSet)
344  return _isDisplayedWhenStopped;
345 
347  return YES;
348 
349  return NO;
350 }
351 
352 /* @ignore */
353 - (void)_hideOrDisplay
354 {
355  [self setHidden:!_isAnimating && ![self isDisplayedWhenStopped]];
356 }
357 
358 - (void)setFrameSize:(CGSize)aSize
359 {
360  [super setFrameSize:aSize];
361 
362  [self drawBar];
363 }
364 
365 /* @ignore */
366 - (void)drawBar
367 {
368  [self setNeedsLayout];
369  [self setNeedsDisplay:YES];
370 }
371 
372 - (CPView)createEphemeralSubviewNamed:(CPString)aName
373 {
374  return [[CPView alloc] initWithFrame:CGRectMakeZero()];
375 }
376 
377 - (CGRect)rectForEphemeralSubviewNamed:(CPString)aViewName
378 {
379  if (aViewName === @"bar-view" && _style !== CPProgressIndicatorSpinningStyle)
380  {
381  var width = CGRectGetWidth([self bounds]),
382  barWidth = width * ((_doubleValue - _minValue) / (_maxValue - _minValue));
383 
384  if (barWidth > 0.0 && barWidth < 4.0)
385  barWidth = 4.0;
386 
387  if (_indeterminate)
388  barWidth = width;
389 
390  return CGRectMake(0, 0, barWidth, [self valueForThemeAttribute:@"default-height"]);
391  }
392 
393  return nil;
394 }
395 
396 /* @ignore */
397 - (void)updateBackgroundColor
398 {
399  if ([CPProgressIndicatorSpinningStyleColors count] === 0)
400  {
401  CPProgressIndicatorSpinningStyleColors[CPMiniControlSize] = [self valueForThemeAttribute:@"spinning-mini-gif"];
402  CPProgressIndicatorSpinningStyleColors[CPSmallControlSize] = [self valueForThemeAttribute:@"spinning-small-gif"];
403  CPProgressIndicatorSpinningStyleColors[CPRegularControlSize] = [self valueForThemeAttribute:@"spinning-regular-gif"];
404  }
405 
406  [self setNeedsLayout];
407 }
408 
409 - (void)layoutSubviews
410 {
411  if (YES)//_isBezeled)
412  {
413  if (_style == CPProgressIndicatorSpinningStyle)
414  {
415  if (!_indeterminate)
416  return;
417 
418  // This will cause the bar view to go away due to having a nil rect when _style == CPProgressIndicatorSpinningStyle.
419  [self layoutEphemeralSubviewNamed:"bar-view"
420  positioned:CPWindowBelow
422 
423  [self setBackgroundColor:CPProgressIndicatorSpinningStyleColors[_controlSize]];
424  }
425  else
426  {
427  [self setBackgroundColor:[self currentValueForThemeAttribute:@"bezel-color"]];
428 
429  var barView = [self layoutEphemeralSubviewNamed:"bar-view"
430  positioned:CPWindowBelow
432 
433  if (_indeterminate)
434  [barView setBackgroundColor:[self currentValueForThemeAttribute:@"indeterminate-bar-color"]];
435  else
436  [barView setBackgroundColor:[self currentValueForThemeAttribute:@"bar-color"]];
437  }
438  }
439  else
440  [self setBackgroundColor:nil];
441 }
442 
443 - (void)drawRect:(CGRect)aRect
444 {
445  if (_style == CPProgressIndicatorSpinningStyle && !_indeterminate)
446  {
448  rect = CGRectMakeCopy(aRect),
449  borderSize = [self currentValueForThemeAttribute:@"circular-border-size"];
450 
451  rect.origin.x += borderSize;
452  rect.origin.y += borderSize;
453  rect.size.width = rect.size.width - borderSize * 2;
454  rect.size.height = rect.size.height - borderSize * 2;
455 
456  if ([self doubleValue] > [self minValue] && [self doubleValue] < [self maxValue])
457  {
458  var midX = CGRectGetMidX(rect),
459  midY = CGRectGetMidY(rect),
460  endAngle = Math.PI * 2 * (([self doubleValue] - [self minValue]) / ([self maxValue] - [self minValue])) - Math.PI / 2,
461  radius = MIN(rect.size.width / 2, rect.size.height / 2);
462 
463  CGContextBeginPath(context);
464  CGContextSetLineWidth(context, borderSize);
465  CGContextSetFillColor(context, [self currentValueForThemeAttribute:@"circular-color"]);
466  CGContextMoveToPoint(context, midX, midY);
467  CGContextAddArc(context, midX, midY, radius, 3 * Math.PI / 2, endAngle, YES);
468  CGContextAddLineToPoint(context, midX, midY);
469  CGContextClosePath(context);
470  CGContextFillPath(context);
471  CGContextStrokePath(context);
472  }
473  else if ([self doubleValue] == [self maxValue])
474  {
475  CGContextBeginPath(context);
476  CGContextSetFillColor(context, [self currentValueForThemeAttribute:@"circular-color"]);
477  CGContextAddEllipseInRect(context, rect);
478  CGContextClosePath(context);
479  CGContextFillPath(context);
480  }
481 
482  CGContextBeginPath(context);
483  CGContextSetStrokeColor(context , [self currentValueForThemeAttribute:@"circular-border-color"]);
484  CGContextSetLineWidth(context, borderSize);
485  CGContextAddEllipseInRect(context, rect);
486  CGContextClosePath(context);
487  CGContextStrokePath(context);
488  }
489 }
490 
491 @end
492 
493 
495 
496 - (id)initWithCoder:(CPCoder)aCoder
497 {
498  if (self = [super initWithCoder:aCoder])
499  {
500  _minValue = [aCoder decodeObjectForKey:@"_minValue"];
501  _maxValue = [aCoder decodeObjectForKey:@"_maxValue"];
502  _doubleValue = [aCoder decodeObjectForKey:@"_doubleValue"];
503  _controlSize = [aCoder decodeObjectForKey:@"_controlSize"];
504  _indeterminate = [aCoder decodeObjectForKey:@"_indeterminate"];
505  _style = [aCoder decodeIntForKey:@"_style"];
506  _isAnimating = [aCoder decodeObjectForKey:@"_isAnimating"];
507  _isDisplayedWhenStoppedSet = [aCoder decodeObjectForKey:@"_isDisplayedWhenStoppedSet"];
508  _isDisplayedWhenStopped = [aCoder decodeObjectForKey:@"_isDisplayedWhenStopped"];
509 
510  [self updateBackgroundColor];
511  }
512 
513  return self;
514 }
515 
516 - (void)encodeWithCoder:(CPCoder)aCoder
517 {
518  // Don't encode the background colour. It can be recreated based on the flags
519  // and if encoded causes hardcoded image paths in the cib while just wasting space.
520  var backgroundColor = [self backgroundColor];
521  [self setBackgroundColor:nil];
522  [super encodeWithCoder:aCoder];
523  [self setBackgroundColor:backgroundColor];
524 
525  [aCoder encodeObject:_minValue forKey:@"_minValue"];
526  [aCoder encodeObject:_maxValue forKey:@"_maxValue"];
527  [aCoder encodeObject:_doubleValue forKey:@"_doubleValue"];
528  [aCoder encodeObject:_controlSize forKey:@"_controlSize"];
529  [aCoder encodeObject:_indeterminate forKey:@"_indeterminate"];
530  [aCoder encodeInt:_style forKey:@"_style"];
531  [aCoder encodeObject:_isAnimating forKey:@"_isAnimating"];
532  [aCoder encodeObject:_isDisplayedWhenStoppedSet forKey:@"_isDisplayedWhenStoppedSet"];
533  [aCoder encodeObject:_isDisplayedWhenStopped forKey:@"_isDisplayedWhenStopped"];
534 }
535 
536 @end
537 @implementation _CPProgressIndicatorBinder : CPBinder
538 {
539  id __doxygen__;
540 }
541 
542 - (void)_updatePlaceholdersWithOptions:(CPDictionary)options forBinding:(CPString)aBinding
543 {
544  var value = aBinding === CPValueBinding ? 0.0 : YES;
545 
546  [self _setPlaceholder:value forMarker:CPMultipleValuesMarker isDefault:YES];
547  [self _setPlaceholder:value forMarker:CPNoSelectionMarker isDefault:YES];
548  [self _setPlaceholder:value forMarker:CPNotApplicableMarker isDefault:YES];
549  [self _setPlaceholder:value forMarker:CPNullMarker isDefault:YES];
550 }
551 
552 - (id)valueForBinding:(CPString)aBinding
553 {
554  if (aBinding === CPValueBinding)
555  return [_source doubleValue];
556  else if (aBinding === @"isIndeterminate")
557  [_source isIndeterminate];
558  else
559  return [super valueForBinding:aBinding];
560 }
561 
562 - (BOOL)_setValue:(id)aValue forBinding:(CPString)aBinding
563 {
564  if (aBinding === CPValueBinding)
565  [_source setDoubleValue:aValue];
566  else if (aBinding === @"isIndeterminate")
567  [_source setIndeterminate:aValue];
568  else
569  return NO;
570 
571  return YES;
572 }
573 
574 - (void)setValue:(id)aValue forBinding:(CPString)aBinding
575 {
576  if (![self _setValue:aValue forBinding:aBinding])
577  [super setValue:aValue forBinding:aBinding];
578 }
579 
580 - (void)setPlaceholderValue:(id)aValue withMarker:(CPString)aMarker forBinding:(CPString)aBinding
581 {
582  if (![self _setValue:aValue forBinding:aBinding])
583  [super setPlaceholderValue:aValue withMarker:aMarker forBinding:aBinding];
584 }
585 
586 @end
id initWithFrame:(CGRect aFrame)
Definition: CPView.j:351
CPTheme defaultHudTheme()
Definition: CPTheme.j:63
CPGraphicsContext currentContext()
An object representation of nil.
Definition: CPNull.h:2
function CGContextSetStrokeColor(aContext, aColor)
Definition: CGContext.j:675
void setFrameSize:(CGSize aSize)
int width
CPColor backgroundColor()
Definition: CPView.j:2237
function CGContextAddLineToPoint(aContext, x, y)
Definition: CGContext.j:247
function CGContextAddArc(aContext, x, y, radius, startAngle, endAngle, clockwise)
Definition: CGContext.j:192
function CGContextStrokePath(aContext)
Definition: CGContext.j:619
function CGContextAddEllipseInRect(aContext, aRect)
Definition: CGContext.j:579
A mutable key-value pair collection.
Definition: CPDictionary.h:2
function CGContextSetLineWidth(aContext, aLineWidth)
Definition: CGContext.j:177
function CGContextClosePath(aContext)
Definition: CGContext.j:322
CGRect bounds()
Definition: CALayer.j:203
An immutable string (collection of characters).
Definition: CPString.h:2
CPNull null()
Definition: CPNull.j:51
function CGContextSetFillColor(aContext, aColor)
Definition: CGContext.j:663
void setNeedsDisplay:(BOOL aFlag)
Definition: CPView.j:2597
void encodeWithCoder:(CPCoder aCoder)
Definition: CPView.j:3806
function CGContextBeginPath(aContext)
Definition: CGContext.j:311
CPColor backgroundColor()
Definition: CALayer.j:629
CPTheme defaultTheme()
Definition: CPTheme.j:44
var CPProgressIndicatorSpinningStyleColors
void setNeedsLayout()
Definition: CPView.j:2748
Defines methods for use when archiving & restoring (enc/decoding).
Definition: CPCoder.h:2
CPProgressIndicatorSpinningStyle
CPControlSize CPRegularControlSize
Definition: CPControl.j:40
Definition: CPTheme.h:2
void setBackgroundColor:(CPColor aColor)
Definition: CPView.j:1947
void setFrameSize:(CGSize aSize)
Definition: CPView.j:1124
CGRect frame()
Definition: CPView.j:1046
CPProgressIndicatorHUDBarStyle
CPProgressIndicatorStyle CPProgressIndicatorBarStyle
function CGContextFillPath(aContext)
Definition: CGContext.j:548
function CGContextMoveToPoint(aContext, x, y)
Definition: CGContext.j:344
void setDoubleValue:(double aValue)
CPView layoutEphemeralSubviewNamed:positioned:relativeToEphemeralSubviewNamed:(CPString aViewName, [positioned] CPWindowOrderingMode anOrderingMode, [relativeToEphemeralSubviewNamed] CPString relativeToViewName)
Definition: CPView.j:3407
Definition: CPView.j:137