API  0.9.6
 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:_CGRectMake(0, 0, 19, 25)];
56  [stepper setDoubleValue:aValue];
57  [stepper setMinValue:aMinValue];
58  [stepper setMaxValue:aMaxValue];
59 
60  return stepper;
61 }
62 
71 + (CPStepper)stepper
72 {
73  return [CPStepper stepperWithInitialValue:0.0 minValue:0.0 maxValue:59.0];
74 }
75 
76 + (Class)_binderClassForBinding:(CPString)theBinding
77 {
78  if (theBinding == CPValueBinding || theBinding == CPMinValueBinding || theBinding == CPMaxValueBinding)
79  return [_CPStepperValueBinder class];
80 
81  return [super _binderClassForBinding:theBinding];
82 }
83 
84 - (id)_replacementKeyPathForBinding:(CPString)aBinding
85 {
86  if (aBinding == CPValueBinding)
87  return @"doubleValue";
88 
89  return [super _replacementKeyPathForBinding:aBinding];
90 }
91 
97 - (id)initWithFrame:(CGRect)aFrame
98 {
99  if (self = [super initWithFrame:aFrame])
100  {
101  _maxValue = 59.0;
102  _minValue = 0.0;
103  _increment = 1.0;
104  _valueWraps = YES;
105  _autorepeat = YES;
106 
107  [self setDoubleValue:0.0];
108  [self _init];
109  }
110 
111  return self;
112 }
113 
116 - (void)_init
117 {
118  _buttonUp = [[CPButton alloc] initWithFrame:_CGRectMakeZero()];
119  [_buttonUp setContinuous:_autorepeat];
120  [_buttonUp setTarget:self];
121  [_buttonUp setAction:@selector(_buttonDidClick:)];
122  [_buttonUp setAutoresizingMask:CPViewNotSizable];
123  [self addSubview:_buttonUp];
124 
125  _buttonDown = [[CPButton alloc] initWithFrame:_CGRectMakeZero()];
126  [_buttonDown setContinuous:_autorepeat];
127  [_buttonDown setTarget:self];
128  [_buttonDown setAction:@selector(_buttonDidClick:)];
129  [_buttonDown setAutoresizingMask:CPViewNotSizable];
130 
131  [self setContinuous:_autorepeat];
132  [self addSubview:_buttonDown];
133 
134  [self setNeedsLayout];
135 }
136 
137 #pragma mark -
138 #pragma mark Superclass overrides
139 
144 - (void)setEnabled:(BOOL)shouldEnabled
145 {
146  [super setEnabled:shouldEnabled];
147 
148  [_buttonUp setEnabled:shouldEnabled];
149  [_buttonDown setEnabled:shouldEnabled];
150 }
151 
152 
153 - (void)setFrame:(CGRect)aFrame
154 {
155  var upSize = [self valueForThemeAttribute:@"up-button-size"],
156  downSize = [self valueForThemeAttribute:@"down-button-size"],
157  minSize = _CGSizeMake(upSize.width, upSize.height + downSize.height),
158  frame = _CGRectMakeCopy(aFrame);
159 
160  frame.size.width = MAX(minSize.width, frame.size.width);
161  frame.size.height = MAX(minSize.height, frame.size.height);
162  [super setFrame:frame];
163 }
164 
166 - (void)layoutSubviews
167 {
168  var aFrame = [self frame],
169  upSize = [self valueForThemeAttribute:@"up-button-size"],
170  downSize = [self valueForThemeAttribute:@"down-button-size"],
171  upFrame = _CGRectMake(aFrame.size.width - upSize.width, 0, upSize.width, upSize.height),
172  downFrame = _CGRectMake(aFrame.size.width - downSize.width, upSize.height, downSize.width, downSize.height);
173  [_buttonUp setFrame:upFrame];
174  [_buttonDown setFrame:downFrame];
175 
176  [_buttonUp setValue:[self valueForThemeAttribute:@"bezel-color-up-button" inState:CPThemeStateBordered] forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered];
177  [_buttonUp setValue:[self valueForThemeAttribute:@"bezel-color-up-button" inState:CPThemeStateBordered | CPThemeStateDisabled] forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered | CPThemeStateDisabled];
178  [_buttonUp setValue:[self valueForThemeAttribute:@"bezel-color-up-button" inState:CPThemeStateBordered | CPThemeStateHighlighted] forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered | CPThemeStateHighlighted];
179  [_buttonDown setValue:[self valueForThemeAttribute:@"bezel-color-down-button" inState:CPThemeStateBordered] forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered];
180  [_buttonDown setValue:[self valueForThemeAttribute:@"bezel-color-down-button" inState:CPThemeStateBordered | CPThemeStateDisabled] forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered | CPThemeStateDisabled];
181  [_buttonDown setValue:[self valueForThemeAttribute:@"bezel-color-down-button" inState:CPThemeStateBordered | CPThemeStateHighlighted] forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered | CPThemeStateHighlighted];
182 }
183 
188 - (void)setAutorepeat:(BOOL)shouldAutoRepeat
189 {
190  if (shouldAutoRepeat !== _autorepeat)
191  {
192  [_buttonUp setContinuous:shouldAutoRepeat];
193  [_buttonDown setContinuous:shouldAutoRepeat];
194  }
195 
196  [self setContinuous:shouldAutoRepeat];
197 }
198 
203 - (void)setDoubleValue:(float)aValue
204 {
205  if (aValue > _maxValue)
206  [super setDoubleValue:_valueWraps ? _minValue : _maxValue];
207  else if (aValue < _minValue)
208  [super setDoubleValue:_valueWraps ? _maxValue : _minValue];
209  else
210  [super setDoubleValue:aValue];
211 }
212 
213 #pragma mark -
214 #pragma mark Actions
215 
217 - (IBAction)_buttonDidClick:(id)aSender
218 {
219  if (![self isEnabled])
220  return;
221 
222  if (aSender == _buttonUp)
223  [self setDoubleValue:([self doubleValue] + _increment)];
224  else
225  [self setDoubleValue:([self doubleValue] - _increment)];
226 
227  [self sendAction:[self action] to:[self target]];
228 }
229 
234 - (IBAction)performClickUp:(id)aSender
235 {
236  [_buttonUp performClick:aSender];
237 }
238 
243 - (IBAction)performClickDown:(id)aSender
244 {
245  [_buttonDown performClick:aSender];
246 }
247 
248 
249 #pragma mark -
250 #pragma mark Theming
251 
252 + (CPString)defaultThemeClass
253 {
254  return @"stepper";
255 }
256 
257 + (id)themeAttributes
258 {
259  return [CPDictionary dictionaryWithObjects:[[CPNull null], [CPNull null], _CGSizeMakeZero(), _CGSizeMakeZero()]
260  forKeys:[@"bezel-color-up-button", @"bezel-color-down-button", @"up-button-size", @"down-button-size"]];
261 }
262 
263 @end
264 @implementation _CPStepperValueBinder : CPBinder
265 {
266  id __doxygen__;
267 }
268 
269 - (void)_updatePlaceholdersWithOptions:(CPDictionary)options forBinding:(CPString)aBinding
270 {
271  var placeholder = (aBinding == CPMaxValueBinding) ? [_source maxValue] : [_source minValue];
272 
273  [super _updatePlaceholdersWithOptions:options];
274 
275  [self _setPlaceholder:placeholder forMarker:CPMultipleValuesMarker isDefault:YES];
276  [self _setPlaceholder:placeholder forMarker:CPNoSelectionMarker isDefault:YES];
277  [self _setPlaceholder:placeholder forMarker:CPNotApplicableMarker isDefault:YES];
278  [self _setPlaceholder:placeholder forMarker:CPNullMarker isDefault:YES];
279 }
280 
281 @end
282 
283 var CPStepperMinValue = @"CPStepperMinValue",
284  CPStepperMaxValue = @"CPStepperMaxValue",
285  CPStepperValueWraps = @"CPStepperValueWraps",
286  CPStepperAutorepeat = @"CPStepperAutorepeat",
287  CPStepperIncrement = @"CPStepperIncrement";
288 
289 @implementation CPStepper (CPCoding)
290 
291 - (id)initWithCoder:(CPCoder)aCoder
292 {
293  if (self = [super initWithCoder:aCoder])
294  {
295  _increment = [aCoder decodeIntForKey:CPStepperIncrement];
296  _minValue = [aCoder decodeIntForKey:CPStepperMinValue];
297  _maxValue = [aCoder decodeIntForKey:CPStepperMaxValue];
298  _valueWraps = [aCoder decodeBoolForKey:CPStepperValueWraps];
299  _autorepeat = [aCoder decodeBoolForKey:CPStepperAutorepeat];
300 
301  [self _init];
302  }
303 
304  return self;
305 }
306 
307 - (void)encodeWithCoder:(CPCoder)aCoder
308 {
309  [super encodeWithCoder:aCoder];
310 
311  [aCoder encodeInt:_increment forKey:CPStepperIncrement];
312 
313  if (_minValue)
314  [aCoder encodeInt:_minValue forKey:CPStepperMinValue];
315  if (_maxValue)
316  [aCoder encodeInt:_maxValue forKey:CPStepperMaxValue];
317  if (_valueWraps)
318  [aCoder encodeBool:_valueWraps forKey:CPStepperValueWraps];
319  if (_autorepeat)
320  [aCoder encodeBool:_autorepeat forKey:CPStepperAutorepeat];
321 }
322 
323 @end
324 
326 
330 - (BOOL)valueWraps
331 {
332  return _valueWraps;
333 }
334 
338 - (void)setValueWraps:(BOOL)aValue
339 {
340  _valueWraps = aValue;
341 }
342 
346 - (BOOL)autorepeat
347 {
348  return _autorepeat;
349 }
350 
354 - (int)increment
355 {
356  return _increment;
357 }
358 
362 - (void)setIncrement:(int)aValue
363 {
364  _increment = aValue;
365 }
366 
370 - (int)maxValue
371 {
372  return _maxValue;
373 }
374 
378 - (void)setMaxValue:(int)aValue
379 {
380  _maxValue = aValue;
381 }
382 
386 - (int)minValue
387 {
388  return _minValue;
389 }
390 
394 - (void)setMinValue:(int)aValue
395 {
396  _minValue = aValue;
397 }
398 
399 @end