Skip to content
Browse files

Merge remote-tracking branch 'galaxas/dev' into dev

  • Loading branch information...
2 parents 36e4a81 + 7f3f9e5 commit 1c7a8cda9f9a22e87f4eebe85211677a1d343feb @jspahrsummers jspahrsummers committed
View
2 ExampleProject/ConcordeExample/ExampleAppDelegate.m
@@ -52,7 +52,7 @@ - (void)applicationDidFinishLaunching:(NSNotification *)aNotification
tuiScrollViewContainer.rootView = scrollExample;
[self showTableViewExampleWindow:nil];
-
+
}
/**
View
7 README.md
@@ -1,4 +1,4 @@
-# TwUI 0.2.0
+# TwUI 0.2.0r1
TwUI is a hardware accelerated UI framework for Mac, inspired by UIKit. It enables:
@@ -11,6 +11,7 @@ It differs from UIKit in a few ways:
* Block-based layout and drawRect
* A consistent coordinate system (bottom left origin)
* Sub-pixel text rendering
+* Layout Constraints (ala Cocoa Auto-Layout)
# Setup
@@ -26,7 +27,7 @@ An included example project shows off the basic construction of a pure TwUI-base
# Status
-TwUI should be considered an alpha project. It is current shipping in Twitter for Mac, in use 24/7 by many, many users and has proven itself very stable. The code still has a few Twitter-for-Mac-isms that should be refactored and cleaned up.
+TwUI should be considered an alpha project. It is current shipping in Twitter for Mac and Github for Mac, in use 24/7 by many, many users and has proven itself very stable.
This project follows the [SemVer](http://semver.org/) standard. The API may change in backwards-incompatible ways before the 1.0 release.
@@ -40,8 +41,6 @@ There are many places where TwUI could be improved:
* Text editing. TUITextEditor is a simple text editor (built on TUITextRenderer). It provides basic editing support and handles a number of standard keybindings. Fleshing this out to be indistinguishable from NSTextView (read: spellchecking, autocorrect) would be useful. If the logic around this were self-contained it would even be great as a standalone project, useful for anyone looking to build a custom text editor for the Mac.
-* Reverse-hosting. Right now TUIViews may be hosted inside of an existing NSView hierarchy. It would be useful if the reverse were possible, adding NSViews inside of TUIViews. Doing so in a robust way so event routing, the responder chain, and CAAnimations all just work is a challenge.
-
# Documentation
You can generate documentation with [doxygen](http://www.doxygen.org). Install it, and then run:
View
24 TwUI.xcodeproj/project.pbxproj
@@ -9,6 +9,12 @@
/* Begin PBXBuildFile section */
30D399C9156D8ADD006ECDAE /* TUIProgressBar.m in Sources */ = {isa = PBXBuildFile; fileRef = 30D399C7156D8ADD006ECDAE /* TUIProgressBar.m */; };
30D39A0D156D8F71006ECDAE /* TUIProgressBar.h in Headers */ = {isa = PBXBuildFile; fileRef = 30D399C6156D8ADD006ECDAE /* TUIProgressBar.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 48A10E8115B7769A007F9EE3 /* TUILayoutConstraint.h in Headers */ = {isa = PBXBuildFile; fileRef = 48A10E7D15B7769A007F9EE3 /* TUILayoutConstraint.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 48A10E8215B7769A007F9EE3 /* TUILayoutConstraint.m in Sources */ = {isa = PBXBuildFile; fileRef = 48A10E7E15B7769A007F9EE3 /* TUILayoutConstraint.m */; };
+ 48A10E8315B7769A007F9EE3 /* TUILayoutManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 48A10E7F15B7769A007F9EE3 /* TUILayoutManager.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 48A10E8415B7769A007F9EE3 /* TUILayoutManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 48A10E8015B7769A007F9EE3 /* TUILayoutManager.m */; };
+ 48A10E8915B778E8007F9EE3 /* TUIView+Layout.m in Sources */ = {isa = PBXBuildFile; fileRef = 48A10E8715B778E8007F9EE3 /* TUIView+Layout.m */; };
+ 48A10E8B15B77A46007F9EE3 /* TUIView+Layout.h in Headers */ = {isa = PBXBuildFile; fileRef = 48A10E8A15B77A46007F9EE3 /* TUIView+Layout.h */; settings = {ATTRIBUTES = (Public, ); }; };
5E6ECEB313BE791600109598 /* ApplicationServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5EE9839C13BE7650005F430D /* ApplicationServices.framework */; };
5E6ECEB413BE791600109598 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CB5E31B613BE6F49004B7899 /* QuartzCore.framework */; };
5E6ECEB613BE791C00109598 /* ApplicationServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5EE9839C13BE7650005F430D /* ApplicationServices.framework */; };
@@ -284,6 +290,12 @@
/* Begin PBXFileReference section */
30D399C6156D8ADD006ECDAE /* TUIProgressBar.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TUIProgressBar.h; sourceTree = "<group>"; };
30D399C7156D8ADD006ECDAE /* TUIProgressBar.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TUIProgressBar.m; sourceTree = "<group>"; };
+ 48A10E7D15B7769A007F9EE3 /* TUILayoutConstraint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TUILayoutConstraint.h; sourceTree = "<group>"; };
+ 48A10E7E15B7769A007F9EE3 /* TUILayoutConstraint.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TUILayoutConstraint.m; sourceTree = "<group>"; };
+ 48A10E7F15B7769A007F9EE3 /* TUILayoutManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TUILayoutManager.h; sourceTree = "<group>"; };
+ 48A10E8015B7769A007F9EE3 /* TUILayoutManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TUILayoutManager.m; sourceTree = "<group>"; };
+ 48A10E8715B778E8007F9EE3 /* TUIView+Layout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "TUIView+Layout.m"; sourceTree = "<group>"; };
+ 48A10E8A15B77A46007F9EE3 /* TUIView+Layout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "TUIView+Layout.h"; sourceTree = "<group>"; };
5EE9839C13BE7650005F430D /* ApplicationServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ApplicationServices.framework; path = System/Library/Frameworks/ApplicationServices.framework; sourceTree = SDKROOT; };
5EE983B713BE7809005F430D /* libtwui.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libtwui.a; sourceTree = BUILT_PRODUCTS_DIR; };
8819794213E26E0200AA39EB /* TUIView+Accessibility.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "TUIView+Accessibility.h"; sourceTree = "<group>"; };
@@ -604,6 +616,10 @@
CBB74C5913BE6E1900C85CB5 /* TUIImageView.m */,
CBB74C5C13BE6E1900C85CB5 /* TUILabel.h */,
CBB74C5D13BE6E1900C85CB5 /* TUILabel.m */,
+ 48A10E7D15B7769A007F9EE3 /* TUILayoutConstraint.h */,
+ 48A10E7E15B7769A007F9EE3 /* TUILayoutConstraint.m */,
+ 48A10E7F15B7769A007F9EE3 /* TUILayoutManager.h */,
+ 48A10E8015B7769A007F9EE3 /* TUILayoutManager.m */,
CBB74C5E13BE6E1900C85CB5 /* TUINSView+Hyperfocus.h */,
CBB74C5F13BE6E1900C85CB5 /* TUINSView+Hyperfocus.m */,
CBB74C6013BE6E1900C85CB5 /* TUINSView+NSTextInputClient.m */,
@@ -661,6 +677,8 @@
CBB74C8213BE6E1900C85CB5 /* TUIView+Animation.m */,
CBB74C8313BE6E1900C85CB5 /* TUIView+Event.h */,
CBB74C8413BE6E1900C85CB5 /* TUIView+Event.m */,
+ 48A10E8A15B77A46007F9EE3 /* TUIView+Layout.h */,
+ 48A10E8715B778E8007F9EE3 /* TUIView+Layout.m */,
CBB74C8513BE6E1900C85CB5 /* TUIView+NSTextInputClient.h */,
CBB74C8613BE6E1900C85CB5 /* TUIView+NSTextInputClient.m */,
CBB74C8713BE6E1900C85CB5 /* TUIView+PasteboardDragging.h */,
@@ -756,6 +774,9 @@
88EFFB5113F417E200CF91A9 /* TUITextViewEditor.h in Headers */,
88D25F5513F5D96500CFAAA9 /* TUITableView+Cell.h in Headers */,
88A4AFDE145A16CA0071CF22 /* TUITextRenderer+Accessibility.h in Headers */,
+ 48A10E8115B7769A007F9EE3 /* TUILayoutConstraint.h in Headers */,
+ 48A10E8315B7769A007F9EE3 /* TUILayoutManager.h in Headers */,
+ 48A10E8B15B77A46007F9EE3 /* TUIView+Layout.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -1041,6 +1062,9 @@
884E8F5E1538809C000F7A8D /* CAAnimation+TUIExtensions.m in Sources */,
30D399C9156D8ADD006ECDAE /* TUIProgressBar.m in Sources */,
88D81D001577EF0D009D453B /* TUIStyledView.m in Sources */,
+ 48A10E8215B7769A007F9EE3 /* TUILayoutConstraint.m in Sources */,
+ 48A10E8415B7769A007F9EE3 /* TUILayoutManager.m in Sources */,
+ 48A10E8915B778E8007F9EE3 /* TUIView+Layout.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
View
61 lib/UIKit/TUILayoutConstraint.h
@@ -0,0 +1,61 @@
+@class TUIView;
+
+typedef enum : NSUInteger {
+ TUILayoutConstraintAttributeMinY = 1,
+ TUILayoutConstraintAttributeMaxY = 2,
+ TUILayoutConstraintAttributeMinX = 3,
+ TUILayoutConstraintAttributeMaxX = 4,
+ TUILayoutConstraintAttributeWidth = 5,
+ TUILayoutConstraintAttributeHeight = 6,
+ TUILayoutConstraintAttributeMidY = 7,
+ TUILayoutConstraintAttributeMidX = 8,
+
+ TUILayoutConstraintAttributeMinXMinY = 101,
+ TUILayoutConstraintAttributeMinXMidY = 102,
+ TUILayoutConstraintAttributeMinXMaxY = 103,
+
+ TUILayoutConstraintAttributeMidXMinY = 104,
+ TUILayoutConstraintAttributeMidXMidY = 105,
+ TUILayoutConstraintAttributeMidXMaxY = 106,
+
+ TUILayoutConstraintAttributeMaxXMinY = 107,
+ TUILayoutConstraintAttributeMaxXMidY = 108,
+ TUILayoutConstraintAttributeMaxXMaxY = 109,
+
+ TUILayoutConstraintAttributeBoundsCenter = 110,
+
+ TUILayoutConstraintAttributeFrame = 1000,
+ TUILayoutConstraintAttributeBounds = 1001
+} TUILayoutConstraintAttribute;
+
+typedef CGFloat (^TUILayoutTransformer)(CGFloat);
+
+@interface TUILayoutConstraint : NSObject
+
+@property (readonly) TUILayoutConstraintAttribute attribute;
+@property (readonly) TUILayoutConstraintAttribute sourceAttribute;
+@property (readonly) NSString *sourceName;
+
++ (id)constraintWithAttribute:(TUILayoutConstraintAttribute)attr
+ relativeTo:(NSString *)source
+ attribute:(TUILayoutConstraintAttribute)srcAttr;
++ (id)constraintWithAttribute:(TUILayoutConstraintAttribute)attr
+ relativeTo:(NSString *)source
+ attribute:(TUILayoutConstraintAttribute)srcAttr
+ offset:(CGFloat)offset;
++ (id)constraintWithAttribute:(TUILayoutConstraintAttribute)attr
+ relativeTo:(NSString *)source
+ attribute:(TUILayoutConstraintAttribute)srcAttr
+ scale:(CGFloat)scale
+ offset:(CGFloat)offset;
+
++ (id)constraintWithAttribute:(TUILayoutConstraintAttribute)attr
+ relativeTo:(NSString *)source
+ attribute:(TUILayoutConstraintAttribute)srcAttr
+ blockTransformer:(TUILayoutTransformer)transformer;
++ (id)constraintWithAttribute:(TUILayoutConstraintAttribute)attr
+ relativeTo:(NSString *)source
+ attribute:(TUILayoutConstraintAttribute)srcAttr
+ valueTransformer:(NSValueTransformer *)transformer;
+
+@end
View
142 lib/UIKit/TUILayoutConstraint.m
@@ -0,0 +1,142 @@
+#import "TUILayoutConstraint.h"
+#import "TUILayoutManager.h"
+
+@interface TUIView (Layout_Private)
+
+- (NSRect)valueForLayoutAttribute:(TUILayoutConstraintAttribute)attribute;
+- (void)setValue:(NSRect)newValue forLayoutAttribute:(TUILayoutConstraintAttribute)attribute;
+
+@end
+
+
+@interface TUILayoutConstraint ()
+
+@property (nonatomic, copy) NSValueTransformer *valueTransformer;
+
+- (CGFloat)transformValue:(CGFloat)original;
+- (void)applyToTargetView:(TUIView *)target;
+- (void)applyToTargetView:(TUIView *)target sourceView:(TUIView *)source;
+
+@end
+
+@interface TUILayoutBlockValueTransformer : NSValueTransformer
+
+@property (nonatomic, copy) TUILayoutTransformer transformer;
+
++ (id)transformerWithBlock:(TUILayoutTransformer)block;
+- (id)initWithBlock:(TUILayoutTransformer)block;
+
+@end
+
+@implementation TUILayoutConstraint
+
+@synthesize attribute = _attribute;
+@synthesize sourceAttribute = _sourceAttribute;
+@synthesize sourceName = _sourceName;
+@synthesize valueTransformer = _valueTransformer;
+
++ (id)constraintWithAttribute:(TUILayoutConstraintAttribute)attr
+ relativeTo:(NSString *)srcLayer
+ attribute:(TUILayoutConstraintAttribute)srcAttr {
+ return [self constraintWithAttribute:attr relativeTo:srcLayer attribute:srcAttr scale:1.0 offset:0.0];
+}
+
++ (id)constraintWithAttribute:(TUILayoutConstraintAttribute)attr
+ relativeTo:(NSString *)srcLayer
+ attribute:(TUILayoutConstraintAttribute)srcAttr
+ offset:(CGFloat)offset {
+ return [self constraintWithAttribute:attr relativeTo:srcLayer attribute:srcAttr scale:1.0 offset:offset];
+}
+
++ (id)constraintWithAttribute:(TUILayoutConstraintAttribute)attr
+ relativeTo:(NSString *)source
+ attribute:(TUILayoutConstraintAttribute)srcAttr
+ scale:(CGFloat)scale
+ offset:(CGFloat)offset {
+ return [[self alloc] initWithAttribute:attr relativeTo:source attribute:srcAttr scale:scale offset:offset];
+}
+
++ (id)constraintWithAttribute:(TUILayoutConstraintAttribute)attr
+ relativeTo:(NSString *)srcLayer
+ attribute:(TUILayoutConstraintAttribute)srcAttr
+ blockTransformer:(TUILayoutTransformer)transformer {
+ TUILayoutBlockValueTransformer *t = [TUILayoutBlockValueTransformer transformerWithBlock:transformer];
+ return [self constraintWithAttribute:attr relativeTo:srcLayer attribute:srcAttr valueTransformer:t];
+}
+
++ (id)constraintWithAttribute:(TUILayoutConstraintAttribute)attr
+ relativeTo:(NSString *)srcLayer
+ attribute:(TUILayoutConstraintAttribute)srcAttr
+ valueTransformer:(NSValueTransformer *)transformer {
+ return [[self alloc] initWithAttribute:attr relativeTo:srcLayer attribute:srcAttr valueTransformer:transformer];
+}
+
+- (id)initWithAttribute:(TUILayoutConstraintAttribute)attr
+ relativeTo:(NSString *)srcLayer
+ attribute:(TUILayoutConstraintAttribute)srcAttr
+ valueTransformer:(NSValueTransformer *)transformer {
+
+ double attributeRange = floor(log10(attr));
+ double sourceAttributeRange = floor(log10(srcAttr));
+
+ NSAssert(fabs(attributeRange - sourceAttributeRange) < 0.001, @"Invalid source and target attributes: %f, %f", sourceAttributeRange, attributeRange);
+
+ if((self = [super init])) {
+ _attribute = attr;
+ _sourceAttribute = srcAttr;
+
+ _sourceName = [srcLayer copy];
+ _valueTransformer = transformer;
+ }
+ return self;
+}
+
+- (CGFloat)transformValue:(CGFloat)original {
+ id transformed = [self.valueTransformer transformedValue:[NSNumber numberWithFloat:original]];
+ return [transformed floatValue];
+}
+
+- (void)applyToTargetView:(TUIView *)target {
+ TUIView *source = [target relativeViewForName:[self sourceName]];
+ [self applyToTargetView:target sourceView:source];
+}
+
+- (void)applyToTargetView:(TUIView *)target sourceView:(TUIView *)source {
+ if(source == target) return;
+ if(source == nil) return;
+ if([self sourceAttribute] == 0) return;
+
+ NSRect sourceValue = [source valueForLayoutAttribute:[self sourceAttribute]];
+ NSRect targetValue = sourceValue;
+
+ if(self.attribute >= TUILayoutConstraintAttributeMinY && self.attribute <= TUILayoutConstraintAttributeMidX)
+ targetValue.origin.x = [self transformValue:sourceValue.origin.x];
+
+ [target setValue:targetValue forLayoutAttribute:[self attribute]];
+}
+
+@end
+
+@implementation TUILayoutBlockValueTransformer
+
+@synthesize transformer = _transformer;
+
++ (id)transformerWithBlock:(TUILayoutTransformer)block {
+ return [[self alloc] initWithBlock:block];
+}
+
+- (id)initWithBlock:(TUILayoutTransformer)block {
+ if((self = [super init])) {
+ _transformer = [block copy];
+ }
+ return self;
+}
+
+- (id)transformedValue:(id)value {
+ if(!self.transformer) return nil;
+ CGFloat source = [value floatValue];
+ CGFloat transformed = self.transformer(source);
+ return [NSNumber numberWithFloat:transformed];
+}
+
+@end
View
20 lib/UIKit/TUILayoutManager.h
@@ -0,0 +1,20 @@
+#import "TUIView.h"
+
+@class TUILayoutConstraint;
+
+@interface TUILayoutManager : NSObject
+
++ (id)sharedLayoutManager;
+
+- (void)addLayoutConstraint:(TUILayoutConstraint *)constraint toView:(TUIView *)view;
+- (void)removeLayoutConstraintsFromView:(TUIView *)view;
+
+- (NSArray *)layoutConstraintsOnView:(TUIView *)view;
+- (void)removeAllLayoutConstraints;
+
+- (NSString *)layoutNameForView:(TUIView *)view;
+- (void)setLayoutName:(NSString *)name forView:(TUIView *)view;
+
+- (void)beginProcessingView:(TUIView *)aView;
+
+@end
View
201 lib/UIKit/TUILayoutManager.m
@@ -0,0 +1,201 @@
+#import <objc/runtime.h>
+#import "TUILayoutManager.h"
+#import "TUIView+Layout.h"
+
+@interface TUILayoutConstraint ()
+
+@property (nonatomic, copy) NSValueTransformer *valueTransformer;
+
+- (CGFloat)transformValue:(CGFloat)original;
+- (void)applyToTargetView:(TUIView *)target;
+- (void)applyToTargetView:(TUIView *)target sourceView:(TUIView *)source;
+
+@end
+
+@interface TUILayoutContainer : NSObject
+
+@property (nonatomic, copy) NSString *layoutName;
+@property (nonatomic, strong, readonly) NSMutableArray *layoutConstraints;
+
+@end
+
+@implementation TUILayoutContainer
+
+@synthesize layoutName = _layoutName;
+@synthesize layoutConstraints = _layoutConstraints;
+
++ (id)container {
+ return [[self alloc] init];
+}
+
+- (id)init {
+ if((self = [super init])) {
+ _layoutConstraints = [[NSMutableArray alloc] init];
+ }
+ return self;
+}
+
+@end
+
+
+@interface TUILayoutManager ()
+
+@property (nonatomic, assign) BOOL isProcessingChanges;
+
+@property (nonatomic, strong) NSMapTable *constraints;
+@property (nonatomic, strong) NSMutableArray *viewsToProcess;
+@property (nonatomic, strong) NSMutableSet *processedViews;
+
+@end
+
+@implementation TUILayoutManager
+
+@synthesize isProcessingChanges = _isProcessingChanges;
+@synthesize constraints = _constraints;
+@synthesize viewsToProcess = _viewsToProcess;
+@synthesize processedViews = _processedViews;
+
++ (id)sharedLayoutManager {
+ static TUILayoutManager *_sharedLayoutManager = nil;
+ static dispatch_once_t onceToken;
+
+ dispatch_once(&onceToken, ^{
+ _sharedLayoutManager = [[TUILayoutManager alloc] init];
+ });
+
+ return _sharedLayoutManager;
+}
+
+- (id)init {
+ if((self = [super init])) {
+ [[NSNotificationCenter defaultCenter] addObserver:self
+ selector:@selector(frameChanged:)
+ name:TUIViewFrameDidChangeNotification
+ object:nil];
+ _isProcessingChanges = NO;
+
+ _constraints = [NSMapTable mapTableWithWeakToStrongObjects];
+ _viewsToProcess = [[NSMutableArray alloc] init];
+ _processedViews = [[NSMutableSet alloc] init];
+ }
+ return self;
+}
+
+- (void)dealloc {
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+}
+
+- (void)removeAllLayoutConstraints {
+ [self.constraints removeAllObjects];
+}
+
+- (void)processView:(TUIView *)aView {
+ [self.processedViews addObject:aView];
+
+ NSArray *viewConstraints = [self layoutConstraintsOnView:aView];
+ for(TUILayoutConstraint * constraint in viewConstraints)
+ [constraint applyToTargetView:aView];
+
+ // Order of Operations:
+ // 1. Siblings with constraints to this view.
+ // 2. Children with constraints to superview.
+
+ if([self layoutNameForView:aView] != nil) {
+ NSArray *siblings = [[aView superview] subviews];
+ for(TUIView *subview in siblings) {
+ if(subview == aView) continue;
+
+ NSArray *subviewConstraints = [self layoutConstraintsOnView:subview];
+ for(TUILayoutConstraint *subviewConstraint in subviewConstraints) {
+ TUIView *sourceView = [subview relativeViewForName:[subviewConstraint sourceName]];
+ if(sourceView == aView)
+ [subviewConstraint applyToTargetView:subview sourceView:sourceView];
+ }
+ }
+ }
+
+ NSArray *subviews = [aView subviews];
+ for(TUIView *subview in subviews) {
+ NSArray *subviewConstraints = [self layoutConstraintsOnView:subview];
+ for(TUILayoutConstraint *subviewConstraint in subviewConstraints) {
+ TUIView *sourceView = [subview relativeViewForName:[subviewConstraint sourceName]];
+ if(sourceView == aView)
+ [subviewConstraint applyToTargetView:subview sourceView:sourceView];
+ }
+ }
+}
+
+- (void)beginProcessingView:(TUIView *)view {
+ if(self.isProcessingChanges == NO) {
+ self.isProcessingChanges = YES;
+
+ @autoreleasepool {
+ [self.viewsToProcess addObject:view];
+
+ while([self.viewsToProcess count] > 0) {
+ TUIView *currentView = [self.viewsToProcess objectAtIndex:0];
+ [self.viewsToProcess removeObjectAtIndex:0];
+ if([self.viewsToProcess containsObject:currentView] == NO)
+ [self processView:currentView];
+ }
+
+ [self.viewsToProcess removeAllObjects];
+ [self.processedViews removeAllObjects];
+ }
+
+ self.isProcessingChanges = NO;
+ } else {
+ if([self.processedViews containsObject:view] == NO)
+ [self.viewsToProcess addObject:view];
+ }
+}
+
+- (void)frameChanged:(NSNotification *)notification {
+ TUIView *view = [notification object];
+ [self beginProcessingView:view];
+}
+
+- (void)addLayoutConstraint:(TUILayoutConstraint *)constraint toView:(TUIView *)view {
+ TUILayoutContainer *viewContainer = [self.constraints objectForKey:view];
+ if(viewContainer == nil) {
+ viewContainer = [TUILayoutContainer container];
+ [self.constraints setObject:viewContainer forKey:view];
+ }
+
+ [[viewContainer layoutConstraints] addObject:constraint];
+ [self beginProcessingView:view];
+}
+
+- (void)removeLayoutConstraintsFromView:(TUIView *)view {
+ TUILayoutContainer *viewContainer = [self.constraints objectForKey:view];
+ [[viewContainer layoutConstraints] removeAllObjects];
+ [self.constraints removeObjectForKey:view];
+}
+
+- (NSArray *)layoutConstraintsOnView:(TUIView *)view {
+ TUILayoutContainer *container = [self.constraints objectForKey:view];
+
+ if(!container) return nil;
+ else return [[container layoutConstraints] copy];
+}
+
+- (NSString *)layoutNameForView:(TUIView *)view {
+ TUILayoutContainer *container = [self.constraints objectForKey:view];
+ return [container layoutName];
+}
+
+- (void)setLayoutName:(NSString *)name forView:(TUIView *)view {
+ TUILayoutContainer *viewContainer = [self.constraints objectForKey:view];
+
+ if(name == nil && [[viewContainer layoutConstraints] count] == 0)
+ [self.constraints removeObjectForKey:view];
+ else {
+ if(viewContainer == nil) {
+ viewContainer = [TUILayoutContainer container];
+ [self.constraints setObject:viewContainer forKey:view];
+ }
+ [viewContainer setLayoutName:name];
+ }
+}
+
+@end
View
14 lib/UIKit/TUIView+Layout.h
@@ -0,0 +1,14 @@
+#import <Cocoa/Cocoa.h>
+#import "TUILayoutConstraint.h"
+
+@interface TUIView (Layout)
+
+@property (nonatomic, copy) NSString *layoutName;
+
+- (void)addLayoutConstraint:(TUILayoutConstraint *)constraint;
+- (NSArray *)layoutConstraints;
+- (void)removeAllLayoutConstraints;
+
+- (TUIView *)relativeViewForName:(NSString *)name;
+
+@end
View
197 lib/UIKit/TUIView+Layout.m
@@ -0,0 +1,197 @@
+#import "TUIView.h"
+#import "TUILayoutManager.h"
+
+#define TUIScalarRect(_s) (NSMakeRect((_s), 0, 0, 0))
+#define TUIPointRect(_x,_y) (NSMakeRect((_x), (_y), 0, 0))
+
+#define TUIRectScalar(_r) ((_r).origin.x)
+#define TUIRectPoint(_r) ((_r).origin)
+
+#define TUISetMinX(_r,_v) ((_r).origin.x = (_v))
+#define TUISetMinY(_r,_v) ((_r).origin.y = (_v))
+#define TUISetMidX(_r,_v) ((_r).origin.x = (_v) - ((_r).size.width/2))
+#define TUISetMidY(_r,_v) ((_r).origin.y = (_v) - ((_r).size.height/2))
+#define TUISetMaxX(_r,_v) ((_r).origin.x = (_v) - (_r).size.width)
+#define TUISetMaxY(_r,_v) ((_r).origin.y = (_v) - (_r).size.height)
+
+@interface TUIView (Layout_Private)
+
+- (NSRect)valueForLayoutAttribute:(TUILayoutConstraintAttribute)attribute;
+- (void)setValue:(NSRect)newValue forLayoutAttribute:(TUILayoutConstraintAttribute)attribute;
+
+@end
+
+@implementation TUIView (Layout)
+
+- (void)setLayoutName:(NSString *)newLayoutName {
+ [[TUILayoutManager sharedLayoutManager] setLayoutName:newLayoutName forView:self];
+}
+
+- (NSString *)layoutName {
+ return [[TUILayoutManager sharedLayoutManager] layoutNameForView:self];
+}
+
+- (void)addLayoutConstraint:(TUILayoutConstraint *)constraint {
+ [[TUILayoutManager sharedLayoutManager] addLayoutConstraint:constraint toView:self];
+}
+
+- (NSArray *)layoutConstraints {
+ return [[TUILayoutManager sharedLayoutManager] layoutConstraintsOnView:self];
+}
+
+- (void)removeAllLayoutConstraints {
+ [[TUILayoutManager sharedLayoutManager] removeLayoutConstraintsFromView:self];
+}
+
+- (CGRect)valueForLayoutAttribute:(TUILayoutConstraintAttribute)attribute {
+ CGRect frame = self.frame;
+ CGRect bounds = self.bounds;
+
+ switch(attribute) {
+ case TUILayoutConstraintAttributeMinY:
+ return TUIScalarRect(NSMinY(frame));
+ case TUILayoutConstraintAttributeMaxY:
+ return TUIScalarRect(NSMaxY(frame));
+ case TUILayoutConstraintAttributeMinX:
+ return TUIScalarRect(NSMinX(frame));
+ case TUILayoutConstraintAttributeMaxX:
+ return TUIScalarRect(NSMaxX(frame));
+ case TUILayoutConstraintAttributeWidth:
+ return TUIScalarRect(NSWidth(frame));
+ case TUILayoutConstraintAttributeHeight:
+ return TUIScalarRect(NSHeight(frame));
+ case TUILayoutConstraintAttributeMidY:
+ return TUIScalarRect(NSMidY(frame));
+ case TUILayoutConstraintAttributeMidX:
+ return TUIScalarRect(NSMidX(frame));
+ case TUILayoutConstraintAttributeMinXMinY:
+ return TUIPointRect(NSMinX(frame), NSMinY(frame));
+ case TUILayoutConstraintAttributeMinXMidY:
+ return TUIPointRect(NSMinX(frame), NSMidY(frame));
+ case TUILayoutConstraintAttributeMinXMaxY:
+ return TUIPointRect(NSMinX(frame), NSMaxY(frame));
+ case TUILayoutConstraintAttributeMidXMinY:
+ return TUIPointRect(NSMidX(frame), NSMinY(frame));
+ case TUILayoutConstraintAttributeMidXMidY:
+ return TUIPointRect(NSMidX(frame), NSMidY(frame));
+ case TUILayoutConstraintAttributeMidXMaxY:
+ return TUIPointRect(NSMidX(frame), NSMaxY(frame));
+ case TUILayoutConstraintAttributeMaxXMinY:
+ return TUIPointRect(NSMaxX(frame), NSMinY(frame));
+ case TUILayoutConstraintAttributeMaxXMidY:
+ return TUIPointRect(NSMaxX(frame), NSMidY(frame));
+ case TUILayoutConstraintAttributeMaxXMaxY:
+ return TUIPointRect(NSMaxX(frame), NSMaxY(frame));
+ case TUILayoutConstraintAttributeBoundsCenter:
+ return TUIPointRect(NSMidX(bounds), NSMidY(bounds));
+ case TUILayoutConstraintAttributeFrame:
+ return frame;
+ case TUILayoutConstraintAttributeBounds:
+ return bounds;
+ default:
+ NSAssert(NO, @"Invalid constraint attribute.");
+ return NSZeroRect;
+ }
+}
+
+- (void)setValue:(CGRect)newValue forLayoutAttribute:(TUILayoutConstraintAttribute)attribute {
+ CGRect frame = self.frame;
+ CGRect bounds = self.bounds;
+
+ CGFloat scalarValue = TUIRectScalar(newValue);
+ CGPoint pointValue = TUIRectPoint(newValue);
+ CGRect rectValue = newValue;
+
+ switch(attribute) {
+ case TUILayoutConstraintAttributeMinY:
+ TUISetMinY(frame, scalarValue);
+ break;
+ case TUILayoutConstraintAttributeMaxY:
+ TUISetMaxY(frame, scalarValue);
+ break;
+ case TUILayoutConstraintAttributeMinX:
+ TUISetMinX(frame, scalarValue);
+ break;
+ case TUILayoutConstraintAttributeMaxX:
+ TUISetMaxX(frame, scalarValue);
+ break;
+ case TUILayoutConstraintAttributeWidth:
+ frame.size.width = scalarValue;
+ break;
+ case TUILayoutConstraintAttributeHeight:
+ frame.size.height = scalarValue;
+ break;
+ case TUILayoutConstraintAttributeMidY:
+ TUISetMidY(frame, scalarValue);
+ break;
+ case TUILayoutConstraintAttributeMidX:
+ TUISetMidX(frame, scalarValue);
+ break;
+ case TUILayoutConstraintAttributeMinXMinY:
+ TUISetMinX(frame, pointValue.x);
+ TUISetMinY(frame, pointValue.y);
+ break;
+ case TUILayoutConstraintAttributeMinXMidY:
+ TUISetMinX(frame, pointValue.x);
+ TUISetMidY(frame, pointValue.y);
+ break;
+ case TUILayoutConstraintAttributeMinXMaxY:
+ TUISetMinX(frame, pointValue.x);
+ TUISetMaxY(frame, pointValue.y);
+ break;
+ case TUILayoutConstraintAttributeMidXMinY:
+ TUISetMidX(frame, pointValue.x);
+ TUISetMinY(frame, pointValue.y);
+ break;
+ case TUILayoutConstraintAttributeMidXMidY:
+ TUISetMidX(frame, pointValue.x);
+ TUISetMidY(frame, pointValue.y);
+ break;
+ case TUILayoutConstraintAttributeBoundsCenter:
+ TUISetMidX(bounds, pointValue.x);
+ TUISetMidY(bounds, pointValue.y);
+ [self setBounds:bounds];
+ break;
+ case TUILayoutConstraintAttributeMidXMaxY:
+ TUISetMidX(frame, pointValue.x);
+ TUISetMaxY(frame, pointValue.y);
+ break;
+ case TUILayoutConstraintAttributeMaxXMinY:
+ TUISetMaxX(frame, pointValue.x);
+ TUISetMinY(frame, pointValue.y);
+ break;
+ case TUILayoutConstraintAttributeMaxXMidY:
+ TUISetMaxX(frame, pointValue.x);
+ TUISetMidY(frame, pointValue.y);
+ break;
+ case TUILayoutConstraintAttributeMaxXMaxY:
+ TUISetMaxX(frame, pointValue.x);
+ TUISetMaxY(frame, pointValue.y);
+ break;
+ case TUILayoutConstraintAttributeFrame:
+ frame = rectValue;
+ break;
+ case TUILayoutConstraintAttributeBounds:
+ bounds = rectValue;
+ [self setBounds:bounds];
+ break;
+ default:
+ NSAssert(NO, @"Invalid constraint attribute.");
+ break;
+ }
+
+ [self setFrame:frame];
+}
+
+- (TUIView *)relativeViewForName:(NSString *)name {
+ if([name isEqual:@"superview"])
+ return [self superview];
+
+ NSArray *siblings = [[self superview] subviews];
+ for(TUIView *view in siblings)
+ if([view.layoutName isEqual:name])
+ return (view == self ? nil : view);
+ return nil;
+}
+
+@end
View
2 lib/UIKit/TUIView.h
@@ -21,6 +21,7 @@
extern NSString * const TUIViewWillMoveToWindowNotification; // both notification's userInfo will contain the new window under the key TUIViewWindow
extern NSString * const TUIViewDidMoveToWindowNotification;
extern NSString * const TUIViewWindow;
+extern NSString * const TUIViewFrameDidChangeNotification;
enum {
TUIViewAutoresizingNone = 0,
@@ -489,6 +490,7 @@ extern CGRect(^TUIViewCenteredLayout)(TUIView*);
@end
+#import "TUIView+Layout.h"
#import "TUIView+Private.h"
#import "TUIView+Event.h"
#import "TUIView+PasteboardDragging.h"
View
8 lib/UIKit/TUIView.m
@@ -14,15 +14,17 @@
limitations under the License.
*/
+#import <pthread.h>
#import "TUIView.h"
#import "TUIKit.h"
#import "TUIView+Private.h"
#import "TUIViewController.h"
-#import <pthread.h>
+#import "TUILayoutManager.h"
NSString * const TUIViewWillMoveToWindowNotification = @"TUIViewWillMoveToWindowNotification";
NSString * const TUIViewDidMoveToWindowNotification = @"TUIViewDidMoveToWindowNotification";
NSString * const TUIViewWindow = @"TUIViewWindow";
+NSString * const TUIViewFrameDidChangeNotification = @"TUIViewFrameDidChangeNotification";
CGRect(^TUIViewCenteredLayout)(TUIView*) = nil;
@@ -109,6 +111,9 @@ + (Class)layerClass
- (void)dealloc
{
+ [[TUILayoutManager sharedLayoutManager] removeLayoutConstraintsFromView:self];
+ [[TUILayoutManager sharedLayoutManager] setLayoutName:nil forView:self];
+
[self setTextRenderers:nil];
_layer.delegate = nil;
if(_context.context) {
@@ -499,6 +504,7 @@ - (CGRect)frame
- (void)setFrame:(CGRect)f
{
self.layer.frame = f;
+ [[NSNotificationCenter defaultCenter] postNotificationName:TUIViewFrameDidChangeNotification object:self];
}
- (CGRect)bounds

0 comments on commit 1c7a8cd

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