132 lines
3.5 KiB
Swift
132 lines
3.5 KiB
Swift
//
|
|
// Components.swift
|
|
// MagicCounter
|
|
//
|
|
// Created by Atridad Lahiji on 2025-12-06.
|
|
//
|
|
|
|
import SwiftUI
|
|
|
|
/**
|
|
* A circular button with an icon, used for game controls.
|
|
*/
|
|
struct CircleButton: View {
|
|
let icon: String
|
|
let color: Color
|
|
let size: CGFloat
|
|
let action: () -> Void
|
|
|
|
init(icon: String, color: Color, size: CGFloat = 44, action: @escaping () -> Void) {
|
|
self.icon = icon
|
|
self.color = color
|
|
self.size = size
|
|
self.action = action
|
|
}
|
|
|
|
var body: some View {
|
|
Button(action: action) {
|
|
Image(systemName: icon)
|
|
.font(size < 40 ? .caption : .title3)
|
|
.frame(width: size, height: size)
|
|
.background(color.opacity(0.2))
|
|
.clipShape(Circle())
|
|
.foregroundStyle(color)
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A control for adjusting a large numerical value (like Life).
|
|
*/
|
|
struct LifeCounterControl: View {
|
|
let value: Int
|
|
let onDecrease: () -> Void
|
|
let onIncrease: () -> Void
|
|
|
|
var body: some View {
|
|
HStack(spacing: 16) {
|
|
CircleButton(icon: "minus", color: .red, action: onDecrease)
|
|
|
|
Text("\(value)")
|
|
.font(.system(size: 56, weight: .bold, design: .rounded))
|
|
.minimumScaleFactor(0.5)
|
|
.lineLimit(1)
|
|
.foregroundStyle(.primary)
|
|
|
|
CircleButton(icon: "plus", color: .green, action: onIncrease)
|
|
}
|
|
.padding(.horizontal, 16)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A smaller control for adjusting secondary values (like Poison).
|
|
*/
|
|
struct SmallCounterControl: View {
|
|
let value: Int
|
|
let icon: String
|
|
let color: Color
|
|
let onDecrease: () -> Void
|
|
let onIncrease: () -> Void
|
|
|
|
var body: some View {
|
|
HStack(spacing: 4) {
|
|
CircleButton(icon: "minus", color: .gray, size: 24, action: onDecrease)
|
|
|
|
VStack(spacing: 0) {
|
|
Image(systemName: icon)
|
|
.font(.caption2)
|
|
.foregroundStyle(color)
|
|
Text("\(value)")
|
|
.font(.title3.bold())
|
|
.foregroundStyle(color)
|
|
}
|
|
.frame(minWidth: 30)
|
|
|
|
CircleButton(icon: "plus", color: .gray, size: 24, action: onIncrease)
|
|
}
|
|
.padding(6)
|
|
.background(color.opacity(0.1))
|
|
.cornerRadius(12)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A reusable slider row for settings.
|
|
*/
|
|
struct SettingSlider: View {
|
|
let title: String
|
|
let value: Binding<Double>
|
|
let range: ClosedRange<Double>
|
|
let step: Double
|
|
|
|
var body: some View {
|
|
VStack(alignment: .leading) {
|
|
HStack {
|
|
Text(title)
|
|
Spacer()
|
|
Text("\(Int(value.wrappedValue))")
|
|
.bold()
|
|
}
|
|
Slider(value: value, in: range, step: step)
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Helper for haptic feedback.
|
|
*/
|
|
enum Haptics {
|
|
static func play(_ style: UIImpactFeedbackGenerator.FeedbackStyle) {
|
|
guard UserDefaults.standard.bool(forKey: "hapticFeedbackEnabled") else { return }
|
|
let generator = UIImpactFeedbackGenerator(style: style)
|
|
generator.impactOccurred()
|
|
}
|
|
|
|
static func notification(_ type: UINotificationFeedbackGenerator.FeedbackType) {
|
|
guard UserDefaults.standard.bool(forKey: "hapticFeedbackEnabled") else { return }
|
|
let generator = UINotificationFeedbackGenerator()
|
|
generator.notificationOccurred(type)
|
|
}
|
|
}
|