Yattee v2 rewrite

This commit is contained in:
Arkadiusz Fal
2026-02-08 18:31:16 +01:00
parent 20d0cfc0c7
commit 05f921d605
1043 changed files with 163875 additions and 68430 deletions

View File

@@ -0,0 +1,150 @@
//
// TapZoneCalculator.swift
// Yattee
//
// Calculator for determining tap zone hit-testing and frames.
//
#if os(iOS)
import CoreGraphics
import Foundation
/// Utility for calculating tap zone positions and hit-testing.
enum TapZoneCalculator {
/// Safe margin from screen edges to avoid system gesture conflicts.
static let safeMargin: CGFloat = 25
/// Determines which zone was tapped based on the layout and tap point.
/// - Parameters:
/// - point: The tap location in the bounds coordinate system.
/// - bounds: The total gesture-recognizable area bounds.
/// - layout: The current tap zone layout.
/// - Returns: The zone position that was tapped, or nil if outside safe margins.
static func zone(
for point: CGPoint,
in bounds: CGRect,
layout: TapZoneLayout
) -> TapZonePosition? {
// Check safe margins
let safeArea = bounds.insetBy(dx: safeMargin, dy: safeMargin)
guard safeArea.contains(point) else { return nil }
// Normalize point to 0-1 range within safe area
let normalizedX = (point.x - safeArea.minX) / safeArea.width
let normalizedY = (point.y - safeArea.minY) / safeArea.height
switch layout {
case .single:
return .full
case .horizontalSplit:
return normalizedX < 0.5 ? .left : .right
case .verticalSplit:
return normalizedY < 0.5 ? .top : .bottom
case .threeColumns:
if normalizedX < 1.0 / 3.0 {
return .leftThird
} else if normalizedX < 2.0 / 3.0 {
return .center
} else {
return .rightThird
}
case .quadrants:
let isTop = normalizedY < 0.5
let isLeft = normalizedX < 0.5
if isTop {
return isLeft ? .topLeft : .topRight
} else {
return isLeft ? .bottomLeft : .bottomRight
}
}
}
/// Returns the frame for a specific zone position within bounds.
/// - Parameters:
/// - position: The zone position.
/// - bounds: The total gesture-recognizable area bounds.
/// - layout: The current tap zone layout.
/// - Returns: The frame for the zone, or nil if position doesn't match layout.
static func frame(
for position: TapZonePosition,
in bounds: CGRect,
layout: TapZoneLayout
) -> CGRect? {
// Use safe area for calculations
let safeArea = bounds.insetBy(dx: safeMargin, dy: safeMargin)
switch layout {
case .single:
guard position == .full else { return nil }
return safeArea
case .horizontalSplit:
let halfWidth = safeArea.width / 2
switch position {
case .left:
return CGRect(x: safeArea.minX, y: safeArea.minY,
width: halfWidth, height: safeArea.height)
case .right:
return CGRect(x: safeArea.minX + halfWidth, y: safeArea.minY,
width: halfWidth, height: safeArea.height)
default:
return nil
}
case .verticalSplit:
let halfHeight = safeArea.height / 2
switch position {
case .top:
return CGRect(x: safeArea.minX, y: safeArea.minY,
width: safeArea.width, height: halfHeight)
case .bottom:
return CGRect(x: safeArea.minX, y: safeArea.minY + halfHeight,
width: safeArea.width, height: halfHeight)
default:
return nil
}
case .threeColumns:
let thirdWidth = safeArea.width / 3
switch position {
case .leftThird:
return CGRect(x: safeArea.minX, y: safeArea.minY,
width: thirdWidth, height: safeArea.height)
case .center:
return CGRect(x: safeArea.minX + thirdWidth, y: safeArea.minY,
width: thirdWidth, height: safeArea.height)
case .rightThird:
return CGRect(x: safeArea.minX + thirdWidth * 2, y: safeArea.minY,
width: thirdWidth, height: safeArea.height)
default:
return nil
}
case .quadrants:
let halfWidth = safeArea.width / 2
let halfHeight = safeArea.height / 2
switch position {
case .topLeft:
return CGRect(x: safeArea.minX, y: safeArea.minY,
width: halfWidth, height: halfHeight)
case .topRight:
return CGRect(x: safeArea.minX + halfWidth, y: safeArea.minY,
width: halfWidth, height: halfHeight)
case .bottomLeft:
return CGRect(x: safeArea.minX, y: safeArea.minY + halfHeight,
width: halfWidth, height: halfHeight)
case .bottomRight:
return CGRect(x: safeArea.minX + halfWidth, y: safeArea.minY + halfHeight,
width: halfWidth, height: halfHeight)
default:
return nil
}
}
}
}
#endif