Signed-off-by: Matt Bruce <mbrucedogs@gmail.com>
This commit is contained in:
parent
78f7cb1544
commit
cac0af4ab3
@ -34,22 +34,9 @@
|
|||||||
EAD890CE2EF1E9CF006DBA80 /* BaccaratUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = BaccaratUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
EAD890CE2EF1E9CF006DBA80 /* BaccaratUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = BaccaratUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */
|
|
||||||
EAD891062EF1F51E006DBA80 /* Exceptions for "Baccarat" folder in "Baccarat" target */ = {
|
|
||||||
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
|
|
||||||
membershipExceptions = (
|
|
||||||
Agents.md,
|
|
||||||
);
|
|
||||||
target = EAD890B62EF1E9CE006DBA80 /* Baccarat */;
|
|
||||||
};
|
|
||||||
/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */
|
|
||||||
|
|
||||||
/* Begin PBXFileSystemSynchronizedRootGroup section */
|
/* Begin PBXFileSystemSynchronizedRootGroup section */
|
||||||
EAD890B92EF1E9CE006DBA80 /* Baccarat */ = {
|
EAD890B92EF1E9CE006DBA80 /* Baccarat */ = {
|
||||||
isa = PBXFileSystemSynchronizedRootGroup;
|
isa = PBXFileSystemSynchronizedRootGroup;
|
||||||
exceptions = (
|
|
||||||
EAD891062EF1F51E006DBA80 /* Exceptions for "Baccarat" folder in "Baccarat" target */,
|
|
||||||
);
|
|
||||||
path = Baccarat;
|
path = Baccarat;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
|||||||
@ -29,15 +29,50 @@ enum Design {
|
|||||||
// MARK: - Baccarat-Specific Component Sizes
|
// MARK: - Baccarat-Specific Component Sizes
|
||||||
|
|
||||||
enum Size {
|
enum Size {
|
||||||
// Cards - use CasinoDesign values
|
// MARK: - Hand Scaling
|
||||||
|
|
||||||
|
/// Hand scaling factor for cards and related elements.
|
||||||
|
/// 1.0 = original size, 1.5 = 50% larger, 2.0 = double size.
|
||||||
|
/// Adjust this value to change card sizes across the app.
|
||||||
|
static let handScale: CGFloat = 1.5
|
||||||
|
|
||||||
|
/// Scale multiplier for small screens (iPhone SE, etc).
|
||||||
|
/// Applied instead of handScale on screens narrower than smallScreenThreshold.
|
||||||
|
static let smallScreenScale: CGFloat = 1.2
|
||||||
|
|
||||||
|
/// Screen width threshold for small screen detection (iPhone SE is 375pt)
|
||||||
|
static let smallScreenThreshold: CGFloat = 390
|
||||||
|
|
||||||
|
/// Additional scale multiplier for large screens (iPad).
|
||||||
|
/// Applied on top of handScale when on regular size class.
|
||||||
|
static let largeScreenMultiplier: CGFloat = 1.2
|
||||||
|
|
||||||
|
// Cards - base values from CasinoDesign
|
||||||
static let cardWidthSmall: CGFloat = CasinoDesign.Size.cardWidthSmall
|
static let cardWidthSmall: CGFloat = CasinoDesign.Size.cardWidthSmall
|
||||||
static let cardWidthMedium: CGFloat = CasinoDesign.Size.cardWidthMedium
|
static let cardWidthMedium: CGFloat = CasinoDesign.Size.cardWidthMedium
|
||||||
static let cardWidthLarge: CGFloat = CasinoDesign.Size.cardWidthLarge
|
static let cardWidthLarge: CGFloat = CasinoDesign.Size.cardWidthLarge
|
||||||
static let cardAspectRatio: CGFloat = CasinoDesign.Size.cardAspectRatio
|
static let cardAspectRatio: CGFloat = CasinoDesign.Size.cardAspectRatio
|
||||||
static let cardOverlap: CGFloat = CasinoDesign.Size.cardOverlap
|
|
||||||
|
|
||||||
// Baccarat table cards (smaller for compact layout)
|
/// Base card width before scaling (for reference)
|
||||||
static let cardWidthTable: CGFloat = 45
|
private static let cardWidthTableBase: CGFloat = 45
|
||||||
|
|
||||||
|
/// Card overlap scaled with hand size (standard iPhone)
|
||||||
|
static let cardOverlap: CGFloat = CasinoDesign.Size.cardOverlap * handScale
|
||||||
|
|
||||||
|
/// Card overlap for small screens
|
||||||
|
static let cardOverlapSmall: CGFloat = CasinoDesign.Size.cardOverlap * smallScreenScale
|
||||||
|
|
||||||
|
// Baccarat table cards - scaled for better visibility (standard iPhone)
|
||||||
|
static let cardWidthTable: CGFloat = cardWidthTableBase * handScale
|
||||||
|
|
||||||
|
/// Card width for small screens (iPhone SE, etc)
|
||||||
|
static let cardWidthTableSmall: CGFloat = cardWidthTableBase * smallScreenScale
|
||||||
|
|
||||||
|
/// Card width for large screens (iPad) - applies additional multiplier
|
||||||
|
static let cardWidthTableLarge: CGFloat = cardWidthTableBase * handScale * largeScreenMultiplier
|
||||||
|
|
||||||
|
/// Card overlap for large screens
|
||||||
|
static let cardOverlapLarge: CGFloat = CasinoDesign.Size.cardOverlap * handScale * largeScreenMultiplier
|
||||||
|
|
||||||
// Chips - use CasinoDesign values
|
// Chips - use CasinoDesign values
|
||||||
static let chipSmall: CGFloat = CasinoDesign.Size.chipSmall
|
static let chipSmall: CGFloat = CasinoDesign.Size.chipSmall
|
||||||
|
|||||||
@ -52,6 +52,7 @@ struct GameTableView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
|
GeometryReader { geometry in
|
||||||
ZStack {
|
ZStack {
|
||||||
// Table background
|
// Table background
|
||||||
TableBackgroundView()
|
TableBackgroundView()
|
||||||
@ -81,7 +82,8 @@ struct GameTableView: View {
|
|||||||
bankerValue: state.bankerHandValue,
|
bankerValue: state.bankerHandValue,
|
||||||
playerIsWinner: playerIsWinner,
|
playerIsWinner: playerIsWinner,
|
||||||
bankerIsWinner: bankerIsWinner,
|
bankerIsWinner: bankerIsWinner,
|
||||||
isTie: isTie
|
isTie: isTie,
|
||||||
|
screenWidth: geometry.size.width
|
||||||
)
|
)
|
||||||
.frame(maxWidth: isLargeScreen ? maxContentWidth : .infinity)
|
.frame(maxWidth: isLargeScreen ? maxContentWidth : .infinity)
|
||||||
|
|
||||||
@ -167,6 +169,7 @@ struct GameTableView: View {
|
|||||||
.transition(.opacity)
|
.transition(.opacity)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
.onAppear {
|
.onAppear {
|
||||||
if gameState == nil {
|
if gameState == nil {
|
||||||
gameState = GameState(settings: settings)
|
gameState = GameState(settings: settings)
|
||||||
@ -347,11 +350,27 @@ struct CardsDisplayArea: View {
|
|||||||
let playerIsWinner: Bool
|
let playerIsWinner: Bool
|
||||||
let bankerIsWinner: Bool
|
let bankerIsWinner: Bool
|
||||||
let isTie: Bool
|
let isTie: Bool
|
||||||
|
/// Screen width for responsive card sizing
|
||||||
|
var screenWidth: CGFloat = 400
|
||||||
|
|
||||||
// MARK: - Fixed font sizes for card area
|
// MARK: - Environment
|
||||||
// Fixed because the card display has strict layout constraints
|
|
||||||
|
|
||||||
private let labelFontSize: CGFloat = 14
|
@Environment(\.horizontalSizeClass) private var horizontalSizeClass
|
||||||
|
|
||||||
|
// MARK: - Scaled font sizes for card area
|
||||||
|
// Scales with hand size for proportional appearance
|
||||||
|
|
||||||
|
/// Whether we're on a large screen (iPad)
|
||||||
|
private var isLargeScreen: Bool {
|
||||||
|
horizontalSizeClass == .regular
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Label font size - only scales on iPad to avoid clipping on small iPhones
|
||||||
|
private var labelFontSize: CGFloat {
|
||||||
|
let baseSize: CGFloat = 14
|
||||||
|
// Only apply scaling on large screens; keep original size on iPhone
|
||||||
|
return isLargeScreen ? baseSize * Design.Size.handScale * Design.Size.largeScreenMultiplier : baseSize
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - Accessibility
|
// MARK: - Accessibility
|
||||||
|
|
||||||
@ -387,8 +406,25 @@ struct CardsDisplayArea: View {
|
|||||||
return visibleCards.joined(separator: ", ") + ". " + String(format: format, bankerValue)
|
return visibleCards.joined(separator: ", ") + ". " + String(format: format, bankerValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Minimum height for label row - only scales on iPad
|
||||||
|
private var labelRowMinHeight: CGFloat {
|
||||||
|
let baseHeight: CGFloat = 30
|
||||||
|
// Only apply scaling on large screens; keep original size on iPhone
|
||||||
|
return isLargeScreen ? baseHeight * Design.Size.handScale * Design.Size.largeScreenMultiplier : baseHeight
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Spacing between PLAYER and BANKER hands - reduced on smaller screens
|
||||||
|
private var handsSpacing: CGFloat {
|
||||||
|
isLargeScreen ? Design.Spacing.xxxLarge : Design.Spacing.medium
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Horizontal padding inside the container - reduced on smaller screens
|
||||||
|
private var containerPaddingH: CGFloat {
|
||||||
|
isLargeScreen ? Design.Spacing.xLarge : Design.Spacing.small
|
||||||
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
HStack(spacing: Design.Spacing.xxxLarge) {
|
HStack(spacing: handsSpacing) {
|
||||||
// Player side
|
// Player side
|
||||||
VStack(spacing: Design.Spacing.small) {
|
VStack(spacing: Design.Spacing.small) {
|
||||||
// Label with value
|
// Label with value
|
||||||
@ -401,13 +437,14 @@ struct CardsDisplayArea: View {
|
|||||||
ValueBadge(value: playerValue, color: .blue)
|
ValueBadge(value: playerValue, color: .blue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.frame(minHeight: 30)
|
.frame(minHeight: labelRowMinHeight)
|
||||||
|
|
||||||
// Cards
|
// Cards
|
||||||
CompactHandView(
|
CompactHandView(
|
||||||
cards: playerCards,
|
cards: playerCards,
|
||||||
cardsFaceUp: playerCardsFaceUp,
|
cardsFaceUp: playerCardsFaceUp,
|
||||||
isWinner: playerIsWinner
|
isWinner: playerIsWinner,
|
||||||
|
screenWidth: screenWidth
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
.accessibilityElement(children: .ignore)
|
.accessibilityElement(children: .ignore)
|
||||||
@ -426,28 +463,29 @@ struct CardsDisplayArea: View {
|
|||||||
ValueBadge(value: bankerValue, color: .red)
|
ValueBadge(value: bankerValue, color: .red)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.frame(minHeight: 30)
|
.frame(minHeight: labelRowMinHeight)
|
||||||
|
|
||||||
// Cards
|
// Cards
|
||||||
CompactHandView(
|
CompactHandView(
|
||||||
cards: bankerCards,
|
cards: bankerCards,
|
||||||
cardsFaceUp: bankerCardsFaceUp,
|
cardsFaceUp: bankerCardsFaceUp,
|
||||||
isWinner: bankerIsWinner
|
isWinner: bankerIsWinner,
|
||||||
|
screenWidth: screenWidth
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
.accessibilityElement(children: .ignore)
|
.accessibilityElement(children: .ignore)
|
||||||
.accessibilityLabel(String(localized: "Banker hand"))
|
.accessibilityLabel(String(localized: "Banker hand"))
|
||||||
.accessibilityValue(bankerHandDescription + (bankerIsWinner ? ", " + String(localized: "Winner") : ""))
|
.accessibilityValue(bankerHandDescription + (bankerIsWinner ? ", " + String(localized: "Winner") : ""))
|
||||||
}
|
}
|
||||||
.padding(.top, Design.Spacing.large)
|
.padding(.top, Design.Spacing.medium)
|
||||||
.padding(.bottom, Design.Spacing.xLarge)
|
.padding(.bottom, Design.Spacing.large)
|
||||||
.padding(.horizontal, Design.Spacing.xLarge)
|
.padding(.horizontal, containerPaddingH)
|
||||||
.background(
|
.background(
|
||||||
RoundedRectangle(cornerRadius: Design.CornerRadius.xLarge)
|
RoundedRectangle(cornerRadius: Design.CornerRadius.xLarge)
|
||||||
.fill(Color.black.opacity(Design.Opacity.quarter))
|
.fill(Color.black.opacity(Design.Opacity.quarter))
|
||||||
.accessibilityHidden(true)
|
.accessibilityHidden(true)
|
||||||
)
|
)
|
||||||
.padding(.horizontal)
|
.padding(.horizontal, Design.Spacing.small)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -456,17 +494,59 @@ struct CompactHandView: View {
|
|||||||
let cards: [Card]
|
let cards: [Card]
|
||||||
let cardsFaceUp: [Bool]
|
let cardsFaceUp: [Bool]
|
||||||
let isWinner: Bool
|
let isWinner: Bool
|
||||||
|
/// Screen width passed from parent for responsive sizing
|
||||||
|
var screenWidth: CGFloat = 400
|
||||||
|
|
||||||
// MARK: - Scaled Font Sizes (Dynamic Type)
|
// MARK: - Environment
|
||||||
|
|
||||||
@ScaledMetric(relativeTo: .caption) private var winBadgeFontSize: CGFloat = 10
|
@Environment(\.horizontalSizeClass) private var horizontalSizeClass
|
||||||
|
|
||||||
// MARK: - Layout Constants
|
// MARK: - Layout Constants
|
||||||
// Fixed size: cards have strict visual constraints
|
// Responsive sizing based on device
|
||||||
|
|
||||||
|
/// Whether we're on a large screen (iPad)
|
||||||
|
private var isLargeScreen: Bool {
|
||||||
|
horizontalSizeClass == .regular
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Whether we're on a small screen (iPhone SE, etc)
|
||||||
|
private var isSmallScreen: Bool {
|
||||||
|
!isLargeScreen && screenWidth < Design.Size.smallScreenThreshold
|
||||||
|
}
|
||||||
|
|
||||||
|
/// WIN badge font size - only scales on iPad
|
||||||
|
private var winBadgeFontSize: CGFloat {
|
||||||
|
let baseSize: CGFloat = 10
|
||||||
|
return isLargeScreen ? baseSize * Design.Size.handScale * Design.Size.largeScreenMultiplier : baseSize
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Card width - responsive based on screen size
|
||||||
|
private var cardWidth: CGFloat {
|
||||||
|
if isLargeScreen {
|
||||||
|
return Design.Size.cardWidthTableLarge
|
||||||
|
} else if isSmallScreen {
|
||||||
|
return Design.Size.cardWidthTableSmall
|
||||||
|
} else {
|
||||||
|
return Design.Size.cardWidthTable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Card height based on aspect ratio
|
||||||
|
private var cardHeight: CGFloat {
|
||||||
|
cardWidth * Design.Size.cardAspectRatio
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Card overlap - scaled with card size
|
||||||
|
private var cardOverlap: CGFloat {
|
||||||
|
if isLargeScreen {
|
||||||
|
return Design.Size.cardOverlapLarge
|
||||||
|
} else if isSmallScreen {
|
||||||
|
return Design.Size.cardOverlapSmall
|
||||||
|
} else {
|
||||||
|
return Design.Size.cardOverlap
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private let cardWidth: CGFloat = Design.Size.cardWidthTable
|
|
||||||
private let cardHeight: CGFloat = Design.Size.cardWidthTable * Design.Size.cardAspectRatio
|
|
||||||
private let cardOverlap: CGFloat = Design.Size.cardOverlap
|
|
||||||
private let placeholderSpacing: CGFloat = Design.Spacing.small
|
private let placeholderSpacing: CGFloat = Design.Spacing.small
|
||||||
|
|
||||||
/// Fixed container width to prevent resizing during deal
|
/// Fixed container width to prevent resizing during deal
|
||||||
@ -538,10 +618,27 @@ struct ValueBadge: View {
|
|||||||
let value: Int
|
let value: Int
|
||||||
let color: Color
|
let color: Color
|
||||||
|
|
||||||
|
// MARK: - Environment
|
||||||
|
|
||||||
|
@Environment(\.horizontalSizeClass) private var horizontalSizeClass
|
||||||
|
|
||||||
// MARK: - Scaled Font Sizes (Dynamic Type)
|
// MARK: - Scaled Font Sizes (Dynamic Type)
|
||||||
|
|
||||||
@ScaledMetric(relativeTo: .headline) private var valueFontSize: CGFloat = 15
|
/// Whether we're on a large screen (iPad)
|
||||||
@ScaledMetric(relativeTo: .headline) private var badgeSize: CGFloat = 26
|
private var isLargeScreen: Bool {
|
||||||
|
horizontalSizeClass == .regular
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Scale factor for badge sizing - only applies on iPad to avoid clipping on iPhone
|
||||||
|
private var scale: CGFloat {
|
||||||
|
isLargeScreen ? Design.Size.handScale * Design.Size.largeScreenMultiplier : 1.0
|
||||||
|
}
|
||||||
|
|
||||||
|
@ScaledMetric(relativeTo: .headline) private var baseValueFontSize: CGFloat = 15
|
||||||
|
@ScaledMetric(relativeTo: .headline) private var baseBadgeSize: CGFloat = 26
|
||||||
|
|
||||||
|
private var valueFontSize: CGFloat { baseValueFontSize * scale }
|
||||||
|
private var badgeSize: CGFloat { baseBadgeSize * scale }
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Text("\(value)")
|
Text("\(value)")
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user