|
7 | 7 |
|
8 | 8 | import SwiftUI |
9 | 9 |
|
| 10 | +/// A color, gradient, or other style. |
| 11 | +/// |
| 12 | +/// Create a shape style with a type, options, and modifiers. |
| 13 | +/// |
| 14 | +/// ```elixir |
| 15 | +/// {:color, :blue} |
| 16 | +/// {:color, :red, [{:opacity, 0.5}]} |
| 17 | +/// ``` |
| 18 | +/// |
| 19 | +/// ## Shape Styles |
| 20 | +/// To create a shape style, use a tuple where the first element is an atom for the style type, and the second element is the style value. |
| 21 | +/// |
| 22 | +/// ```elixir |
| 23 | +/// {type, value} |
| 24 | +/// ``` |
| 25 | +/// |
| 26 | +/// ### :color |
| 27 | +/// See ``LiveViewNative/SwiftUI/Color/init(from:)`` for a list of possible color values. |
| 28 | +/// |
| 29 | +/// ```elixir |
| 30 | +/// {:color, :blue} |
| 31 | +/// ``` |
| 32 | +/// |
| 33 | +/// ### :angular_gradient |
| 34 | +/// See ``LiveViewNative/SwiftUI/AngularGradient`` for details on creating this style. |
| 35 | +/// |
| 36 | +/// ```elixir |
| 37 | +/// {:angular_gradient, [gradient: {:colors, [:pink, :blue]}, angle: {:degrees, 45}]} |
| 38 | +/// ``` |
| 39 | +/// |
| 40 | +/// ### :elliptical_gradient |
| 41 | +/// See ``LiveViewNative/SwiftUI/EllipticalGradient`` for details on creating this style. |
| 42 | +/// |
| 43 | +/// ```elixir |
| 44 | +/// {:elliptical_gradient, [gradient: {:colors, [:pink, :blue]}]} |
| 45 | +/// ``` |
| 46 | +/// |
| 47 | +/// ### :linear_gradient |
| 48 | +/// See ``LiveViewNative/SwiftUI/LinearGradient`` for details on creating this style. |
| 49 | +/// |
| 50 | +/// ```elixir |
| 51 | +/// {:linear_gradient, [gradient: {:colors, [:pink, :blue]}]} |
| 52 | +/// ``` |
| 53 | +/// |
| 54 | +/// ### :radial_gradient |
| 55 | +/// See ``LiveViewNative/SwiftUI/RadialGradient`` for details on creating this style. |
| 56 | +/// |
| 57 | +/// ```elixir |
| 58 | +/// {:radial_gradient, [gradient: {:colors, [:pink, :blue]}, start_radius: 0, end_radius: 100]} |
| 59 | +/// ``` |
| 60 | +/// |
| 61 | +/// ### :hierarchical |
| 62 | +/// See ``LiveViewNative/SwiftUI/HierarchicalShapeStyle`` for a list of possible values. |
| 63 | +/// |
| 64 | +/// ```elixir |
| 65 | +/// {:hierarchical, :tertiary} |
| 66 | +/// ``` |
| 67 | +/// |
| 68 | +/// ### :material |
| 69 | +/// See ``LiveViewNative/SwiftUI/Material`` for a list of possible values. |
| 70 | +/// |
| 71 | +/// - Note: Materials are only available on iOS and macOS. |
| 72 | +/// |
| 73 | +/// ```elixir |
| 74 | +/// {:material, :regular} |
| 75 | +/// ``` |
| 76 | +/// |
| 77 | +/// ### :image |
| 78 | +/// See ``LiveViewNative/SwiftUI/ImagePaint`` for details on creating this style. |
| 79 | +/// |
| 80 | +/// ```elixir |
| 81 | +/// {:image, [image: {:system, "basketball.fill"}]} |
| 82 | +/// ``` |
| 83 | +/// |
| 84 | +/// ### :selection |
| 85 | +/// The selection color in the current context. |
| 86 | +/// |
| 87 | +/// - Note: Only available on iOS and macOS |
| 88 | +/// |
| 89 | +/// ```elixir |
| 90 | +/// :selection |
| 91 | +/// ``` |
| 92 | +/// |
| 93 | +/// ### :separator |
| 94 | +/// The separator color in the current context. |
| 95 | +/// |
| 96 | +/// - Note: Only available on macOS |
| 97 | +/// |
| 98 | +/// ```elixir |
| 99 | +/// :separator |
| 100 | +/// ``` |
| 101 | +/// |
| 102 | +/// ### :tint |
| 103 | +/// The tint color in the current context. |
| 104 | +/// |
| 105 | +/// ```elixir |
| 106 | +/// :tint |
| 107 | +/// ``` |
| 108 | +/// |
| 109 | +/// ### :foreground |
| 110 | +/// The foreground style in the current context. |
| 111 | +/// |
| 112 | +/// ```elixir |
| 113 | +/// :foreground |
| 114 | +/// ``` |
| 115 | +/// |
| 116 | +/// ### :background |
| 117 | +/// The background style in the current context. |
| 118 | +/// |
| 119 | +/// ```elixir |
| 120 | +/// :background |
| 121 | +/// ``` |
| 122 | +/// |
| 123 | +/// ## Modifiers |
| 124 | +/// A third element can be contained in the style tuple. This element is a list of modifiers. |
| 125 | +/// |
| 126 | +/// ```elixir |
| 127 | +/// {type, value, [modifier1, modifier2, ...]} |
| 128 | +/// ``` |
| 129 | +/// |
| 130 | +/// Modifiers are created with tuples as well, with the first element being the name of the modifier and the second being any options. |
| 131 | +/// |
| 132 | +/// ```elixir |
| 133 | +/// {:opacity, 0.5} |
| 134 | +/// ``` |
| 135 | +/// |
| 136 | +/// Modifiers with only one option can have the value directly as the second element. |
| 137 | +/// If a modifier has multiply options, use a keyword list to set the values. |
| 138 | +/// |
| 139 | +/// ### :opacity |
| 140 | +/// Arguments: |
| 141 | +/// * `opacity` (required) - The transparency of the style, from 0-1. |
| 142 | +/// |
| 143 | +/// ```elixir |
| 144 | +/// {:opacity, 0.5} |
| 145 | +/// ``` |
| 146 | +/// |
| 147 | +/// ### :blend_mode |
| 148 | +/// Arguments: |
| 149 | +/// * `blend_mode` (required) - The ``LiveViewNative/SwiftUI/BlendMode`` to use for this style. |
| 150 | +/// |
| 151 | +/// ```elixir |
| 152 | +/// {:blend_mode, :multiply} |
| 153 | +/// ``` |
| 154 | +/// |
| 155 | +/// ### :shadow |
| 156 | +/// Arguments: |
| 157 | +/// * `shadow_style` (required) - The ``LiveViewNative/SwiftUI/ShadowStyle`` to apply. |
| 158 | +/// |
| 159 | +/// ```elixir |
| 160 | +/// {:shadow, {:drop, [radius: 10]}} |
| 161 | +/// ``` |
| 162 | +#if swift(>=5.8) |
| 163 | +@_documentation(visibility: public) |
| 164 | +#endif |
10 | 165 | extension AnyShapeStyle: Decodable { |
11 | 166 | public init(from decoder: Decoder) throws { |
12 | 167 | let container = try decoder.container(keyedBy: CodingKeys.self) |
13 | | - |
14 | | - if let color = try? container.decode(SwiftUI.Color?.self, forKey: .style) { |
15 | | - self = Self(color) |
16 | | - } else if let linearGradient = try? container.decode(LinearGradient?.self, forKey: .style) { |
17 | | - self = Self(linearGradient) |
18 | | - } else { |
19 | | - throw DecodingError.dataCorrupted(.init(codingPath: container.codingPath, debugDescription: "expected valid value for AnyShapeStyle")) |
| 168 | + |
| 169 | + switch try container.decode(ConcreteStyle.self, forKey: .concreteStyle) { |
| 170 | + case .color: |
| 171 | + self = Self(try container.decode(SwiftUI.Color.self, forKey: .style)) |
| 172 | + case .angularGradient: |
| 173 | + self = Self(try container.decode(AngularGradient.self, forKey: .style)) |
| 174 | + case .ellipticalGradient: |
| 175 | + self = Self(try container.decode(EllipticalGradient.self, forKey: .style)) |
| 176 | + case .linearGradient: |
| 177 | + self = Self(try container.decode(LinearGradient.self, forKey: .style)) |
| 178 | + case .radialGradient: |
| 179 | + self = Self(try container.decode(RadialGradient.self, forKey: .style)) |
| 180 | + case .hierarchical: |
| 181 | + self = Self(try container.decode(HierarchicalShapeStyle.self, forKey: .style)) |
| 182 | + #if os(iOS) || os(macOS) |
| 183 | + case .material: |
| 184 | + self = Self(try container.decode(Material.self, forKey: .style)) |
| 185 | + #endif |
| 186 | + case .image: |
| 187 | + self = Self(try container.decode(ImagePaint.self, forKey: .style)) |
| 188 | + #if os(iOS) || os(macOS) |
| 189 | + case .selection: |
| 190 | + self = Self(SelectionShapeStyle()) |
| 191 | + #endif |
| 192 | + #if os(macOS) |
| 193 | + case .separator: |
| 194 | + self = Self(SeparatorShapeStyle()) |
| 195 | + #endif |
| 196 | + case .tint: |
| 197 | + self = Self(TintShapeStyle()) |
| 198 | + case .foreground: |
| 199 | + self = Self(ForegroundStyle()) |
| 200 | + case .background: |
| 201 | + self = Self(BackgroundStyle()) |
| 202 | + } |
| 203 | + |
| 204 | + var modifiers = try container.nestedUnkeyedContainer(forKey: .modifiers) |
| 205 | + while !modifiers.isAtEnd { |
| 206 | + let modifier = try modifiers.nestedContainer(keyedBy: CodingKeys.Modifier.self) |
| 207 | + switch try modifier.decode(Modifier.self, forKey: .type) { |
| 208 | + case .opacity: |
| 209 | + self = Self(self.opacity(try modifier.decode(Double.self, forKey: .properties))) |
| 210 | + case .blendMode: |
| 211 | + self = Self(self.blendMode(try modifier.decode(BlendMode.self, forKey: .properties))) |
| 212 | + case .shadow: |
| 213 | + self = Self(self.shadow(try modifier.decode(ShadowStyle.self, forKey: .properties))) |
| 214 | + } |
20 | 215 | } |
21 | 216 | } |
22 | 217 | enum CodingKeys: String, CodingKey { |
| 218 | + case concreteStyle = "concrete_style" |
23 | 219 | case style |
| 220 | + case modifiers |
| 221 | + |
| 222 | + enum Modifier: CodingKey { |
| 223 | + case type |
| 224 | + case properties |
| 225 | + } |
| 226 | + } |
| 227 | + |
| 228 | + enum ConcreteStyle: String, Decodable { |
| 229 | + case color |
| 230 | + case angularGradient = "angular_gradient" |
| 231 | + case ellipticalGradient = "elliptical_gradient" |
| 232 | + case linearGradient = "linear_gradient" |
| 233 | + case radialGradient = "radial_gradient" |
| 234 | + case hierarchical |
| 235 | + #if os(iOS) || os(macOS) |
| 236 | + case material |
| 237 | + #endif |
| 238 | + case image |
| 239 | + #if os(iOS) || os(macOS) |
| 240 | + case selection |
| 241 | + #endif |
| 242 | + #if os(macOS) |
| 243 | + case separator |
| 244 | + #endif |
| 245 | + case tint |
| 246 | + case foreground |
| 247 | + case background |
| 248 | + } |
| 249 | + |
| 250 | + enum Modifier: String, Decodable { |
| 251 | + case opacity |
| 252 | + case blendMode = "blend_mode" |
| 253 | + case shadow |
24 | 254 | } |
25 | 255 | } |
0 commit comments