31f5937a7f
Reduce extension memory pressure and delivery races while adding richer candidates, tactile feedback, and safer two-level creative polishing.
101 lines
3.3 KiB
Swift
101 lines
3.3 KiB
Swift
// TypingLayoutProviding.swift
|
||
// OSGKeyboard · Shared
|
||
//
|
||
// Phase 2 escape hatch: replace the in-repo SwiftUI key shell with a
|
||
// KeyboardKit-based (or other) layout without changing the engine bridge.
|
||
|
||
import Foundation
|
||
|
||
/// Page shown by the typing key shell.
|
||
public enum TypingKeyPage: String, CaseIterable, Sendable {
|
||
case letters
|
||
case numbers
|
||
case symbols
|
||
}
|
||
|
||
/// Abstraction over “which characters the current page shows”.
|
||
/// The Phase 1 SwiftUI keyboard reads this; a future Kit-backed shell
|
||
/// can feed the same consumer.
|
||
public protocol TypingLayoutProviding: Sendable {
|
||
func rows(
|
||
for page: TypingKeyPage,
|
||
language: TypingInputLanguage,
|
||
shiftActive: Bool
|
||
) -> [[String]]
|
||
}
|
||
|
||
/// Phone QWERTY + 123 / #+= pages aligned with iOS system keyboards
|
||
/// (English US and Simplified Chinese Pinyin).
|
||
public struct StandardTypingLayout: TypingLayoutProviding {
|
||
public init() {}
|
||
|
||
public func rows(
|
||
for page: TypingKeyPage,
|
||
language: TypingInputLanguage,
|
||
shiftActive: Bool
|
||
) -> [[String]] {
|
||
switch page {
|
||
case .letters:
|
||
return letterRows(shiftActive: shiftActive)
|
||
case .numbers:
|
||
return language == .chinese ? chineseNumberRows : englishNumberRows
|
||
case .symbols:
|
||
return language == .chinese ? chineseSymbolRows : englishSymbolRows
|
||
}
|
||
}
|
||
|
||
// MARK: - Letters (shared)
|
||
|
||
private func letterRows(shiftActive: Bool) -> [[String]] {
|
||
let upper = shiftActive
|
||
let map: (String) -> String = { upper ? $0.uppercased() : $0 }
|
||
return [
|
||
["q", "w", "e", "r", "t", "y", "u", "i", "o", "p"].map(map),
|
||
["a", "s", "d", "f", "g", "h", "j", "k", "l"].map(map),
|
||
["⇧", "z", "x", "c", "v", "b", "n", "m", "⌫"].map { $0.count == 1 ? map($0) : $0 }
|
||
]
|
||
}
|
||
|
||
// MARK: - English (iOS US)
|
||
|
||
/// iOS English US · Numbers
|
||
private var englishNumberRows: [[String]] {
|
||
[
|
||
["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"],
|
||
["-", "/", ":", ";", "(", ")", "$", "&", "@", "\""],
|
||
["#+=", ".", ",", "?", "!", "'", "⌫"]
|
||
]
|
||
}
|
||
|
||
/// iOS English US · Symbols
|
||
private var englishSymbolRows: [[String]] {
|
||
[
|
||
["[", "]", "{", "}", "#", "%", "^", "*", "+", "="],
|
||
["_", "\\", "|", "~", "<", ">", "€", "£", "¥", "·"],
|
||
["123", ".", ",", "?", "!", "'", "⌫"]
|
||
]
|
||
}
|
||
|
||
// MARK: - Chinese (iOS 简体拼音)
|
||
|
||
/// iOS Simplified Chinese · Numbers (fullwidth punctuation where system uses it)
|
||
private var chineseNumberRows: [[String]] {
|
||
[
|
||
["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"],
|
||
// System 123 page uses curly double quotes “ ” (not corner brackets).
|
||
["-", "/", ":", ";", "(", ")", "¥", "@", "“", "”"],
|
||
["#+=", "。", ",", "、", "?", "!", ".", "⌫"]
|
||
]
|
||
}
|
||
|
||
/// iOS Simplified Chinese · Symbols
|
||
private var chineseSymbolRows: [[String]] {
|
||
[
|
||
// 「」 live on #+= so they remain reachable after numbers use “ ”.
|
||
["【", "】", "「", "」", "#", "%", "^", "*", "+", "="],
|
||
["_", "\\", "|", "~", "《", "》", "€", "£", "¥", "·"],
|
||
["123", "。", ",", "、", "?", "!", ".", "⌫"]
|
||
]
|
||
}
|
||
}
|