Commits

Joe Hewitt  committed cb87286

* Support background images with a rounded rect on TTStyleView
* Rename TTBackgroundView to TTStyleView

  • Participants
  • Parent commits 2faca25

Comments (0)

Files changed (26)

File src/TTActivityLabel.m

 #import "Three20/TTActivityLabel.h"
-#import "Three20/TTBackgroundView.h"
+#import "Three20/TTStyleView.h"
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
     
     self.backgroundColor = [UIColor clearColor];
   
-    _bezelView = [[TTBackgroundView alloc] initWithFrame:CGRectZero];
+    _bezelView = [[TTStyleView alloc] initWithFrame:CGRectZero];
     if (_style == TTActivityLabelStyleBlackBezel || _style == TTActivityLabelStyleBlackThinBezel) {
       _bezelView.opaque = NO;
       _bezelView.style = TTDrawFillRect;
       _bezelView.opaque = NO;
       _bezelView.style = TTDrawFillRect;
       _bezelView.fillColor = [UIColor colorWithRed:1 green:1 blue:1 alpha:1];
-      _bezelView.strokeColor = [UIColor colorWithRed:0.7 green:0.7 blue:0.7 alpha:1];
+      _bezelView.borderColor = [UIColor colorWithRed:0.7 green:0.7 blue:0.7 alpha:1];
       _bezelView.borderRadius = 10;
     } else if (_style == TTActivityLabelStyleWhiteBox) {
       _bezelView.backgroundColor = [UIColor whiteColor];

File src/TTAppearance.m

 }
 
 - (void)drawRoundedRect:(CGRect)rect fill:(UIColor**)fillColors fillCount:(int)fillCount
-    stroke:(UIColor*)strokeColor radius:(CGFloat)radius {
+    stroke:(UIColor*)strokeColor thickness:(CGFloat)thickness radius:(CGFloat)radius {
   CGContextRef context = UIGraphicsGetCurrentContext();
 
   if (radius == TT_RADIUS_ROUNDED) {
   if (strokeColor) {
     CGContextSaveGState(context);
     [self addRoundedRectToPath:context rect:rect radius:radius];
-    [self stroke:strokeColor];
+    [self stroke:strokeColor thickness:thickness];
     CGContextRestoreGState(context);
   }
 }
 
 - (void)drawRoundedMask:(CGRect)rect fill:(UIColor**)fillColors stroke:(UIColor*)strokeColor
-    radius:(CGFloat)radius {
+        thickness:(CGFloat)thickness radius:(CGFloat)radius {
   CGContextRef context = UIGraphicsGetCurrentContext();
 
   if (radius == TT_RADIUS_ROUNDED) {
     CGContextSaveGState(context);
     [self addRoundedRectToPath:context rect:rect radius:radius];
     [strokeColor setStroke];
-    CGContextSetLineWidth(context, 1.0);
+    CGContextSetLineWidth(context, thickness);
     CGContextStrokePath(context);
     CGContextRestoreGState(context);
   }
 }
 
 - (void)drawReflection:(CGRect)rect fill:(UIColor**)fillColors fillCount:(int)fillCount
-    stroke:(UIColor*)strokeColor radius:(CGFloat)radius {
+    stroke:(UIColor*)strokeColor thickness:(CGFloat)thickness radius:(CGFloat)radius {
   if (fillColors && fillCount) {
     UIColor* tintColor = fillColors[0];
     UIColor* ligherTint = [tintColor transformHue:1 saturation:0.4 value:1.1];
     
     CGRect topRect = CGRectMake(rect.origin.x, rect.origin.y,
       rect.size.width, rect.size.height/1.5);
-    [[TTAppearance appearance] draw:TTDrawFillRect rect:topRect
-      fill:barFill fillCount:2 stroke:nil radius:0];
+    [self draw:TTDrawFillRect rect:topRect
+          fill:barFill fillCount:2 stroke:nil thickness:thickness radius:0];
 
     UIColor* tintFill[] = {tintColor};
     CGRect bottomRect = CGRectMake(rect.origin.x, ceil(rect.origin.y+rect.size.height/(2*2)),
       rect.size.width, (rect.size.height/2));
     [self draw:TTDrawFillRect rect:bottomRect
-      fill:tintFill fillCount:1 stroke:nil radius:0];
+          fill:tintFill fillCount:1 stroke:nil thickness:thickness radius:0];
 
     UIColor* highlight = [UIColor colorWithWhite:1 alpha:0.3];
     [self draw:TTDrawStrokeTop rect:CGRectInset(rect, 0, 1)
-      fill:nil fillCount:0 stroke:highlight radius:0];
+          fill:nil fillCount:0 stroke:highlight thickness:thickness radius:0];
 
     UIColor* shadow = [UIColor colorWithWhite:0 alpha:0.1];
     [self draw:TTDrawStrokeBottom rect:rect
-      fill:nil fillCount:0 stroke:shadow radius:0];
+          fill:nil fillCount:0 stroke:shadow thickness:thickness radius:0];
   }
 }
 
 }
 
 - (void)drawRoundInnerShadow:(CGRect)rect fill:(UIColor**)fillColors fillCount:(int)fillCount
-    stroke:(UIColor*)strokeColor radius:(CGFloat)radius {
+    stroke:(UIColor*)strokeColor thickness:(CGFloat)thickness radius:(CGFloat)radius {
   UIImage* image = [[UIImage imageNamed:@"Three20.bundle/images/textBox.png"]
     stretchableImageWithLeftCapWidth:15 topCapHeight:15];
   [image drawInRect:rect];
 
   if (strokeColor) {
-    [self drawRoundedRect:rect fill:nil fillCount:0 stroke:strokeColor radius:TT_RADIUS_ROUNDED];
+    [self drawRoundedRect:rect fill:nil fillCount:0 stroke:strokeColor thickness:thickness
+          radius:TT_RADIUS_ROUNDED];
   }
 }
 
-- (void)strokeLines:(CGRect)rect style:(TTDrawStyle)style stroke:(UIColor*)strokeColor {
+- (void)strokeLines:(CGRect)rect style:(TTDrawStyle)style stroke:(UIColor*)strokeColor
+        thickness:(CGFloat)thickness {
   CGContextRef context = UIGraphicsGetCurrentContext();
   CGContextSaveGState(context);
 
   [strokeColor setStroke];
-  CGContextSetLineWidth(context, 1.0);
+  CGContextSetLineWidth(context, thickness);
   
   if (style == TTDrawStrokeTop) {
     CGPoint points[] = {rect.origin.x, rect.origin.y-0.5,
 
 - (void)draw:(TTDrawStyle)style rect:(CGRect)rect fill:(UIColor**)fillColors
     fillCount:(int)fillCount stroke:(UIColor*)strokeColor radius:(CGFloat)radius {
+  [self draw:style rect:rect fill:fillColors fillCount:fillCount stroke:strokeColor
+        thickness:1 radius:radius];
+}
+
+- (void)draw:(TTDrawStyle)style rect:(CGRect)rect fill:(UIColor**)fillColors
+    fillCount:(int)fillCount stroke:(UIColor*)strokeColor thickness:(CGFloat)thickness
+    radius:(CGFloat)radius {
   switch (style) {
     case TTDrawFillRect:
       [self drawRoundedRect:rect fill:fillColors fillCount:fillCount stroke:strokeColor
-        radius:radius];
+        thickness:thickness radius:radius];
       break;
     case TTDrawFillRectInverted:
-      [self drawRoundedMask:rect fill:fillColors stroke:strokeColor radius:radius];
+      [self drawRoundedMask:rect fill:fillColors stroke:strokeColor thickness:thickness
+            radius:radius];
       break;
     case TTDrawReflection:
       [self drawReflection:rect fill:fillColors fillCount:fillCount stroke:strokeColor
-        radius:radius];
+            thickness:thickness radius:radius];
       break;
     case TTDrawInnerShadow:
       [self drawInnerShadow:rect];
       break;
     case TTDrawRoundInnerShadow:
       [self drawRoundInnerShadow:rect fill:fillColors fillCount:fillCount stroke:strokeColor
-        radius:radius];
+        thickness:thickness radius:radius];
       break;
     case TTDrawStrokeTop:
     case TTDrawStrokeRight:
     case TTDrawStrokeBottom:
     case TTDrawStrokeLeft:
-      [self strokeLines:rect style:style stroke:strokeColor];
+      [self strokeLines:rect style:style stroke:strokeColor thickness:thickness];
       break;
     default:
       break;
     radius:TT_RADIUS_ROUNDED];
 }
 
-- (void)drawLine:(CGPoint)from to:(CGPoint)to color:(UIColor*)color {
+- (void)drawLine:(CGPoint)from to:(CGPoint)to color:(UIColor*)color thickness:(CGFloat)thickness {
   CGContextRef context = UIGraphicsGetCurrentContext();
   CGContextSaveGState(context);
 
   CGPoint points[] = {from.x, from.y, to.x, from.y};
   [color setStroke];
-  CGContextSetLineWidth(context, 1.0);
+  CGContextSetLineWidth(context, thickness);
   CGContextStrokeLineSegments(context, points, 2);
 
   CGContextRestoreGState(context);
   CGColorSpaceRelease(space);
 }
 
-- (void)stroke:(UIColor*)strokeColor {
+- (void)stroke:(UIColor*)strokeColor thickness:(CGFloat)thickness {
   CGContextRef context = UIGraphicsGetCurrentContext();
   [strokeColor setStroke];
-  CGContextSetLineWidth(context, 1.0);
+  CGContextSetLineWidth(context, thickness);
   CGContextStrokePath(context);
 }
 

File src/TTBackgroundView.m

-#import "Three20/TTBackgroundView.h"
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-@implementation TTBackgroundView
-
-@synthesize style = _style, fillColor = _fillColor, fillColor2 = _fillColor2,
-  strokeColor = _strokeColor, borderRadius = _borderRadius, backgroundInset = _backgroundInset;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-- (id)initWithFrame:(CGRect)frame {
-  if (self = [super initWithFrame:frame]) {
-    _style = TTDrawStyleNone;
-    _fillColor = nil;
-    _fillColor2 = nil;
-    _strokeColor = nil;
-    _borderRadius = 0;
-    _backgroundInset = UIEdgeInsetsZero;
-    self.contentMode = UIViewContentModeRedraw;
-  }
-  return self;
-}
-
-- (void)dealloc {
-  [_fillColor release];
-  [_fillColor2 release];
-  [_strokeColor release];
-  [super dealloc];
-}
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// UIView
-
-- (void)drawRect:(CGRect)rect {
-  if (_style) {
-    CGRect frame = CGRectMake(rect.origin.x + _backgroundInset.left,
-      rect.origin.y + _backgroundInset.top,
-      rect.size.width - (_backgroundInset.left + _backgroundInset.right),
-      rect.size.height - (_backgroundInset.top + _backgroundInset.bottom));
-
-    if (_fillColor2 && _fillColor) {
-      UIColor* fillColors[] = {_fillColor, _fillColor2};
-      [[TTAppearance appearance] draw:_style rect:frame fill:fillColors fillCount:2
-        stroke:_strokeColor radius:_borderRadius];
-    } else if (_fillColor) {
-      [[TTAppearance appearance] draw:_style rect:frame fill:&_fillColor fillCount:1
-        stroke:_strokeColor radius:_borderRadius];
-    } else {
-      [[TTAppearance appearance] draw:_style rect:frame fill:nil fillCount:0
-        stroke:_strokeColor radius:_borderRadius];
-    }
-  }
-}
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-- (void)setFillColor:(UIColor*)color {
-  [_fillColor release];
-  _fillColor = [color retain];
-  
-  [self setNeedsDisplay];
-}
-
-@end
-

File src/TTLinkView.m

 #include "Three20/TTLinkView.h"
 #include "Three20/TTNavigationCenter.h"
-#include "Three20/TTBackgroundView.h"
+#include "Three20/TTStyleView.h"
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
 - (void)setHighlighted:(BOOL)highlighted {
   [super setHighlighted:highlighted];
   if (!_screenView) {
-    _screenView = [[TTBackgroundView alloc] initWithFrame:self.bounds];
+    _screenView = [[TTStyleView alloc] initWithFrame:self.bounds];
     _screenView.style = TTDrawFillRect;
     _screenView.fillColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.5];
     _screenView.opaque = NO;

File src/TTMessageController.m

   [self.view addSubview:_scrollView];
 
   _textEditor = [[TTTextEditor alloc] initWithFrame:CGRectMake(0, 0, appFrame.size.width, 0)];
-  _textEditor.delegate = self;
+  _textEditor.textDelegate = self;
   _textEditor.backgroundColor = [UIColor whiteColor];
   _textEditor.textView.font = [UIFont systemFontOfSize:15];
   _textEditor.autoresizesToText = YES;

File src/TTSearchBar.m

 #import "Three20/TTSearchBar.h"
 #import "Three20/TTSearchTextField.h"
-#import "Three20/TTBackgroundView.h"
+#import "Three20/TTStyleView.h"
 #import "Three20/TTAppearance.h"
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
   if (self = [super initWithFrame:frame]) {
     _style = TTDrawReflection;
     
-    _boxView = [[TTBackgroundView alloc] initWithFrame:CGRectZero];
+    _boxView = [[TTStyleView alloc] initWithFrame:CGRectZero];
     _boxView.backgroundColor = [UIColor clearColor];
     _boxView.style = TTDrawRoundInnerShadow;
     _boxView.contentMode = UIViewContentModeRedraw;
     [_tintColor release];
     _tintColor = [tintColor retain];
     
-    _boxView.strokeColor = [_tintColor transformHue:1 saturation:1 value:0.9];
+    _boxView.borderColor = [_tintColor transformHue:1 saturation:1 value:0.9];
   }
 }
 

File src/TTSearchTextField.m

 #import "Three20/TTSearchTextField.h"
 #import "Three20/TTNavigationCenter.h"
-#import "Three20/TTBackgroundView.h"
+#import "Three20/TTStyleView.h"
 #import "Three20/TTTableFieldCell.h"
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
     self.tableView;
     
     if (!_shadowView) {
-      _shadowView = [[TTBackgroundView alloc] initWithFrame:CGRectZero];
+      _shadowView = [[TTStyleView alloc] initWithFrame:CGRectZero];
       _shadowView.style = TTDrawInnerShadow;
       _shadowView.backgroundColor = [UIColor clearColor];
       _shadowView.contentMode = UIViewContentModeRedraw;

File src/TTStyleView.m

+#import "Three20/TTStyleView.h"
+#import "Three20/TTURLRequest.h"
+#import "Three20/TTURLResponse.h"
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+@implementation TTStyleView
+
+@synthesize delegate = _delegate, style = _style, fillColor = _fillColor, fillColor2 = _fillColor2,
+  borderColor = _borderColor, borderWidth = _borderWidth, borderRadius = _borderRadius,
+  backgroundInset = _backgroundInset, backgroundImageURL = _backgroundImageURL,
+  backgroundImage = _backgroundImage, backgroundImageDefault = _backgroundImageDefault;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// private
+
+- (CGRect)backgroundBounds {
+  CGRect frame = self.frame;
+  return CGRectMake(_backgroundInset.left, _backgroundInset.top,
+    frame.size.width - (_backgroundInset.left + _backgroundInset.right),
+    frame.size.height - (_backgroundInset.top + _backgroundInset.bottom));
+}
+
+- (void)drawBackground:(CGRect)rect {
+  if (_style) {
+    if (_fillColor2 && _fillColor) {
+      UIColor* fillColors[] = {_fillColor, _fillColor2};
+      [[TTAppearance appearance] draw:_style rect:rect fill:fillColors fillCount:2
+        stroke:nil thickness:_borderWidth radius:_borderRadius];
+    } else if (_fillColor) {
+      [[TTAppearance appearance] draw:_style rect:rect fill:&_fillColor fillCount:1
+        stroke:nil thickness:_borderWidth radius:_borderRadius];
+    }
+  }
+}
+
+- (void)drawImage:(CGRect)rect {
+  if (_backgroundImage) {
+    if (_borderRadius) {
+      [_backgroundImage drawInRect:rect radius:_borderRadius];
+    } else {
+      [_backgroundImage drawInRect:rect];
+    }
+  } else if (_backgroundImageDefault) {
+    if (_borderRadius) {
+      [_backgroundImageDefault drawInRect:rect radius:_borderRadius];
+    } else {
+      [_backgroundImageDefault drawInRect:rect];
+    }
+  }
+}
+
+- (void)drawForeground:(CGRect)rect {
+  if (_style) {
+    if (_borderColor) {
+      [[TTAppearance appearance] draw:_style rect:rect fill:nil fillCount:0
+        stroke:_borderColor thickness:_borderWidth radius:_borderRadius];
+    }
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// NSObject
+
+- (id)initWithFrame:(CGRect)frame {
+  if (self = [super initWithFrame:frame]) {
+    _delegate = nil;
+    _request = nil;
+    _style = TTDrawFillRect;
+    _fillColor = nil;
+    _fillColor2 = nil;
+    _borderColor = nil;
+    _borderWidth = 1;
+    _borderRadius = 0;
+    _backgroundInset = UIEdgeInsetsZero;
+    _backgroundImageURL = nil;
+    _backgroundImage = nil;
+    _backgroundImageDefault = nil;
+
+    self.contentMode = UIViewContentModeRedraw;
+  }
+  return self;
+}
+
+- (void)dealloc {
+  _delegate = nil;
+  [_request cancel];
+  [_request release];
+  [_fillColor release];
+  [_fillColor2 release];
+  [_borderColor release];
+  [_backgroundImageURL release];
+  [_backgroundImage release];
+  [_backgroundImageDefault release];
+  [super dealloc];
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// UIView
+
+- (void)drawRect:(CGRect)rect {
+  CGRect bounds = self.backgroundBounds;
+  [self drawBackground:bounds];
+  [self drawImage:rect];
+  [self drawForeground:bounds];
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+// TTURLRequestDelegate
+
+- (void)requestDidStartLoad:(TTURLRequest*)request {
+  _request = [request retain];
+  
+  if ([_delegate respondsToSelector:@selector(styleViewDidStartLoad:)]) {
+    [_delegate styleViewDidStartLoad:self];
+  }
+}
+
+- (void)requestDidFinishLoad:(TTURLRequest*)request {
+  TTURLImageResponse* response = request.response;
+  self.backgroundImage = response.image;
+  
+  [_request release];
+  _request = nil;
+}
+
+- (void)request:(TTURLRequest*)request didFailLoadWithError:(NSError*)error {
+  [_request release];
+  _request = nil;
+
+  if ([_delegate respondsToSelector:@selector(styleView:didFailLoadWithError:)]) {
+    [_delegate styleView:self didFailLoadWithError:error];
+  }
+}
+
+- (void)requestDidCancelLoad:(TTURLRequest*)request {
+  [_request release];
+  _request = nil;
+
+  if ([_delegate respondsToSelector:@selector(styleView:didFailLoadWithError:)]) {
+    [_delegate styleView:self didFailLoadWithError:nil];
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// public
+
+- (void)setFillColor:(UIColor*)color {
+  [_fillColor release];
+  _fillColor = [color retain];
+  
+  [self setNeedsDisplay];
+}
+
+- (void)setBackgroundImageURL:(NSString*)url {
+  if (self.backgroundImage && _backgroundImageURL && [url isEqualToString:_backgroundImageURL])
+    return;
+  
+  [self stopLoading];
+  [_backgroundImageURL release];
+  _backgroundImageURL = [url retain];
+  
+  if (!_backgroundImageURL || !_backgroundImageURL.length) {
+    if (self.backgroundImage != _backgroundImageDefault) {
+      self.backgroundImage = _backgroundImageDefault;
+    }
+  } else {
+    [self reload];
+  }
+}
+
+
+- (void)setBackgroundImage:(UIImage*)image {
+  if (image != _backgroundImage) {
+    [_backgroundImage release];
+    _backgroundImage = [image retain];
+  }
+  
+  if (!_backgroundImageDefault || image != _backgroundImageDefault) {
+    if ([_delegate respondsToSelector:@selector(styleView:didLoadImage:)]) {
+      [_delegate styleView:self didLoadImage:image];
+    }
+  }
+}
+
+- (BOOL)isLoading {
+  return !!_request;
+}
+
+- (BOOL)isLoaded {
+  return self.backgroundImage && self.backgroundImage != _backgroundImageDefault;
+}
+
+- (void)reload {
+  if (_request)
+    return;
+  
+  TTURLRequest* request = [TTURLRequest requestWithURL:_backgroundImageURL delegate:self];
+  request.response = [[[TTURLImageResponse alloc] init] autorelease];
+  if (_backgroundImageURL && ![request send]) {
+    // Put the default image in place while waiting for the request to load
+    if (_backgroundImageDefault && self.backgroundImage != _backgroundImageDefault) {
+      self.backgroundImage = _backgroundImageDefault;
+    }
+  }
+}
+
+- (void)stopLoading {
+  [_request cancel];
+}
+
+
+@end

File src/TTTableFieldCell.m

 #import "Three20/TTTableFieldCell.h"
 #import "Three20/TTTableField.h"
-#import "Three20/TTImageView.h"
+#import "Three20/TTStyleView.h"
 #import "Three20/TTErrorView.h"
 #import "Three20/TTNavigationCenter.h"
 #import "Three20/TTURLCache.h"
 
 - (id)initWithFrame:(CGRect)frame reuseIdentifier:(NSString*)identifier {
   if (self = [super initWithFrame:frame reuseIdentifier:identifier]) {
-    _iconView = [[TTImageView alloc] initWithFrame:CGRectZero];
+    _iconView = [[TTStyleView alloc] initWithFrame:CGRectZero];
+    _iconView.borderRadius = 8;
+    _iconView.backgroundColor = [UIColor clearColor];
     [self.contentView addSubview:_iconView];
 	}
 	return self;
   if (!image) {
     image = field.defaultImage;
   }
-  if (_iconView.url) {
+  if (_iconView.backgroundImageURL) {
     CGFloat iconWidth = image
       ? image.size.width
       : (field.image ? kDefaultIconSize : 0);
     [super setObject:object];
   
     TTImageTableField* field = object;
-    _iconView.defaultImage = field.defaultImage;
-    _iconView.url = field.image;
+    _iconView.backgroundImageDefault = field.defaultImage;
+    _iconView.backgroundImageURL = field.image;
   }  
 }
 @end
     ? image.size.height
     : (field.image ? kDefaultIconSize : 0);
   
-  if (_iconView.url) {
+  if (_iconView.backgroundImageURL) {
     CGFloat innerWidth = self.contentView.width - (kHPadding*2 + iconWidth + kKeySpacing);
     CGFloat innerHeight = self.contentView.height - kVPadding*2;
     _label.frame = CGRectMake(kHPadding, kVPadding, innerWidth, innerHeight);

File src/TTTextEditor.m

 
 @implementation TTTextEditor
 
-@synthesize delegate = _delegate, textView = _textView, placeholder = _placeholder,
+@synthesize textDelegate = _textDelegate, textView = _textView, placeholder = _placeholder,
   fixedText = _fixedText, minNumberOfLines = _minNumberOfLines,
   maxNumberOfLines = _maxNumberOfLines, editing = _editing, autoresizesToText = _autoresizesToText,
   showsExtraLine= _showsExtraLine;
 
 - (id)initWithFrame:(CGRect)frame {
   if (self = [super initWithFrame:frame]) {
-    _delegate = nil;
+    _textDelegate = nil;
     _internal = [[TTTextEditorInternal alloc] initWithTextEditor:self];
     _placeholder = nil;
     _fixedText = nil;
   CGFloat diff = newHeight - oldHeight;
   
   if (oldHeight && diff) {
-    if ([_delegate respondsToSelector:@selector(textEditor:shouldResizeBy:)]) {
-      if (![_delegate textEditor:self shouldResizeBy:diff]) {
+    if ([_textDelegate respondsToSelector:@selector(textEditor:shouldResizeBy:)]) {
+      if (![_textDelegate textEditor:self shouldResizeBy:diff]) {
         return;
       }
     }
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
 - (void)setDelegate:(id<TTTextEditorDelegate>)delegate {
-  _delegate = delegate;
+  _textDelegate = delegate;
   _internal.delegate = delegate;
 }
 

File src/TTThumbView.m

 #import "Three20/TTThumbView.h"
 #import "Three20/TTImageView.h"
-#import "Three20/TTBackgroundView.h"
+#import "Three20/TTStyleView.h"
 
 @implementation TTThumbView
 
     imageView.userInteractionEnabled = NO;
     [self addSubview:imageView];
 
-    borderView = [[TTBackgroundView alloc] initWithFrame:CGRectZero];
+    borderView = [[TTStyleView alloc] initWithFrame:CGRectZero];
     borderView.opaque = NO;
     borderView.style = TTDrawFillRect;
-    borderView.strokeColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.4];
+    borderView.borderColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.4];
     borderView.contentMode = UIViewContentModeRedraw;
     borderView.userInteractionEnabled = NO;
     [self addSubview:borderView];

File src/Three20.xcodeproj/project.pbxproj

 		BEF31F710F352E64000DE5D2 /* TTErrorView.m in Sources */ = {isa = PBXBuildFile; fileRef = BEF31F470F352E64000DE5D2 /* TTErrorView.m */; };
 		BEF31F720F352E64000DE5D2 /* TTGlobal.m in Sources */ = {isa = PBXBuildFile; fileRef = BEF31F480F352E64000DE5D2 /* TTGlobal.m */; };
 		BEF31F730F352E64000DE5D2 /* TTImageView.m in Sources */ = {isa = PBXBuildFile; fileRef = BEF31F490F352E64000DE5D2 /* TTImageView.m */; };
-		BEF31F740F352E64000DE5D2 /* TTBackgroundView.m in Sources */ = {isa = PBXBuildFile; fileRef = BEF31F4A0F352E64000DE5D2 /* TTBackgroundView.m */; };
+		BEF31F740F352E64000DE5D2 /* TTStyleView.m in Sources */ = {isa = PBXBuildFile; fileRef = BEF31F4A0F352E64000DE5D2 /* TTStyleView.m */; };
 		BEF31F750F352E64000DE5D2 /* TTPhotoView.m in Sources */ = {isa = PBXBuildFile; fileRef = BEF31F4B0F352E64000DE5D2 /* TTPhotoView.m */; };
 		BEF31F760F352E64000DE5D2 /* TTPhotoViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = BEF31F4C0F352E64000DE5D2 /* TTPhotoViewController.m */; };
 		BEF31F770F352E64000DE5D2 /* TTScrollView.m in Sources */ = {isa = PBXBuildFile; fileRef = BEF31F4D0F352E64000DE5D2 /* TTScrollView.m */; };
 		BEF31F840F352E64000DE5D2 /* TTErrorView.h in Headers */ = {isa = PBXBuildFile; fileRef = BEF31F5B0F352E64000DE5D2 /* TTErrorView.h */; };
 		BEF31F850F352E64000DE5D2 /* TTGlobal.h in Headers */ = {isa = PBXBuildFile; fileRef = BEF31F5C0F352E64000DE5D2 /* TTGlobal.h */; };
 		BEF31F860F352E64000DE5D2 /* TTImageView.h in Headers */ = {isa = PBXBuildFile; fileRef = BEF31F5D0F352E64000DE5D2 /* TTImageView.h */; };
-		BEF31F880F352E64000DE5D2 /* TTBackgroundView.h in Headers */ = {isa = PBXBuildFile; fileRef = BEF31F5F0F352E64000DE5D2 /* TTBackgroundView.h */; };
+		BEF31F880F352E64000DE5D2 /* TTStyleView.h in Headers */ = {isa = PBXBuildFile; fileRef = BEF31F5F0F352E64000DE5D2 /* TTStyleView.h */; };
 		BEF31F890F352E64000DE5D2 /* TTPhotoSource.h in Headers */ = {isa = PBXBuildFile; fileRef = BEF31F600F352E64000DE5D2 /* TTPhotoSource.h */; };
 		BEF31F8A0F352E64000DE5D2 /* TTPhotoView.h in Headers */ = {isa = PBXBuildFile; fileRef = BEF31F610F352E64000DE5D2 /* TTPhotoView.h */; };
 		BEF31F8B0F352E64000DE5D2 /* TTPhotoViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = BEF31F620F352E64000DE5D2 /* TTPhotoViewController.h */; };
 		BEF31F470F352E64000DE5D2 /* TTErrorView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TTErrorView.m; sourceTree = "<group>"; };
 		BEF31F480F352E64000DE5D2 /* TTGlobal.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TTGlobal.m; sourceTree = "<group>"; };
 		BEF31F490F352E64000DE5D2 /* TTImageView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TTImageView.m; sourceTree = "<group>"; };
-		BEF31F4A0F352E64000DE5D2 /* TTBackgroundView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TTBackgroundView.m; sourceTree = "<group>"; };
+		BEF31F4A0F352E64000DE5D2 /* TTStyleView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TTStyleView.m; sourceTree = "<group>"; };
 		BEF31F4B0F352E64000DE5D2 /* TTPhotoView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TTPhotoView.m; sourceTree = "<group>"; };
 		BEF31F4C0F352E64000DE5D2 /* TTPhotoViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TTPhotoViewController.m; sourceTree = "<group>"; };
 		BEF31F4D0F352E64000DE5D2 /* TTScrollView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TTScrollView.m; sourceTree = "<group>"; };
 		BEF31F5B0F352E64000DE5D2 /* TTErrorView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TTErrorView.h; path = Three20/TTErrorView.h; sourceTree = "<group>"; };
 		BEF31F5C0F352E64000DE5D2 /* TTGlobal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TTGlobal.h; path = Three20/TTGlobal.h; sourceTree = "<group>"; };
 		BEF31F5D0F352E64000DE5D2 /* TTImageView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TTImageView.h; path = Three20/TTImageView.h; sourceTree = "<group>"; };
-		BEF31F5F0F352E64000DE5D2 /* TTBackgroundView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TTBackgroundView.h; path = Three20/TTBackgroundView.h; sourceTree = "<group>"; };
+		BEF31F5F0F352E64000DE5D2 /* TTStyleView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TTStyleView.h; path = Three20/TTStyleView.h; sourceTree = "<group>"; };
 		BEF31F600F352E64000DE5D2 /* TTPhotoSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TTPhotoSource.h; path = Three20/TTPhotoSource.h; sourceTree = "<group>"; };
 		BEF31F610F352E64000DE5D2 /* TTPhotoView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TTPhotoView.h; path = Three20/TTPhotoView.h; sourceTree = "<group>"; };
 		BEF31F620F352E64000DE5D2 /* TTPhotoViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TTPhotoViewController.h; path = Three20/TTPhotoViewController.h; sourceTree = "<group>"; };
 				BEF31F580F352E64000DE5D2 /* TTYouTubeView.m */,
 				BE42FB150F3BBAB400A3B65A /* TTLinkView.h */,
 				BE42FB120F3BBAA900A3B65A /* TTLinkView.m */,
-				BEF31F5F0F352E64000DE5D2 /* TTBackgroundView.h */,
-				BEF31F4A0F352E64000DE5D2 /* TTBackgroundView.m */,
+				BEF31F5F0F352E64000DE5D2 /* TTStyleView.h */,
+				BEF31F4A0F352E64000DE5D2 /* TTStyleView.m */,
 				BEF31F690F352E64000DE5D2 /* TTUnclippedView.h */,
 				BEF31F530F352E64000DE5D2 /* TTUnclippedView.m */,
 				BE42FB7B0F3BBF3D00A3B65A /* TTTabBar.h */,
 				BEF31F830F352E64000DE5D2 /* TTActivityLabel.h in Headers */,
 				BEF31F840F352E64000DE5D2 /* TTErrorView.h in Headers */,
 				BEF31F860F352E64000DE5D2 /* TTImageView.h in Headers */,
-				BEF31F880F352E64000DE5D2 /* TTBackgroundView.h in Headers */,
+				BEF31F880F352E64000DE5D2 /* TTStyleView.h in Headers */,
 				BEF31F890F352E64000DE5D2 /* TTPhotoSource.h in Headers */,
 				BEF31F8A0F352E64000DE5D2 /* TTPhotoView.h in Headers */,
 				BEF31F8B0F352E64000DE5D2 /* TTPhotoViewController.h in Headers */,
 				BEF31F700F352E64000DE5D2 /* TTActivityLabel.m in Sources */,
 				BEF31F710F352E64000DE5D2 /* TTErrorView.m in Sources */,
 				BEF31F730F352E64000DE5D2 /* TTImageView.m in Sources */,
-				BEF31F740F352E64000DE5D2 /* TTBackgroundView.m in Sources */,
+				BEF31F740F352E64000DE5D2 /* TTStyleView.m in Sources */,
 				BEF31F750F352E64000DE5D2 /* TTPhotoView.m in Sources */,
 				BEF31F760F352E64000DE5D2 /* TTPhotoViewController.m in Sources */,
 				BEF31F770F352E64000DE5D2 /* TTScrollView.m in Sources */,

File src/Three20/TTActivityLabel.h

 } TTActivityLabelStyle;
 
 @protocol TTActivityLabelDelegate;
-@class TTBackgroundView;
+@class TTStyleView;
 
 @interface TTActivityLabel : UIView {
   id<TTActivityLabelDelegate> _delegate;
   TTActivityLabelStyle _style;
-  TTBackgroundView* _bezelView;
+  TTStyleView* _bezelView;
   UIActivityIndicatorView* _spinner;
   UILabel* _textView;
   UIButton* _stopButton;

File src/Three20/TTAppearance.h

 @property(nonatomic,retain) UIImage* blackButtonImage;
 
 - (void)draw:(TTDrawStyle)background rect:(CGRect)rect fill:(UIColor**)fillColor
-  fillCount:(int)fillCount stroke:(UIColor*)strokeColor radius:(CGFloat)radius;
+        fillCount:(int)fillCount stroke:(UIColor*)strokeColor radius:(CGFloat)radius;
+
+- (void)draw:(TTDrawStyle)background rect:(CGRect)rect fill:(UIColor**)fillColor
+        fillCount:(int)fillCount stroke:(UIColor*)strokeColor thickness:(CGFloat)thickness
+        radius:(CGFloat)radius;
 
 - (void)draw:(TTDrawStyle)background rect:(CGRect)rect;
 
-- (void)drawLine:(CGPoint)from to:(CGPoint)to color:(UIColor*)color;
+- (void)drawLine:(CGPoint)from to:(CGPoint)to color:(UIColor*)color thickness:(CGFloat)thickness;
 
 - (void)fill:(CGRect)rect fillColors:(UIColor**)fillColors count:(int)count;
 
-- (void)stroke:(UIColor*)strokeColor;
+- (void)stroke:(UIColor*)strokeColor thickness:(CGFloat)thickness;
 
 @end

File src/Three20/TTBackgroundView.h

-#import "Three20/TTAppearance.h"
-
-/**
- * A decorational view that can painted using a variety of visual properties.
- */
-@interface TTBackgroundView : UIView {
-  TTDrawStyle _style;
-  UIColor* _fillColor;
-  UIColor* _fillColor2;
-  UIColor* _strokeColor;
-  NSInteger _borderRadius;
-  UIEdgeInsets _backgroundInset;
-}
-
-@property(nonatomic) TTDrawStyle style;
-@property(nonatomic,retain) UIColor* fillColor;
-@property(nonatomic,retain) UIColor* fillColor2;
-@property(nonatomic,retain) UIColor* strokeColor;
-@property(nonatomic) NSInteger borderRadius;
-@property(nonatomic) UIEdgeInsets backgroundInset;
-
-@end

File src/Three20/TTImageView.h

 
 @protocol TTImageViewDelegate;
 
-@interface TTImageView : UIImageView<TTURLRequestDelegate> {
+@interface TTImageView : UIImageView <TTURLRequestDelegate> {
   id<TTImageViewDelegate> _delegate;
   TTURLRequest* _request;
   NSString* _url;
 
 @end
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
 @protocol TTImageViewDelegate <NSObject>
 
 @optional

File src/Three20/TTLinkView.h

 #import "Three20/TTGlobal.h"
 
-@class TTBackgroundView;
+@class TTStyleView;
 
 @interface TTLinkView : UIControl {
   id _delegate;
   id _url;
-  TTBackgroundView* _screenView;
+  TTStyleView* _screenView;
   int _borderRadius;
 }
 

File src/Three20/TTSearchBar.h

-#import "Three20/TTBackgroundView.h"
+#import "Three20/TTStyleView.h"
 
 @protocol TTTableViewDataSource, TTSearchTextFieldDelegate;
 @class TTSearchTextField;
 
-@interface TTSearchBar : TTBackgroundView {
+@interface TTSearchBar : TTStyleView {
   TTSearchTextField* _searchField;
-  TTBackgroundView* _boxView;
+  TTStyleView* _boxView;
   UIColor* _tintColor;
   UIButton* _cancelButton;
   BOOL _showsCancelButton;
 @property(nonatomic,copy) NSString* text;
 @property(nonatomic,copy) NSString* placeholder;
 @property(nonatomic,readonly) UITableView* tableView;
-@property(nonatomic,readonly) TTBackgroundView* boxView;
+@property(nonatomic,readonly) TTStyleView* boxView;
 @property(nonatomic,retain) UIColor* tintColor;
 @property(nonatomic,retain) UIColor* textColor;
 @property(nonatomic,retain) UIFont* font;

File src/Three20/TTSearchTextField.h

 #import "Three20/TTTableViewDataSource.h"
 
 @protocol TTTableViewDataSource;
-@class TTSearchTextFieldInternal, TTBackgroundView;
+@class TTSearchTextFieldInternal, TTStyleView;
 
 @interface TTSearchTextField : UITextField <UITableViewDelegate> {
   id<TTTableViewDataSource> _dataSource;
   TTSearchTextFieldInternal* _internal;
   UITableView* _tableView;
-  TTBackgroundView* _shadowView;
+  TTStyleView* _shadowView;
   UIButton* _screenView;
   UINavigationItem* _previousNavigationItem;
   UIBarButtonItem* _previousRightBarButtonItem;

File src/Three20/TTStyleView.h

+#import "Three20/TTAppearance.h"
+#import "Three20/TTURLRequest.h"
+
+@protocol TTStyleViewDelegate;
+@class TTURLRequest;
+
+/**
+ * A decorational view that can styled using a variety of visual properties.
+ */
+@interface TTStyleView : UIView <TTURLRequestDelegate> {
+  id<TTStyleViewDelegate> _delegate;
+  TTURLRequest* _request;
+  TTDrawStyle _style;
+  UIColor* _fillColor;
+  UIColor* _fillColor2;
+  UIColor* _borderColor;
+  CGFloat _borderWidth;
+  CGFloat _borderRadius;
+  UIEdgeInsets _backgroundInset;
+  NSString* _backgroundImageURL;
+  UIImage* _backgroundImage;
+  UIImage* _backgroundImageDefault;
+}
+
+@property(nonatomic,assign) id<TTStyleViewDelegate> delegate;
+@property(nonatomic) TTDrawStyle style;
+@property(nonatomic,retain) UIColor* fillColor;
+@property(nonatomic,retain) UIColor* fillColor2;
+@property(nonatomic,retain) UIColor* borderColor;
+@property(nonatomic) CGFloat borderWidth;
+@property(nonatomic) CGFloat borderRadius;
+@property(nonatomic) UIEdgeInsets backgroundInset;
+@property(nonatomic,copy) NSString* backgroundImageURL;
+@property(nonatomic,retain) UIImage* backgroundImage;
+@property(nonatomic,retain) UIImage* backgroundImageDefault;
+
+- (void)reload;
+- (void)stopLoading;
+
+@end
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+@protocol TTStyleViewDelegate <NSObject>
+
+@optional
+
+- (void)styleView:(TTStyleView*)imageView didLoadImage:(UIImage*)image;
+- (void)styleViewDidStartLoad:(TTStyleView*)styleView;
+- (void)styleView:(TTStyleView*)styleView didFailLoadWithError:(NSError*)error;
+
+@end

File src/Three20/TTTableFieldCell.h

 #import "Three20/TTTableViewCell.h"
 
-@class TTTableField, TTImageView, TTErrorView;
+@class TTTableField, TTStyleView, TTErrorView;
 
 @interface TTTableFieldCell : TTTableViewCell {
   TTTableField* _field;
 @end
 
 @interface TTIconTableFieldCell : TTTableFieldCell {
-  TTImageView* _iconView;
+  TTStyleView* _iconView;
 }
 @end
 

File src/Three20/TTTextEditor.h

-#import "Three20/TTBackgroundView.h"
+#import "Three20/TTStyleView.h"
 
 @protocol TTTextEditorDelegate;
 @class TTTextEditorInternal;
 
-@interface TTTextEditor : TTBackgroundView {
-  id<TTTextEditorDelegate> _delegate;
+@interface TTTextEditor : TTStyleView {
+  id<TTTextEditorDelegate> _textDelegate;
   TTTextEditorInternal* _internal;
   NSString* _placeholder;
   NSString* _fixedText;
   BOOL _showsExtraLine;
 }
 
-@property(nonatomic,assign) id<TTTextEditorDelegate> delegate;
+@property(nonatomic,assign) id<TTTextEditorDelegate> textDelegate;
 @property(nonatomic,readonly) UITextView* textView;
 @property(nonatomic,copy) NSString* placeholder;
 @property(nonatomic,copy) NSString* fixedText;

File src/Three20/TTThumbView.h

 #import "Three20/TTLinkView.h"
 
 @class TTImageView;
-@class TTBackgroundView;
+@class TTStyleView;
 
 @interface TTThumbView : TTLinkView {
   TTImageView* imageView;
-  TTBackgroundView* borderView;
+  TTStyleView* borderView;
 }
 
 @property(nonatomic,copy) NSString* thumbURL;

File src/Three20/Three20.h

 #import "Three20/TTImageView.h"
 #import "Three20/TTLinkView.h"
 #import "Three20/TTYouTubeView.h"
-#import "Three20/TTBackgroundView.h"
+#import "Three20/TTStyleView.h"
 #import "Three20/TTScrollView.h"
 #import "Three20/TTTabBar.h"
 #import "Three20/TTActivityLabel.h"

File src/Three20/UIImageAdditions.h

  */
 - (UIImage*)transformWidth:(CGFloat)width height:(CGFloat)height rotate:(BOOL)rotate;
 
+/**
+ * Draws the image as a rounded rectangle.
+ */
+- (void)drawInRect:(CGRect)rect radius:(CGFloat)radius;
+
 @end

File src/UIImageAdditions.m

 #import "Three20/TTGlobal.h"
 
+@implementation UIImage (TTCategory)
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
+// private
 
-@implementation UIImage (TTCategory)
+- (void)addRoundedRectToPath:(CGContextRef)context rect:(CGRect)rect radius:(float)radius {
+  TTLOGRECT(rect);
+  
+  CGContextBeginPath(context);
+  CGContextSaveGState(context);
+
+  if (radius == 0) {
+    CGContextTranslateCTM(context, CGRectGetMinX(rect), CGRectGetMinY(rect));
+    CGContextAddRect(context, rect);
+  } else {
+    CGContextTranslateCTM(context, CGRectGetMinX(rect), CGRectGetMinY(rect));
+    CGContextScaleCTM(context, radius, radius);
+    float fw = CGRectGetWidth(rect) / radius;
+    float fh = CGRectGetHeight(rect) / radius;
+    
+    CGContextMoveToPoint(context, fw, fh/2);
+    CGContextAddArcToPoint(context, fw, fh, fw/2, fh, 1);
+    CGContextAddArcToPoint(context, 0, fh, 0, fh/2, 1);
+    CGContextAddArcToPoint(context, 0, 0, fw/2, 0, 1);
+    CGContextAddArcToPoint(context, fw, 0, fw, fh/2, 1);
+  }
+
+  CGContextClosePath(context);
+  CGContextRestoreGState(context);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// public
 
 - (UIImage*)transformWidth:(CGFloat)width height:(CGFloat)height rotate:(BOOL)rotate {
   CGFloat destW = width;
   return result;
 }
 
+- (void)drawInRect:(CGRect)rect radius:(CGFloat)radius {
+  CGContextRef context = UIGraphicsGetCurrentContext();
+  CGContextSaveGState(context);
+  [self addRoundedRectToPath:context rect:rect radius:radius];
+  CGContextClip(context);
+  [self drawInRect:rect];
+  CGContextRestoreGState(context);
+}
+
 @end