API  0.9.7
 All Classes Files Functions Variables Macros Groups Pages
CPButtonBar.j
Go to the documentation of this file.
1 /*
2  * CPButtonBar.j
3  * AppKit
4  *
5  * Created by Francisco Tolmasky.
6  * Copyright 2009, 280 North, Inc.
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 @class CPSplitView
25 
27 
28 
29 @implementation CPButtonBar : CPView
30 {
31  BOOL _hasResizeControl;
32  BOOL _resizeControlIsLeftAligned;
33  CPArray _buttons;
34 }
35 
36 + (id)plusButton
37 {
38  var button = [[CPButton alloc] initWithFrame:CGRectMake(0, 0, 35, 25)],
39  image = [[CPTheme defaultTheme] valueForAttributeWithName:@"button-image-plus" forClass:[CPButtonBar class]];
40 
41  [button setBordered:NO];
42  [button setImage:image];
43  [button setImagePosition:CPImageOnly];
44 
45  return button;
46 }
47 
48 + (id)minusButton
49 {
50  var button = [[CPButton alloc] initWithFrame:CGRectMake(0, 0, 35, 25)],
51  image = [[CPTheme defaultTheme] valueForAttributeWithName:@"button-image-minus" forClass:[CPButtonBar class]];
52 
53  [button setBordered:NO];
54  [button setImage:image];
55  [button setImagePosition:CPImageOnly];
56 
57  return button;
58 }
59 
60 + (id)actionPopupButton
61 {
62  var button = [[CPPopUpButton alloc] initWithFrame:CGRectMake(0, 0, 35, 25)],
63  image = [[CPTheme defaultTheme] valueForAttributeWithName:@"button-image-action" forClass:[CPButtonBar class]];
64 
65  [button addItemWithTitle:nil];
66  [[button lastItem] setImage:image];
67  [button setImagePosition:CPImageOnly];
68  [button setValue:CGInsetMake(0, 0, 0, 0) forThemeAttribute:"content-inset"];
69 
70  [button setPullsDown:YES];
71 
72  return button;
73 }
74 
75 + (CPString)defaultThemeClass
76 {
77  return @"button-bar";
78 }
79 
80 + (CPDictionary)themeAttributes
81 {
82  return @{
83  @"resize-control-inset": CGInsetMake(0.0, 0.0, 0.0, 0.0),
84  @"resize-control-size": CGSizeMakeZero(),
85  @"resize-control-color": [CPNull null],
86  @"bezel-color": [CPNull null],
87  @"button-bezel-color": [CPNull null],
88  @"button-text-color": [CPNull null],
89  @"button-image-plus": [CPNull null],
90  @"button-image-minus": [CPNull null],
91  @"button-image-action": [CPNull null],
92  };
93 }
94 
95 - (id)initWithFrame:(CGRect)aFrame
96 {
97  self = [super initWithFrame:aFrame];
98 
99  if (self)
100  {
101  _buttons = [];
102  [self setNeedsLayout];
103  }
104 
105  return self;
106 }
107 
108 - (void)awakeFromCib
109 {
110  var view = [self superview],
111  subview = self;
112 
113  while (view)
114  {
115  if ([view isKindOfClass:[CPSplitView class]])
116  {
117  var viewIndex = [[view subviews] indexOfObject:subview];
118  [view setButtonBar:self forDividerAtIndex:viewIndex];
119 
120  break;
121  }
122 
123  subview = view;
124  view = [view superview];
125  }
126 }
127 
128 - (void)setButtons:(CPArray)buttons
129 {
130  _buttons = [CPArray arrayWithArray:buttons];
131 
132  for (var i = 0, count = [_buttons count]; i < count; i++)
133  [_buttons[i] setBordered:YES];
134 
135  [self setNeedsLayout];
136 }
137 
138 - (CPArray)buttons
139 {
140  return [CPArray arrayWithArray:_buttons];
141 }
142 
143 - (void)setHasResizeControl:(BOOL)shouldHaveResizeControl
144 {
145  if (_hasResizeControl === shouldHaveResizeControl)
146  return;
147 
148  _hasResizeControl = !!shouldHaveResizeControl;
149  [self setNeedsLayout];
150 }
151 
152 - (BOOL)hasResizeControl
153 {
154  return _hasResizeControl;
155 }
156 
157 - (void)setResizeControlIsLeftAligned:(BOOL)shouldBeLeftAligned
158 {
159  if (_resizeControlIsLeftAligned === shouldBeLeftAligned)
160  return;
161 
162  _resizeControlIsLeftAligned = !!shouldBeLeftAligned;
163  [self setNeedsLayout];
164 }
165 
166 - (BOOL)resizeControlIsLeftAligned
167 {
168  return _resizeControlIsLeftAligned;
169 }
170 
171 - (CGRect)resizeControlFrame
172 {
173  var inset = [self currentValueForThemeAttribute:@"resize-control-inset"],
174  size = [self currentValueForThemeAttribute:@"resize-control-size"],
175  currentSize = [self bounds],
176  leftOrigin = _resizeControlIsLeftAligned ? 0 : currentSize.size.width - size.width - inset.right - inset.left;
177 
178  return CGRectMake(leftOrigin, 0, size.width + inset.left + inset.right, size.height + inset.top + inset.bottom);
179 }
180 
181 - (CGRect)rectForEphemeralSubviewNamed:(CPString)aName
182 {
183  if (aName === "resize-control-view")
184  {
185  var inset = [self currentValueForThemeAttribute:@"resize-control-inset"],
186  size = [self currentValueForThemeAttribute:@"resize-control-size"],
187  currentSize = [self bounds];
188 
189  if (_resizeControlIsLeftAligned)
190  return CGRectMake(inset.left, inset.top, size.width, size.height);
191  else
192  return CGRectMake(currentSize.size.width - size.width - inset.right, inset.top, size.width, size.height);
193  }
194 
195  return [super rectForEphemeralSubviewNamed:aName];
196 }
197 
198 - (CPView)createEphemeralSubviewNamed:(CPString)aName
199 {
200  if (aName === "resize-control-view")
201  return [[CPView alloc] initWithFrame:CGRectMakeZero()];
202 
203  return [super createEphemeralSubviewNamed:aName];
204 }
205 
206 - (void)layoutSubviews
207 {
208  [self setBackgroundColor:[self currentValueForThemeAttribute:@"bezel-color"]];
209 
210  var normalColor = [self valueForThemeAttribute:@"button-bezel-color" inState:CPThemeStateNormal],
211  highlightedColor = [self valueForThemeAttribute:@"button-bezel-color" inState:CPThemeStateHighlighted],
212  disabledColor = [self valueForThemeAttribute:@"button-bezel-color" inState:CPThemeStateDisabled],
213  textColor = [self valueForThemeAttribute:@"button-text-color" inState:CPThemeStateNormal];
214 
215  var buttonsNotHidden = [CPArray arrayWithArray:_buttons],
216  count = [buttonsNotHidden count];
217 
218  while (count--)
219  if ([buttonsNotHidden[count] isHidden])
220  [buttonsNotHidden removeObject:buttonsNotHidden[count]];
221 
222  var currentButtonOffset = _resizeControlIsLeftAligned ? CGRectGetMaxX([self bounds]) + 1 : -1,
223  bounds = [self bounds],
224  height = CGRectGetHeight(bounds) - 1,
225  frameWidth = CGRectGetWidth(bounds),
226  resizeRect = _hasResizeControl ? [self rectForEphemeralSubviewNamed:"resize-control-view"] : CGRectMakeZero(),
227  resizeWidth = CGRectGetWidth(resizeRect),
228  availableWidth = frameWidth - resizeWidth - 1;
229 
230  for (var i = 0, count = [buttonsNotHidden count]; i < count; i++)
231  {
232  var button = buttonsNotHidden[i],
233  width = CGRectGetWidth([button frame]);
234 
235  if (availableWidth > width)
236  availableWidth -= width;
237  else
238  break;
239 
240  if (_resizeControlIsLeftAligned)
241  {
242  [button setFrame:CGRectMake(currentButtonOffset - width, 1, width, height)];
243  currentButtonOffset -= width - 1;
244  }
245  else
246  {
247  [button setFrame:CGRectMake(currentButtonOffset, 1, width, height)];
248  currentButtonOffset += width - 1;
249  }
250 
251  [button setValue:normalColor forThemeAttribute:@"bezel-color" inState:CPThemeStateNormal | CPThemeStateBordered];
252  [button setValue:highlightedColor forThemeAttribute:@"bezel-color" inState:CPThemeStateHighlighted | CPThemeStateBordered];
253  [button setValue:disabledColor forThemeAttribute:@"bezel-color" inState:CPThemeStateDisabled | CPThemeStateBordered];
254  [button setValue:textColor forThemeAttribute:@"text-color" inState:CPThemeStateBordered];
255 
256  // FIXME shouldn't need this
257  [button setValue:normalColor forThemeAttribute:@"bezel-color" inState:CPThemeStateNormal | CPThemeStateBordered | CPPopUpButtonStatePullsDown];
258  [button setValue:highlightedColor forThemeAttribute:@"bezel-color" inState:CPThemeStateHighlighted | CPThemeStateBordered | CPPopUpButtonStatePullsDown];
259  [button setValue:disabledColor forThemeAttribute:@"bezel-color" inState:CPThemeStateDisabled | CPThemeStateBordered | CPPopUpButtonStatePullsDown];
260 
261  [self addSubview:button];
262  }
263 
264  if (_hasResizeControl)
265  {
266  var resizeControlView = [self layoutEphemeralSubviewNamed:@"resize-control-view"
267  positioned:CPWindowAbove
268  relativeToEphemeralSubviewNamed:nil];
269 
270  [resizeControlView setAutoresizingMask: _resizeControlIsLeftAligned ? CPViewMaxXMargin : CPViewMinXMargin];
271  [resizeControlView setBackgroundColor:[self currentValueForThemeAttribute:@"resize-control-color"]];
272  }
273 }
274 
275 - (void)setFrameSize:(CGSize)aSize
276 {
277  [super setFrameSize:aSize];
278  [self setNeedsLayout];
279 }
280 
281 @end
282 
283 var CPButtonBarHasResizeControlKey = @"CPButtonBarHasResizeControlKey",
284  CPButtonBarResizeControlIsLeftAlignedKey = @"CPButtonBarResizeControlIsLeftAlignedKey",
285  CPButtonBarButtonsKey = @"CPButtonBarButtonsKey";
286 
287 @implementation CPButtonBar (CPCoding)
288 
289 - (void)encodeWithCoder:(CPCoder)aCoder
290 {
291  [super encodeWithCoder:aCoder];
292 
293  [aCoder encodeBool:_hasResizeControl forKey:CPButtonBarHasResizeControlKey];
294  [aCoder encodeBool:_resizeControlIsLeftAligned forKey:CPButtonBarResizeControlIsLeftAlignedKey];
295  [aCoder encodeObject:_buttons forKey:CPButtonBarButtonsKey];
296 }
297 
298 - (id)initWithCoder:(CPCoder)aCoder
299 {
300  if (self = [super initWithCoder:aCoder])
301  {
302  _buttons = [aCoder decodeObjectForKey:CPButtonBarButtonsKey] || [];
303  _hasResizeControl = [aCoder decodeBoolForKey:CPButtonBarHasResizeControlKey];
304  _resizeControlIsLeftAligned = [aCoder decodeBoolForKey:CPButtonBarResizeControlIsLeftAlignedKey];
305  }
306 
307  return self;
308 }
309 
310 @end