-
Notifications
You must be signed in to change notification settings - Fork 79
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Require macOS 14 for the generic pack support Would bumping the swift-tools-version to 5.10 be better? Use generic signal instead of generating helper Don't use SimpleSignal Moved GenericSignal to its own file. Removed some unused code, tidied comments. Fixed popping objects. Cleaner unpacking, without the need to copy the arguments. revert unrelated formatting changes put back SignalSupport to minimise changes Removed SimpleSignal completely tweaked names for a cleaner diff Added test for built in signals
- Loading branch information
Showing
5 changed files
with
153 additions
and
206 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
// | ||
// Created by Sam Deane on 25/10/2024. | ||
// | ||
|
||
/// Signal support. | ||
/// Use the ``GenericSignal/connect(flags:_:)`` method to connect to the signal on the container object, | ||
/// and ``GenericSignal/disconnect(_:)`` to drop the connection. | ||
/// | ||
/// Use the ``GenericSignal/emit(...)`` method to emit a signal. | ||
/// | ||
/// You can also await the ``Signal1/emitted`` property for waiting for a single emission of the signal. | ||
/// | ||
public class GenericSignal<each T: VariantStorable> { | ||
var target: Object | ||
var signalName: StringName | ||
public init(target: Object, signalName: StringName) { | ||
self.target = target | ||
self.signalName = signalName | ||
} | ||
|
||
/// Connects the signal to the specified callback | ||
/// To disconnect, call the disconnect method, with the returned token on success | ||
/// | ||
/// - Parameters: | ||
/// - callback: the method to invoke when this signal is raised | ||
/// - flags: Optional, can be also added to configure the connection's behavior (see ``Object/ConnectFlags`` constants). | ||
/// - Returns: an object token that can be used to disconnect the object from the target on success, or the error produced by Godot. | ||
/// | ||
@discardableResult /* Signal1 */ | ||
public func connect(flags: Object.ConnectFlags = [], _ callback: @escaping (_ t: repeat each T) -> Void) -> Object { | ||
let signalProxy = SignalProxy() | ||
signalProxy.proxy = { args in | ||
var index = 0 | ||
do { | ||
callback(repeat try args.unpack(as: (each T).self, index: &index)) | ||
} catch { | ||
print("Error unpacking signal arguments: \(error)") | ||
} | ||
} | ||
|
||
let callable = Callable(object: signalProxy, method: SignalProxy.proxyName) | ||
let r = target.connect(signal: signalName, callable: callable, flags: UInt32(flags.rawValue)) | ||
if r != .ok { print("Warning, error connecting to signal, code: \(r)") } | ||
return signalProxy | ||
} | ||
|
||
/// Disconnects a signal that was previously connected, the return value from calling | ||
/// ``connect(flags:_:)`` | ||
public func disconnect(_ token: Object) { | ||
target.disconnect(signal: signalName, callable: Callable(object: token, method: SignalProxy.proxyName)) | ||
} | ||
|
||
/// You can await this property to wait for the signal to be emitted once. | ||
public var emitted: Void { | ||
get async { | ||
await withCheckedContinuation { c in | ||
let signalProxy = SignalProxy() | ||
signalProxy.proxy = { _ in c.resume() } | ||
let callable = Callable(object: signalProxy, method: SignalProxy.proxyName) | ||
let r = target.connect(signal: signalName, callable: callable, flags: UInt32(Object.ConnectFlags.oneShot.rawValue)) | ||
if r != .ok { print("Warning, error connecting to signal, code: \(r)") } | ||
} | ||
|
||
} | ||
|
||
} | ||
|
||
} | ||
|
||
|
||
extension Arguments { | ||
enum UnpackError: Error { | ||
case typeMismatch | ||
case missingArgument | ||
} | ||
|
||
/// Unpack an argument as a specific type. | ||
/// We throw a runtime error if the argument is not of the expected type, | ||
/// or if there are not enough arguments to unpack. | ||
func unpack<T: VariantStorable>(as type: T.Type, index: inout Int) throws -> T { | ||
if index >= count { | ||
throw UnpackError.missingArgument | ||
} | ||
let argument = self[index] | ||
index += 1 | ||
let value: T? | ||
if argument.gtype == .object { | ||
value = T.Representable.godotType == .object ? argument.asObject(Object.self) as? T : nil | ||
} else { | ||
value = T(argument) | ||
} | ||
|
||
guard let value else { | ||
throw UnpackError.typeMismatch | ||
} | ||
|
||
return value | ||
} | ||
} |
Oops, something went wrong.