Files
TUIkit/Sources/TUIKit/Modifiers/OverlayModifier.swift
T
phranck 45e73faafb refactor: Rename package from SwiftTUI to TUIKit
BREAKING CHANGE: Package name changed due to name collision with
existing rensbreur/SwiftTUI package.

Changes:
- Rename package from SwiftTUI to TUIKit in Package.swift
- Rename Sources/SwiftTUI to Sources/TUIKit
- Rename Sources/SwiftTUIExample to Sources/TUIKitExample
- Rename Tests/SwiftTUITests to Tests/TUIKitTests
- Rename SwiftTUI.swift to TUIKit.swift
- Update all imports: import SwiftTUI -> import TUIKit
- Update all code references: SwiftTUI.renderToBuffer -> TUIKit.renderToBuffer
- Update documentation comments
- Rename swiftTUIVersion to tuiKitVersion

All 181 tests passing.
2026-01-28 19:32:09 +01:00

105 lines
3.1 KiB
Swift

//
// OverlayModifier.swift
// TUIKit
//
// A modifier that renders an overlay on top of the base view.
//
/// Internal modifier that layers an overlay view on top of the base content.
///
/// The overlay is rendered on top of the base content. Both views are rendered
/// to their natural size, and the overlay is positioned according to the
/// specified alignment within the base content's bounds.
public struct OverlayModifier<Base: View, Overlay: View>: View {
/// The base content.
let base: Base
/// The overlay content.
let overlay: Overlay
/// The alignment of the overlay within the base bounds.
let alignment: Alignment
public var body: Never {
fatalError("OverlayModifier renders via Renderable")
}
}
// MARK: - Renderable
extension OverlayModifier: Renderable {
public func renderToBuffer(context: RenderContext) -> FrameBuffer {
// Render both contents
let baseBuffer = TUIKit.renderToBuffer(base, context: context)
let overlayBuffer = TUIKit.renderToBuffer(overlay, context: context)
guard !baseBuffer.isEmpty else {
return overlayBuffer
}
guard !overlayBuffer.isEmpty else {
return baseBuffer
}
// Calculate the position of the overlay based on alignment
let baseWidth = baseBuffer.width
let baseHeight = baseBuffer.height
let overlayWidth = overlayBuffer.width
let overlayHeight = overlayBuffer.height
// Calculate horizontal position
let xOffset: Int
switch alignment.horizontal {
case .leading:
xOffset = 0
case .center:
xOffset = max(0, (baseWidth - overlayWidth) / 2)
case .trailing:
xOffset = max(0, baseWidth - overlayWidth)
}
// Calculate vertical position
let yOffset: Int
switch alignment.vertical {
case .top:
yOffset = 0
case .center:
yOffset = max(0, (baseHeight - overlayHeight) / 2)
case .bottom:
yOffset = max(0, baseHeight - overlayHeight)
}
// Composite the overlay onto the base
return baseBuffer.composited(with: overlayBuffer, at: (x: xOffset, y: yOffset))
}
}
// MARK: - View Extension
extension View {
/// Layers the specified view on top of this view.
///
/// The overlay is positioned according to the specified alignment
/// within the bounds of the base view.
///
/// # Example
///
/// ```swift
/// Text("Background content here")
/// .overlay(alignment: .center) {
/// Text("Centered overlay")
/// }
/// ```
///
/// - Parameters:
/// - alignment: The alignment of the overlay (default: .center).
/// - content: The overlay content.
/// - Returns: A view with the overlay applied.
public func overlay<Overlay: View>(
alignment: Alignment = .center,
@ViewBuilder content: () -> Overlay
) -> some View {
OverlayModifier(base: self, overlay: content(), alignment: alignment)
}
}