API  0.9.10
CPStepper.j
Go to the documentation of this file.
1 /*
2  * CPStepper.j
3  * AppKit
4  *
5  * Created by Antoine Mercadal
6  * Copyright 2009, Antoine Mercadal
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 
30 @implementation CPStepper: CPControl
31 {
32  BOOL _valueWraps;
33  BOOL _autorepeat;
34  int _increment;
35  int _maxValue;
36  int _minValue;
37 
38  CPButton _buttonDown;
39  CPButton _buttonUp;
40 }
41 
42 #pragma mark -
43 #pragma mark Initialization
44 
52 + (id)stepperWithInitialValue:(float)aValue minValue:(float)aMinValue maxValue:(float)aMaxValue
53 {
54  var stepper = [[self alloc] initWithFrame:CGRectMakeZero()];
55 
56  [stepper setDoubleValue:aValue];
57  [stepper setMinValue:aMinValue];
58  [stepper setMaxValue:aMaxValue];
59 
60  // _sizeToFit will put the good size for the stepper depending of the current theme
61  [stepper _sizeToFit];
62 
63  return stepper;
64 }
65 
74 + (id)stepper
75 {
76  return [CPStepper stepperWithInitialValue:0.0 minValue:0.0 maxValue:59.0];
77 }
78 
79 + (Class)_binderClassForBinding:(CPString)aBinding
80 {
81  if (aBinding === CPValueBinding || aBinding === CPMinValueBinding || aBinding === CPMaxValueBinding)
82  return [_CPStepperValueBinder class];
83 
84  return [super _binderClassForBinding:aBinding];
85 }
86 
87 - (CPString)_replacementKeyPathForBinding:(CPString)aBinding
88 {
89  if (aBinding === CPValueBinding)
90  return @"doubleValue";
91 
92  return [super _replacementKeyPathForBinding:aBinding];
93 }
94 
100 - (id)initWithFrame:(CGRect)aFrame
101 {
102  if (self = [super initWithFrame:aFrame])
103  {
104  _maxValue = 59.0;
105  _minValue = 0.0;
106  _increment = 1.0;
107  _valueWraps = YES;
108  _autorepeat = YES;
109 
110  [self setDoubleValue:0.0];
111  [self _init];
112  }
113 
114  return self;
115 }
116 
119 - (void)_init
120 {
121  _buttonUp = [[CPButton alloc] initWithFrame:CGRectMakeZero()];
122  [_buttonUp setContinuous:_autorepeat];
123  [_buttonUp setTarget:self];
124  [_buttonUp setAction:@selector(_buttonDidClick:)];
125  [_buttonUp setAutoresizingMask:CPViewNotSizable];
126  [self addSubview:_buttonUp];
127 
128  _buttonDown = [[CPButton alloc] initWithFrame:CGRectMakeZero()];
129  [_buttonDown setContinuous:_autorepeat];
130  [_buttonDown setTarget:self];
131  [_buttonDown setAction:@selector(_buttonDidClick:)];
132  [_buttonDown setAutoresizingMask:CPViewNotSizable];
133 
134  [self setContinuous:_autorepeat];
135  [self addSubview:_buttonDown];
136 
137  [self _sizeToFit];
138  [self setNeedsLayout];
139 }
140 
141 - (void)setControlSize:(CPControlSize)aControlSize
142 {
143  [super setControlSize:aControlSize];
144  [self _sizeToFit];
145 }
146 
147 #pragma mark -
148 #pragma mark Superclass overrides
149 
154 - (void)setEnabled:(BOOL)shouldEnabled
155 {
156  [super setEnabled:shouldEnabled];
157 
158  [_buttonUp setEnabled:shouldEnabled];
159  [_buttonDown setEnabled:shouldEnabled];
160 }
161 
162 
163 - (void)setFrame:(CGRect)aFrame
164 {
165  var upSize = [self currentValueForThemeAttribute:@"up-button-size"],
166  downSize = [self currentValueForThemeAttribute:@"down-button-size"],
167  minSize = CGSizeMake(upSize.width, upSize.height + downSize.height),
168  frame = CGRectMakeCopy(aFrame);
169 
170  frame.size.width = MAX(minSize.width, frame.size.width);
171  frame.size.height = MAX(minSize.height, frame.size.height);
172  [super setFrame:frame];
173 }
174 
177 {
178  var controlSizeThemeState = [self _controlSizeThemeState],
179  aFrame = [self frame],
180  upSize = [self valueForThemeAttribute:@"up-button-size" inState:controlSizeThemeState],
181  downSize = [self valueForThemeAttribute:@"down-button-size" inState:controlSizeThemeState],
182  upFrame = CGRectMake(0, 0, upSize.width, upSize.height),
183  downFrame = CGRectMake(0, upSize.height, downSize.width, downSize.height);
184 
185  [_buttonUp setFrame:upFrame];
186  [_buttonDown setFrame:downFrame];
187 
188  [_buttonUp setValue:[self valueForThemeAttribute:@"bezel-color-up-button" inStates:[controlSizeThemeState, CPThemeStateBordered]] forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered];
189  [_buttonUp setValue:[self valueForThemeAttribute:@"bezel-color-up-button" inStates:[controlSizeThemeState, CPThemeStateBordered, CPThemeStateDisabled]] forThemeAttribute:@"bezel-color" inStates:[CPThemeStateBordered, CPThemeStateDisabled]];
190  [_buttonUp setValue:[self valueForThemeAttribute:@"bezel-color-up-button" inStates:[controlSizeThemeState, CPThemeStateBordered, CPThemeStateHighlighted]] forThemeAttribute:@"bezel-color" inStates:[CPThemeStateBordered, CPThemeStateHighlighted]];
191  [_buttonDown setValue:[self valueForThemeAttribute:@"bezel-color-down-button" inStates:[controlSizeThemeState, CPThemeStateBordered]] forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered];
192  [_buttonDown setValue:[self valueForThemeAttribute:@"bezel-color-down-button" inStates:[controlSizeThemeState, CPThemeStateBordered, CPThemeStateDisabled]] forThemeAttribute:@"bezel-color" inStates:[CPThemeStateBordered, CPThemeStateDisabled]];
193  [_buttonDown setValue:[self valueForThemeAttribute:@"bezel-color-down-button" inStates:[controlSizeThemeState, CPThemeStateBordered, CPThemeStateHighlighted]] forThemeAttribute:@"bezel-color" inStates:[CPThemeStateBordered, CPThemeStateHighlighted]];
194 }
195 
196 - (void)_sizeToFit
197 {
198  [self setFrame:CGRectMake([self frameOrigin].x, [self frameOrigin].y, 0, 0)];
199 }
200 
205 - (void)setAutorepeat:(BOOL)shouldAutoRepeat
206 {
207  if (shouldAutoRepeat !== _autorepeat)
208  {
209  [_buttonUp setContinuous:shouldAutoRepeat];
210  [_buttonDown setContinuous:shouldAutoRepeat];
211  }
212 
213  [self setContinuous:shouldAutoRepeat];
214 }
215 
220 - (void)setDoubleValue:(double)aValue
221 {
222  if (aValue > _maxValue)
223  [super setDoubleValue:_valueWraps ? _minValue : _maxValue];
224  else if (aValue < _minValue)
225  [super setDoubleValue:_valueWraps ? _maxValue : _minValue];
226  else
227  [super setDoubleValue:aValue];
228 }
229 
230 #pragma mark -
231 #pragma mark Actions
232 
234 - (IBAction)_buttonDidClick:(id)aSender
235 {
236  if (![self isEnabled])
237  return;
238 
239  if (aSender === _buttonUp)
240  [self setDoubleValue:([self doubleValue] + _increment)];
241  else
242  [self setDoubleValue:([self doubleValue] - _increment)];
243 
244  [self sendAction:[self action] to:[self target]];
245 }
246 
251 - (IBAction)performClickUp:(id)aSender
252 {
253  [_buttonUp performClick:aSender];
254 }
255 
260 - (IBAction)performClickDown:(id)aSender
261 {
262  [_buttonDown performClick:aSender];
263 }
264 
265 
266 #pragma mark -
267 #pragma mark Theming
268 
270 {
271  return @"stepper";
272 }
273 
275 {
276  return @{
277  @"bezel-color-up-button": [CPNull null],
278  @"bezel-color-down-button": [CPNull null],
279  @"up-button-size": CGSizeMakeZero(),
280  @"down-button-size": CGSizeMakeZero(),
281  };
282 }
283 
284 @end
285 @implementation _CPStepperValueBinder : CPBinder
286 {
287  id __doxygen__;
288 }
289 
290 - (void)_updatePlaceholdersWithOptions:(CPDictionary)options forBinding:(CPString)aBinding
291 {
292  var placeholder = (aBinding === CPMaxValueBinding) ? [_source maxValue] : [_source minValue];
293 
294  [super _updatePlaceholdersWithOptions:options];
295 
296  [self _setPlaceholder:placeholder forMarker:CPMultipleValuesMarker isDefault:YES];
297  [self _setPlaceholder:placeholder forMarker:CPNoSelectionMarker isDefault:YES];
298  [self _setPlaceholder:placeholder forMarker:CPNotApplicableMarker isDefault:YES];
299  [self _setPlaceholder:placeholder forMarker:CPNullMarker isDefault:YES];
300 }
301 
302 @end
303 
304 var CPStepperMinValue = @"CPStepperMinValue",
305  CPStepperMaxValue = @"CPStepperMaxValue",
306  CPStepperValueWraps = @"CPStepperValueWraps",
307  CPStepperAutorepeat = @"CPStepperAutorepeat",
308  CPStepperIncrement = @"CPStepperIncrement";
309 
310 @implementation CPStepper (CPCoding)
311 
312 - (id)initWithCoder:(CPCoder)aCoder
313 {
314  if (self = [super initWithCoder:aCoder])
315  {
316  _increment = [aCoder decodeIntForKey:CPStepperIncrement];
317  _minValue = [aCoder decodeIntForKey:CPStepperMinValue];
318  _maxValue = [aCoder decodeIntForKey:CPStepperMaxValue];
319  _valueWraps = [aCoder decodeBoolForKey:CPStepperValueWraps];
320  _autorepeat = [aCoder decodeBoolForKey:CPStepperAutorepeat];
321 
322  [self _init];
323  }
324 
325  return self;
326 }
327 
328 - (void)encodeWithCoder:(CPCoder)aCoder
329 {
330  [super encodeWithCoder:aCoder];
331 
332  [aCoder encodeInt:_increment forKey:CPStepperIncrement];
333 
334  if (_minValue)
335  [aCoder encodeInt:_minValue forKey:CPStepperMinValue];
336  if (_maxValue)
337  [aCoder encodeInt:_maxValue forKey:CPStepperMaxValue];
338  if (_valueWraps)
339  [aCoder encodeBool:_valueWraps forKey:CPStepperValueWraps];
340  if (_autorepeat)
341  [aCoder encodeBool:_autorepeat forKey:CPStepperAutorepeat];
342 }
343 
344 @end
345 
347 
351 - (BOOL)valueWraps
352 {
353  return _valueWraps;
354 }
355 
359 - (void)setValueWraps:(BOOL)aValue
360 {
361  _valueWraps = aValue;
362 }
363 
367 - (BOOL)autorepeat
368 {
369  return _autorepeat;
370 }
371 
375 - (int)increment
376 {
377  return _increment;
378 }
379 
383 - (void)setIncrement:(int)aValue
384 {
385  _increment = aValue;
386 }
387 
391 - (int)maxValue
392 {
393  return _maxValue;
394 }
395 
399 - (void)setMaxValue:(int)aValue
400 {
401  _maxValue = aValue;
402 }
403 
407 - (int)minValue
408 {
409  return _minValue;
410 }
411 
415 - (void)setMinValue:(int)aValue
416 {
417  _minValue = aValue;
418 }
419 
420 @end
void setContinuous:(BOOL flag)
Definition: CPControl.j:350
void addSubview:(CPView aSubview)
Definition: CPView.j:512
CGPoint frameOrigin()
Definition: CPView.j:1027
BOOL isEnabled()
Definition: CPControl.j:970
An object representation of nil.
Definition: CPNull.h:2
CPMinValueBinding
void setFrame:(CGRect aFrame)
Definition: CPView.j:996
void setControlSize:(CPControlSize aControlSize)
Definition: CPControl.j:211
void layoutSubviews()
Definition: CPStepper.j:176
void setEnabled:(BOOL isEnabled)
Definition: CPControl.j:959
CPDictionary themeAttributes()
Definition: CPStepper.j:274
A mutable key-value pair collection.
Definition: CPDictionary.h:2
An immutable string (collection of characters).
Definition: CPString.h:2
CPNull null()
Definition: CPNull.j:51
var CPStepperAutorepeat
Definition: CPStepper.j:307
BOOL sendAction:to:(SEL anAction, [to] id anObject)
Definition: CPControl.j:319
SEL action()
Definition: CPControl.j:290
CPString defaultThemeClass()
Definition: CPStepper.j:269
var CPStepperMinValue
Definition: CPStepper.j:304
var CPStepperValueWraps
Definition: CPStepper.j:306
CPMaxValueBinding
void setNeedsLayout()
Definition: CPView.j:2707
id target()
Definition: CPControl.j:308
double doubleValue()
Definition: CPControl.j:562
Defines methods for use when archiving & restoring (enc/decoding).
Definition: CPCoder.h:2
void setFrame:(CGRect aFrame)
Definition: CPStepper.j:163
void setDoubleValue:(double anObject)
Definition: CPControl.j:571
id stepperWithInitialValue:minValue:maxValue:(float aValue, [minValue] float aMinValue, [maxValue] float aMaxValue)
Definition: CPStepper.j:52
CGRect frame()
Definition: CPView.j:1022
var CPStepperIncrement
Definition: CPStepper.j:308
CPValueBinding
void encodeWithCoder:(CPCoder aCoder)
Definition: CPControl.j:1114
var CPStepperMaxValue
Definition: CPStepper.j:305
id stepper()
Definition: CPStepper.j:74
void setDoubleValue:(double aValue)
Definition: CPStepper.j:220