Files
cappuccino/AppKit/CPScroller.j
T

693 lines
27 KiB
Plaintext

/*
* CPScroller.j
* AppKit
*
* Created by Francisco Tolmasky.
* Copyright 2008, 280 North, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
import "CPControl.j"
#include "CoreGraphics/CGGeometry.h"
// CPScroller Constants
CPScrollerNoPart = 0;
CPScrollerDecrementPage = 1;
CPScrollerKnob = 2;
CPScrollerIncrementPage = 3;
CPScrollerDecrementLine = 4;
CPScrollerIncrementLine = 5;
CPScrollerKnobSlot = 6;
CPScrollerIncrementArrow = 0;
CPScrollerDecrementArrow = 1;
CPNoScrollerParts = 0;
CPOnlyScrollerArrows = 1;
CPAllScrollerParts = 2;
var _CPScrollerWidths = [],
_CPScrollerKnobMinimumHeights = [],
_CPScrollerArrowHeights = [],
_CPScrollerArrowUsableHeights = [];
_CPScrollerWidths[CPRegularControlSize] = 14.0;
_CPScrollerWidths[CPSmallControlSize] = 11.0;
_CPScrollerWidths[CPMiniControlSize] = 11.0; // FIXME
_CPScrollerKnobMinimumHeights[CPRegularControlSize] = 18.0;
_CPScrollerKnobMinimumHeights[CPSmallControlSize] = 12.0;
_CPScrollerKnobMinimumHeights[CPMiniControlSize] = 12.0; // FIXME
_CPScrollerArrowHeights[CPRegularControlSize] = 21.0;
_CPScrollerArrowHeights[CPSmallControlSize] = 16.0;
_CPScrollerArrowHeights[CPMiniControlSize] = 16.0; // FIXME
_CPScrollerArrowUsableHeights[CPRegularControlSize] = 16.0
_CPScrollerArrowUsableHeights[CPSmallControlSize] = 10.0;
_CPScrollerArrowUsableHeights[CPMiniControlSize] = 10.0; // FIXME
var _CPScrollerKnobIdentifier = @"Knob",
_CPScrollerKnobSlotIdentifier = @"KnobSlot",
_CPScrollerDecrementArrowIdentifier = @"DecrementArrow",
_CPScrollerIncrementArrowIdentifier = @"IncrementArrow",
_CPScrollerHorizontalIdentifier = @"Horizontal",
_CPScrollerVerticalIdentifier = @"Vertical",
_CPScrollerHighlightedIdentifier = @"Highlighted",
_CPScrollerDisabledIdentifier = @"Disabled";
var _CPScrollerClassName = nil,
_CPScrollerPartSizes = {};
@implementation CPScroller : CPControl
{
CPControlSize _controlSize;
CPUsableScrollerParts _usableParts;
CPArray _partRects;
BOOL _isHorizontal;
float _knobProportion;
CPScrollerPart _hitPart;
CPScrollerPart _trackingPart;
float _trackingFloatValue;
CGPoint _trackingStartPoint;
CPView _knobView;
CPView _knobSlotView;
CPView _decrementArrowView;
CPView _incrementArrowView;
}
/*
@ignore
*/
+ (void)initialize
{
if (self != [CPScroller class])
return;
_CPScrollerClassName = [self className];
var regularIdentifier = _CPControlIdentifierForControlSize(CPRegularControlSize),
smallIdentifier = _CPControlIdentifierForControlSize(CPSmallControlSize),
miniIdentifier = _CPControlIdentifierForControlSize(CPMiniControlSize);
// Horizontal Knob Sizes
var prefix = _CPScrollerClassName + _CPScrollerKnobIdentifier + _CPScrollerHorizontalIdentifier;
_CPScrollerPartSizes[prefix + regularIdentifier] = [_CGSizeMake(9.0, _CPScrollerWidths[CPRegularControlSize]), _CGSizeMake(1.0, _CPScrollerWidths[CPRegularControlSize]), _CGSizeMake(9.0, _CPScrollerWidths[CPRegularControlSize])];
_CPScrollerPartSizes[prefix + smallIdentifier] = [_CGSizeMake(6.0, _CPScrollerWidths[CPSmallControlSize]), _CGSizeMake(1.0, _CPScrollerWidths[CPSmallControlSize]), _CGSizeMake(6.0, _CPScrollerWidths[CPSmallControlSize])];
_CPScrollerPartSizes[prefix + miniIdentifier] = [_CGSizeMake(6.0, _CPScrollerWidths[CPMiniControlSize]), _CGSizeMake(1.0, _CPScrollerWidths[CPMiniControlSize]), _CGSizeMake(6.0, _CPScrollerWidths[CPMiniControlSize])];
// Vertical Knob Sizes
var prefix = _CPScrollerClassName + _CPScrollerKnobIdentifier + _CPScrollerVerticalIdentifier;
_CPScrollerPartSizes[prefix + regularIdentifier] = [_CGSizeMake(_CPScrollerWidths[CPRegularControlSize], 9.0), _CGSizeMake(_CPScrollerWidths[CPRegularControlSize], 1.0), _CGSizeMake(_CPScrollerWidths[CPRegularControlSize], 9.0)];
_CPScrollerPartSizes[prefix + smallIdentifier] = [_CGSizeMake(_CPScrollerWidths[CPSmallControlSize], 6.0), _CGSizeMake(_CPScrollerWidths[CPSmallControlSize], 1.0), _CGSizeMake(_CPScrollerWidths[CPSmallControlSize], 6.0)];
_CPScrollerPartSizes[prefix + miniIdentifier] = [_CGSizeMake(_CPScrollerWidths[CPMiniControlSize], 6.0), _CGSizeMake(_CPScrollerWidths[CPMiniControlSize], 1.0), _CGSizeMake(_CPScrollerWidths[CPMiniControlSize], 6.0)];
// Horizontal Knob Slot Sizes
var prefix = _CPScrollerClassName + _CPScrollerKnobSlotIdentifier + _CPScrollerHorizontalIdentifier;
_CPScrollerPartSizes[prefix + regularIdentifier] = _CGSizeMake(1.0, _CPScrollerWidths[CPRegularControlSize]);
_CPScrollerPartSizes[prefix + smallIdentifier] = _CGSizeMake(1.0, _CPScrollerWidths[CPSmallControlSize]);
_CPScrollerPartSizes[prefix + miniIdentifier] = _CGSizeMake(1.0, _CPScrollerWidths[CPMiniControlSize]);
// Vertical Knob Slot Sizes
var prefix = _CPScrollerClassName + _CPScrollerKnobSlotIdentifier + _CPScrollerVerticalIdentifier;
_CPScrollerPartSizes[prefix + regularIdentifier] = _CGSizeMake(_CPScrollerWidths[CPRegularControlSize], 1.0);
_CPScrollerPartSizes[prefix + smallIdentifier] = _CGSizeMake(_CPScrollerWidths[CPSmallControlSize], 1.0);
_CPScrollerPartSizes[prefix + miniIdentifier] = _CGSizeMake(_CPScrollerWidths[CPMiniControlSize], 1.0);
// Horizontal Decrement Arrows Sizes
var prefix = _CPScrollerClassName + _CPScrollerDecrementArrowIdentifier + _CPScrollerHorizontalIdentifier;
_CPScrollerPartSizes[prefix + regularIdentifier] = _CGSizeMake(_CPScrollerArrowHeights[CPRegularControlSize], _CPScrollerWidths[CPRegularControlSize]);
_CPScrollerPartSizes[prefix + regularIdentifier + _CPScrollerHighlightedIdentifier] = _CGSizeMake(_CPScrollerArrowHeights[CPRegularControlSize], _CPScrollerWidths[CPRegularControlSize]);
_CPScrollerPartSizes[prefix + smallIdentifier] = _CGSizeMake(_CPScrollerArrowHeights[CPSmallControlSize] , _CPScrollerWidths[CPSmallControlSize]);
_CPScrollerPartSizes[prefix + smallIdentifier + _CPScrollerHighlightedIdentifier] = _CGSizeMake(_CPScrollerArrowHeights[CPSmallControlSize] , _CPScrollerWidths[CPSmallControlSize]);
_CPScrollerPartSizes[prefix + miniIdentifier] = _CGSizeMake(_CPScrollerArrowHeights[CPMiniControlSize], _CPScrollerWidths[CPMiniControlSize]);
_CPScrollerPartSizes[prefix + miniIdentifier + _CPScrollerHighlightedIdentifier] = _CGSizeMake(_CPScrollerArrowHeights[CPMiniControlSize], _CPScrollerWidths[CPMiniControlSize]);
// Vertical Decrement Arrows Sizes
var prefix = _CPScrollerClassName + _CPScrollerDecrementArrowIdentifier + _CPScrollerVerticalIdentifier;
_CPScrollerPartSizes[prefix + regularIdentifier] = _CGSizeMake(_CPScrollerWidths[CPRegularControlSize], _CPScrollerArrowHeights[CPRegularControlSize]);
_CPScrollerPartSizes[prefix + regularIdentifier + _CPScrollerHighlightedIdentifier] = _CGSizeMake(_CPScrollerWidths[CPRegularControlSize], _CPScrollerArrowHeights[CPRegularControlSize]);
_CPScrollerPartSizes[prefix + smallIdentifier] = _CGSizeMake(_CPScrollerWidths[CPSmallControlSize], _CPScrollerArrowHeights[CPSmallControlSize]);
_CPScrollerPartSizes[prefix + smallIdentifier + _CPScrollerHighlightedIdentifier] = _CGSizeMake(_CPScrollerWidths[CPSmallControlSize], _CPScrollerArrowHeights[CPSmallControlSize]);
_CPScrollerPartSizes[prefix + miniIdentifier] = _CGSizeMake(_CPScrollerWidths[CPMiniControlSize], _CPScrollerArrowHeights[CPMiniControlSize]);
_CPScrollerPartSizes[prefix + miniIdentifier + _CPScrollerHighlightedIdentifier] = _CGSizeMake(_CPScrollerWidths[CPMiniControlSize], _CPScrollerArrowHeights[CPMiniControlSize]);
// Horizontal Increment Arrows Sizes
var prefix = _CPScrollerClassName + _CPScrollerIncrementArrowIdentifier + _CPScrollerHorizontalIdentifier;
_CPScrollerPartSizes[prefix + regularIdentifier] = _CGSizeMake(_CPScrollerArrowHeights[CPRegularControlSize], _CPScrollerWidths[CPRegularControlSize]);
_CPScrollerPartSizes[prefix + regularIdentifier + _CPScrollerHighlightedIdentifier] = _CGSizeMake(_CPScrollerArrowHeights[CPRegularControlSize], _CPScrollerWidths[CPRegularControlSize]);
_CPScrollerPartSizes[prefix + smallIdentifier] = _CGSizeMake(_CPScrollerArrowHeights[CPSmallControlSize], _CPScrollerWidths[CPSmallControlSize]);
_CPScrollerPartSizes[prefix + smallIdentifier + _CPScrollerHighlightedIdentifier] = _CGSizeMake(_CPScrollerArrowHeights[CPSmallControlSize], _CPScrollerWidths[CPSmallControlSize]);
_CPScrollerPartSizes[prefix + miniIdentifier] = _CGSizeMake(_CPScrollerArrowHeights[CPMiniControlSize], _CPScrollerWidths[CPMiniControlSize]);
_CPScrollerPartSizes[prefix + miniIdentifier + _CPScrollerHighlightedIdentifier] = _CGSizeMake(_CPScrollerArrowHeights[CPMiniControlSize], _CPScrollerWidths[CPMiniControlSize]);
// Vertical Increment Arrows Sizes
var prefix = _CPScrollerClassName + _CPScrollerIncrementArrowIdentifier + _CPScrollerVerticalIdentifier;
_CPScrollerPartSizes[prefix + regularIdentifier] = _CGSizeMake(_CPScrollerWidths[CPRegularControlSize], _CPScrollerArrowHeights[CPRegularControlSize]);
_CPScrollerPartSizes[prefix + regularIdentifier + _CPScrollerHighlightedIdentifier] = _CGSizeMake(_CPScrollerWidths[CPRegularControlSize], _CPScrollerArrowHeights[CPRegularControlSize]);
_CPScrollerPartSizes[prefix + smallIdentifier] = _CGSizeMake(_CPScrollerWidths[CPSmallControlSize], _CPScrollerArrowHeights[CPSmallControlSize]);
_CPScrollerPartSizes[prefix + smallIdentifier + _CPScrollerHighlightedIdentifier] = _CGSizeMake(_CPScrollerWidths[CPSmallControlSize], _CPScrollerArrowHeights[CPSmallControlSize]);
_CPScrollerPartSizes[prefix + miniIdentifier] = _CGSizeMake(_CPScrollerWidths[CPMiniControlSize], _CPScrollerArrowHeights[CPMiniControlSize]);
_CPScrollerPartSizes[prefix + miniIdentifier + _CPScrollerHighlightedIdentifier] = _CGSizeMake(_CPScrollerWidths[CPMiniControlSize], _CPScrollerArrowHeights[CPMiniControlSize]);
}
// Calculating Layout
- (id)initWithFrame:(CGRect)aFrame
{
self = [super initWithFrame:aFrame];
if (self)
{
_controlSize = CPRegularControlSize;
_partRects = [];
[self setFloatValue:0.0 knobProportion:1.0];
_isHorizontal = CPRectGetWidth(aFrame) > CPRectGetHeight(aFrame);
_hitPart = CPScrollerNoPart;
[self checkSpaceForParts];
[self drawParts];
[self layoutSubviews];
}
return self;
}
// Determining CPScroller Size
/*
Returns the <objj>CPScroller</objj>'s width for a <objj>CPRegularControlSize</objj>.
*/
+ (float)scrollerWidth
{
return [self scrollerWidthForControlSize:CPRegularControlSize];
}
/*
Returns the width of a <objj>CPScroller</objj> for the specified <objj>CPControlSize</objj>.
@param aControlSize the size of a controller to return the width for
*/
+ (float)scrollerWidthForControlSize:(CPControlSize)aControlSize
{
return _CPScrollerWidths[aControlSize];
}
/*
Sets the scroller's size.
@param aControlSize the scroller's size
*/
- (void)setControlSize:(CPControlSize)aControlSize
{
if (_controlSize == aControlSize)
return;
_controlSize = aControlSize;
[self drawKnobSlot];
[self drawKnob];
[self drawArrow:CPScrollerDecrementArrow highlight:NO];
[self drawArrow:CPScrollerIncrementArrow highlight:NO];
[self layoutSubviews];
}
/*
Returns the scroller's control size
*/
- (CPControlSize)controlSize
{
return _controlSize;
}
// Setting the Knob Position
/*
Sets the scroller's knob position (ranges from 0.0 to 1.0).
@param aValue the knob position (ranges from 0.0 to 1.0)
*/
- (void)setFloatValue:(float)aValue
{
[super setFloatValue:MIN(1.0, MAX(0.0, aValue))];
[self checkSpaceForParts];
[self layoutSubviews];
}
/*
Sets the position and proportion of the knob.
@param aValue the knob position (ranges from 0.0 to 1.0)
@param aProportion the knob's proportion (ranges from 0.0 to 1.0)
*/
- (void)setFloatValue:(float)aValue knobProportion:(float)aProportion
{
_knobProportion = MIN(1.0, MAX(0.0001, aProportion));
[self setFloatValue:aValue];
}
/*
Return's the knob's proportion
*/
- (float)knobProportion
{
return _knobProportion;
}
// Calculating Layout
- (CGRect)rectForPart:(CPScrollerPart)aPart
{
if (aPart == CPScrollerNoPart)
return _CGRectMakeZero();
return _partRects[aPart];
}
/*
Returns the part of the scroller that would be hit by <code>aPoint</code>.
@param aPoint the simulated point hit
@return the part of the scroller that intersects the point
*/
- (CPScrollerPart)testPart:(CGPoint)aPoint
{
aPoint = [self convertPoint:aPoint fromView:nil];
// The ordering of these tests is important. We check the knob and
// page rects first since they may overlap with the arrows.
if (CGRectContainsPoint([self rectForPart:CPScrollerKnob], aPoint))
return CPScrollerKnob;
if (CGRectContainsPoint([self rectForPart:CPScrollerDecrementPage], aPoint))
return CPScrollerDecrementPage;
if (CGRectContainsPoint([self rectForPart:CPScrollerIncrementPage], aPoint))
return CPScrollerIncrementPage;
if (CGRectContainsPoint([self rectForPart:CPScrollerDecrementLine], aPoint))
return CPScrollerDecrementLine;
if (CGRectContainsPoint([self rectForPart:CPScrollerIncrementLine], aPoint))
return CPScrollerIncrementLine;
if (CGRectContainsPoint([self rectForPart:CPScrollerKnobSlot], aPoint))
return CPScrollerKnobSlot;
return CPScrollerNoPart;
}
/*
Check if there's enough space in the scroller to display the knob
*/
- (void)checkSpaceForParts
{
var bounds = [self bounds];
// Assume we won't be needing the arrows.
if (_knobProportion == 1.0)
{
_usableParts = CPNoScrollerParts;
_partRects[CPScrollerDecrementPage] = _CGRectMakeZero();
_partRects[CPScrollerKnob] = _CGRectMakeZero();
_partRects[CPScrollerIncrementPage] = _CGRectMakeZero();
_partRects[CPScrollerDecrementLine] = _CGRectMakeZero();
_partRects[CPScrollerIncrementLine] = _CGRectMakeZero();
// In this case, the slot is the entirety of the scroller.
_partRects[CPScrollerKnobSlot] = _CGRectMakeCopy(bounds);
return;
}
var width = _CGRectGetWidth(bounds),
height = _CGRectGetHeight(bounds),
usableArrowHeight = _CPScrollerArrowUsableHeights[_controlSize],
slotWidth = (_isHorizontal ? width : height) - 2.0 * usableArrowHeight,
knobWidth = MAX(_CPScrollerKnobMinimumHeights[_controlSize], (slotWidth * _knobProportion));
_usableParts = CPAllScrollerParts;
var arrowHeight = _CPScrollerArrowHeights[_controlSize],
knobLocation = usableArrowHeight + (slotWidth - knobWidth) * [self floatValue];
// At this point we know we're going to need arrows.
if (_isHorizontal)
{
// ASSERT(_CPScrollerWidths[_controlSize] == height)
_partRects[CPScrollerDecrementPage] = _CGRectMake(usableArrowHeight, 0.0, knobLocation - usableArrowHeight, height);
_partRects[CPScrollerKnob] = _CGRectMake(knobLocation, 0.0, knobWidth, _CPScrollerWidths[_controlSize]);
_partRects[CPScrollerIncrementPage] = _CGRectMake(knobLocation + knobWidth, 0.0, width - (knobLocation + knobWidth) - usableArrowHeight, height);
_partRects[CPScrollerKnobSlot] = _CGRectMake(usableArrowHeight, 0.0, slotWidth, height);
_partRects[CPScrollerDecrementLine] = _CGRectMake(0.0, 0.0, arrowHeight, height);
_partRects[CPScrollerIncrementLine] = _CGRectMake(width - _CPScrollerArrowHeights[_controlSize], 0.0, arrowHeight, height);
}
else
{
// ASSERT(_CPScrollerWidths[_controlSize] == width)
_partRects[CPScrollerDecrementPage] = _CGRectMake(0.0, usableArrowHeight, width, knobLocation - usableArrowHeight);
_partRects[CPScrollerKnob] = _CGRectMake(0.0, knobLocation, _CPScrollerWidths[_controlSize], knobWidth);
_partRects[CPScrollerIncrementPage] = _CGRectMake(0.0, knobLocation + knobWidth, width, height - (knobLocation + knobWidth) - usableArrowHeight);
_partRects[CPScrollerKnobSlot] = _CGRectMake(0.0, usableArrowHeight, width, slotWidth);
_partRects[CPScrollerDecrementLine] = _CGRectMake(0.0, 0.0, width, arrowHeight);
_partRects[CPScrollerIncrementLine] = _CGRectMake(0.0, height - _CPScrollerArrowHeights[_controlSize], width, arrowHeight);
}
}
/*
Returns all the parts of the scroller that
are usable for displaying.
*/
- (CPUsableScrollerParts)usableParts
{
return _usableParts;
}
// Drawing the Parts
/*
Draws the specified arrow and sets the highlight.
@param anArrow the arrow to draw
@param shouldHighlight sets whether the arrow should be highlighted
*/
- (void)drawArrow:(CPScrollerArrow)anArrow highlight:(BOOL)shouldHighlight
{
var identifier = (anArrow == CPScrollerDecrementArrow ? _CPScrollerDecrementArrowIdentifier : _CPScrollerIncrementArrowIdentifier),
arrowView = (anArrow == CPScrollerDecrementArrow ? _decrementArrowView : _incrementArrowView);
[arrowView setBackgroundColor:_CPControlColorWithPatternImage(
_CPScrollerPartSizes,
_CPScrollerClassName,
identifier,
_isHorizontal ? _CPScrollerHorizontalIdentifier : _CPScrollerVerticalIdentifier,
_CPControlIdentifierForControlSize(_controlSize),
shouldHighlight ? _CPScrollerHighlightedIdentifier : @"")];
}
/*
Draws the knob
*/
- (void)drawKnob
{
[_knobView setBackgroundColor:_CPControlThreePartImagePattern(
!_isHorizontal,
_CPScrollerPartSizes,
_CPScrollerClassName,
_CPScrollerKnobIdentifier,
_isHorizontal ? _CPScrollerHorizontalIdentifier : _CPScrollerVerticalIdentifier,
_CPControlIdentifierForControlSize(_controlSize))];
}
/*
Draws the knob's slot
*/
- (void)drawKnobSlot
{
[_knobSlotView setBackgroundColor:_CPControlColorWithPatternImage(
_CPScrollerPartSizes,
_CPScrollerClassName,
_CPScrollerKnobSlotIdentifier,
_isHorizontal ? _CPScrollerHorizontalIdentifier : _CPScrollerVerticalIdentifier,
_CPControlIdentifierForControlSize(_controlSize))];
}
/*
Caches images for the scroll arrow and knob.
*/
- (void)drawParts
{
_knobSlotView = [[CPView alloc] initWithFrame:_CGRectMakeZero()];
[_knobSlotView setHitTests:NO];
[self addSubview:_knobSlotView];
[self drawKnobSlot];
_knobView = [[CPView alloc] initWithFrame:_CGRectMakeZero()];
[_knobView setHitTests:NO];
[self addSubview:_knobView];
[self drawKnob];
_decrementArrowView = [[CPView alloc] initWithFrame:_CGRectMakeZero()];
[_decrementArrowView setHitTests:NO];
[self addSubview:_decrementArrowView];
[self drawArrow:CPScrollerDecrementArrow highlight:NO];
_incrementArrowView = [[CPView alloc] initWithFrame:_CGRectMakeZero()];
[_incrementArrowView setHitTests:NO];
[self addSubview:_incrementArrowView];
[self drawArrow:CPScrollerIncrementArrow highlight:NO];
}
/*
Draws the scroller's arrow with a possible highlight,
if the user's mouse is over it.
@param shouldHighlight <code>YES</code> will draw the
arrow highlighted if the mouse is hovering over it.
*/
- (void)highlight:(BOOL)shouldHighlight
{
if (_trackingPart == CPScrollerDecrementLine)
[self drawArrow:CPScrollerDecrementArrow highlight:shouldHighlight];
else if (_trackingPart == CPScrollerIncrementLine)
[self drawArrow:CPScrollerIncrementArrow highlight:shouldHighlight];
}
// Event Handling
/*
Returns the part of the scroller that was hit.
*/
- (CPScrollerPart)hitPart
{
return _hitPart;
}
/*
Tracks the knob.
@param anEvent the input event
*/
- (void)trackKnob:(CPEvent)anEvent
{
var type = [anEvent type];
if (type == CPLeftMouseUp)
{
_hitPart = CPScrollerNoPart;
return;
}
if (type == CPLeftMouseDown)
{
_trackingFloatValue = [self floatValue];
_trackingStartPoint = [self convertPoint:[anEvent locationInWindow] fromView:nil];
}
else if (type == CPLeftMouseDragged)
{
var knobRect = [self rectForPart:CPScrollerKnob],
knobSlotRect = [self rectForPart:CPScrollerKnobSlot],
remainder = _isHorizontal ? (_CGRectGetWidth(knobSlotRect) - _CGRectGetWidth(knobRect)) : (_CGRectGetHeight(knobSlotRect) - _CGRectGetHeight(knobRect));
if (remainder <= 0)
[self setFloatValue:0.0];
else
{
var location = [self convertPoint:[anEvent locationInWindow] fromView:nil];
delta = _isHorizontal ? location.x - _trackingStartPoint.x : location.y - _trackingStartPoint.y;
[self setFloatValue:_trackingFloatValue + delta / remainder];
}
}
[CPApp setTarget:self selector:@selector(trackKnob:) forNextEventMatchingMask:CPLeftMouseDraggedMask | CPLeftMouseUpMask untilDate:nil inMode:nil dequeue:YES];
[self sendAction:[self action] to:[self target]];
}
/*
Tracks the scroll button.
@param anEvent the input event
*/
- (void)trackScrollButtons:(CPEvent)anEvent
{
var type = [anEvent type];
if (type == CPLeftMouseUp)
{
[self highlight:NO];
[CPEvent stopPeriodicEvents];
_hitPart = CPScrollerNoPart;
return;
}
if (type == CPLeftMouseDown)
{
_trackingPart = [self hitPart];
_trackingStartPoint = [self convertPoint:[anEvent locationInWindow] fromView:nil];
if ([anEvent modifierFlags] & CPAlternateKeyMask)
{
if (_trackingPart == CPScrollerDecrementLine)
_hitPart = CPScrollerDecrementPage;
else if (_trackingPart == CPScrollerIncrementLine)
_hitPart = CPScrollerIncrementPage;
else if (_trackingPart == CPScrollerDecrementPage || _trackingPart == CPScrollerIncrementPage)
{
var knobRect = [self rectForPart:CPScrollerKnob],
knobWidth = _isHorizontal ? _CGRectGetWidth(knobRect) : _CGRectGetHeight(knobRect),
knobSlotRect = [self rectForPart:CPScrollerKnobSlot],
remainder = (_isHorizontal ? _CGRectGetWidth(knobSlotRect) : _CGRectGetHeight(knobSlotRect)) - knobWidth;
[self setFloatValue:((_isHorizontal ? _trackingStartPoint.x - _CGRectGetMinX(knobSlotRect) : _trackingStartPoint.y - _CGRectGetMinY(knobSlotRect)) - knobWidth / 2.0) / remainder];
_hitPart = CPScrollerKnob;
[self sendAction:[self action] to:[self target]];
// Now just track the knob.
return [self trackKnob:anEvent];
}
}
[self highlight:YES];
[self sendAction:[self action] to:[self target]];
[CPEvent startPeriodicEventsAfterDelay:0.5 withPeriod:0.04];
}
else if (type == CPLeftMouseDragged)
{
_trackingStartPoint = [self convertPoint:[anEvent locationInWindow] fromView:nil];
if (_trackingPart == CPScrollerDecrementPage || _trackingPart == CPScrollerIncrementPage)
{
var hitPart = [self testPart:[anEvent locationInWindow]];
if (hitPart == CPScrollerDecrementPage || hitPart == CPScrollerIncrementPage)
{
_trackingPart = hitPart;
_hitPart = hitPart;
}
}
[self highlight:CGRectContainsPoint([self rectForPart:_trackingPart], _trackingStartPoint)];
}
else if (type == CPPeriodic && CGRectContainsPoint([self rectForPart:_trackingPart], _trackingStartPoint))
[self sendAction:[self action] to:[self target]];
[CPApp setTarget:self selector:@selector(trackScrollButtons:) forNextEventMatchingMask:CPPeriodicMask | CPLeftMouseDraggedMask | CPLeftMouseUpMask untilDate:nil inMode:nil dequeue:YES];
}
- (void)setFrameSize:(CGSize)aSize
{
[super setFrameSize:aSize];
[self checkSpaceForParts];
var frame = [self frame],
isHorizontal = CPRectGetWidth(frame) > CPRectGetHeight(frame);
if (_isHorizontal != isHorizontal)
{
_isHorizontal = isHorizontal;
[self drawParts];
}
[self layoutSubviews];
}
/*
Lays out the scrollers subviews
*/
- (void)layoutSubviews
{
[_knobSlotView setFrame:[self rectForPart:CPScrollerKnobSlot]];
var usableParts = [self usableParts],
hidden = !(usableParts == CPAllScrollerParts);
if (hidden != [_knobView isHidden])
{
[_knobView setHidden:hidden];
[_decrementArrowView setHidden:hidden];
[_incrementArrowView setHidden:hidden];
}
if (!hidden)
{
[_knobView setFrame:[self rectForPart:CPScrollerKnob]];
[_decrementArrowView setFrame:[self rectForPart:CPScrollerDecrementLine]];
[_incrementArrowView setFrame:[self rectForPart:CPScrollerIncrementLine]];
}
}
- (void)mouseDown:(CPEvent)anEvent
{
_hitPart = [self testPart:[anEvent locationInWindow]];
switch (_hitPart)
{
case CPScrollerKnob: return [self trackKnob:anEvent];
case CPScrollerDecrementLine:
case CPScrollerIncrementLine:
case CPScrollerDecrementPage:
case CPScrollerIncrementPage: return [self trackScrollButtons:anEvent];
}
}
// FIXME: This is a result of the "dumb" code that just makes things transparent when disabled.
- (void)setEnabled:(BOOL)shouldBeEnabled
{
}
@end