/AppKit/CPButtonBar.j

http://github.com/cacaodev/cappuccino · Unknown · 340 lines · 273 code · 67 blank · 0 comment · 0 complexity · c5be201767d2b6b9cf37c5ea1bb6d4ea MD5 · raw 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. @import "CPView.j"
  23. @import "CPWindow_Constants.j"
  24. @class CPSplitView
  25. @global CPPopUpButtonStatePullsDown
  26. @global CPKeyValueChangeOldKey
  27. @global CPKeyValueChangeNewKey
  28. @global CPKeyValueObservingOptionNew
  29. @global CPKeyValueObservingOptionOld
  30. @implementation CPButtonBar : CPView
  31. {
  32. BOOL _hasResizeControl;
  33. BOOL _resizeControlIsLeftAligned;
  34. CPArray _buttons;
  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. [button setBordered:NO];
  41. [button setImage:image];
  42. [button setImagePosition:CPImageOnly];
  43. return button;
  44. }
  45. + (id)minusButton
  46. {
  47. var button = [[CPButton alloc] initWithFrame:CGRectMake(0, 0, 35, 25)],
  48. image = [[CPTheme defaultTheme] valueForAttributeWithName:@"button-image-minus" forClass:[CPButtonBar class]];
  49. [button setBordered:NO];
  50. [button setImage:image];
  51. [button setImagePosition:CPImageOnly];
  52. return button;
  53. }
  54. + (id)actionPopupButton
  55. {
  56. var button = [[CPPopUpButton alloc] initWithFrame:CGRectMake(0, 0, 35, 25)],
  57. image = [[CPTheme defaultTheme] valueForAttributeWithName:@"button-image-action" forClass:[CPButtonBar class]];
  58. [button addItemWithTitle:nil];
  59. [[button lastItem] setImage:image];
  60. [button setImagePosition:CPImageOnly];
  61. [button setValue:CGInsetMake(0, 0, 0, 0) forThemeAttribute:"content-inset"];
  62. [button setPullsDown:YES];
  63. return button;
  64. }
  65. + (CPString)defaultThemeClass
  66. {
  67. return @"button-bar";
  68. }
  69. + (CPDictionary)themeAttributes
  70. {
  71. return @{
  72. @"resize-control-inset": CGInsetMake(0.0, 0.0, 0.0, 0.0),
  73. @"resize-control-size": CGSizeMakeZero(),
  74. @"resize-control-color": [CPNull null],
  75. @"bezel-color": [CPNull null],
  76. @"button-bezel-color": [CPNull null],
  77. @"button-text-color": [CPNull null],
  78. @"button-image-plus": [CPNull null],
  79. @"button-image-minus": [CPNull null],
  80. @"button-image-action": [CPNull null],
  81. };
  82. }
  83. - (id)initWithFrame:(CGRect)aFrame
  84. {
  85. self = [super initWithFrame:aFrame];
  86. if (self)
  87. {
  88. _buttons = [];
  89. [self setNeedsLayout];
  90. }
  91. return self;
  92. }
  93. - (void)awakeFromCib
  94. {
  95. var view = [self superview],
  96. subview = self;
  97. while (view)
  98. {
  99. if ([view isKindOfClass:[CPSplitView class]])
  100. {
  101. var viewIndex = [[view subviews] indexOfObject:subview];
  102. [view setButtonBar:self forDividerAtIndex:viewIndex];
  103. break;
  104. }
  105. subview = view;
  106. view = [view superview];
  107. }
  108. }
  109. - (void)setButtons:(CPArray)buttons
  110. {
  111. for (var i = [_buttons count] - 1; i >= 0; i--)
  112. {
  113. [_buttons[i] removeFromSuperview];
  114. [_buttons[i] removeObserver:self forKeyPath:@"hidden"];
  115. }
  116. _buttons = [CPArray arrayWithArray:buttons];
  117. for (var i = [_buttons count] - 1; i >= 0; i--)
  118. {
  119. [_buttons[i] addObserver:self forKeyPath:@"hidden" options:CPKeyValueObservingOptionNew | CPKeyValueObservingOptionOld context:nil];
  120. [_buttons[i] setBordered:YES];
  121. }
  122. [self setNeedsLayout];
  123. }
  124. - (CPArray)buttons
  125. {
  126. return [CPArray arrayWithArray:_buttons];
  127. }
  128. - (void)setHasResizeControl:(BOOL)shouldHaveResizeControl
  129. {
  130. if (_hasResizeControl === shouldHaveResizeControl)
  131. return;
  132. _hasResizeControl = !!shouldHaveResizeControl;
  133. [self setNeedsLayout];
  134. }
  135. - (BOOL)hasResizeControl
  136. {
  137. return _hasResizeControl;
  138. }
  139. - (void)setResizeControlIsLeftAligned:(BOOL)shouldBeLeftAligned
  140. {
  141. if (_resizeControlIsLeftAligned === shouldBeLeftAligned)
  142. return;
  143. _resizeControlIsLeftAligned = !!shouldBeLeftAligned;
  144. [self setNeedsLayout];
  145. }
  146. - (BOOL)resizeControlIsLeftAligned
  147. {
  148. return _resizeControlIsLeftAligned;
  149. }
  150. - (CGRect)resizeControlFrame
  151. {
  152. var inset = [self currentValueForThemeAttribute:@"resize-control-inset"],
  153. size = [self currentValueForThemeAttribute:@"resize-control-size"],
  154. currentSize = [self bounds],
  155. leftOrigin = _resizeControlIsLeftAligned ? 0 : currentSize.size.width - size.width - inset.right - inset.left;
  156. return CGRectMake(leftOrigin, 0, size.width + inset.left + inset.right, size.height + inset.top + inset.bottom);
  157. }
  158. - (CGRect)rectForEphemeralSubviewNamed:(CPString)aName
  159. {
  160. if (aName === "resize-control-view")
  161. {
  162. var inset = [self currentValueForThemeAttribute:@"resize-control-inset"],
  163. size = [self currentValueForThemeAttribute:@"resize-control-size"],
  164. currentSize = [self bounds];
  165. if (_resizeControlIsLeftAligned)
  166. return CGRectMake(inset.left, inset.top, size.width, size.height);
  167. else
  168. return CGRectMake(currentSize.size.width - size.width - inset.right, inset.top, size.width, size.height);
  169. }
  170. return [super rectForEphemeralSubviewNamed:aName];
  171. }
  172. - (CPView)createEphemeralSubviewNamed:(CPString)aName
  173. {
  174. if (aName === "resize-control-view")
  175. return [[CPView alloc] initWithFrame:CGRectMakeZero()];
  176. return [super createEphemeralSubviewNamed:aName];
  177. }
  178. - (void)layoutSubviews
  179. {
  180. [self setBackgroundColor:[self currentValueForThemeAttribute:@"bezel-color"]];
  181. var normalColor = [self valueForThemeAttribute:@"button-bezel-color" inState:CPThemeStateNormal],
  182. highlightedColor = [self valueForThemeAttribute:@"button-bezel-color" inState:CPThemeStateHighlighted],
  183. disabledColor = [self valueForThemeAttribute:@"button-bezel-color" inState:CPThemeStateDisabled],
  184. textColor = [self valueForThemeAttribute:@"button-text-color" inState:CPThemeStateNormal];
  185. var buttonsNotHidden = [CPArray arrayWithArray:_buttons],
  186. count = [buttonsNotHidden count];
  187. while (count--)
  188. {
  189. var button = buttonsNotHidden[count];
  190. if ([button isHidden])
  191. {
  192. [button removeFromSuperview];
  193. [buttonsNotHidden removeObject:button];
  194. }
  195. }
  196. var currentButtonOffset = _resizeControlIsLeftAligned ? CGRectGetMaxX([self bounds]) + 1 : -1,
  197. bounds = [self bounds],
  198. height = CGRectGetHeight(bounds) - 1,
  199. frameWidth = CGRectGetWidth(bounds),
  200. resizeRect = _hasResizeControl ? [self rectForEphemeralSubviewNamed:"resize-control-view"] : CGRectMakeZero(),
  201. resizeWidth = CGRectGetWidth(resizeRect),
  202. availableWidth = frameWidth - resizeWidth - 1;
  203. for (var i = 0, count = [buttonsNotHidden count]; i < count; i++)
  204. {
  205. var button = buttonsNotHidden[i],
  206. width = CGRectGetWidth([button frame]);
  207. if (availableWidth > width)
  208. availableWidth -= width;
  209. else
  210. break;
  211. if (_resizeControlIsLeftAligned)
  212. {
  213. [button setFrame:CGRectMake(currentButtonOffset - width, 1, width, height)];
  214. currentButtonOffset -= width - 1;
  215. }
  216. else
  217. {
  218. [button setFrame:CGRectMake(currentButtonOffset, 1, width, height)];
  219. currentButtonOffset += width - 1;
  220. }
  221. [button setValue:normalColor forThemeAttribute:@"bezel-color" inState:[CPThemeStateNormal, CPThemeStateBordered]];
  222. [button setValue:highlightedColor forThemeAttribute:@"bezel-color" inState:[CPThemeStateHighlighted, CPThemeStateBordered, ]];
  223. [button setValue:disabledColor forThemeAttribute:@"bezel-color" inState:[CPThemeStateDisabled, CPThemeStateBordered]];
  224. [button setValue:textColor forThemeAttribute:@"text-color" inState:CPThemeStateBordered];
  225. // FIXME shouldn't need this
  226. [button setValue:normalColor forThemeAttribute:@"bezel-color" inState:[CPThemeStateNormal, CPThemeStateBordered, CPPopUpButtonStatePullsDown]];
  227. [button setValue:highlightedColor forThemeAttribute:@"bezel-color" inState:[CPThemeStateHighlighted, CPThemeStateBordered, CPPopUpButtonStatePullsDown]];
  228. [button setValue:disabledColor forThemeAttribute:@"bezel-color" inState:[CPThemeStateDisabled, CPThemeStateBordered, CPPopUpButtonStatePullsDown]];
  229. [self addSubview:button];
  230. }
  231. if (_hasResizeControl)
  232. {
  233. var resizeControlView = [self layoutEphemeralSubviewNamed:@"resize-control-view"
  234. positioned:CPWindowAbove
  235. relativeToEphemeralSubviewNamed:nil];
  236. [resizeControlView setAutoresizingMask: _resizeControlIsLeftAligned ? CPViewMaxXMargin : CPViewMinXMargin];
  237. [resizeControlView setBackgroundColor:[self currentValueForThemeAttribute:@"resize-control-color"]];
  238. }
  239. }
  240. - (void)observeValueForKeyPath:(CPString)keyPath ofObject:(id)object change:(CPDictionary)change context:(id)context
  241. {
  242. if ([change objectForKey:CPKeyValueChangeOldKey] == [change objectForKey:CPKeyValueChangeNewKey])
  243. return;
  244. [self setNeedsLayout];
  245. }
  246. - (void)setFrameSize:(CGSize)aSize
  247. {
  248. [super setFrameSize:aSize];
  249. [self setNeedsLayout];
  250. }
  251. @end
  252. var CPButtonBarHasResizeControlKey = @"CPButtonBarHasResizeControlKey",
  253. CPButtonBarResizeControlIsLeftAlignedKey = @"CPButtonBarResizeControlIsLeftAlignedKey",
  254. CPButtonBarButtonsKey = @"CPButtonBarButtonsKey";
  255. @implementation CPButtonBar (CPCoding)
  256. - (void)encodeWithCoder:(CPCoder)aCoder
  257. {
  258. [super encodeWithCoder:aCoder];
  259. [aCoder encodeBool:_hasResizeControl forKey:CPButtonBarHasResizeControlKey];
  260. [aCoder encodeBool:_resizeControlIsLeftAligned forKey:CPButtonBarResizeControlIsLeftAlignedKey];
  261. [aCoder encodeObject:_buttons forKey:CPButtonBarButtonsKey];
  262. }
  263. - (id)initWithCoder:(CPCoder)aCoder
  264. {
  265. if (self = [super initWithCoder:aCoder])
  266. {
  267. _buttons = [aCoder decodeObjectForKey:CPButtonBarButtonsKey] || [];
  268. _hasResizeControl = [aCoder decodeBoolForKey:CPButtonBarHasResizeControlKey];
  269. _resizeControlIsLeftAligned = [aCoder decodeBoolForKey:CPButtonBarResizeControlIsLeftAlignedKey];
  270. }
  271. return self;
  272. }
  273. @end