diff --git a/Core/Base/Alignment.swift b/Core/Base/Alignment.swift index ee14f51c..62ee357a 100644 --- a/Core/Base/Alignment.swift +++ b/Core/Base/Alignment.swift @@ -5,6 +5,7 @@ import CoreGraphics import BaseUI /// Determines postioning of child item inside parent container +@frozen public enum Alignment { /// Child items are laid out starting from top/left case leading diff --git a/Core/Base/Combine.swift b/Core/Base/Combine.swift index f650a5de..9cf47f4e 100644 --- a/Core/Base/Combine.swift +++ b/Core/Base/Combine.swift @@ -14,10 +14,8 @@ public enum Combine { } public struct CombineFailure: Error, CustomDebugStringConvertible { - @usableFromInline let values: [T] - @inlinable public init(values: [T]) { self.values = values } diff --git a/Core/Base/Either.swift b/Core/Base/Either.swift index 2035968e..9c828544 100644 --- a/Core/Base/Either.swift +++ b/Core/Base/Either.swift @@ -2,6 +2,7 @@ import Foundation +@frozen public enum Either { case left(T) case right(U) diff --git a/Core/Base/Gradient.swift b/Core/Base/Gradient.swift index 0a498ac9..6471d206 100644 --- a/Core/Base/Gradient.swift +++ b/Core/Base/Gradient.swift @@ -4,6 +4,7 @@ import CoreGraphics import BaseUI +@frozen public enum Gradient: Equatable { public typealias Point = (color: Color, location: CGFloat) @@ -53,27 +54,75 @@ public enum Gradient: Equatable { } public struct Radial: Equatable { - public let center: RelativePoint - public let end: RelativePoint + public let centerX: CenterPoint + public let centerY: CenterPoint + public let end: Radius public let centerColor: Color public let intermediatePoints: [Point] public let outerColor: Color + public let shape: Shape public init( center: RelativePoint = .mid, end: RelativePoint? = nil, centerColor: Color, intermediatePoints: [Point] = [], + outerColor: Color? = nil, + shape: Shape? = nil + ) { + let positions = intermediatePoints.map { $0.location } + assert(positions == positions.sorted()) + + self.centerX = .relative(center.x) + self.centerY = .relative(center.y) + self.end = .relativeToSize(end ?? RelativeRect.full.radialEndPoint(for: center)) + self.centerColor = centerColor + self.intermediatePoints = intermediatePoints + self.outerColor = outerColor ?? centerColor.withAlphaComponent(0) + self.shape = shape ?? .ellipse + } + + public init( + centerX: CenterPoint, + centerY: CenterPoint, + end: Radius, + centerColor: Color, + intermediatePoints: [Point] = [], outerColor: Color? = nil ) { let positions = intermediatePoints.map { $0.location } assert(positions == positions.sorted()) - self.center = center - self.end = end ?? RelativeRect.full.radialEndPoint(for: center) + self.centerX = centerX + self.centerY = centerY + self.end = end self.centerColor = centerColor self.intermediatePoints = intermediatePoints self.outerColor = outerColor ?? centerColor.withAlphaComponent(0) + self.shape = .circle + } + + public enum CenterPoint: Equatable { + case relative(CGFloat) + case absolute(Int) + } + + public enum Radius: Equatable { + case relativeToBorders(RelativeToBorder) + case relativeToSize(RelativePoint) + case absolute(Int) + + public enum RelativeToBorder { + case nearestCorner + case farthestCorner + case nearestSide + case farthestSide + } + } + + public enum Shape { + case circle + case ellipse } } @@ -111,7 +160,8 @@ extension Gradient.Linear { extension Gradient.Radial { public static func ==(lhs: Gradient.Radial, rhs: Gradient.Radial) -> Bool { - lhs.center == rhs.center + lhs.centerX == rhs.centerX + && lhs.centerY == rhs.centerY && lhs.end == rhs.end && lhs.centerColor == rhs.centerColor && lhs.intermediatePoints == rhs.intermediatePoints @@ -152,7 +202,7 @@ extension Gradient.Radial: CustomDebugStringConvertible { path.append(centerColor.debugDescription) path += intermediatePoints.map { "(\($0.location):\($0.color.debugDescription)" } path.append(outerColor.debugDescription) - return "Radial c: \(center), \(path.joined(separator: ".."))" + return "Radial c: \(centerX), \(centerY), \(path.joined(separator: ".."))" } } diff --git a/Core/Base/ImagePlaceholder.swift b/Core/Base/ImagePlaceholder.swift index 3bc9cc3a..6290f3e6 100644 --- a/Core/Base/ImagePlaceholder.swift +++ b/Core/Base/ImagePlaceholder.swift @@ -2,6 +2,7 @@ import BaseUI +@frozen public enum ImagePlaceholder: Equatable, CustomDebugStringConvertible { case image(Image) case color(Color) diff --git a/Core/Base/Observer.swift b/Core/Base/Observer.swift index ee1fe9a3..213b6069 100644 --- a/Core/Base/Observer.swift +++ b/Core/Base/Observer.swift @@ -2,7 +2,6 @@ public struct Observer { public let action: (T) -> Void - @inlinable public init(action: @escaping (T) -> Void) { self.action = action } diff --git a/Core/Base/RadialGradientView.swift b/Core/Base/RadialGradientView.swift index cb0906cb..162eb3ef 100644 --- a/Core/Base/RadialGradientView.swift +++ b/Core/Base/RadialGradientView.swift @@ -37,7 +37,91 @@ public final class RadialGradientView: UIView { + [gradient.outerColor.cgColor] gradientLayer.locations = ([0] + gradient.intermediatePoints.map { $0.location } + [1]) as [NSNumber] - gradientLayer.startPoint = gradient.center.rawValue - gradientLayer.endPoint = gradient.end.rawValue + + } + + public override func layoutSubviews() { + super.layoutSubviews() + gradientLayer.startPoint = startPoint + gradientLayer.endPoint = endPoint + } + + private var startPoint: CGPoint { + let centerX: CGFloat + switch gradient.centerX { + case let .relative(x): + centerX = x + case let .absolute(x): + centerX = CGFloat(x) / bounds.width + } + let centerY: CGFloat + switch gradient.centerY { + case let .relative(y): + centerY = y + case let .absolute(y): + centerY = CGFloat(y) / bounds.height + } + return CGPoint(x: centerX, y: centerY) + } + + private var endPoint: CGPoint { + let startPoint = startPoint + let maxSize = max(bounds.width, bounds.height) + let kx = maxSize / bounds.width + let ky = maxSize / bounds.height + switch gradient.end { + case let .relativeToBorders(relativeRaduis): + let radius: CGFloat + switch relativeRaduis { + case .nearestSide: + if startPoint.x.isOnBorder || startPoint.y.isOnBorder { + return CGPoint(x: startPoint.x + .ulpOfOne, y: startPoint.y + .ulpOfOne) + } + radius = min(startPoint.x.nearestDistance / kx, startPoint.y.nearestDistance / ky) + case .nearestCorner: + if startPoint.x.isOnBorder && startPoint.y.isOnBorder { + return CGPoint(x: startPoint.x + .ulpOfOne, y: startPoint.y + .ulpOfOne) + } + radius = sqrt(pow(startPoint.x.nearestDistance / kx, 2) + pow(startPoint.y.nearestDistance / ky, 2)) + case .farthestCorner: + radius = sqrt(pow(startPoint.x.farthestDistance / kx, 2) + pow(startPoint.y.farthestDistance / ky, 2)) + case .farthestSide: + radius = max(startPoint.x.farthestDistance / kx, startPoint.y.farthestDistance / ky) + } + return CGPoint(x: startPoint.x + radius * kx, y: startPoint.y + radius * ky) + case let .relativeToSize(point): + switch gradient.shape { + case .ellipse: + return point.rawValue + case .circle: + return CGPoint(x: point.x * kx, y: point.y * ky) + } + case let .absolute(radius): + let x = CGFloat(radius) / bounds.width + startPoint.x + let y = CGFloat(radius) / bounds.height + startPoint.y + return CGPoint(x: x, y: y) + } + } +} + +fileprivate extension CGFloat { + var nearest: CGFloat { + self < 0.5 ? 0 : 1 + } + + var farthest: CGFloat { + self > 0.5 ? 0 : 1 + } + + var nearestDistance: CGFloat { + abs(self.nearest - self) + } + + var farthestDistance: CGFloat { + abs(self.farthest - self) + } + + var isOnBorder: Bool { + self == 0 || self == 1 } } diff --git a/Core/Base/Signal.swift b/Core/Base/Signal.swift index 9581311e..bb6a3027 100644 --- a/Core/Base/Signal.swift +++ b/Core/Base/Signal.swift @@ -9,7 +9,6 @@ public struct Signal { addObserver(Observer(action: action)) } - @inlinable public init(addObserver: @escaping (Observer) -> Disposable) { self.addObserver = addObserver } diff --git a/Core/BaseTiny/Tagged.swift b/Core/BaseTiny/Tagged.swift index 98cc31a1..e34b7551 100644 --- a/Core/BaseTiny/Tagged.swift +++ b/Core/BaseTiny/Tagged.swift @@ -3,7 +3,6 @@ public struct Tagged: RawRepresentable { public var rawValue: RawValue - @inlinable public init(rawValue: RawValue) { self.rawValue = rawValue } diff --git a/Core/BaseUI/FontSettings.swift b/Core/BaseUI/FontSettings.swift index 4a4764e0..60e53e1f 100644 --- a/Core/BaseUI/FontSettings.swift +++ b/Core/BaseUI/FontSettings.swift @@ -8,6 +8,7 @@ public enum FontFamily: Hashable { case YSDisplay } +@frozen public enum FontWeight: Hashable { case light case regular diff --git a/Core/CommonCore/ImageViewProtocol.swift b/Core/CommonCore/ImageViewProtocol.swift new file mode 100644 index 00000000..5da5fb5a --- /dev/null +++ b/Core/CommonCore/ImageViewProtocol.swift @@ -0,0 +1,31 @@ +// Copyright 2022 Yandex LLC. All rights reserved. + +import UIKit + +public protocol ImageViewProtocol { + var appearanceAnimation: ImageViewAnimation? { get set } + var imageRedrawingColor: Color? { get set } + var imageContentMode: ImageContentMode { get set } +} + +public struct ImageViewAnimation { + let duration: Double + let delay: Double + let startAlpha: Double + let endAlpha: Double + let options: UIView.AnimationOptions + + public init( + duration: Double, + delay: Double, + startAlpha: Double, + endAlpha: Double, + options: UIView.AnimationOptions + ) { + self.duration = duration + self.delay = delay + self.startAlpha = startAlpha + self.endAlpha = endAlpha + self.options = options + } +} diff --git a/Core/CommonCore/ObjectsReusability.swift b/Core/CommonCore/ObjectsReusability.swift index 297bf205..47487289 100644 --- a/Core/CommonCore/ObjectsReusability.swift +++ b/Core/CommonCore/ObjectsReusability.swift @@ -2,6 +2,7 @@ import Foundation +@frozen public enum ModelReusability { case orphan case hasReusableObject(Object) @@ -11,7 +12,6 @@ public struct ReuseResult { public let modelsReusability: [(Model, ModelReusability)] public let orphanObjects: [Object] - @inlinable public init(modelsReusability: [(Model, ModelReusability)], orphanObjects: [Object]) { self.modelsReusability = modelsReusability self.orphanObjects = orphanObjects diff --git a/Core/CommonCore/RemoteImageView.swift b/Core/CommonCore/RemoteImageView.swift index 061c61a8..147a3946 100644 --- a/Core/CommonCore/RemoteImageView.swift +++ b/Core/CommonCore/RemoteImageView.swift @@ -4,29 +4,7 @@ import UIKit import Base -public final class RemoteImageView: UIView { - public struct Animation { - let duration: Double - let delay: Double - let startAlpha: Double - let endAlpha: Double - let options: UIView.AnimationOptions - - public init( - duration: Double, - delay: Double, - startAlpha: Double, - endAlpha: Double, - options: UIView.AnimationOptions - ) { - self.duration = duration - self.delay = delay - self.startAlpha = startAlpha - self.endAlpha = endAlpha - self.options = options - } - } - +public final class RemoteImageView: UIView, RemoteImageViewContentProtocol { private let contentsLayer = CALayer() private lazy var clipMask: CALayer = { let mask = CALayer() @@ -34,32 +12,7 @@ public final class RemoteImageView: UIView { return mask }() - var imageRequest: Cancellable? - public var imageHolder: ImageHolder? { - didSet { - imageRequest?.cancel() - setImage(nil, animated: false) - switch imageHolder?.placeholder { - case let .color(color)?: - backgroundColor = color.systemColor - case .none, .image?: - backgroundColor = nil - } - - let newValue = imageHolder - imageRequest = imageHolder?.requestImageWithSource { [weak self] result in - guard let self = self, - newValue === self.imageHolder else { - return - } - - self.setImage(result?.0, animated: result?.1.shouldAnimate) - self.backgroundColor = nil - } - } - } - - public var appearanceAnimation: Animation? + public var appearanceAnimation: ImageViewAnimation? private var templateLayer: CALayer? @@ -94,7 +47,7 @@ public final class RemoteImageView: UIView { setNeedsLayout() } - private func setImage(_ image: UIImage?, animated: Bool?) { + public func setImage(_ image: UIImage?, animated: Bool?) { self.image = image if let appearanceAnimation = appearanceAnimation, animated == true { self.alpha = appearanceAnimation.startAlpha @@ -172,10 +125,6 @@ public final class RemoteImageView: UIView { } layer.masksToBounds = true } - - deinit { - imageRequest?.cancel() - } } extension UIImage.Orientation { diff --git a/Core/CommonCore/RemoteImageViewContainer.swift b/Core/CommonCore/RemoteImageViewContainer.swift new file mode 100644 index 00000000..c3b6ce74 --- /dev/null +++ b/Core/CommonCore/RemoteImageViewContainer.swift @@ -0,0 +1,54 @@ +// Copyright 2015 Yandex LLC. All rights reserved. + +import UIKit + +import Base + +public final class RemoteImageViewContainer: UIView { + public var contentView: RemoteImageViewContentProtocol + + var imageRequest: Cancellable? + public var imageHolder: ImageHolder? { + didSet { + imageRequest?.cancel() + contentView.setImage(nil, animated: false) + switch imageHolder?.placeholder { + case let .color(color)?: + backgroundColor = color.systemColor + case .none, .image?: + backgroundColor = nil + } + + let newValue = imageHolder + imageRequest = imageHolder?.requestImageWithSource { [weak self] result in + guard let self = self, + newValue === self.imageHolder else { + return + } + + self.contentView.setImage(result?.0, animated: result?.1.shouldAnimate) + self.backgroundColor = nil + } + } + } + + public init(contentView: RemoteImageViewContentProtocol) { + self.contentView = contentView + super.init(frame: .zero) + addSubview(contentView) + } + + @available(*, unavailable) + required init?(coder _: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + public override func layoutSubviews() { + super.layoutSubviews() + contentView.frame = bounds + } + + deinit { + imageRequest?.cancel() + } +} diff --git a/Core/CommonCore/RemoteImageViewContentProtocol.swift b/Core/CommonCore/RemoteImageViewContentProtocol.swift new file mode 100644 index 00000000..fbd74aec --- /dev/null +++ b/Core/CommonCore/RemoteImageViewContentProtocol.swift @@ -0,0 +1,8 @@ +// Copyright 2022 Yandex LLC. All rights reserved. + +import Foundation +import UIKit + +public protocol RemoteImageViewContentProtocol: UIView, ImageViewProtocol { + func setImage(_ image: UIImage?, animated: Bool?) +} diff --git a/Core/CommonCore/Theme.swift b/Core/CommonCore/Theme.swift index d1f38a10..97a725c5 100644 --- a/Core/CommonCore/Theme.swift +++ b/Core/CommonCore/Theme.swift @@ -2,6 +2,7 @@ import BaseUI +@frozen public enum Theme: String { case dark case light diff --git a/DivKit/DivBlockModelingContext.swift b/DivKit/DivBlockModelingContext.swift index b7271cac..9535bc73 100644 --- a/DivKit/DivBlockModelingContext.swift +++ b/DivKit/DivBlockModelingContext.swift @@ -15,6 +15,7 @@ public struct DivBlockModelingContext { public let visibilityCounter: DivVisibilityCounting public var galleryResizableInsets: InsetMode.Resizable? public let imageHolderFactory: ImageHolderFactory + public let highPriorityImageHolderFactory: ImageHolderFactory? public let divCustomBlockFactory: DivCustomBlockFactory public let fontSpecifiers: FontSpecifiers public let flagsInfo: DivFlagsInfo @@ -25,6 +26,8 @@ public struct DivBlockModelingContext { public var childrenA11yDescription: String? public weak var parentScrollView: ScrollView? let expressionErrorsStorage = ExpressionErrorsStorage() + var overridenWidth: DivOverridenSize? = nil + var overridenHeight: DivOverridenSize? = nil public init( cardId: DivCardID, @@ -36,6 +39,7 @@ public struct DivBlockModelingContext { visibilityCounter: DivVisibilityCounting = DivVisibilityCounter(), galleryResizableInsets: InsetMode.Resizable? = nil, imageHolderFactory: ImageHolderFactory, + highPriorityImageHolderFactory: ImageHolderFactory? = nil, divCustomBlockFactory: DivCustomBlockFactory = EmptyDivCustomBlockFactory(), fontSpecifiers: FontSpecifiers = BaseUI.fontSpecifiers, flagsInfo: DivFlagsInfo = .default, @@ -55,6 +59,7 @@ public struct DivBlockModelingContext { self.visibilityCounter = visibilityCounter self.galleryResizableInsets = galleryResizableInsets self.imageHolderFactory = imageHolderFactory + self.highPriorityImageHolderFactory = highPriorityImageHolderFactory self.divCustomBlockFactory = divCustomBlockFactory self.flagsInfo = flagsInfo self.fontSpecifiers = fontSpecifiers @@ -113,4 +118,24 @@ public struct DivBlockModelingContext { public func getStateInterceptor(for divState: DivState) -> DivStateInterceptor? { divState.extensions?.compactMap { stateInterceptors[$0.id] }.first } + + func override(width: DivSize) -> DivSize { + guard let overridenWidth = overridenWidth else { + return width + } + if overridenWidth.original == width { + return overridenWidth.overriden + } + return width + } + + func override(height: DivSize) -> DivSize { + guard let overridenHeight = overridenHeight else { + return height + } + if overridenHeight.original == height { + return overridenHeight.overriden + } + return height + } } diff --git a/DivKit/DivKitInfo.swift b/DivKit/DivKitInfo.swift index b520da23..dd8589b4 100644 --- a/DivKit/DivKitInfo.swift +++ b/DivKit/DivKitInfo.swift @@ -1,3 +1,3 @@ public enum DivKitInfo { - public static let version = "10.0.0" + public static let version = "11.0.0" } diff --git a/DivKit/DivOverridenSize.swift b/DivKit/DivOverridenSize.swift new file mode 100644 index 00000000..c4a3dbd9 --- /dev/null +++ b/DivKit/DivOverridenSize.swift @@ -0,0 +1,11 @@ +import Foundation + +struct DivOverridenSize { + public let original: DivSize + public let overriden: DivSize + + public init(original: DivSize, overriden: DivSize) { + self.original = original + self.overriden = overriden + } +} diff --git a/DivKit/Expressions/Expression.swift b/DivKit/Expressions/Expression.swift index 09ec8d59..65835a47 100644 --- a/DivKit/Expressions/Expression.swift +++ b/DivKit/Expressions/Expression.swift @@ -1,5 +1,6 @@ import Foundation +@frozen public enum Expression { case value(T) case link(ExpressionLink) diff --git a/DivKit/Extensions/ArrayExtensions.swift b/DivKit/Extensions/ArrayExtensions.swift index 6f3d9466..84964ce3 100644 --- a/DivKit/Extensions/ArrayExtensions.swift +++ b/DivKit/Extensions/ArrayExtensions.swift @@ -4,10 +4,16 @@ import LayoutKit extension Array where Element == Div { func makeBlocks( context: DivBlockModelingContext, + overridenWidth: DivOverridenSize? = nil, + overridenHeight: DivOverridenSize? = nil, mappedBy modificator: (Div, Block) throws -> T ) throws -> [T] { try iterativeFlatMap { div, index in - let itemContext = modified(context) { $0.parentPath += index } + let itemContext = modified(context) { + $0.parentPath += index + $0.overridenWidth = overridenWidth + $0.overridenHeight = overridenHeight + } let block: Block do { block = try div.value.makeBlock(context: itemContext) diff --git a/DivKit/Extensions/DivBackgroundExtensions.swift b/DivKit/Extensions/DivBackgroundExtensions.swift index 5008fb25..b34c8ddb 100644 --- a/DivKit/Extensions/DivBackgroundExtensions.swift +++ b/DivKit/Extensions/DivBackgroundExtensions.swift @@ -21,9 +21,17 @@ extension DivBackground { ) ) ) - case .divRadialGradient: - DivKitLogger.error("DivRadialGradient not supported") - return nil + case let .divRadialGradient(gradient): + return .gradient( + .radial( + Gradient.Radial( + colors: gradient.resolveColors(expressionResolver) ?? [], + end: gradient.resolveRadius(expressionResolver), + centerX: gradient.resolveCenterX(expressionResolver), + centerY: gradient.resolveCenterY(expressionResolver) + ) + ) + ) case let .divImageBackground(imageBackground): let image = BackgroundImage( imageHolder: imageHolderFactory.make( @@ -72,3 +80,66 @@ extension Gradient.Linear.Direction { } extension DivImageBackground: DivImageContentMode {} + +extension Gradient.Radial { + fileprivate init( + colors: [Color], + end: Gradient.Radial.Radius, + centerX: Gradient.Radial.CenterPoint, + centerY: Gradient.Radial.CenterPoint + ) { + var intermediateColors = colors + let centerColor = intermediateColors.removeFirst() + let outerColor = intermediateColors.removeLast() + let step = 1 / CGFloat(intermediateColors.count + 1) + let points = intermediateColors.enumerated().map { + (color: $0.element, location: CGFloat($0.offset + 1) * step) + } + self.init( + centerX: centerX, + centerY: centerY, + end: end, + centerColor: centerColor, + intermediatePoints: points, + outerColor: outerColor + ) + } +} + +extension DivRadialGradient { + fileprivate func resolveRadius(_ resolver: ExpressionResolver) -> Gradient.Radial.Radius { + switch radius { + case let .divFixedSize(size): + return .absolute(size.resolveValue(resolver) ?? 0) + case let .divRadialGradientRelativeRadius(radius): + switch radius.resolveValue(resolver) ?? .farthestCorner { + case .farthestCorner: + return .relativeToBorders(.farthestCorner) + case .farthestSide: + return .relativeToBorders(.farthestSide) + case .nearestCorner: + return .relativeToBorders(.nearestCorner) + case .nearestSide: + return .relativeToBorders(.nearestSide) + } + } + } + + fileprivate func resolveCenterX(_ resolver: ExpressionResolver) -> Gradient.Radial.CenterPoint { + switch (centerX) { + case let (.divRadialGradientRelativeCenter(x)): + return .relative(x.resolveValue(resolver) ?? 0) + case let (.divRadialGradientFixedCenter(x)): + return .absolute(x.resolveValue(resolver) ?? 0) + } + } + + fileprivate func resolveCenterY(_ resolver: ExpressionResolver) -> Gradient.Radial.CenterPoint { + switch (centerY) { + case let (.divRadialGradientRelativeCenter(y)): + return .relative(y.resolveValue(resolver) ?? 0) + case let (.divRadialGradientFixedCenter(y)): + return .absolute(y.resolveValue(resolver) ?? 0) + } + } +} diff --git a/DivKit/Extensions/DivBase/DivBaseExtensions.swift b/DivKit/Extensions/DivBase/DivBaseExtensions.swift index 60f34347..3dad02eb 100644 --- a/DivKit/Extensions/DivBase/DivBaseExtensions.swift +++ b/DivKit/Extensions/DivBase/DivBaseExtensions.swift @@ -22,7 +22,7 @@ extension DivBase { let visibility = resolveVisibility(expressionResolver) if visibility == .gone { context.stateManager.setBlockVisibility(statePath: statePath, div: self, isVisible: false) - return SeparatorBlock(color: .clear, size: 0) + return EmptyBlock.zeroSized } var block = try block() @@ -272,15 +272,17 @@ extension DivBase { } extension DivBase { - func makeContentWidthTrait(with expressionResolver: ExpressionResolver) -> LayoutTrait { - width.makeLayoutTrait(with: expressionResolver).contentTrait( - consideringInsets: paddings.makeEdgeInsets(with: expressionResolver).horizontalInsets + func makeContentWidthTrait(with context: DivBlockModelingContext) -> LayoutTrait { + let overridenWidth = context.override(width: width) + return overridenWidth.makeLayoutTrait(with: context.expressionResolver).contentTrait( + consideringInsets: paddings.makeEdgeInsets(with: context.expressionResolver).horizontalInsets ) } - func makeContentHeightTrait(with expressionResolver: ExpressionResolver) -> LayoutTrait { - height.makeLayoutTrait(with: expressionResolver).contentTrait( - consideringInsets: paddings.makeEdgeInsets(with: expressionResolver).verticalInsets + func makeContentHeightTrait(with context: DivBlockModelingContext) -> LayoutTrait { + let overridenHeight = context.override(height: height) + return overridenHeight.makeLayoutTrait(with: context.expressionResolver).contentTrait( + consideringInsets: paddings.makeEdgeInsets(with: context.expressionResolver).verticalInsets ) } } diff --git a/DivKit/Extensions/DivContainerExtensions.swift b/DivKit/Extensions/DivContainerExtensions.swift index 01720db6..63cbcbba 100644 --- a/DivKit/Extensions/DivContainerExtensions.swift +++ b/DivKit/Extensions/DivContainerExtensions.swift @@ -42,31 +42,25 @@ extension DivContainer: DivBlockModeling { private func checkConstraints( for children: [Block], - path: UIElementPath, - orientation: Orientation, - layoutMode: LayoutMode = .noWrap + path: UIElementPath ) throws { guard !children.isEmpty else { throw DivBlockModelingError("DivContainer is empty", path: path) } + } + private func getFallbackWidth( + orientation: Orientation, + layoutMode: LayoutMode = .noWrap + ) -> DivOverridenSize? { if layoutMode == .wrap { switch orientation { - case .horizontal: - if children.hasVerticallyResizable { - throw DivBlockModelingError( - "Horizontal DivContainer with wrap layout mode contains item with match_parent height", - path: path - ) - } case .vertical: - if children.hasHorizontallyResizable { - throw DivBlockModelingError( - "Vertical DivContainer with wrap layout mode contains item with match_parent width", - path: path - ) + if items.hasHorizontallyMatchParent { + DivKitLogger.error("Vertical DivContainer with wrap layout mode contains item with match_parent width") + return defaultFallbackSize } - case .overlap: + case .horizontal, .overlap: break } } @@ -74,40 +68,52 @@ extension DivContainer: DivBlockModeling { if width.isIntrinsic { switch orientation { case .horizontal: - if children.hasHorizontallyResizable { - throw DivBlockModelingError( - "Horizontal DivContainer with wrap_content width contains item with match_parent width", - path: path - ) + if items.hasHorizontallyMatchParent { + DivKitLogger.error("Horizontal DivContainer with wrap_content width contains item with match_parent width") + return defaultFallbackSize } case .vertical, .overlap: - if children.allHorizontallyResizable { - throw DivBlockModelingError( - "All items in DivContainer with wrap_content width has match_parent width", - path: path - ) + if items.allHorizontallyMatchParent { + DivKitLogger.error("All items in DivContainer with wrap_content width has match_parent width") + return defaultFallbackSize + } + } + } + + return nil + } + + private func getFallbackHeight( + orientation: Orientation, + layoutMode: LayoutMode = .noWrap + ) -> DivOverridenSize? { + if layoutMode == .wrap { + switch orientation { + case .horizontal: + if items.hasVerticallyMatchParent { + DivKitLogger.error("Horizontal DivContainer with wrap layout mode contains item with match_parent height") + return defaultFallbackSize } + case .vertical, .overlap: + break } } if height.isIntrinsic { switch orientation { case .horizontal, .overlap: - if children.allVerticallyResizable { - throw DivBlockModelingError( - "All items in DivContainer with wrap_content height has match_parent height", - path: path - ) + if items.allVerticallyMatchParent { + DivKitLogger.error("All items in DivContainer with wrap_content height has match_parent height") + return defaultFallbackSize } case .vertical: - if children.hasVerticallyResizable { - throw DivBlockModelingError( - "Vertical DivContainer with wrap_content height contains item with match_parent height", - path: path - ) + if items.hasVerticallyMatchParent { + DivKitLogger.error("Vertical DivContainer with wrap_content height contains item with match_parent height") + return defaultFallbackSize } } } + return nil } private func makeLayeredBlock( @@ -119,8 +125,14 @@ extension DivContainer: DivBlockModeling { .alignment, vertical: resolveContentAlignmentVertical(childContext.expressionResolver).alignment ) + + let fallbackWidth = getFallbackWidth(orientation: orientation) + let fallbackHeight = getFallbackHeight(orientation: orientation) + let children = try items.makeBlocks( context: childContext, + overridenWidth: fallbackWidth, + overridenHeight: fallbackHeight, mappedBy: { div, block in LayeredBlock.Child( content: block, @@ -134,13 +146,12 @@ extension DivContainer: DivBlockModeling { try checkConstraints( for: children.map { $0.content }, - path: childContext.parentPath, - orientation: orientation + path: childContext.parentPath ) return LayeredBlock( - widthTrait: makeContentWidthTrait(with: childContext.expressionResolver), - heightTrait: makeContentHeightTrait(with: childContext.expressionResolver), + widthTrait: makeContentWidthTrait(with: childContext), + heightTrait: makeContentHeightTrait(with: childContext), children: children ) } @@ -170,8 +181,13 @@ extension DivContainer: DivBlockModeling { .alignment } + let fallbackWidth = getFallbackWidth(orientation: orientation, layoutMode: layoutMode) + let fallbackHeight = getFallbackHeight(orientation: orientation, layoutMode: layoutMode) + let children = try items.makeBlocks( context: childContext, + overridenWidth: fallbackWidth, + overridenHeight: fallbackHeight, mappedBy: { div, block in ContainerBlock.Child( content: block, @@ -185,16 +201,14 @@ extension DivContainer: DivBlockModeling { try checkConstraints( for: children.map { $0.content }, - path: childContext.parentPath, - orientation: orientation, - layoutMode: layoutMode + path: childContext.parentPath ) return try ContainerBlock( layoutDirection: layoutDirection, layoutMode: layoutMode.system, - widthTrait: makeContentWidthTrait(with: childContext.expressionResolver), - heightTrait: makeContentHeightTrait(with: childContext.expressionResolver), + widthTrait: makeContentWidthTrait(with: childContext), + heightTrait: makeContentHeightTrait(with: childContext), axialAlignment: axialAlignment, children: children ) @@ -282,3 +296,8 @@ extension Div { } } } + +private let defaultFallbackSize = DivOverridenSize( + original: .divMatchParentSize(DivMatchParentSize()), + overriden: .divWrapContentSize(DivWrapContentSize(constrained: .value(true))) +) diff --git a/DivKit/Extensions/DivCustom/DivCustomBlockFactory.swift b/DivKit/Extensions/DivCustom/DivCustomBlockFactory.swift index 64b43d1c..aa93c06d 100644 --- a/DivKit/Extensions/DivCustom/DivCustomBlockFactory.swift +++ b/DivKit/Extensions/DivCustom/DivCustomBlockFactory.swift @@ -9,6 +9,6 @@ public struct EmptyDivCustomBlockFactory: DivCustomBlockFactory { public func makeBlock(data: DivCustomData, context _: DivBlockModelingContext) -> Block { DivKitLogger.error("No block factory for DivCustom: \(data.name)") - return SeparatorBlock() + return EmptyBlock.zeroSized } } diff --git a/DivKit/Extensions/DivCustom/DivCustomExtensions.swift b/DivKit/Extensions/DivCustom/DivCustomExtensions.swift index 5ea59745..93b9fa4e 100644 --- a/DivKit/Extensions/DivCustom/DivCustomExtensions.swift +++ b/DivKit/Extensions/DivCustom/DivCustomExtensions.swift @@ -24,10 +24,10 @@ extension DivCustom: DivBlockModeling { data: customProps ?? [:], children: children ) - let contentHeightTrait = makeContentHeightTrait(with: context.expressionResolver) + let contentHeightTrait = makeContentHeightTrait(with: context) return try ContainerBlock( layoutDirection: contentHeightTrait.isResizable ? .vertical : .horizontal, - widthTrait: makeContentWidthTrait(with: context.expressionResolver), + widthTrait: makeContentWidthTrait(with: context), heightTrait: contentHeightTrait, children: [ context.divCustomBlockFactory.makeBlock(data: customData, context: context), diff --git a/DivKit/Extensions/DivData/DivDataExtensions.swift b/DivKit/Extensions/DivData/DivDataExtensions.swift index 9c172fcc..1ffc6d87 100644 --- a/DivKit/Extensions/DivData/DivDataExtensions.swift +++ b/DivKit/Extensions/DivData/DivDataExtensions.swift @@ -80,19 +80,3 @@ extension DivData { return result } } - -extension Div { - var children: [Div] { - switch self { - case let .divContainer(div): return div.items - case let .divGrid(div): return div.items - case let .divGallery(div): return div.items - case let .divPager(div): return div.items - case let .divTabs(div): return div.items.map(\.div) - case let .divCustom(div): return div.items ?? [] - case let .divState(div): return div.states.compactMap(\.div) - case .divImage, .divGifImage, .divText, .divSlider, .divIndicator, .divSeparator, .divInput: - return [] - } - } -} diff --git a/DivKit/Extensions/DivExtensions.swift b/DivKit/Extensions/DivExtensions.swift new file mode 100644 index 00000000..661991d7 --- /dev/null +++ b/DivKit/Extensions/DivExtensions.swift @@ -0,0 +1,55 @@ +import Foundation + +extension Div { + var children: [Div] { + switch self { + case let .divContainer(div): return div.items + case let .divGrid(div): return div.items + case let .divGallery(div): return div.items + case let .divPager(div): return div.items + case let .divTabs(div): return div.items.map(\.div) + case let .divCustom(div): return div.items ?? [] + case let .divState(div): return div.states.compactMap(\.div) + case .divImage, .divGifImage, .divText, .divSlider, .divIndicator, .divSeparator, .divInput: + return [] + } + } +} + +extension Div { + var isHorizontallyMatchParent: Bool { + switch value.width { + case .divMatchParentSize: + return true + case .divFixedSize, .divWrapContentSize: + return false + } + } + + var isVerticallyMatchParent: Bool { + switch value.height { + case .divMatchParentSize: + return true + case .divFixedSize, .divWrapContentSize: + return false + } + } +} + +extension Sequence where Element == Div { + var hasHorizontallyMatchParent: Bool { + contains { $0.isHorizontallyMatchParent } + } + + var allHorizontallyMatchParent: Bool { + allSatisfy { $0.isHorizontallyMatchParent } + } + + var hasVerticallyMatchParent: Bool { + contains { $0.isVerticallyMatchParent } + } + + var allVerticallyMatchParent: Bool { + allSatisfy { $0.isVerticallyMatchParent } + } +} diff --git a/DivKit/Extensions/DivGalleryExtensions.swift b/DivKit/Extensions/DivGalleryExtensions.swift index d87c3fd7..b3203c56 100644 --- a/DivKit/Extensions/DivGalleryExtensions.swift +++ b/DivKit/Extensions/DivGalleryExtensions.swift @@ -36,6 +36,9 @@ extension DivGallery: DivBlockModeling, DivGalleryProtocol { columnCount: resolveColumnCount(expressionResolver) ) + let width = context.override(width: width) + let height = context.override(height: height) + return try GalleryBlock( model: model, state: getState(context: galleryContext, itemsCount: model.items.count), diff --git a/DivKit/Extensions/DivGalleryProtocol.swift b/DivKit/Extensions/DivGalleryProtocol.swift index 8c44e35a..1134485f 100644 --- a/DivKit/Extensions/DivGalleryProtocol.swift +++ b/DivKit/Extensions/DivGalleryProtocol.swift @@ -19,8 +19,12 @@ extension DivGalleryProtocol { columnCount: Int? = nil ) throws -> GalleryViewModel { let expressionResolver = context.expressionResolver + let fallbackWidth = getFallbackWidth(direction: direction) + let fallbackHeight = getFallbackHeight(direction: direction) let children: [GalleryViewModel.Item] = try items.makeBlocks( context: context, + overridenWidth: fallbackWidth, + overridenHeight: fallbackHeight, mappedBy: { GalleryViewModel.Item( crossAlignment: ( @@ -35,7 +39,6 @@ extension DivGalleryProtocol { try checkLayoutConstraints( children, - direction: direction, path: context.parentPath ) @@ -90,9 +93,43 @@ extension DivGalleryProtocol { ) } + private func getFallbackWidth( + direction: GalleryViewModel.Direction + ) -> DivOverridenSize? { + if width.isIntrinsic { + switch direction { + case .vertical: + if items.allHorizontallyMatchParent { + DivKitLogger.error("All items in vertical \(typeName) with wrap_content width has match_parent width") + return defaultFallbackSize + } + case .horizontal: + break + } + } + + return nil + } + + private func getFallbackHeight( + direction: GalleryViewModel.Direction + ) -> DivOverridenSize? { + if height.isIntrinsic { + switch direction { + case .horizontal: + if items.allVerticallyMatchParent { + DivKitLogger.error("All items in horizontal \(typeName) with wrap_content height has match_parent height") + return defaultFallbackSize + } + case .vertical: + break + } + } + return nil + } + private func checkLayoutConstraints( _ children: [GalleryViewModel.Item], - direction: GalleryViewModel.Direction, path: UIElementPath ) throws { guard !children.isEmpty else { @@ -101,24 +138,6 @@ extension DivGalleryProtocol { path: path ) } - - let blocks = children.map { $0.content } - switch direction { - case .horizontal: - if height.isIntrinsic, blocks.allVerticallyResizable { - throw DivBlockModelingError( - "All items in horizontal \(typeName) with wrap_content height has match_parent height", - path: path - ) - } - case .vertical: - if width.isIntrinsic, blocks.allHorizontallyResizable { - throw DivBlockModelingError( - "All items in vertical \(typeName) with wrap_content width has match_parent width", - path: path - ) - } - } } private var typeName: String { @@ -128,3 +147,8 @@ extension DivGalleryProtocol { return String(typeName) } } + +private let defaultFallbackSize = DivOverridenSize( + original: .divMatchParentSize(DivMatchParentSize()), + overriden: .divWrapContentSize(DivWrapContentSize(constrained: .value(true))) +) diff --git a/DivKit/Extensions/DivGridExtensions.swift b/DivKit/Extensions/DivGridExtensions.swift index 3df4070a..67b2398d 100644 --- a/DivKit/Extensions/DivGridExtensions.swift +++ b/DivKit/Extensions/DivGridExtensions.swift @@ -23,8 +23,8 @@ extension DivGrid: DivBlockModeling { } let expressionResolver = context.expressionResolver return try GridBlock( - widthTrait: makeContentWidthTrait(with: expressionResolver), - heightTrait: makeContentHeightTrait(with: expressionResolver), + widthTrait: makeContentWidthTrait(with: context), + heightTrait: makeContentHeightTrait(with: context), contentAlignment: contentAlignment(with: expressionResolver), items: gridItems, columnCount: resolveColumnCount(expressionResolver) ?? 0, diff --git a/DivKit/Extensions/DivImage/DivGifImageExtensions.swift b/DivKit/Extensions/DivImage/DivGifImageExtensions.swift index 2aa2f83a..c0e44637 100644 --- a/DivKit/Extensions/DivImage/DivGifImageExtensions.swift +++ b/DivKit/Extensions/DivImage/DivGifImageExtensions.swift @@ -25,8 +25,8 @@ extension DivGifImage: DivBlockModeling, DivImageProtocol { ) return AnimatableImageBlock( imageHolder: imageHolder, - widthTrait: makeContentWidthTrait(with: expressionResolver), - height: resolveHeight(expressionResolver), + widthTrait: makeContentWidthTrait(with: context), + height: resolveHeight(context), contentMode: resolveContentMode(expressionResolver) ) } diff --git a/DivKit/Extensions/DivImage/DivImageExtensions.swift b/DivKit/Extensions/DivImage/DivImageExtensions.swift index 77ec4cd7..70b29f93 100644 --- a/DivKit/Extensions/DivImage/DivImageExtensions.swift +++ b/DivKit/Extensions/DivImage/DivImageExtensions.swift @@ -1,6 +1,7 @@ import Foundation import CommonCore +import Networking import LayoutKit extension DivImage: DivBlockModeling, DivImageProtocol { @@ -18,15 +19,24 @@ extension DivImage: DivBlockModeling, DivImageProtocol { private func makeBaseBlock(context: DivBlockModelingContext) throws -> Block { try checkLayoutTraits(context: context) + let expressionResolver = context.expressionResolver - let imageHolder = context.imageHolderFactory.make( + let highPriority = resolveHighPriorityPreviewShow(expressionResolver) + let imageHolderFactory: ImageHolderFactory + if highPriority, let highPriorityImageHolderFactory = context.highPriorityImageHolderFactory { + imageHolderFactory = highPriorityImageHolderFactory + } else { + imageHolderFactory = context.imageHolderFactory + } + + let imageHolder = imageHolderFactory.make( resolveImageUrl(expressionResolver), resolvePlaceholder(expressionResolver) ) return ImageBlock( imageHolder: imageHolder, - widthTrait: makeContentWidthTrait(with: expressionResolver), - height: resolveHeight(expressionResolver), + widthTrait: makeContentWidthTrait(with: context), + height: resolveHeight(context), contentMode: resolveContentMode(expressionResolver), tintColor: resolveTintColor(expressionResolver), appearanceAnimation: appearanceAnimation?.makeAppearanceAnimation(with: expressionResolver) diff --git a/DivKit/Extensions/DivImage/DivImageProtocol.swift b/DivKit/Extensions/DivImage/DivImageProtocol.swift index 0fce6849..4a413366 100644 --- a/DivKit/Extensions/DivImage/DivImageProtocol.swift +++ b/DivKit/Extensions/DivImage/DivImageProtocol.swift @@ -11,11 +11,11 @@ protocol DivImageProtocol: DivBase, DivImageContentMode { } extension DivImageProtocol { - func resolveHeight(_ expressionResolver: ExpressionResolver) -> ImageBlockHeight { + func resolveHeight(_ context: DivBlockModelingContext) -> ImageBlockHeight { if let aspect = aspect { - return .ratio(aspect.resolveRatio(expressionResolver) ?? 0) + return .ratio(aspect.resolveRatio(context.expressionResolver) ?? 0) } - return .trait(makeContentHeightTrait(with: expressionResolver)) + return .trait(makeContentHeightTrait(with: context)) } func resolvePlaceholder(_ expressionResolver: ExpressionResolver) -> ImagePlaceholder { diff --git a/DivKit/Extensions/DivIndicatorExtensions.swift b/DivKit/Extensions/DivIndicatorExtensions.swift index 80cb33c0..2dc1eeb0 100644 --- a/DivKit/Extensions/DivIndicatorExtensions.swift +++ b/DivKit/Extensions/DivIndicatorExtensions.swift @@ -51,8 +51,8 @@ extension DivIndicator: DivBlockModeling { return PageControlBlock( pagerPath: pagerPath, - widthTrait: makeContentWidthTrait(with: expressionResolver), - heightTrait: makeContentHeightTrait(with: expressionResolver), + widthTrait: makeContentWidthTrait(with: context), + heightTrait: makeContentHeightTrait(with: context), configuration: configuration, state: state ) diff --git a/DivKit/Extensions/DivInputExtensions.swift b/DivKit/Extensions/DivInputExtensions.swift index 2ca7e39e..161788b2 100644 --- a/DivKit/Extensions/DivInputExtensions.swift +++ b/DivKit/Extensions/DivInputExtensions.swift @@ -61,8 +61,8 @@ extension DivInput: DivBlockModeling { } return TextInputBlock( - widthTrait: makeContentWidthTrait(with: context.expressionResolver), - heightTrait: makeContentHeightTrait(with: context.expressionResolver), + widthTrait: makeContentWidthTrait(with: context), + heightTrait: makeContentHeightTrait(with: context), hint: hintValue.with(typo: hintTypo), textValue: textValue, textTypo: textTypo, diff --git a/DivKit/Extensions/DivPagerExtensions.swift b/DivKit/Extensions/DivPagerExtensions.swift index a8959220..02c178f3 100644 --- a/DivKit/Extensions/DivPagerExtensions.swift +++ b/DivKit/Extensions/DivPagerExtensions.swift @@ -38,6 +38,8 @@ extension DivPager: DivBlockModeling, DivGalleryProtocol { resizableInsets: context.galleryResizableInsets, scrollMode: .autoPaging ) + let width = context.override(width: width) + let height = context.override(height: height) return try PagerBlock( pagerPath: pagerModelPath, layoutMode: layoutMode.cast(with: expressionResolver), diff --git a/DivKit/Extensions/DivSeparatorExtensions.swift b/DivKit/Extensions/DivSeparatorExtensions.swift index c13f1d8e..2da7acbe 100644 --- a/DivKit/Extensions/DivSeparatorExtensions.swift +++ b/DivKit/Extensions/DivSeparatorExtensions.swift @@ -15,8 +15,8 @@ extension DivSeparator: DivBlockModeling { } private func makeBaseBlock(context: DivBlockModelingContext) throws -> Block { - let widthTrait = makeContentWidthTrait(with: context.expressionResolver) - let heightTrait = makeContentHeightTrait(with: context.expressionResolver) + let widthTrait = makeContentWidthTrait(with: context) + let heightTrait = makeContentHeightTrait(with: context) let needsBeWrappedInContainer: Bool let trait: LayoutTrait diff --git a/DivKit/Extensions/DivSizeExtensions.swift b/DivKit/Extensions/DivSizeExtensions.swift index 75d9c90d..b2340069 100644 --- a/DivKit/Extensions/DivSizeExtensions.swift +++ b/DivKit/Extensions/DivSizeExtensions.swift @@ -31,3 +31,54 @@ extension DivSize { } } } + +#if !DEBUG +extension DivSize: Equatable { + public static func ==(lhs: DivSize, rhs: DivSize) -> Bool { + switch (lhs, rhs) { + case let (.divFixedSize(l), .divFixedSize(r)): + return l == r + case let (.divMatchParentSize(l), .divMatchParentSize(r)): + return l == r + case let (.divWrapContentSize(l), .divWrapContentSize(r)): + return l == r + default: + return false + } + } +} + +extension DivFixedSize: Equatable { + public static func ==(lhs: DivFixedSize, rhs: DivFixedSize) -> Bool { + guard + lhs.unit == rhs.unit, + lhs.value == rhs.value + else { + return false + } + return true + } +} + +extension DivMatchParentSize: Equatable { + public static func ==(lhs: DivMatchParentSize, rhs: DivMatchParentSize) -> Bool { + guard + lhs.weight == rhs.weight + else { + return false + } + return true + } +} + +extension DivWrapContentSize: Equatable { + public static func ==(lhs: DivWrapContentSize, rhs: DivWrapContentSize) -> Bool { + guard + lhs.constrained == rhs.constrained + else { + return false + } + return true + } +} +#endif diff --git a/DivKit/Extensions/DivSliderExtensions.swift b/DivKit/Extensions/DivSliderExtensions.swift index 38b88a28..c6886765 100644 --- a/DivKit/Extensions/DivSliderExtensions.swift +++ b/DivKit/Extensions/DivSliderExtensions.swift @@ -80,7 +80,7 @@ extension DivSlider: DivBlockModeling { secondThumb: secondThumb, activeMarkModel: tickMarkActiveStyle.flatMap { SliderModel.MarkModel( - block: (try? $0.makeBlock(context: context, corners: .all)) ?? SeparatorBlock(), + block: (try? $0.makeBlock(context: context, corners: .all)) ?? EmptyBlock.zeroSized, size: CGSize( width: $0.getWidth(context: context), height: $0.getHeight(context: context) @@ -89,7 +89,7 @@ extension DivSlider: DivBlockModeling { }, inactiveMarkModel: tickMarkInactiveStyle.flatMap { SliderModel.MarkModel( - block: (try? $0.makeBlock(context: context, corners: .all)) ?? SeparatorBlock(), + block: (try? $0.makeBlock(context: context, corners: .all)) ?? EmptyBlock.zeroSized, size: CGSize( width: $0.getWidth(context: context), height: $0.getHeight(context: context) @@ -102,13 +102,15 @@ extension DivSlider: DivBlockModeling { context: context, widthTrait: .resizable, corners: .all - )) ?? SeparatorBlock(), + )) ?? EmptyBlock.zeroSized, inactiveTrack: (try? self.trackInactiveStyle.makeBlock( context: context, widthTrait: .resizable, corners: .all - )) ?? SeparatorBlock() + )) ?? EmptyBlock.zeroSized ) + let width = context.override(width: width) + let height = context.override(height: height) return SliderBlock( sliderModel: sliderModel, widthTrait: width.makeLayoutTrait(with: context.expressionResolver), diff --git a/DivKit/Extensions/DivStateExtensions.swift b/DivKit/Extensions/DivStateExtensions.swift index 9216d0de..4f0b4bab 100644 --- a/DivKit/Extensions/DivStateExtensions.swift +++ b/DivKit/Extensions/DivStateExtensions.swift @@ -86,8 +86,8 @@ extension DivState: DivBlockModeling { defaultStateAlignment return LayeredBlock( - widthTrait: makeContentWidthTrait(with: expressionResolver), - heightTrait: makeContentHeightTrait(with: expressionResolver), + widthTrait: makeContentWidthTrait(with: context), + heightTrait: makeContentHeightTrait(with: context), children: [ LayeredBlock.Child( content: TransitioningBlock( @@ -135,7 +135,7 @@ extension DivState: DivBlockModeling { } } -private let stub: Block = SeparatorBlock(size: .zero) +private let stub: Block = EmptyBlock.zeroSized extension DivBlockModelingContext { fileprivate func makeContextForState( diff --git a/DivKit/Extensions/DivTabsExtensions.swift b/DivKit/Extensions/DivTabsExtensions.swift index 5ef6be8f..25f2bff1 100644 --- a/DivKit/Extensions/DivTabsExtensions.swift +++ b/DivKit/Extensions/DivTabsExtensions.swift @@ -61,8 +61,8 @@ extension DivTabs: DivBlockModeling { separatorStyle: makeSeparatorStyle(with: context.expressionResolver) ), state: makeState(context: tabsContext, tabs: tabs), - widthTrait: makeContentWidthTrait(with: context.expressionResolver), - heightTrait: makeContentHeightTrait(with: context.expressionResolver) + widthTrait: makeContentWidthTrait(with: context), + heightTrait: makeContentHeightTrait(with: context) ) } diff --git a/DivKit/Extensions/DivTextExtensions.swift b/DivKit/Extensions/DivTextExtensions.swift index 62818021..7ff8edc9 100644 --- a/DivKit/Extensions/DivTextExtensions.swift +++ b/DivKit/Extensions/DivTextExtensions.swift @@ -88,8 +88,8 @@ extension DivText: DivBlockModeling { } return TextBlock( - widthTrait: makeContentWidthTrait(with: context.expressionResolver), - heightTrait: makeContentHeightTrait(with: context.expressionResolver), + widthTrait: makeContentWidthTrait(with: context), + heightTrait: makeContentHeightTrait(with: context), text: attributedString, verticalAlignment: resolveTextAlignmentVertical(context.expressionResolver).alignment, diff --git a/DivKit/Variables/DivVariablesStorage.swift b/DivKit/Variables/DivVariablesStorage.swift index 7dd05532..ee6566bd 100644 --- a/DivKit/Variables/DivVariablesStorage.swift +++ b/DivKit/Variables/DivVariablesStorage.swift @@ -5,6 +5,7 @@ import CommonCore public enum DivVariableNameTag {} public typealias DivVariableName = Tagged +@frozen public enum DivVariableValue: Equatable { case string(String) case number(Double) diff --git a/DivKit/generated_sources/Div.swift b/DivKit/generated_sources/Div.swift index 475ed57e..12dcf6ae 100644 --- a/DivKit/generated_sources/Div.swift +++ b/DivKit/generated_sources/Div.swift @@ -5,6 +5,7 @@ import Foundation import Serialization import TemplatesSupport +@frozen public enum Div { case divImage(DivImage) case divGifImage(DivGifImage) diff --git a/DivKit/generated_sources/DivAccessibility.swift b/DivKit/generated_sources/DivAccessibility.swift index e18dbfb9..d10aae25 100644 --- a/DivKit/generated_sources/DivAccessibility.swift +++ b/DivKit/generated_sources/DivAccessibility.swift @@ -6,6 +6,7 @@ import Serialization import TemplatesSupport public final class DivAccessibility { + @frozen public enum Kind: String, CaseIterable { case none = "none" case button = "button" @@ -16,6 +17,7 @@ public final class DivAccessibility { case tabBar = "tab_bar" } + @frozen public enum Mode: String, CaseIterable { case `default` = "default" case merge = "merge" diff --git a/DivKit/generated_sources/DivAlignmentHorizontal.swift b/DivKit/generated_sources/DivAlignmentHorizontal.swift index f85c78b5..74ca26d1 100644 --- a/DivKit/generated_sources/DivAlignmentHorizontal.swift +++ b/DivKit/generated_sources/DivAlignmentHorizontal.swift @@ -5,6 +5,7 @@ import Foundation import Serialization import TemplatesSupport +@frozen public enum DivAlignmentHorizontal: String, CaseIterable { case left = "left" case center = "center" diff --git a/DivKit/generated_sources/DivAlignmentVertical.swift b/DivKit/generated_sources/DivAlignmentVertical.swift index f68b5346..c7994bed 100644 --- a/DivKit/generated_sources/DivAlignmentVertical.swift +++ b/DivKit/generated_sources/DivAlignmentVertical.swift @@ -5,6 +5,7 @@ import Foundation import Serialization import TemplatesSupport +@frozen public enum DivAlignmentVertical: String, CaseIterable { case top = "top" case center = "center" diff --git a/DivKit/generated_sources/DivAnimation.swift b/DivKit/generated_sources/DivAnimation.swift index 6ace0314..3bcf6680 100644 --- a/DivKit/generated_sources/DivAnimation.swift +++ b/DivKit/generated_sources/DivAnimation.swift @@ -6,6 +6,7 @@ import Serialization import TemplatesSupport public final class DivAnimation { + @frozen public enum Name: String, CaseIterable { case fade = "fade" case translate = "translate" diff --git a/DivKit/generated_sources/DivAnimationInterpolator.swift b/DivKit/generated_sources/DivAnimationInterpolator.swift index ad7033aa..2934a784 100644 --- a/DivKit/generated_sources/DivAnimationInterpolator.swift +++ b/DivKit/generated_sources/DivAnimationInterpolator.swift @@ -5,6 +5,7 @@ import Foundation import Serialization import TemplatesSupport +@frozen public enum DivAnimationInterpolator: String, CaseIterable { case linear = "linear" case ease = "ease" diff --git a/DivKit/generated_sources/DivAppearanceTransition.swift b/DivKit/generated_sources/DivAppearanceTransition.swift index 2aa560b7..4446eb98 100644 --- a/DivKit/generated_sources/DivAppearanceTransition.swift +++ b/DivKit/generated_sources/DivAppearanceTransition.swift @@ -5,6 +5,7 @@ import Foundation import Serialization import TemplatesSupport +@frozen public enum DivAppearanceTransition { case divAppearanceSetTransition(DivAppearanceSetTransition) case divFadeTransition(DivFadeTransition) diff --git a/DivKit/generated_sources/DivAppearanceTransitionTemplate.swift b/DivKit/generated_sources/DivAppearanceTransitionTemplate.swift index 2c175953..c66f3aa6 100644 --- a/DivKit/generated_sources/DivAppearanceTransitionTemplate.swift +++ b/DivKit/generated_sources/DivAppearanceTransitionTemplate.swift @@ -5,6 +5,7 @@ import Foundation import Serialization import TemplatesSupport +@frozen public enum DivAppearanceTransitionTemplate: TemplateValue { case divAppearanceSetTransitionTemplate(DivAppearanceSetTransitionTemplate) case divFadeTransitionTemplate(DivFadeTransitionTemplate) diff --git a/DivKit/generated_sources/DivBackground.swift b/DivKit/generated_sources/DivBackground.swift index 2b5b7b01..cf978d29 100644 --- a/DivKit/generated_sources/DivBackground.swift +++ b/DivKit/generated_sources/DivBackground.swift @@ -5,6 +5,7 @@ import Foundation import Serialization import TemplatesSupport +@frozen public enum DivBackground { case divLinearGradient(DivLinearGradient) case divRadialGradient(DivRadialGradient) diff --git a/DivKit/generated_sources/DivBackgroundTemplate.swift b/DivKit/generated_sources/DivBackgroundTemplate.swift index 05df3942..9095a776 100644 --- a/DivKit/generated_sources/DivBackgroundTemplate.swift +++ b/DivKit/generated_sources/DivBackgroundTemplate.swift @@ -5,6 +5,7 @@ import Foundation import Serialization import TemplatesSupport +@frozen public enum DivBackgroundTemplate: TemplateValue { case divLinearGradientTemplate(DivLinearGradientTemplate) case divRadialGradientTemplate(DivRadialGradientTemplate) diff --git a/DivKit/generated_sources/DivChangeTransition.swift b/DivKit/generated_sources/DivChangeTransition.swift index b1aed4e7..1e3a8379 100644 --- a/DivKit/generated_sources/DivChangeTransition.swift +++ b/DivKit/generated_sources/DivChangeTransition.swift @@ -5,6 +5,7 @@ import Foundation import Serialization import TemplatesSupport +@frozen public enum DivChangeTransition { case divChangeSetTransition(DivChangeSetTransition) case divChangeBoundsTransition(DivChangeBoundsTransition) diff --git a/DivKit/generated_sources/DivChangeTransitionTemplate.swift b/DivKit/generated_sources/DivChangeTransitionTemplate.swift index e1ab52f7..c1849c0c 100644 --- a/DivKit/generated_sources/DivChangeTransitionTemplate.swift +++ b/DivKit/generated_sources/DivChangeTransitionTemplate.swift @@ -5,6 +5,7 @@ import Foundation import Serialization import TemplatesSupport +@frozen public enum DivChangeTransitionTemplate: TemplateValue { case divChangeSetTransitionTemplate(DivChangeSetTransitionTemplate) case divChangeBoundsTransitionTemplate(DivChangeBoundsTransitionTemplate) diff --git a/DivKit/generated_sources/DivContainer.swift b/DivKit/generated_sources/DivContainer.swift index 8d886316..7e78ede8 100644 --- a/DivKit/generated_sources/DivContainer.swift +++ b/DivKit/generated_sources/DivContainer.swift @@ -6,11 +6,13 @@ import Serialization import TemplatesSupport public final class DivContainer: DivBase { + @frozen public enum LayoutMode: String, CaseIterable { case noWrap = "no_wrap" case wrap = "wrap" } + @frozen public enum Orientation: String, CaseIterable { case vertical = "vertical" case horizontal = "horizontal" diff --git a/DivKit/generated_sources/DivCount.swift b/DivKit/generated_sources/DivCount.swift index d1303975..6072f3d9 100644 --- a/DivKit/generated_sources/DivCount.swift +++ b/DivKit/generated_sources/DivCount.swift @@ -5,6 +5,7 @@ import Foundation import Serialization import TemplatesSupport +@frozen public enum DivCount { case divInfinityCount(DivInfinityCount) case divFixedCount(DivFixedCount) diff --git a/DivKit/generated_sources/DivCountTemplate.swift b/DivKit/generated_sources/DivCountTemplate.swift index 6861ec01..60a228ec 100644 --- a/DivKit/generated_sources/DivCountTemplate.swift +++ b/DivKit/generated_sources/DivCountTemplate.swift @@ -5,6 +5,7 @@ import Foundation import Serialization import TemplatesSupport +@frozen public enum DivCountTemplate: TemplateValue { case divInfinityCountTemplate(DivInfinityCountTemplate) case divFixedCountTemplate(DivFixedCountTemplate) diff --git a/DivKit/generated_sources/DivDrawable.swift b/DivKit/generated_sources/DivDrawable.swift index 2abc0fd0..863d3ad6 100644 --- a/DivKit/generated_sources/DivDrawable.swift +++ b/DivKit/generated_sources/DivDrawable.swift @@ -5,6 +5,7 @@ import Foundation import Serialization import TemplatesSupport +@frozen public enum DivDrawable { case divShapeDrawable(DivShapeDrawable) diff --git a/DivKit/generated_sources/DivDrawableTemplate.swift b/DivKit/generated_sources/DivDrawableTemplate.swift index fc2a146a..ca4854b0 100644 --- a/DivKit/generated_sources/DivDrawableTemplate.swift +++ b/DivKit/generated_sources/DivDrawableTemplate.swift @@ -5,6 +5,7 @@ import Foundation import Serialization import TemplatesSupport +@frozen public enum DivDrawableTemplate: TemplateValue { case divShapeDrawableTemplate(DivShapeDrawableTemplate) diff --git a/DivKit/generated_sources/DivFontFamily.swift b/DivKit/generated_sources/DivFontFamily.swift index b3c3798a..afab4b13 100644 --- a/DivKit/generated_sources/DivFontFamily.swift +++ b/DivKit/generated_sources/DivFontFamily.swift @@ -5,6 +5,7 @@ import Foundation import Serialization import TemplatesSupport +@frozen public enum DivFontFamily: String, CaseIterable { case text = "text" case display = "display" diff --git a/DivKit/generated_sources/DivFontWeight.swift b/DivKit/generated_sources/DivFontWeight.swift index adc57845..f3ab252a 100644 --- a/DivKit/generated_sources/DivFontWeight.swift +++ b/DivKit/generated_sources/DivFontWeight.swift @@ -5,6 +5,7 @@ import Foundation import Serialization import TemplatesSupport +@frozen public enum DivFontWeight: String, CaseIterable { case light = "light" case medium = "medium" diff --git a/DivKit/generated_sources/DivGallery.swift b/DivKit/generated_sources/DivGallery.swift index d7ba1902..4682768f 100644 --- a/DivKit/generated_sources/DivGallery.swift +++ b/DivKit/generated_sources/DivGallery.swift @@ -6,17 +6,20 @@ import Serialization import TemplatesSupport public final class DivGallery: DivBase { + @frozen public enum CrossContentAlignment: String, CaseIterable { case start = "start" case center = "center" case end = "end" } + @frozen public enum Orientation: String, CaseIterable { case horizontal = "horizontal" case vertical = "vertical" } + @frozen public enum ScrollMode: String, CaseIterable { case paging = "paging" case `default` = "default" diff --git a/DivKit/generated_sources/DivImageScale.swift b/DivKit/generated_sources/DivImageScale.swift index e7d36840..225fae81 100644 --- a/DivKit/generated_sources/DivImageScale.swift +++ b/DivKit/generated_sources/DivImageScale.swift @@ -5,6 +5,7 @@ import Foundation import Serialization import TemplatesSupport +@frozen public enum DivImageScale: String, CaseIterable { case fill = "fill" case noScale = "no_scale" diff --git a/DivKit/generated_sources/DivIndicator.swift b/DivKit/generated_sources/DivIndicator.swift index f28a8208..855fc039 100644 --- a/DivKit/generated_sources/DivIndicator.swift +++ b/DivKit/generated_sources/DivIndicator.swift @@ -6,6 +6,7 @@ import Serialization import TemplatesSupport public final class DivIndicator: DivBase { + @frozen public enum Animation: String, CaseIterable { case scale = "scale" case worm = "worm" diff --git a/DivKit/generated_sources/DivInput.swift b/DivKit/generated_sources/DivInput.swift index 1de6679b..88e86e02 100644 --- a/DivKit/generated_sources/DivInput.swift +++ b/DivKit/generated_sources/DivInput.swift @@ -6,6 +6,7 @@ import Serialization import TemplatesSupport public final class DivInput: DivBase { + @frozen public enum KeyboardType: String, CaseIterable { case singleLineText = "single_line_text" case multiLineText = "multi_line_text" diff --git a/DivKit/generated_sources/DivLineStyle.swift b/DivKit/generated_sources/DivLineStyle.swift index abff5f50..6137fa53 100644 --- a/DivKit/generated_sources/DivLineStyle.swift +++ b/DivKit/generated_sources/DivLineStyle.swift @@ -5,6 +5,7 @@ import Foundation import Serialization import TemplatesSupport +@frozen public enum DivLineStyle: String, CaseIterable { case none = "none" case single = "single" diff --git a/DivKit/generated_sources/DivPager.swift b/DivKit/generated_sources/DivPager.swift index e0d14d90..f9c1ae06 100644 --- a/DivKit/generated_sources/DivPager.swift +++ b/DivKit/generated_sources/DivPager.swift @@ -6,6 +6,7 @@ import Serialization import TemplatesSupport public final class DivPager: DivBase { + @frozen public enum Orientation: String, CaseIterable { case horizontal = "horizontal" case vertical = "vertical" diff --git a/DivKit/generated_sources/DivPagerLayoutMode.swift b/DivKit/generated_sources/DivPagerLayoutMode.swift index 28576254..a92b7370 100644 --- a/DivKit/generated_sources/DivPagerLayoutMode.swift +++ b/DivKit/generated_sources/DivPagerLayoutMode.swift @@ -5,6 +5,7 @@ import Foundation import Serialization import TemplatesSupport +@frozen public enum DivPagerLayoutMode { case divPageSize(DivPageSize) case divNeighbourPageSize(DivNeighbourPageSize) diff --git a/DivKit/generated_sources/DivPagerLayoutModeTemplate.swift b/DivKit/generated_sources/DivPagerLayoutModeTemplate.swift index bc2207cb..8efff0f2 100644 --- a/DivKit/generated_sources/DivPagerLayoutModeTemplate.swift +++ b/DivKit/generated_sources/DivPagerLayoutModeTemplate.swift @@ -5,6 +5,7 @@ import Foundation import Serialization import TemplatesSupport +@frozen public enum DivPagerLayoutModeTemplate: TemplateValue { case divPageSizeTemplate(DivPageSizeTemplate) case divNeighbourPageSizeTemplate(DivNeighbourPageSizeTemplate) diff --git a/DivKit/generated_sources/DivPatch.swift b/DivKit/generated_sources/DivPatch.swift index 1e4c397d..ae524238 100644 --- a/DivKit/generated_sources/DivPatch.swift +++ b/DivKit/generated_sources/DivPatch.swift @@ -6,6 +6,7 @@ import Serialization import TemplatesSupport public final class DivPatch { + @frozen public enum Mode: String, CaseIterable { case transactional = "transactional" case partial = "partial" diff --git a/DivKit/generated_sources/DivPivot.swift b/DivKit/generated_sources/DivPivot.swift index eec15759..355f302f 100644 --- a/DivKit/generated_sources/DivPivot.swift +++ b/DivKit/generated_sources/DivPivot.swift @@ -5,6 +5,7 @@ import Foundation import Serialization import TemplatesSupport +@frozen public enum DivPivot { case divPivotFixed(DivPivotFixed) case divPivotPercentage(DivPivotPercentage) diff --git a/DivKit/generated_sources/DivPivotTemplate.swift b/DivKit/generated_sources/DivPivotTemplate.swift index f6e4c44f..91ba924f 100644 --- a/DivKit/generated_sources/DivPivotTemplate.swift +++ b/DivKit/generated_sources/DivPivotTemplate.swift @@ -5,6 +5,7 @@ import Foundation import Serialization import TemplatesSupport +@frozen public enum DivPivotTemplate: TemplateValue { case divPivotFixedTemplate(DivPivotFixedTemplate) case divPivotPercentageTemplate(DivPivotPercentageTemplate) diff --git a/DivKit/generated_sources/DivRadialGradient.swift b/DivKit/generated_sources/DivRadialGradient.swift index cc738d51..7dcb3325 100644 --- a/DivKit/generated_sources/DivRadialGradient.swift +++ b/DivKit/generated_sources/DivRadialGradient.swift @@ -7,43 +7,37 @@ import TemplatesSupport public final class DivRadialGradient { public static let type: String = "radial_gradient" - public let centerX: Expression // default value: 0.5 - public let centerY: Expression // default value: 0.5 + public let centerX: DivRadialGradientCenter // default value: .divRadialGradientRelativeCenter(DivRadialGradientRelativeCenter(value: .value(0.5))) + public let centerY: DivRadialGradientCenter // default value: .divRadialGradientRelativeCenter(DivRadialGradientRelativeCenter(value: .value(0.5))) public let colors: [Expression] // at least 2 elements - public let radius: Expression // constraint: number > 0 - - public func resolveCenterX(_ resolver: ExpressionResolver) -> Double { - resolver.resolveNumericValue(expression: centerX) ?? 0.5 - } - - public func resolveCenterY(_ resolver: ExpressionResolver) -> Double { - resolver.resolveNumericValue(expression: centerY) ?? 0.5 - } + public let radius: DivRadialGradientRadius // default value: .divRadialGradientRelativeRadius(DivRadialGradientRelativeRadius(value: .value(.farthestCorner))) public func resolveColors(_ resolver: ExpressionResolver) -> [Color]? { colors.map { resolver.resolveStringBasedValue(expression: $0, initializer: Color.color(withHexString:)) }.compactMap { $0 } } - public func resolveRadius(_ resolver: ExpressionResolver) -> Int? { - resolver.resolveNumericValue(expression: radius) - } + static let centerXValidator: AnyValueValidator = + makeNoOpValueValidator() + + static let centerYValidator: AnyValueValidator = + makeNoOpValueValidator() static let colorsValidator: AnyArrayValueValidator> = makeArrayValidator(minItems: 2) - static let radiusValidator: AnyValueValidator = - makeValueValidator(valueValidator: { $0 > 0 }) + static let radiusValidator: AnyValueValidator = + makeNoOpValueValidator() init( - centerX: Expression? = nil, - centerY: Expression? = nil, + centerX: DivRadialGradientCenter? = nil, + centerY: DivRadialGradientCenter? = nil, colors: [Expression], - radius: Expression + radius: DivRadialGradientRadius? = nil ) { - self.centerX = centerX ?? .value(0.5) - self.centerY = centerY ?? .value(0.5) + self.centerX = centerX ?? .divRadialGradientRelativeCenter(DivRadialGradientRelativeCenter(value: .value(0.5))) + self.centerY = centerY ?? .divRadialGradientRelativeCenter(DivRadialGradientRelativeCenter(value: .value(0.5))) self.colors = colors - self.radius = radius + self.radius = radius ?? .divRadialGradientRelativeRadius(DivRadialGradientRelativeRadius(value: .value(.farthestCorner))) } } @@ -71,10 +65,10 @@ extension DivRadialGradient: Serializable { public func toDictionary() -> [String: ValidSerializationValue] { var result: [String: ValidSerializationValue] = [:] result["type"] = Self.type - result["center_x"] = centerX.toValidSerializationValue() - result["center_y"] = centerY.toValidSerializationValue() + result["center_x"] = centerX.toDictionary() + result["center_y"] = centerY.toDictionary() result["colors"] = colors.map { $0.toValidSerializationValue() } - result["radius"] = radius.toValidSerializationValue() + result["radius"] = radius.toDictionary() return result } } diff --git a/DivKit/generated_sources/DivRadialGradientCenter.swift b/DivKit/generated_sources/DivRadialGradientCenter.swift new file mode 100644 index 00000000..6fac09c0 --- /dev/null +++ b/DivKit/generated_sources/DivRadialGradientCenter.swift @@ -0,0 +1,42 @@ +// Generated code. Do not modify. + +import CommonCore +import Foundation +import Serialization +import TemplatesSupport + +@frozen +public enum DivRadialGradientCenter { + case divRadialGradientFixedCenter(DivRadialGradientFixedCenter) + case divRadialGradientRelativeCenter(DivRadialGradientRelativeCenter) + + public var value: Serializable { + switch self { + case let .divRadialGradientFixedCenter(value): + return value + case let .divRadialGradientRelativeCenter(value): + return value + } + } +} + +#if DEBUG +extension DivRadialGradientCenter: Equatable { + public static func ==(lhs: DivRadialGradientCenter, rhs: DivRadialGradientCenter) -> Bool { + switch (lhs, rhs) { + case let (.divRadialGradientFixedCenter(l), .divRadialGradientFixedCenter(r)): + return l == r + case let (.divRadialGradientRelativeCenter(l), .divRadialGradientRelativeCenter(r)): + return l == r + default: + return false + } + } +} +#endif + +extension DivRadialGradientCenter: Serializable { + public func toDictionary() -> [String: ValidSerializationValue] { + return value.toDictionary() + } +} diff --git a/DivKit/generated_sources/DivRadialGradientCenterTemplate.swift b/DivKit/generated_sources/DivRadialGradientCenterTemplate.swift new file mode 100644 index 00000000..ece43b68 --- /dev/null +++ b/DivKit/generated_sources/DivRadialGradientCenterTemplate.swift @@ -0,0 +1,101 @@ +// Generated code. Do not modify. + +import CommonCore +import Foundation +import Serialization +import TemplatesSupport + +@frozen +public enum DivRadialGradientCenterTemplate: TemplateValue { + case divRadialGradientFixedCenterTemplate(DivRadialGradientFixedCenterTemplate) + case divRadialGradientRelativeCenterTemplate(DivRadialGradientRelativeCenterTemplate) + + public var value: Any { + switch self { + case let .divRadialGradientFixedCenterTemplate(value): + return value + case let .divRadialGradientRelativeCenterTemplate(value): + return value + } + } + + public func resolveParent(templates: Templates) throws -> DivRadialGradientCenterTemplate { + switch self { + case let .divRadialGradientFixedCenterTemplate(value): + return .divRadialGradientFixedCenterTemplate(try value.resolveParent(templates: templates)) + case let .divRadialGradientRelativeCenterTemplate(value): + return .divRadialGradientRelativeCenterTemplate(try value.resolveParent(templates: templates)) + } + } + + public static func resolveValue(context: Context, parent: DivRadialGradientCenterTemplate?, useOnlyLinks: Bool) -> DeserializationResult { + guard let parent = parent else { + if useOnlyLinks { + return .failure(NonEmptyArray(.missingType(representation: context.templateData))) + } else { + return resolveUnknownValue(context: context, useOnlyLinks: useOnlyLinks) + } + } + + switch parent { + case let .divRadialGradientFixedCenterTemplate(value): + let result = value.resolveValue(context: context, useOnlyLinks: useOnlyLinks) + switch result { + case let .success(value): return .success(.divRadialGradientFixedCenter(value)) + case let .partialSuccess(value, warnings): return .partialSuccess(.divRadialGradientFixedCenter(value), warnings: warnings) + case let .failure(errors): return .failure(errors) + case .noValue: return .noValue + } + case let .divRadialGradientRelativeCenterTemplate(value): + let result = value.resolveValue(context: context, useOnlyLinks: useOnlyLinks) + switch result { + case let .success(value): return .success(.divRadialGradientRelativeCenter(value)) + case let .partialSuccess(value, warnings): return .partialSuccess(.divRadialGradientRelativeCenter(value), warnings: warnings) + case let .failure(errors): return .failure(errors) + case .noValue: return .noValue + } + } + } + + private static func resolveUnknownValue(context: Context, useOnlyLinks: Bool) -> DeserializationResult { + guard let type = (context.templateData["type"] as? String).flatMap({ context.templateToType[$0] ?? $0 }) else { + return .failure(NonEmptyArray(FieldError(fieldName: "type", level: .error, error: .requiredFieldIsMissing))) + } + + switch type { + case DivRadialGradientFixedCenter.type: + let result = DivRadialGradientFixedCenterTemplate.resolveValue(context: context, useOnlyLinks: useOnlyLinks) + switch result { + case let .success(value): return .success(.divRadialGradientFixedCenter(value)) + case let .partialSuccess(value, warnings): return .partialSuccess(.divRadialGradientFixedCenter(value), warnings: warnings) + case let .failure(errors): return .failure(errors) + case .noValue: return .noValue + } + case DivRadialGradientRelativeCenter.type: + let result = DivRadialGradientRelativeCenterTemplate.resolveValue(context: context, useOnlyLinks: useOnlyLinks) + switch result { + case let .success(value): return .success(.divRadialGradientRelativeCenter(value)) + case let .partialSuccess(value, warnings): return .partialSuccess(.divRadialGradientRelativeCenter(value), warnings: warnings) + case let .failure(errors): return .failure(errors) + case .noValue: return .noValue + } + default: + return .failure(NonEmptyArray(FieldError(fieldName: "type", level: .error, error: .requiredFieldIsMissing))) + } + } +} + +extension DivRadialGradientCenterTemplate: TemplateDeserializable { + public init(dictionary: [String: Any], templateToType: TemplateToType) throws { + let receivedType = try dictionary.getField("type") as String + let blockType = templateToType[receivedType] ?? receivedType + switch blockType { + case DivRadialGradientFixedCenterTemplate.type: + self = .divRadialGradientFixedCenterTemplate(try DivRadialGradientFixedCenterTemplate(dictionary: dictionary, templateToType: templateToType)) + case DivRadialGradientRelativeCenterTemplate.type: + self = .divRadialGradientRelativeCenterTemplate(try DivRadialGradientRelativeCenterTemplate(dictionary: dictionary, templateToType: templateToType)) + default: + throw DeserializationError.invalidFieldRepresentation(field: "div-radial-gradient-center_template", representation: dictionary) + } + } +} diff --git a/DivKit/generated_sources/DivRadialGradientFixedCenter.swift b/DivKit/generated_sources/DivRadialGradientFixedCenter.swift new file mode 100644 index 00000000..df1b137a --- /dev/null +++ b/DivKit/generated_sources/DivRadialGradientFixedCenter.swift @@ -0,0 +1,55 @@ +// Generated code. Do not modify. + +import CommonCore +import Foundation +import Serialization +import TemplatesSupport + +public final class DivRadialGradientFixedCenter { + public static let type: String = "fixed" + public let unit: Expression // default value: dp + public let value: Expression + + public func resolveUnit(_ resolver: ExpressionResolver) -> DivSizeUnit { + resolver.resolveStringBasedValue(expression: unit, initializer: DivSizeUnit.init(rawValue:)) ?? DivSizeUnit.dp + } + + public func resolveValue(_ resolver: ExpressionResolver) -> Int? { + resolver.resolveNumericValue(expression: value) + } + + static let unitValidator: AnyValueValidator = + makeNoOpValueValidator() + + init( + unit: Expression? = nil, + value: Expression + ) { + self.unit = unit ?? .value(.dp) + self.value = value + } +} + +#if DEBUG +extension DivRadialGradientFixedCenter: Equatable { + public static func ==(lhs: DivRadialGradientFixedCenter, rhs: DivRadialGradientFixedCenter) -> Bool { + guard + lhs.unit == rhs.unit, + lhs.value == rhs.value + else { + return false + } + return true + } +} +#endif + +extension DivRadialGradientFixedCenter: Serializable { + public func toDictionary() -> [String: ValidSerializationValue] { + var result: [String: ValidSerializationValue] = [:] + result["type"] = Self.type + result["unit"] = unit.toValidSerializationValue() + result["value"] = value.toValidSerializationValue() + return result + } +} diff --git a/DivKit/generated_sources/DivRadialGradientFixedCenterTemplate.swift b/DivKit/generated_sources/DivRadialGradientFixedCenterTemplate.swift new file mode 100644 index 00000000..6748ec32 --- /dev/null +++ b/DivKit/generated_sources/DivRadialGradientFixedCenterTemplate.swift @@ -0,0 +1,116 @@ +// Generated code. Do not modify. + +import CommonCore +import Foundation +import Serialization +import TemplatesSupport + +public final class DivRadialGradientFixedCenterTemplate: TemplateValue, TemplateDeserializable { + public static let type: String = "fixed" + public let parent: String? // at least 1 char + public let unit: Field>? // default value: dp + public let value: Field>? + + static let parentValidator: AnyValueValidator = + makeStringValidator(minLength: 1) + + public convenience init(dictionary: [String: Any], templateToType: TemplateToType) throws { + do { + self.init( + parent: try dictionary.getOptionalField("type", validator: Self.parentValidator), + unit: try dictionary.getOptionalExpressionField("unit"), + value: try dictionary.getOptionalExpressionField("value") + ) + } catch let DeserializationError.invalidFieldRepresentation(field: field, representation: representation) { + throw DeserializationError.invalidFieldRepresentation(field: "div-radial-gradient-fixed-center_template." + field, representation: representation) + } + } + + init( + parent: String?, + unit: Field>? = nil, + value: Field>? = nil + ) { + self.parent = parent + self.unit = unit + self.value = value + } + + private static func resolveOnlyLinks(context: Context, parent: DivRadialGradientFixedCenterTemplate?) -> DeserializationResult { + let unitValue = parent?.unit?.resolveOptionalValue(context: context, validator: ResolvedValue.unitValidator) ?? .noValue + let valueValue = parent?.value?.resolveValue(context: context) ?? .noValue + var errors = mergeErrors( + unitValue.errorsOrWarnings?.map { .right($0.asError(deserializing: "unit", level: .warning)) }, + valueValue.errorsOrWarnings?.map { .right($0.asError(deserializing: "value", level: .error)) } + ) + if case .noValue = valueValue { + errors.append(.right(FieldError(fieldName: "value", level: .error, error: .requiredFieldIsMissing))) + } + guard + let valueNonNil = valueValue.value + else { + return .failure(NonEmptyArray(errors)!) + } + let result = DivRadialGradientFixedCenter( + unit: unitValue.value, + value: valueNonNil + ) + return errors.isEmpty ? .success(result) : .partialSuccess(result, warnings: NonEmptyArray(errors)!) + } + + public static func resolveValue(context: Context, parent: DivRadialGradientFixedCenterTemplate?, useOnlyLinks: Bool) -> DeserializationResult { + if useOnlyLinks { + return resolveOnlyLinks(context: context, parent: parent) + } + var unitValue: DeserializationResult> = parent?.unit?.value() ?? .noValue + var valueValue: DeserializationResult> = parent?.value?.value() ?? .noValue + context.templateData.forEach { key, __dictValue in + switch key { + case "unit": + unitValue = deserialize(__dictValue, validator: ResolvedValue.unitValidator).merged(with: unitValue) + case "value": + valueValue = deserialize(__dictValue).merged(with: valueValue) + case parent?.unit?.link: + unitValue = unitValue.merged(with: deserialize(__dictValue, validator: ResolvedValue.unitValidator)) + case parent?.value?.link: + valueValue = valueValue.merged(with: deserialize(__dictValue)) + default: break + } + } + var errors = mergeErrors( + unitValue.errorsOrWarnings?.map { Either.right($0.asError(deserializing: "unit", level: .warning)) }, + valueValue.errorsOrWarnings?.map { Either.right($0.asError(deserializing: "value", level: .error)) } + ) + if case .noValue = valueValue { + errors.append(.right(FieldError(fieldName: "value", level: .error, error: .requiredFieldIsMissing))) + } + guard + let valueNonNil = valueValue.value + else { + return .failure(NonEmptyArray(errors)!) + } + let result = DivRadialGradientFixedCenter( + unit: unitValue.value, + value: valueNonNil + ) + return errors.isEmpty ? .success(result) : .partialSuccess(result, warnings: NonEmptyArray(errors)!) + } + + private func mergedWithParent(templates: Templates) throws -> DivRadialGradientFixedCenterTemplate { + guard let parent = parent, parent != Self.type else { return self } + guard let parentTemplate = templates[parent] as? DivRadialGradientFixedCenterTemplate else { + throw DeserializationError.unknownType(type: parent) + } + let mergedParent = try parentTemplate.mergedWithParent(templates: templates) + + return DivRadialGradientFixedCenterTemplate( + parent: nil, + unit: unit ?? mergedParent.unit, + value: value ?? mergedParent.value + ) + } + + public func resolveParent(templates: Templates) throws -> DivRadialGradientFixedCenterTemplate { + return try mergedWithParent(templates: templates) + } +} diff --git a/DivKit/generated_sources/DivRadialGradientRadius.swift b/DivKit/generated_sources/DivRadialGradientRadius.swift new file mode 100644 index 00000000..905147ee --- /dev/null +++ b/DivKit/generated_sources/DivRadialGradientRadius.swift @@ -0,0 +1,42 @@ +// Generated code. Do not modify. + +import CommonCore +import Foundation +import Serialization +import TemplatesSupport + +@frozen +public enum DivRadialGradientRadius { + case divFixedSize(DivFixedSize) + case divRadialGradientRelativeRadius(DivRadialGradientRelativeRadius) + + public var value: Serializable { + switch self { + case let .divFixedSize(value): + return value + case let .divRadialGradientRelativeRadius(value): + return value + } + } +} + +#if DEBUG +extension DivRadialGradientRadius: Equatable { + public static func ==(lhs: DivRadialGradientRadius, rhs: DivRadialGradientRadius) -> Bool { + switch (lhs, rhs) { + case let (.divFixedSize(l), .divFixedSize(r)): + return l == r + case let (.divRadialGradientRelativeRadius(l), .divRadialGradientRelativeRadius(r)): + return l == r + default: + return false + } + } +} +#endif + +extension DivRadialGradientRadius: Serializable { + public func toDictionary() -> [String: ValidSerializationValue] { + return value.toDictionary() + } +} diff --git a/DivKit/generated_sources/DivRadialGradientRadiusTemplate.swift b/DivKit/generated_sources/DivRadialGradientRadiusTemplate.swift new file mode 100644 index 00000000..437461e5 --- /dev/null +++ b/DivKit/generated_sources/DivRadialGradientRadiusTemplate.swift @@ -0,0 +1,101 @@ +// Generated code. Do not modify. + +import CommonCore +import Foundation +import Serialization +import TemplatesSupport + +@frozen +public enum DivRadialGradientRadiusTemplate: TemplateValue { + case divFixedSizeTemplate(DivFixedSizeTemplate) + case divRadialGradientRelativeRadiusTemplate(DivRadialGradientRelativeRadiusTemplate) + + public var value: Any { + switch self { + case let .divFixedSizeTemplate(value): + return value + case let .divRadialGradientRelativeRadiusTemplate(value): + return value + } + } + + public func resolveParent(templates: Templates) throws -> DivRadialGradientRadiusTemplate { + switch self { + case let .divFixedSizeTemplate(value): + return .divFixedSizeTemplate(try value.resolveParent(templates: templates)) + case let .divRadialGradientRelativeRadiusTemplate(value): + return .divRadialGradientRelativeRadiusTemplate(try value.resolveParent(templates: templates)) + } + } + + public static func resolveValue(context: Context, parent: DivRadialGradientRadiusTemplate?, useOnlyLinks: Bool) -> DeserializationResult { + guard let parent = parent else { + if useOnlyLinks { + return .failure(NonEmptyArray(.missingType(representation: context.templateData))) + } else { + return resolveUnknownValue(context: context, useOnlyLinks: useOnlyLinks) + } + } + + switch parent { + case let .divFixedSizeTemplate(value): + let result = value.resolveValue(context: context, useOnlyLinks: useOnlyLinks) + switch result { + case let .success(value): return .success(.divFixedSize(value)) + case let .partialSuccess(value, warnings): return .partialSuccess(.divFixedSize(value), warnings: warnings) + case let .failure(errors): return .failure(errors) + case .noValue: return .noValue + } + case let .divRadialGradientRelativeRadiusTemplate(value): + let result = value.resolveValue(context: context, useOnlyLinks: useOnlyLinks) + switch result { + case let .success(value): return .success(.divRadialGradientRelativeRadius(value)) + case let .partialSuccess(value, warnings): return .partialSuccess(.divRadialGradientRelativeRadius(value), warnings: warnings) + case let .failure(errors): return .failure(errors) + case .noValue: return .noValue + } + } + } + + private static func resolveUnknownValue(context: Context, useOnlyLinks: Bool) -> DeserializationResult { + guard let type = (context.templateData["type"] as? String).flatMap({ context.templateToType[$0] ?? $0 }) else { + return .failure(NonEmptyArray(FieldError(fieldName: "type", level: .error, error: .requiredFieldIsMissing))) + } + + switch type { + case DivFixedSize.type: + let result = DivFixedSizeTemplate.resolveValue(context: context, useOnlyLinks: useOnlyLinks) + switch result { + case let .success(value): return .success(.divFixedSize(value)) + case let .partialSuccess(value, warnings): return .partialSuccess(.divFixedSize(value), warnings: warnings) + case let .failure(errors): return .failure(errors) + case .noValue: return .noValue + } + case DivRadialGradientRelativeRadius.type: + let result = DivRadialGradientRelativeRadiusTemplate.resolveValue(context: context, useOnlyLinks: useOnlyLinks) + switch result { + case let .success(value): return .success(.divRadialGradientRelativeRadius(value)) + case let .partialSuccess(value, warnings): return .partialSuccess(.divRadialGradientRelativeRadius(value), warnings: warnings) + case let .failure(errors): return .failure(errors) + case .noValue: return .noValue + } + default: + return .failure(NonEmptyArray(FieldError(fieldName: "type", level: .error, error: .requiredFieldIsMissing))) + } + } +} + +extension DivRadialGradientRadiusTemplate: TemplateDeserializable { + public init(dictionary: [String: Any], templateToType: TemplateToType) throws { + let receivedType = try dictionary.getField("type") as String + let blockType = templateToType[receivedType] ?? receivedType + switch blockType { + case DivFixedSizeTemplate.type: + self = .divFixedSizeTemplate(try DivFixedSizeTemplate(dictionary: dictionary, templateToType: templateToType)) + case DivRadialGradientRelativeRadiusTemplate.type: + self = .divRadialGradientRelativeRadiusTemplate(try DivRadialGradientRelativeRadiusTemplate(dictionary: dictionary, templateToType: templateToType)) + default: + throw DeserializationError.invalidFieldRepresentation(field: "div-radial-gradient-radius_template", representation: dictionary) + } + } +} diff --git a/DivKit/generated_sources/DivRadialGradientRelativeCenter.swift b/DivKit/generated_sources/DivRadialGradientRelativeCenter.swift new file mode 100644 index 00000000..d22a9959 --- /dev/null +++ b/DivKit/generated_sources/DivRadialGradientRelativeCenter.swift @@ -0,0 +1,43 @@ +// Generated code. Do not modify. + +import CommonCore +import Foundation +import Serialization +import TemplatesSupport + +public final class DivRadialGradientRelativeCenter { + public static let type: String = "relative" + public let value: Expression + + public func resolveValue(_ resolver: ExpressionResolver) -> Double? { + resolver.resolveNumericValue(expression: value) + } + + init( + value: Expression + ) { + self.value = value + } +} + +#if DEBUG +extension DivRadialGradientRelativeCenter: Equatable { + public static func ==(lhs: DivRadialGradientRelativeCenter, rhs: DivRadialGradientRelativeCenter) -> Bool { + guard + lhs.value == rhs.value + else { + return false + } + return true + } +} +#endif + +extension DivRadialGradientRelativeCenter: Serializable { + public func toDictionary() -> [String: ValidSerializationValue] { + var result: [String: ValidSerializationValue] = [:] + result["type"] = Self.type + result["value"] = value.toValidSerializationValue() + return result + } +} diff --git a/DivKit/generated_sources/DivRadialGradientRelativeCenterTemplate.swift b/DivKit/generated_sources/DivRadialGradientRelativeCenterTemplate.swift new file mode 100644 index 00000000..5f58a143 --- /dev/null +++ b/DivKit/generated_sources/DivRadialGradientRelativeCenterTemplate.swift @@ -0,0 +1,101 @@ +// Generated code. Do not modify. + +import CommonCore +import Foundation +import Serialization +import TemplatesSupport + +public final class DivRadialGradientRelativeCenterTemplate: TemplateValue, TemplateDeserializable { + public static let type: String = "relative" + public let parent: String? // at least 1 char + public let value: Field>? + + static let parentValidator: AnyValueValidator = + makeStringValidator(minLength: 1) + + public convenience init(dictionary: [String: Any], templateToType: TemplateToType) throws { + do { + self.init( + parent: try dictionary.getOptionalField("type", validator: Self.parentValidator), + value: try dictionary.getOptionalExpressionField("value") + ) + } catch let DeserializationError.invalidFieldRepresentation(field: field, representation: representation) { + throw DeserializationError.invalidFieldRepresentation(field: "div-radial-gradient-relative-center_template." + field, representation: representation) + } + } + + init( + parent: String?, + value: Field>? = nil + ) { + self.parent = parent + self.value = value + } + + private static func resolveOnlyLinks(context: Context, parent: DivRadialGradientRelativeCenterTemplate?) -> DeserializationResult { + let valueValue = parent?.value?.resolveValue(context: context) ?? .noValue + var errors = mergeErrors( + valueValue.errorsOrWarnings?.map { .right($0.asError(deserializing: "value", level: .error)) } + ) + if case .noValue = valueValue { + errors.append(.right(FieldError(fieldName: "value", level: .error, error: .requiredFieldIsMissing))) + } + guard + let valueNonNil = valueValue.value + else { + return .failure(NonEmptyArray(errors)!) + } + let result = DivRadialGradientRelativeCenter( + value: valueNonNil + ) + return errors.isEmpty ? .success(result) : .partialSuccess(result, warnings: NonEmptyArray(errors)!) + } + + public static func resolveValue(context: Context, parent: DivRadialGradientRelativeCenterTemplate?, useOnlyLinks: Bool) -> DeserializationResult { + if useOnlyLinks { + return resolveOnlyLinks(context: context, parent: parent) + } + var valueValue: DeserializationResult> = parent?.value?.value() ?? .noValue + context.templateData.forEach { key, __dictValue in + switch key { + case "value": + valueValue = deserialize(__dictValue).merged(with: valueValue) + case parent?.value?.link: + valueValue = valueValue.merged(with: deserialize(__dictValue)) + default: break + } + } + var errors = mergeErrors( + valueValue.errorsOrWarnings?.map { Either.right($0.asError(deserializing: "value", level: .error)) } + ) + if case .noValue = valueValue { + errors.append(.right(FieldError(fieldName: "value", level: .error, error: .requiredFieldIsMissing))) + } + guard + let valueNonNil = valueValue.value + else { + return .failure(NonEmptyArray(errors)!) + } + let result = DivRadialGradientRelativeCenter( + value: valueNonNil + ) + return errors.isEmpty ? .success(result) : .partialSuccess(result, warnings: NonEmptyArray(errors)!) + } + + private func mergedWithParent(templates: Templates) throws -> DivRadialGradientRelativeCenterTemplate { + guard let parent = parent, parent != Self.type else { return self } + guard let parentTemplate = templates[parent] as? DivRadialGradientRelativeCenterTemplate else { + throw DeserializationError.unknownType(type: parent) + } + let mergedParent = try parentTemplate.mergedWithParent(templates: templates) + + return DivRadialGradientRelativeCenterTemplate( + parent: nil, + value: value ?? mergedParent.value + ) + } + + public func resolveParent(templates: Templates) throws -> DivRadialGradientRelativeCenterTemplate { + return try mergedWithParent(templates: templates) + } +} diff --git a/DivKit/generated_sources/DivRadialGradientRelativeRadius.swift b/DivKit/generated_sources/DivRadialGradientRelativeRadius.swift new file mode 100644 index 00000000..b530ed5d --- /dev/null +++ b/DivKit/generated_sources/DivRadialGradientRelativeRadius.swift @@ -0,0 +1,51 @@ +// Generated code. Do not modify. + +import CommonCore +import Foundation +import Serialization +import TemplatesSupport + +public final class DivRadialGradientRelativeRadius { + @frozen + public enum Value: String, CaseIterable { + case nearestCorner = "nearest_corner" + case farthestCorner = "farthest_corner" + case nearestSide = "nearest_side" + case farthestSide = "farthest_side" + } + + public static let type: String = "relative" + public let value: Expression + + public func resolveValue(_ resolver: ExpressionResolver) -> Value? { + resolver.resolveStringBasedValue(expression: value, initializer: Value.init(rawValue:)) + } + + init( + value: Expression + ) { + self.value = value + } +} + +#if DEBUG +extension DivRadialGradientRelativeRadius: Equatable { + public static func ==(lhs: DivRadialGradientRelativeRadius, rhs: DivRadialGradientRelativeRadius) -> Bool { + guard + lhs.value == rhs.value + else { + return false + } + return true + } +} +#endif + +extension DivRadialGradientRelativeRadius: Serializable { + public func toDictionary() -> [String: ValidSerializationValue] { + var result: [String: ValidSerializationValue] = [:] + result["type"] = Self.type + result["value"] = value.toValidSerializationValue() + return result + } +} diff --git a/DivKit/generated_sources/DivRadialGradientRelativeRadiusTemplate.swift b/DivKit/generated_sources/DivRadialGradientRelativeRadiusTemplate.swift new file mode 100644 index 00000000..5d4a6598 --- /dev/null +++ b/DivKit/generated_sources/DivRadialGradientRelativeRadiusTemplate.swift @@ -0,0 +1,103 @@ +// Generated code. Do not modify. + +import CommonCore +import Foundation +import Serialization +import TemplatesSupport + +public final class DivRadialGradientRelativeRadiusTemplate: TemplateValue, TemplateDeserializable { + public typealias Value = DivRadialGradientRelativeRadius.Value + + public static let type: String = "relative" + public let parent: String? // at least 1 char + public let value: Field>? + + static let parentValidator: AnyValueValidator = + makeStringValidator(minLength: 1) + + public convenience init(dictionary: [String: Any], templateToType: TemplateToType) throws { + do { + self.init( + parent: try dictionary.getOptionalField("type", validator: Self.parentValidator), + value: try dictionary.getOptionalExpressionField("value") + ) + } catch let DeserializationError.invalidFieldRepresentation(field: field, representation: representation) { + throw DeserializationError.invalidFieldRepresentation(field: "div-radial-gradient-relative-radius_template." + field, representation: representation) + } + } + + init( + parent: String?, + value: Field>? = nil + ) { + self.parent = parent + self.value = value + } + + private static func resolveOnlyLinks(context: Context, parent: DivRadialGradientRelativeRadiusTemplate?) -> DeserializationResult { + let valueValue = parent?.value?.resolveValue(context: context) ?? .noValue + var errors = mergeErrors( + valueValue.errorsOrWarnings?.map { .right($0.asError(deserializing: "value", level: .error)) } + ) + if case .noValue = valueValue { + errors.append(.right(FieldError(fieldName: "value", level: .error, error: .requiredFieldIsMissing))) + } + guard + let valueNonNil = valueValue.value + else { + return .failure(NonEmptyArray(errors)!) + } + let result = DivRadialGradientRelativeRadius( + value: valueNonNil + ) + return errors.isEmpty ? .success(result) : .partialSuccess(result, warnings: NonEmptyArray(errors)!) + } + + public static func resolveValue(context: Context, parent: DivRadialGradientRelativeRadiusTemplate?, useOnlyLinks: Bool) -> DeserializationResult { + if useOnlyLinks { + return resolveOnlyLinks(context: context, parent: parent) + } + var valueValue: DeserializationResult> = parent?.value?.value() ?? .noValue + context.templateData.forEach { key, __dictValue in + switch key { + case "value": + valueValue = deserialize(__dictValue).merged(with: valueValue) + case parent?.value?.link: + valueValue = valueValue.merged(with: deserialize(__dictValue)) + default: break + } + } + var errors = mergeErrors( + valueValue.errorsOrWarnings?.map { Either.right($0.asError(deserializing: "value", level: .error)) } + ) + if case .noValue = valueValue { + errors.append(.right(FieldError(fieldName: "value", level: .error, error: .requiredFieldIsMissing))) + } + guard + let valueNonNil = valueValue.value + else { + return .failure(NonEmptyArray(errors)!) + } + let result = DivRadialGradientRelativeRadius( + value: valueNonNil + ) + return errors.isEmpty ? .success(result) : .partialSuccess(result, warnings: NonEmptyArray(errors)!) + } + + private func mergedWithParent(templates: Templates) throws -> DivRadialGradientRelativeRadiusTemplate { + guard let parent = parent, parent != Self.type else { return self } + guard let parentTemplate = templates[parent] as? DivRadialGradientRelativeRadiusTemplate else { + throw DeserializationError.unknownType(type: parent) + } + let mergedParent = try parentTemplate.mergedWithParent(templates: templates) + + return DivRadialGradientRelativeRadiusTemplate( + parent: nil, + value: value ?? mergedParent.value + ) + } + + public func resolveParent(templates: Templates) throws -> DivRadialGradientRelativeRadiusTemplate { + return try mergedWithParent(templates: templates) + } +} diff --git a/DivKit/generated_sources/DivRadialGradientTemplate.swift b/DivKit/generated_sources/DivRadialGradientTemplate.swift index dc349448..4a3f0bb6 100644 --- a/DivKit/generated_sources/DivRadialGradientTemplate.swift +++ b/DivKit/generated_sources/DivRadialGradientTemplate.swift @@ -8,10 +8,10 @@ import TemplatesSupport public final class DivRadialGradientTemplate: TemplateValue, TemplateDeserializable { public static let type: String = "radial_gradient" public let parent: String? // at least 1 char - public let centerX: Field>? // default value: 0.5 - public let centerY: Field>? // default value: 0.5 + public let centerX: Field? // default value: .divRadialGradientRelativeCenter(DivRadialGradientRelativeCenter(value: .value(0.5))) + public let centerY: Field? // default value: .divRadialGradientRelativeCenter(DivRadialGradientRelativeCenter(value: .value(0.5))) public let colors: Field<[Expression]>? // at least 2 elements - public let radius: Field>? // constraint: number > 0 + public let radius: Field? // default value: .divRadialGradientRelativeRadius(DivRadialGradientRelativeRadius(value: .value(.farthestCorner))) static let parentValidator: AnyValueValidator = makeStringValidator(minLength: 1) @@ -20,10 +20,10 @@ public final class DivRadialGradientTemplate: TemplateValue, TemplateDeserializa do { self.init( parent: try dictionary.getOptionalField("type", validator: Self.parentValidator), - centerX: try dictionary.getOptionalExpressionField("center_x"), - centerY: try dictionary.getOptionalExpressionField("center_y"), + centerX: try dictionary.getOptionalField("center_x", templateToType: templateToType), + centerY: try dictionary.getOptionalField("center_y", templateToType: templateToType), colors: try dictionary.getOptionalExpressionArray("colors", transform: Color.color(withHexString:)), - radius: try dictionary.getOptionalExpressionField("radius") + radius: try dictionary.getOptionalField("radius", templateToType: templateToType) ) } catch let DeserializationError.invalidFieldRepresentation(field: field, representation: representation) { throw DeserializationError.invalidFieldRepresentation(field: "div-radial-gradient_template." + field, representation: representation) @@ -32,10 +32,10 @@ public final class DivRadialGradientTemplate: TemplateValue, TemplateDeserializa init( parent: String?, - centerX: Field>? = nil, - centerY: Field>? = nil, + centerX: Field? = nil, + centerY: Field? = nil, colors: Field<[Expression]>? = nil, - radius: Field>? = nil + radius: Field? = nil ) { self.parent = parent self.centerX = centerX @@ -45,25 +45,21 @@ public final class DivRadialGradientTemplate: TemplateValue, TemplateDeserializa } private static func resolveOnlyLinks(context: Context, parent: DivRadialGradientTemplate?) -> DeserializationResult { - let centerXValue = parent?.centerX?.resolveOptionalValue(context: context) ?? .noValue - let centerYValue = parent?.centerY?.resolveOptionalValue(context: context) ?? .noValue + let centerXValue = parent?.centerX?.resolveOptionalValue(context: context, validator: ResolvedValue.centerXValidator, useOnlyLinks: true) ?? .noValue + let centerYValue = parent?.centerY?.resolveOptionalValue(context: context, validator: ResolvedValue.centerYValidator, useOnlyLinks: true) ?? .noValue let colorsValue = parent?.colors?.resolveValue(context: context, transform: Color.color(withHexString:), validator: ResolvedValue.colorsValidator) ?? .noValue - let radiusValue = parent?.radius?.resolveValue(context: context, validator: ResolvedValue.radiusValidator) ?? .noValue + let radiusValue = parent?.radius?.resolveOptionalValue(context: context, validator: ResolvedValue.radiusValidator, useOnlyLinks: true) ?? .noValue var errors = mergeErrors( centerXValue.errorsOrWarnings?.map { .right($0.asError(deserializing: "center_x", level: .warning)) }, centerYValue.errorsOrWarnings?.map { .right($0.asError(deserializing: "center_y", level: .warning)) }, colorsValue.errorsOrWarnings?.map { .right($0.asError(deserializing: "colors", level: .error)) }, - radiusValue.errorsOrWarnings?.map { .right($0.asError(deserializing: "radius", level: .error)) } + radiusValue.errorsOrWarnings?.map { .right($0.asError(deserializing: "radius", level: .warning)) } ) if case .noValue = colorsValue { errors.append(.right(FieldError(fieldName: "colors", level: .error, error: .requiredFieldIsMissing))) } - if case .noValue = radiusValue { - errors.append(.right(FieldError(fieldName: "radius", level: .error, error: .requiredFieldIsMissing))) - } guard - let colorsNonNil = colorsValue.value, - let radiusNonNil = radiusValue.value + let colorsNonNil = colorsValue.value else { return .failure(NonEmptyArray(errors)!) } @@ -71,7 +67,7 @@ public final class DivRadialGradientTemplate: TemplateValue, TemplateDeserializa centerX: centerXValue.value, centerY: centerYValue.value, colors: colorsNonNil, - radius: radiusNonNil + radius: radiusValue.value ) return errors.isEmpty ? .success(result) : .partialSuccess(result, warnings: NonEmptyArray(errors)!) } @@ -80,46 +76,47 @@ public final class DivRadialGradientTemplate: TemplateValue, TemplateDeserializa if useOnlyLinks { return resolveOnlyLinks(context: context, parent: parent) } - var centerXValue: DeserializationResult> = parent?.centerX?.value() ?? .noValue - var centerYValue: DeserializationResult> = parent?.centerY?.value() ?? .noValue + var centerXValue: DeserializationResult = .noValue + var centerYValue: DeserializationResult = .noValue var colorsValue: DeserializationResult<[Expression]> = parent?.colors?.value() ?? .noValue - var radiusValue: DeserializationResult> = parent?.radius?.value() ?? .noValue + var radiusValue: DeserializationResult = .noValue context.templateData.forEach { key, __dictValue in switch key { case "center_x": - centerXValue = deserialize(__dictValue).merged(with: centerXValue) + centerXValue = deserialize(__dictValue, templates: context.templates, templateToType: context.templateToType, validator: ResolvedValue.centerXValidator, type: DivRadialGradientCenterTemplate.self).merged(with: centerXValue) case "center_y": - centerYValue = deserialize(__dictValue).merged(with: centerYValue) + centerYValue = deserialize(__dictValue, templates: context.templates, templateToType: context.templateToType, validator: ResolvedValue.centerYValidator, type: DivRadialGradientCenterTemplate.self).merged(with: centerYValue) case "colors": colorsValue = deserialize(__dictValue, transform: Color.color(withHexString:), validator: ResolvedValue.colorsValidator).merged(with: colorsValue) case "radius": - radiusValue = deserialize(__dictValue, validator: ResolvedValue.radiusValidator).merged(with: radiusValue) + radiusValue = deserialize(__dictValue, templates: context.templates, templateToType: context.templateToType, validator: ResolvedValue.radiusValidator, type: DivRadialGradientRadiusTemplate.self).merged(with: radiusValue) case parent?.centerX?.link: - centerXValue = centerXValue.merged(with: deserialize(__dictValue)) + centerXValue = centerXValue.merged(with: deserialize(__dictValue, templates: context.templates, templateToType: context.templateToType, validator: ResolvedValue.centerXValidator, type: DivRadialGradientCenterTemplate.self)) case parent?.centerY?.link: - centerYValue = centerYValue.merged(with: deserialize(__dictValue)) + centerYValue = centerYValue.merged(with: deserialize(__dictValue, templates: context.templates, templateToType: context.templateToType, validator: ResolvedValue.centerYValidator, type: DivRadialGradientCenterTemplate.self)) case parent?.colors?.link: colorsValue = colorsValue.merged(with: deserialize(__dictValue, transform: Color.color(withHexString:), validator: ResolvedValue.colorsValidator)) case parent?.radius?.link: - radiusValue = radiusValue.merged(with: deserialize(__dictValue, validator: ResolvedValue.radiusValidator)) + radiusValue = radiusValue.merged(with: deserialize(__dictValue, templates: context.templates, templateToType: context.templateToType, validator: ResolvedValue.radiusValidator, type: DivRadialGradientRadiusTemplate.self)) default: break } } + if let parent = parent { + centerXValue = centerXValue.merged(with: parent.centerX?.resolveOptionalValue(context: context, validator: ResolvedValue.centerXValidator, useOnlyLinks: true)) + centerYValue = centerYValue.merged(with: parent.centerY?.resolveOptionalValue(context: context, validator: ResolvedValue.centerYValidator, useOnlyLinks: true)) + radiusValue = radiusValue.merged(with: parent.radius?.resolveOptionalValue(context: context, validator: ResolvedValue.radiusValidator, useOnlyLinks: true)) + } var errors = mergeErrors( centerXValue.errorsOrWarnings?.map { Either.right($0.asError(deserializing: "center_x", level: .warning)) }, centerYValue.errorsOrWarnings?.map { Either.right($0.asError(deserializing: "center_y", level: .warning)) }, colorsValue.errorsOrWarnings?.map { Either.right($0.asError(deserializing: "colors", level: .error)) }, - radiusValue.errorsOrWarnings?.map { Either.right($0.asError(deserializing: "radius", level: .error)) } + radiusValue.errorsOrWarnings?.map { Either.right($0.asError(deserializing: "radius", level: .warning)) } ) if case .noValue = colorsValue { errors.append(.right(FieldError(fieldName: "colors", level: .error, error: .requiredFieldIsMissing))) } - if case .noValue = radiusValue { - errors.append(.right(FieldError(fieldName: "radius", level: .error, error: .requiredFieldIsMissing))) - } guard - let colorsNonNil = colorsValue.value, - let radiusNonNil = radiusValue.value + let colorsNonNil = colorsValue.value else { return .failure(NonEmptyArray(errors)!) } @@ -127,7 +124,7 @@ public final class DivRadialGradientTemplate: TemplateValue, TemplateDeserializa centerX: centerXValue.value, centerY: centerYValue.value, colors: colorsNonNil, - radius: radiusNonNil + radius: radiusValue.value ) return errors.isEmpty ? .success(result) : .partialSuccess(result, warnings: NonEmptyArray(errors)!) } @@ -149,6 +146,14 @@ public final class DivRadialGradientTemplate: TemplateValue, TemplateDeserializa } public func resolveParent(templates: Templates) throws -> DivRadialGradientTemplate { - return try mergedWithParent(templates: templates) + let merged = try mergedWithParent(templates: templates) + + return DivRadialGradientTemplate( + parent: nil, + centerX: merged.centerX?.tryResolveParent(templates: templates), + centerY: merged.centerY?.tryResolveParent(templates: templates), + colors: merged.colors, + radius: merged.radius?.tryResolveParent(templates: templates) + ) } } diff --git a/DivKit/generated_sources/DivSeparator.swift b/DivKit/generated_sources/DivSeparator.swift index d02a3ee9..75033191 100644 --- a/DivKit/generated_sources/DivSeparator.swift +++ b/DivKit/generated_sources/DivSeparator.swift @@ -7,6 +7,7 @@ import TemplatesSupport public final class DivSeparator: DivBase { public final class DelimiterStyle { + @frozen public enum Orientation: String, CaseIterable { case vertical = "vertical" case horizontal = "horizontal" diff --git a/DivKit/generated_sources/DivShape.swift b/DivKit/generated_sources/DivShape.swift index b5db18ba..0b5f79a7 100644 --- a/DivKit/generated_sources/DivShape.swift +++ b/DivKit/generated_sources/DivShape.swift @@ -5,6 +5,7 @@ import Foundation import Serialization import TemplatesSupport +@frozen public enum DivShape { case divRoundedRectangleShape(DivRoundedRectangleShape) diff --git a/DivKit/generated_sources/DivShapeTemplate.swift b/DivKit/generated_sources/DivShapeTemplate.swift index 2363d6ab..9a64b6fb 100644 --- a/DivKit/generated_sources/DivShapeTemplate.swift +++ b/DivKit/generated_sources/DivShapeTemplate.swift @@ -5,6 +5,7 @@ import Foundation import Serialization import TemplatesSupport +@frozen public enum DivShapeTemplate: TemplateValue { case divRoundedRectangleShapeTemplate(DivRoundedRectangleShapeTemplate) diff --git a/DivKit/generated_sources/DivSize.swift b/DivKit/generated_sources/DivSize.swift index eeeb009f..9db16d13 100644 --- a/DivKit/generated_sources/DivSize.swift +++ b/DivKit/generated_sources/DivSize.swift @@ -5,6 +5,7 @@ import Foundation import Serialization import TemplatesSupport +@frozen public enum DivSize { case divFixedSize(DivFixedSize) case divMatchParentSize(DivMatchParentSize) diff --git a/DivKit/generated_sources/DivSizeTemplate.swift b/DivKit/generated_sources/DivSizeTemplate.swift index d20e8aee..d8a8fd31 100644 --- a/DivKit/generated_sources/DivSizeTemplate.swift +++ b/DivKit/generated_sources/DivSizeTemplate.swift @@ -5,6 +5,7 @@ import Foundation import Serialization import TemplatesSupport +@frozen public enum DivSizeTemplate: TemplateValue { case divFixedSizeTemplate(DivFixedSizeTemplate) case divMatchParentSizeTemplate(DivMatchParentSizeTemplate) diff --git a/DivKit/generated_sources/DivSizeUnit.swift b/DivKit/generated_sources/DivSizeUnit.swift index a19b585f..5d6481e6 100644 --- a/DivKit/generated_sources/DivSizeUnit.swift +++ b/DivKit/generated_sources/DivSizeUnit.swift @@ -5,6 +5,7 @@ import Foundation import Serialization import TemplatesSupport +@frozen public enum DivSizeUnit: String, CaseIterable { case dp = "dp" case sp = "sp" diff --git a/DivKit/generated_sources/DivSlideTransition.swift b/DivKit/generated_sources/DivSlideTransition.swift index a31809af..acadc3e0 100644 --- a/DivKit/generated_sources/DivSlideTransition.swift +++ b/DivKit/generated_sources/DivSlideTransition.swift @@ -6,6 +6,7 @@ import Serialization import TemplatesSupport public final class DivSlideTransition: DivTransitionBase { + @frozen public enum Edge: String, CaseIterable { case left = "left" case top = "top" diff --git a/DivKit/generated_sources/DivTabs.swift b/DivKit/generated_sources/DivTabs.swift index bd987764..c7c9836b 100644 --- a/DivKit/generated_sources/DivTabs.swift +++ b/DivKit/generated_sources/DivTabs.swift @@ -33,6 +33,7 @@ public final class DivTabs: DivBase { } public final class TabTitleStyle { + @frozen public enum AnimationType: String, CaseIterable { case slide = "slide" case fade = "fade" diff --git a/DivKit/generated_sources/DivTemplate.swift b/DivKit/generated_sources/DivTemplate.swift index eff9f79f..c4f4af8b 100644 --- a/DivKit/generated_sources/DivTemplate.swift +++ b/DivKit/generated_sources/DivTemplate.swift @@ -5,6 +5,7 @@ import Foundation import Serialization import TemplatesSupport +@frozen public enum DivTemplate: TemplateValue { case divImageTemplate(DivImageTemplate) case divGifImageTemplate(DivGifImageTemplate) diff --git a/DivKit/generated_sources/DivTextGradient.swift b/DivKit/generated_sources/DivTextGradient.swift index 8d3b71be..b9f6567f 100644 --- a/DivKit/generated_sources/DivTextGradient.swift +++ b/DivKit/generated_sources/DivTextGradient.swift @@ -5,6 +5,7 @@ import Foundation import Serialization import TemplatesSupport +@frozen public enum DivTextGradient { case divLinearGradient(DivLinearGradient) case divRadialGradient(DivRadialGradient) diff --git a/DivKit/generated_sources/DivTextGradientTemplate.swift b/DivKit/generated_sources/DivTextGradientTemplate.swift index f24eec60..ddfb6e30 100644 --- a/DivKit/generated_sources/DivTextGradientTemplate.swift +++ b/DivKit/generated_sources/DivTextGradientTemplate.swift @@ -5,6 +5,7 @@ import Foundation import Serialization import TemplatesSupport +@frozen public enum DivTextGradientTemplate: TemplateValue { case divLinearGradientTemplate(DivLinearGradientTemplate) case divRadialGradientTemplate(DivRadialGradientTemplate) diff --git a/DivKit/generated_sources/DivTooltip.swift b/DivKit/generated_sources/DivTooltip.swift index 93d0ad55..a4f9920d 100644 --- a/DivKit/generated_sources/DivTooltip.swift +++ b/DivKit/generated_sources/DivTooltip.swift @@ -6,6 +6,7 @@ import Serialization import TemplatesSupport public final class DivTooltip { + @frozen public enum Position: String, CaseIterable { case left = "left" case topLeft = "top-left" diff --git a/DivKit/generated_sources/DivTransitionSelector.swift b/DivKit/generated_sources/DivTransitionSelector.swift index 5ff3e916..bfc2b43a 100644 --- a/DivKit/generated_sources/DivTransitionSelector.swift +++ b/DivKit/generated_sources/DivTransitionSelector.swift @@ -5,6 +5,7 @@ import Foundation import Serialization import TemplatesSupport +@frozen public enum DivTransitionSelector: String, CaseIterable { case none = "none" case dataChange = "data_change" diff --git a/DivKit/generated_sources/DivTransitionTrigger.swift b/DivKit/generated_sources/DivTransitionTrigger.swift index f315c494..0319c2f0 100644 --- a/DivKit/generated_sources/DivTransitionTrigger.swift +++ b/DivKit/generated_sources/DivTransitionTrigger.swift @@ -5,6 +5,7 @@ import Foundation import Serialization import TemplatesSupport +@frozen public enum DivTransitionTrigger: String, CaseIterable { case dataChange = "data_change" case stateChange = "state_change" diff --git a/DivKit/generated_sources/DivTrigger.swift b/DivKit/generated_sources/DivTrigger.swift index c570dfa1..93cc5bb8 100644 --- a/DivKit/generated_sources/DivTrigger.swift +++ b/DivKit/generated_sources/DivTrigger.swift @@ -6,6 +6,7 @@ import Serialization import TemplatesSupport public final class DivTrigger { + @frozen public enum Mode: String, CaseIterable { case onCondition = "on_condition" case onVariable = "on_variable" diff --git a/DivKit/generated_sources/DivVariable.swift b/DivKit/generated_sources/DivVariable.swift index 7c6b4e40..133467ec 100644 --- a/DivKit/generated_sources/DivVariable.swift +++ b/DivKit/generated_sources/DivVariable.swift @@ -5,6 +5,7 @@ import Foundation import Serialization import TemplatesSupport +@frozen public enum DivVariable { case stringVariable(StringVariable) case numberVariable(NumberVariable) diff --git a/DivKit/generated_sources/DivVariableTemplate.swift b/DivKit/generated_sources/DivVariableTemplate.swift index 030a9941..ca59bc51 100644 --- a/DivKit/generated_sources/DivVariableTemplate.swift +++ b/DivKit/generated_sources/DivVariableTemplate.swift @@ -5,6 +5,7 @@ import Foundation import Serialization import TemplatesSupport +@frozen public enum DivVariableTemplate: TemplateValue { case stringVariableTemplate(StringVariableTemplate) case numberVariableTemplate(NumberVariableTemplate) diff --git a/DivKit/generated_sources/DivVisibility.swift b/DivKit/generated_sources/DivVisibility.swift index d125a298..92373782 100644 --- a/DivKit/generated_sources/DivVisibility.swift +++ b/DivKit/generated_sources/DivVisibility.swift @@ -5,6 +5,7 @@ import Foundation import Serialization import TemplatesSupport +@frozen public enum DivVisibility: String, CaseIterable { case visible = "visible" case invisible = "invisible" diff --git a/DivKitExtensions/Lottie/AnimationHolder.swift b/DivKitExtensions/Lottie/AnimationHolder.swift index 8f82a13d..9b1dba66 100644 --- a/DivKitExtensions/Lottie/AnimationHolder.swift +++ b/DivKitExtensions/Lottie/AnimationHolder.swift @@ -10,11 +10,13 @@ public protocol AnimatableView: UIView { func setSource(_ source: AnimationSourceType) } +@frozen public enum AnimationRepeatMode { case restart case reverse } +@frozen public enum AnimationSourceType: Equatable { case json([String: Any]) case data(Data) diff --git a/LayoutKit/Interface/CommonHandler.swift b/LayoutKit/Interface/CommonHandler.swift index f656ffe9..c55a438f 100644 --- a/LayoutKit/Interface/CommonHandler.swift +++ b/LayoutKit/Interface/CommonHandler.swift @@ -1,5 +1,6 @@ import BaseTiny +@frozen public enum CommonHandler { case action(UserInterfaceAction) case block(Action) diff --git a/LayoutKit/Interface/UserInterfaceAction.swift b/LayoutKit/Interface/UserInterfaceAction.swift index b093aa2e..11afefb4 100644 --- a/LayoutKit/Interface/UserInterfaceAction.swift +++ b/LayoutKit/Interface/UserInterfaceAction.swift @@ -4,6 +4,7 @@ import BaseTiny import BaseUI public struct UserInterfaceAction: Equatable, Codable { + @frozen public enum Payload: Equatable { case empty // means just analytics logging case url(URL) @@ -32,6 +33,7 @@ public struct UserInterfaceAction: Equatable, Codable { } } + @frozen public enum DivActionSource: String { case tap case visibility diff --git a/LayoutKit/LayoutKit/Blocks/Container/ContainerBlock.swift b/LayoutKit/LayoutKit/Blocks/Container/ContainerBlock.swift index c5124363..218c346a 100644 --- a/LayoutKit/LayoutKit/Blocks/Container/ContainerBlock.swift +++ b/LayoutKit/LayoutKit/Blocks/Container/ContainerBlock.swift @@ -12,6 +12,7 @@ public final class ContainerBlock: BlockWithLayout { ) /// Determines direction in which child blocks are laid out in a container + @frozen public enum LayoutDirection: CaseIterable { /// Child blocks are laid out horizontally one after another case horizontal diff --git a/LayoutKit/LayoutKit/Blocks/EmptyBlock.swift b/LayoutKit/LayoutKit/Blocks/EmptyBlock.swift index 297ac5e2..afa82547 100644 --- a/LayoutKit/LayoutKit/Blocks/EmptyBlock.swift +++ b/LayoutKit/LayoutKit/Blocks/EmptyBlock.swift @@ -38,6 +38,10 @@ public func ==(lhs: EmptyBlock, rhs: EmptyBlock) -> Bool { lhs.widthTrait == rhs.widthTrait && lhs.heightTrait == rhs.heightTrait } +extension EmptyBlock { + public static let zeroSized = EmptyBlock(widthTrait: .fixed(0), heightTrait: .fixed(0)) +} + extension EmptyBlock: LayoutCachingDefaultImpl {} extension EmptyBlock: ElementStateUpdatingDefaultImpl {} diff --git a/LayoutKit/LayoutKit/Blocks/LayoutTrait.swift b/LayoutKit/LayoutKit/Blocks/LayoutTrait.swift index 578b3247..71c65145 100644 --- a/LayoutKit/LayoutKit/Blocks/LayoutTrait.swift +++ b/LayoutKit/LayoutKit/Blocks/LayoutTrait.swift @@ -1,6 +1,7 @@ import CoreGraphics import Foundation +@frozen public enum LayoutTrait: Equatable { /// Corresponding dimension is fixed and equal to associated value case fixed(CGFloat) diff --git a/LayoutKit/LayoutKit/Blocks/Slider/SliderModel.swift b/LayoutKit/LayoutKit/Blocks/Slider/SliderModel.swift index 91beaaef..a65e2427 100644 --- a/LayoutKit/LayoutKit/Blocks/Slider/SliderModel.swift +++ b/LayoutKit/LayoutKit/Blocks/Slider/SliderModel.swift @@ -27,7 +27,7 @@ public struct SliderModel: Equatable { static var empty: Self { ThumbModel( - block: SeparatorBlock(), + block: EmptyBlock.zeroSized, value: Binding(name: "", getValue: { _ in 0 }, userInterfaceActionFactory: { _, _ in nil }), size: .zero, offsetX: 0, @@ -139,8 +139,8 @@ public struct SliderModel: Equatable { inactiveMarkModel: nil, minValue: 0, maxValue: 0, - activeTrack: SeparatorBlock(), - inactiveTrack: SeparatorBlock() + activeTrack: EmptyBlock.zeroSized, + inactiveTrack: EmptyBlock.zeroSized ) } diff --git a/LayoutKit/LayoutKit/Blocks/TransitioningAnimation.swift b/LayoutKit/LayoutKit/Blocks/TransitioningAnimation.swift index 86572ed5..0f184c8b 100644 --- a/LayoutKit/LayoutKit/Blocks/TransitioningAnimation.swift +++ b/LayoutKit/LayoutKit/Blocks/TransitioningAnimation.swift @@ -3,6 +3,7 @@ import CoreGraphics import CommonCore public struct TransitioningAnimation: Equatable { + @frozen public enum Kind: String, Equatable, CaseIterable { case fade case scaleXY diff --git a/LayoutKit/LayoutKit/UI/Blocks/ImageBlock+UIViewRenderableBlock.swift b/LayoutKit/LayoutKit/UI/Blocks/ImageBlock+UIViewRenderableBlock.swift index a794b9e1..eebe53e4 100644 --- a/LayoutKit/LayoutKit/UI/Blocks/ImageBlock+UIViewRenderableBlock.swift +++ b/LayoutKit/LayoutKit/UI/Blocks/ImageBlock+UIViewRenderableBlock.swift @@ -4,7 +4,7 @@ import UIKit import CommonCore extension ImageBlock { - public static func makeBlockView() -> BlockView { RemoteImageView() } + public static func makeBlockView() -> BlockView { RemoteImageViewContainer(contentView: RemoteImageView()) } public func configureBlockView( _ view: BlockView, @@ -12,29 +12,30 @@ extension ImageBlock { overscrollDelegate _: ScrollDelegate?, renderingDelegate _: RenderingDelegate? ) { - let remoteImageView = view as! RemoteImageView - remoteImageView.appearanceAnimation = appearanceAnimation?.cast() - if remoteImageView.imageHolder !== imageHolder { - remoteImageView.imageHolder = imageHolder + let remoteImageViewContainer = view as! RemoteImageViewContainer + remoteImageViewContainer.contentView.appearanceAnimation = appearanceAnimation?.cast() + if remoteImageViewContainer.imageHolder !== imageHolder { + remoteImageViewContainer.imageHolder = imageHolder } - remoteImageView.imageContentMode = contentMode - remoteImageView.imageRedrawingColor = tintColor - remoteImageView.isUserInteractionEnabled = false - remoteImageView.applyAccessibility(accessibilityElement) + remoteImageViewContainer.contentView.imageContentMode = contentMode + remoteImageViewContainer.contentView.imageRedrawingColor = tintColor + remoteImageViewContainer.contentView.isUserInteractionEnabled = false + remoteImageViewContainer.isUserInteractionEnabled = false + remoteImageViewContainer.applyAccessibility(accessibilityElement) } public func canConfigureBlockView(_ view: BlockView) -> Bool { - view is RemoteImageView + view is RemoteImageViewContainer } } -extension RemoteImageView: BlockViewProtocol, VisibleBoundsTrackingLeaf { +extension RemoteImageViewContainer: BlockViewProtocol, VisibleBoundsTrackingLeaf { public var effectiveBackgroundColor: UIColor? { nil } } extension TransitioningAnimation { - fileprivate func cast() -> RemoteImageView.Animation { - RemoteImageView.Animation( + fileprivate func cast() -> ImageViewAnimation { + ImageViewAnimation( duration: duration.value, delay: delay.value, startAlpha: start, diff --git a/LayoutKit/LayoutKit/UI/Blocks/VideoBlock+UIViewRenderableBlock.swift b/LayoutKit/LayoutKit/UI/Blocks/VideoBlock+UIViewRenderableBlock.swift index 1542ee8e..44117501 100644 --- a/LayoutKit/LayoutKit/UI/Blocks/VideoBlock+UIViewRenderableBlock.swift +++ b/LayoutKit/LayoutKit/UI/Blocks/VideoBlock+UIViewRenderableBlock.swift @@ -35,6 +35,12 @@ private final class VideoBlockView: BlockView { init() { super.init(frame: .zero) + NotificationCenter.default.addObserver( + self, + selector: #selector(resumePlayer), + name: UIApplication.willEnterForegroundNotification, + object: nil + ) playerLayer.player = avPlayer playerLayer.videoGravity = .resizeAspectFill } @@ -44,6 +50,10 @@ private final class VideoBlockView: BlockView { fatalError("init(coder:) has not been implemented") } + deinit { + NotificationCenter.default.removeObserver(self) + } + private let avPlayer = AVQueuePlayer() private var playerLooper: AVPlayerLooper? @@ -60,4 +70,8 @@ private final class VideoBlockView: BlockView { playerLooper = AVPlayerLooper(player: avPlayer, templateItem: playerItem) avPlayer.play() } + + @objc private func resumePlayer() { + avPlayer.play() + } } diff --git a/LayoutKit/LayoutKit/UI/Views/Background+UIViewRendering.swift b/LayoutKit/LayoutKit/UI/Views/Background+UIViewRendering.swift index 9fef5c8b..27166684 100644 --- a/LayoutKit/LayoutKit/UI/Views/Background+UIViewRendering.swift +++ b/LayoutKit/LayoutKit/UI/Views/Background+UIViewRendering.swift @@ -89,7 +89,7 @@ extension BackgroundView { view = ColoredView() case .image: - view = RemoteImageView() + view = RemoteImageViewContainer(contentView: RemoteImageView()) case let .gradient(type): switch type { @@ -141,12 +141,12 @@ extension BackgroundView { coloredView.backgroundColor = UIColor(patternImage: image) case let .image(image): - let imageView = innerView as! RemoteImageView - imageView.clipsToBounds = true - imageView.alpha = CGFloat(image.alpha) - imageView.imageContentMode = image.contentMode - if imageView.imageHolder != image.imageHolder { - imageView.imageHolder = image.imageHolder + let imageViewContainer = innerView as! RemoteImageViewContainer + imageViewContainer.contentView.clipsToBounds = true + imageViewContainer.contentView.alpha = CGFloat(image.alpha) + imageViewContainer.contentView.imageContentMode = image.contentMode + if imageViewContainer.imageHolder != image.imageHolder { + imageViewContainer.imageHolder = image.imageHolder } case let .gradient(type): diff --git a/LayoutKit/LayoutKit/ViewModels/GalleryViewModel.swift b/LayoutKit/LayoutKit/ViewModels/GalleryViewModel.swift index e9378a09..ae9d5bec 100644 --- a/LayoutKit/LayoutKit/ViewModels/GalleryViewModel.swift +++ b/LayoutKit/LayoutKit/ViewModels/GalleryViewModel.swift @@ -11,6 +11,7 @@ public struct GalleryViewModel: Equatable { case fixedPaging(pageSize: CGFloat) } + @frozen public enum Direction: Equatable { case horizontal case vertical diff --git a/LayoutKit/LayoutKit/ViewModels/GalleryViewState.swift b/LayoutKit/LayoutKit/ViewModels/GalleryViewState.swift index 665e43b6..cdafca92 100644 --- a/LayoutKit/LayoutKit/ViewModels/GalleryViewState.swift +++ b/LayoutKit/LayoutKit/ViewModels/GalleryViewState.swift @@ -4,6 +4,7 @@ import Foundation import CommonCore public struct GalleryViewState: ElementState, Equatable { + @frozen public enum Position: Equatable { case offset(CGFloat) case paging(index: CGFloat) diff --git a/Serialization/DeserializationError.swift b/Serialization/DeserializationError.swift index a74e5add..bb18c335 100644 --- a/Serialization/DeserializationError.swift +++ b/Serialization/DeserializationError.swift @@ -2,6 +2,7 @@ import Foundation import CommonCore +@frozen public indirect enum DeserializationError: Error { case generic case nonUTF8String(string: String) diff --git a/Serialization/DeserializationResult.swift b/Serialization/DeserializationResult.swift index 83af6364..0817f569 100644 --- a/Serialization/DeserializationResult.swift +++ b/Serialization/DeserializationResult.swift @@ -69,6 +69,7 @@ extension NonEmptyArray where C: RangeReplaceableCollection, Element == Either< } } +@frozen public indirect enum DeserializationResult { public typealias Error = Either public typealias Errors = NonEmptyArray diff --git a/TemplatesSupport/Field.swift b/TemplatesSupport/Field.swift index aea41a8c..ed4021db 100644 --- a/TemplatesSupport/Field.swift +++ b/TemplatesSupport/Field.swift @@ -3,6 +3,7 @@ import CoreFoundation import CommonCore import Serialization +@frozen public indirect enum Field { case value(T) case link(Link)