Skip to content

Commit

Permalink
* Load image URLs in styled text and redraw after asynchronous load c…
Browse files Browse the repository at this point in the history
…ompletes

* Draw images with clipping
  • Loading branch information
joehewitt committed Apr 24, 2009
1 parent 3af8819 commit 00e77cb
Show file tree
Hide file tree
Showing 9 changed files with 204 additions and 45 deletions.
22 changes: 13 additions & 9 deletions samples/TTCatalog/Classes/StyledTextTestController.m
Expand Up @@ -67,15 +67,15 @@ - (void)dealloc {
- (void)loadView {
[super loadView];

NSString* kText = @"\
This is a test of styled labels. Styled labels support \
<b>bold text</b>, <i>italic text</i>, <span class=\"blueText\">colored text</span>, \
<span class=\"largeText\">font sizes</span>, \
<span class=\"blueBox\">spans with backgrounds</span>, inline images \
<img src=\"bundle://smiley.png\"/>, and <a href=\"http://www.google.com\">hyperlinks</a> you can \
actually touch. URLs are automatically converted into links, like this: http://www.foo.com\
<div>You can enclose blocks within an HTML div.</div>\
Both line break characters\n\nand HTML line breaks<br/>are respected.";
// NSString* kText = @"\
//This is a test of styled labels. Styled labels support \
//<b>bold text</b>, <i>italic text</i>, <span class=\"blueText\">colored text</span>, \
//<span class=\"largeText\">font sizes</span>, \
//<span class=\"blueBox\">spans with backgrounds</span>, inline images \
//<img src=\"bundle://smiley.png\"/>, and <a href=\"http://www.google.com\">hyperlinks</a> you can \
//actually touch. URLs are automatically converted into links, like this: http://www.foo.com\
//<div>You can enclose blocks within an HTML div.</div>\
//Both line break characters\n\nand HTML line breaks<br/>are respected.";
// NSString* kText = @"<span class=\"largeText\">bah</span><span class=\"inlineBox\">hyper links</span>";
// NSString* kText = @"blah blah blah black sheep blah <span class=\"inlineBox\">\
//<img src=\"bundle://smiley.png\"/>hyperlinks</span> blah fun";
Expand All @@ -93,6 +93,10 @@ - (void)loadView {
//<span class=\"floated\"><img src=\"bundle://smiley.png\" width=\"50\" height=\"50\"/></span>This \
//is a test of floats. This is still a test of floats. This text will wrap itself around \
//the image that is being floated on the left. I repeat, this is a test of floats.";
NSString* kText = @"\
<span class=\"floated\"><img src=\"bundle://smiley.png\" width=\"50\" height=\"50\"/></span>This \
is a test of floats. This is still a test of floats. This text will wrap itself around \
the image that is being floated on the left. I repeat, this is a test of floats.";

TTStyledTextLabel* label1 = [[[TTStyledTextLabel alloc] initWithFrame:self.view.bounds] autorelease];
label1.font = [UIFont systemFontOfSize:17];
Expand Down
13 changes: 9 additions & 4 deletions src/TTStyledFrame.m
Expand Up @@ -113,13 +113,13 @@ - (void)drawLayer:(TTStyleContext*)context withStyle:(TTStyle*)style {
UIFont* font = context.font;
context.font = textStyle.font;
if (textStyle.color) {
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextSaveGState(ctx);
[textStyle.color setFill];

[self drawSubframes];

CGContextRestoreGState(context);
CGContextRestoreGState(ctx);
} else {
[self drawSubframes];
}
Expand Down Expand Up @@ -242,7 +242,12 @@ - (void)dealloc {
// public

- (void)drawInRect:(CGRect)rect {
[_imageNode.image drawInRect:rect];
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextSaveGState(ctx);
CGContextAddRect(ctx, rect);
CGContextClip(ctx);
[_imageNode.image drawInRect:rect contentMode:UIViewContentModeScaleAspectFill];
CGContextRestoreGState(ctx);
}

@end
25 changes: 17 additions & 8 deletions src/TTStyledLayout.m
Expand Up @@ -7,7 +7,8 @@

@implementation TTStyledLayout

@synthesize width = _width, height = _height, rootFrame = _rootFrame, font = _font;
@synthesize width = _width, height = _height, rootFrame = _rootFrame, font = _font,
invalidImages = _invalidImages;

//////////////////////////////////////////////////////////////////////////////////////////////////
// private
Expand Down Expand Up @@ -222,10 +223,6 @@ - (void)breakLine {
}
}

- (void)expandWidth:(CGFloat)width {
_width = width;
}

- (TTStyledFrame*)addFrameForText:(NSString*)text element:(TTStyledElement*)element
node:(TTStyledTextNode*)node width:(CGFloat)width height:(CGFloat)height {
TTStyledTextFrame* frame = [[[TTStyledTextFrame alloc] initWithText:text element:element
Expand Down Expand Up @@ -284,7 +281,11 @@ - (void)layoutElement:(TTStyledElement*)elt {
layout.width = 0; // XXXjoe Get width from the box style here once it supports it
layout.height = _height;
layout.font = _font;
layout.invalidImages = _invalidImages;
[layout layout:child];
if (!_invalidImages && layout.invalidImages) {
_invalidImages = [layout.invalidImages retain];
}

TTStyledFrame* frame = [self addContentFrame:layout.rootFrame width:layout.width];

Expand Down Expand Up @@ -416,7 +417,13 @@ - (void)layoutElement:(TTStyledElement*)elt {
}

- (void)layoutImage:(TTStyledImageNode*)imageNode container:(TTStyledElement*)element {
UIImage* image = [imageNode image];
UIImage* image = imageNode.image;
if (!image && imageNode.url) {
if (!_invalidImages) {
_invalidImages = TTCreateNonRetainingArray();
}
[_invalidImages addObject:imageNode];
}

TTStyle* style = imageNode.className
? [[TTStyleSheet globalStyleSheet] styleWithSelector:imageNode.className] : nil;
Expand All @@ -439,7 +446,7 @@ - (void)layoutImage:(TTStyledImageNode*)imageNode container:(TTStyledElement*)el
// the current line and mark it with a line break
[self breakLine];
} else {
[self expandWidth:contentWidth];
_width = contentWidth;
}
}

Expand Down Expand Up @@ -525,7 +532,7 @@ - (void)layoutText:(TTStyledTextNode*)textNode container:(TTStyledElement*)eleme
if (_lineWidth) {
[self breakLine];
} else {
[self expandWidth:wordSize.width];
_width = wordSize.width;
}
lineStartIndex = lineRange.location + lineRange.length;
frameWidth = 0;
Expand Down Expand Up @@ -597,6 +604,7 @@ - (id)init {
_linkStyle = nil;
_rootNode = nil;
_lastNode = nil;
_invalidImages = nil;
}
return self;
}
Expand All @@ -607,6 +615,7 @@ - (void)dealloc {
[_boldFont release];
[_italicFont release];
[_linkStyle release];
[_invalidImages release];
[super dealloc];
}

Expand Down
22 changes: 11 additions & 11 deletions src/TTStyledNode.m
@@ -1,6 +1,5 @@
#import "Three20/TTStyledNode.h"
#import "Three20/TTURLRequest.h"
#import "Three20/TTURLResponse.h"
#import "Three20/TTURLCache.h"
#import "Three20/TTNavigationCenter.h"

//////////////////////////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -433,16 +432,17 @@ - (NSString*)outerHTML {
//////////////////////////////////////////////////////////////////////////////////////////////////
// public

- (UIImage*)image {
if (!_image && _url) {
TTURLRequest* request = [TTURLRequest requestWithURL:_url delegate:nil];
TTURLImageResponse* response = [[[TTURLImageResponse alloc] init] autorelease];
request.response = response;
if ([request send]) {
_image = [response.image retain];
}
- (void)setUrl:(NSString*)url {
if (!_url || ![url isEqualToString:_url]) {
[_url release];
_url = [url retain];

if (_url) {
self.image = [[TTURLCache sharedCache] imageForURL:_url];
} else {
self.image = nil;
}
}
return _image;
}

@end
Expand Down
118 changes: 110 additions & 8 deletions src/TTStyledText.m
Expand Up @@ -3,12 +3,16 @@
#import "Three20/TTStyledFrame.h"
#import "Three20/TTStyledLayout.h"
#import "Three20/TTStyledTextParser.h"
#import "Three20/TTURLRequest.h"
#import "Three20/TTURLResponse.h"
#import "Three20/TTURLCache.h"

//////////////////////////////////////////////////////////////////////////////////////////////////

@implementation TTStyledText

@synthesize rootNode = _rootNode, font = _font, width = _width, height = _height;
@synthesize delegate = _delegate, rootNode = _rootNode, font = _font, width = _width,
height = _height, invalidImages = _invalidImages;

//////////////////////////////////////////////////////////////////////////////////////////////////
// class public
Expand Down Expand Up @@ -45,6 +49,56 @@ + (TTStyledText*)textWithURLs:(NSString*)source lineBreaks:(BOOL)lineBreaks {
}
}

///////////////////////////////////////////////////////////////////////////////////////////////////
// private

- (void)stopLoadingImages {
if (_imageRequests) {
NSMutableArray* requests = [_imageRequests retain];
[_imageRequests release];
_imageRequests = nil;

if (!_invalidImages) {
_invalidImages = [[NSMutableArray alloc] init];
}

for (TTURLRequest* request in requests) {
[_invalidImages addObject:request.userInfo];
[request cancel];
}
[requests release];
}
}

- (void)loadImages {
[self stopLoadingImages];

if (_delegate && _invalidImages) {
BOOL loadedSome = NO;
for (TTStyledImageNode* imageNode in _invalidImages) {
if (imageNode.url) {
UIImage* image = [[TTURLCache sharedCache] imageForURL:imageNode.url];
if (image) {
imageNode.image = image;
loadedSome = YES;
} else {
TTURLRequest* request = [TTURLRequest requestWithURL:imageNode.url delegate:self];
request.userInfo = imageNode;
request.response = [[[TTURLImageResponse alloc] init] autorelease];
[request send];
}
}
}

[_invalidImages release];
_invalidImages = nil;

if (loadedSome) {
[_delegate styledTextNeedsDisplay:self];
}
}
}

///////////////////////////////////////////////////////////////////////////////////////////////////
// NSObject

Expand All @@ -55,24 +109,64 @@ - (id)initWithNode:(TTStyledNode*)rootNode {
_font = nil;
_width = 0;
_height = 0;
_invalidImages = nil;
_imageRequests = nil;
}
return self;
}

- (void)dealloc {
[self stopLoadingImages];
[_rootNode release];
[_rootFrame release];
[_font release];
[_invalidImages release];
[_imageRequests release];
[super dealloc];
}

- (NSString*)description {
return [self.rootFrame description];
}

//////////////////////////////////////////////////////////////////////////////////////////////////
// TTURLRequestDelegate

- (void)requestDidStartLoad:(TTURLRequest*)request {
if (!_imageRequests) {
_imageRequests = [[NSMutableArray alloc] init];
}
[_imageRequests addObject:request];
}

- (void)requestDidFinishLoad:(TTURLRequest*)request {
TTURLImageResponse* response = request.response;
TTStyledImageNode* imageNode = request.userInfo;
imageNode.image = response.image;

[_imageRequests removeObject:request];

[_delegate styledTextNeedsDisplay:self];
}

- (void)request:(TTURLRequest*)request didFailLoadWithError:(NSError*)error {
[_imageRequests removeObject:request];
}

- (void)requestDidCancelLoad:(TTURLRequest*)request {
[_imageRequests removeObject:request];
}

///////////////////////////////////////////////////////////////////////////////////////////////////
// public

- (void)setDelegate:(id<TTStyledTextDelegate>)delegate {
if (_delegate != delegate) {
_delegate = delegate;
[self loadImages];
}
}

- (TTStyledFrame*)rootFrame {
[self layoutIfNeeded];
return _rootFrame;
Expand All @@ -98,16 +192,24 @@ - (CGFloat)height {
return _height;
}

- (BOOL)needsLayout {
return !_rootFrame;
}

- (void)layoutFrames {
TTStyledLayout* ctx = [[TTStyledLayout alloc] initWithRootNode:_rootNode];
ctx.width = _width;
ctx.font = _font;
TTStyledLayout* layout = [[TTStyledLayout alloc] initWithRootNode:_rootNode];
layout.width = _width;
layout.font = _font;

[layout layout:_rootNode];

[ctx layout:_rootNode];
_rootFrame = [layout.rootFrame retain];
_height = ceil(layout.height);
[_invalidImages release];
_invalidImages = [layout.invalidImages retain];
[layout release];

_rootFrame = [ctx.rootFrame retain];
_height = ceil(ctx.height);
[ctx release];
[self loadImages];
}

- (void)layoutIfNeeded {
Expand Down
10 changes: 10 additions & 0 deletions src/TTStyledTextLabel.m
Expand Up @@ -51,6 +51,7 @@ - (id)initWithFrame:(CGRect)frame {
}

- (void)dealloc {
_text.delegate = nil;
[_text release];
[_font release];
[_textColor release];
Expand Down Expand Up @@ -162,13 +163,22 @@ - (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event {
[super touchesEnded:touches withEvent:event];
}

///////////////////////////////////////////////////////////////////////////////////////////////////
// TTStyledTextDelegate

- (void)styledTextNeedsDisplay:(TTStyledText*)text {
[self setNeedsDisplay];
}

///////////////////////////////////////////////////////////////////////////////////////////////////
// public

- (void)setText:(TTStyledText*)text {
if (text != _text) {
_text.delegate = nil;
[_text release];
_text = [text retain];
_text.delegate = self;
_text.font = _font;
[self setNeedsDisplay];
}
Expand Down

0 comments on commit 00e77cb

Please sign in to comment.