Refactor and Rename to CommandNotch

This commit is contained in:
2026-03-07 23:14:31 +11:00
parent 2bf1cbad2a
commit 5d161bb214
45 changed files with 76 additions and 69 deletions

View File

@@ -0,0 +1,109 @@
import SwiftUI
/// Custom SwiftUI Shape that draws the characteristic MacBook notch outline.
/// Both top and bottom corner radii are animatable, enabling smooth transitions
/// between the compact closed state and the expanded open state.
///
/// The shape uses quadratic Bezier curves to produce the distinctive
/// top-edge cut-ins of the closed notch, and a clean rounded-bottom
/// rectangle when open (topCornerRadius approaches 0).
struct NotchShape: Shape {
/// Radius applied to the top-left and top-right transitions where the notch
/// curves away from the screen edge. When close to 0, the top corners become
/// sharp and the shape is a rectangle with rounded bottom corners.
var topCornerRadius: CGFloat
/// Radius applied to the bottom-left and bottom-right inner corners.
var bottomCornerRadius: CGFloat
// MARK: - Animatable conformance
var animatableData: AnimatablePair<CGFloat, CGFloat> {
get { AnimatablePair(topCornerRadius, bottomCornerRadius) }
set {
topCornerRadius = newValue.first
bottomCornerRadius = newValue.second
}
}
// MARK: - Path
func path(in rect: CGRect) -> Path {
var path = Path()
let minX = rect.minX
let maxX = rect.maxX
let minY = rect.minY
let maxY = rect.maxY
let width = rect.width
let height = rect.height
let topR = min(topCornerRadius, width / 4, height / 2)
let botR = min(bottomCornerRadius, width / 4, height / 2)
// Start at the top-left corner of the rect
path.move(to: CGPoint(x: minX, y: minY))
if topR > 0.5 {
// Leave the screen edge horizontally, then turn into the side wall.
path.addQuadCurve(
to: CGPoint(x: minX + topR, y: minY + topR),
control: CGPoint(x: minX + topR, y: minY)
)
} else {
path.addLine(to: CGPoint(x: minX, y: minY))
}
// Left edge down to bottom-left corner area
path.addLine(to: CGPoint(x: minX + topR, y: maxY - botR))
// Bottom-left inner corner
path.addQuadCurve(
to: CGPoint(x: minX + topR + botR, y: maxY),
control: CGPoint(x: minX + topR, y: maxY)
)
// Bottom edge across
path.addLine(to: CGPoint(x: maxX - topR - botR, y: maxY))
// Bottom-right inner corner
path.addQuadCurve(
to: CGPoint(x: maxX - topR, y: maxY - botR),
control: CGPoint(x: maxX - topR, y: maxY)
)
// Right edge up to the top-right transition
path.addLine(to: CGPoint(x: maxX - topR, y: minY + topR))
if topR > 0.5 {
// Mirror the top-left transition.
path.addQuadCurve(
to: CGPoint(x: maxX, y: minY),
control: CGPoint(x: maxX - topR, y: minY)
)
} else {
path.addLine(to: CGPoint(x: maxX, y: minY))
}
path.closeSubpath()
return path
}
}
// MARK: - Convenience initializers
extension NotchShape {
/// Closed-state shape with tight corner radii that mimic the physical notch.
static var closed: NotchShape {
NotchShape(topCornerRadius: 6, bottomCornerRadius: 14)
}
/// Open-state shape: no top-edge cut-ins, just rounded bottom corners.
/// topCornerRadius is near-zero so the top becomes effectively flat and the panel
/// extends flush to the top edge of the screen.
static var opened: NotchShape {
NotchShape(topCornerRadius: 0, bottomCornerRadius: 24)
}
}