Skip to content
Browse files

Added a new TUIStretchableImage class, which is an NSImage with resiz…

…ing support
  • Loading branch information...
1 parent 514b439 commit 3a67a27600ed596f7e282b1b6cd52f908b18db55 @jspahrsummers jspahrsummers committed Jul 24, 2012
Showing with 230 additions and 7 deletions.
  1. +23 −7 TwUI.xcodeproj/project.pbxproj
  2. +1 −0 lib/UIKit/TUIKit.h
  3. +33 −0 lib/UIKit/TUIStretchableImage.h
  4. +173 −0 lib/UIKit/TUIStretchableImage.m
View
30 TwUI.xcodeproj/project.pbxproj
@@ -287,6 +287,12 @@
D040611615B6A7CD00F753ED /* NSTextView+TUIExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = D040611215B6A7CC00F753ED /* NSTextView+TUIExtensions.m */; };
D040611715B6A7CD00F753ED /* NSTextView+TUIExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = D040611215B6A7CC00F753ED /* NSTextView+TUIExtensions.m */; };
D040611815B6A7CD00F753ED /* NSTextView+TUIExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = D040611215B6A7CC00F753ED /* NSTextView+TUIExtensions.m */; };
+ D05DEE8C15BF645D005D8769 /* TUIStretchableImage.h in Headers */ = {isa = PBXBuildFile; fileRef = D05DEE8A15BF645D005D8769 /* TUIStretchableImage.h */; };
+ D05DEE8D15BF645D005D8769 /* TUIStretchableImage.h in Headers */ = {isa = PBXBuildFile; fileRef = D05DEE8A15BF645D005D8769 /* TUIStretchableImage.h */; };
+ D05DEE8E15BF645D005D8769 /* TUIStretchableImage.h in Headers */ = {isa = PBXBuildFile; fileRef = D05DEE8A15BF645D005D8769 /* TUIStretchableImage.h */; };
+ D05DEE8F15BF645D005D8769 /* TUIStretchableImage.m in Sources */ = {isa = PBXBuildFile; fileRef = D05DEE8B15BF645D005D8769 /* TUIStretchableImage.m */; };
+ D05DEE9015BF645D005D8769 /* TUIStretchableImage.m in Sources */ = {isa = PBXBuildFile; fileRef = D05DEE8B15BF645D005D8769 /* TUIStretchableImage.m */; };
+ D05DEE9115BF645D005D8769 /* TUIStretchableImage.m in Sources */ = {isa = PBXBuildFile; fileRef = D05DEE8B15BF645D005D8769 /* TUIStretchableImage.m */; };
D07AA82315BDD6B600F736C0 /* TUINSView+Hyperfocus.h in Headers */ = {isa = PBXBuildFile; fileRef = CBB74C5E13BE6E1900C85CB5 /* TUINSView+Hyperfocus.h */; settings = {ATTRIBUTES = (Public, ); }; };
D07AA82415BDD6B700F736C0 /* TUINSView+Hyperfocus.h in Headers */ = {isa = PBXBuildFile; fileRef = CBB74C5E13BE6E1900C85CB5 /* TUINSView+Hyperfocus.h */; settings = {ATTRIBUTES = (Public, ); }; };
D07AA82615BDD72F00F736C0 /* TUINSView+NSTextInputClient.h in Headers */ = {isa = PBXBuildFile; fileRef = D07AA82515BDD72D00F736C0 /* TUINSView+NSTextInputClient.h */; settings = {ATTRIBUTES = (Public, ); }; };
@@ -499,6 +505,8 @@
D040610915B6A77500F753ED /* TUIAnimationManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TUIAnimationManager.m; sourceTree = "<group>"; };
D040611115B6A7CC00F753ED /* NSTextView+TUIExtensions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSTextView+TUIExtensions.h"; sourceTree = "<group>"; };
D040611215B6A7CC00F753ED /* NSTextView+TUIExtensions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSTextView+TUIExtensions.m"; sourceTree = "<group>"; };
+ D05DEE8A15BF645D005D8769 /* TUIStretchableImage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TUIStretchableImage.h; sourceTree = "<group>"; };
+ D05DEE8B15BF645D005D8769 /* TUIStretchableImage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TUIStretchableImage.m; sourceTree = "<group>"; };
D07AA82515BDD72D00F736C0 /* TUINSView+NSTextInputClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "TUINSView+NSTextInputClient.h"; sourceTree = "<group>"; };
D0C764EA15B611C200E7AC2C /* TUIBridgedView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TUIBridgedView.h; sourceTree = "<group>"; };
D0C7650415B6156A00E7AC2C /* TUIHostView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TUIHostView.h; sourceTree = "<group>"; };
@@ -733,14 +741,14 @@
CBB74C5B13BE6E1900C85CB5 /* TUIKit.m */,
CBB74C5C13BE6E1900C85CB5 /* TUILabel.h */,
CBB74C5D13BE6E1900C85CB5 /* TUILabel.m */,
- D0C7650C15B6189D00E7AC2C /* TUINSHostView.h */,
- D0C7650D15B6189D00E7AC2C /* TUINSHostView.m */,
- 8819794A13E26E5800AA39EB /* TUINSView+Accessibility.h */,
- 8819794B13E26E5800AA39EB /* TUINSView+Accessibility.m */,
48A10E7D15B7769A007F9EE3 /* TUILayoutConstraint.h */,
48A10E7E15B7769A007F9EE3 /* TUILayoutConstraint.m */,
48A10E7F15B7769A007F9EE3 /* TUILayoutManager.h */,
48A10E8015B7769A007F9EE3 /* TUILayoutManager.m */,
+ D0C7650C15B6189D00E7AC2C /* TUINSHostView.h */,
+ D0C7650D15B6189D00E7AC2C /* TUINSHostView.m */,
+ 8819794A13E26E5800AA39EB /* TUINSView+Accessibility.h */,
+ 8819794B13E26E5800AA39EB /* TUINSView+Accessibility.m */,
CBB74C5E13BE6E1900C85CB5 /* TUINSView+Hyperfocus.h */,
CBB74C5F13BE6E1900C85CB5 /* TUINSView+Hyperfocus.m */,
D07AA82515BDD72D00F736C0 /* TUINSView+NSTextInputClient.h */,
@@ -758,10 +766,12 @@
CBB74C6613BE6E1900C85CB5 /* TUIResponder.m */,
CBB74C6713BE6E1900C85CB5 /* TUIScrollKnob.h */,
CBB74C6813BE6E1900C85CB5 /* TUIScrollKnob.m */,
- CBB74C6913BE6E1900C85CB5 /* TUIScrollView.h */,
- CBB74C6A13BE6E1900C85CB5 /* TUIScrollView.m */,
D0C7655015B6294400E7AC2C /* TUIScrollView+TUIBridgedScrollView.h */,
D0C7655115B6294400E7AC2C /* TUIScrollView+TUIBridgedScrollView.m */,
+ CBB74C6913BE6E1900C85CB5 /* TUIScrollView.h */,
+ CBB74C6A13BE6E1900C85CB5 /* TUIScrollView.m */,
+ D05DEE8A15BF645D005D8769 /* TUIStretchableImage.h */,
+ D05DEE8B15BF645D005D8769 /* TUIStretchableImage.m */,
CBB74C6B13BE6E1900C85CB5 /* TUIStringDrawing.h */,
CBB74C6C13BE6E1900C85CB5 /* TUIStringDrawing.m */,
88D81CFD1577EF0D009D453B /* TUIStyledView.h */,
@@ -813,9 +823,9 @@
CBB74C8C13BE6E1900C85CB5 /* TUIView.m */,
CBB74C8D13BE6E1900C85CB5 /* TUIViewController.h */,
CBB74C8E13BE6E1900C85CB5 /* TUIViewController.m */,
+ D0C7656D15B6322A00E7AC2C /* TUIViewNSViewContainer+Private.h */,
CBB74C8F13BE6E1900C85CB5 /* TUIViewNSViewContainer.h */,
CBB74C9013BE6E1900C85CB5 /* TUIViewNSViewContainer.m */,
- D0C7656D15B6322A00E7AC2C /* TUIViewNSViewContainer+Private.h */,
);
name = UIKit;
path = lib/UIKit;
@@ -851,6 +861,7 @@
D039724B15B7D7DE0092CD26 /* TUILayoutConstraint.h in Headers */,
D07AA82415BDD6B700F736C0 /* TUINSView+Hyperfocus.h in Headers */,
D07AA82815BDD72F00F736C0 /* TUINSView+NSTextInputClient.h in Headers */,
+ D05DEE8E15BF645D005D8769 /* TUIStretchableImage.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -924,6 +935,7 @@
48A10E8315B7769A007F9EE3 /* TUILayoutManager.h in Headers */,
48A10E8B15B77A46007F9EE3 /* TUIView+Layout.h in Headers */,
D07AA82615BDD72F00F736C0 /* TUINSView+NSTextInputClient.h in Headers */,
+ D05DEE8C15BF645D005D8769 /* TUIStretchableImage.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -954,6 +966,7 @@
D039724A15B7D7DE0092CD26 /* TUILayoutConstraint.h in Headers */,
D07AA82315BDD6B600F736C0 /* TUINSView+Hyperfocus.h in Headers */,
D07AA82715BDD72F00F736C0 /* TUINSView+NSTextInputClient.h in Headers */,
+ D05DEE8D15BF645D005D8769 /* TUIStretchableImage.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -1169,6 +1182,7 @@
D039724315B7D7CE0092CD26 /* TUILayoutManager.m in Sources */,
D039724515B7D7D40092CD26 /* TUIView+Layout.m in Sources */,
D07AA82B15BDD79A00F736C0 /* TUINSView+NSTextInputClient.m in Sources */,
+ D05DEE9115BF645D005D8769 /* TUIStretchableImage.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -1248,6 +1262,7 @@
48A10E8415B7769A007F9EE3 /* TUILayoutManager.m in Sources */,
48A10E8915B778E8007F9EE3 /* TUIView+Layout.m in Sources */,
D07AA82915BDD79900F736C0 /* TUINSView+NSTextInputClient.m in Sources */,
+ D05DEE8F15BF645D005D8769 /* TUIStretchableImage.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -1333,6 +1348,7 @@
D039724215B7D7CE0092CD26 /* TUILayoutManager.m in Sources */,
D039724415B7D7D40092CD26 /* TUIView+Layout.m in Sources */,
D07AA82A15BDD79A00F736C0 /* TUINSView+NSTextInputClient.m in Sources */,
+ D05DEE9015BF645D005D8769 /* TUIStretchableImage.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
View
1 lib/UIKit/TUIKit.h
@@ -44,6 +44,7 @@
#import "TUIResponder.h"
#import "TUIScrollView.h"
#import "TUIScrollView+TUIBridgedScrollView.h"
+#import "TUIStretchableImage.h"
#import "TUIStringDrawing.h"
#import "TUIStyledView.h"
#import "TUITableView+Additions.h"
View
33 lib/UIKit/TUIStretchableImage.h
@@ -0,0 +1,33 @@
+/*
+ Copyright 2011 Twitter, Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this work except in compliance with the License.
+ You may obtain a copy of the License in the LICENSE file, or at:
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
+#import <Cocoa/Cocoa.h>
+#import "TUIGeometry.h"
+
+/*
+ * An image that supports resizing based on end caps.
+ */
+@interface TUIStretchableImage : NSImage <NSCoding, NSCopying>
+
+/*
+ * The end cap insets for the image.
+ *
+ * Any portion of the image not covered by end caps will be tiled when the image
+ * is drawn.
+ */
+@property (nonatomic, assign) TUIEdgeInsets capInsets;
+
+@end
View
173 lib/UIKit/TUIStretchableImage.m
@@ -0,0 +1,173 @@
+/*
+ Copyright 2011 Twitter, Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this work except in compliance with the License.
+ You may obtain a copy of the License in the LICENSE file, or at:
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
+#import "TUIStretchableImage.h"
+
+@implementation TUIStretchableImage
+
+#pragma mark Properties
+
+@synthesize capInsets = _capInsets;
+
+#pragma mark Drawing
+
+- (void)drawInRect:(NSRect)dstRect fromRect:(NSRect)srcRect operation:(NSCompositingOperation)op fraction:(CGFloat)alpha {
+ [self drawInRect:dstRect fromRect:srcRect operation:op fraction:alpha];
+}
+
+- (void)drawInRect:(NSRect)dstRect fromRect:(NSRect)srcRect operation:(NSCompositingOperation)op fraction:(CGFloat)alpha respectFlipped:(BOOL)respectFlipped hints:(NSDictionary *)hints {
+ CGImageRef image = [self CGImageForProposedRect:&dstRect context:[NSGraphicsContext currentContext] hints:hints];
+ if (image == NULL) {
+ NSLog(@"*** Could not get CGImage of %@", self);
+ return;
+ }
+
+ CGSize size = CGSizeMake(CGImageGetWidth(image), CGImageGetHeight(image));
+ TUIEdgeInsets insets = self.capInsets;
+
+ // TODO: Cache the nine-part images for this common case of wanting to draw
+ // the whole source image.
+ if (CGRectIsEmpty(srcRect)) {
+ // Match the image creation that occurs in the 'else' clause.
+ CGImageRetain(image);
+ } else {
+ image = CGImageCreateWithImageInRect(image, srcRect);
+ if (!image) return;
+
+ // Reduce insets to account for taking only part of the original image.
+ insets.left = fmax(0, insets.left - CGRectGetMinX(srcRect));
+ insets.bottom = fmax(0, insets.bottom - CGRectGetMinY(srcRect));
+
+ CGFloat srcRightInset = size.width - CGRectGetMaxX(srcRect);
+ insets.right = fmax(0, insets.right - srcRightInset);
+
+ CGFloat srcTopInset = size.height - CGRectGetMaxY(srcRect);
+ insets.top = fmax(0, insets.top - srcTopInset);
+ }
+
+ NSImage *topLeft = nil, *topEdge = nil, *topRight = nil;
+ NSImage *leftEdge = nil, *center = nil, *rightEdge = nil;
+ NSImage *bottomLeft = nil, *bottomEdge = nil, *bottomRight = nil;
+
+ // Length of sides that run vertically.
+ CGFloat verticalEdgeLength = fmax(0, size.height - insets.top - insets.bottom);
+
+ // Length of sides that run horizontally.
+ CGFloat horizontalEdgeLength = fmax(0, size.height - insets.left - insets.right);
+
+ NSImage *(^imageWithRect)(CGRect) = ^ id (CGRect rect){
+ CGImageRef part = CGImageCreateWithImageInRect(image, rect);
+ if (part == NULL) return nil;
+
+ NSImage *image = [[NSImage alloc] initWithCGImage:part size:rect.size];
+ CGImageRelease(part);
+
+ return image;
+ };
+
+ if (verticalEdgeLength > 0) {
+ if (insets.left > 0) {
+ CGRect partRect = CGRectMake(0, insets.bottom, insets.left, verticalEdgeLength);
+ leftEdge = imageWithRect(partRect);
+ }
+
+ if (insets.right > 0) {
+ CGRect partRect = CGRectMake(size.width - insets.right, insets.bottom, insets.right, verticalEdgeLength);
+ rightEdge = imageWithRect(partRect);
+ }
+ }
+
+ if (horizontalEdgeLength > 0) {
+ if (insets.bottom > 0) {
+ CGRect partRect = CGRectMake(insets.left, 0, horizontalEdgeLength, insets.bottom);
+ bottomEdge = imageWithRect(partRect);
+ }
+
+ if (insets.top > 0) {
+ CGRect partRect = CGRectMake(insets.left, size.height - insets.top, horizontalEdgeLength, insets.top);
+ topEdge = imageWithRect(partRect);
+ }
+ }
+
+ if (insets.left > 0 && insets.top > 0) {
+ CGRect partRect = CGRectMake(0, size.height - insets.top, insets.left, insets.top);
+ topLeft = imageWithRect(partRect);
+ }
+
+ if (insets.left > 0 && insets.bottom > 0) {
+ CGRect partRect = CGRectMake(0, 0, insets.left, insets.bottom);
+ bottomLeft = imageWithRect(partRect);
+ }
+
+ if (insets.right > 0 && insets.top > 0) {
+ CGRect partRect = CGRectMake(size.width - insets.right, size.height - insets.top, insets.right, insets.top);
+ topRight = imageWithRect(partRect);
+ }
+
+ if (insets.right > 0 && insets.bottom > 0) {
+ CGRect partRect = CGRectMake(size.width - insets.right, 0, insets.right, insets.bottom);
+ bottomRight = imageWithRect(partRect);
+ }
+
+ CGRect centerRect = TUIEdgeInsetsInsetRect(CGRectMake(0, 0, size.width, size.height), insets);
+ if (centerRect.size.width > 0 && centerRect.size.height > 0) {
+ center = imageWithRect(centerRect);
+ }
+
+ CGImageRelease(image);
+
+ BOOL flipped = NO;
+ if (respectFlipped) {
+ flipped = [[NSGraphicsContext currentContext] isFlipped];
+ }
+
+ NSDrawNinePartImage(dstRect, topLeft, topEdge, topRight, leftEdge, center, rightEdge, bottomLeft, bottomEdge, bottomRight, op, alpha, flipped);
+}
+
+#pragma mark NSCopying
+
+- (id)copyWithZone:(NSZone *)zone {
+ TUIStretchableImage *image = [super copyWithZone:zone];
+ image.capInsets = self.capInsets;
+ return image;
+}
+
+#pragma mark NSCoding
+
+- (id)initWithCoder:(NSCoder *)coder {
+ self = [super initWithCoder:coder];
+ if (self == nil) return nil;
+
+ self.capInsets = TUIEdgeInsetsMake(
+ [coder decodeFloatForKey:@"capInsetTop"],
+ [coder decodeFloatForKey:@"capInsetLeft"],
+ [coder decodeFloatForKey:@"capInsetBottom"],
+ [coder decodeFloatForKey:@"capInsetRight"]
+ );
+
+ return self;
+}
+
+- (void)encodeWithCoder:(NSCoder *)coder {
+ [super encodeWithCoder:coder];
+
+ [coder encodeFloat:self.capInsets.top forKey:@"capInsetTop"];
+ [coder encodeFloat:self.capInsets.left forKey:@"capInsetLeft"];
+ [coder encodeFloat:self.capInsets.bottom forKey:@"capInsetBottom"];
+ [coder encodeFloat:self.capInsets.right forKey:@"capInsetRight"];
+}
+
+@end

0 comments on commit 3a67a27

Please sign in to comment.
Something went wrong with that request. Please try again.