From f6e5d293851d7d7840de22555fd40896dbff5faa Mon Sep 17 00:00:00 2001 From: Kyle Date: Sat, 24 Oct 2026 16:40:55 +0800 Subject: [PATCH] feat(animation): add symbol animation types and option keys --- Package.resolved | 2 +- .../Animation/ORBSymbolAnimation.swift | 55 +++++++++++++ .../Animation/ORBSymbolAnimator.swift | 9 ++- .../Animation/ORBSymbolAnimation.m | 29 +++++++ .../OpenRenderBox/ORBSymbolAnimation.h | 79 +++++++++++++++++++ .../include/OpenRenderBox/OpenRenderBox.h | 1 + .../Animation/ORBSymbolAnimation.h | 39 +++++++++ .../Animation/ORBSymbolAnimator.h | 9 ++- Sources/OpenRenderBoxShims/ORBShims.swift | 2 + .../ORBShims.swift | 2 + .../SymbolAnimationTests.swift | 52 ++++++++++++ .../SymbolAnimatorTests.swift | 4 +- 12 files changed, 272 insertions(+), 11 deletions(-) create mode 100644 Sources/OpenRenderBox/Animation/ORBSymbolAnimation.swift create mode 100644 Sources/OpenRenderBoxCxx/Animation/ORBSymbolAnimation.m create mode 100644 Sources/OpenRenderBoxCxx/include/OpenRenderBox/ORBSymbolAnimation.h create mode 100644 Sources/OpenRenderBoxCxx/include/OpenRenderBoxObjC/Animation/ORBSymbolAnimation.h create mode 100644 Tests/OpenRenderBoxCompatibilityTests/SymbolAnimationTests.swift diff --git a/Package.resolved b/Package.resolved index 9758f19..51f60e4 100644 --- a/Package.resolved +++ b/Package.resolved @@ -7,7 +7,7 @@ "location" : "https://github.com/OpenSwiftUIProject/DarwinPrivateFrameworks.git", "state" : { "branch" : "main", - "revision" : "10ace67d6ed884f4dbae0c52e157adf2bbb5301c" + "revision" : "d46803e009851e31ff8190fcf2cfc9dee3ed1e0e" } }, { diff --git a/Sources/OpenRenderBox/Animation/ORBSymbolAnimation.swift b/Sources/OpenRenderBox/Animation/ORBSymbolAnimation.swift new file mode 100644 index 0000000..9f14c39 --- /dev/null +++ b/Sources/OpenRenderBox/Animation/ORBSymbolAnimation.swift @@ -0,0 +1,55 @@ +// +// ORBSymbolAnimation.swift +// OpenRenderBox + +#if !canImport(ObjectiveC) +public import OpenRenderBoxCxx + +extension ORBSymbolAnimation { + public struct OptionKey: RawRepresentable, Hashable, Sendable { + public let rawValue: String + + public init(rawValue: String) { + self.rawValue = rawValue + } + + public static let timing = Self(rawValue: "timingObject") + + public static let speed = Self(rawValue: "speed") + + public static let repeatCount = Self(rawValue: "repeatCount") + + public static let repeatDelay = Self(rawValue: "repeatDelay") + + public static let priority = Self(rawValue: "priority") + + public static let byLayer = Self(rawValue: "byLayer") + + public static let onCompletion = Self(rawValue: "onCompletion") + + public static let breatheOptions = Self(rawValue: "breatheOptions") + + public static let bounceOptions = Self(rawValue: "bounceOptions") + + public static let wiggleOptions = Self(rawValue: "wiggleOptions") + + public static let wiggleAngle = Self(rawValue: "wiggleAngle") + + public static let rotateOptions = Self(rawValue: "rotateOptions") + + public static let variableColorOptions = Self(rawValue: "variableColorOptions") + + public static let appearDisappearOptions = Self(rawValue: "appearDisappearOptions") + + public static let replaceOptions = Self(rawValue: "replaceOptions") + + public static let interpolateOptions = Self(rawValue: "interpolateOptions") + + public static let clampsSpeed = Self(rawValue: "clampsSpeed") + + public static let symbolDefaults = Self(rawValue: "symbolDefaults") + + public static let pulseOptions = Self(rawValue: "pulseOptions") + } +} +#endif diff --git a/Sources/OpenRenderBox/Animation/ORBSymbolAnimator.swift b/Sources/OpenRenderBox/Animation/ORBSymbolAnimator.swift index 934df81..59b9241 100644 --- a/Sources/OpenRenderBox/Animation/ORBSymbolAnimator.swift +++ b/Sources/OpenRenderBox/Animation/ORBSymbolAnimator.swift @@ -5,6 +5,7 @@ #if !canImport(ObjectiveC) public import Foundation +public import OpenRenderBoxCxx // MARK: - ORBSymbolAnimatorObserver @@ -23,7 +24,7 @@ public class ORBSymbolAnimator: NSObject { public var variableValue: Double = 0 public var opacities: ORBColor = .clear public var isHidden: Bool = false - public var scaleLevel: Int32 = 0 + public var scaleLevel: ORBSymbolScaleLevel = .normal public var anchorPoint: CGPoint = .zero public var position: CGPoint = .zero public var size: CGSize = .zero @@ -44,7 +45,7 @@ public class ORBSymbolAnimator: NSObject { // MARK: - Animation Management - public func addAnimation(_ animation: UInt32, options: Any?) -> UInt32 { + public func addAnimation(_ animation: ORBSymbolAnimation, options: Any?) -> UInt32 { _openRenderBoxUnimplementedFailure() } @@ -52,7 +53,7 @@ public class ORBSymbolAnimator: NSObject { _openRenderBoxUnimplementedFailure() } - public func removeAnimation(_ animation: UInt32) { + public func removeAnimation(_ animation: ORBSymbolAnimation) { _openRenderBoxUnimplementedFailure() } @@ -64,7 +65,7 @@ public class ORBSymbolAnimator: NSObject { _openRenderBoxUnimplementedFailure() } - public func cancelAnimation(_ animation: UInt32) { + public func cancelAnimation(_ animation: ORBSymbolAnimation) { _openRenderBoxUnimplementedFailure() } diff --git a/Sources/OpenRenderBoxCxx/Animation/ORBSymbolAnimation.m b/Sources/OpenRenderBoxCxx/Animation/ORBSymbolAnimation.m new file mode 100644 index 0000000..2d9b293 --- /dev/null +++ b/Sources/OpenRenderBoxCxx/Animation/ORBSymbolAnimation.m @@ -0,0 +1,29 @@ +// +// ORBSymbolAnimation.m +// OpenRenderBox + +#include + +#if ORB_OBJC_FOUNDATION + +ORBSymbolAnimationOptionKey const ORBSymbolAnimationTiming = @"timingObject"; +ORBSymbolAnimationOptionKey const ORBSymbolAnimationSpeed = @"speed"; +ORBSymbolAnimationOptionKey const ORBSymbolAnimationRepeatCount = @"repeatCount"; +ORBSymbolAnimationOptionKey const ORBSymbolAnimationRepeatDelay = @"repeatDelay"; +ORBSymbolAnimationOptionKey const ORBSymbolAnimationPriority = @"priority"; +ORBSymbolAnimationOptionKey const ORBSymbolAnimationByLayer = @"byLayer"; +ORBSymbolAnimationOptionKey const ORBSymbolAnimationOnCompletion = @"onCompletion"; +ORBSymbolAnimationOptionKey const ORBSymbolAnimationBreatheOptions = @"breatheOptions"; +ORBSymbolAnimationOptionKey const ORBSymbolAnimationBounceOptions = @"bounceOptions"; +ORBSymbolAnimationOptionKey const ORBSymbolAnimationWiggleOptions = @"wiggleOptions"; +ORBSymbolAnimationOptionKey const ORBSymbolAnimationWiggleAngle = @"wiggleAngle"; +ORBSymbolAnimationOptionKey const ORBSymbolAnimationRotateOptions = @"rotateOptions"; +ORBSymbolAnimationOptionKey const ORBSymbolAnimationVariableColorOptions = @"variableColorOptions"; +ORBSymbolAnimationOptionKey const ORBSymbolAnimationAppearDisappearOptions = @"appearDisappearOptions"; +ORBSymbolAnimationOptionKey const ORBSymbolAnimationReplaceOptions = @"replaceOptions"; +ORBSymbolAnimationOptionKey const ORBSymbolAnimationInterpolateOptions = @"interpolateOptions"; +ORBSymbolAnimationOptionKey const ORBSymbolAnimationClampsSpeed = @"clampsSpeed"; +ORBSymbolAnimationOptionKey const ORBSymbolAnimationSymbolDefaults = @"symbolDefaults"; +ORBSymbolAnimationOptionKey const ORBSymbolAnimationPulseOptions = @"pulseOptions"; + +#endif /* ORB_OBJC_FOUNDATION */ diff --git a/Sources/OpenRenderBoxCxx/include/OpenRenderBox/ORBSymbolAnimation.h b/Sources/OpenRenderBoxCxx/include/OpenRenderBox/ORBSymbolAnimation.h new file mode 100644 index 0000000..f1d558b --- /dev/null +++ b/Sources/OpenRenderBoxCxx/include/OpenRenderBox/ORBSymbolAnimation.h @@ -0,0 +1,79 @@ +// +// ORBSymbolAnimation.h +// OpenRenderBox + +#pragma once + +#include + +ORB_EXTERN_C_BEGIN + +typedef ORB_ENUM(uint32_t, ORBSymbolAnimation) { + ORBSymbolAnimationVariableValue = 0, + ORBSymbolAnimationVariableColor = 1, + ORBSymbolAnimationPulse = 2, + ORBSymbolAnimationBounce = 3, + ORBSymbolAnimationScale = 4, + ORBSymbolAnimationAppear = 5, + ORBSymbolAnimationReplace = 6, + ORBSymbolAnimationInterpolate = 7, + ORBSymbolAnimationPush = 8, + ORBSymbolAnimationDisappear = 9, + ORBSymbolAnimationWiggle = 10, + ORBSymbolAnimationRotate = 11, + ORBSymbolAnimationBreathe = 12, +}; + +typedef ORB_ENUM(int32_t, ORBSymbolScaleLevel) { + ORBSymbolScaleLevelDown = -1, + ORBSymbolScaleLevelNormal = 0, + ORBSymbolScaleLevelUp = 1, +}; + +typedef ORB_OPTIONS(uint32_t, ORBSymbolAnimationBreatheFlags) { + ORBSymbolAnimationBreatheFlagsOnlyScale = 1 << 0, +} ORB_SWIFT_NAME(ORBSymbolAnimation.BreatheOptions); + +typedef ORB_OPTIONS(uint32_t, ORBSymbolAnimationBounceFlags) { + ORBSymbolAnimationBounceFlagsUp = 1 << 0, + ORBSymbolAnimationBounceFlagsDown = 1 << 1, + ORBSymbolAnimationBounceFlagsContinuous = 1 << 4, +} ORB_SWIFT_NAME(ORBSymbolAnimation.BounceOptions); + +typedef ORB_OPTIONS(uint32_t, ORBSymbolAnimationWiggleFlags) { + ORBSymbolAnimationWiggleFlagsLocalized = 0x1, + ORBSymbolAnimationWiggleFlagsRotational = 0x3, + ORBSymbolAnimationWiggleFlagsForward = 0x10, + ORBSymbolAnimationWiggleFlagsBackward = 0x20, + ORBSymbolAnimationWiggleFlagsDirectionMask = 0xF0, + ORBSymbolAnimationWiggleFlagsContinuous = 0x100, +} ORB_SWIFT_NAME(ORBSymbolAnimation.WiggleOptions); + +typedef ORB_OPTIONS(uint32_t, ORBSymbolAnimationRotateFlags) { + ORBSymbolAnimationRotateFlagsClockwise = 1 << 0, + ORBSymbolAnimationRotateFlagsCounterClockwise = 1 << 1, + ORBSymbolAnimationRotateFlagsContinuous = 1 << 4, +} ORB_SWIFT_NAME(ORBSymbolAnimation.RotateOptions); + +typedef ORB_OPTIONS(uint32_t, ORBSymbolAnimationVariableColorFlags) { + ORBSymbolAnimationVariableColorFlagsIterative = 1 << 0, + ORBSymbolAnimationVariableColorFlagsHideInactiveLayers = 1 << 1, + ORBSymbolAnimationVariableColorFlagsReversing = 1 << 3, +} ORB_SWIFT_NAME(ORBSymbolAnimation.VariableColorOptions); + +typedef ORB_OPTIONS(uint32_t, ORBSymbolAnimationAppearDisappearFlags) { + ORBSymbolAnimationAppearDisappearFlagsDown = 0x2, + ORBSymbolAnimationAppearDisappearFlagsUp = 0x3, + ORBSymbolAnimationAppearDisappearFlagsWholeSymbol = 0x10, +} ORB_SWIFT_NAME(ORBSymbolAnimation.AppearDisappearOptions); + +typedef ORB_OPTIONS(uint32_t, ORBSymbolAnimationReplaceFlags) { + ORBSymbolAnimationReplaceFlagsDown = 0x2, + ORBSymbolAnimationReplaceFlagsUp = 0x3, + ORBSymbolAnimationReplaceFlagsOffUp = 0x4, + ORBSymbolAnimationReplaceFlagsWholeSymbol = 0x10, + ORBSymbolAnimationReplaceFlagsNoSlashes = 0x20, + ORBSymbolAnimationReplaceFlagsNoBadges = 0x40, +} ORB_SWIFT_NAME(ORBSymbolAnimation.ReplaceOptions); + +ORB_EXTERN_C_END diff --git a/Sources/OpenRenderBoxCxx/include/OpenRenderBox/OpenRenderBox.h b/Sources/OpenRenderBoxCxx/include/OpenRenderBox/OpenRenderBox.h index 8794461..1528e35 100644 --- a/Sources/OpenRenderBoxCxx/include/OpenRenderBox/OpenRenderBox.h +++ b/Sources/OpenRenderBoxCxx/include/OpenRenderBox/OpenRenderBox.h @@ -1,4 +1,5 @@ #include +#include #include #include #include diff --git a/Sources/OpenRenderBoxCxx/include/OpenRenderBoxObjC/Animation/ORBSymbolAnimation.h b/Sources/OpenRenderBoxCxx/include/OpenRenderBoxObjC/Animation/ORBSymbolAnimation.h new file mode 100644 index 0000000..c7b5de9 --- /dev/null +++ b/Sources/OpenRenderBoxCxx/include/OpenRenderBoxObjC/Animation/ORBSymbolAnimation.h @@ -0,0 +1,39 @@ +// +// ORBSymbolAnimation.h +// OpenRenderBox + +#pragma once + +#include + +#if ORB_OBJC_FOUNDATION + +#include + +ORB_ASSUME_NONNULL_BEGIN + +typedef NSString *ORBSymbolAnimationOptionKey NS_TYPED_EXTENSIBLE_ENUM ORB_SWIFT_NAME(ORBSymbolAnimation.OptionKey); + +ORB_EXPORT ORBSymbolAnimationOptionKey const ORBSymbolAnimationTiming; +ORB_EXPORT ORBSymbolAnimationOptionKey const ORBSymbolAnimationSpeed; +ORB_EXPORT ORBSymbolAnimationOptionKey const ORBSymbolAnimationClampsSpeed; +ORB_EXPORT ORBSymbolAnimationOptionKey const ORBSymbolAnimationRepeatCount; +ORB_EXPORT ORBSymbolAnimationOptionKey const ORBSymbolAnimationRepeatDelay; +ORB_EXPORT ORBSymbolAnimationOptionKey const ORBSymbolAnimationPriority; +ORB_EXPORT ORBSymbolAnimationOptionKey const ORBSymbolAnimationByLayer; +ORB_EXPORT ORBSymbolAnimationOptionKey const ORBSymbolAnimationOnCompletion; +ORB_EXPORT ORBSymbolAnimationOptionKey const ORBSymbolAnimationSymbolDefaults; +ORB_EXPORT ORBSymbolAnimationOptionKey const ORBSymbolAnimationBreatheOptions; +ORB_EXPORT ORBSymbolAnimationOptionKey const ORBSymbolAnimationPulseOptions; +ORB_EXPORT ORBSymbolAnimationOptionKey const ORBSymbolAnimationBounceOptions; +ORB_EXPORT ORBSymbolAnimationOptionKey const ORBSymbolAnimationWiggleOptions; +ORB_EXPORT ORBSymbolAnimationOptionKey const ORBSymbolAnimationWiggleAngle; +ORB_EXPORT ORBSymbolAnimationOptionKey const ORBSymbolAnimationRotateOptions; +ORB_EXPORT ORBSymbolAnimationOptionKey const ORBSymbolAnimationVariableColorOptions; +ORB_EXPORT ORBSymbolAnimationOptionKey const ORBSymbolAnimationAppearDisappearOptions; +ORB_EXPORT ORBSymbolAnimationOptionKey const ORBSymbolAnimationReplaceOptions; +ORB_EXPORT ORBSymbolAnimationOptionKey const ORBSymbolAnimationInterpolateOptions; + +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 index b369053..2a91a17 100644 --- a/Sources/OpenRenderBoxCxx/include/OpenRenderBoxObjC/Animation/ORBSymbolAnimator.h +++ b/Sources/OpenRenderBoxCxx/include/OpenRenderBoxObjC/Animation/ORBSymbolAnimator.h @@ -10,6 +10,7 @@ #include #include +#include #include #include @@ -27,7 +28,7 @@ struct _ORBSymbolUpdate; @property (nonatomic) double variableValue; @property (nonatomic) ORBColor opacities; @property (nonatomic, getter=isHidden) BOOL hidden; -@property (nonatomic) int scaleLevel; +@property (nonatomic) ORBSymbolScaleLevel scaleLevel; @property (nonatomic) CGPoint anchorPoint; @property (nonatomic) CGPoint position; @property (nonatomic) CGSize size; @@ -43,14 +44,14 @@ struct _ORBSymbolUpdate; @property (readonly, nonatomic) CGRect boundingRect; /* instance methods */ -- (unsigned int)addAnimation:(unsigned int)animation options:(nullable id)options; +- (unsigned int)addAnimation:(ORBSymbolAnimation)animation options:(nullable id)options ORB_SWIFT_NAME(addAnimation(_:options:)); - (struct _ORBSymbolUpdate *)beginUpdateWithRenderingMode:(unsigned int)mode; - (void)endUpdate:(struct _ORBSymbolUpdate *)update; - (void)removeAllAnimations; -- (void)removeAnimation:(unsigned int)animation; +- (void)removeAnimation:(ORBSymbolAnimation)animation ORB_SWIFT_NAME(removeAnimation(_:)); - (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)cancelAnimation:(ORBSymbolAnimation)animation ORB_SWIFT_NAME(cancelAnimation(_:)); - (void)cancelAnimationWithID:(unsigned int)animationID; - (ORBColor)colorForStyle:(unsigned int)style; - (nullable id)copyDebugDescriptionForUpdate:(struct _ORBSymbolUpdate *)update; diff --git a/Sources/OpenRenderBoxShims/ORBShims.swift b/Sources/OpenRenderBoxShims/ORBShims.swift index 7ac442e..7c3bbf9 100644 --- a/Sources/OpenRenderBoxShims/ORBShims.swift +++ b/Sources/OpenRenderBoxShims/ORBShims.swift @@ -39,6 +39,8 @@ public typealias ORBLayerDelegate = RBLayerDelegate public typealias ORBPath = RBPath public typealias ORBPathApplyCallback = RBPathApplyCallback public typealias ORBUUID = RBUUID +public typealias ORBSymbolAnimation = RBSymbolAnimation +public typealias ORBSymbolScaleLevel = RBSymbolScaleLevel public typealias ORBSymbolAnimator = RBSymbolAnimator public typealias ORBSymbolAnimatorObserver = RBSymbolAnimatorObserver diff --git a/Tests/OpenRenderBoxCompatibilityTests/ORBShims.swift b/Tests/OpenRenderBoxCompatibilityTests/ORBShims.swift index fcb8026..705a5b2 100644 --- a/Tests/OpenRenderBoxCompatibilityTests/ORBShims.swift +++ b/Tests/OpenRenderBoxCompatibilityTests/ORBShims.swift @@ -16,6 +16,8 @@ public typealias ORBLayerDelegate = RBLayerDelegate public typealias ORBPath = RBPath public typealias ORBPathApplyCallback = RBPathApplyCallback public typealias ORBUUID = RBUUID +public typealias ORBSymbolAnimation = RBSymbolAnimation +public typealias ORBSymbolScaleLevel = RBSymbolScaleLevel public typealias ORBSymbolAnimator = RBSymbolAnimator public typealias ORBSymbolAnimatorObserver = RBSymbolAnimatorObserver diff --git a/Tests/OpenRenderBoxCompatibilityTests/SymbolAnimationTests.swift b/Tests/OpenRenderBoxCompatibilityTests/SymbolAnimationTests.swift new file mode 100644 index 0000000..aca845c --- /dev/null +++ b/Tests/OpenRenderBoxCompatibilityTests/SymbolAnimationTests.swift @@ -0,0 +1,52 @@ +// +// SymbolAnimationTests.swift +// OpenRenderBoxCompatibilityTests + +import Foundation +import Testing + +struct SymbolAnimationTests { + @Test + func optionDictionaryBridging() { + let options: [ORBSymbolAnimation.OptionKey: Any] = [ + .timing: "customTiming", + .clampsSpeed: false, + .wiggleOptions: ORBSymbolAnimation.WiggleOptions([.rotational, .continuous]).rawValue, + .symbolDefaults: ["wiggleStyle": 3], + ] + let dictionary = Dictionary( + uniqueKeysWithValues: options.map { ($0.key.rawValue, $0.value) } + ) as NSDictionary + + #expect(dictionary["timingObject"] as? String == "customTiming") + #expect(dictionary["clampsSpeed"] as? Bool == false) + #expect((dictionary["wiggleOptions"] as? NSNumber)?.uint32Value == 0x103) + #expect((dictionary["symbolDefaults"] as? NSDictionary)?["wiggleStyle"] as? Int == 3) + } + + @Test(arguments: [ + (ORBSymbolAnimation.OptionKey.priority, "priority"), + (.speed, "speed"), + (.clampsSpeed, "clampsSpeed"), + (.repeatCount, "repeatCount"), + (.repeatDelay, "repeatDelay"), + (.byLayer, "byLayer"), + (.timing, "timingObject"), + (.onCompletion, "onCompletion"), + (.pulseOptions, "pulseOptions"), + (.breatheOptions, "breatheOptions"), + (.bounceOptions, "bounceOptions"), + (.wiggleAngle, "wiggleAngle"), + (.wiggleOptions, "wiggleOptions"), + (.rotateOptions, "rotateOptions"), + (.variableColorOptions, "variableColorOptions"), + (.replaceOptions, "replaceOptions"), + (.appearDisappearOptions, "appearDisappearOptions"), + (.interpolateOptions, "interpolateOptions"), + (.symbolDefaults, "symbolDefaults"), + ]) + func optionKeyRawValue(key: ORBSymbolAnimation.OptionKey, expected: String) { + #expect(key.rawValue == expected) + #expect(ORBSymbolAnimation.OptionKey(rawValue: expected) == key) + } +} diff --git a/Tests/OpenRenderBoxCompatibilityTests/SymbolAnimatorTests.swift b/Tests/OpenRenderBoxCompatibilityTests/SymbolAnimatorTests.swift index d25bf2a..274efcd 100644 --- a/Tests/OpenRenderBoxCompatibilityTests/SymbolAnimatorTests.swift +++ b/Tests/OpenRenderBoxCompatibilityTests/SymbolAnimatorTests.swift @@ -47,8 +47,8 @@ struct SymbolAnimatorTests { animator.depth = 2 #expect(animator.depth == 2) - animator.scaleLevel = 1 - #expect(animator.scaleLevel == 1) + animator.scaleLevel = .up + #expect(animator.scaleLevel == .up) } @Test