diff --git a/Package.resolved b/Package.resolved index d34bb27..a784678 100644 --- a/Package.resolved +++ b/Package.resolved @@ -7,7 +7,7 @@ "location" : "https://github.com/OpenSwiftUIProject/DarwinPrivateFrameworks.git", "state" : { "branch" : "main", - "revision" : "b2642fdcc1ec7db833900421e479729305c939f8" + "revision" : "ad86657d9bdd98f7c40c977b34ff0c3de3e2fcee" } }, { @@ -16,7 +16,7 @@ "location" : "https://github.com/OpenSwiftUIProject/OpenCoreGraphics", "state" : { "branch" : "main", - "revision" : "03ddedb7e6b364573c37445f3aee267353cd4710" + "revision" : "8d63c405f565fb287042e0b8dc3bf0c4b8b2b56f" } }, { diff --git a/Sources/OpenRenderBox/Animation/ORBAnimation.swift b/Sources/OpenRenderBox/Animation/ORBAnimation.swift new file mode 100644 index 0000000..a7635bd --- /dev/null +++ b/Sources/OpenRenderBox/Animation/ORBAnimation.swift @@ -0,0 +1,52 @@ +// +// ORBAnimation.swift +// OpenRenderBox +// + +#if !canImport(ObjectiveC) +public import Foundation + +public class ORBAnimation: NSObject { + public private(set) var activeDuration: Double = 0 + + public override init() { + super.init() + } + + public func removeAll() { + _openRenderBoxUnimplementedFailure() + } + + public func evaluate(atTime time: Double) -> Float { + _openRenderBoxUnimplementedFailure() + } + + public func addBezierDuration(_ duration: Double, controlPoint1 point1: CGPoint, controlPoint2 point2: CGPoint) { + _openRenderBoxUnimplementedFailure() + } + + public func addDelay(_ delay: Double) { + _openRenderBoxUnimplementedFailure() + } + + public func addPreset(_ preset: UInt32, duration: Double) { + _openRenderBoxUnimplementedFailure() + } + + public func addRepeatCount(_ count: Double, autoreverses: Bool) { + _openRenderBoxUnimplementedFailure() + } + + public func addSampledFunction(withDuration duration: Double, count: UInt64, values: UnsafePointer) { + _openRenderBoxUnimplementedFailure() + } + + public func addSpeed(_ speed: Double) { + _openRenderBoxUnimplementedFailure() + } + + public func addSpringDuration(_ duration: Double, mass: Double, stiffness: Double, damping: Double, initialVelocity velocity: Double) { + _openRenderBoxUnimplementedFailure() + } +} +#endif diff --git a/Sources/OpenRenderBox/Animation/ORBSymbolAnimator.swift b/Sources/OpenRenderBox/Animation/ORBSymbolAnimator.swift new file mode 100644 index 0000000..934df81 --- /dev/null +++ b/Sources/OpenRenderBox/Animation/ORBSymbolAnimator.swift @@ -0,0 +1,117 @@ +// +// ORBSymbolAnimator.swift +// OpenRenderBox +// + +#if !canImport(ObjectiveC) +public import Foundation + +// MARK: - ORBSymbolAnimatorObserver + +public protocol ORBSymbolAnimatorObserver: AnyObject { + func symbolAnimatorDidChange(_ animator: ORBSymbolAnimator) +} + +// MARK: - ORBSymbolAnimator + +public class ORBSymbolAnimator: NSObject { + // MARK: - Properties + + public var glyph: AnyObject? + public var renderingMode: UInt32 = 0 + public var flipsRightToLeft: Bool = false + public var variableValue: Double = 0 + public var opacities: ORBColor = .clear + public var isHidden: Bool = false + public var scaleLevel: Int32 = 0 + public var anchorPoint: CGPoint = .zero + public var position: CGPoint = .zero + public var size: CGSize = .zero + public var presentationPosition: CGPoint = .zero + public var currentTime: Double = 0 + public private(set) var maxVelocity: Double = 0 + public private(set) var version: UInt32 = 0 + public private(set) var isAnimating: Bool = false + public private(set) var alignmentRect: CGRect = .zero + public private(set) var unroundedAlignmentRect: CGRect = .zero + public private(set) var styleMask: UInt32 = 0 + public var depth: UInt32 = 0 + public private(set) var boundingRect: CGRect = .zero + + public override init() { + super.init() + } + + // MARK: - Animation Management + + public func addAnimation(_ animation: UInt32, options: Any?) -> UInt32 { + _openRenderBoxUnimplementedFailure() + } + + public func removeAllAnimations() { + _openRenderBoxUnimplementedFailure() + } + + public func removeAnimation(_ animation: UInt32) { + _openRenderBoxUnimplementedFailure() + } + + public func removeAnimation(withID animationID: UInt32) { + _openRenderBoxUnimplementedFailure() + } + + public func cancelAllAnimations() { + _openRenderBoxUnimplementedFailure() + } + + public func cancelAnimation(_ animation: UInt32) { + _openRenderBoxUnimplementedFailure() + } + + public func cancelAnimation(withID animationID: UInt32) { + _openRenderBoxUnimplementedFailure() + } + + public func setPriority(_ priority: Float, ofAnimationWithID animationID: UInt32) { + _openRenderBoxUnimplementedFailure() + } + + // MARK: - Update + + public func beginUpdate(withRenderingMode mode: UInt32) -> OpaquePointer { + _openRenderBoxUnimplementedFailure() + } + + public func beginUpdate(withRenderingMode mode: UInt32, position: UnsafePointer?, size: UnsafePointer?, flags: UInt32) -> OpaquePointer { + _openRenderBoxUnimplementedFailure() + } + + public func endUpdate(_ update: OpaquePointer) { + _openRenderBoxUnimplementedFailure() + } + + public func copyDebugDescription(for update: OpaquePointer) -> Any? { + _openRenderBoxUnimplementedFailure() + } + + // MARK: - Color + + public func color(forStyle style: UInt32) -> ORBColor { + _openRenderBoxUnimplementedFailure() + } + + public func setColor(_ color: ORBColor, forStyle style: UInt32) { + _openRenderBoxUnimplementedFailure() + } + + // MARK: - Observer + + public func addObserver(_ observer: any ORBSymbolAnimatorObserver) { + _openRenderBoxUnimplementedFailure() + } + + public func removeObserver(_ observer: any ORBSymbolAnimatorObserver) { + _openRenderBoxUnimplementedFailure() + } +} +#endif diff --git a/Sources/OpenRenderBoxCxx/Animation/ORBAnimation.m b/Sources/OpenRenderBoxCxx/Animation/ORBAnimation.m new file mode 100644 index 0000000..b8b8fbf --- /dev/null +++ b/Sources/OpenRenderBoxCxx/Animation/ORBAnimation.m @@ -0,0 +1,15 @@ +// +// ORBAnimation.m +// OpenRenderBox + +#include + +#if ORB_OBJC_FOUNDATION + +@implementation ORBAnimation + +// TODO + +@end + +#endif /* ORB_OBJC_FOUNDATION */ diff --git a/Sources/OpenRenderBoxCxx/Animation/ORBSymbolAnimator.m b/Sources/OpenRenderBoxCxx/Animation/ORBSymbolAnimator.m new file mode 100644 index 0000000..7aa3634 --- /dev/null +++ b/Sources/OpenRenderBoxCxx/Animation/ORBSymbolAnimator.m @@ -0,0 +1,15 @@ +// +// ORBSymbolAnimator.m +// OpenRenderBox + +#include + +#if ORB_OBJC_FOUNDATION + +@implementation ORBSymbolAnimator + +// TODO + +@end + +#endif /* ORB_OBJC_FOUNDATION */ diff --git a/Sources/OpenRenderBoxCxx/include/OpenRenderBoxObjC/Animation/ORBAnimation.h b/Sources/OpenRenderBoxCxx/include/OpenRenderBoxObjC/Animation/ORBAnimation.h new file mode 100644 index 0000000..50c4567 --- /dev/null +++ b/Sources/OpenRenderBoxCxx/include/OpenRenderBoxObjC/Animation/ORBAnimation.h @@ -0,0 +1,37 @@ +// +// ORBAnimation.h +// OpenRenderBox + +#pragma once + +#include + +#if ORB_OBJC_FOUNDATION + +#include +#include + +ORB_ASSUME_NONNULL_BEGIN + +// TODO +@interface ORBAnimation : NSObject + +@property (readonly, nonatomic) double activeDuration; + +- (id)copyWithZone:(nullable NSZone *)zone; +- (BOOL)isEqual:(nullable id)object; +- (void)removeAll; +- (float)evaluateAtTime:(double)time; +- (void)addBezierDuration:(double)duration controlPoint1:(CGPoint)point1 controlPoint2:(CGPoint)point2; +- (void)addDelay:(double)delay; +- (void)addPreset:(unsigned int)preset duration:(double)duration; +- (void)addRepeatCount:(double)count autoreverses:(BOOL)autoreverses; +- (void)addSampledFunctionWithDuration:(double)duration count:(unsigned long long)count values:(const float *)values; +- (void)addSpeed:(double)speed; +- (void)addSpringDuration:(double)duration mass:(double)mass stiffness:(double)stiffness damping:(double)damping initialVelocity:(double)velocity; + +@end + +ORB_ASSUME_NONNULL_END + +#endif /* ORB_OBJC_FOUNDATION */ diff --git a/Sources/OpenRenderBoxCxx/include/OpenRenderBoxObjC/Animation/ORBSymbolAnimator.h b/Sources/OpenRenderBoxCxx/include/OpenRenderBoxObjC/Animation/ORBSymbolAnimator.h new file mode 100644 index 0000000..b369053 --- /dev/null +++ b/Sources/OpenRenderBoxCxx/include/OpenRenderBoxObjC/Animation/ORBSymbolAnimator.h @@ -0,0 +1,67 @@ +// +// ORBSymbolAnimator.h +// OpenRenderBox + +#pragma once + +#include + +#if ORB_OBJC_FOUNDATION + +#include +#include +#include +#include + +ORB_ASSUME_NONNULL_BEGIN + +@class CUINamedVectorGlyph; +struct _ORBSymbolUpdate; + +// TODO +@interface ORBSymbolAnimator : NSObject + +@property (retain, nonatomic, nullable) CUINamedVectorGlyph *glyph; +@property (nonatomic) unsigned int renderingMode; +@property (nonatomic) BOOL flipsRightToLeft; +@property (nonatomic) double variableValue; +@property (nonatomic) ORBColor opacities; +@property (nonatomic, getter=isHidden) BOOL hidden; +@property (nonatomic) int scaleLevel; +@property (nonatomic) CGPoint anchorPoint; +@property (nonatomic) CGPoint position; +@property (nonatomic) CGSize size; +@property (nonatomic) CGPoint presentationPosition; +@property (nonatomic) double currentTime; +@property (readonly, nonatomic) double maxVelocity; +@property (readonly, nonatomic) unsigned int version; +@property (readonly, nonatomic, getter=isAnimating) BOOL animating; +@property (readonly, nonatomic) CGRect alignmentRect; +@property (readonly, nonatomic) CGRect unroundedAlignmentRect; +@property (readonly, nonatomic) unsigned int styleMask; +@property (nonatomic) unsigned int depth; +@property (readonly, nonatomic) CGRect boundingRect; + +/* instance methods */ +- (unsigned int)addAnimation:(unsigned int)animation options:(nullable id)options; +- (struct _ORBSymbolUpdate *)beginUpdateWithRenderingMode:(unsigned int)mode; +- (void)endUpdate:(struct _ORBSymbolUpdate *)update; +- (void)removeAllAnimations; +- (void)removeAnimation:(unsigned int)animation; +- (struct _ORBSymbolUpdate *)beginUpdateWithRenderingMode:(unsigned int)mode position:(nullable const CGPoint *)position size:(nullable const CGSize *)size flags:(unsigned int)flags; +- (void)cancelAllAnimations; +- (void)cancelAnimation:(unsigned int)animation; +- (void)cancelAnimationWithID:(unsigned int)animationID; +- (ORBColor)colorForStyle:(unsigned int)style; +- (nullable id)copyDebugDescriptionForUpdate:(struct _ORBSymbolUpdate *)update; +- (void)removeAnimationWithID:(unsigned int)animationID; +- (void)setColor:(ORBColor)color forStyle:(unsigned int)style; +- (void)setPriority:(float)priority ofAnimationWithID:(unsigned int)animationID; +- (void)addObserver:(id)observer; +- (void)removeObserver:(id)observer; + +@end + +ORB_ASSUME_NONNULL_END + +#endif /* ORB_OBJC_FOUNDATION */ diff --git a/Sources/OpenRenderBoxCxx/include/OpenRenderBoxObjC/Animation/ORBSymbolAnimatorObserver.h b/Sources/OpenRenderBoxCxx/include/OpenRenderBoxObjC/Animation/ORBSymbolAnimatorObserver.h new file mode 100644 index 0000000..2927f66 --- /dev/null +++ b/Sources/OpenRenderBoxCxx/include/OpenRenderBoxObjC/Animation/ORBSymbolAnimatorObserver.h @@ -0,0 +1,29 @@ +// +// ORBSymbolAnimatorObserver.h +// OpenRenderBox + +#pragma once + +#include + +#if ORB_OBJC_FOUNDATION + +#include + +ORB_ASSUME_NONNULL_BEGIN + +@class ORBSymbolAnimator; + +@protocol ORBSymbolAnimatorObserver + +@required + +- (void)symbolAnimatorDidChange:(ORBSymbolAnimator *)animator; + +@optional + +@end + +ORB_ASSUME_NONNULL_END + +#endif /* ORB_OBJC_FOUNDATION */ diff --git a/Sources/OpenRenderBoxShims/Export.swift b/Sources/OpenRenderBoxShims/Export.swift index 22e364f..92872ac 100644 --- a/Sources/OpenRenderBoxShims/Export.swift +++ b/Sources/OpenRenderBoxShims/Export.swift @@ -11,6 +11,9 @@ public typealias ORBLayerDelegate = RBLayerDelegate public typealias ORBDisplayList = RBDisplayList public typealias ORBPath = RBPath public typealias ORBUUID = RBUUID +public typealias ORBAnimation = RBAnimation +public typealias ORBSymbolAnimator = RBSymbolAnimator +public typealias ORBSymbolAnimatorObserver = RBSymbolAnimatorObserver public let renderBoxEnabled = true #else @_exported import OpenRenderBox diff --git a/Tests/OpenRenderBoxCompatibilityTests/AnimationTests.swift b/Tests/OpenRenderBoxCompatibilityTests/AnimationTests.swift new file mode 100644 index 0000000..d822f64 --- /dev/null +++ b/Tests/OpenRenderBoxCompatibilityTests/AnimationTests.swift @@ -0,0 +1,82 @@ +// +// AnimationTests.swift +// OpenRenderBoxCompatibilityTests + +import Testing + +#if canImport(CoreGraphics) +import CoreGraphics + +@Suite(.enabled(if: compatibilityTestEnabled)) +struct AnimationTests { + @Test + func basicAnimation() { + let animation = ORBAnimation() + + // activeDuration should be 0 for empty animation + #expect(animation.activeDuration == 0) + + // removeAll on empty animation should not crash + animation.removeAll() + + // evaluateAtTime should return a value + let value = animation.evaluate(atTime: 0) + _ = value + } + + @Test + func addBezier() { + let animation = ORBAnimation() + animation.addBezierDuration(0.3, controlPoint1: CGPoint(x: 0.42, y: 0), controlPoint2: CGPoint(x: 0.58, y: 1)) + #expect(animation.activeDuration > 0) + } + + @Test + func addDelay() { + let animation = ORBAnimation() + animation.addDelay(0.5) + #expect(animation.activeDuration == 0.5) + } + + @Test + func addSpeed() { + let animation = ORBAnimation() + animation.addBezierDuration(1.0, controlPoint1: .zero, controlPoint2: CGPoint(x: 1, y: 1)) + let baseDuration = animation.activeDuration + animation.addSpeed(2.0) + _ = baseDuration + } + + @Test + func addSpring() { + let animation = ORBAnimation() + animation.addSpringDuration(0.5, mass: 1.0, stiffness: 100, damping: 10, initialVelocity: 0) + #expect(animation.activeDuration > 0) + } + + @Test + func addRepeat() { + let animation = ORBAnimation() + animation.addBezierDuration(0.3, controlPoint1: .zero, controlPoint2: CGPoint(x: 1, y: 1)) + animation.addRepeatCount(3, autoreverses: true) + #expect(animation.activeDuration > 0) + } + + @Test + func addPreset() { + let animation = ORBAnimation() + animation.addPreset(0, duration: 0.5) + #expect(animation.activeDuration > 0) + } + + @Test + func copyAndEqual() { + let animation = ORBAnimation() + animation.addDelay(0.5) + let copy = animation.copy() as! ORBAnimation + #expect(animation.isEqual(copy)) + #expect(copy.activeDuration == animation.activeDuration) + } +} + +#endif diff --git a/Tests/OpenRenderBoxCompatibilityTests/Shims.swift b/Tests/OpenRenderBoxCompatibilityTests/Shims.swift index 0160549..3ef5e40 100644 --- a/Tests/OpenRenderBoxCompatibilityTests/Shims.swift +++ b/Tests/OpenRenderBoxCompatibilityTests/Shims.swift @@ -9,6 +9,9 @@ public typealias ORBDevice = RBDevice public typealias ORBLayer = RBLayer public typealias ORBPath = RBPath public typealias ORBUUID = RBUUID +public typealias ORBAnimation = RBAnimation +public typealias ORBSymbolAnimator = RBSymbolAnimator +public typealias ORBSymbolAnimatorObserver = RBSymbolAnimatorObserver public let compatibilityTestEnabled = true #else @_exported import OpenRenderBox diff --git a/Tests/OpenRenderBoxCompatibilityTests/SymbolAnimatorTests.swift b/Tests/OpenRenderBoxCompatibilityTests/SymbolAnimatorTests.swift new file mode 100644 index 0000000..d25bf2a --- /dev/null +++ b/Tests/OpenRenderBoxCompatibilityTests/SymbolAnimatorTests.swift @@ -0,0 +1,86 @@ +// +// SymbolAnimatorTests.swift +// OpenRenderBoxCompatibilityTests + +import Testing + +#if canImport(CoreGraphics) +import CoreGraphics + +@Suite(.enabled(if: compatibilityTestEnabled)) +struct SymbolAnimatorTests { + @Test + func basicProperties() { + let animator = ORBSymbolAnimator() + + // Default state + #expect(animator.isAnimating == false) + #expect(animator.isHidden == false) + + // Geometry + animator.anchorPoint = CGPoint(x: 0.5, y: 0.5) + #expect(animator.anchorPoint.x == 0.5) + #expect(animator.anchorPoint.y == 0.5) + + animator.position = CGPoint(x: 100, y: 200) + #expect(animator.position.x == 100) + #expect(animator.position.y == 200) + + animator.size = CGSize(width: 44, height: 44) + #expect(animator.size.width == 44) + #expect(animator.size.height == 44) + } + + @Test + func renderingProperties() { + let animator = ORBSymbolAnimator() + + animator.renderingMode = 1 + #expect(animator.renderingMode == 1) + + animator.flipsRightToLeft = true + #expect(animator.flipsRightToLeft == true) + + animator.isHidden = true + #expect(animator.isHidden == true) + + animator.depth = 2 + #expect(animator.depth == 2) + + animator.scaleLevel = 1 + #expect(animator.scaleLevel == 1) + } + + @Test + func animationManagement() { + let animator = ORBSymbolAnimator() + + // removeAllAnimations on empty animator should not crash + animator.removeAllAnimations() + + // cancelAllAnimations on empty animator should not crash + animator.cancelAllAnimations() + } + + @Test + func timeAndVelocity() { + let animator = ORBSymbolAnimator() + + animator.currentTime = 1.5 + #expect(animator.currentTime == 1.5) + + _ = animator.maxVelocity + _ = animator.version + } + + @Test + func rectProperties() { + let animator = ORBSymbolAnimator() + _ = animator.alignmentRect + _ = animator.unroundedAlignmentRect + _ = animator.boundingRect + _ = animator.styleMask + } +} + +#endif