PageRenderTime 47ms CodeModel.GetById 0ms RepoModel.GetById 0ms app.codeStats 1ms

/src/Three20UI/Sources/TTButton.m

https://github.com/GetMoPix/three20
Objective C | 487 lines | 290 code | 126 blank | 71 comment | 37 complexity | 5bd8c483f6a6eb20c40d84398464d41c MD5 | raw file
  1. //
  2. // Copyright 2009-2011 Facebook
  3. //
  4. // Licensed under the Apache License, Version 2.0 (the "License");
  5. // you may not use this file except in compliance with the License.
  6. // You may obtain a copy of the License at
  7. //
  8. // http://www.apache.org/licenses/LICENSE-2.0
  9. //
  10. // Unless required by applicable law or agreed to in writing, software
  11. // distributed under the License is distributed on an "AS IS" BASIS,
  12. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. // See the License for the specific language governing permissions and
  14. // limitations under the License.
  15. //
  16. #import "Three20UI/TTButton.h"
  17. // UI (private)
  18. #import "Three20UI/private/TTButtonContent.h"
  19. // UI
  20. #import "Three20UI/TTImageViewDelegate.h"
  21. // Style
  22. #import "Three20Style/TTGlobalStyle.h"
  23. #import "Three20Style/TTDefaultStyleSheet.h"
  24. #import "Three20Style/TTStyleContext.h"
  25. #import "Three20Style/TTTextStyle.h"
  26. #import "Three20Style/TTPartStyle.h"
  27. #import "Three20Style/TTBoxStyle.h"
  28. #import "Three20Style/TTImageStyle.h"
  29. #import "Three20Style/UIImageAdditions.h"
  30. // Core
  31. #import "Three20Core/TTCorePreprocessorMacros.h"
  32. static const CGFloat kHPadding = 8.0f;
  33. static const CGFloat kVPadding = 7.0f;
  34. ///////////////////////////////////////////////////////////////////////////////////////////////////
  35. ///////////////////////////////////////////////////////////////////////////////////////////////////
  36. ///////////////////////////////////////////////////////////////////////////////////////////////////
  37. @implementation TTButton
  38. @synthesize font = _font;
  39. @synthesize isVertical = _isVertical;
  40. @synthesize imageDelegate = _imageDelegate;
  41. ///////////////////////////////////////////////////////////////////////////////////////////////////
  42. - (id)initWithFrame:(CGRect)frame {
  43. self = [super initWithFrame:frame];
  44. if (self) {
  45. self.backgroundColor = [UIColor clearColor];
  46. self.contentMode = UIViewContentModeRedraw;
  47. }
  48. return self;
  49. }
  50. ///////////////////////////////////////////////////////////////////////////////////////////////////
  51. - (void)dealloc {
  52. TT_RELEASE_SAFELY(_content);
  53. TT_RELEASE_SAFELY(_font);
  54. self.imageDelegate = nil;
  55. [super dealloc];
  56. }
  57. ///////////////////////////////////////////////////////////////////////////////////////////////////
  58. ///////////////////////////////////////////////////////////////////////////////////////////////////
  59. #pragma mark -
  60. #pragma mark Public
  61. ///////////////////////////////////////////////////////////////////////////////////////////////////
  62. + (TTButton*)buttonWithStyle:(NSString*)selector {
  63. TTButton* button = [[[self alloc] init] autorelease];
  64. [button setStylesWithSelector:selector];
  65. return button;
  66. }
  67. ///////////////////////////////////////////////////////////////////////////////////////////////////
  68. + (TTButton*)buttonWithStyle:(NSString*)selector title:(NSString*)title {
  69. TTButton* button = [[[self alloc] init] autorelease];
  70. [button setTitle:title forState:UIControlStateNormal];
  71. [button setStylesWithSelector:selector];
  72. return button;
  73. }
  74. ///////////////////////////////////////////////////////////////////////////////////////////////////
  75. ///////////////////////////////////////////////////////////////////////////////////////////////////
  76. #pragma mark -
  77. #pragma mark Private
  78. ///////////////////////////////////////////////////////////////////////////////////////////////////
  79. - (id)keyForState:(UIControlState)state {
  80. static NSString* normalKey = @"normal";
  81. static NSString* highlighted = @"highlighted";
  82. static NSString* selected = @"selected";
  83. static NSString* disabled = @"disabled";
  84. if (state & UIControlStateHighlighted) {
  85. return highlighted;
  86. } else if (state & UIControlStateSelected) {
  87. return selected;
  88. } else if (state & UIControlStateDisabled) {
  89. return disabled;
  90. } else {
  91. return normalKey;
  92. }
  93. }
  94. ///////////////////////////////////////////////////////////////////////////////////////////////////
  95. - (TTButtonContent*)contentForState:(UIControlState)state {
  96. if (!_content) {
  97. _content = [[NSMutableDictionary alloc] init];
  98. }
  99. id key = [self keyForState:state];
  100. TTButtonContent* content = [_content objectForKey:key];
  101. if (!content) {
  102. content = [[[TTButtonContent alloc] initWithButton:self] autorelease];
  103. [_content setObject:content forKey:key];
  104. }
  105. return content;
  106. }
  107. ///////////////////////////////////////////////////////////////////////////////////////////////////
  108. - (TTButtonContent*)contentForCurrentState {
  109. TTButtonContent* content = nil;
  110. if (self.selected) {
  111. content = [self contentForState:UIControlStateSelected];
  112. } else if (self.highlighted) {
  113. content = [self contentForState:UIControlStateHighlighted];
  114. } else if (!self.enabled) {
  115. content = [self contentForState:UIControlStateDisabled];
  116. }
  117. return content ? content : [self contentForState:UIControlStateNormal];
  118. }
  119. ///////////////////////////////////////////////////////////////////////////////////////////////////
  120. - (NSString*)titleForCurrentState {
  121. TTButtonContent* content = [self contentForCurrentState];
  122. return content.title ? content.title : [self contentForState:UIControlStateNormal].title;
  123. }
  124. ///////////////////////////////////////////////////////////////////////////////////////////////////
  125. - (UIImage*)imageForCurrentState {
  126. TTButtonContent* content = [self contentForCurrentState];
  127. return content.image ? content.image : [self contentForState:UIControlStateNormal].image;
  128. }
  129. ///////////////////////////////////////////////////////////////////////////////////////////////////
  130. - (TTStyle*)styleForCurrentState {
  131. TTButtonContent* content = [self contentForCurrentState];
  132. return content.style ? content.style : [self contentForState:UIControlStateNormal].style;
  133. }
  134. ///////////////////////////////////////////////////////////////////////////////////////////////////
  135. - (UIFont*)fontForCurrentState {
  136. if (_font) {
  137. return _font;
  138. } else {
  139. TTStyle* style = [self styleForCurrentState];
  140. TTTextStyle* textStyle = (TTTextStyle*)[style firstStyleOfClass:[TTTextStyle class]];
  141. if (textStyle.font) {
  142. return textStyle.font;
  143. } else {
  144. return self.font;
  145. }
  146. }
  147. }
  148. ///////////////////////////////////////////////////////////////////////////////////////////////////
  149. ///////////////////////////////////////////////////////////////////////////////////////////////////
  150. #pragma mark -
  151. #pragma mark UIView
  152. ///////////////////////////////////////////////////////////////////////////////////////////////////
  153. - (void)drawRect:(CGRect)rect {
  154. TTStyle* style = [self styleForCurrentState];
  155. if (style) {
  156. CGRect textFrame = self.bounds;
  157. TTStyleContext* context = [[[TTStyleContext alloc] init] autorelease];
  158. context.delegate = self;
  159. TTPartStyle* imageStyle = [style styleForPart:@"image"];
  160. TTBoxStyle* imageBoxStyle = nil;
  161. CGSize imageSize = CGSizeZero;
  162. if (imageStyle) {
  163. imageBoxStyle = [imageStyle.style firstStyleOfClass:[TTBoxStyle class]];
  164. imageSize = [imageStyle.style addToSize:CGSizeZero context:context];
  165. if (_isVertical) {
  166. CGFloat height = imageSize.height + imageBoxStyle.margin.top + imageBoxStyle.margin.bottom;
  167. textFrame.origin.y += height;
  168. textFrame.size.height -= height;
  169. } else {
  170. textFrame.origin.x += imageSize.width + imageBoxStyle.margin.right;
  171. textFrame.size.width -= imageSize.width + imageBoxStyle.margin.right;
  172. }
  173. }
  174. context.delegate = self;
  175. context.frame = self.bounds;
  176. context.contentFrame = textFrame;
  177. context.font = [self fontForCurrentState];
  178. [style draw:context];
  179. if (imageStyle) {
  180. CGRect frame = context.contentFrame;
  181. if (_isVertical) {
  182. frame = self.bounds;
  183. frame.origin.x += imageBoxStyle.margin.left;
  184. frame.origin.y += imageBoxStyle.margin.top;
  185. } else {
  186. frame.size = imageSize;
  187. frame.origin.x += imageBoxStyle.margin.left;
  188. frame.origin.y += imageBoxStyle.margin.top;
  189. }
  190. context.frame = frame;
  191. context.contentFrame = context.frame;
  192. context.shape = nil;
  193. [imageStyle drawPart:context];
  194. }
  195. }
  196. }
  197. ///////////////////////////////////////////////////////////////////////////////////////////////////
  198. - (CGSize)sizeThatFits:(CGSize)size {
  199. TTStyleContext* context = [[[TTStyleContext alloc] init] autorelease];
  200. context.delegate = self;
  201. context.font = [self fontForCurrentState];
  202. TTStyle* style = [self styleForCurrentState];
  203. if (style) {
  204. return [style addToSize:CGSizeZero context:context];
  205. } else {
  206. return size;
  207. }
  208. }
  209. ///////////////////////////////////////////////////////////////////////////////////////////////////
  210. ///////////////////////////////////////////////////////////////////////////////////////////////////
  211. #pragma mark -
  212. #pragma mark UIControl
  213. ///////////////////////////////////////////////////////////////////////////////////////////////////
  214. - (void)setHighlighted:(BOOL)highlighted {
  215. [super setHighlighted:highlighted];
  216. [self setNeedsDisplay];
  217. }
  218. ///////////////////////////////////////////////////////////////////////////////////////////////////
  219. - (void)setSelected:(BOOL)selected {
  220. [super setSelected:selected];
  221. [self setNeedsDisplay];
  222. }
  223. ///////////////////////////////////////////////////////////////////////////////////////////////////
  224. - (void)setEnabled:(BOOL)enabled {
  225. [super setEnabled:enabled];
  226. [self setNeedsDisplay];
  227. }
  228. ///////////////////////////////////////////////////////////////////////////////////////////////////
  229. ///////////////////////////////////////////////////////////////////////////////////////////////////
  230. #pragma mark -
  231. #pragma mark UIAccessibility
  232. ///////////////////////////////////////////////////////////////////////////////////////////////////
  233. - (BOOL)isAccessibilityElement {
  234. return YES;
  235. }
  236. ///////////////////////////////////////////////////////////////////////////////////////////////////
  237. - (NSString *)accessibilityLabel {
  238. return [self titleForCurrentState];
  239. }
  240. ///////////////////////////////////////////////////////////////////////////////////////////////////
  241. - (UIAccessibilityTraits)accessibilityTraits {
  242. return [super accessibilityTraits] | UIAccessibilityTraitButton;
  243. }
  244. ///////////////////////////////////////////////////////////////////////////////////////////////////
  245. ///////////////////////////////////////////////////////////////////////////////////////////////////
  246. #pragma mark -
  247. #pragma mark TTStyleDelegate
  248. ///////////////////////////////////////////////////////////////////////////////////////////////////
  249. - (NSString*)textForLayerWithStyle:(TTStyle*)style {
  250. return [self titleForCurrentState];
  251. }
  252. ///////////////////////////////////////////////////////////////////////////////////////////////////
  253. - (UIImage*)imageForLayerWithStyle:(TTStyle*)style {
  254. return [self imageForCurrentState];
  255. }
  256. ///////////////////////////////////////////////////////////////////////////////////////////////////
  257. ///////////////////////////////////////////////////////////////////////////////////////////////////
  258. #pragma mark -
  259. #pragma mark TTImageViewDelegate
  260. ///////////////////////////////////////////////////////////////////////////////////////////////////
  261. - (void)imageView:(TTImageView*)imageView didLoadImage:(UIImage*)image {
  262. }
  263. ///////////////////////////////////////////////////////////////////////////////////////////////////
  264. ///////////////////////////////////////////////////////////////////////////////////////////////////
  265. #pragma mark -
  266. #pragma mark Public
  267. ///////////////////////////////////////////////////////////////////////////////////////////////////
  268. - (UIFont*)font {
  269. if (!_font) {
  270. _font = [TTSTYLEVAR(buttonFont) retain];
  271. }
  272. return _font;
  273. }
  274. ///////////////////////////////////////////////////////////////////////////////////////////////////
  275. - (void)setFont:(UIFont*)font {
  276. if (font != _font) {
  277. [_font release];
  278. _font = [font retain];
  279. [self setNeedsDisplay];
  280. }
  281. }
  282. ///////////////////////////////////////////////////////////////////////////////////////////////////
  283. - (NSString*)titleForState:(UIControlState)state {
  284. return [self contentForState:state].title;
  285. }
  286. ///////////////////////////////////////////////////////////////////////////////////////////////////
  287. - (void)setTitle:(NSString*)title forState:(UIControlState)state {
  288. TTButtonContent* content = [self contentForState:state];
  289. content.title = title;
  290. [self setNeedsDisplay];
  291. }
  292. ///////////////////////////////////////////////////////////////////////////////////////////////////
  293. - (NSString*)imageForState:(UIControlState)state {
  294. return [self contentForState:state].imageURL;
  295. }
  296. ///////////////////////////////////////////////////////////////////////////////////////////////////
  297. - (void)setImage:(NSString*)imageURL forState:(UIControlState)state {
  298. TTButtonContent* content = [self contentForState:state];
  299. content.delegate = self.imageDelegate;
  300. content.imageURL = imageURL;
  301. [self setNeedsDisplay];
  302. }
  303. ///////////////////////////////////////////////////////////////////////////////////////////////////
  304. - (TTStyle*)styleForState:(UIControlState)state {
  305. return [self contentForState:state].style;
  306. }
  307. ///////////////////////////////////////////////////////////////////////////////////////////////////
  308. - (void)setStyle:(TTStyle*)style forState:(UIControlState)state {
  309. TTButtonContent* content = [self contentForState:state];
  310. content.style = style;
  311. [self setNeedsDisplay];
  312. }
  313. ///////////////////////////////////////////////////////////////////////////////////////////////////
  314. - (void)setStylesWithSelector:(NSString*)selector {
  315. TTStyleSheet* ss = [TTStyleSheet globalStyleSheet];
  316. TTStyle* normalStyle = [ss styleWithSelector:selector forState:UIControlStateNormal];
  317. [self setStyle:normalStyle forState:UIControlStateNormal];
  318. TTStyle* highlightedStyle = [ss styleWithSelector:selector forState:UIControlStateHighlighted];
  319. [self setStyle:highlightedStyle forState:UIControlStateHighlighted];
  320. TTStyle* selectedStyle = [ss styleWithSelector:selector forState:UIControlStateSelected];
  321. [self setStyle:selectedStyle forState:UIControlStateSelected];
  322. TTStyle* disabledStyle = [ss styleWithSelector:selector forState:UIControlStateDisabled];
  323. [self setStyle:disabledStyle forState:UIControlStateDisabled];
  324. }
  325. ///////////////////////////////////////////////////////////////////////////////////////////////////
  326. - (void)suspendLoadingImages:(BOOL)suspended {
  327. TTButtonContent* content = [self contentForCurrentState];
  328. if (suspended) {
  329. [content stopLoading];
  330. } else if (!content.image) {
  331. [content reload];
  332. }
  333. }
  334. ///////////////////////////////////////////////////////////////////////////////////////////////////
  335. - (CGRect)rectForImage {
  336. TTStyle* style = [self styleForCurrentState];
  337. if (style) {
  338. TTStyleContext* context = [[[TTStyleContext alloc] init] autorelease];
  339. context.delegate = self;
  340. TTPartStyle* imagePartStyle = [style styleForPart:@"image"];
  341. if (imagePartStyle) {
  342. TTImageStyle* imageStyle = [imagePartStyle.style firstStyleOfClass:[TTImageStyle class]];
  343. TTBoxStyle* imageBoxStyle = [imagePartStyle.style firstStyleOfClass:[TTBoxStyle class]];
  344. CGSize imageSize = [imagePartStyle.style addToSize:CGSizeZero context:context];
  345. CGRect frame = context.contentFrame;
  346. if (_isVertical) {
  347. frame = self.bounds;
  348. frame.origin.x += imageBoxStyle.margin.left;
  349. frame.origin.y += imageBoxStyle.margin.top;
  350. } else {
  351. frame.size = imageSize;
  352. frame.origin.x += imageBoxStyle.margin.left;
  353. frame.origin.y += imageBoxStyle.margin.top;
  354. }
  355. UIImage* image = [self imageForCurrentState];
  356. return [image convertRect:frame withContentMode:imageStyle.contentMode];
  357. }
  358. }
  359. return CGRectZero;
  360. }
  361. @end