5 // Created by Jens Alfke on 3/7/08.
6 // Copyright 2008 __MyCompanyName__. All rights reserved.
9 #import "BoardUIView.h"
13 #import "QuartzUtils.h"
17 @implementation BoardUIView
20 - (id)initWithFrame:(CGRect)frame {
21 if ( (self = [super initWithFrame:frame]) ) {
22 // Initialization code here.
35 @synthesize game=_game, gameboard=_gameboard;
38 - (void) startGameNamed: (NSString*)gameClassName
41 [_gameboard removeFromSuperlayer];
44 _gameboard = [[GGBLayer alloc] init];
45 _gameboard.frame = [self gameBoardFrame];
46 _gameboard.autoresizingMask = kCALayerWidthSizable | kCALayerHeightSizable;
47 [self.layer addSublayer: _gameboard];
50 Class gameClass = NSClassFromString(gameClassName);
51 NSAssert1(gameClass,@"Unknown game '%@'",gameClassName);
52 setObj(&_game, [[gameClass alloc] initWithBoard: _gameboard]);
56 - (CGRect) gameBoardFrame
58 return self.layer.bounds;
63 #pragma mark HIT-TESTING:
66 // Hit-testing callbacks (to identify which layers caller is interested in):
67 typedef BOOL (*LayerMatchCallback)(CALayer*);
69 static BOOL layerIsBit( CALayer* layer ) {return [layer isKindOfClass: [Bit class]];}
70 static BOOL layerIsBitHolder( CALayer* layer ) {return [layer conformsToProtocol: @protocol(BitHolder)];}
73 /** Locates the layer at a given point in window coords.
74 If the leaf layer doesn't pass the layer-match callback, the nearest ancestor that does is returned.
75 If outOffset is provided, the point's position relative to the layer is stored into it. */
76 - (CALayer*) hitTestPoint: (CGPoint)where
77 forLayerMatching: (LayerMatchCallback)match
78 offset: (CGPoint*)outOffset
80 where = [_gameboard convertPoint: where fromLayer: self.layer];
81 CALayer *layer = [_gameboard hitTest: where];
84 CGPoint bitPos = [self.layer convertPoint: layer.position
85 fromLayer: layer.superlayer];
87 *outOffset = CGPointMake( bitPos.x-where.x, bitPos.y-where.y);
90 layer = layer.superlayer;
97 #pragma mark MOUSE CLICKS & DRAGS:
100 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
102 NSAssert(touches.count==1,@"No multitouch support yet");
103 UITouch *touch = touches.anyObject;
105 _dragStartPos = touch.locationInView;
106 _dragBit = (Bit*) [self hitTestPoint: _dragStartPos
107 forLayerMatching: layerIsBit
108 offset: &_dragOffset];
112 _oldHolder = _dragBit.holder;
113 // Ask holder's and game's permission before dragging:
115 _dragBit = [_oldHolder canDragBit: _dragBit];
116 if( _dragBit && ! [_game canBit: _dragBit moveFrom: _oldHolder] ) {
117 [_oldHolder cancelDragBit: _dragBit];
126 _oldSuperlayer = _dragBit.superlayer;
127 _oldLayerIndex = [_oldSuperlayer.sublayers indexOfObjectIdenticalTo: _dragBit];
128 _oldPos = _dragBit.position;
129 ChangeSuperlayer(_dragBit, self.layer, self.layer.sublayers.count);
130 _dragBit.pickedUp = YES;
136 - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
138 NSAssert(touches.count==1,@"No multitouch support yet");
139 UITouch *touch = touches.anyObject;
142 // Get the mouse position, and see if we've moved 3 pixels since the mouseDown:
143 CGPoint pos = touch.locationInView;
144 if( fabs(pos.x-_dragStartPos.x)>=3 || fabs(pos.y-_dragStartPos.y)>=3 )
147 // Move the _dragBit (without animation -- it's unnecessary and slows down responsiveness):
148 pos.x += _dragOffset.x;
149 pos.y += _dragOffset.y;
151 CGPoint newPos = [_dragBit.superlayer convertPoint: pos fromLayer: self.layer];
153 [CATransaction flush];
154 [CATransaction begin];
155 [CATransaction setValue:(id)kCFBooleanTrue
156 forKey:kCATransactionDisableActions];
157 _dragBit.position = newPos;
158 [CATransaction commit];
160 // Find what it's over:
161 id<BitHolder> target = (id<BitHolder>) [self hitTestPoint: pos
162 forLayerMatching: layerIsBitHolder
164 if( target == _oldHolder )
166 if( target != _dropTarget ) {
167 [_dropTarget willNotDropBit: _dragBit];
168 _dropTarget.highlighted = NO;
172 CGPoint targetPos = [(CALayer*)target convertPoint: _dragBit.position
173 fromLayer: _dragBit.superlayer];
174 if( [target canDropBit: _dragBit atPoint: targetPos]
175 && [_game canBit: _dragBit moveFrom: _oldHolder to: target] ) {
176 _dropTarget = target;
177 _dropTarget.highlighted = YES;
184 - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
188 // Update the drag tracking to the final mouse position:
189 [self touchesMoved: touches withEvent: event];
190 _dropTarget.highlighted = NO;
191 _dragBit.pickedUp = NO;
193 // Is the move legal?
194 if( _dropTarget && [_dropTarget dropBit: _dragBit
195 atPoint: [(CALayer*)_dropTarget convertPoint: _dragBit.position
196 fromLayer: _dragBit.superlayer]] ) {
197 // Yes, notify the interested parties:
198 [_oldHolder draggedBit: _dragBit to: _dropTarget];
199 [_game bit: _dragBit movedFrom: _oldHolder to: _dropTarget];
202 [_dropTarget willNotDropBit: _dragBit];
203 ChangeSuperlayer(_dragBit, _oldSuperlayer, _oldLayerIndex);
204 _dragBit.position = _oldPos;
205 [_oldHolder cancelDragBit: _dragBit];
208 // Just a click, without a drag:
209 _dropTarget.highlighted = NO;
210 _dragBit.pickedUp = NO;
211 ChangeSuperlayer(_dragBit, _oldSuperlayer, _oldLayerIndex);
212 [_oldHolder cancelDragBit: _dragBit];
213 if( ! [_game clickedBit: _dragBit] )