Permalink
Browse files

Implemented <TUIBridgedView> for TUIView

  • Loading branch information...
1 parent 6094ba6 commit 1ea4a42b7a522be182f4356102950ea397ef197e @jspahrsummers jspahrsummers committed Jul 17, 2012
Showing with 214 additions and 0 deletions.
  1. +16 −0 TwUI.xcodeproj/project.pbxproj
  2. +1 −0 lib/UIKit/TUIKit.h
  3. +20 −0 lib/UIKit/TUIView+TUIBridgedView.h
  4. +177 −0 lib/UIKit/TUIView+TUIBridgedView.m
@@ -293,6 +293,12 @@
D0C7653615B624D900E7AC2C /* NSView+TUIExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = D0C7653215B624D800E7AC2C /* NSView+TUIExtensions.m */; };
D0C7653715B624D900E7AC2C /* NSView+TUIExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = D0C7653215B624D800E7AC2C /* NSView+TUIExtensions.m */; };
D0C7653815B624D900E7AC2C /* NSView+TUIExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = D0C7653215B624D800E7AC2C /* NSView+TUIExtensions.m */; };
+ D0C7654615B626E200E7AC2C /* TUIView+TUIBridgedView.h in Headers */ = {isa = PBXBuildFile; fileRef = D0C7653E15B626E200E7AC2C /* TUIView+TUIBridgedView.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D0C7654715B626E200E7AC2C /* TUIView+TUIBridgedView.h in Headers */ = {isa = PBXBuildFile; fileRef = D0C7653E15B626E200E7AC2C /* TUIView+TUIBridgedView.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D0C7654815B626E200E7AC2C /* TUIView+TUIBridgedView.h in Headers */ = {isa = PBXBuildFile; fileRef = D0C7653E15B626E200E7AC2C /* TUIView+TUIBridgedView.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D0C7654915B626E200E7AC2C /* TUIView+TUIBridgedView.m in Sources */ = {isa = PBXBuildFile; fileRef = D0C7653F15B626E200E7AC2C /* TUIView+TUIBridgedView.m */; };
+ D0C7654A15B626E200E7AC2C /* TUIView+TUIBridgedView.m in Sources */ = {isa = PBXBuildFile; fileRef = D0C7653F15B626E200E7AC2C /* TUIView+TUIBridgedView.m */; };
+ D0C7654B15B626E200E7AC2C /* TUIView+TUIBridgedView.m in Sources */ = {isa = PBXBuildFile; fileRef = D0C7653F15B626E200E7AC2C /* TUIView+TUIBridgedView.m */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -448,6 +454,8 @@
D0C7652315B6232100E7AC2C /* CATransaction+TUIExtensions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "CATransaction+TUIExtensions.m"; sourceTree = "<group>"; };
D0C7653115B624D800E7AC2C /* NSView+TUIExtensions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSView+TUIExtensions.h"; sourceTree = "<group>"; };
D0C7653215B624D800E7AC2C /* NSView+TUIExtensions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSView+TUIExtensions.m"; sourceTree = "<group>"; };
+ D0C7653E15B626E200E7AC2C /* TUIView+TUIBridgedView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "TUIView+TUIBridgedView.h"; sourceTree = "<group>"; };
+ D0C7653F15B626E200E7AC2C /* TUIView+TUIBridgedView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "TUIView+TUIBridgedView.m"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -713,6 +721,8 @@
CBB74C8813BE6E1900C85CB5 /* TUIView+PasteboardDragging.m */,
CBB74C8913BE6E1900C85CB5 /* TUIView+Private.h */,
CBB74C8A13BE6E1900C85CB5 /* TUIView+Private.m */,
+ D0C7653E15B626E200E7AC2C /* TUIView+TUIBridgedView.h */,
+ D0C7653F15B626E200E7AC2C /* TUIView+TUIBridgedView.m */,
CBB74C8B13BE6E1900C85CB5 /* TUIView.h */,
CBB74C8C13BE6E1900C85CB5 /* TUIView.m */,
CBB74C8D13BE6E1900C85CB5 /* TUIViewController.h */,
@@ -745,6 +755,7 @@
D0C7650715B6156A00E7AC2C /* TUIHostView.h in Headers */,
D0C7651815B61E5A00E7AC2C /* TUIBridgedScrollView.h in Headers */,
D0C7653515B624D900E7AC2C /* NSView+TUIExtensions.h in Headers */,
+ D0C7654815B626E200E7AC2C /* TUIView+TUIBridgedView.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -810,6 +821,7 @@
D0C7650515B6156A00E7AC2C /* TUIHostView.h in Headers */,
D0C7651615B61E5A00E7AC2C /* TUIBridgedScrollView.h in Headers */,
D0C7653315B624D900E7AC2C /* NSView+TUIExtensions.h in Headers */,
+ D0C7654615B626E200E7AC2C /* TUIView+TUIBridgedView.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -831,6 +843,7 @@
D0C7650615B6156A00E7AC2C /* TUIHostView.h in Headers */,
D0C7651715B61E5A00E7AC2C /* TUIBridgedScrollView.h in Headers */,
D0C7653415B624D900E7AC2C /* NSView+TUIExtensions.h in Headers */,
+ D0C7654715B626E200E7AC2C /* TUIView+TUIBridgedView.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -1037,6 +1050,7 @@
D0C7652915B6232100E7AC2C /* CALayer+TUIExtensions.m in Sources */,
D0C7652F15B6232100E7AC2C /* CATransaction+TUIExtensions.m in Sources */,
D0C7653815B624D900E7AC2C /* NSView+TUIExtensions.m in Sources */,
+ D0C7654B15B626E200E7AC2C /* TUIView+TUIBridgedView.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -1107,6 +1121,7 @@
D0C7652715B6232100E7AC2C /* CALayer+TUIExtensions.m in Sources */,
D0C7652D15B6232100E7AC2C /* CATransaction+TUIExtensions.m in Sources */,
D0C7653615B624D900E7AC2C /* NSView+TUIExtensions.m in Sources */,
+ D0C7654915B626E200E7AC2C /* TUIView+TUIBridgedView.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -1183,6 +1198,7 @@
D0C7652815B6232100E7AC2C /* CALayer+TUIExtensions.m in Sources */,
D0C7652E15B6232100E7AC2C /* CATransaction+TUIExtensions.m in Sources */,
D0C7653715B624D900E7AC2C /* NSView+TUIExtensions.m in Sources */,
+ D0C7654A15B626E200E7AC2C /* TUIView+TUIBridgedView.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
View
@@ -48,6 +48,7 @@
#import "TUITextField.h"
#import "TUITextView.h"
#import "TUIView.h"
+#import "TUIView+TUIBridgedView.h"
#import "TUIViewController.h"
extern CGContextRef TUIGraphicsGetCurrentContext(void);
@@ -0,0 +1,20 @@
+//
+// TUIView+TUIBridgedView.h
+// TwUI
+//
+// Created by Justin Spahr-Summers on 17.07.12.
+//
+// Portions of this code were taken from Velvet,
+// which is copyright (c) 2012 Bitswift, Inc.
+// See LICENSE.txt for more information.
+//
+
+#import "TUIView.h"
+#import "TUIBridgedView.h"
+
+/**
+ * Implements support for the <TUIBridgedView> protocol in TUIView.
+ */
+@interface TUIView (TUIBridgedView) <TUIBridgedView>
+- (id<TUIBridgedView>)descendantViewAtPoint:(NSPoint)point;
+@end
@@ -0,0 +1,177 @@
+//
+// TUIView+TUIBridgedView.m
+// TwUI
+//
+// Created by Justin Spahr-Summers on 17.07.12.
+//
+// Portions of this code were taken from Velvet,
+// which is copyright (c) 2012 Bitswift, Inc.
+// See LICENSE.txt for more information.
+//
+
+#import "TUIView+TUIBridgedView.h"
+#import "TUINSView.h"
+#import "TUIBridgedScrollView.h"
+#import <objc/runtime.h>
+
+@implementation TUIView (TUIBridgedView)
+
+#pragma mark Properties
+
+// implemented by TUIView proper
+@dynamic layer;
+
+#pragma mark Geometry
+
+// TODO: the implementations of these conversion methods are not strictly
+// correct -- they only take into account position, and do not include things
+// like transforms (however, the implementation does match TUIView geometry)
+
+- (CGPoint)convertFromWindowPoint:(CGPoint)point; {
+ if (self.hostView) {
+ CGPoint pointInHostView = [self.hostView convertFromWindowPoint:point];
+ return [self.layer convertPoint:pointInHostView fromLayer:self.hostView.layer];
+ }
+
+ CGRect hostViewFrame = self.frameInNSView;
+
+ CGPoint pointInHostView = [self.nsView convertPoint:point fromView:nil];
+ return CGPointMake(pointInHostView.x - hostViewFrame.origin.x, pointInHostView.y - hostViewFrame.origin.y);
+}
+
+- (CGPoint)convertToWindowPoint:(CGPoint)point; {
+ if (self.hostView) {
+ CGPoint pointInHostView = [self.layer convertPoint:point toLayer:self.hostView.layer];
+ return [self.hostView convertToWindowPoint:pointInHostView];
+ }
+
+ CGRect hostViewFrame = self.frameInNSView;
+
+ CGPoint pointInHostView = CGPointMake(point.x + hostViewFrame.origin.x, point.y + hostViewFrame.origin.y);
+ return [self.nsView convertPoint:pointInHostView toView:nil];
+}
+
+- (CGRect)convertFromWindowRect:(CGRect)rect; {
+ if (self.hostView) {
+ CGRect rectInHostView = [self.hostView convertFromWindowRect:rect];
+ return [self.layer convertRect:rectInHostView fromLayer:self.hostView.layer];
+ }
+
+ CGRect hostViewFrame = self.frameInNSView;
+
+ CGRect rectInHostView = [self.nsView convertRect:rect fromView:nil];
+ return CGRectOffset(rectInHostView, -hostViewFrame.origin.x, -hostViewFrame.origin.y);
+}
+
+- (CGRect)convertToWindowRect:(CGRect)rect; {
+ if (self.hostView) {
+ CGRect rectInHostView = [self.layer convertRect:rect toLayer:self.hostView.layer];
+ return [self.hostView convertToWindowRect:rectInHostView];
+ }
+
+ CGRect hostViewFrame = self.frameInNSView;
+
+ CGRect rectInHostView = CGRectOffset(rect, hostViewFrame.origin.x, hostViewFrame.origin.y);
+ return [self.nsView convertRect:rectInHostView toView:nil];
+}
+
+#pragma mark Hit testing
+
+- (id<TUIBridgedView>)descendantViewAtPoint:(NSPoint)point {
+ // Clip to self
+ if (!self.userInteractionEnabled || self.hidden || ![self pointInside:point] || self.alpha <= 0.0f)
+ return nil;
+
+ __block id<TUIBridgedView> result = self;
+
+ [self.subviews
+ enumerateObjectsUsingBlock:^(TUIView *view, NSUInteger index, BOOL *stop){
+ CGPoint subviewPoint = [view convertPoint:point fromView:self];
+
+ id<TUIBridgedView> hitTestedView = [view descendantViewAtPoint:subviewPoint];
+ if (hitTestedView) {
+ result = hitTestedView;
+ *stop = YES;
+ }
+ }];
+
+ return result;
+}
+
+- (BOOL)pointInside:(CGPoint)point; {
+ return [self pointInside:point withEvent:nil];
+}
+
+#pragma mark View hierarchy
+
+- (id<TUIHostView>)hostView {
+ id<TUIHostView> hostView = objc_getAssociatedObject(self, @selector(hostView));
+ if (hostView)
+ return hostView;
+ else
+ return self.superview.hostView;
+}
+
+- (id<TUIBridgedView>)immediateParentView {
+ id<TUIHostView> hostView = objc_getAssociatedObject(self, @selector(hostView));
+ if (hostView)
+ return hostView;
+ else
+ return self.superview;
+}
+
+- (void)setHostView:(id<TUIHostView>)hostView {
+ objc_setAssociatedObject(self, @selector(hostView), hostView, OBJC_ASSOCIATION_ASSIGN);
+
+ [self viewHierarchyDidChange];
+}
+
+- (void)ancestorDidLayout; {
+ [self.subviews makeObjectsPerformSelector:_cmd];
+}
+
+- (TUINSView *)ancestorTUINSView; {
+ id<TUIHostView> hostView = self.hostView;
+ if (!hostView)
+ return nil;
+
+ return hostView.ancestorTUINSView;
+}
+
+- (id<TUIBridgedScrollView>)ancestorScrollView; {
+ if ([self conformsToProtocol:@protocol(TUIBridgedScrollView)])
+ return (id)self;
+
+ TUIView *superview = self.superview;
+ if (superview)
+ return superview.ancestorScrollView;
+
+ return self.hostView.ancestorScrollView;
+}
+
+- (void)didMoveFromTUINSView:(TUINSView *)view; {
+ [self.subviews makeObjectsPerformSelector:_cmd withObject:view];
+}
+
+- (void)willMoveToTUINSView:(TUINSView *)view; {
+ [self.subviews makeObjectsPerformSelector:_cmd withObject:view];
+}
+
+- (void)viewHierarchyDidChange; {
+ [self.subviews makeObjectsPerformSelector:_cmd];
+}
+
+- (BOOL)isFocused {
+ return [objc_getAssociatedObject(self, @selector(isFocused)) boolValue];
+}
+
+- (void)setFocused:(BOOL)focused {
+ objc_setAssociatedObject(self, @selector(isFocused), [NSNumber numberWithBool:focused], OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+
+ for (TUIView *view in self.subviews) {
+ view.focused = focused;
+ }
+}
+
+@end
+

0 comments on commit 1ea4a42

Please sign in to comment.