diff --git a/Example/Example.xcodeproj/xcshareddata/xcschemes/OSUI_Example.xcscheme b/Example/Example.xcodeproj/xcshareddata/xcschemes/OSUI_Example.xcscheme index 430e75275..f8c687531 100644 --- a/Example/Example.xcodeproj/xcshareddata/xcschemes/OSUI_Example.xcscheme +++ b/Example/Example.xcodeproj/xcshareddata/xcschemes/OSUI_Example.xcscheme @@ -63,6 +63,13 @@ ReferencedContainer = "container:Example.xcodeproj"> + + + + - - + isEnabled = "NO"> diff --git a/Example/Example.xcodeproj/xcshareddata/xcschemes/OSUI_UITests.xcscheme b/Example/Example.xcodeproj/xcshareddata/xcschemes/OSUI_UITests.xcscheme index 9c7f44871..382e37f12 100644 --- a/Example/Example.xcodeproj/xcshareddata/xcschemes/OSUI_UITests.xcscheme +++ b/Example/Example.xcodeproj/xcshareddata/xcschemes/OSUI_UITests.xcscheme @@ -42,13 +42,6 @@ debugDocumentVersioning = "YES" debugServiceExtension = "internal" allowLocationSimulation = "YES"> - - - - + + + + - - + isEnabled = "NO"> + isEnabled = "NO"> diff --git a/Example/OpenSwiftUIUITests/Shape/ShapeUITests.swift b/Example/OpenSwiftUIUITests/Shape/ShapeUITests.swift index 8c65a7f2c..d128d45cf 100644 --- a/Example/OpenSwiftUIUITests/Shape/ShapeUITests.swift +++ b/Example/OpenSwiftUIUITests/Shape/ShapeUITests.swift @@ -13,6 +13,8 @@ struct ShapeUITests { struct ContentView: View { var body: some View { Rectangle() + .fill(Color.red) + .frame(width: 100, height: 100, alignment: .center) } } openSwiftUIAssertSnapshot(of: ContentView()) @@ -23,8 +25,24 @@ struct ShapeUITests { struct ContentView: View { var body: some View { Circle() + .fill(Color.red) + .frame(width: 100, height: 100, alignment: .center) } } - openSwiftUIAssertSnapshot(of: ContentView()) + // FIXME + openSwiftUIAssertSnapshot(of: ContentView(), perceptualPrecision: 0.8) + } + + @Test + func shadow() { + struct ContentView: View { + var body: some View { + Circle() + .fill(Color.red.shadow(.drop(color: .blue,radius: 5, x: 10, y: 10))) + .frame(width: 100, height: 100, alignment: .center) + } + } + // FIXME + openSwiftUIAssertSnapshot(of: ContentView(), perceptualPrecision: 0.8) } } diff --git a/Sources/OpenSwiftUICore/Shape/ShapeStyle/ShadowStyle.swift b/Sources/OpenSwiftUICore/Shape/ShapeStyle/ShadowStyle.swift index 5a33532fb..3b4197db5 100644 --- a/Sources/OpenSwiftUICore/Shape/ShapeStyle/ShadowStyle.swift +++ b/Sources/OpenSwiftUICore/Shape/ShapeStyle/ShadowStyle.swift @@ -9,6 +9,7 @@ public import Foundation // MARK: - ShadowStyle +/// A style to use when rendering shadows. @available(OpenSwiftUI_v4_0, *) public struct ShadowStyle: Equatable, Sendable { package struct Kind: OptionSet { @@ -64,6 +65,20 @@ public struct ShadowStyle: Equatable, Sendable { @_spi(Private) public static let inner: ShadowStyle = .inner(radius: 0) + /// Creates a custom drop shadow style. + /// + /// Drop shadows draw behind the source content by blurring, + /// tinting and offsetting its per-pixel alpha values. + /// + /// - Parameters: + /// - color: The shadow's color. + /// - radius: The shadow's size. + /// - x: A horizontal offset you use to position the shadow + /// relative to this view. + /// - y: A vertical offset you use to position the shadow + /// relative to this view. + /// + /// - Returns: A new shadow style. public static func drop( color: Color = .init(.sRGBLinear, white: 0, opacity: 0.33), radius: CGFloat, @@ -76,6 +91,20 @@ public struct ShadowStyle: Equatable, Sendable { ) } + /// Creates a custom inner shadow style. + /// + /// Inner shadows draw on top of the source content by blurring, + /// tinting, inverting and offsetting its per-pixel alpha values. + /// + /// - Parameters: + /// - color: The shadow's color. + /// - radius: The shadow's size. + /// - x: A horizontal offset you use to position the shadow + /// relative to this view. + /// - y: A vertical offset you use to position the shadow + /// relative to this view. + /// + /// - Returns: A new shadow style. public static func inner( color: Color = .init(.sRGBLinear, white: 0, opacity: 0.55), radius: CGFloat, @@ -151,6 +180,17 @@ extension ShadowStyle { @available(OpenSwiftUI_v4_0, *) extension ShapeStyle { + + /// Applies the specified shadow effect to the shape style. + /// + /// For example, you can create a rectangle that adds a drop shadow to + /// the ``ShapeStyle/red`` shape style. + /// + /// Rectangle().fill(.red.shadow(.drop(radius: 2, y: 3))) + /// + /// - Parameter style: The shadow style to apply. + /// + /// - Returns: A new shape style that uses the specified shadow style. @inlinable public func shadow(_ style: ShadowStyle) -> some ShapeStyle { return _ShadowShapeStyle(style: self, shadowStyle: style) @@ -159,6 +199,23 @@ extension ShapeStyle { @available(OpenSwiftUI_v4_0, *) extension ShapeStyle where Self == AnyShapeStyle { + + /// Returns a shape style that applies the specified shadow style to the + /// current style. + /// + /// In most contexts the current style is the foreground, but not always. + /// For example, when setting the value of the background style, that + /// becomes the current implicit style. + /// + /// The following example creates a circle filled with the current + /// foreground style that uses an inner shadow: + /// + /// Circle().fill(.shadow(.inner(radius: 1, y: 1))) + /// + /// - Parameter style: The shadow style to apply. + /// + /// - Returns: A new shape style based on the current style that uses the + /// specified shadow style. @_alwaysEmitIntoClient public static func shadow(_ style: ShadowStyle) -> some ShapeStyle { return _ShadowShapeStyle( diff --git a/Sources/OpenSwiftUICore/Shape/ShapeStyle/ShapeStyleRendering.swift b/Sources/OpenSwiftUICore/Shape/ShapeStyle/ShapeStyleRendering.swift index bbf5df84a..54c38f842 100644 --- a/Sources/OpenSwiftUICore/Shape/ShapeStyle/ShapeStyleRendering.swift +++ b/Sources/OpenSwiftUICore/Shape/ShapeStyle/ShapeStyleRendering.swift @@ -187,6 +187,13 @@ package struct _ShapeStyle_RenderedShape { default: _openSwiftUIUnimplementedFailure() } + // FIXME + for effect in style.effects { + guard case let .shadow(shadow) = effect.kind else { + continue + } + addEffect(.filter(.shadow(shadow))) + } _openSwiftUIUnimplementedWarning() }