Created
September 16, 2008 21:28
-
-
Save erichocean/11138 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// SCWindow.h | |
// StateCode | |
// | |
// Created by Erich Ocean on 8/13/08. | |
// Copyright 2008 Erich Atlas Ocean. All rights reserved. | |
// | |
@class SCLayerView, SCView, SCPage; | |
@interface SCWindow : NSWindow | |
{ | |
NSString *nowShowing; | |
NSMutableDictionary *pages; | |
NSMutableArray *bindings; | |
BOOL mouseIsDragging; | |
BOOL isAwake; | |
id delegate; | |
// private | |
CALayer *_mainLayer; | |
SCView *_mouseDownView; | |
NSMutableArray *_lastHovered; | |
NSTrackingArea *_contentViewTrackingArea; | |
NSMutableDictionary *_bindings; | |
SCPage *_currentPage; | |
} | |
@property id delegate; | |
@property BOOL isAwake; | |
@property (copy) NSString *nowShowing; | |
@property (readonly) BOOL mouseIsDragging; | |
@property (readonly) CALayer *rootLayer; | |
@property (readonly) SCView *rootView; | |
+ window; | |
+ window: (NSDictionary *) config; | |
- (void) awake; | |
- (void) insert: (SCView *) view before: (SCView *) beforeView; | |
- (void) append: (SCView *) view; | |
- (SCView *) firstViewForEvent: (NSEvent *) evt; | |
@end | |
@interface SCWindow (Delegate) | |
- (BOOL) wantsMouseMovedEventsInWindow: (SCWindow *) win; | |
- (void) mouseDown: (NSEvent *) evt inWindow: (SCWindow *) win; | |
- (void) mouseMoved: (NSEvent *) evt inWindow: (SCWindow *) win; | |
- (void) mouseUp: (NSEvent *) evt inWindow: (SCWindow *) win; | |
@end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// SCWindow.m | |
// StateCode | |
// | |
// Created by Erich Ocean on 8/13/08. | |
// Copyright 2008 Erich Atlas Ocean. All rights reserved. | |
// | |
#import "SCWindow.h" | |
#import "SCObservable.h" | |
#import "SCView.h" | |
#import "SCPage.h" | |
#import "SCBinding.h" | |
@interface SCWindow (Private) | |
- (NSTrackingArea *) _contentViewTrackingArea; | |
- (void) replaceMainLayerWithLayer: (CALayer *) aLayer; | |
- (void) _notifyDelegateMouseDown: (NSEvent *) evt; | |
- (void) _notifyDelegateMouseMoved: (NSEvent *) evt; | |
- (void) _notifyDelegateMouseUp: (NSEvent *) evt; | |
@end | |
@implementation SCWindow | |
@synthesize delegate; | |
@synthesize isAwake; | |
@synthesize nowShowing; | |
@synthesize mouseIsDragging; | |
+ window { return [[self alloc] init]; } | |
+ window: (NSDictionary *) config | |
{ | |
SCWindow *window = [[self alloc] | |
initWithContentRect: NSMakeRect( 0.0, 0.0, 540.0, 960.0 ) | |
styleMask: NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask | |
backing: NSBackingStoreBuffered | |
defer: NO | |
]; | |
// configure options | |
for ( NSString *key in [config allKeys] ) { | |
if ( [key hasSuffix: @"Page"] ) { | |
[window->pages setObject: [config objectForKey: key] forKey: [key substringWithRange: NSMakeRange( 0, [key length] - 4 )]]; | |
continue; | |
} | |
if ( [key hasSuffix: @"Binding"] ) { | |
// bindings are configure in awake | |
[window->_bindings setObject: [config objectForKey: key] forKey: key]; | |
continue; | |
} | |
// must be an option... | |
NSRange range = [key rangeOfString: @"."]; // check for key paths... | |
if ( range.location == NSNotFound ) { | |
[window setValue: [config valueForKey: key] forKey: key]; | |
} | |
else [window setValue: [config valueForKey: key] forKeyPath: key]; | |
} | |
[window awake]; | |
[window makeKeyAndOrderFront: nil]; | |
return window; | |
} | |
- initWithContentRect: (NSRect) contentRect styleMask: (NSUInteger) windowStyle backing: (NSBackingStoreType) bufferingType defer: (BOOL) deferCreation | |
{ | |
if ( self = [super initWithContentRect: contentRect styleMask: windowStyle backing: bufferingType defer: deferCreation] ) { | |
isAwake = NO; | |
pages = [NSMutableDictionary dictionary]; | |
bindings = [NSMutableArray array]; | |
_bindings = [NSMutableDictionary dictionary]; // used to store bindings until the object awakes | |
} | |
return self; | |
} | |
- (CALayer *) rootLayer | |
{ | |
return [[self contentView] layer]; | |
} | |
- (SCView *) rootView | |
{ | |
return [self rootLayer].delegate; | |
} | |
- (void) awake | |
{ | |
if ( !self.isAwake ) { | |
NSView *nsview = [self contentView]; | |
CALayer *layer = [CALayer layer]; | |
layer.backgroundColor = BLACKCOLOR; | |
[layer setValue: [NSNumber numberWithFloat: SCALE] forKeyPath: @"transform.scale.x"]; | |
[layer setValue: [NSNumber numberWithFloat: SCALE] forKeyPath: @"transform.scale.y"]; | |
// order matters here ... | |
[nsview setLayer: layer]; | |
[nsview setWantsLayer: YES]; // must be called in or after awakeFromNib | |
// ... add a view to manage the rootLayer of the window ... | |
[[SCView alloc] initWithLayer: layer]; | |
[self setAcceptsMouseMovedEvents: YES]; | |
[nsview addTrackingArea: [self _contentViewTrackingArea]]; // FIXME: how does this update on resize? | |
// ... and then configures any bindings ... | |
for ( NSString *key in _bindings ) { | |
SCBinding *binding = [self bindTo: [key substringWithRange: NSMakeRange( 0, [key length] - 7)] from: [_bindings objectForKey: key]]; | |
[bindings addObject: binding]; | |
} | |
// ... and then load the default page | |
SCPage *page = ( nowShowing ) ? [pages objectForKey: nowShowing] : nil; | |
if ( page ) { | |
_currentPage = page; | |
[page awake]; | |
[self.rootView append: page]; | |
} | |
if ( !page && nowShowing ) nowShowing = nil; // don't pretend we're showing a page that doesn't exist | |
_bindings = nil; // don't need to keep this around anymore | |
self.isAwake = YES; | |
} | |
} | |
- (void) setNowShowing: (NSString *) outletName | |
{ | |
if ( ![nowShowing isEqualToString: outletName] ) { | |
[self willChangeValueForKey: @"nowShowing"]; | |
nowShowing = [outletName copy]; | |
SCPage *page = ( nowShowing ) ? [pages objectForKey: nowShowing] : nil; | |
if ( page ) { | |
[page awake]; | |
if ( _currentPage ) [self.rootView replaceChild: _currentPage with: page]; | |
else [self.rootView append: page]; | |
_currentPage = page; | |
} | |
if ( !page && nowShowing ) nowShowing = nil; // don't pretend we're showing a page that doesn't exist | |
[self didChangeValueForKey: @"nowShowing"]; | |
} | |
} | |
- (NSTrackingArea *) _contentViewTrackingArea | |
{ | |
if ( !_contentViewTrackingArea ) { | |
_contentViewTrackingArea = [[NSTrackingArea alloc] initWithRect: [[self contentView] frame] | |
options: NSTrackingMouseMoved | NSTrackingActiveInActiveApp | |
owner: self | |
userInfo: nil ]; | |
} | |
return _contentViewTrackingArea; | |
} | |
- (void) insert: (SCView *) view before: (SCView *) beforeView | |
{ | |
[[self rootView] insert: view before: beforeView]; | |
} | |
- (void) append: (SCView *) view | |
{ | |
[self insert: view before: nil]; | |
} | |
// This finds the first view responding to the event. | |
- (SCView *) firstViewForEvent: (NSEvent *) evt | |
{ | |
NSPoint thePoint = [evt locationInWindow]; | |
CALayer *rootLayer = self.rootLayer; | |
CALayer *hitLayer = [rootLayer hitTest: CGPointMake( thePoint.x, thePoint.y )]; | |
if ( hitLayer ) { | |
SCView *view = hitLayer.delegate; | |
while ( !view ) { | |
hitLayer = hitLayer.superlayer; | |
view = hitLayer.delegate; | |
} | |
return view; | |
} | |
else return nil; | |
} | |
- (void) mouseDown: (NSEvent *) evt | |
{ | |
mouseIsDragging = YES; | |
[self _notifyDelegateMouseDown: evt]; | |
// feed the event to the appropriate layer's view | |
_mouseDownView = [self firstViewForEvent: evt]; | |
if ( _mouseDownView ) [_mouseDownView mouseDown: evt]; | |
} | |
- (void) mouseMoved: (NSEvent *) evt | |
{ | |
NSMutableArray *lh = ( _lastHovered ) ? _lastHovered : [NSMutableArray array]; | |
NSMutableArray *nh = [NSMutableArray array]; | |
SCView *view = [self firstViewForEvent: evt]; | |
SCView *rootView = [self rootView]; | |
[self _notifyDelegateMouseMoved: evt]; | |
// work up the view chain. Notify of mouse entered and mouseMoved if implemented. | |
while ( view && (view != rootView) ) | |
{ | |
if ( [lh containsObject: view] ) { | |
if ( view.wantsMouseMoved ) [view mouseMoved: evt]; | |
[nh addObject: view]; | |
} | |
else { | |
[view mouseEntered: evt]; | |
[nh addObject: view]; | |
} | |
view = [view nextResponder]; | |
} | |
// now find those views last hovered over that were no longer found | |
// in this chain and notify of mouseExited. | |
for ( SCView *exitedView in lh ) | |
{ | |
if ( ![nh containsObject: exitedView] ) [exitedView mouseExited: evt]; | |
} | |
_lastHovered = nh; | |
// also, if a mouseDownView exists, call the mouseDragged action, if it exists. | |
if ( _mouseDownView ) [_mouseDownView mouseDragged: evt]; | |
} | |
- (void) mouseDragged: (NSEvent *) evt | |
{ | |
[self mouseMoved: evt]; | |
} | |
- (void) mouseEntered: (NSEvent *) evt | |
{ | |
[self mouseMoved: evt]; | |
} | |
- (void) mouseExited: (NSEvent *) evt | |
{ | |
[self mouseMoved: evt]; | |
} | |
- (void) mouseUp: (NSEvent *) evt | |
{ | |
[self _notifyDelegateMouseUp: evt]; | |
if ( _mouseDownView ) [_mouseDownView mouseUp: evt]; | |
_mouseDownView = nil; | |
mouseIsDragging = NO; | |
} | |
- (void) setWindowTitle: (NSString *) title | |
{ | |
LOGO( title ); | |
[self setTitle: title]; | |
} | |
#pragma mark Core Animation Helpers | |
- (void) replaceMainLayerWithLayer: (CALayer *) aLayer | |
{ | |
[self.rootLayer insertSublayer: aLayer below: _mainLayer]; | |
if ( _mainLayer ) [_mainLayer removeFromSuperlayer]; | |
_mainLayer = aLayer; | |
} | |
#pragma mark delegate helpers | |
- (void) _notifyDelegateMouseDown: (NSEvent *) evt | |
{ | |
if ( !delegate ) return; | |
if ( [delegate respondsToSelector: @selector(mouseDown:inWindow:)] ) { | |
[delegate mouseDown: evt inWindow: self]; | |
} | |
} | |
- (void) _notifyDelegateMouseMoved: (NSEvent *) evt | |
{ | |
if ( !delegate ) return; | |
if ( [delegate respondsToSelector: @selector(wantsMouseMovedEventsInWindow:)] ) { | |
if ( [delegate wantsMouseMovedEventsInWindow: self] && [delegate respondsToSelector: @selector(mouseMoved:inWindow:)]) { | |
[delegate mouseMoved: evt inWindow: self]; | |
} | |
} | |
} | |
- (void) _notifyDelegateMouseUp: (NSEvent *) evt | |
{ | |
if ( !delegate ) return; | |
if ( [delegate respondsToSelector: @selector(mouseDown:inWindow:)] ) { | |
[delegate mouseUp: evt inWindow: self]; | |
} | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment