API  0.9.6
 All Classes Files Functions Variables Macros Groups Pages
CPScroller.j
Go to the documentation of this file.
1 /*
2  * CPScroller.j
3  * AppKit
4  *
5  * Created by Francisco Tolmasky.
6  * Copyright 2008, 280 North, Inc.
7  *
8  * Modified to match Lion style by Antoine Mercadal 2011
9  * <antoine.mercadal@archipelproject.org>
10  *
11  * This library is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU Lesser General Public
13  * License as published by the Free Software Foundation; either
14  * version 2.1 of the License, or (at your option) any later version.
15  *
16  * This library is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19  * Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public
22  * License along with this library; if not, write to the Free Software
23  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
24  */
25 
26 #include "../Foundation/Foundation.h"
27 
28 
29 // CPScroller Constants
37 
40 
44 
53 
54 NAMES_FOR_PARTS[CPScrollerDecrementLine] = @"decrement-line";
55 NAMES_FOR_PARTS[CPScrollerIncrementLine] = @"increment-line";
56 NAMES_FOR_PARTS[CPScrollerKnobSlot] = @"knob-slot";
58 
59 
62 
66 
67 CPThemeStateScrollViewLegacy = CPThemeState("scroller-style-legacy");
70 
71 @implementation CPScroller : CPControl
72 {
73  CPControlSize _controlSize;
74  CPUsableScrollerParts _usableParts;
75  CPArray _partRects;
76 
77  BOOL _isVertical;
78  float _knobProportion;
79 
80  CPScrollerPart _hitPart;
81 
82  CPScrollerPart _trackingPart;
83  float _trackingFloatValue;
84  CGPoint _trackingStartPoint;
85 
86  CPViewAnimation _animationScroller;
87 
88  BOOL _allowFadingOut;
89  int _style;
90  CPTimer _timerFadeOut;
91  BOOL _isMouseOver;
92 }
93 
94 
95 #pragma mark -
96 #pragma mark Class methods
97 
98 + (CPString)defaultThemeClass
99 {
100  return "scroller";
101 }
102 
103 + (id)themeAttributes
104 {
106  @"scroller-width": 7.0,
107  @"knob-slot-color": [CPNull null],
108  @"decrement-line-color": [CPNull null],
109  @"increment-line-color": [CPNull null],
110  @"knob-color": [CPNull null],
111  @"decrement-line-size":_CGSizeMakeZero(),
112  @"increment-line-size":_CGSizeMakeZero(),
113  @"track-inset":_CGInsetMakeZero(),
114  @"knob-inset": _CGInsetMakeZero(),
115  @"minimum-knob-length":21.0,
116  @"track-border-overlay": 9.0
117  }];
118 }
119 
120 + (float)scrollerWidth
121 {
122  return [self scrollerWidthInStyle:CPScrollerStyleLegacy];
123 }
124 
128 + (float)scrollerWidthInStyle:(int)aStyle
129 {
130  var scroller = [[self alloc] init];
131 
132  if (aStyle == CPScrollerStyleLegacy)
133  return [scroller valueForThemeAttribute:@"scroller-width" inState:CPThemeStateScrollViewLegacy];
134  return [scroller currentValueForThemeAttribute:@"scroller-width"];
135 }
136 
140 + (float)scrollerOverlay
141 {
142  return [[[self alloc] init] currentValueForThemeAttribute:@"track-border-overlay"];
143 }
144 
149 + (float)scrollerWidthForControlSize:(CPControlSize)aControlSize
150 {
151  return [self scrollerWidth];
152 }
153 
154 
155 #pragma mark -
156 #pragma mark Initialization
157 
158 - (id)initWithFrame:(CGRect)aFrame
159 {
160  if (self = [super initWithFrame:aFrame])
161  {
162  _controlSize = CPRegularControlSize;
163  _partRects = [];
164 
165  [self setFloatValue:0.0];
166  [self setKnobProportion:1.0];
167 
168  _hitPart = CPScrollerNoPart;
169  _allowFadingOut = YES;
170  _isMouseOver = NO;
171  _style = CPScrollerStyleOverlay;
172  var paramAnimFadeOut = [CPDictionary dictionaryWithObjects:[self, CPViewAnimationFadeOutEffect]
173  forKeys:[CPViewAnimationTargetKey, CPViewAnimationEffectKey]];
174 
175  _animationScroller = [[CPViewAnimation alloc] initWithDuration:0.2 animationCurve:CPAnimationEaseInOut];
176  [_animationScroller setViewAnimations:[paramAnimFadeOut]];
177  [_animationScroller setDelegate:self];
178  [self setAlphaValue:0.0];
179  [self _calculateIsVertical];
180  }
181 
182  return self;
183 }
184 
185 
186 #pragma mark -
187 #pragma mark Getters / Setters
188 
192 - (void)style
193 {
194  return _style;
195 }
196 
201 - (void)setStyle:(id)aStyle
202 {
203  if (_style != nil && _style === aStyle)
204  return;
205 
206  _style = aStyle;
207 
208  if (_style === CPScrollerStyleLegacy)
209  {
210  [self fadeIn];
211  [self setThemeState:CPThemeStateScrollViewLegacy];
212  }
213  else
214  {
215  _allowFadingOut = YES;
216  [self unsetThemeState:CPThemeStateScrollViewLegacy];
217  }
218 
219  [self _adjustScrollerSize];
220 }
221 
222 - (void)setObjectValue:(id)aValue
223 {
224  [super setObjectValue:MIN(1.0, MAX(0.0, +aValue))];
225 }
226 
230 - (CPControlSize)controlSize
231 {
232  return _controlSize;
233 }
234 
239 - (void)setControlSize:(CPControlSize)aControlSize
240 {
241  if (_controlSize == aControlSize)
242  return;
243 
244  _controlSize = aControlSize;
245 
246  [self setNeedsLayout];
247  [self setNeedsDisplay:YES];
248 }
249 
253 - (float)knobProportion
254 {
255  return _knobProportion;
256 }
257 
262 - (void)setKnobProportion:(float)aProportion
263 {
264  if (!_IS_NUMERIC(aProportion))
265  [CPException raise:CPInvalidArgumentException reason:"aProportion must be numeric"];
266 
267  _knobProportion = MIN(1.0, MAX(0.0001, aProportion));
268 
269  [self setNeedsDisplay:YES];
270  [self setNeedsLayout];
271 }
272 
273 
274 #pragma mark -
275 #pragma mark Privates
276 
278 - (void)_adjustScrollerSize
279 {
280  var frame = [self frame],
281  scrollerWidth = [self currentValueForThemeAttribute:@"scroller-width"];
282 
283  if ([self isVertical] && CGRectGetWidth(frame) !== scrollerWidth)
284  frame.size.width = scrollerWidth;
285 
286  if (![self isVertical] && CGRectGetHeight(frame) !== scrollerWidth)
287  frame.size.height = scrollerWidth;
288 
289  [self setFrame:frame];
290 }
291 
293 - (void)_performFadeOut:(CPTimer)aTimer
294 {
295  [self fadeOut];
296  _timerFadeOut = nil;
297 }
298 
299 
300 #pragma mark -
301 #pragma mark Utilities
302 
303 - (CGRect)rectForPart:(CPScrollerPart)aPart
304 {
305  if (aPart == CPScrollerNoPart)
306  return _CGRectMakeZero();
307 
308  return _partRects[aPart];
309 }
310 
316 - (CPScrollerPart)testPart:(CGPoint)aPoint
317 {
318  aPoint = [self convertPoint:aPoint fromView:nil];
319 
320  // The ordering of these tests is important. We check the knob and
321  // page rects first since they may overlap with the arrows.
322 
323  if (![self hasThemeState:CPThemeStateSelected])
324  return CPScrollerNoPart;
325 
326  if (CGRectContainsPoint([self rectForPart:CPScrollerKnob], aPoint))
327  return CPScrollerKnob;
328 
329  if (CGRectContainsPoint([self rectForPart:CPScrollerDecrementPage], aPoint))
331 
332  if (CGRectContainsPoint([self rectForPart:CPScrollerIncrementPage], aPoint))
334 
335  if (CGRectContainsPoint([self rectForPart:CPScrollerDecrementLine], aPoint))
337 
338  if (CGRectContainsPoint([self rectForPart:CPScrollerIncrementLine], aPoint))
340 
341  if (CGRectContainsPoint([self rectForPart:CPScrollerKnobSlot], aPoint))
342  return CPScrollerKnobSlot;
343 
344  return CPScrollerNoPart;
345 }
346 
350 - (void)checkSpaceForParts
351 {
352  var bounds = [self bounds];
353 
354  // Assume we won't be needing the arrows.
355  if (_knobProportion === 1.0)
356  {
357  _usableParts = CPNoScrollerParts;
358 
359  _partRects[CPScrollerDecrementPage] = CGRectMakeZero();
360  _partRects[CPScrollerKnob] = CGRectMakeZero();
361  _partRects[CPScrollerIncrementPage] = CGRectMakeZero();
362  _partRects[CPScrollerDecrementLine] = CGRectMakeZero();
363  _partRects[CPScrollerIncrementLine] = CGRectMakeZero();
364 
365  // In this case, the slot is the entirety of the scroller.
366  _partRects[CPScrollerKnobSlot] = CGRectMakeCopy(bounds);
367 
368  return;
369  }
370 
371  // At this point we know we're going to need arrows.
372  _usableParts = CPAllScrollerParts;
373 
374  var knobInset = [self currentValueForThemeAttribute:@"knob-inset"],
375  trackInset = [self currentValueForThemeAttribute:@"track-inset"],
376  width = _CGRectGetWidth(bounds),
377  height = _CGRectGetHeight(bounds);
378 
379  if ([self isVertical])
380  {
381  var decrementLineSize = [self currentValueForThemeAttribute:"decrement-line-size"],
382  incrementLineSize = [self currentValueForThemeAttribute:"increment-line-size"],
383  effectiveDecrementLineHeight = decrementLineSize.height + trackInset.top,
384  effectiveIncrementLineHeight = incrementLineSize.height + trackInset.bottom,
385  slotSize = height - effectiveDecrementLineHeight - effectiveIncrementLineHeight,
386  minimumKnobLength = [self currentValueForThemeAttribute:"minimum-knob-length"],
387  knobWidth = width - knobInset.left - knobInset.right,
388  knobHeight = MAX(minimumKnobLength, (slotSize * _knobProportion)),
389  knobLocation = effectiveDecrementLineHeight + (slotSize - knobHeight) * [self floatValue];
390 
391  _partRects[CPScrollerDecrementPage] = _CGRectMake(0.0, effectiveDecrementLineHeight, width, knobLocation - effectiveDecrementLineHeight);
392  _partRects[CPScrollerKnob] = _CGRectMake(knobInset.left, knobLocation, knobWidth, knobHeight);
393  _partRects[CPScrollerIncrementPage] = _CGRectMake(0.0, knobLocation + knobHeight, width, height - (knobLocation + knobHeight) - effectiveIncrementLineHeight);
394  _partRects[CPScrollerKnobSlot] = _CGRectMake(trackInset.left, effectiveDecrementLineHeight, width - trackInset.left - trackInset.right, slotSize);
395  _partRects[CPScrollerDecrementLine] = _CGRectMake(0.0, 0.0, decrementLineSize.width, decrementLineSize.height);
396  _partRects[CPScrollerIncrementLine] = _CGRectMake(0.0, height - incrementLineSize.height, incrementLineSize.width, incrementLineSize.height);
397 
398  if (height < knobHeight + decrementLineSize.height + incrementLineSize.height + trackInset.top + trackInset.bottom)
399  _partRects[CPScrollerKnob] = _CGRectMakeZero();
400 
401  if (height < decrementLineSize.height + incrementLineSize.height - 2)
402  {
403  _partRects[CPScrollerIncrementLine] = _CGRectMakeZero();
404  _partRects[CPScrollerDecrementLine] = _CGRectMakeZero();
405  _partRects[CPScrollerKnobSlot] = _CGRectMake(trackInset.left, 0, width - trackInset.left - trackInset.right, height);
406  }
407  }
408  else
409  {
410  var decrementLineSize = [self currentValueForThemeAttribute:"decrement-line-size"],
411  incrementLineSize = [self currentValueForThemeAttribute:"increment-line-size"],
412  effectiveDecrementLineWidth = decrementLineSize.width + trackInset.left,
413  effectiveIncrementLineWidth = incrementLineSize.width + trackInset.right,
414  slotSize = width - effectiveDecrementLineWidth - effectiveIncrementLineWidth,
415  minimumKnobLength = [self currentValueForThemeAttribute:"minimum-knob-length"],
416  knobWidth = MAX(minimumKnobLength, (slotSize * _knobProportion)),
417  knobHeight = height - knobInset.top - knobInset.bottom,
418  knobLocation = effectiveDecrementLineWidth + (slotSize - knobWidth) * [self floatValue];
419 
420  _partRects[CPScrollerDecrementPage] = _CGRectMake(effectiveDecrementLineWidth, 0.0, knobLocation - effectiveDecrementLineWidth, height);
421  _partRects[CPScrollerKnob] = _CGRectMake(knobLocation, knobInset.top, knobWidth, knobHeight);
422  _partRects[CPScrollerIncrementPage] = _CGRectMake(knobLocation + knobWidth, 0.0, width - (knobLocation + knobWidth) - effectiveIncrementLineWidth, height);
423  _partRects[CPScrollerKnobSlot] = _CGRectMake(effectiveDecrementLineWidth, trackInset.top, slotSize, height - trackInset.top - trackInset.bottom);
424  _partRects[CPScrollerDecrementLine] = _CGRectMake(0.0, 0.0, decrementLineSize.width, decrementLineSize.height);
425  _partRects[CPScrollerIncrementLine] = _CGRectMake(width - incrementLineSize.width, 0.0, incrementLineSize.width, incrementLineSize.height);
426 
427  if (width < knobWidth + decrementLineSize.width + incrementLineSize.width + trackInset.left + trackInset.right)
428  _partRects[CPScrollerKnob] = _CGRectMakeZero();
429 
430  if (width < decrementLineSize.width + incrementLineSize.width - 2)
431  {
432  _partRects[CPScrollerIncrementLine] = _CGRectMakeZero();
433  _partRects[CPScrollerDecrementLine] = _CGRectMakeZero();
434  _partRects[CPScrollerKnobSlot] = _CGRectMake(0.0, 0.0, width, slotSize);
435  }
436  }
437 }
438 
443 - (CPUsableScrollerParts)usableParts
444 {
445  return _usableParts;
446 }
447 
451 - (void)fadeIn
452 {
453  if (_isMouseOver && _knobProportion != 1.0)
454  [self setThemeState:CPThemeStateSelected];
455 
456  if (_timerFadeOut)
457  [_timerFadeOut invalidate];
458 
459  [self setAlphaValue:1.0];
460 }
461 
465 - (void)fadeOut
466 {
467  if ([self hasThemeState:CPThemeStateScrollViewLegacy])
468  return;
469 
470  [_animationScroller startAnimation];
471 }
472 
473 
474 #pragma mark -
475 #pragma mark Drawing
476 
482 - (void)drawArrow:(CPScrollerArrow)anArrow highlight:(BOOL)shouldHighlight
483 {
484 }
485 
489 - (void)drawKnob
490 {
491 }
492 
496 - (void)drawKnobSlot
497 {
498 }
499 
500 - (CPView)createViewForPart:(CPScrollerPart)aPart
501 {
502  var view = [[CPView alloc] initWithFrame:_CGRectMakeZero()];
503 
504  [view setHitTests:NO];
505 
506  return view;
507 }
508 
509 - (CGRect)rectForEphemeralSubviewNamed:(CPString)aName
510 {
511  return _partRects[aName];
512 }
513 
514 - (CPView)createEphemeralSubviewNamed:(CPString)aName
515 {
516  var view = [[CPView alloc] initWithFrame:_CGRectMakeZero()];
517 
518  [view setHitTests:NO];
519 
520  return view;
521 }
522 
523 - (void)layoutSubviews
524 {
525  [self checkSpaceForParts];
526 
527  var index = 0,
528  count = PARTS_ARRANGEMENT.length,
529  view;
530 
531  for (; index < count; ++index)
532  {
533  var part = PARTS_ARRANGEMENT[index];
534 
535  if (index === 0)
536  view = [self layoutEphemeralSubviewNamed:part positioned:CPWindowBelow relativeToEphemeralSubviewNamed:PARTS_ARRANGEMENT[index + 1]];
537  else
538  view = [self layoutEphemeralSubviewNamed:part positioned:CPWindowAbove relativeToEphemeralSubviewNamed:PARTS_ARRANGEMENT[index - 1]];
539 
540  if (view)
541  [view setBackgroundColor:[self currentValueForThemeAttribute:NAMES_FOR_PARTS[part] + "-color"]];
542  }
543 }
544 
548 - (void)drawParts
549 {
550  [self drawKnobSlot];
551  [self drawKnob];
552  [self drawArrow:CPScrollerDecrementArrow highlight:NO];
553  [self drawArrow:CPScrollerIncrementArrow highlight:NO];
554 }
555 
556 // Event Handling
560 - (CPScrollerPart)hitPart
561 {
562  return _hitPart;
563 }
564 
569 - (void)trackKnob:(CPEvent)anEvent
570 {
571  var type = [anEvent type];
572 
573  if (type === CPLeftMouseUp)
574  {
575  _hitPart = CPScrollerNoPart;
576 
577  return;
578  }
579 
580  if (type === CPLeftMouseDown)
581  {
582  _trackingFloatValue = [self floatValue];
583  _trackingStartPoint = [self convertPoint:[anEvent locationInWindow] fromView:nil];
584  }
585 
586  else if (type === CPLeftMouseDragged)
587  {
588  var knobRect = [self rectForPart:CPScrollerKnob],
589  knobSlotRect = [self rectForPart:CPScrollerKnobSlot],
590  remainder = ![self isVertical] ? (_CGRectGetWidth(knobSlotRect) - _CGRectGetWidth(knobRect)) : (_CGRectGetHeight(knobSlotRect) - _CGRectGetHeight(knobRect));
591 
592  if (remainder <= 0)
593  [self setFloatValue:0.0];
594  else
595  {
596  var location = [self convertPoint:[anEvent locationInWindow] fromView:nil],
597  delta = ![self isVertical] ? location.x - _trackingStartPoint.x : location.y - _trackingStartPoint.y;
598 
599  [self setFloatValue:_trackingFloatValue + delta / remainder];
600  }
601  }
602 
603  [CPApp setTarget:self selector:@selector(trackKnob:) forNextEventMatchingMask:CPLeftMouseDraggedMask | CPLeftMouseUpMask untilDate:nil inMode:nil dequeue:YES];
604 
605  if (type === CPLeftMouseDragged)
606  [self sendAction:[self action] to:[self target]];
607 }
608 
613 - (void)trackScrollButtons:(CPEvent)anEvent
614 {
615  var type = [anEvent type];
616 
617  if (type === CPLeftMouseUp)
618  {
619  [self highlight:NO];
621 
622  _hitPart = CPScrollerNoPart;
623 
624  return;
625  }
626 
627  if (type === CPLeftMouseDown)
628  {
629  _trackingPart = [self hitPart];
630 
631  _trackingStartPoint = [self convertPoint:[anEvent locationInWindow] fromView:nil];
632 
633  if ([anEvent modifierFlags] & CPAlternateKeyMask)
634  {
635  if (_trackingPart == CPScrollerDecrementLine)
636  _hitPart = CPScrollerDecrementPage;
637 
638  else if (_trackingPart == CPScrollerIncrementLine)
639  _hitPart = CPScrollerIncrementPage;
640 
641  else if (_trackingPart == CPScrollerDecrementPage || _trackingPart == CPScrollerIncrementPage)
642  {
643  var knobRect = [self rectForPart:CPScrollerKnob],
644  knobWidth = ![self isVertical] ? _CGRectGetWidth(knobRect) : _CGRectGetHeight(knobRect),
645  knobSlotRect = [self rectForPart:CPScrollerKnobSlot],
646  remainder = (![self isVertical] ? _CGRectGetWidth(knobSlotRect) : _CGRectGetHeight(knobSlotRect)) - knobWidth;
647 
648  [self setFloatValue:((![self isVertical] ? _trackingStartPoint.x - _CGRectGetMinX(knobSlotRect) : _trackingStartPoint.y - _CGRectGetMinY(knobSlotRect)) - knobWidth / 2.0) / remainder];
649 
650  _hitPart = CPScrollerKnob;
651 
652  [self sendAction:[self action] to:[self target]];
653 
654  // Now just track the knob.
655  return [self trackKnob:anEvent];
656  }
657  }
658 
659  [self highlight:YES];
660  [self sendAction:[self action] to:[self target]];
661 
663  }
664 
665  else if (type === CPLeftMouseDragged)
666  {
667  _trackingStartPoint = [self convertPoint:[anEvent locationInWindow] fromView:nil];
668 
669  if (_trackingPart == CPScrollerDecrementPage || _trackingPart == CPScrollerIncrementPage)
670  {
671  var hitPart = [self testPart:[anEvent locationInWindow]];
672 
673  if (hitPart == CPScrollerDecrementPage || hitPart == CPScrollerIncrementPage)
674  {
675  _trackingPart = hitPart;
676  _hitPart = hitPart;
677  }
678  }
679 
680  [self highlight:CGRectContainsPoint([self rectForPart:_trackingPart], _trackingStartPoint)];
681  }
682  else if (type == CPPeriodic && CGRectContainsPoint([self rectForPart:_trackingPart], _trackingStartPoint))
683  [self sendAction:[self action] to:[self target]];
684 
685  [CPApp setTarget:self selector:@selector(trackScrollButtons:) forNextEventMatchingMask:CPPeriodicMask | CPLeftMouseDraggedMask | CPLeftMouseUpMask untilDate:nil inMode:nil dequeue:YES];
686 
687 }
688 
689 - (void)_calculateIsVertical
690 {
691  // Recalculate isVertical.
692  var bounds = [self bounds],
693  width = _CGRectGetWidth(bounds),
694  height = _CGRectGetHeight(bounds);
695 
696  _isVertical = width < height ? 1 : (width > height ? 0 : -1);
697 
698  if (_isVertical === 1)
699  [self setThemeState:CPThemeStateVertical];
700  else if (_isVertical === 0)
701  [self unsetThemeState:CPThemeStateVertical];
702 }
703 
704 - (void)setFrameSize:(CGSize)aSize
705 {
706  [super setFrameSize:aSize];
707 
708  [self checkSpaceForParts];
709  [self setNeedsLayout];
710 }
711 
712 
713 #pragma mark -
714 #pragma mark Overrides
715 
716 - (id)currentValueForThemeAttribute:(CPString)anAttributeName
717 {
718  var themeState = _themeState;
719 
720  if (NAMES_FOR_PARTS[_hitPart] + "-color" !== anAttributeName)
721  themeState &= ~CPThemeStateHighlighted;
722 
723  return [self valueForThemeAttribute:anAttributeName inState:themeState];
724 }
725 
726 - (void)mouseDown:(CPEvent)anEvent
727 {
728  if (![self isEnabled])
729  return;
730 
731  _hitPart = [self testPart:[anEvent locationInWindow]];
732 
733  switch (_hitPart)
734  {
735  case CPScrollerKnob: return [self trackKnob:anEvent];
736 
740  case CPScrollerIncrementPage: return [self trackScrollButtons:anEvent];
741  }
742 }
743 
744 - (void)mouseEntered:(CPEvent)anEvent
745 {
746  [super mouseEntered:anEvent];
747 
748  if (_timerFadeOut)
749  [_timerFadeOut invalidate];
750 
751  if (![self isEnabled])
752  return;
753 
754  _allowFadingOut = NO;
755  _isMouseOver = YES;
756 
757  if ([self alphaValue] > 0 && _knobProportion != 1.0)
758  [self setThemeState:CPThemeStateSelected];
759 }
760 
761 - (void)mouseExited:(CPEvent)anEvent
762 {
763  [super mouseExited:anEvent];
764 
765  if ([self isHidden] || ![self isEnabled] || !_isMouseOver)
766  return;
767 
768  _allowFadingOut = YES;
769  _isMouseOver = NO;
770 
771  if (_timerFadeOut)
772  [_timerFadeOut invalidate];
773 
774  _timerFadeOut = [CPTimer scheduledTimerWithTimeInterval:1.2 target:self selector:@selector(_performFadeOut:) userInfo:nil repeats:NO];
775 }
776 
777 
778 #pragma mark -
779 #pragma mark Delegates
780 
781 - (void)animationDidEnd:(CPAnimation)animation
782 {
783  [self unsetThemeState:CPThemeStateSelected];
784 }
785 
786 @end
787 
788 var CPScrollerControlSizeKey = @"CPScrollerControlSize",
789  CPScrollerKnobProportionKey = @"CPScrollerKnobProportion",
790  CPScrollerStyleKey = @"CPScrollerStyleKey";
791 
792 @implementation CPScroller (CPCoding)
793 
794 - (id)initWithCoder:(CPCoder)aCoder
795 {
796  if (self = [super initWithCoder:aCoder])
797  {
798  _controlSize = CPRegularControlSize;
799 
800  if ([aCoder containsValueForKey:CPScrollerControlSizeKey])
801  _controlSize = [aCoder decodeIntForKey:CPScrollerControlSizeKey];
802 
803  _knobProportion = 1.0;
804 
805  if ([aCoder containsValueForKey:CPScrollerKnobProportionKey])
806  _knobProportion = [aCoder decodeFloatForKey:CPScrollerKnobProportionKey];
807 
808  _partRects = [];
809 
810  _hitPart = CPScrollerNoPart;
811 
812  _allowFadingOut = YES;
813  _isMouseOver = NO;
814 
815  var paramAnimFadeOut = [CPDictionary dictionaryWithObjects:[self, CPViewAnimationFadeOutEffect]
816  forKeys:[CPViewAnimationTargetKey, CPViewAnimationEffectKey]];
817 
818  _animationScroller = [[CPViewAnimation alloc] initWithDuration:0.2 animationCurve:CPAnimationEaseInOut];
819  [_animationScroller setViewAnimations:[paramAnimFadeOut]];
820  [_animationScroller setDelegate:self];
821  [self setAlphaValue:0.0];
822 
823  [self _calculateIsVertical];
824 
825  [self setStyle:[aCoder decodeIntForKey:CPScrollerStyleKey]];
826  }
827 
828  return self;
829 }
830 
831 - (void)encodeWithCoder:(CPCoder)aCoder
832 {
833  [super encodeWithCoder:aCoder];
834 
835  [aCoder encodeInt:_controlSize forKey:CPScrollerControlSizeKey];
836  [aCoder encodeFloat:_knobProportion forKey:CPScrollerKnobProportionKey];
837  [aCoder encodeInt:_style forKey:CPScrollerStyleKey];
838 }
839 
840 @end
841 
842 @implementation CPScroller (Deprecated)
843 
849 - (void)setFloatValue:(float)aValue knobProportion:(float)aProportion
850 {
851  [self setFloatValue:aValue];
852  [self setKnobProportion:aProportion];
853 }
854 
855 @end
856 
858 
862 - (BOOL)isVertical
863 {
864  return _isVertical;
865 }
866 
870 - (BOOL)allowFadingOut
871 {
872  return _allowFadingOut;
873 }
874 
875 @end