mirror of
https://github.com/yattee/yattee.git
synced 2026-02-20 01:39:46 +00:00
Yattee v2 rewrite
This commit is contained in:
64
Yattee/Models/PlayerControls/Gestures/GesturesSettings.swift
Normal file
64
Yattee/Models/PlayerControls/Gestures/GesturesSettings.swift
Normal file
@@ -0,0 +1,64 @@
|
||||
//
|
||||
// GesturesSettings.swift
|
||||
// Yattee
|
||||
//
|
||||
// Combined settings for all player gestures.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/// Combined settings for player gestures.
|
||||
struct GesturesSettings: Codable, Hashable, Sendable {
|
||||
/// Settings for tap gestures.
|
||||
var tapGestures: TapGesturesSettings
|
||||
|
||||
/// Settings for horizontal seek gesture.
|
||||
var seekGesture: SeekGestureSettings
|
||||
|
||||
/// Settings for pinch-to-panscan gesture.
|
||||
var panscanGesture: PanscanGestureSettings
|
||||
|
||||
// MARK: - Initialization
|
||||
|
||||
/// Creates combined gestures settings.
|
||||
/// - Parameters:
|
||||
/// - tapGestures: Tap gesture settings.
|
||||
/// - seekGesture: Seek gesture settings.
|
||||
/// - panscanGesture: Panscan gesture settings.
|
||||
init(
|
||||
tapGestures: TapGesturesSettings = .default,
|
||||
seekGesture: SeekGestureSettings = .default,
|
||||
panscanGesture: PanscanGestureSettings = .default
|
||||
) {
|
||||
self.tapGestures = tapGestures
|
||||
self.seekGesture = seekGesture
|
||||
self.panscanGesture = panscanGesture
|
||||
}
|
||||
|
||||
// MARK: - Defaults
|
||||
|
||||
/// Default settings with all gestures disabled.
|
||||
static let `default` = GesturesSettings()
|
||||
|
||||
// MARK: - Computed Properties
|
||||
|
||||
/// Whether tap gestures are enabled.
|
||||
var areTapGesturesActive: Bool {
|
||||
tapGestures.isEnabled
|
||||
}
|
||||
|
||||
/// Whether seek gesture is enabled.
|
||||
var isSeekGestureActive: Bool {
|
||||
seekGesture.isEnabled
|
||||
}
|
||||
|
||||
/// Whether panscan gesture is enabled.
|
||||
var isPanscanGestureActive: Bool {
|
||||
panscanGesture.isEnabled
|
||||
}
|
||||
|
||||
/// Whether any gestures are effectively enabled.
|
||||
var hasActiveGestures: Bool {
|
||||
areTapGesturesActive || isSeekGestureActive || isPanscanGestureActive
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
//
|
||||
// PanscanGestureSettings.swift
|
||||
// Yattee
|
||||
//
|
||||
// Settings for pinch-to-panscan gesture.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/// Settings for the pinch-to-panscan gesture on the player.
|
||||
struct PanscanGestureSettings: Codable, Hashable, Sendable {
|
||||
/// Whether the panscan gesture is enabled.
|
||||
var isEnabled: Bool
|
||||
|
||||
/// Whether to snap to 0 (fit) or 1 (fill) when released.
|
||||
/// If false, the value stays exactly where released (free zoom).
|
||||
var snapToEnds: Bool
|
||||
|
||||
// MARK: - Initialization
|
||||
|
||||
/// Creates panscan gesture settings.
|
||||
/// - Parameters:
|
||||
/// - isEnabled: Whether enabled (default: true).
|
||||
/// - snapToEnds: Whether to snap to fit/fill (default: true).
|
||||
init(
|
||||
isEnabled: Bool = true,
|
||||
snapToEnds: Bool = true
|
||||
) {
|
||||
self.isEnabled = isEnabled
|
||||
self.snapToEnds = snapToEnds
|
||||
}
|
||||
|
||||
// MARK: - Defaults
|
||||
|
||||
/// Default settings with gesture enabled and snap mode on.
|
||||
static let `default` = PanscanGestureSettings()
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
//
|
||||
// SeekGestureSensitivity.swift
|
||||
// Yattee
|
||||
//
|
||||
// Sensitivity levels for horizontal seek gesture.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/// Sensitivity presets for the horizontal seek gesture.
|
||||
/// Controls how much seeking occurs per screen width of drag.
|
||||
enum SeekGestureSensitivity: String, Codable, CaseIterable, Hashable, Sendable {
|
||||
case low
|
||||
case medium
|
||||
case high
|
||||
|
||||
// MARK: - Seek Configuration
|
||||
|
||||
/// Base seconds of seeking per full screen width drag.
|
||||
/// This value is scaled by video duration using a multiplier.
|
||||
var baseSecondsPerScreenWidth: Double {
|
||||
switch self {
|
||||
case .low: 30
|
||||
case .medium: 60
|
||||
case .high: 120
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Display
|
||||
|
||||
/// Localized display name for the sensitivity level.
|
||||
var displayName: String {
|
||||
switch self {
|
||||
case .low:
|
||||
String(localized: "gestures.seek.sensitivity.low", defaultValue: "Low")
|
||||
case .medium:
|
||||
String(localized: "gestures.seek.sensitivity.medium", defaultValue: "Medium")
|
||||
case .high:
|
||||
String(localized: "gestures.seek.sensitivity.high", defaultValue: "High")
|
||||
}
|
||||
}
|
||||
|
||||
/// Localized description of what this sensitivity level is best for.
|
||||
var description: String {
|
||||
switch self {
|
||||
case .low:
|
||||
String(localized: "gestures.seek.sensitivity.low.description", defaultValue: "Precise control")
|
||||
case .medium:
|
||||
String(localized: "gestures.seek.sensitivity.medium.description", defaultValue: "Balanced")
|
||||
case .high:
|
||||
String(localized: "gestures.seek.sensitivity.high.description", defaultValue: "Fast navigation")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
//
|
||||
// SeekGestureSettings.swift
|
||||
// Yattee
|
||||
//
|
||||
// Settings for horizontal seek gesture.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/// Settings for the horizontal drag-to-seek gesture on the player.
|
||||
struct SeekGestureSettings: Codable, Hashable, Sendable {
|
||||
/// Whether the seek gesture is enabled.
|
||||
var isEnabled: Bool
|
||||
|
||||
/// Sensitivity level controlling seek speed.
|
||||
var sensitivity: SeekGestureSensitivity
|
||||
|
||||
// MARK: - Initialization
|
||||
|
||||
/// Creates seek gesture settings.
|
||||
/// - Parameters:
|
||||
/// - isEnabled: Whether enabled (default: false).
|
||||
/// - sensitivity: Sensitivity level (default: medium).
|
||||
init(
|
||||
isEnabled: Bool = false,
|
||||
sensitivity: SeekGestureSensitivity = .medium
|
||||
) {
|
||||
self.isEnabled = isEnabled
|
||||
self.sensitivity = sensitivity
|
||||
}
|
||||
|
||||
// MARK: - Defaults
|
||||
|
||||
/// Default settings with gesture disabled.
|
||||
static let `default` = SeekGestureSettings()
|
||||
}
|
||||
202
Yattee/Models/PlayerControls/Gestures/TapGestureAction.swift
Normal file
202
Yattee/Models/PlayerControls/Gestures/TapGestureAction.swift
Normal file
@@ -0,0 +1,202 @@
|
||||
//
|
||||
// TapGestureAction.swift
|
||||
// Yattee
|
||||
//
|
||||
// Defines the available actions for tap gestures.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/// Action to perform when a tap gesture zone is activated.
|
||||
enum TapGestureAction: Codable, Hashable, Sendable {
|
||||
/// Toggle play/pause state.
|
||||
case togglePlayPause
|
||||
|
||||
/// Seek forward by the specified number of seconds.
|
||||
case seekForward(seconds: Int)
|
||||
|
||||
/// Seek backward by the specified number of seconds.
|
||||
case seekBackward(seconds: Int)
|
||||
|
||||
/// Toggle fullscreen mode.
|
||||
case toggleFullscreen
|
||||
|
||||
/// Toggle Picture-in-Picture mode.
|
||||
case togglePiP
|
||||
|
||||
/// Play next item in queue.
|
||||
case playNext
|
||||
|
||||
/// Play previous item in queue.
|
||||
case playPrevious
|
||||
|
||||
/// Cycle through playback speeds.
|
||||
case cyclePlaybackSpeed
|
||||
|
||||
/// Toggle mute state.
|
||||
case toggleMute
|
||||
|
||||
/// Display name for the action.
|
||||
var displayName: String {
|
||||
switch self {
|
||||
case .togglePlayPause:
|
||||
String(localized: "gestures.action.togglePlayPause", defaultValue: "Toggle Play/Pause")
|
||||
case .seekForward(let seconds):
|
||||
String(localized: "gestures.action.seekForward", defaultValue: "Seek Forward") + " \(seconds)s"
|
||||
case .seekBackward(let seconds):
|
||||
String(localized: "gestures.action.seekBackward", defaultValue: "Seek Backward") + " \(seconds)s"
|
||||
case .toggleFullscreen:
|
||||
String(localized: "gestures.action.toggleFullscreen", defaultValue: "Toggle Fullscreen")
|
||||
case .togglePiP:
|
||||
String(localized: "gestures.action.togglePiP", defaultValue: "Toggle PiP")
|
||||
case .playNext:
|
||||
String(localized: "gestures.action.playNext", defaultValue: "Play Next")
|
||||
case .playPrevious:
|
||||
String(localized: "gestures.action.playPrevious", defaultValue: "Play Previous")
|
||||
case .cyclePlaybackSpeed:
|
||||
String(localized: "gestures.action.cyclePlaybackSpeed", defaultValue: "Cycle Playback Speed")
|
||||
case .toggleMute:
|
||||
String(localized: "gestures.action.toggleMute", defaultValue: "Toggle Mute")
|
||||
}
|
||||
}
|
||||
|
||||
/// SF Symbol name for the action icon.
|
||||
var systemImage: String {
|
||||
switch self {
|
||||
case .togglePlayPause:
|
||||
"playpause.fill"
|
||||
case .seekForward:
|
||||
"arrow.trianglehead.clockwise"
|
||||
case .seekBackward:
|
||||
"arrow.trianglehead.counterclockwise"
|
||||
case .toggleFullscreen:
|
||||
"arrow.up.left.and.arrow.down.right"
|
||||
case .togglePiP:
|
||||
"pip"
|
||||
case .playNext:
|
||||
"forward.fill"
|
||||
case .playPrevious:
|
||||
"backward.fill"
|
||||
case .cyclePlaybackSpeed:
|
||||
"gauge.with.dots.needle.67percent"
|
||||
case .toggleMute:
|
||||
"speaker.slash.fill"
|
||||
}
|
||||
}
|
||||
|
||||
/// Base action type for grouping (ignoring associated values).
|
||||
var actionType: TapGestureActionType {
|
||||
switch self {
|
||||
case .togglePlayPause: .togglePlayPause
|
||||
case .seekForward: .seekForward
|
||||
case .seekBackward: .seekBackward
|
||||
case .toggleFullscreen: .toggleFullscreen
|
||||
case .togglePiP: .togglePiP
|
||||
case .playNext: .playNext
|
||||
case .playPrevious: .playPrevious
|
||||
case .cyclePlaybackSpeed: .cyclePlaybackSpeed
|
||||
case .toggleMute: .toggleMute
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether this action requires a seconds parameter.
|
||||
var requiresSecondsParameter: Bool {
|
||||
switch self {
|
||||
case .seekForward, .seekBackward:
|
||||
true
|
||||
default:
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// The seek seconds value if applicable.
|
||||
var seekSeconds: Int? {
|
||||
switch self {
|
||||
case .seekForward(let seconds), .seekBackward(let seconds):
|
||||
seconds
|
||||
default:
|
||||
nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Action Type Enum
|
||||
|
||||
/// Base action types without associated values (for UI selection).
|
||||
enum TapGestureActionType: String, CaseIterable, Identifiable, Sendable {
|
||||
case togglePlayPause
|
||||
case seekForward
|
||||
case seekBackward
|
||||
case toggleFullscreen
|
||||
case togglePiP
|
||||
case playNext
|
||||
case playPrevious
|
||||
case cyclePlaybackSpeed
|
||||
case toggleMute
|
||||
|
||||
var id: String { rawValue }
|
||||
|
||||
/// Display name for the action type.
|
||||
var displayName: String {
|
||||
switch self {
|
||||
case .togglePlayPause:
|
||||
String(localized: "gestures.actionType.togglePlayPause", defaultValue: "Toggle Play/Pause")
|
||||
case .seekForward:
|
||||
String(localized: "gestures.actionType.seekForward", defaultValue: "Seek Forward")
|
||||
case .seekBackward:
|
||||
String(localized: "gestures.actionType.seekBackward", defaultValue: "Seek Backward")
|
||||
case .toggleFullscreen:
|
||||
String(localized: "gestures.actionType.toggleFullscreen", defaultValue: "Toggle Fullscreen")
|
||||
case .togglePiP:
|
||||
String(localized: "gestures.actionType.togglePiP", defaultValue: "Toggle PiP")
|
||||
case .playNext:
|
||||
String(localized: "gestures.actionType.playNext", defaultValue: "Play Next")
|
||||
case .playPrevious:
|
||||
String(localized: "gestures.actionType.playPrevious", defaultValue: "Play Previous")
|
||||
case .cyclePlaybackSpeed:
|
||||
String(localized: "gestures.actionType.cyclePlaybackSpeed", defaultValue: "Cycle Playback Speed")
|
||||
case .toggleMute:
|
||||
String(localized: "gestures.actionType.toggleMute", defaultValue: "Toggle Mute")
|
||||
}
|
||||
}
|
||||
|
||||
/// SF Symbol name for the action type.
|
||||
var systemImage: String {
|
||||
switch self {
|
||||
case .togglePlayPause: "playpause.fill"
|
||||
case .seekForward: "arrow.trianglehead.clockwise"
|
||||
case .seekBackward: "arrow.trianglehead.counterclockwise"
|
||||
case .toggleFullscreen: "arrow.up.left.and.arrow.down.right"
|
||||
case .togglePiP: "pip"
|
||||
case .playNext: "forward.fill"
|
||||
case .playPrevious: "backward.fill"
|
||||
case .cyclePlaybackSpeed: "gauge.with.dots.needle.67percent"
|
||||
case .toggleMute: "speaker.slash.fill"
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether this action type requires a seconds parameter.
|
||||
var requiresSecondsParameter: Bool {
|
||||
switch self {
|
||||
case .seekForward, .seekBackward:
|
||||
true
|
||||
default:
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a TapGestureAction from this type with default seconds if needed.
|
||||
func toAction(seconds: Int = 10) -> TapGestureAction {
|
||||
switch self {
|
||||
case .togglePlayPause: .togglePlayPause
|
||||
case .seekForward: .seekForward(seconds: seconds)
|
||||
case .seekBackward: .seekBackward(seconds: seconds)
|
||||
case .toggleFullscreen: .toggleFullscreen
|
||||
case .togglePiP: .togglePiP
|
||||
case .playNext: .playNext
|
||||
case .playPrevious: .playPrevious
|
||||
case .cyclePlaybackSpeed: .cyclePlaybackSpeed
|
||||
case .toggleMute: .toggleMute
|
||||
}
|
||||
}
|
||||
}
|
||||
115
Yattee/Models/PlayerControls/Gestures/TapGesturesSettings.swift
Normal file
115
Yattee/Models/PlayerControls/Gestures/TapGesturesSettings.swift
Normal file
@@ -0,0 +1,115 @@
|
||||
//
|
||||
// TapGesturesSettings.swift
|
||||
// Yattee
|
||||
//
|
||||
// Settings for tap gesture recognition and behavior.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/// Settings for tap gestures on the player.
|
||||
struct TapGesturesSettings: Codable, Hashable, Sendable {
|
||||
/// Whether tap gestures are enabled.
|
||||
var isEnabled: Bool
|
||||
|
||||
/// The zone layout to use.
|
||||
var layout: TapZoneLayout
|
||||
|
||||
/// Configuration for each zone in the layout.
|
||||
var zoneConfigurations: [TapZoneConfiguration]
|
||||
|
||||
/// Double-tap timing window in milliseconds.
|
||||
var doubleTapInterval: Int
|
||||
|
||||
// MARK: - Initialization
|
||||
|
||||
/// Creates tap gestures settings.
|
||||
/// - Parameters:
|
||||
/// - isEnabled: Whether enabled (default: false).
|
||||
/// - layout: Zone layout (default: horizontalSplit).
|
||||
/// - zoneConfigurations: Zone configurations.
|
||||
/// - doubleTapInterval: Double-tap timing in ms (default: 300).
|
||||
init(
|
||||
isEnabled: Bool = false,
|
||||
layout: TapZoneLayout = .horizontalSplit,
|
||||
zoneConfigurations: [TapZoneConfiguration]? = nil,
|
||||
doubleTapInterval: Int = 300
|
||||
) {
|
||||
self.isEnabled = isEnabled
|
||||
self.layout = layout
|
||||
self.zoneConfigurations = zoneConfigurations ?? Self.defaultConfigurations(for: layout)
|
||||
self.doubleTapInterval = doubleTapInterval
|
||||
}
|
||||
|
||||
// MARK: - Defaults
|
||||
|
||||
/// Default settings with gestures disabled.
|
||||
static let `default` = TapGesturesSettings()
|
||||
|
||||
/// Creates default zone configurations for a layout.
|
||||
/// - Parameter layout: The zone layout.
|
||||
/// - Returns: Default configurations with sensible actions.
|
||||
static func defaultConfigurations(for layout: TapZoneLayout) -> [TapZoneConfiguration] {
|
||||
layout.positions.map { position in
|
||||
TapZoneConfiguration(
|
||||
position: position,
|
||||
action: defaultAction(for: position)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the default action for a zone position.
|
||||
private static func defaultAction(for position: TapZonePosition) -> TapGestureAction {
|
||||
switch position {
|
||||
case .full:
|
||||
.togglePlayPause
|
||||
case .left, .leftThird, .topLeft, .bottomLeft:
|
||||
.seekBackward(seconds: 10)
|
||||
case .right, .rightThird, .topRight, .bottomRight:
|
||||
.seekForward(seconds: 10)
|
||||
case .top:
|
||||
.togglePlayPause
|
||||
case .bottom:
|
||||
.togglePlayPause
|
||||
case .center:
|
||||
.togglePlayPause
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Helpers
|
||||
|
||||
/// Returns the configuration for a specific position.
|
||||
/// - Parameter position: The zone position.
|
||||
/// - Returns: The configuration, or nil if not found.
|
||||
func configuration(for position: TapZonePosition) -> TapZoneConfiguration? {
|
||||
zoneConfigurations.first { $0.position == position }
|
||||
}
|
||||
|
||||
/// Updates the configuration for a zone, or adds it if not present.
|
||||
/// - Parameter config: The updated configuration.
|
||||
/// - Returns: Updated settings.
|
||||
func withUpdatedConfiguration(_ config: TapZoneConfiguration) -> TapGesturesSettings {
|
||||
var settings = self
|
||||
if let index = settings.zoneConfigurations.firstIndex(where: { $0.position == config.position }) {
|
||||
settings.zoneConfigurations[index] = config
|
||||
} else {
|
||||
settings.zoneConfigurations.append(config)
|
||||
}
|
||||
return settings
|
||||
}
|
||||
|
||||
/// Creates settings with a new layout, generating default configurations.
|
||||
/// - Parameter newLayout: The new layout.
|
||||
/// - Returns: Updated settings with new layout and configurations.
|
||||
func withLayout(_ newLayout: TapZoneLayout) -> TapGesturesSettings {
|
||||
var settings = self
|
||||
settings.layout = newLayout
|
||||
settings.zoneConfigurations = Self.defaultConfigurations(for: newLayout)
|
||||
return settings
|
||||
}
|
||||
|
||||
// MARK: - Validation
|
||||
|
||||
/// Double-tap interval range in milliseconds.
|
||||
static let doubleTapIntervalRange = 150...600
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
//
|
||||
// TapZoneConfiguration.swift
|
||||
// Yattee
|
||||
//
|
||||
// Configuration for a single tap zone's action.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/// Configuration for a tap zone, mapping a position to an action.
|
||||
struct TapZoneConfiguration: Codable, Hashable, Sendable, Identifiable {
|
||||
/// Unique identifier for this configuration.
|
||||
var id: UUID
|
||||
|
||||
/// The position of this zone within the layout.
|
||||
var position: TapZonePosition
|
||||
|
||||
/// The action to perform when this zone is double-tapped.
|
||||
var action: TapGestureAction
|
||||
|
||||
/// Creates a new tap zone configuration.
|
||||
/// - Parameters:
|
||||
/// - id: Unique identifier (defaults to new UUID).
|
||||
/// - position: Zone position.
|
||||
/// - action: Action to perform.
|
||||
init(
|
||||
id: UUID = UUID(),
|
||||
position: TapZonePosition,
|
||||
action: TapGestureAction
|
||||
) {
|
||||
self.id = id
|
||||
self.position = position
|
||||
self.action = action
|
||||
}
|
||||
|
||||
/// Creates a configuration with a new action, preserving the ID.
|
||||
/// - Parameter newAction: The new action.
|
||||
/// - Returns: Updated configuration.
|
||||
func withAction(_ newAction: TapGestureAction) -> TapZoneConfiguration {
|
||||
TapZoneConfiguration(id: id, position: position, action: newAction)
|
||||
}
|
||||
}
|
||||
90
Yattee/Models/PlayerControls/Gestures/TapZoneLayout.swift
Normal file
90
Yattee/Models/PlayerControls/Gestures/TapZoneLayout.swift
Normal file
@@ -0,0 +1,90 @@
|
||||
//
|
||||
// TapZoneLayout.swift
|
||||
// Yattee
|
||||
//
|
||||
// Defines the available tap zone layouts for gesture recognition.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/// Layout options for tap gesture zones on the player.
|
||||
enum TapZoneLayout: String, Codable, CaseIterable, Sendable, Identifiable {
|
||||
/// Single full-screen zone.
|
||||
case single
|
||||
|
||||
/// Left and right zones (vertical split).
|
||||
case horizontalSplit
|
||||
|
||||
/// Top and bottom zones (horizontal split).
|
||||
case verticalSplit
|
||||
|
||||
/// Three vertical columns: left, center, right.
|
||||
case threeColumns
|
||||
|
||||
/// Four quadrants: top-left, top-right, bottom-left, bottom-right.
|
||||
case quadrants
|
||||
|
||||
var id: String { rawValue }
|
||||
|
||||
/// Number of zones in this layout.
|
||||
var zoneCount: Int {
|
||||
switch self {
|
||||
case .single:
|
||||
1
|
||||
case .horizontalSplit, .verticalSplit:
|
||||
2
|
||||
case .threeColumns:
|
||||
3
|
||||
case .quadrants:
|
||||
4
|
||||
}
|
||||
}
|
||||
|
||||
/// The zone positions available in this layout.
|
||||
var positions: [TapZonePosition] {
|
||||
switch self {
|
||||
case .single:
|
||||
[.full]
|
||||
case .horizontalSplit:
|
||||
[.left, .right]
|
||||
case .verticalSplit:
|
||||
[.top, .bottom]
|
||||
case .threeColumns:
|
||||
[.leftThird, .center, .rightThird]
|
||||
case .quadrants:
|
||||
[.topLeft, .topRight, .bottomLeft, .bottomRight]
|
||||
}
|
||||
}
|
||||
|
||||
/// Display name for the layout.
|
||||
var displayName: String {
|
||||
switch self {
|
||||
case .single:
|
||||
String(localized: "gestures.layout.single", defaultValue: "Single Zone")
|
||||
case .horizontalSplit:
|
||||
String(localized: "gestures.layout.horizontalSplit", defaultValue: "Left / Right")
|
||||
case .verticalSplit:
|
||||
String(localized: "gestures.layout.verticalSplit", defaultValue: "Top / Bottom")
|
||||
case .threeColumns:
|
||||
String(localized: "gestures.layout.threeColumns", defaultValue: "Three Columns")
|
||||
case .quadrants:
|
||||
String(localized: "gestures.layout.quadrants", defaultValue: "Four Quadrants")
|
||||
}
|
||||
}
|
||||
|
||||
/// Short description showing zone arrangement.
|
||||
var layoutDescription: String {
|
||||
switch self {
|
||||
case .single:
|
||||
"1"
|
||||
case .horizontalSplit:
|
||||
"2x1"
|
||||
case .verticalSplit:
|
||||
"1x2"
|
||||
case .threeColumns:
|
||||
"1x3"
|
||||
case .quadrants:
|
||||
"2x2"
|
||||
}
|
||||
}
|
||||
}
|
||||
65
Yattee/Models/PlayerControls/Gestures/TapZonePosition.swift
Normal file
65
Yattee/Models/PlayerControls/Gestures/TapZonePosition.swift
Normal file
@@ -0,0 +1,65 @@
|
||||
//
|
||||
// TapZonePosition.swift
|
||||
// Yattee
|
||||
//
|
||||
// Defines the position identifiers for tap zones.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/// Position identifier for a tap zone within a layout.
|
||||
enum TapZonePosition: String, Codable, CaseIterable, Sendable, Identifiable {
|
||||
// Single layout
|
||||
case full
|
||||
|
||||
// Horizontal split (2x1)
|
||||
case left
|
||||
case right
|
||||
|
||||
// Vertical split (1x2)
|
||||
case top
|
||||
case bottom
|
||||
|
||||
// Three columns (1x3)
|
||||
case leftThird
|
||||
case center
|
||||
case rightThird
|
||||
|
||||
// Quadrants (2x2)
|
||||
case topLeft
|
||||
case topRight
|
||||
case bottomLeft
|
||||
case bottomRight
|
||||
|
||||
var id: String { rawValue }
|
||||
|
||||
/// Display name for the zone position.
|
||||
var displayName: String {
|
||||
switch self {
|
||||
case .full:
|
||||
String(localized: "gestures.zone.full", defaultValue: "Tap Zone")
|
||||
case .left:
|
||||
String(localized: "gestures.zone.left", defaultValue: "Left")
|
||||
case .right:
|
||||
String(localized: "gestures.zone.right", defaultValue: "Right")
|
||||
case .top:
|
||||
String(localized: "gestures.zone.top", defaultValue: "Top")
|
||||
case .bottom:
|
||||
String(localized: "gestures.zone.bottom", defaultValue: "Bottom")
|
||||
case .leftThird:
|
||||
String(localized: "gestures.zone.leftThird", defaultValue: "Left")
|
||||
case .center:
|
||||
String(localized: "gestures.zone.center", defaultValue: "Center")
|
||||
case .rightThird:
|
||||
String(localized: "gestures.zone.rightThird", defaultValue: "Right")
|
||||
case .topLeft:
|
||||
String(localized: "gestures.zone.topLeft", defaultValue: "Top-Left")
|
||||
case .topRight:
|
||||
String(localized: "gestures.zone.topRight", defaultValue: "Top-Right")
|
||||
case .bottomLeft:
|
||||
String(localized: "gestures.zone.bottomLeft", defaultValue: "Bottom-Left")
|
||||
case .bottomRight:
|
||||
String(localized: "gestures.zone.bottomRight", defaultValue: "Bottom-Right")
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user