API  0.9.7
 All Classes Files Functions Variables Macros Groups Pages
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 
25 
31 @implementation CPStepper: CPControl
32 {
33  BOOL _valueWraps;
34  BOOL _autorepeat;
35  int _increment;
36  int _maxValue;
37  int _minValue;
38 
39  CPButton _buttonDown;
40  CPButton _buttonUp;
41 }
42 
43 #pragma mark -
44 #pragma mark Initialization
45 
53 + (CPStepper)stepperWithInitialValue:(float)aValue minValue:(float)aMinValue maxValue:(float)aMaxValue
54 {
55  var stepper = [[CPStepper alloc] initWithFrame:CGRectMakeZero()];
56 
57  [stepper setDoubleValue:aValue];
58  [stepper setMinValue:aMinValue];
59  [stepper setMaxValue:aMaxValue];
60 
61  // _sizeToFit will put the good size for the stepper depending of the current theme
62  [stepper _sizeToFit];
63 
64  return stepper;
65 }
66 
75 + (CPStepper)stepper
76 {
77  return [CPStepper stepperWithInitialValue:0.0 minValue:0.0 maxValue:59.0];
78 }
79 
80 + (Class)_binderClassForBinding:(CPString)aBinding
81 {
82  if (aBinding == CPValueBinding || aBinding == CPMinValueBinding || aBinding == CPMaxValueBinding)
83  return [_CPStepperValueBinder class];
84 
85  return [super _binderClassForBinding:aBinding];
86 }
87 
88 - (CPString)_replacementKeyPathForBinding:(CPString)aBinding
89 {
90  if (aBinding == CPValueBinding)
91  return @"doubleValue";
92 
93  return [super _replacementKeyPathForBinding:aBinding];
94 }
95 
101 - (id)initWithFrame:(CGRect)aFrame
102 {
103  if (self = [super initWithFrame:aFrame])
104  {
105  _maxValue = 59.0;
106  _minValue = 0.0;
107  _increment = 1.0;
108  _valueWraps = YES;
109  _autorepeat = YES;
110 
111  [self setDoubleValue:0.0];
112  [self _init];
113  }
114 
115  return self;
116 }
117 
120 - (void)_init
121 {
122  _buttonUp = [[CPButton alloc] initWithFrame:CGRectMakeZero()];
123  [_buttonUp setContinuous:_autorepeat];
124  [_buttonUp setTarget:self];
125  [_buttonUp setAction:@selector(_buttonDidClick:)];
126  [_buttonUp setAutoresizingMask:CPViewNotSizable];
127  [self addSubview:_buttonUp];
128 
129  _buttonDown = [[CPButton alloc] initWithFrame:CGRectMakeZero()];
130  [_buttonDown setContinuous:_autorepeat];
131  [_buttonDown setTarget:self];
132  [_buttonDown setAction:@selector(_buttonDidClick:)];
133  [_buttonDown setAutoresizingMask:CPViewNotSizable];
134 
135  [self setContinuous:_autorepeat];
136  [self addSubview:_buttonDown];
137 
138  [self setNeedsLayout];
139 }
140 
141 #pragma mark -
142 #pragma mark Superclass overrides
143 
148 - (void)setEnabled:(BOOL)shouldEnabled
149 {
150  [super setEnabled:shouldEnabled];
151 
152  [_buttonUp setEnabled:shouldEnabled];
153  [_buttonDown setEnabled:shouldEnabled];
154 }
155 
156 
157 - (void)setFrame:(CGRect)aFrame
158 {
159  var upSize = [self valueForThemeAttribute:@"up-button-size"],
160  downSize = [self valueForThemeAttribute:@"down-button-size"],
161  minSize = CGSizeMake(upSize.width, upSize.height + downSize.height),
162  frame = CGRectMakeCopy(aFrame);
163 
164  frame.size.width = MAX(minSize.width, frame.size.width);
165  frame.size.height = MAX(minSize.height, frame.size.height);
166  [super setFrame:frame];
167 }
168 
170 - (void)layoutSubviews
171 {
172  var aFrame = [self frame],
173  upSize = [self valueForThemeAttribute:@"up-button-size"],
174  downSize = [self valueForThemeAttribute:@"down-button-size"],
175  upFrame = CGRectMake(aFrame.size.width - upSize.width, 0, upSize.width, upSize.height),
176  downFrame = CGRectMake(aFrame.size.width - downSize.width, upSize.height, downSize.width, downSize.height);
177 
178  [_buttonUp setFrame:upFrame];
179  [_buttonDown setFrame:downFrame];
180 
181  [_buttonUp setValue:[self valueForThemeAttribute:@"bezel-color-up-button" inState:CPThemeStateBordered] forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered];
182  [_buttonUp setValue:[self valueForThemeAttribute:@"bezel-color-up-button" inState:CPThemeStateBordered | CPThemeStateDisabled] forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered | CPThemeStateDisabled];
183  [_buttonUp setValue:[self valueForThemeAttribute:@"bezel-color-up-button" inState:CPThemeStateBordered | CPThemeStateHighlighted] forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered | CPThemeStateHighlighted];
184  [_buttonDown setValue:[self valueForThemeAttribute:@"bezel-color-down-button" inState:CPThemeStateBordered] forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered];
185  [_buttonDown setValue:[self valueForThemeAttribute:@"bezel-color-down-button" inState:CPThemeStateBordered | CPThemeStateDisabled] forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered | CPThemeStateDisabled];
186  [_buttonDown setValue:[self valueForThemeAttribute:@"bezel-color-down-button" inState:CPThemeStateBordered | CPThemeStateHighlighted] forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered | CPThemeStateHighlighted];
187 }
188 
189 - (void)_sizeToFit
190 {
191  [self setFrame:CGRectMake([self frameOrigin].x, [self frameOrigin].y, 0, 0)];
192 }
193 
198 - (void)setAutorepeat:(BOOL)shouldAutoRepeat
199 {
200  if (shouldAutoRepeat !== _autorepeat)
201  {
202  [_buttonUp setContinuous:shouldAutoRepeat];
203  [_buttonDown setContinuous:shouldAutoRepeat];
204  }
205 
206  [self setContinuous:shouldAutoRepeat];
207 }
208 
213 - (void)setDoubleValue:(double)aValue
214 {
215  if (aValue > _maxValue)
216  [super setDoubleValue:_valueWraps ? _minValue : _maxValue];
217  else if (aValue < _minValue)
218  [super setDoubleValue:_valueWraps ? _maxValue : _minValue];
219  else
220  [super setDoubleValue:aValue];
221 }
222 
223 #pragma mark -
224 #pragma mark Actions
225 
227 - (IBAction)_buttonDidClick:(id)aSender
228 {
229  if (![self isEnabled])
230  return;
231 
232  if (aSender == _buttonUp)
233  [self setDoubleValue:([self doubleValue] + _increment)];
234  else
235  [self setDoubleValue:([self doubleValue] - _increment)];
236 
237  [self sendAction:[self action] to:[self target]];
238 }
239 
244 - (IBAction)performClickUp:(id)aSender
245 {
246  [_buttonUp performClick:aSender];
247 }
248 
253 - (IBAction)performClickDown:(id)aSender
254 {
255  [_buttonDown performClick:aSender];
256 }
257 
258 
259 #pragma mark -
260 #pragma mark Theming
261 
262 + (CPString)defaultThemeClass
263 {
264  return @"stepper";
265 }
266 
267 + (CPDictionary)themeAttributes
268 {
269  return @{
270  @"bezel-color-up-button": [CPNull null],
271  @"bezel-color-down-button": [CPNull null],
272  @"up-button-size": CGSizeMakeZero(),
273  @"down-button-size": CGSizeMakeZero(),
274  };
275 }
276 
277 @end
278 @implementation _CPStepperValueBinder : CPBinder
279 {
280  id __doxygen__;
281 }
282 
283 - (void)_updatePlaceholdersWithOptions:(CPDictionary)options forBinding:(CPString)aBinding
284 {
285  var placeholder = (aBinding == CPMaxValueBinding) ? [_source maxValue] : [_source minValue];
286 
287  [super _updatePlaceholdersWithOptions:options];
288 
289  [self _setPlaceholder:placeholder forMarker:CPMultipleValuesMarker isDefault:YES];
290  [self _setPlaceholder:placeholder forMarker:CPNoSelectionMarker isDefault:YES];
291  [self _setPlaceholder:placeholder forMarker:CPNotApplicableMarker isDefault:YES];
292  [self _setPlaceholder:placeholder forMarker:CPNullMarker isDefault:YES];
293 }
294 
295 @end
296 
297 var CPStepperMinValue = @"CPStepperMinValue",
298  CPStepperMaxValue = @"CPStepperMaxValue",
299  CPStepperValueWraps = @"CPStepperValueWraps",
300  CPStepperAutorepeat = @"CPStepperAutorepeat",
301  CPStepperIncrement = @"CPStepperIncrement";
302 
303 @implementation CPStepper (CPCoding)
304 
305 - (id)initWithCoder:(CPCoder)aCoder
306 {
307  if (self = [super initWithCoder:aCoder])
308  {
309  _increment = [aCoder decodeIntForKey:CPStepperIncrement];
310  _minValue = [aCoder decodeIntForKey:CPStepperMinValue];
311  _maxValue = [aCoder decodeIntForKey:CPStepperMaxValue];
312  _valueWraps = [aCoder decodeBoolForKey:CPStepperValueWraps];
313  _autorepeat = [aCoder decodeBoolForKey:CPStepperAutorepeat];
314 
315  [self _init];
316  }
317 
318  return self;
319 }
320 
321 - (void)encodeWithCoder:(CPCoder)aCoder
322 {
323  [super encodeWithCoder:aCoder];
324 
325  [aCoder encodeInt:_increment forKey:CPStepperIncrement];
326 
327  if (_minValue)
328  [aCoder encodeInt:_minValue forKey:CPStepperMinValue];
329  if (_maxValue)
330  [aCoder encodeInt:_maxValue forKey:CPStepperMaxValue];
331  if (_valueWraps)
332  [aCoder encodeBool:_valueWraps forKey:CPStepperValueWraps];
333  if (_autorepeat)
334  [aCoder encodeBool:_autorepeat forKey:CPStepperAutorepeat];
335 }
336 
337 @end
338 
340 
344 - (BOOL)valueWraps
345 {
346  return _valueWraps;
347 }
348 
352 - (void)setValueWraps:(BOOL)aValue
353 {
354  _valueWraps = aValue;
355 }
356 
360 - (BOOL)autorepeat
361 {
362  return _autorepeat;
363 }
364 
368 - (int)increment
369 {
370  return _increment;
371 }
372 
376 - (void)setIncrement:(int)aValue
377 {
378  _increment = aValue;
379 }
380 
384 - (int)maxValue
385 {
386  return _maxValue;
387 }
388 
392 - (void)setMaxValue:(int)aValue
393 {
394  _maxValue = aValue;
395 }
396 
400 - (int)minValue
401 {
402  return _minValue;
403 }
404 
408 - (void)setMinValue:(int)aValue
409 {
410  _minValue = aValue;
411 }
412 
413 @end