# HG changeset patch # User Jens Alfke # Date 1205280590 25200 # Node ID d781b00f3ed484a34238275c8694110f839c648f # Parent 40d225cf9c43faaa7e0c53783e170ee46f13347b Text, playing cards, and Klondike solitaire all work on iPhone now. (Regression: Klondike UI layout has changed, and is awkward on Mac now. Need to special case that.) diff -r 40d225cf9c43 -r d781b00f3ed4 Source/BoardUIView.m --- a/Source/BoardUIView.m Tue Mar 11 09:21:53 2008 -0700 +++ b/Source/BoardUIView.m Tue Mar 11 17:09:50 2008 -0700 @@ -42,19 +42,33 @@ - (void) startGameNamed: (NSString*)gameClassName { + Class gameClass = NSClassFromString(gameClassName); + NSAssert1(gameClass,@"Unknown game '%@'",gameClassName); + + setObj(&_game,nil); if( _gameboard ) { [_gameboard removeFromSuperlayer]; _gameboard = nil; } + + CALayer *rootLayer = self.layer; + self.layer.affineTransform = CGAffineTransformIdentity; + CGRect frame = rootLayer.frame; + frame.origin.x = frame.origin.y = 0; + rootLayer.bounds = frame; + + if( [gameClass landscapeOriented] && frame.size.height > frame.size.width ) { + rootLayer.affineTransform = CGAffineTransformMakeRotation(M_PI/2); + frame = CGRectMake(0,0,frame.size.height,frame.size.width); + rootLayer.bounds = frame; + } + _gameboard = [[GGBLayer alloc] init]; - _gameboard.frame = [self gameBoardFrame]; - _gameboard.autoresizingMask = kCALayerWidthSizable | kCALayerHeightSizable; - [self.layer addSublayer: _gameboard]; + _gameboard.frame = frame; + [rootLayer addSublayer: _gameboard]; [_gameboard release]; - Class gameClass = NSClassFromString(gameClassName); - NSAssert1(gameClass,@"Unknown game '%@'",gameClassName); - setObj(&_game, [[gameClass alloc] initWithBoard: _gameboard]); + _game = [[gameClass alloc] initWithBoard: _gameboard]; } diff -r 40d225cf9c43 -r d781b00f3ed4 Source/Card.h --- a/Source/Card.h Tue Mar 11 09:21:53 2008 -0700 +++ b/Source/Card.h Tue Mar 11 17:09:50 2008 -0700 @@ -23,11 +23,6 @@ #import "Bit.h" -/** Hardcoded dimensions of a Card */ -#define kCardWidth 100 -#define kCardHeight 150 - - /* A card of some type (playing card, Community Chest, money, ...) Has an identifying serial number (could be in the range 1..52 for playing cards). Can be face-up or down. */ @@ -43,6 +38,9 @@ Abstract; must be overridden. */ + (NSRange) serialNumberRange; ++ (CGSize) cardSize; ++ (void) setCardSize: (CGSize)size; + - (id) initWithSerialNumber: (int)serial position: (CGPoint)pos; @property (readonly) int serialNumber; diff -r 40d225cf9c43 -r d781b00f3ed4 Source/Card.m --- a/Source/Card.m Tue Mar 11 09:21:53 2008 -0700 +++ b/Source/Card.m Tue Mar 11 17:09:50 2008 -0700 @@ -28,6 +28,8 @@ @implementation Card +static CGSize sCardSize = {100,150}; + static CATransform3D kFaceUpTransform, kFaceDownTransform; + (void) initialize @@ -51,12 +53,16 @@ } ++ (CGSize) cardSize {return sCardSize;} ++ (void) setCardSize: (CGSize)size {sCardSize = size;} + + - (id) initWithSerialNumber: (int)serial position: (CGPoint)pos { self = [super init]; if (self != nil) { _serialNumber = serial; - self.bounds = CGRectMake(0,0,kCardWidth,kCardHeight); + self.bounds = CGRectMake(0,0,sCardSize.width,sCardSize.height); self.position = pos; self.edgeAntialiasingMask = 0; _back = [self createBack]; @@ -111,11 +117,11 @@ - (GGBLayer*) createFront { GGBLayer *front = [[GGBLayer alloc] init]; - front.bounds = CGRectMake(0,0,kCardWidth,kCardHeight); - front.position = CGPointMake(kCardWidth/2,kCardHeight/2); + front.bounds = CGRectMake(0,0,sCardSize.width,sCardSize.height); + front.position = CGPointMake(sCardSize.width/2,sCardSize.height/2); front.edgeAntialiasingMask = 0; front.backgroundColor = kWhiteColor; - front.cornerRadius = 8; + front.cornerRadius = 8 * (sCardSize.height/150); front.borderWidth = 1; front.borderColor = CreateGray(0.7, 1.0); front.doubleSided = NO; // this makes the layer invisible when it's flipped @@ -128,19 +134,29 @@ CGSize size = self.bounds.size; GGBLayer *back = [[GGBLayer alloc] init]; back.bounds = CGRectMake(0,0,size.width,size.height); - back.position = CGPointMake(kCardWidth/2,kCardHeight/2); + back.position = CGPointMake(sCardSize.width/2,sCardSize.height/2); +#if TARGET_OS_ASPEN + back.backgroundColor = CreateRGB(0.0,0.5,0.5, 1.0); +#else back.contents = (id) GetCGImageNamed(@"/Library/Desktop Pictures/Classic Aqua Blue.jpg"); +#endif back.contentsGravity = kCAGravityResize; back.masksToBounds = YES; - back.borderWidth = 4; + back.borderWidth = 4 * (sCardSize.height/150); back.borderColor = kWhiteColor; - back.cornerRadius = 8; + back.cornerRadius = 8 * (sCardSize.height/150); back.edgeAntialiasingMask = 0; back.doubleSided = NO; // this makes the layer invisible when it's flipped +#if TARGET_OS_ASPEN + // On iPhone, only Hiragana Kaku includes the coveted snowman glyph... who knows why? + UIFont *font = [UIFont fontWithName: @"HiraKakuProN-W3" size: 1*size.width]; +#else + NSFont *font = [NSFont systemFontOfSize: 1*size.width]; +#endif GGBTextLayer *label = [GGBTextLayer textLayerInSuperlayer: back withText: @"\u2603" // Unicode snowman character - fontSize: 0.9*size.width + font: font alignment: kCALayerWidthSizable|kCALayerHeightSizable]; label.foregroundColor = CreateGray(1.0,0.5); return [back autorelease]; diff -r 40d225cf9c43 -r d781b00f3ed4 Source/Deck.m --- a/Source/Deck.m Tue Mar 11 09:21:53 2008 -0700 +++ b/Source/Deck.m Tue Mar 11 17:09:50 2008 -0700 @@ -40,7 +40,7 @@ { self = [super init]; if (self != nil) { - self.bounds = CGRectMake(0,0,kCardWidth,kCardHeight); + self.bounds = (CGRect){{0,0},[Card cardSize]}; self.cornerRadius = 8; self.backgroundColor = kAlmostInvisibleWhiteColor; self.borderColor = kHighlightColor; diff -r 40d225cf9c43 -r d781b00f3ed4 Source/GGBLayer.h --- a/Source/GGBLayer.h Tue Mar 11 09:21:53 2008 -0700 +++ b/Source/GGBLayer.h Tue Mar 11 17:09:50 2008 -0700 @@ -24,23 +24,8 @@ } @property CGFloat cornerRadius, borderWidth; @property CGColorRef borderColor; -@property unsigned int autoresizingMask; #endif +- (void) redisplayAll; + @end - - -#if TARGET_OS_ASPEN -/* Bit definitions for `autoresizingMask' property. */ - -enum CAAutoresizingMask -{ - kCALayerNotSizable = 0, - kCALayerMinXMargin = 1U << 0, - kCALayerWidthSizable = 1U << 1, - kCALayerMaxXMargin = 1U << 2, - kCALayerMinYMargin = 1U << 3, - kCALayerHeightSizable = 1U << 4, - kCALayerMaxYMargin = 1U << 5 -}; -#endif diff -r 40d225cf9c43 -r d781b00f3ed4 Source/GGBLayer.m --- a/Source/GGBLayer.m Tue Mar 11 09:21:53 2008 -0700 +++ b/Source/GGBLayer.m Tue Mar 11 17:09:50 2008 -0700 @@ -19,6 +19,17 @@ } +- (void) redisplayAll +{ + [self setNeedsDisplay]; + for( CALayer *layer in self.sublayers ) + if( [layer isKindOfClass: [GGBLayer class]] ) + ((GGBLayer*)layer).redisplayAll; + else + [layer setNeedsDisplay]; +} + + #if TARGET_OS_ASPEN #pragma mark - @@ -57,7 +68,6 @@ clone.cornerRadius = self.cornerRadius; clone.borderWidth = self.borderWidth; clone.borderColor = self.borderColor; - clone.autoresizingMask = self.autoresizingMask; for( GGBLayer *sublayer in self.sublayers ) { sublayer = [sublayer copyWithZone: zone]; @@ -67,8 +77,6 @@ } -@synthesize autoresizingMask=_autoresizingMask; - - (CGFloat) cornerRadius {return _cornerRadius;} - (CGFloat) borderWidth {return _borderWidth;} - (CGColorRef) borderColor {return _borderColor;} @@ -115,6 +123,8 @@ - (void)drawInContext:(CGContextRef)ctx { + [super drawInContext: ctx]; + CGContextSaveGState(ctx); if( _realBGColor ) { diff -r 40d225cf9c43 -r d781b00f3ed4 Source/GGBTextLayer.h --- a/Source/GGBTextLayer.h Tue Mar 11 09:21:53 2008 -0700 +++ b/Source/GGBTextLayer.h Tue Mar 11 17:09:50 2008 -0700 @@ -13,13 +13,13 @@ @interface GGBTextLayer : GGBLayer { NSString *_string; - CGFloat _fontSize; + UIFont *_font; CGColorRef _foregroundColor; NSString *_alignmentMode; } @property(copy) id string; -@property CGFloat fontSize; +@property (retain) UIFont *font; @property CGColorRef foregroundColor; @property (copy) NSString *alignmentMode; @@ -31,7 +31,38 @@ withText: (NSString*)text fontSize: (float) fontSize alignment: (enum CAAutoresizingMask) align; ++ (GGBTextLayer*) textLayerInSuperlayer: (CALayer*)superlayer + withText: (NSString*)text + font: (id)inputFont + alignment: (enum CAAutoresizingMask) align; @end +#if TARGET_OS_ASPEN +/* Bit definitions for `autoresizingMask' property. */ + +enum CAAutoresizingMask +{ + kCALayerNotSizable = 0, + kCALayerMinXMargin = 1U << 0, + kCALayerWidthSizable = 1U << 1, + kCALayerMaxXMargin = 1U << 2, + kCALayerMinYMargin = 1U << 3, + kCALayerHeightSizable = 1U << 4, + kCALayerMaxYMargin = 1U << 5 +}; + +enum +{ + kCALayerBottomMargin = kCALayerMaxYMargin, + kCALayerTopMargin = kCALayerMinYMargin +}; + +#else +enum +{ + kCALayerBottomMargin = kCALayerMinYMargin, + kCALayerTopMargin = kCALayerMaxYMargin +}; +#endif diff -r 40d225cf9c43 -r d781b00f3ed4 Source/GGBTextLayer.m --- a/Source/GGBTextLayer.m Tue Mar 11 09:21:53 2008 -0700 +++ b/Source/GGBTextLayer.m Tue Mar 11 17:09:50 2008 -0700 @@ -18,17 +18,36 @@ fontSize: (float) fontSize alignment: (enum CAAutoresizingMask) align { +#if TARGET_OS_ASPEN + UIFont *font = [UIFont systemFontOfSize: fontSize]; +#else + NSFont *font = [NSFont systemFontOfSize: fontSize]; +#endif + return [self textLayerInSuperlayer: superlayer + withText: text + font: font + alignment: align]; +} + + ++ (GGBTextLayer*) textLayerInSuperlayer: (CALayer*)superlayer + withText: (NSString*)text + font: (id)inputFont + alignment: (enum CAAutoresizingMask) align +{ GGBTextLayer *label = [[self alloc] init]; label.string = text; #if TARGET_OS_ASPEN - UIFont *font = [UIFont systemFontOfSize: fontSize]; + UIFont *font = inputFont; + [label setNeedsDisplay]; + label.needsDisplayOnBoundsChange = YES; #else - NSFont *font = [NSFont systemFontOfSize: fontSize]; - label.font = font; + NSFont *font = inputFont; + label.fontSize = font.pointSize; #endif - label.fontSize = fontSize; + label.font = font; label.foregroundColor = kBlackColor; NSString *mode; @@ -45,29 +64,116 @@ if( [superlayer respondsToSelector: @selector(borderWidth)] ) inset += ((GGBLayer*)superlayer).borderWidth; CGRect bounds = CGRectInset(superlayer.bounds, inset, inset); + if( mode==@"center" ) + bounds = CGRectInset(bounds,-inset,0); CGFloat height = font.ascender; + float descender = font.descender; +#if TARGET_OS_ASPEN + descender = -descender; +#endif CGFloat y = bounds.origin.y; - if( align & kCALayerHeightSizable ) + if( align & kCALayerHeightSizable ) { y += (bounds.size.height-height)/2.0; - else if( align & kCALayerMinYMargin ) +#if TARGET_OS_ASPEN + y -= descender/2.0; +#endif + } else if( align & kCALayerMinYMargin ) y += bounds.size.height - height; align &= ~kCALayerHeightSizable; - label.bounds = CGRectMake(0, font.descender, - bounds.size.width, height - font.descender); - label.position = CGPointMake(bounds.origin.x,y+font.descender); + label.bounds = CGRectMake(0, descender, + bounds.size.width, height - descender); + label.position = CGPointMake(bounds.origin.x,y+descender); label.anchorPoint = CGPointMake(0,0); +#if ! TARGET_OS_ASPEN label.autoresizingMask = align; +#endif [superlayer addSublayer: label]; [label release]; + + //label.borderWidth = 1; + //label.borderColor = kBlackColor; + return label; } #if TARGET_OS_ASPEN -@synthesize string=_string, fontSize=_fontSize, +@synthesize string=_string, font=_font, foregroundColor=_foregroundColor, alignmentMode=_alignmentMode; + + +- (id) copyWithZone: (NSZone*)zone +{ + GGBTextLayer *clone = [super copyWithZone: zone]; + clone.string = _string; + clone.font = _font; + clone.foregroundColor = _foregroundColor; + clone.alignmentMode = _alignmentMode; + return clone; +} + + +- (void)drawInContext:(CGContextRef)ctx +{ + [super drawInContext: ctx]; + + if( _string.length > 0 ) { + CGContextSaveGState(ctx); + UIGraphicsPushContext(ctx); + + if( _foregroundColor ) + CGContextSetFillColorWithColor(ctx, _foregroundColor); + + UITextAlignment align; + if( [_alignmentMode isEqualToString: @"center"] ) + align = UITextAlignmentCenter; + else if( [_alignmentMode isEqualToString: @"right"] ) + align = UITextAlignmentRight; + else + align = UITextAlignmentLeft; + + CGRect bounds = self.bounds; + bounds.origin.y += _font.ascender+_font.descender - _font.leading; + [_string drawInRect: bounds + withFont: _font + lineBreakMode: UILineBreakModeClip + alignment: align]; + + UIGraphicsPopContext(); + CGContextRestoreGState(ctx); + } +} + + #endif @end + + +/* + .times lt mm: (TimesLTMM) + times new roman: (TimesNewRomanBoldItalic, TimesNewRomanItalic, TimesNewRoman, TimesNewRomanBold) + phonepadtwo: (PhonepadTwo) + hiragino kaku gothic pron w3: (HiraKakuProN-W3) + helvetica neue: (HelveticaNeueBold, HelveticaNeue) + trebuchet ms: (TrebuchetMSItalic, TrebuchetMSBoldItalic, TrebuchetMSBold, TrebuchetMS) + courier new: (CourierNewBoldItalic, CourierNewBold, CourierNewItalic, CourierNew) + arial unicode ms: (arialuni) + georgia: (Georgia, GeorgiaBold, GeorgiaBoldItalic, GeorgiaItalic) + zapfino: (Zapfino) + arial rounded mt bold: (ArialRoundedMTBold) + db lcd temp: (DB_LCD_Temp-Black) + verdana: (Verdana, VerdanaItalic, VerdanaBoldItalic, VerdanaBold) + american typewriter: (AmericanTypewriterCondensedBold, AmericanTypewriter) + helvetica: (HelveticaBoldOblique, Helvetica, HelveticaOblique, HelveticaBold) + lock clock: (LockClock) + courier: (CourierBoldOblique, CourierOblique) + hiragino kaku gothic pron w6: (HiraKakuProN-W6) + arial: (ArialItalic, ArialBold, Arial, ArialBoldItalic) + .helvetica lt mm: (HelveticaLTMM) + stheiti: (STHeiti, STXihei) + applegothic: (AppleGothicRegular) + marker felt: (MarkerFeltThin) +*/ \ No newline at end of file diff -r 40d225cf9c43 -r d781b00f3ed4 Source/Game.h --- a/Source/Game.h Tue Mar 11 09:21:53 2008 -0700 +++ b/Source/Game.h Tue Mar 11 17:09:50 2008 -0700 @@ -36,6 +36,8 @@ (By default it just returns the class name with the "Game" suffix removed.) */ + (NSString*) displayName; ++ (BOOL) landscapeOriented; + @property (readonly, copy) NSArray *players; @property (readonly) Player *currentPlayer, *winner; diff -r 40d225cf9c43 -r d781b00f3ed4 Source/Game.m --- a/Source/Game.m Tue Mar 11 09:21:53 2008 -0700 +++ b/Source/Game.m Tue Mar 11 17:09:50 2008 -0700 @@ -110,6 +110,12 @@ #pragma mark GAMEPLAY METHODS TO BE OVERRIDDEN: ++ (BOOL) landscapeOriented +{ + return NO; +} + + - (BOOL) canBit: (Bit*)bit moveFrom: (id)src { return YES; diff -r 40d225cf9c43 -r d781b00f3ed4 Source/KlondikeGame.m --- a/Source/KlondikeGame.m Tue Mar 11 09:21:53 2008 -0700 +++ b/Source/KlondikeGame.m Tue Mar 11 17:09:50 2008 -0700 @@ -24,21 +24,19 @@ #import "Deck.h" #import "PlayingCard.h" #import "Stack.h" +#import "QuartzUtils.h" #define kStackHeight 500 -/** WARNING: THIS CODE REQUIRES GARBAGE COLLECTION! - ** This sample application uses Objective-C 2.0 garbage collection. - ** Therefore, the source code in this file does NOT perform manual object memory management. - ** If you reuse any of this code in a process that isn't garbage collected, you will need to - ** add all necessary retain/release/autorelease calls, and implement -dealloc methods, - ** otherwise unpleasant leakage will occur! - **/ +@implementation KlondikeGame -@implementation KlondikeGame ++ (BOOL) landscapeOriented +{ + return YES; +} - (id) initWithBoard: (GGBLayer*)board @@ -47,29 +45,43 @@ if (self != nil) { [self setNumberOfPlayers: 1]; + CGSize boardSize = board.bounds.size; + CGFloat xSpacing = floor(boardSize.width/7); + CGSize kCardSize; + kCardSize.width = round(xSpacing * 0.9); // 1/7th of width, with 10% gap + kCardSize.height = round(kCardSize.width * 1.5); + CGFloat gap = xSpacing-kCardSize.width; + [Card setCardSize: kCardSize]; + + CGPoint pos = {floor(gap/2)+kCardSize.width/2, floor(boardSize.height-kCardSize.height/2)}; _deck = [[Deck alloc] initWithCardsOfClass: [PlayingCard class]]; [_deck shuffle]; - _deck.position = CGPointMake(kCardWidth/2+16,kCardHeight/2+16); + _deck.position = pos; [board addSublayer: _deck]; + pos.x += xSpacing; _sink = [[Deck alloc] init]; - _sink.position = CGPointMake(3*kCardWidth/2+32,kCardHeight/2+16); + _sink.position = pos; [board addSublayer: _sink]; + pos.x += xSpacing; for( CardSuit suit=kSuitClubs; suit<=kSuitSpades; suit++ ) { + pos.x += xSpacing; Deck *aces = [[Deck alloc] init]; - aces.position = CGPointMake(kCardWidth/2+16+(kCardWidth+16)*(suit%2), - 120+kCardHeight+(kCardHeight+16)*(suit/2)); + aces.position = pos; [board addSublayer: aces]; _aces[suit] = aces; } + CGRect stackFrame = {{floor(gap/2), gap}, + {kCardSize.width, boardSize.height-kCardSize.height-2*gap}}; + CGPoint startPos = CGPointMake(kCardSize.width/2,kCardSize.height/2); + CGSize spacing = {0, floor((stackFrame.size.height-kCardSize.height)/11.0)}; for( int s=0; s<7; s++ ) { - Stack *stack = [[Stack alloc] initWithStartPos: CGPointMake(kCardWidth/2, - kStackHeight-kCardHeight/2.0) - spacing: CGSizeMake(0,-22)]; - stack.frame = CGRectMake(260+s*(kCardWidth+16),16, kCardWidth,kStackHeight); - stack.backgroundColor = nil; + Stack *stack = [[Stack alloc] initWithStartPos: startPos spacing: spacing]; + stack.frame = stackFrame; + stackFrame.origin.x += xSpacing; + stack.backgroundColor = nil; //kAlmostInvisibleWhiteColor; stack.dragAsStacks = YES; [board addSublayer: stack]; diff -r 40d225cf9c43 -r d781b00f3ed4 Source/PlayingCard.m --- a/Source/PlayingCard.m Tue Mar 11 09:21:53 2008 -0700 +++ b/Source/PlayingCard.m Tue Mar 11 17:09:50 2008 -0700 @@ -41,25 +41,37 @@ self.rankString, self.suitString]; CGColorRef suitColor = self.suitColor; + float scale = [Card cardSize].height/150; + float cornerFontSize = MAX(18*scale, 14); + float centerFontSize = 80*scale; + +#if TARGET_OS_ASPEN + UIFont *cornerFont = [UIFont boldSystemFontOfSize: cornerFontSize]; +#else + NSFont *cornerFont = [NSFont boldSystemFontOfSize: cornerFontSize]; +#endif GGBTextLayer *label; label = [GGBTextLayer textLayerInSuperlayer: front withText: name - fontSize: 18.0 - alignment: kCALayerMaxXMargin | kCALayerMinYMargin]; + font: cornerFont + alignment: kCALayerMaxXMargin | kCALayerBottomMargin]; label.foregroundColor = suitColor; label = [GGBTextLayer textLayerInSuperlayer: front withText: name - fontSize: 18.0 - alignment: kCALayerMaxXMargin | kCALayerMaxYMargin]; + font: cornerFont + alignment: kCALayerMaxXMargin | kCALayerTopMargin]; label.foregroundColor = suitColor; label.anchorPoint = CGPointMake(1,1); [label setValue: [NSNumber numberWithFloat: M_PI] forKeyPath: @"transform.rotation"]; label = [GGBTextLayer textLayerInSuperlayer: front withText: self.faceSymbol - fontSize: 80 + fontSize: centerFontSize alignment: kCALayerWidthSizable | kCALayerHeightSizable]; label.foregroundColor = suitColor; + //label.borderWidth = 1; + //label.borderColor = kBlackColor; + return front; } diff -r 40d225cf9c43 -r d781b00f3ed4 Source/iPhoneAppDelegate.m --- a/Source/iPhoneAppDelegate.m Tue Mar 11 09:21:53 2008 -0700 +++ b/Source/iPhoneAppDelegate.m Tue Mar 11 17:09:50 2008 -0700 @@ -23,6 +23,9 @@ - (void)applicationDidFinishLaunching:(UIApplication *)application { + for( NSString *family in [UIFont familyNames] ) + NSLog(@"%@: (%@)", family, [[UIFont fontNamesForFamilyName: family] componentsJoinedByString: @", "]); + // Create window self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease]; _window.layer.backgroundColor = GetCGPatternNamed(@"Background.png"); @@ -45,7 +48,7 @@ [_window addSubview: _headline]; // Start game: - [self startGameNamed: @"GoGame"]; + [self startGameNamed: @"KlondikeGame"]; // Show window [_window makeKeyAndVisible];