Files
cappuccino/AppKit/CPStepper.j
T
David Richardson 1a9a42ff54 Replace #pragma mark with // MARK:
Replace objc directive with native Javascript version // MARK: to facilitate elimination of preproccsing pass.
2026-07-21 15:15:53 -06:00

349 lines
11 KiB
Plaintext

/*
* CPStepper.j
* AppKit
*
* Created by Antoine Mercadal
* Copyright 2009, Antoine Mercadal
*
* 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"
@import "CPButton.j"
@import "CPTextField.j"
/*!
CPStepper is an implementation of Cocoa NSStepper.
This control displays a two part button that can be used to increase or decrease a value with a given interval.
*/
@implementation CPStepper: CPControl
{
BOOL _valueWraps @accessors(property=valueWraps);
BOOL _autorepeat @accessors(getter=autorepeat);
int _increment @accessors(property=increment);
int _maxValue @accessors(property=maxValue);
int _minValue @accessors(property=minValue);
CPButton _buttonDown;
CPButton _buttonUp;
}
// MARK: -
// MARK: Initialization
/*!
Initializes a CPStepper with given values.
@param aValue the initial value of the CPStepper
@param minValue the minimal acceptable value of the stepper
@param maxValue the maximal acceptable value of the stepper
@return Initialized CPStepper
*/
+ (id)stepperWithInitialValue:(float)aValue minValue:(float)aMinValue maxValue:(float)aMaxValue
{
var stepper = [[self alloc] initWithFrame:CGRectMakeZero()];
[stepper setDoubleValue:aValue];
[stepper setMinValue:aMinValue];
[stepper setMaxValue:aMaxValue];
// _sizeToFit will put the good size for the stepper depending of the current theme
[stepper _sizeToFit];
return stepper;
}
/*!
Initializes a CPStepper with default values:
- minValue = 0.0
- maxValue = 59.0
- value = 0.0
@return Initialized CPStepper
*/
+ (id)stepper
{
return [CPStepper stepperWithInitialValue:0.0 minValue:0.0 maxValue:59.0];
}
+ (Class)_binderClassForBinding:(CPString)aBinding
{
if (aBinding === CPValueBinding || aBinding === CPMinValueBinding || aBinding === CPMaxValueBinding)
return [_CPStepperValueBinder class];
return [super _binderClassForBinding:aBinding];
}
- (CPString)_replacementKeyPathForBinding:(CPString)aBinding
{
if (aBinding === CPValueBinding)
return @"doubleValue";
return [super _replacementKeyPathForBinding:aBinding];
}
/*!
Initializes a CPStepper.
@param aFrame the frame of the control
@return initialized CPStepper
*/
- (id)initWithFrame:(CGRect)aFrame
{
if (self = [super initWithFrame:aFrame])
{
_maxValue = 59.0;
_minValue = 0.0;
_increment = 1.0;
_valueWraps = YES;
_autorepeat = YES;
[self setDoubleValue:0.0];
[self _init];
}
return self;
}
/*! @ignore */
- (void)_init
{
_buttonUp = [[CPButton alloc] initWithFrame:CGRectMakeZero()];
[_buttonUp setContinuous:_autorepeat];
[_buttonUp setTarget:self];
[_buttonUp setAction:@selector(_buttonDidClick:)];
[_buttonUp setAutoresizingMask:CPViewNotSizable];
[self addSubview:_buttonUp];
_buttonDown = [[CPButton alloc] initWithFrame:CGRectMakeZero()];
[_buttonDown setContinuous:_autorepeat];
[_buttonDown setTarget:self];
[_buttonDown setAction:@selector(_buttonDidClick:)];
[_buttonDown setAutoresizingMask:CPViewNotSizable];
[self setContinuous:_autorepeat];
[self addSubview:_buttonDown];
[self _sizeToFit];
[self setNeedsLayout];
}
- (void)setControlSize:(CPControlSize)aControlSize
{
[super setControlSize:aControlSize];
[self _sizeToFit];
}
// MARK: -
// MARK: Superclass overrides
/*!
Set if the CPStepper is enabled or not.
@param shouldEnabled BOOL that define if stepper is enabled or not.
*/
- (void)setEnabled:(BOOL)shouldEnabled
{
[super setEnabled:shouldEnabled];
[_buttonUp setEnabled:shouldEnabled];
[_buttonDown setEnabled:shouldEnabled];
}
- (void)setFrame:(CGRect)aFrame
{
var upSize = [self currentValueForThemeAttribute:@"up-button-size"],
downSize = [self currentValueForThemeAttribute:@"down-button-size"],
minSize = CGSizeMake(upSize.width, upSize.height + downSize.height),
frame = CGRectMakeCopy(aFrame);
frame.size.width = MAX(minSize.width, frame.size.width);
frame.size.height = MAX(minSize.height, frame.size.height);
[super setFrame:frame];
}
/*! @ignore */
- (void)layoutSubviews
{
var controlSizeThemeState = [self _controlSizeThemeState],
aFrame = [self frame],
upSize = [self valueForThemeAttribute:@"up-button-size" inState:controlSizeThemeState],
downSize = [self valueForThemeAttribute:@"down-button-size" inState:controlSizeThemeState],
upFrame = CGRectMake(0, 0, upSize.width, upSize.height),
downFrame = CGRectMake(0, upSize.height, downSize.width, downSize.height);
[_buttonUp setFrame:upFrame];
[_buttonDown setFrame:downFrame];
[_buttonUp setValue:[self valueForThemeAttribute:@"bezel-color-up-button" inStates:[controlSizeThemeState, CPThemeStateBordered]] forThemeAttribute:@"bezel-color" inStates:[CPThemeStateBordered, CPButtonStateBezelStyleRoundRect]];
[_buttonUp setValue:[self valueForThemeAttribute:@"bezel-color-up-button" inStates:[controlSizeThemeState, CPThemeStateBordered, CPThemeStateDisabled]] forThemeAttribute:@"bezel-color" inStates:[CPThemeStateBordered, CPThemeStateDisabled, CPButtonStateBezelStyleRoundRect]];
[_buttonUp setValue:[self valueForThemeAttribute:@"bezel-color-up-button" inStates:[controlSizeThemeState, CPThemeStateBordered, CPThemeStateHighlighted]] forThemeAttribute:@"bezel-color" inStates:[CPThemeStateBordered, CPThemeStateHighlighted, CPButtonStateBezelStyleRoundRect]];
[_buttonDown setValue:[self valueForThemeAttribute:@"bezel-color-down-button" inStates:[controlSizeThemeState, CPThemeStateBordered]] forThemeAttribute:@"bezel-color" inStates:[CPThemeStateBordered, CPButtonStateBezelStyleRoundRect]];
[_buttonDown setValue:[self valueForThemeAttribute:@"bezel-color-down-button" inStates:[controlSizeThemeState, CPThemeStateBordered, CPThemeStateDisabled]] forThemeAttribute:@"bezel-color" inStates:[CPThemeStateBordered, CPThemeStateDisabled, CPButtonStateBezelStyleRoundRect]];
[_buttonDown setValue:[self valueForThemeAttribute:@"bezel-color-down-button" inStates:[controlSizeThemeState, CPThemeStateBordered, CPThemeStateHighlighted]] forThemeAttribute:@"bezel-color" inStates:[CPThemeStateBordered, CPThemeStateHighlighted, CPButtonStateBezelStyleRoundRect]];
}
- (void)_sizeToFit
{
[self setFrame:CGRectMake([self frameOrigin].x, [self frameOrigin].y, 0, 0)];
}
/*!
Set if CPStepper should autorepeat.
@param shouldAutoRepeat if YES, the first mouse down does one increment (decrement) and, after each delay of 0.5 seconds
*/
- (void)setAutorepeat:(BOOL)shouldAutoRepeat
{
if (shouldAutoRepeat !== _autorepeat)
{
[_buttonUp setContinuous:shouldAutoRepeat];
[_buttonDown setContinuous:shouldAutoRepeat];
}
[self setContinuous:shouldAutoRepeat];
}
/*!
Set the current value of the stepper.
@param aValue a float containing the value
*/
- (void)setDoubleValue:(double)aValue
{
if (aValue > _maxValue)
[super setDoubleValue:_valueWraps ? _minValue : _maxValue];
else if (aValue < _minValue)
[super setDoubleValue:_valueWraps ? _maxValue : _minValue];
else
[super setDoubleValue:aValue];
}
// MARK: -
// MARK: Actions
/*! @ignore */
- (IBAction)_buttonDidClick:(id)aSender
{
if (![self isEnabled])
return;
if (aSender === _buttonUp)
[self setDoubleValue:([self doubleValue] + _increment)];
else
[self setDoubleValue:([self doubleValue] - _increment)];
[self sendAction:[self action] to:[self target]];
}
/*!
Perform a programatic click on up button.
@param aSender sender of the action
*/
- (IBAction)performClickUp:(id)aSender
{
[_buttonUp performClick:aSender];
}
/*!
Perform a programatic click on down button.
@param aSender sender of the action
*/
- (IBAction)performClickDown:(id)aSender
{
[_buttonDown performClick:aSender];
}
// MARK: -
// MARK: Theming
+ (CPString)defaultThemeClass
{
return @"stepper";
}
+ (CPDictionary)themeAttributes
{
return @{
@"bezel-color-up-button": [CPNull null],
@"bezel-color-down-button": [CPNull null],
@"up-button-size": CGSizeMakeZero(),
@"down-button-size": CGSizeMakeZero(),
};
}
@end
@implementation _CPStepperValueBinder : CPBinder
{
}
- (void)_updatePlaceholdersWithOptions:(CPDictionary)options forBinding:(CPString)aBinding
{
var placeholder = (aBinding === CPMaxValueBinding) ? [_source maxValue] : [_source minValue];
[super _updatePlaceholdersWithOptions:options];
[self _setPlaceholder:placeholder forMarker:CPMultipleValuesMarker isDefault:YES];
[self _setPlaceholder:placeholder forMarker:CPNoSelectionMarker isDefault:YES];
[self _setPlaceholder:placeholder forMarker:CPNotApplicableMarker isDefault:YES];
[self _setPlaceholder:placeholder forMarker:CPNullMarker isDefault:YES];
}
@end
var CPStepperMinValue = @"CPStepperMinValue",
CPStepperMaxValue = @"CPStepperMaxValue",
CPStepperValueWraps = @"CPStepperValueWraps",
CPStepperAutorepeat = @"CPStepperAutorepeat",
CPStepperIncrement = @"CPStepperIncrement";
@implementation CPStepper (CPCoding)
- (id)initWithCoder:(CPCoder)aCoder
{
if (self = [super initWithCoder:aCoder])
{
_increment = [aCoder decodeIntForKey:CPStepperIncrement];
_minValue = [aCoder decodeIntForKey:CPStepperMinValue];
_maxValue = [aCoder decodeIntForKey:CPStepperMaxValue];
_valueWraps = [aCoder decodeBoolForKey:CPStepperValueWraps];
_autorepeat = [aCoder decodeBoolForKey:CPStepperAutorepeat];
[self _init];
[self setEnabled:[self isEnabled]];
}
return self;
}
- (void)encodeWithCoder:(CPCoder)aCoder
{
[super encodeWithCoder:aCoder];
[aCoder encodeInt:_increment forKey:CPStepperIncrement];
if (_minValue)
[aCoder encodeInt:_minValue forKey:CPStepperMinValue];
if (_maxValue)
[aCoder encodeInt:_maxValue forKey:CPStepperMaxValue];
if (_valueWraps)
[aCoder encodeBool:_valueWraps forKey:CPStepperValueWraps];
if (_autorepeat)
[aCoder encodeBool:_autorepeat forKey:CPStepperAutorepeat];
}
@end