[FEAT] LinearGradient, RadialGradient & AngularGradient#450
[FEAT] LinearGradient, RadialGradient & AngularGradient#450MiaKoring wants to merge 28 commits intomoreSwift:mainfrom
Conversation
|
Related issue: #427 |
|
The new commit now offers full API parity to SwiftUI on AngularGradient and correctly renders all 6 test gradients. “Correct” meaning looking like the SwiftUI rendered result. All 3 supported frameworks recieved this change and the shared screenshots were updated. |
|
Outstanding Tasks have been completed, this can now be reviewed and merged |
stackotter
left a comment
There was a problem hiding this comment.
Thanks for tackling this feature! I've reviewed all of the code now, but I haven't tested anything locally yet. I plan to construct a SwiftUI app with a bunch of edge cases that I have thought of, and then compile that with your PR and ensure that all of the edge cases act the same across platforms. It should be a useful tool to help you address some of my PR comments too. I likely won't get around to doing that today though.
# Conflicts: # Examples/Bundler.toml # Examples/Package.resolved # Examples/Package.swift # Package.resolved # Package.swift # Sources/SwiftCrossUI/Backend/AppBackend.swift
|
conflicts should be resolved now, same for all requested changes without open discussion |
stackotter
left a comment
There was a problem hiding this comment.
Thanks for applying all that feedback! I've got a few more things, but I think most of them are relatively small things this time (organisation, documentation, etc). I don't have time to test things locally right now, but I will try to do that soon, because I may be able to resolve some of my own questions and comments by testing out the edge cases that I'm worried about.
|
|
||
| #if DEBUG | ||
| if range < 0 { | ||
| logger.warning( |
There was a problem hiding this comment.
I think it's easy enough for us to support right? Because we can just automatically reverse the gradient. Use cases where it's probably most applicable would be graphical editors where the user is entering the radii with draggable handles or something similar. I assume some backends that don't use adjustedStops may already support having the radii swapped?
|
GtkBackend will not support the AngularGradient type right now due to rendering inconsistencies with SwiftUI and the Apple Backends which are not easily resolvable. For future reference if needed: public func createAngularGradient() -> Widget {
Box()
}
public func updateAngularGradient(
_ widget: Widget,
gradient: AngularGradient,
withSize size: SIMD2<Int>,
in environment: EnvironmentValues
) {
let widget = widget as! Box
let adjustedStops = gradient.adjustedStops
let stops = adjustedStops.map { stop in
let resolved = stop.color.resolve(in: environment)
let red = resolved.red * 255
let green = resolved.green * 255
let blue = resolved.blue * 255
let location = stop.location * 360
return "rgba(\(red), \(green), \(blue), \(resolved.opacity)) \(location)deg”
}.joined(separator: ", “)
let startDegrees = gradient.startAngle.degrees + 90
let centerXPercent = gradient.center.x * 100
let centerYPercent = gradient.center.y * 100
widget.css.set(
property: .init(
key: “background”,
value: “””
conic-gradient(from \(startDegrees)deg \
at \(centerXPercent)% \(centerYPercent)%, \
\(stops))
“””
)
)
} |
…to feat/gradient # Conflicts: # Examples/Package.resolved
|
I believe I’m caught up. main is merged again too. |
stackotter
left a comment
There was a problem hiding this comment.
Thanks for resolving those comments, the implementation looks great now. My feedback this time is purely formatting and documentation wording, so this should be the home straight.
I believe the @bbrk24 was having issues with Xcode adding additional trailing whitespace indents, so maybe some default setting has changed recently?
| struct GradientsApp: App { | ||
| static let colors: [Color] = [.red, .orange, .yellow, .green, .blue, .purple] | ||
|
|
||
There was a problem hiding this comment.
This file has ended up with a lot of trailing whitespace added. Maybe Xcode did it?
|
|
||
| #if canImport(SwiftBundlerRuntime) | ||
| import SwiftBundlerRuntime | ||
| import SwiftBundlerRuntime |
| /// - widget: The widget to update. | ||
| /// - gradient: The SwiftCrossUI struct housing the information for the gradient's rendering. | ||
| /// - size: The new size of the widget. | ||
| /// - environment: The widgets environment, used to resolve its colors. |
| /// - widget: The widget to update. | ||
| /// - gradient: The SwiftCrossUI struct housing the information for the gradient's rendering. | ||
| /// - size: The new size of the widget. | ||
| /// - environment: The widgets environment, used to resolve its colors. |
| @@ -0,0 +1,22 @@ | |||
| extension BackendFeatures { | |||
| /// Backend Methods for Angular (conic) Gradients | |||
There was a problem hiding this comment.
Don't need to capitalise the Methods here, or the Angular and Gradients either
| /// Creates an angular gradient that completes a partial rotation. | ||
| /// | ||
| /// Stops are expected to be in 360° unit space. | ||
| /// For ``Gradient.Stop`` location of 0 corresponds to 0° and 1 to 360°. |
There was a problem hiding this comment.
/// For each ``Gradient.Stop``, a location of 0 corresponds to 0°, and a location of 1 corresponds to 360°.| /// The radius at which the first gradient stop will be placed. | ||
| /// | ||
| /// All space outside inside radius gets filled with the color of the first gradient stop. | ||
| /// All space inside radius gets filled with the color of the first gradient stop. |
| /// The normalized center point of the gradient in its coordinate space. | ||
| public let center: UnitPoint | ||
|
|
||
| } | ||
|
|
||
| public func updateLinearGradient( | ||
| let widget = widget as! Box | ||
|
|
||
| let stops = cssStops(gradient: gradient.gradient, environment: environment) | ||
| let stops = gradient.startRadius < gradient.endRadius ? |
There was a problem hiding this comment.
Wrap the ternary like this;
let stops = gradient.startRadius < gradient.endRadius
? gradient.gradient.stops
: invertedStops(stops: gradient.gradient.stops)





Summary
Added support for 3 types of gradients to be rendered as Views:
Changes
SwiftCrossUI
AppBackendprotocol.ElementaryViewstruct for each type of gradient.Angleintended for use byAngularGradientlaterAnglefrom aUnitPointUnitPoint, meant to represent a normalized point in a view’s coordinate spaceUnitPoints are not clamped, so you can use them with arbitrary values to benefit for example fromAngle(origin:destination:)Gradient, used by all implementations of a Gradient, its a gradient type agnostic representation of a color gradientGradient.Stoprepresenting a color and its normalized position in the gradient[Color]or[Gradient.Stop]AppKitBackend, UIKitBackend
AppBackendmethodsWinUI, GtkBackend
AppBackendmethods related toLinearGradientandRadialGradientSwiftCrossUITests
Gradient[Color]Color(to make future support of addition possible and remove edgecases to check outside ofGradient)[Color]with one entryExamples
LinearGradientRadialGradientAngularGradientNotes
ShapeStylecould easily re-use the added structs tho.TODO / Status
RadialGradientsupport to WinUIBackend (pending RadialGradientBrush generation)RadialGradient