2bc8c1b87d
Adapt typing/voice surfaces for iPad width and height, add the system globe key and last-input editing flow, harden host-only Rime deployment, and remove clipboard voice commands. Bump build to 61.
61 lines
2.1 KiB
Swift
61 lines
2.1 KiB
Swift
// RecordButtonGesturePolicy.swift
|
|
// OSGKeyboard · Shared
|
|
//
|
|
// Pure tap / hold routing for the mic button. Kept out of the view so the
|
|
// "one press produces at most one action" invariant is unit-testable: the
|
|
// edit long-press flips the phase while the finger is still down, and
|
|
// regressions there let a single press both open and close a round.
|
|
|
|
import Foundation
|
|
|
|
public enum RecordButtonGestureAction: Equatable, Sendable {
|
|
case none
|
|
/// Start dictation, cancel a preparing edit, or stop recording —
|
|
/// all of which the coordinator resolves from its own phase.
|
|
case toggle
|
|
case beginEditLastInput
|
|
}
|
|
|
|
public enum RecordButtonGesturePolicy {
|
|
/// Action for a press released before the hold threshold.
|
|
public static func tapAction(
|
|
phase: RecordButton.Phase,
|
|
isEnabled: Bool
|
|
) -> RecordButtonGestureAction {
|
|
switch phase {
|
|
case .idleUnavailable:
|
|
return .toggle
|
|
case .idleReady, .error, .preparing, .recording:
|
|
return isEnabled ? .toggle : .none
|
|
case .processing:
|
|
return .none
|
|
}
|
|
}
|
|
|
|
/// Action for a press held past the threshold. A `.none` result means the
|
|
/// hold did not consume the press, so its release still counts as a tap and
|
|
/// no gesture becomes a dead key.
|
|
public static func holdAction(
|
|
phase: RecordButton.Phase,
|
|
isEnabled: Bool,
|
|
supportsEditLongPress: Bool
|
|
) -> RecordButtonGestureAction {
|
|
switch phase {
|
|
case .idleReady, .idleUnavailable, .error:
|
|
guard supportsEditLongPress else { return .none }
|
|
guard isEnabled || phase == .idleUnavailable else { return .none }
|
|
return .beginEditLastInput
|
|
case .recording:
|
|
return isEnabled ? .toggle : .none
|
|
case .preparing, .processing:
|
|
return .none
|
|
}
|
|
}
|
|
|
|
/// A hold that produced an action owns the press; its release must be
|
|
/// swallowed rather than replayed as a tap.
|
|
public static func consumesPress(_ action: RecordButtonGestureAction) -> Bool {
|
|
action != .none
|
|
}
|
|
}
|