1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/Source/BoardUIView.m Mon Mar 10 17:32:04 2008 -0700
1.3 @@ -0,0 +1,222 @@
1.4 +//
1.5 +// BoardUIView.m
1.6 +// GeekGameBoard
1.7 +//
1.8 +// Created by Jens Alfke on 3/7/08.
1.9 +// Copyright 2008 __MyCompanyName__. All rights reserved.
1.10 +//
1.11 +
1.12 +#import "BoardUIView.h"
1.13 +#import "Bit.h"
1.14 +#import "BitHolder.h"
1.15 +#import "Game.h"
1.16 +#import "QuartzUtils.h"
1.17 +#import "GGBUtils.h"
1.18 +
1.19 +
1.20 +@implementation BoardUIView
1.21 +
1.22 +
1.23 +- (id)initWithFrame:(CGRect)frame {
1.24 + if ( (self = [super initWithFrame:frame]) ) {
1.25 + // Initialization code here.
1.26 + }
1.27 + return self;
1.28 +}
1.29 +
1.30 +
1.31 +- (void)dealloc
1.32 +{
1.33 + [_game release];
1.34 + [super dealloc];
1.35 +}
1.36 +
1.37 +
1.38 +@synthesize game=_game, gameboard=_gameboard;
1.39 +
1.40 +
1.41 +- (void) startGameNamed: (NSString*)gameClassName
1.42 +{
1.43 + if( _gameboard ) {
1.44 + [_gameboard removeFromSuperlayer];
1.45 + _gameboard = nil;
1.46 + }
1.47 + _gameboard = [[GGBLayer alloc] init];
1.48 + _gameboard.frame = [self gameBoardFrame];
1.49 + _gameboard.autoresizingMask = kCALayerWidthSizable | kCALayerHeightSizable;
1.50 + [self.layer addSublayer: _gameboard];
1.51 + [_gameboard release];
1.52 +
1.53 + Class gameClass = NSClassFromString(gameClassName);
1.54 + NSAssert1(gameClass,@"Unknown game '%@'",gameClassName);
1.55 + setObj(&_game, [[gameClass alloc] initWithBoard: _gameboard]);
1.56 +}
1.57 +
1.58 +
1.59 +- (CGRect) gameBoardFrame
1.60 +{
1.61 + return self.layer.bounds;
1.62 +}
1.63 +
1.64 +
1.65 +#pragma mark -
1.66 +#pragma mark HIT-TESTING:
1.67 +
1.68 +
1.69 +// Hit-testing callbacks (to identify which layers caller is interested in):
1.70 +typedef BOOL (*LayerMatchCallback)(CALayer*);
1.71 +
1.72 +static BOOL layerIsBit( CALayer* layer ) {return [layer isKindOfClass: [Bit class]];}
1.73 +static BOOL layerIsBitHolder( CALayer* layer ) {return [layer conformsToProtocol: @protocol(BitHolder)];}
1.74 +
1.75 +
1.76 +/** Locates the layer at a given point in window coords.
1.77 + If the leaf layer doesn't pass the layer-match callback, the nearest ancestor that does is returned.
1.78 + If outOffset is provided, the point's position relative to the layer is stored into it. */
1.79 +- (CALayer*) hitTestPoint: (CGPoint)where
1.80 + forLayerMatching: (LayerMatchCallback)match
1.81 + offset: (CGPoint*)outOffset
1.82 +{
1.83 + where = [_gameboard convertPoint: where fromLayer: self.layer];
1.84 + CALayer *layer = [_gameboard hitTest: where];
1.85 + while( layer ) {
1.86 + if( match(layer) ) {
1.87 + CGPoint bitPos = [self.layer convertPoint: layer.position
1.88 + fromLayer: layer.superlayer];
1.89 + if( outOffset )
1.90 + *outOffset = CGPointMake( bitPos.x-where.x, bitPos.y-where.y);
1.91 + return layer;
1.92 + } else
1.93 + layer = layer.superlayer;
1.94 + }
1.95 + return nil;
1.96 +}
1.97 +
1.98 +
1.99 +#pragma mark -
1.100 +#pragma mark MOUSE CLICKS & DRAGS:
1.101 +
1.102 +
1.103 +- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
1.104 +{
1.105 + NSAssert(touches.count==1,@"No multitouch support yet");
1.106 + UITouch *touch = touches.anyObject;
1.107 +
1.108 + _dragStartPos = touch.locationInView;
1.109 + _dragBit = (Bit*) [self hitTestPoint: _dragStartPos
1.110 + forLayerMatching: layerIsBit
1.111 + offset: &_dragOffset];
1.112 + if( _dragBit ) {
1.113 + _dragMoved = NO;
1.114 + _dropTarget = nil;
1.115 + _oldHolder = _dragBit.holder;
1.116 + // Ask holder's and game's permission before dragging:
1.117 + if( _oldHolder )
1.118 + _dragBit = [_oldHolder canDragBit: _dragBit];
1.119 + if( _dragBit && ! [_game canBit: _dragBit moveFrom: _oldHolder] ) {
1.120 + [_oldHolder cancelDragBit: _dragBit];
1.121 + _dragBit = nil;
1.122 + }
1.123 + if( ! _dragBit ) {
1.124 + _oldHolder = nil;
1.125 + Beep();
1.126 + return;
1.127 + }
1.128 + // Start dragging:
1.129 + _oldSuperlayer = _dragBit.superlayer;
1.130 + _oldLayerIndex = [_oldSuperlayer.sublayers indexOfObjectIdenticalTo: _dragBit];
1.131 + _oldPos = _dragBit.position;
1.132 + ChangeSuperlayer(_dragBit, self.layer, self.layer.sublayers.count);
1.133 + _dragBit.pickedUp = YES;
1.134 + } else
1.135 + Beep();
1.136 +}
1.137 +
1.138 +
1.139 +- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
1.140 +{
1.141 + NSAssert(touches.count==1,@"No multitouch support yet");
1.142 + UITouch *touch = touches.anyObject;
1.143 +
1.144 + if( _dragBit ) {
1.145 + // Get the mouse position, and see if we've moved 3 pixels since the mouseDown:
1.146 + CGPoint pos = touch.locationInView;
1.147 + if( fabs(pos.x-_dragStartPos.x)>=3 || fabs(pos.y-_dragStartPos.y)>=3 )
1.148 + _dragMoved = YES;
1.149 +
1.150 + // Move the _dragBit (without animation -- it's unnecessary and slows down responsiveness):
1.151 + pos.x += _dragOffset.x;
1.152 + pos.y += _dragOffset.y;
1.153 +
1.154 + CGPoint newPos = [_dragBit.superlayer convertPoint: pos fromLayer: self.layer];
1.155 +
1.156 + [CATransaction flush];
1.157 + [CATransaction begin];
1.158 + [CATransaction setValue:(id)kCFBooleanTrue
1.159 + forKey:kCATransactionDisableActions];
1.160 + _dragBit.position = newPos;
1.161 + [CATransaction commit];
1.162 +
1.163 + // Find what it's over:
1.164 + id<BitHolder> target = (id<BitHolder>) [self hitTestPoint: pos
1.165 + forLayerMatching: layerIsBitHolder
1.166 + offset: NULL];
1.167 + if( target == _oldHolder )
1.168 + target = nil;
1.169 + if( target != _dropTarget ) {
1.170 + [_dropTarget willNotDropBit: _dragBit];
1.171 + _dropTarget.highlighted = NO;
1.172 + _dropTarget = nil;
1.173 + }
1.174 + if( target ) {
1.175 + CGPoint targetPos = [(CALayer*)target convertPoint: _dragBit.position
1.176 + fromLayer: _dragBit.superlayer];
1.177 + if( [target canDropBit: _dragBit atPoint: targetPos]
1.178 + && [_game canBit: _dragBit moveFrom: _oldHolder to: target] ) {
1.179 + _dropTarget = target;
1.180 + _dropTarget.highlighted = YES;
1.181 + }
1.182 + }
1.183 + }
1.184 +}
1.185 +
1.186 +
1.187 +- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
1.188 +{
1.189 + if( _dragBit ) {
1.190 + if( _dragMoved ) {
1.191 + // Update the drag tracking to the final mouse position:
1.192 + [self touchesMoved: touches withEvent: event];
1.193 + _dropTarget.highlighted = NO;
1.194 + _dragBit.pickedUp = NO;
1.195 +
1.196 + // Is the move legal?
1.197 + if( _dropTarget && [_dropTarget dropBit: _dragBit
1.198 + atPoint: [(CALayer*)_dropTarget convertPoint: _dragBit.position
1.199 + fromLayer: _dragBit.superlayer]] ) {
1.200 + // Yes, notify the interested parties:
1.201 + [_oldHolder draggedBit: _dragBit to: _dropTarget];
1.202 + [_game bit: _dragBit movedFrom: _oldHolder to: _dropTarget];
1.203 + } else {
1.204 + // Nope, cancel:
1.205 + [_dropTarget willNotDropBit: _dragBit];
1.206 + ChangeSuperlayer(_dragBit, _oldSuperlayer, _oldLayerIndex);
1.207 + _dragBit.position = _oldPos;
1.208 + [_oldHolder cancelDragBit: _dragBit];
1.209 + }
1.210 + } else {
1.211 + // Just a click, without a drag:
1.212 + _dropTarget.highlighted = NO;
1.213 + _dragBit.pickedUp = NO;
1.214 + ChangeSuperlayer(_dragBit, _oldSuperlayer, _oldLayerIndex);
1.215 + [_oldHolder cancelDragBit: _dragBit];
1.216 + if( ! [_game clickedBit: _dragBit] )
1.217 + Beep();
1.218 + }
1.219 + _dropTarget = nil;
1.220 + _dragBit = nil;
1.221 + }
1.222 +}
1.223 +
1.224 +
1.225 +@end