Files
Rocky 2bc8c1b87d fix(ipad): ship iPad P0 layout/globe fixes, edit-last-input, drop clipboard commands
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.
2026-08-10 13:50:50 +08:00

91 lines
4.1 KiB
Swift
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// TypingSurfaceMetrics.swift
// OSGKeyboard · Shared
//
// Size decisions for the typing surface: which key metrics apply, and how tall
// the keyboard must be to hold them.
//
// These live here rather than next to the SwiftUI view because two independent
// consumers must agree on them exactly — `KeyboardViewController` sets a UIKit
// height constraint, and the SwiftUI grid lays keys out inside it. If they ever
// pick different metrics the bottom row is clipped, so there is one source of
// truth and it is unit-testable without the extension target.
import CoreGraphics
public enum TypingSurfaceMetrics {
// MARK: - Structural bands (identical on every device)
/// Candidate / top control band above the key grid.
public static let topRegionHeight: CGFloat = 44
/// Gap between the top band and the first key row.
public static let verticalKeySpacing: CGFloat = 8
public static let outerPaddingTop: CGFloat = 4
public static let outerPaddingBottom: CGFloat = 4
// MARK: - Phone
public static let keyRowHeight: CGFloat = 50
public static let keyRowSpacing: CGFloat = 7
public static let keyHorizontalSpacing: CGFloat = 6
public static let secondRowInset: CGFloat = 18
// MARK: - iPad (narrow: portrait, or a compact-ish regular window)
public static let iPadKeyRowHeight: CGFloat = 54
public static let iPadKeyRowSpacing: CGFloat = 8
public static let iPadKeyHorizontalSpacing: CGFloat = 8
// MARK: - iPad (wide: landscape or a large Stage Manager window)
/// The grid spans the full host width here, so rows must grow with it.
/// Holding the narrow 54 pt height at ~1200 pt wide produces 110×54 keys —
/// flatter than any system key.
public static let wideIPadKeyRowHeight: CGFloat = 76
public static let wideIPadKeyRowSpacing: CGFloat = 10
public static let wideIPadKeyHorizontalSpacing: CGFloat = 10
/// Key metrics for a device class and available width. Width — not device
/// orientation — picks the wide bucket, so a resized Stage Manager window
/// lands on the right metrics without consulting orientation at all.
public static func metrics(isIPad: Bool, width: CGFloat) -> TypingKeyLayoutBuilder.Metrics {
guard isIPad else {
return TypingKeyLayoutBuilder.Metrics(
keyRowHeight: keyRowHeight,
keyRowSpacing: keyRowSpacing,
keyHorizontalSpacing: keyHorizontalSpacing,
secondRowInset: secondRowInset,
bottomRowHeight: KeyboardChromeLayout.actionKeyHeight,
bottomActionSpacing: KeyboardChromeLayout.actionKeySpacing,
gridToBottomSpacing: keyRowSpacing
)
}
let isWide = KeyboardChromeLayout.usesWideIPadMetrics(isIPad: true, width: width)
let rowHeight = isWide ? wideIPadKeyRowHeight : iPadKeyRowHeight
let rowSpacing = isWide ? wideIPadKeyRowSpacing : iPadKeyRowSpacing
return TypingKeyLayoutBuilder.Metrics(
keyRowHeight: rowHeight,
keyRowSpacing: rowSpacing,
keyHorizontalSpacing: isWide ? wideIPadKeyHorizontalSpacing : iPadKeyHorizontalSpacing,
// Ignored: iPad derives the indent from the first row's key width.
secondRowInset: 0,
bottomRowHeight: rowHeight,
bottomActionSpacing: KeyboardChromeLayout.actionKeySpacing,
gridToBottomSpacing: rowSpacing,
derivesSecondRowInsetFromKeyWidth: true
)
}
/// Content-driven keyboard height. iPad grows its rows, so the shell has to
/// grow with them or the bottom row is clipped.
public static func contentHeight(isIPad: Bool, width: CGFloat) -> CGFloat {
guard isIPad else { return KeyboardChromeLayout.totalHeight }
let m = metrics(isIPad: true, width: width)
let inner = topRegionHeight
+ verticalKeySpacing
+ (3 * m.keyRowHeight + 2 * m.keyRowSpacing)
+ m.gridToBottomSpacing
+ m.bottomRowHeight
return inner + outerPaddingTop + outerPaddingBottom
}
}