Source/BoardUIView.m
author Jens Alfke <jens@mooseyard.com>
Mon Mar 10 17:32:04 2008 -0700 (2008-03-10)
changeset 2 7b0441db81e5
child 3 40d225cf9c43
permissions -rw-r--r--
Oops, needed to fix an #include
jens@1
     1
//
jens@1
     2
//  BoardUIView.m
jens@1
     3
//  GeekGameBoard
jens@1
     4
//
jens@1
     5
//  Created by Jens Alfke on 3/7/08.
jens@1
     6
//  Copyright 2008 __MyCompanyName__. All rights reserved.
jens@1
     7
//
jens@1
     8
jens@1
     9
#import "BoardUIView.h"
jens@1
    10
#import "Bit.h"
jens@1
    11
#import "BitHolder.h"
jens@1
    12
#import "Game.h"
jens@1
    13
#import "QuartzUtils.h"
jens@1
    14
#import "GGBUtils.h"
jens@1
    15
jens@1
    16
jens@1
    17
@implementation BoardUIView
jens@1
    18
jens@1
    19
jens@1
    20
- (id)initWithFrame:(CGRect)frame {
jens@1
    21
    if ( (self = [super initWithFrame:frame]) ) {
jens@1
    22
        // Initialization code here.
jens@1
    23
    }
jens@1
    24
    return self;
jens@1
    25
}
jens@1
    26
jens@1
    27
jens@1
    28
- (void)dealloc
jens@1
    29
{
jens@1
    30
    [_game release];
jens@1
    31
    [super dealloc];
jens@1
    32
}
jens@1
    33
jens@1
    34
jens@1
    35
@synthesize game=_game, gameboard=_gameboard;
jens@1
    36
jens@1
    37
jens@1
    38
- (void) startGameNamed: (NSString*)gameClassName
jens@1
    39
{
jens@1
    40
    if( _gameboard ) {
jens@1
    41
        [_gameboard removeFromSuperlayer];
jens@1
    42
        _gameboard = nil;
jens@1
    43
    }
jens@1
    44
    _gameboard = [[GGBLayer alloc] init];
jens@1
    45
    _gameboard.frame = [self gameBoardFrame];
jens@1
    46
    _gameboard.autoresizingMask = kCALayerWidthSizable | kCALayerHeightSizable;
jens@1
    47
    [self.layer addSublayer: _gameboard];
jens@1
    48
    [_gameboard release];
jens@1
    49
    
jens@1
    50
    Class gameClass = NSClassFromString(gameClassName);
jens@1
    51
    NSAssert1(gameClass,@"Unknown game '%@'",gameClassName);
jens@1
    52
    setObj(&_game, [[gameClass alloc] initWithBoard: _gameboard]);
jens@1
    53
}
jens@1
    54
jens@1
    55
jens@1
    56
- (CGRect) gameBoardFrame
jens@1
    57
{
jens@1
    58
    return self.layer.bounds;
jens@1
    59
}
jens@1
    60
jens@1
    61
jens@1
    62
#pragma mark -
jens@1
    63
#pragma mark HIT-TESTING:
jens@1
    64
jens@1
    65
jens@1
    66
// Hit-testing callbacks (to identify which layers caller is interested in):
jens@1
    67
typedef BOOL (*LayerMatchCallback)(CALayer*);
jens@1
    68
jens@1
    69
static BOOL layerIsBit( CALayer* layer )        {return [layer isKindOfClass: [Bit class]];}
jens@1
    70
static BOOL layerIsBitHolder( CALayer* layer )  {return [layer conformsToProtocol: @protocol(BitHolder)];}
jens@1
    71
jens@1
    72
jens@1
    73
/** Locates the layer at a given point in window coords.
jens@1
    74
    If the leaf layer doesn't pass the layer-match callback, the nearest ancestor that does is returned.
jens@1
    75
    If outOffset is provided, the point's position relative to the layer is stored into it. */
jens@1
    76
- (CALayer*) hitTestPoint: (CGPoint)where
jens@1
    77
         forLayerMatching: (LayerMatchCallback)match
jens@1
    78
                   offset: (CGPoint*)outOffset
jens@1
    79
{
jens@1
    80
    where = [_gameboard convertPoint: where fromLayer: self.layer];
jens@1
    81
    CALayer *layer = [_gameboard hitTest: where];
jens@1
    82
    while( layer ) {
jens@1
    83
        if( match(layer) ) {
jens@1
    84
            CGPoint bitPos = [self.layer convertPoint: layer.position 
jens@1
    85
                              fromLayer: layer.superlayer];
jens@1
    86
            if( outOffset )
jens@1
    87
                *outOffset = CGPointMake( bitPos.x-where.x, bitPos.y-where.y);
jens@1
    88
            return layer;
jens@1
    89
        } else
jens@1
    90
            layer = layer.superlayer;
jens@1
    91
    }
jens@1
    92
    return nil;
jens@1
    93
}
jens@1
    94
jens@1
    95
jens@1
    96
#pragma mark -
jens@1
    97
#pragma mark MOUSE CLICKS & DRAGS:
jens@1
    98
jens@1
    99
jens@1
   100
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
jens@1
   101
{
jens@1
   102
    NSAssert(touches.count==1,@"No multitouch support yet");
jens@1
   103
    UITouch *touch = touches.anyObject;
jens@1
   104
    
jens@1
   105
    _dragStartPos = touch.locationInView;
jens@1
   106
    _dragBit = (Bit*) [self hitTestPoint: _dragStartPos
jens@1
   107
                        forLayerMatching: layerIsBit 
jens@1
   108
                                  offset: &_dragOffset];
jens@1
   109
    if( _dragBit ) {
jens@1
   110
        _dragMoved = NO;
jens@1
   111
        _dropTarget = nil;
jens@1
   112
        _oldHolder = _dragBit.holder;
jens@1
   113
        // Ask holder's and game's permission before dragging:
jens@1
   114
        if( _oldHolder )
jens@1
   115
            _dragBit = [_oldHolder canDragBit: _dragBit];
jens@1
   116
        if( _dragBit && ! [_game canBit: _dragBit moveFrom: _oldHolder] ) {
jens@1
   117
            [_oldHolder cancelDragBit: _dragBit];
jens@1
   118
            _dragBit = nil;
jens@1
   119
        }
jens@1
   120
        if( ! _dragBit ) {
jens@1
   121
            _oldHolder = nil;
jens@1
   122
            Beep();
jens@1
   123
            return;
jens@1
   124
        }
jens@1
   125
        // Start dragging:
jens@1
   126
        _oldSuperlayer = _dragBit.superlayer;
jens@1
   127
        _oldLayerIndex = [_oldSuperlayer.sublayers indexOfObjectIdenticalTo: _dragBit];
jens@1
   128
        _oldPos = _dragBit.position;
jens@1
   129
        ChangeSuperlayer(_dragBit, self.layer, self.layer.sublayers.count);
jens@1
   130
        _dragBit.pickedUp = YES;
jens@1
   131
    } else
jens@1
   132
        Beep();
jens@1
   133
}
jens@1
   134
jens@1
   135
jens@1
   136
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
jens@1
   137
{
jens@1
   138
    NSAssert(touches.count==1,@"No multitouch support yet");
jens@1
   139
    UITouch *touch = touches.anyObject;
jens@1
   140
    
jens@1
   141
    if( _dragBit ) {
jens@1
   142
        // Get the mouse position, and see if we've moved 3 pixels since the mouseDown:
jens@1
   143
        CGPoint pos = touch.locationInView;
jens@1
   144
        if( fabs(pos.x-_dragStartPos.x)>=3 || fabs(pos.y-_dragStartPos.y)>=3 )
jens@1
   145
            _dragMoved = YES;
jens@1
   146
        
jens@1
   147
        // Move the _dragBit (without animation -- it's unnecessary and slows down responsiveness):
jens@1
   148
        pos.x += _dragOffset.x;
jens@1
   149
        pos.y += _dragOffset.y;
jens@1
   150
        
jens@1
   151
        CGPoint newPos = [_dragBit.superlayer convertPoint: pos fromLayer: self.layer];
jens@1
   152
jens@1
   153
        [CATransaction flush];
jens@1
   154
        [CATransaction begin];
jens@1
   155
        [CATransaction setValue:(id)kCFBooleanTrue
jens@1
   156
                         forKey:kCATransactionDisableActions];
jens@1
   157
        _dragBit.position = newPos;
jens@1
   158
        [CATransaction commit];
jens@1
   159
jens@1
   160
        // Find what it's over:
jens@1
   161
        id<BitHolder> target = (id<BitHolder>) [self hitTestPoint: pos
jens@1
   162
                                                 forLayerMatching: layerIsBitHolder
jens@1
   163
                                                           offset: NULL];
jens@1
   164
        if( target == _oldHolder )
jens@1
   165
            target = nil;
jens@1
   166
        if( target != _dropTarget ) {
jens@1
   167
            [_dropTarget willNotDropBit: _dragBit];
jens@1
   168
            _dropTarget.highlighted = NO;
jens@1
   169
            _dropTarget = nil;
jens@1
   170
        }
jens@1
   171
        if( target ) {
jens@1
   172
            CGPoint targetPos = [(CALayer*)target convertPoint: _dragBit.position
jens@1
   173
                                                     fromLayer: _dragBit.superlayer];
jens@1
   174
            if( [target canDropBit: _dragBit atPoint: targetPos]
jens@1
   175
               && [_game canBit: _dragBit moveFrom: _oldHolder to: target] ) {
jens@1
   176
                _dropTarget = target;
jens@1
   177
                _dropTarget.highlighted = YES;
jens@1
   178
            }
jens@1
   179
        }
jens@1
   180
    }
jens@1
   181
}
jens@1
   182
jens@1
   183
jens@1
   184
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
jens@1
   185
{
jens@1
   186
    if( _dragBit ) {
jens@1
   187
        if( _dragMoved ) {
jens@1
   188
            // Update the drag tracking to the final mouse position:
jens@1
   189
            [self touchesMoved: touches withEvent: event];
jens@1
   190
            _dropTarget.highlighted = NO;
jens@1
   191
            _dragBit.pickedUp = NO;
jens@1
   192
jens@1
   193
            // Is the move legal?
jens@1
   194
            if( _dropTarget && [_dropTarget dropBit: _dragBit
jens@1
   195
                                            atPoint: [(CALayer*)_dropTarget convertPoint: _dragBit.position 
jens@1
   196
                                                                            fromLayer: _dragBit.superlayer]] ) {
jens@1
   197
                // Yes, notify the interested parties:
jens@1
   198
                [_oldHolder draggedBit: _dragBit to: _dropTarget];
jens@1
   199
                [_game bit: _dragBit movedFrom: _oldHolder to: _dropTarget];
jens@1
   200
            } else {
jens@1
   201
                // Nope, cancel:
jens@1
   202
                [_dropTarget willNotDropBit: _dragBit];
jens@1
   203
                ChangeSuperlayer(_dragBit, _oldSuperlayer, _oldLayerIndex);
jens@1
   204
                _dragBit.position = _oldPos;
jens@1
   205
                [_oldHolder cancelDragBit: _dragBit];
jens@1
   206
            }
jens@1
   207
        } else {
jens@1
   208
            // Just a click, without a drag:
jens@1
   209
            _dropTarget.highlighted = NO;
jens@1
   210
            _dragBit.pickedUp = NO;
jens@1
   211
            ChangeSuperlayer(_dragBit, _oldSuperlayer, _oldLayerIndex);
jens@1
   212
            [_oldHolder cancelDragBit: _dragBit];
jens@1
   213
            if( ! [_game clickedBit: _dragBit] )
jens@1
   214
                Beep();
jens@1
   215
        }
jens@1
   216
        _dropTarget = nil;
jens@1
   217
        _dragBit = nil;
jens@1
   218
    }
jens@1
   219
}
jens@1
   220
jens@1
   221
jens@1
   222
@end