Signed-off-by: Matt Bruce <mbrucedogs@gmail.com>
This commit is contained in:
parent
21fc2d6f5b
commit
a2ecfcd580
@ -14,7 +14,7 @@ import CasinoKit
|
|||||||
enum Design {
|
enum Design {
|
||||||
|
|
||||||
/// Set to true to show layout debug borders on views
|
/// Set to true to show layout debug borders on views
|
||||||
static let showDebugBorders = true
|
static let showDebugBorders = false
|
||||||
|
|
||||||
// MARK: - Shared Constants (from CasinoKit)
|
// MARK: - Shared Constants (from CasinoKit)
|
||||||
|
|
||||||
|
|||||||
@ -20,6 +20,10 @@ struct CardsDisplayArea: View {
|
|||||||
let bankerIsWinner: Bool
|
let bankerIsWinner: Bool
|
||||||
let isTie: Bool
|
let isTie: Bool
|
||||||
|
|
||||||
|
// MARK: - State
|
||||||
|
|
||||||
|
@State private var containerWidth: CGFloat = 300
|
||||||
|
|
||||||
// MARK: - Environment
|
// MARK: - Environment
|
||||||
|
|
||||||
@Environment(\.horizontalSizeClass) private var horizontalSizeClass
|
@Environment(\.horizontalSizeClass) private var horizontalSizeClass
|
||||||
@ -83,21 +87,37 @@ struct CardsDisplayArea: View {
|
|||||||
return visibleCards.joined(separator: ", ") + ". " + String(format: format, bankerValue)
|
return visibleCards.joined(separator: ", ") + ". " + String(format: format, bankerValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Calculate hand section width from total container width
|
||||||
|
private var handSectionWidth: CGFloat {
|
||||||
|
(containerWidth - handsSpacing) / 2
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - Body
|
// MARK: - Body
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
HStack(spacing: handsSpacing) {
|
HStack(spacing: handsSpacing) {
|
||||||
// Player side
|
// Player side
|
||||||
playerHandSection
|
playerHandSection(width: handSectionWidth)
|
||||||
.debugBorder(showDebugBorders, color: .blue, label: "Player")
|
.debugBorder(showDebugBorders, color: .blue, label: "Player")
|
||||||
|
|
||||||
// Banker side
|
// Banker side
|
||||||
bankerHandSection
|
bankerHandSection(width: handSectionWidth)
|
||||||
.debugBorder(showDebugBorders, color: .red, label: "Banker")
|
.debugBorder(showDebugBorders, color: .red, label: "Banker")
|
||||||
}
|
}
|
||||||
.frame(maxWidth: .infinity)
|
.frame(maxWidth: .infinity)
|
||||||
.padding(.top, Design.Spacing.medium)
|
.padding(.top, Design.Spacing.medium)
|
||||||
.padding(.bottom, Design.Spacing.large)
|
.padding(.bottom, Design.Spacing.large)
|
||||||
|
.background(
|
||||||
|
GeometryReader { geometry in
|
||||||
|
Color.clear
|
||||||
|
.onAppear {
|
||||||
|
containerWidth = geometry.size.width
|
||||||
|
}
|
||||||
|
.onChange(of: geometry.size.width) { _, newWidth in
|
||||||
|
containerWidth = newWidth
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
.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))
|
||||||
@ -108,7 +128,7 @@ struct CardsDisplayArea: View {
|
|||||||
|
|
||||||
// MARK: - Private Views
|
// MARK: - Private Views
|
||||||
|
|
||||||
private var playerHandSection: some View {
|
private func playerHandSection(width: CGFloat) -> some View {
|
||||||
VStack(spacing: Design.Spacing.small) {
|
VStack(spacing: Design.Spacing.small) {
|
||||||
// Label with value
|
// Label with value
|
||||||
HStack(spacing: Design.Spacing.small) {
|
HStack(spacing: Design.Spacing.small) {
|
||||||
@ -126,16 +146,17 @@ struct CardsDisplayArea: View {
|
|||||||
CompactHandView(
|
CompactHandView(
|
||||||
cards: playerCards,
|
cards: playerCards,
|
||||||
cardsFaceUp: playerCardsFaceUp,
|
cardsFaceUp: playerCardsFaceUp,
|
||||||
isWinner: playerIsWinner
|
isWinner: playerIsWinner,
|
||||||
|
containerWidth: width
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
.frame(maxWidth: .infinity)
|
.frame(width: width)
|
||||||
.accessibilityElement(children: .ignore)
|
.accessibilityElement(children: .ignore)
|
||||||
.accessibilityLabel(String(localized: "Player hand"))
|
.accessibilityLabel(String(localized: "Player hand"))
|
||||||
.accessibilityValue(playerHandDescription + (playerIsWinner ? ", " + String(localized: "Winner") : ""))
|
.accessibilityValue(playerHandDescription + (playerIsWinner ? ", " + String(localized: "Winner") : ""))
|
||||||
}
|
}
|
||||||
|
|
||||||
private var bankerHandSection: some View {
|
private func bankerHandSection(width: CGFloat) -> some View {
|
||||||
VStack(spacing: Design.Spacing.small) {
|
VStack(spacing: Design.Spacing.small) {
|
||||||
// Label with value
|
// Label with value
|
||||||
HStack(spacing: Design.Spacing.small) {
|
HStack(spacing: Design.Spacing.small) {
|
||||||
@ -153,10 +174,11 @@ struct CardsDisplayArea: View {
|
|||||||
CompactHandView(
|
CompactHandView(
|
||||||
cards: bankerCards,
|
cards: bankerCards,
|
||||||
cardsFaceUp: bankerCardsFaceUp,
|
cardsFaceUp: bankerCardsFaceUp,
|
||||||
isWinner: bankerIsWinner
|
isWinner: bankerIsWinner,
|
||||||
|
containerWidth: width
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
.frame(maxWidth: .infinity)
|
.frame(width: width)
|
||||||
.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") : ""))
|
||||||
|
|||||||
@ -13,6 +13,8 @@ struct CompactHandView: View {
|
|||||||
let cards: [Card]
|
let cards: [Card]
|
||||||
let cardsFaceUp: [Bool]
|
let cardsFaceUp: [Bool]
|
||||||
let isWinner: Bool
|
let isWinner: Bool
|
||||||
|
/// Container width passed from parent for sizing
|
||||||
|
let containerWidth: CGFloat
|
||||||
|
|
||||||
// MARK: - Environment
|
// MARK: - Environment
|
||||||
|
|
||||||
@ -26,9 +28,6 @@ struct CompactHandView: View {
|
|||||||
/// Maximum number of cards in baccarat hand
|
/// Maximum number of cards in baccarat hand
|
||||||
private let maxCards: Int = 3
|
private let maxCards: Int = 3
|
||||||
|
|
||||||
/// Padding around cards
|
|
||||||
private let containerPadding: CGFloat = Design.Spacing.xSmall
|
|
||||||
|
|
||||||
/// Placeholder spacing when no cards
|
/// Placeholder spacing when no cards
|
||||||
private let placeholderSpacing: CGFloat = Design.Spacing.small
|
private let placeholderSpacing: CGFloat = Design.Spacing.small
|
||||||
|
|
||||||
@ -44,60 +43,38 @@ struct CompactHandView: View {
|
|||||||
isLargeScreen ? 14 : 10
|
isLargeScreen ? 14 : 10
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calculate card width from available container width
|
/// Card width calculated from container width
|
||||||
/// Formula: containerWidth = cardWidth + (cardWidth + overlap) * 2 + padding
|
/// Formula: containerWidth = cardWidth + (cardWidth + overlap) * 2
|
||||||
/// Where overlap = cardWidth * overlapRatio
|
/// Where overlap = cardWidth * overlapRatio
|
||||||
/// Solving: cardWidth = (containerWidth - 2*padding) / (1 + 2*(1 + overlapRatio))
|
private var cardWidth: CGFloat {
|
||||||
private func cardWidth(for containerWidth: CGFloat) -> CGFloat {
|
|
||||||
let availableWidth = containerWidth - containerPadding * 2
|
|
||||||
let divisor = 1 + CGFloat(maxCards - 1) * (1 + overlapRatio)
|
let divisor = 1 + CGFloat(maxCards - 1) * (1 + overlapRatio)
|
||||||
return availableWidth / divisor
|
return containerWidth / divisor
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Card overlap based on card width
|
/// Card overlap based on card width
|
||||||
private func cardOverlap(for cardWidth: CGFloat) -> CGFloat {
|
private var cardOverlap: CGFloat {
|
||||||
cardWidth * overlapRatio
|
cardWidth * overlapRatio
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Card height based on aspect ratio
|
/// Card height based on aspect ratio
|
||||||
private func cardHeight(for cardWidth: CGFloat) -> CGFloat {
|
private var cardHeight: CGFloat {
|
||||||
cardWidth * CasinoDesign.Size.cardAspectRatio
|
cardWidth * CasinoDesign.Size.cardAspectRatio
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Body
|
// MARK: - Body
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
GeometryReader { geometry in
|
cardsContent
|
||||||
let width = cardWidth(for: geometry.size.width)
|
.frame(width: containerWidth, height: cardHeight)
|
||||||
let overlap = cardOverlap(for: width)
|
.background(winnerBorder)
|
||||||
let height = cardHeight(for: width)
|
.overlay(alignment: .bottom) {
|
||||||
|
winBadge
|
||||||
cardsContent(cardWidth: width, cardOverlap: overlap)
|
}
|
||||||
.frame(width: geometry.size.width, height: height + containerPadding * 2)
|
|
||||||
.background(winnerBorder)
|
|
||||||
.overlay(alignment: .bottom) {
|
|
||||||
winBadge
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.aspectRatio(contentWidth / contentHeight, contentMode: .fit)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Aspect ratio helper - use base card dimensions for consistent sizing
|
|
||||||
private var contentWidth: CGFloat {
|
|
||||||
// Base card width for ratio calculation
|
|
||||||
let baseCardWidth: CGFloat = 45
|
|
||||||
let baseOverlap = baseCardWidth * overlapRatio
|
|
||||||
return baseCardWidth + (baseCardWidth + baseOverlap) * CGFloat(maxCards - 1) + containerPadding * 2
|
|
||||||
}
|
|
||||||
|
|
||||||
private var contentHeight: CGFloat {
|
|
||||||
let baseCardWidth: CGFloat = 45
|
|
||||||
return baseCardWidth * CasinoDesign.Size.cardAspectRatio + containerPadding * 2
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Private Views
|
// MARK: - Private Views
|
||||||
|
|
||||||
private func cardsContent(cardWidth: CGFloat, cardOverlap: CGFloat) -> some View {
|
private var cardsContent: some View {
|
||||||
HStack(spacing: cards.isEmpty ? placeholderSpacing : cardOverlap) {
|
HStack(spacing: cards.isEmpty ? placeholderSpacing : cardOverlap) {
|
||||||
if cards.isEmpty {
|
if cards.isEmpty {
|
||||||
// Placeholders - no overlap, just side by side
|
// Placeholders - no overlap, just side by side
|
||||||
@ -152,7 +129,8 @@ struct CompactHandView: View {
|
|||||||
CompactHandView(
|
CompactHandView(
|
||||||
cards: [],
|
cards: [],
|
||||||
cardsFaceUp: [],
|
cardsFaceUp: [],
|
||||||
isWinner: false
|
isWinner: false,
|
||||||
|
containerWidth: 160
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -166,7 +144,8 @@ struct CompactHandView: View {
|
|||||||
Card(suit: .hearts, rank: .eight)
|
Card(suit: .hearts, rank: .eight)
|
||||||
],
|
],
|
||||||
cardsFaceUp: [true, true],
|
cardsFaceUp: [true, true],
|
||||||
isWinner: false
|
isWinner: false,
|
||||||
|
containerWidth: 160
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -181,7 +160,8 @@ struct CompactHandView: View {
|
|||||||
Card(suit: .clubs, rank: .two)
|
Card(suit: .clubs, rank: .two)
|
||||||
],
|
],
|
||||||
cardsFaceUp: [true, true, true],
|
cardsFaceUp: [true, true, true],
|
||||||
isWinner: true
|
isWinner: true,
|
||||||
|
containerWidth: 160
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -195,7 +175,8 @@ struct CompactHandView: View {
|
|||||||
Card(suit: .spades, rank: .seven)
|
Card(suit: .spades, rank: .seven)
|
||||||
],
|
],
|
||||||
cardsFaceUp: [false, false],
|
cardsFaceUp: [false, false],
|
||||||
isWinner: false
|
isWinner: false,
|
||||||
|
containerWidth: 160
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user