| 1 | export interface ReservedRegion { |
| 2 | readonly kind: "division" | "occlusion"; |
| 3 | readonly x: number; |
| 4 | readonly y: number; |
| 5 | readonly width: number; |
| 6 | readonly height: number; |
| 7 | } |
| 8 | |
| 9 | export interface NativeLayoutMetrics { |
| 10 | readonly width: number; |
| 11 | readonly height: number; |
| 12 | readonly hasHinge?: boolean; |
| 13 | readonly horizontalSizeClass: "compact" | "regular"; |
| 14 | readonly verticalBarEdge: "none" | "left" | "right"; |
| 15 | readonly safeArea: { |
| 16 | readonly top: number; |
| 17 | readonly bottom: number; |
| 18 | readonly left: number; |
| 19 | readonly right: number; |
| 20 | }; |
| 21 | readonly reservedRegions: ReadonlyArray<ReservedRegion>; |
| 22 | } |
| 23 | |
| 24 | /** |
| 25 | * Keep bottom controls in one usable region. A book-like fold selects the |
| 26 | * larger side; a horizontal fold leaves the controls on the lower display. |
| 27 | * Text and other scrollable content can continue to use the full canvas. |
| 28 | */ |
| 29 | export function deriveBottomControlInsets(metrics: NativeLayoutMetrics | null) { |
| 30 | if (!metrics) return { left: 0, right: 0, bottom: 0 }; |
| 31 | let left = metrics.safeArea.left; |
| 32 | let right = metrics.width - metrics.safeArea.right; |
| 33 | let bottom = metrics.height; |
| 34 | for (const region of metrics.reservedRegions) { |
| 35 | const x = Math.max(left, region.x); |
| 36 | const endX = Math.min(right, region.x + region.width); |
| 37 | const y = Math.max(0, region.y); |
| 38 | const endY = Math.min(bottom, region.y + region.height); |
| 39 | if (endX <= x || endY <= y) continue; |
| 40 | if (region.kind === "division" && region.height >= metrics.height / 2) { |
| 41 | if (right - endX >= x - left) left = endX; |
| 42 | else right = x; |
| 43 | } else if (endY >= bottom - metrics.safeArea.bottom) { |
| 44 | // An occlusion at the bottom can also occupy just one corner. |
| 45 | if (region.width >= right - left) bottom = y; |
| 46 | else if (right - endX >= x - left) left = endX; |
| 47 | else right = x; |
| 48 | } |
| 49 | } |
| 50 | return { left, right: metrics.width - right, bottom: metrics.height - bottom }; |
| 51 | } |