API  1.0.0
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
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 @global CPApp
30 
31 // CPScroller Constants
32 @typedef CPScrollerPart
40 
43 
44 @typedef CPUsableScrollerParts
48 
49 var PARTS_ARRANGEMENT = [CPScrollerKnobSlot, CPScrollerDecrementLine, CPScrollerIncrementLine, CPScrollerKnob],
52 
53 var _CACHED_THEME_SCROLLER = nil; // This is used by the class methods to pull the theme attributes.
54 
55 NAMES_FOR_PARTS[CPScrollerDecrementLine] = @"decrement-line";
56 NAMES_FOR_PARTS[CPScrollerIncrementLine] = @"increment-line";
57 NAMES_FOR_PARTS[CPScrollerKnobSlot] = @"knob-slot";
58 NAMES_FOR_PARTS[CPScrollerKnob] = @"knob";
59 
60 
63 
67 
68 CPThemeStateScrollViewLegacy = CPThemeState("scroller-style-legacy");
69 CPThemeStateScrollerKnobLight = CPThemeState("scroller-knob-light");
70 CPThemeStateScrollerKnobDark = CPThemeState("scroller-knob-dark");
71 
76 @implementation CPScroller : CPControl
77 {
78  CPUsableScrollerParts _usableParts;
79  CPArray _partRects;
80 
81  BOOL _isVertical;
82  float _knobProportion;
83 
84  CPScrollerPart _hitPart;
85 
86  CPScrollerPart _trackingPart;
87  float _trackingFloatValue;
88  CGPoint _trackingStartPoint;
89 
90  CPViewAnimation _animationScroller;
91 
92  BOOL _allowFadingOut;
93  int _style;
94  CPTimer _timerFadeOut;
95  BOOL _isMouseOver;
96 }
97 
98 
99 #pragma mark -
100 #pragma mark Class methods
101 
102 + (CPString)defaultThemeClass
103 {
104  return "scroller";
105 }
106 
107 + (CPDictionary)themeAttributes
108 {
109  return @{
110  @"scroller-width": 7.0,
111  @"knob-slot-color": [CPNull null],
112  @"decrement-line-color": [CPNull null],
113  @"increment-line-color": [CPNull null],
114  @"knob-color": [CPNull null],
115  @"decrement-line-size": CGSizeMakeZero(),
116  @"increment-line-size": CGSizeMakeZero(),
117  @"track-inset": CGInsetMakeZero(),
118  @"knob-inset": CGInsetMakeZero(),
119  @"minimum-knob-length": 21.0,
120  @"track-border-overlay": 9.0
121  };
122 }
123 
124 + (float)scrollerWidth
125 {
126  return [self scrollerWidthInStyle:CPScrollerStyleLegacy];
127 }
128 
132 + (float)scrollerWidthInStyle:(int)aStyle
133 {
134  if (!_CACHED_THEME_SCROLLER)
135  _CACHED_THEME_SCROLLER = [[self alloc] init];
136 
137  if (aStyle == CPScrollerStyleLegacy)
138  return [_CACHED_THEME_SCROLLER valueForThemeAttribute:@"scroller-width" inState:CPThemeStateScrollViewLegacy];
139 
140  return [_CACHED_THEME_SCROLLER currentValueForThemeAttribute:@"scroller-width"];
141 }
142 
146 + (float)scrollerOverlay
147 {
148  if (!_CACHED_THEME_SCROLLER)
149  _CACHED_THEME_SCROLLER = [[self alloc] init];
150 
151  return [_CACHED_THEME_SCROLLER currentValueForThemeAttribute:@"track-border-overlay"];
152 }
153 
158 + (float)scrollerWidthForControlSize:(CPControlSize)aControlSize
159 {
160  return [self scrollerWidth];
161 }
162 
163 
164 #pragma mark -
165 #pragma mark Initialization
166 
167 - (id)initWithFrame:(CGRect)aFrame
168 {
169  if (self = [super initWithFrame:aFrame])
170  {
171  _partRects = [];
172 
173  [self setFloatValue:0.0];
174  [self setKnobProportion:1.0];
175 
176  _hitPart = CPScrollerNoPart;
177  _allowFadingOut = YES;
178  _isMouseOver = NO;
179  _style = CPScrollerStyleOverlay;
180 
181  var paramAnimFadeOut = @{
184  };
185 
186  _animationScroller = [[CPViewAnimation alloc] initWithDuration:0.2 animationCurve:CPAnimationEaseInOut];
187  [_animationScroller setViewAnimations:[paramAnimFadeOut]];
188  [_animationScroller setDelegate:self];
189  [self setAlphaValue:0.0];
190 
191  // We have to choose an orientation. If for some bizarre reason width === height,
192  // punt and choose vertical.
193  [self _setIsVertical:CGRectGetHeight(aFrame) >= CGRectGetWidth(aFrame)];
194  }
195 
196  return self;
197 }
198 
199 
200 #pragma mark -
201 #pragma mark Getters / Setters
202 
206 - (void)style
207 {
208  return _style;
209 }
210 
215 - (void)setStyle:(id)aStyle
216 {
217  if (_style != nil && _style === aStyle)
218  return;
219 
220  _style = aStyle;
221 
222  if (_style === CPScrollerStyleLegacy)
223  {
224  _allowFadingOut = NO;
225  [self fadeIn];
226  [self setThemeState:CPThemeStateScrollViewLegacy];
227  }
228  else
229  {
230  _allowFadingOut = YES;
231  [self unsetThemeState:CPThemeStateScrollViewLegacy];
232  }
233 
234  //[self _adjustScrollerSize];
235 }
236 
237 - (void)setObjectValue:(id)aValue
238 {
239  [super setObjectValue:MIN(1.0, MAX(0.0, +aValue))];
240 }
241 
245 - (float)knobProportion
246 {
247  return _knobProportion;
248 }
249 
254 - (void)setKnobProportion:(float)aProportion
255 {
256  if (!_IS_NUMERIC(aProportion))
257  [CPException raise:CPInvalidArgumentException reason:"aProportion must be numeric"];
258 
259  _knobProportion = MIN(1.0, MAX(0.0001, aProportion));
260 
261  [self setNeedsDisplay:YES];
262  [self setNeedsLayout];
263 }
264 
265 
266 #pragma mark -
267 #pragma mark Privates
268 
270 - (void)_adjustScrollerSize
271 {
272  var frame = [self frame],
273  scrollerWidth = [self currentValueForThemeAttribute:@"scroller-width"];
274 
275  if ([self isVertical] && CGRectGetWidth(frame) !== scrollerWidth)
276  frame.size.width = scrollerWidth;
277 
278  if (![self isVertical] && CGRectGetHeight(frame) !== scrollerWidth)
279  frame.size.height = scrollerWidth;
280 
281  [self setFrame:frame];
282 }
283 
285 - (void)_performFadeOut:(CPTimer)aTimer
286 {
287  [self fadeOut];
288  _timerFadeOut = nil;
289 }
290 
291 
292 #pragma mark -
293 #pragma mark Utilities
294 
295 - (CGRect)rectForPart:(CPScrollerPart)aPart
296 {
297  if (aPart == CPScrollerNoPart)
298  return CGRectMakeZero();
299 
300  return _partRects[aPart];
301 }
302 
308 - (CPScrollerPart)testPart:(CGPoint)aPoint
309 {
310  aPoint = [self convertPoint:aPoint fromView:nil];
311 
312  // The ordering of these tests is important. We check the knob and
313  // page rects first since they may overlap with the arrows.
314 
315  if (![self hasThemeState:CPThemeStateSelected] && ![self hasThemeState:CPThemeStateScrollViewLegacy])
316  return CPScrollerNoPart;
317 
318  if (CGRectContainsPoint([self rectForPart:CPScrollerKnob], aPoint))
319  return CPScrollerKnob;
320 
321  if (CGRectContainsPoint([self rectForPart:CPScrollerDecrementPage], aPoint))
323 
324  if (CGRectContainsPoint([self rectForPart:CPScrollerIncrementPage], aPoint))
326 
327  if (CGRectContainsPoint([self rectForPart:CPScrollerDecrementLine], aPoint))
329 
330  if (CGRectContainsPoint([self rectForPart:CPScrollerIncrementLine], aPoint))
332 
333  if (CGRectContainsPoint([self rectForPart:CPScrollerKnobSlot], aPoint))
334  return CPScrollerKnobSlot;
335 
336  return CPScrollerNoPart;
337 }
338 
342 - (void)checkSpaceForParts
343 {
344  var bounds = [self bounds];
345 
346  // Assume we won't be needing the arrows.
347  if (_knobProportion === 1.0)
348  {
349  _usableParts = CPNoScrollerParts;
350 
351  _partRects[CPScrollerDecrementPage] = CGRectMakeZero();
352  _partRects[CPScrollerKnob] = CGRectMakeZero();
353  _partRects[CPScrollerIncrementPage] = CGRectMakeZero();
354  _partRects[CPScrollerDecrementLine] = CGRectMakeZero();
355  _partRects[CPScrollerIncrementLine] = CGRectMakeZero();
356 
357  // In this case, the slot is the entirety of the scroller.
358  _partRects[CPScrollerKnobSlot] = CGRectMakeCopy(bounds);
359 
360  return;
361  }
362 
363  // At this point we know we're going to need arrows.
364  _usableParts = CPAllScrollerParts;
365 
366  var knobInset = [self currentValueForThemeAttribute:@"knob-inset"],
367  trackInset = [self currentValueForThemeAttribute:@"track-inset"],
368  width = CGRectGetWidth(bounds),
369  height = CGRectGetHeight(bounds);
370 
371  if ([self isVertical])
372  {
373  var decrementLineSize = [self currentValueForThemeAttribute:"decrement-line-size"],
374  incrementLineSize = [self currentValueForThemeAttribute:"increment-line-size"],
375  effectiveDecrementLineHeight = decrementLineSize.height + trackInset.top,
376  effectiveIncrementLineHeight = incrementLineSize.height + trackInset.bottom,
377  slotSize = height - effectiveDecrementLineHeight - effectiveIncrementLineHeight,
378  minimumKnobLength = [self currentValueForThemeAttribute:"minimum-knob-length"],
379  knobVerticalInset = knobInset.top + knobInset.bottom,
380  knobWidth = width - knobInset.left - knobInset.right,
381  knobHeight = MAX(minimumKnobLength, ((slotSize - knobVerticalInset) * _knobProportion)),
382  knobLocation = effectiveDecrementLineHeight + (slotSize - knobHeight - knobVerticalInset) * [self floatValue] + knobInset.top;
383 
384  _partRects[CPScrollerDecrementPage] = CGRectMake(0.0, effectiveDecrementLineHeight, width, knobLocation - effectiveDecrementLineHeight);
385  _partRects[CPScrollerKnob] = CGRectMake(knobInset.left, knobLocation, knobWidth, knobHeight);
386  _partRects[CPScrollerIncrementPage] = CGRectMake(0.0, knobLocation + knobHeight, width, height - (knobLocation + knobHeight) - effectiveIncrementLineHeight);
387  _partRects[CPScrollerKnobSlot] = CGRectMake(trackInset.left, effectiveDecrementLineHeight, width - trackInset.left - trackInset.right, slotSize);
388  _partRects[CPScrollerDecrementLine] = CGRectMake(0.0, 0.0, decrementLineSize.width, decrementLineSize.height);
389  _partRects[CPScrollerIncrementLine] = CGRectMake(0.0, height - incrementLineSize.height, incrementLineSize.width, incrementLineSize.height);
390 
391  if (height < knobHeight + decrementLineSize.height + incrementLineSize.height + trackInset.top + trackInset.bottom)
392  _partRects[CPScrollerKnob] = CGRectMakeZero();
393 
394  if (height < decrementLineSize.height + incrementLineSize.height - 2)
395  {
396  _partRects[CPScrollerIncrementLine] = CGRectMakeZero();
397  _partRects[CPScrollerDecrementLine] = CGRectMakeZero();
398  _partRects[CPScrollerKnobSlot] = CGRectMake(trackInset.left, 0, width - trackInset.left - trackInset.right, height);
399  }
400  }
401  else
402  {
403  var decrementLineSize = [self currentValueForThemeAttribute:"decrement-line-size"],
404  incrementLineSize = [self currentValueForThemeAttribute:"increment-line-size"],
405  effectiveDecrementLineWidth = decrementLineSize.width + trackInset.left,
406  effectiveIncrementLineWidth = incrementLineSize.width + trackInset.right,
407  slotSize = width - effectiveDecrementLineWidth - effectiveIncrementLineWidth,
408  minimumKnobLength = [self currentValueForThemeAttribute:"minimum-knob-length"],
409  knobHorizontalInset = knobInset.left + knobInset.right,
410  knobWidth = MAX(minimumKnobLength, ((slotSize - knobHorizontalInset) * _knobProportion)),
411  knobHeight = height - knobInset.top - knobInset.bottom,
412  knobLocation = effectiveDecrementLineWidth + (slotSize - knobWidth - knobHorizontalInset) * [self floatValue] + knobInset.left;
413 
414  _partRects[CPScrollerDecrementPage] = CGRectMake(effectiveDecrementLineWidth, 0.0, knobLocation - effectiveDecrementLineWidth, height);
415  _partRects[CPScrollerKnob] = CGRectMake(knobLocation, knobInset.top, knobWidth, knobHeight);
416  _partRects[CPScrollerIncrementPage] = CGRectMake(knobLocation + knobWidth, 0.0, width - (knobLocation + knobWidth) - effectiveIncrementLineWidth, height);
417  _partRects[CPScrollerKnobSlot] = CGRectMake(effectiveDecrementLineWidth, trackInset.top, slotSize, height - trackInset.top - trackInset.bottom);
418  _partRects[CPScrollerDecrementLine] = CGRectMake(0.0, 0.0, decrementLineSize.width, decrementLineSize.height);
419  _partRects[CPScrollerIncrementLine] = CGRectMake(width - incrementLineSize.width, 0.0, incrementLineSize.width, incrementLineSize.height);
420 
421  if (width < knobWidth + decrementLineSize.width + incrementLineSize.width + trackInset.left + trackInset.right)
422  _partRects[CPScrollerKnob] = CGRectMakeZero();
423 
424  if (width < decrementLineSize.width + incrementLineSize.width - 2)
425  {
426  _partRects[CPScrollerIncrementLine] = CGRectMakeZero();
427  _partRects[CPScrollerDecrementLine] = CGRectMakeZero();
428  _partRects[CPScrollerKnobSlot] = CGRectMake(0.0, 0.0, width, slotSize);
429  }
430  }
431 }
432 
437 - (CPUsableScrollerParts)usableParts
438 {
439  return _usableParts;
440 }
441 
445 - (void)fadeIn
446 {
447  if (_isMouseOver && _knobProportion != 1.0)
448  [self setThemeState:CPThemeStateSelected];
449 
450  if (_timerFadeOut)
451  [_timerFadeOut invalidate];
452 
453  [self setAlphaValue:1.0];
454 }
455 
459 - (void)fadeOut
460 {
461  [_animationScroller startAnimation];
462 }
463 
464 
465 #pragma mark -
466 #pragma mark Drawing
467 
473 - (void)drawArrow:(CPScrollerArrow)anArrow highlight:(BOOL)shouldHighlight
474 {
475 }
476 
480 - (void)drawKnob
481 {
482 }
483 
487 - (void)drawKnobSlot
488 {
489 }
490 
491 - (CPView)createViewForPart:(CPScrollerPart)aPart
492 {
493  var view = [[CPView alloc] initWithFrame:CGRectMakeZero()];
494 
495  [view setHitTests:NO];
496 
497  return view;
498 }
499 
500 - (CGRect)rectForEphemeralSubviewNamed:(CPString)aName
501 {
502  return _partRects[aName];
503 }
504 
505 - (CPView)createEphemeralSubviewNamed:(CPString)aName
506 {
507  var view = [[CPView alloc] initWithFrame:CGRectMakeZero()];
508 
509  [view setHitTests:NO];
510 
511  return view;
512 }
513 
514 - (void)layoutSubviews
515 {
516  [self _adjustScrollerSize];
517  [self checkSpaceForParts];
518 
519  var index = 0,
520  count = PARTS_ARRANGEMENT.length,
521  view;
522 
523  for (; index < count; ++index)
524  {
525  var part = PARTS_ARRANGEMENT[index];
526 
527  if (index === 0)
528  view = [self layoutEphemeralSubviewNamed:part positioned:CPWindowBelow relativeToEphemeralSubviewNamed:PARTS_ARRANGEMENT[index + 1]];
529  else
530  view = [self layoutEphemeralSubviewNamed:part positioned:CPWindowAbove relativeToEphemeralSubviewNamed:PARTS_ARRANGEMENT[index - 1]];
531 
532  if (view)
533  [view setBackgroundColor:[self currentValueForThemeAttribute:NAMES_FOR_PARTS[part] + "-color"]];
534  }
535 }
536 
540 - (void)drawParts
541 {
542  [self drawKnobSlot];
543  [self drawKnob];
544  [self drawArrow:CPScrollerDecrementArrow highlight:NO];
545  [self drawArrow:CPScrollerIncrementArrow highlight:NO];
546 }
547 
548 // Event Handling
552 - (CPScrollerPart)hitPart
553 {
554  return _hitPart;
555 }
556 
561 - (void)trackKnob:(CPEvent)anEvent
562 {
563  var type = [anEvent type];
564 
565  if (type === CPLeftMouseUp)
566  {
567  _hitPart = CPScrollerNoPart;
568 
569  return;
570  }
571 
572  if (type === CPLeftMouseDown)
573  {
574  _trackingFloatValue = [self floatValue];
575  _trackingStartPoint = [self convertPoint:[anEvent locationInWindow] fromView:nil];
576  }
577 
578  else if (type === CPLeftMouseDragged)
579  {
580  var knobRect = [self rectForPart:CPScrollerKnob],
581  knobSlotRect = [self rectForPart:CPScrollerKnobSlot],
582  remainder = ![self isVertical] ? (CGRectGetWidth(knobSlotRect) - CGRectGetWidth(knobRect)) : (CGRectGetHeight(knobSlotRect) - CGRectGetHeight(knobRect));
583 
584  if (remainder <= 0)
585  [self setFloatValue:0.0];
586  else
587  {
588  var location = [self convertPoint:[anEvent locationInWindow] fromView:nil],
589  delta = ![self isVertical] ? location.x - _trackingStartPoint.x : location.y - _trackingStartPoint.y;
590 
591  [self setFloatValue:_trackingFloatValue + delta / remainder];
592  }
593  }
594 
595  [CPApp setTarget:self selector:@selector(trackKnob:) forNextEventMatchingMask:CPLeftMouseDraggedMask | CPLeftMouseUpMask untilDate:nil inMode:nil dequeue:YES];
596 
597  if (type === CPLeftMouseDragged)
598  [self sendAction:[self action] to:[self target]];
599 }
600 
605 - (void)trackScrollButtons:(CPEvent)anEvent
606 {
607  var type = [anEvent type];
608 
609  if (type === CPLeftMouseUp)
610  {
611  [self highlight:NO];
613 
614  _hitPart = CPScrollerNoPart;
615 
616  return;
617  }
618 
619  if (type === CPLeftMouseDown)
620  {
621  _trackingPart = [self hitPart];
622 
623  _trackingStartPoint = [self convertPoint:[anEvent locationInWindow] fromView:nil];
624 
625  if ([anEvent modifierFlags] & CPAlternateKeyMask)
626  {
627  if (_trackingPart === CPScrollerDecrementLine)
628  _hitPart = CPScrollerDecrementPage;
629 
630  else if (_trackingPart === CPScrollerIncrementLine)
631  _hitPart = CPScrollerIncrementPage;
632 
633  else if (_trackingPart === CPScrollerDecrementPage || _trackingPart === CPScrollerIncrementPage)
634  {
635  var knobRect = [self rectForPart:CPScrollerKnob],
636  knobWidth = ![self isVertical] ? CGRectGetWidth(knobRect) : CGRectGetHeight(knobRect),
637  knobSlotRect = [self rectForPart:CPScrollerKnobSlot],
638  remainder = (![self isVertical] ? CGRectGetWidth(knobSlotRect) : CGRectGetHeight(knobSlotRect)) - knobWidth;
639 
640  [self setFloatValue:((![self isVertical] ? _trackingStartPoint.x - CGRectGetMinX(knobSlotRect) : _trackingStartPoint.y - CGRectGetMinY(knobSlotRect)) - knobWidth / 2.0) / remainder];
641 
642  _hitPart = CPScrollerKnob;
643 
644  [self sendAction:[self action] to:[self target]];
645 
646  // Now just track the knob.
647  return [self trackKnob:anEvent];
648  }
649  }
650 
651  [self highlight:YES];
652  [self sendAction:[self action] to:[self target]];
653 
655  }
656 
657  else if (type === CPLeftMouseDragged)
658  {
659  _trackingStartPoint = [self convertPoint:[anEvent locationInWindow] fromView:nil];
660 
661  if (_trackingPart === CPScrollerDecrementPage || _trackingPart === CPScrollerIncrementPage)
662  {
663  var hitPart = [self testPart:[anEvent locationInWindow]];
664 
665  if (hitPart === CPScrollerDecrementPage || hitPart === CPScrollerIncrementPage)
666  {
667  _trackingPart = hitPart;
668  _hitPart = hitPart;
669  }
670  }
671 
672  [self highlight:CGRectContainsPoint([self rectForPart:_trackingPart], _trackingStartPoint)];
673  }
674  else if (type == CPPeriodic && CGRectContainsPoint([self rectForPart:_trackingPart], _trackingStartPoint))
675  [self sendAction:[self action] to:[self target]];
676 
677  [CPApp setTarget:self selector:@selector(trackScrollButtons:) forNextEventMatchingMask:CPPeriodicMask | CPLeftMouseDraggedMask | CPLeftMouseUpMask untilDate:nil inMode:nil dequeue:YES];
678 
679 }
680 
681 - (void)_setIsVertical:(BOOL)isVertical
682 {
683  _isVertical = isVertical;
684 
685  if (_isVertical)
686  [self setThemeState:CPThemeStateVertical];
687  else
688  [self unsetThemeState:CPThemeStateVertical];
689 }
690 
691 - (void)setFrameSize:(CGSize)aSize
692 {
693  [super setFrameSize:aSize];
694 
695  [self checkSpaceForParts];
696  [self setNeedsLayout];
697 }
698 
699 
700 #pragma mark -
701 #pragma mark Overrides
702 
703 - (id)currentValueForThemeAttribute:(CPString)anAttributeName
704 {
705  var themeState = _themeState;
706 
707  if (NAMES_FOR_PARTS[_hitPart] + "-color" !== anAttributeName)
708  themeState = themeState.without(CPThemeStateHighlighted);
709 
710  return [self valueForThemeAttribute:anAttributeName inState:themeState];
711 }
712 
713 - (void)mouseDown:(CPEvent)anEvent
714 {
715  if (![self isEnabled])
716  return;
717 
718  _hitPart = [self testPart:[anEvent locationInWindow]];
719 
720  switch (_hitPart)
721  {
722  case CPScrollerKnob:
723  return [self trackKnob:anEvent];
724 
729  return [self trackScrollButtons:anEvent];
730  }
731 }
732 
733 - (void)mouseEntered:(CPEvent)anEvent
734 {
735  [super mouseEntered:anEvent];
736 
737  if (_timerFadeOut)
738  [_timerFadeOut invalidate];
739 
740  if (![self isEnabled])
741  return;
742 
743  _allowFadingOut = NO;
744  _isMouseOver = YES;
745 
746  if ([self alphaValue] > 0 && _knobProportion != 1.0)
747  [self setThemeState:CPThemeStateSelected];
748 }
749 
750 - (void)mouseExited:(CPEvent)anEvent
751 {
752  [super mouseExited:anEvent];
753 
754  if ([self isHidden] || ![self isEnabled] || !_isMouseOver)
755  return;
756 
757  _allowFadingOut = (_style !== CPScrollerStyleLegacy);
758  _isMouseOver = NO;
759 
760  if (_timerFadeOut)
761  [_timerFadeOut invalidate];
762 
763  if ([self hasThemeState:CPThemeStateScrollViewLegacy])
764  [self unsetThemeState:CPThemeStateSelected];
765  else
766  _timerFadeOut = [CPTimer scheduledTimerWithTimeInterval:1.2 target:self selector:@selector(_performFadeOut:) userInfo:nil repeats:NO];
767 }
768 
769 - (float)scrollerWidth
770 {
771  if (_style == CPScrollerStyleLegacy)
772  return [self valueForThemeAttribute:@"scroller-width" inState:CPThemeStateScrollViewLegacy];
773 
774  return [self currentValueForThemeAttribute:@"scroller-width"];
775 }
776 
777 #pragma mark -
778 #pragma mark Delegates
779 
780 - (void)animationDidEnd:(CPAnimation)animation
781 {
782  [self unsetThemeState:CPThemeStateSelected];
783 }
784 
785 @end
786 
787 var CPScrollerIsVerticalKey = @"CPScrollerIsVerticalKey",
788  CPScrollerKnobProportionKey = @"CPScrollerKnobProportion",
789  CPScrollerStyleKey = @"CPScrollerStyleKey";
790 
791 @implementation CPScroller (CPCoding)
792 
793 - (id)initWithCoder:(CPCoder)aCoder
794 {
795  if (self = [super initWithCoder:aCoder])
796  {
797  _knobProportion = 1.0;
798 
799  if ([aCoder containsValueForKey:CPScrollerKnobProportionKey])
800  _knobProportion = [aCoder decodeFloatForKey:CPScrollerKnobProportionKey];
801 
802  _partRects = [];
803 
804  _hitPart = CPScrollerNoPart;
805 
806  _allowFadingOut = YES;
807  _isMouseOver = NO;
808 
809  var paramAnimFadeOut = @{
812  };
813 
814  _animationScroller = [[CPViewAnimation alloc] initWithDuration:0.2 animationCurve:CPAnimationEaseInOut];
815  [_animationScroller setViewAnimations:[paramAnimFadeOut]];
816  [_animationScroller setDelegate:self];
817  [self setAlphaValue:0.0];
818 
819  [self setStyle:[aCoder decodeIntForKey:CPScrollerStyleKey]];
820 
821  [self _setIsVertical:[aCoder decodeBoolForKey:CPScrollerIsVerticalKey]];
822  }
823 
824  return self;
825 }
826 
827 - (void)encodeWithCoder:(CPCoder)aCoder
828 {
829  [super encodeWithCoder:aCoder];
830 
831  [aCoder encodeInt:_isVertical forKey:CPScrollerIsVerticalKey];
832  [aCoder encodeFloat:_knobProportion forKey:CPScrollerKnobProportionKey];
833  [aCoder encodeInt:_style forKey:CPScrollerStyleKey];
834 }
835 
836 @end
837 
838 @implementation CPScroller (Deprecated)
839 
845 - (void)setFloatValue:(float)aValue knobProportion:(float)aProportion
846 {
847  [self setFloatValue:aValue];
848  [self setKnobProportion:aProportion];
849 }
850 
851 @end
852 
854 
858 - (BOOL)isVertical
859 {
860  return _isVertical;
861 }
862 
866 - (BOOL)allowFadingOut
867 {
868  return _allowFadingOut;
869 }
870 
871 @end
var PARTS_FOR_NAMES
Definition: CPScroller.j:51
Used to implement exception handling (creating & raising).
Definition: CPException.h:2
CPThemeStateSelected
Definition: CPTheme.j:613
BOOL isHidden()
Definition: CALayer.j:597
float scrollerWidthInStyle:(int aStyle)
Definition: CPScroller.j:132
void trackScrollButtons:(CPEvent anEvent)
Definition: CPScroller.j:605
CPAllScrollerParts
Definition: CPScroller.j:47
BOOL setThemeState:(ThemeState aState)
Definition: CPView.j:3255
CGRect frame
An object representation of nil.
Definition: CPNull.h:2
float scrollerWidth()
Definition: CPScroller.j:124
CPViewAnimationFadeOutEffect
void mouseExited:(CPEvent anEvent)
Definition: CPControl.j:512
CPViewAnimationTargetKey
CPScrollerKnob
Definition: CPScroller.j:35
CGPoint locationInWindow()
Definition: CPEvent.j:290
CGRect rectForPart:(CPScrollerPart aPart)
Definition: CPScroller.j:295
int width
CPScrollerIncrementLine
Definition: CPScroller.j:38
CGRect bounds()
Definition: CPView.j:1326
CPScrollerDecrementPage
Definition: CPScroller.j:34
void raise:reason:(CPString aName, [reason] CPString aReason)
Definition: CPException.j:66
CPEventType type()
Definition: CPEvent.j:325
A mutable key-value pair collection.
Definition: CPDictionary.h:2
CPScrollerDecrementArrow
Definition: CPScroller.j:42
CGRect bounds()
Definition: CALayer.j:203
CPAlternateKeyMask
CPScrollerPart testPart:(CGPoint aPoint)
Definition: CPScroller.j:308
An immutable string (collection of characters).
Definition: CPString.h:2
CPNull null()
Definition: CPNull.j:51
CPOnlyScrollerArrows
Definition: CPScroller.j:46
CGPoint convertPoint:fromView:(CGPoint aPoint, [fromView] CPView aView)
Definition: CPView.j:2249
void stopPeriodicEvents()
Definition: CPEvent.j:598
void setStyle:(id aStyle)
Definition: CPScroller.j:215
CPScrollerKnobStyleDark
Definition: CPScroller.j:65
CPUsableScrollerParts CPNoScrollerParts
Definition: CPScroller.j:45
id initWithDuration:animationCurve:(float aDuration, [animationCurve] CPAnimationCurve anAnimationCurve)
Definition: CPAnimation.j:100
BOOL sendAction:to:(SEL anAction, [to] id anObject)
Definition: CPControl.j:319
CPScrollerIncrementArrow
Definition: CPScroller.j:41
SEL action()
Definition: CPControl.j:290
BOOL isVertical()
Definition: CPScroller.j:858
void setFloatValue:(float aValue)
Definition: CPControl.j:554
var CPScrollerKnobProportionKey
Definition: CPScroller.j:788
void setNeedsDisplay:(BOOL aFlag)
Definition: CPView.j:2597
void setObjectValue:(id anObject)
Definition: CPControl.j:534
CPScrollerPart hitPart()
Definition: CPScroller.j:552
CPLeftMouseUp
CPScrollerDecrementLine
Definition: CPScroller.j:37
void setKnobProportion:(float aProportion)
Definition: CPScroller.j:254
CPTimer scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:(CPTimeInterval seconds, [target] id aTarget, [selector] SEL aSelector, [userInfo] id userInfo, [repeats] BOOL shouldRepeat)
Definition: CPTimer.j:58
CPScrollerStyleLegacy
Definition: CPScroller.j:61
void setAlphaValue:(float anAlphaValue)
Definition: CPView.j:1748
void setNeedsLayout()
Definition: CPView.j:2748
void fadeIn()
Definition: CPScroller.j:445
CPThemeStateScrollerKnobLight
Definition: CPScroller.j:69
id target()
Definition: CPControl.j:308
void drawArrow:highlight:(CPScrollerArrow anArrow, [highlight] BOOL shouldHighlight)
Definition: CPScroller.j:473
global CPApp typedef CPScrollerPart CPScrollerNoPart
Definition: CPScroller.j:33
void highlight:(BOOL shouldHighlight)
Definition: CPControl.j:980
void trackKnob:(CPEvent anEvent)
Definition: CPScroller.j:561
CPScrollerKnobStyleLight
Definition: CPScroller.j:66
float floatValue()
Definition: CPControl.j:545
CPScrollerKnobSlot
Definition: CPScroller.j:39
A timer object that can send a message after the given time interval.
Definition: CPTimer.h:2
CPLeftMouseDragged
Defines methods for use when archiving & restoring (enc/decoding).
Definition: CPCoder.h:2
CPThemeStateHighlighted
Definition: CPTheme.j:612
void mouseEntered:(CPEvent anEvent)
Definition: CPControl.j:504
BOOL unsetThemeState:(ThemeState aState)
Definition: CPView.j:3268
CPScrollerIncrementPage
Definition: CPScroller.j:36
var PARTS_ARRANGEMENT
Definition: CPScroller.j:49
CPLeftMouseDown
void setFrameSize:(CGSize aSize)
Definition: CPView.j:1124
void drawKnobSlot()
Definition: CPScroller.j:487
CPThemeStateScrollViewLegacy
Definition: CPScroller.j:68
CPThemeStateScrollerKnobDark
Definition: CPScroller.j:70
Definition: CPEvent.h:2
void startPeriodicEventsAfterDelay:withPeriod:(CPTimeInterval aDelay, [withPeriod] CPTimeInterval aPeriod)
Definition: CPEvent.j:587
CPScrollerStyleOverlay
Definition: CPScroller.j:62
var CPScrollerStyleKey
Definition: CPScroller.j:789
void checkSpaceForParts()
Definition: CPScroller.j:342
var NAMES_FOR_PARTS
Definition: CPScroller.j:50
void drawKnob()
Definition: CPScroller.j:480
void encodeWithCoder:(CPCoder aCoder)
Definition: CPControl.j:1121
CPScrollerKnobStyleDefault
Definition: CPScroller.j:64
var CPScrollerIsVerticalKey
Definition: CPScroller.j:787
CPViewAnimationEffectKey
CPView layoutEphemeralSubviewNamed:positioned:relativeToEphemeralSubviewNamed:(CPString aViewName, [positioned] CPWindowOrderingMode anOrderingMode, [relativeToEphemeralSubviewNamed] CPString relativeToViewName)
Definition: CPView.j:3407
id alloc()
Definition: CPObject.j:130
Definition: CPView.j:137
id currentValueForThemeAttribute:(CPString anAttributeName)
Definition: CPScroller.j:703
CPPeriodic