Skip to content

Commit

Permalink
Merge pull request #89 from alicerunsonfedora/main
Browse files Browse the repository at this point in the history
Add convenience registration methods, fix build scripts
  • Loading branch information
migueldeicaza authored Sep 6, 2023
2 parents fd3480d + 57da325 commit 94ebf26
Show file tree
Hide file tree
Showing 3 changed files with 284 additions and 7 deletions.
263 changes: 263 additions & 0 deletions Sources/SwiftGodot/Extensions/ClassInfo+ConvenienceProperties.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,263 @@
//
// ClassInfo+ConvenienceProperties.swift
//
//
// Created by Marquis Kurt on 5/29/23.
//

import Foundation
import UniformTypeIdentifiers

public protocol Nameable {
var name: String { get }
}

@available(macOS 11.0, *)
extension UTType {
/// The file type that corresponds to a Godot resource file.
public static var godotResource = UTType(filenameExtension: "res")

/// The file type that corresponds to a Godot scene file.
public static var godotScene = UTType(filenameExtension: "tscn")

/// The file type that corresponds to a GDScript file.
public static var gdscript = UTType(filenameExtension: "gd")

/// The file type that corresponds to a Godot shader file.
public static var godotShader = UTType(filenameExtension: "gdshader")

/// The file type that corresponds to a Godot tileset resource.
public static var godotTilesetResource = UTType(filenameExtension: "tres")
}

extension ClassInfo {
/// A type alias referencing a class info function that can be registered.
public typealias ClassInfoFunction = (T) -> ([Variant]) -> Variant?

/// A type alias referencing a registerable int enum.
public typealias RegisteredIntEnum = CaseIterable & Nameable & RawRepresentable<Int>

/// Performs an operation on an argument that originates from a setter method.
///
/// This can be used inside setter method to set a property in-class with a guaranteed argument value.
///
/// ```swift
/// func setBubbleCount(args: [Variant]) -> Variant? {
/// withCheckedProperty(named: "bubbles", in: args) { argument in
/// self.bubbles = Int(argument) ?? 0
/// }
/// }
/// ```
///
/// - Parameter name: The name of the property that is being set.
/// - Parameter arguments: The list of arguments that were passed from the setter.
/// - Parameter action: A closure that accepts a valid argument.
public static func withCheckedProperty(named name: String,
in arguments: [Variant],
perform action: (Variant) -> Void) -> Variant? {
guard let arg = arguments.first else {
GD.pushError("Expected argument for \(name), but got nil instead.")
return nil
}
action(arg)
return nil
}

/// Registers a checkbox toggle in the editor.
/// - Parameter name: The name of the property that will appear in the editor.
/// - Parameter prefix: The prefix to apply to the property name. Defaults to the class's name if not provided.
/// - Parameter getter: The getter method the editor will call to get the property.
/// - Parameter setter: The setter method the editor will call to set the property.
public func registerCheckbox(named name: String,
prefix: String? = nil,
getter: @escaping ClassInfoFunction,
setter: @escaping ClassInfoFunction) {
let registeredPrefix = prefix ?? "\(T.self)"
let property = PropInfo(propertyType: .bool,
propertyName: StringName("\(registeredPrefix)_\(name)"),
className: StringName("\(T.self)"),
hint: .flags,
hintStr: "",
usage: .propertyUsageDefault)
registerSetter(prefix: registeredPrefix, name: name, property: property, setter: setter)
registerGetter(prefix: registeredPrefix, name: name, property: property, getter: getter)
registerProperty(property,
getter: StringName("\(registeredPrefix)_get_\(name)"),
setter: StringName("\(registeredPrefix)_set_\(name)"))
}

/// Registers an enumeration in the editor.
/// - Parameter name: The name of the property that will appear in the editor.
/// - Parameter enumType: The enumeration type that will be selected in the editor.
/// - Parameter prefix: The prefix to apply to the property name. Defaults to the class's name if not provided.
/// - Parameter getter: The getter method the editor will call to get the property.
/// - Parameter setter: The setter method the editor will call to set the property.
public func registerEnum<Enum: RegisteredIntEnum>(named name: String,
for enumType: Enum.Type,
prefix: String? = nil,
getter: @escaping ClassInfoFunction,
setter: @escaping ClassInfoFunction) {
let registeredPrefix = prefix ?? "\(T.self)"
let property = PropInfo(propertyType: .int,
propertyName: StringName("\(registeredPrefix)_\(name)"),
className: StringName("\(T.self)"),
hint: .enum,
hintStr: GString(Enum.allCases.map(\.name).joined(separator: ",")),
usage: .propertyUsageDefault)
registerGetter(prefix: registeredPrefix, name: name, property: property, getter: getter)
registerSetter(prefix: registeredPrefix, name: name, property: property, setter: setter)
registerProperty(property,
getter: StringName("\(registeredPrefix)_get_\(name)"),
setter: StringName("\(registeredPrefix)_set_\(name)"))
}

/// Registers a file picker in the editor.
///
/// - Important: This method is deprecated on macOS 12.0 or later; Use the
/// ``ClassInfo/registerFilePicker(named:allowedTypes:prefix:getter:setter:)-9oeps`` method for newer macOS
/// versions.
///
/// - Parameter name: The name of the property that will appear in the editor.
/// - Parameter allowedTypes: The types of files allowed in the file picker.
/// - Parameter prefix: The prefix to apply to the property name. Defaults to the class's name if not provided.
/// - Parameter getter: The getter method the editor will call to get the property.
/// - Parameter setter: The setter method the editor will call to set the property.
@available(macOS, introduced: 10.15, deprecated: 12.0, message: "Use the variant with UTTypes.")
public func registerFilePicker(named name: String,
allowedTypes: [String] = ["*"],
prefix: String? = nil,
getter: @escaping ClassInfoFunction,
setter: @escaping ClassInfoFunction) {
let registeredPrefix = prefix ?? "\(T.self)"
let fileExtensions = allowedTypes.map { "*.\($0)" }.joined(separator: ",")
let property = PropInfo(propertyType: .string,
propertyName: StringName(name),
className: StringName("\(T.self)"),
hint: .file,
hintStr: GString(fileExtensions),
usage: .propertyUsageDefault)
registerSetter(prefix: registeredPrefix, name: name, property: property, setter: setter)
registerGetter(prefix: registeredPrefix, name: name, property: property, getter: getter)
registerProperty(property,
getter: StringName("\(registeredPrefix)_get_\(name)"),
setter: StringName("\(registeredPrefix)_set_\(name)"))
}

/// Registers a file picker in the editor.
/// - Parameter name: The name of the property that will appear in the editor.
/// - Parameter allowedTypes: The types of files allowed in the file picker.
/// - Parameter prefix: The prefix to apply to the property name. Defaults to the class's name if not provided.
/// - Parameter getter: The getter method the editor will call to get the property.
/// - Parameter setter: The setter method the editor will call to set the property.
@available(macOS 11.0, *)
public func registerFilePicker(named name: String,
allowedTypes: [UTType],
prefix: String? = nil,
getter: @escaping ClassInfoFunction,
setter: @escaping ClassInfoFunction) {
let registeredPrefix = prefix ?? "\(T.self)"
let fileExtensions = allowedTypes.map(\.preferredFilenameExtension)
.map { "*.\($0 ?? "*")" }
.joined(separator: ",")
let property = PropInfo(propertyType: .string,
propertyName: StringName(name),
className: StringName("\(T.self)"),
hint: .file,
hintStr: GString(fileExtensions),
usage: .propertyUsageDefault)
registerSetter(prefix: registeredPrefix, name: name, property: property, setter: setter)
registerGetter(prefix: registeredPrefix, name: name, property: property, getter: getter)
registerProperty(property,
getter: StringName("\(registeredPrefix)_get_\(name)"),
setter: StringName("\(registeredPrefix)_set_\(name)"))
}

/// Registers a number field in the number that can be adjusted in a range.
/// - Parameter string: The name of the property that will appear in the editor.
/// - Parameter range: The range that the number must fall between.
/// - Parameter stride: The number to decrease or increase by. Defaults to 1.
/// - Parameter prefix: The prefix to apply to the property name. Defaults to the class's name if not provided.
/// - Parameter getter: The getter method the editor will call to get the property.
/// - Parameter setter: The setter method the editor will call to set the property.
public func registerInt(named name: String,
range: ClosedRange<Int>,
stride: Int = 1,
prefix: String? = nil,
getter: @escaping ClassInfoFunction,
setter: @escaping ClassInfoFunction) {
let registeredPrefix = prefix ?? "\(T.self)"
let property = PropInfo(propertyType: .int,
propertyName: StringName("\(registeredPrefix)_\(name)"),
className: StringName("\(T.self)"),
hint: .range,
hintStr: GString("\(range.lowerBound),\(range.upperBound),\(stride)"),
usage: .propertyUsageDefault)
registerSetter(prefix: registeredPrefix, name: name, property: property, setter: setter)
registerGetter(prefix: registeredPrefix, name: name, property: property, getter: getter)
registerProperty(property,
getter: StringName("\(registeredPrefix)_get_\(name)"),
setter: StringName("\(registeredPrefix)_set_\(name)"))
}

/// Registers a text field.
/// - Parameter name: The name of the property that will appear in the editor.
/// - Parameter prefix: The prefix to apply to the property name. Defaults to the class's name if not provided.
/// - Parameter getter: The getter method the editor will call to get the property.
/// - Parameter setter: The setter method the editor will call to set the property.
public func registerTextField(named name: String,
prefix: String? = nil,
getter: @escaping ClassInfoFunction,
setter: @escaping ClassInfoFunction) {
let registeredPrefix = prefix ?? "\(T.self)"
let property = PropInfo(propertyType: .string,
propertyName: StringName("\(registeredPrefix)_\(name)"),
className: StringName("\(T.self)"),
hint: .typeString,
hintStr: "",
usage: .propertyUsageDefault)
registerSetter(prefix: registeredPrefix, name: name, property: property, setter: setter)
registerGetter(prefix: registeredPrefix, name: name, property: property, getter: getter)
registerProperty(property,
getter: StringName("\(registeredPrefix)_get_\(name)"),
setter: StringName("\(registeredPrefix)_set_\(name)"))
}

/// Registers a multiline text view in the editor.
/// - Parameter name: The name of the property that will appear in the editor.
/// - Parameter prefix: The prefix to apply to the property name. Defaults to the class's name if not provided.
/// - Parameter getter: The getter method the editor will call to get the property.
/// - Parameter setter: The setter method the editor will call to set the property.
public func regsiterTextView(named name: String,
prefix: String? = nil,
getter: @escaping ClassInfoFunction,
setter: @escaping ClassInfoFunction) {
let registeredPrefix = prefix ?? "\(T.self)"
let property = PropInfo(propertyType: .string,
propertyName: StringName("\(registeredPrefix)_\(name)"),
className: StringName("\(T.self)"),
hint: .multilineText,
hintStr: "",
usage: .propertyUsageDefault)
registerSetter(prefix: registeredPrefix, name: name, property: property, setter: setter)
registerGetter(prefix: registeredPrefix, name: name, property: property, getter: getter)
registerProperty(property,
getter: StringName("\(registeredPrefix)_get_\(name)"),
setter: StringName("\(registeredPrefix)_set_\(name)"))
}

private func registerGetter(prefix: String, name: String, property: PropInfo, getter: @escaping ClassInfoFunction) {
registerMethod(name: StringName("\(prefix)_get_\(name)"),
flags: .default,
returnValue: property,
arguments: [],
function: getter)
}

private func registerSetter(prefix: String, name: String, property: PropInfo, setter: @escaping ClassInfoFunction) {
registerMethod(name: StringName("\(prefix)_set_\(name)"),
flags: .default,
returnValue: nil,
arguments: [property],
function: setter)
}
}
20 changes: 15 additions & 5 deletions scripts/make-swiftgodot-framework
Original file line number Diff line number Diff line change
Expand Up @@ -20,24 +20,34 @@ case $2 in
;;
esac

arch=`uname -m`

rm -rf $2
bdMac=$1/Build/Products/Debug/
bdMacArm="$(echo "$1" | rev | cut -c2- | rev)_arm/Build/Products/Debug/"
bdiOS=$1/Build/Products/Debug-iphoneos/
mkdir universal
echo $bdMacArm
cp -rf $bdMacArm/PackageFrameworks/SwiftGodot.framework universal/
lipo -create -output ./universal/SwiftGodot.framework/SwiftGodot $bdMac/PackageFrameworks/SwiftGodot.framework/SwiftGodot $bdMacArm/PackageFrameworks/SwiftGodot.framework/SwiftGodot
cp -rf $bdMac/PackageFrameworks/SwiftGodot.framework universal/
if [ $arch = "arm64" ]; then
lipo -create -output ./universal/SwiftGodot.framework/SwiftGodot $bdMac/PackageFrameworks/SwiftGodot.framework/SwiftGodot $bdMacArm/PackageFrameworks/SwiftGodot.framework/SwiftGodot
lipo -create -output ./universal/SwiftGodot.framework/Versions/A/SwiftGodot $bdMac/PackageFrameworks/SwiftGodot.framework/Versions/A/SwiftGodot $bdMacArm/PackageFrameworks/SwiftGodot.framework/Versions/A/SwiftGodot
fi
xcodebuild -create-xcframework -framework ./universal/SwiftGodot.framework -framework $bdiOS/PackageFrameworks/SwiftGodot.framework -output $2
rm -rf ./universal
fd=$2//macos-arm64_x86_64/SwiftGodot.framework
if [ $arch = "arm64" ]; then
fd=$2//macos-arm64_x86_64/SwiftGodot.framework
else
fd=$2/macos-x86_64/SwiftGodot.framework
fi
ifd=$2/ios-arm64/SwiftGodot.framework
(cd $fd; mkdir -p Versions/Current/Headers; ln -sf Versions/Current/Headers .)
(cd $fd; mkdir -p Versions/Current/Modules; ln -sf Versions/Current/Modules .)
(cd $ifd; mkdir -p Versions/Current/Headers; ln -sf Versions/Current/Headers .)
(cd $ifd; mkdir -p Versions/Current/Modules; ln -sf Versions/Current/Modules .)
rsync -a $bdMac/SwiftGodot.swiftmodule $fd/Versions/Current/Modules/
rsync -a $bdMacArm/SwiftGodot.swiftmodule $fd/Versions/Current/Modules/
if [ $arch = "arm64" ]; then
rsync -a $bdMacArm/SwiftGodot.swiftmodule $fd/Versions/Current/Modules/
fi
rsync -a $bdiOS/SwiftGodot.swiftmodule $ifd/Versions/Current/Modules/

cat > $fd/Modules/module.modulemap << EOF
Expand Down
8 changes: 6 additions & 2 deletions scripts/release
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ if [ -z "$SWIFT_GODOT_NODEPLOY" ]; then
esac
fi

arch=`uname -m`

outputDir=$HOME/sg-builds
dir=$outputDir/$$-build

Expand All @@ -45,8 +47,10 @@ echo DerivedData: $derivedData
echo ArchiveData: $archivePath
start=`date`
xcodebuild -scheme SwiftGodot -destination platform=macOS,arch=x86_64 -derivedDataPath $derivedData -archivePath $archivePath >& $dir/x86_64.log
xcodebuild -scheme SwiftGodot -destination platform=macOS,arch=arm64 -derivedDataPath "${derivedData}_arm" -archivePath $archivePath >& $dir/arm64.log
xcodebuild -scheme SwiftGodot -destination generic/platform=iOS -derivedDataPath $derivedData -archivePath $archivePath >& $dir/arm64.log
if [ $arch = "arm64" ]; then
xcodebuild -scheme SwiftGodot -destination platform=macOS,arch=arm64 -derivedDataPath "${derivedData}_arm" -archivePath $archivePath >& $dir/arm64.log
fi
xcodebuild -scheme SwiftGodot -destination generic/platform=iOS -derivedDataPath $derivedData -archivePath $archivePath >& $dir/ios_arm64.log
sh scripts/make-swiftgodot-framework $dir/derived/ $outputDir/SwiftGodot.xcframework

if [ ! -z "$SWIFT_GODOT_NODEPLOY" ]; then
Expand Down

0 comments on commit 94ebf26

Please sign in to comment.