2323 nimNode: NimNode
2424 isNoGodot: bool
2525
26+ SignalArgDecl = ref object
27+ name: string
28+ typ: NimNode
29+
30+ SignalDecl = ref object
31+ name: string
32+ args: seq [SignalArgDecl ]
33+
2634 ObjectDecl = ref object
2735 name: string
2836 parentName: string
2937 fields: seq [VarDecl ]
38+ signals: seq [SignalDecl ]
3039 methods: seq [MethodDecl ]
3140 isTool: bool
3241
42+
3343 ParseError = object of Exception
3444
3545include " internal/backwardcompat.inc.nim"
@@ -178,11 +188,34 @@ proc parseVarSection(decl: NimNode): seq[VarDecl] =
178188 else :
179189 result .add (identDefsToVarDecls (decl[i]))
180190
191+ proc parseSignal (sig: NimNode ): SignalDecl =
192+ let errorMsg = " Signal declaration must have this format: signal my_signal(param1: int, param2: string)"
193+
194+ if sig.kind != nnkCommand:
195+ parseError (sig, errorMsg)
196+ if not (sig[1 ].kind == nnkCall or sig[1 ].kind == nnkObjConstr):
197+ parseError (sig, errorMsg)
198+
199+ result = SignalDecl (
200+ name: $ sig[1 ][0 ],
201+ args: newSeq [SignalArgDecl ]()
202+ )
203+
204+ if sig[1 ].kind == nnkObjConstr:
205+ for i in 1 ..< sig[1 ].len:
206+ var nexpr = sig[1 ][i]
207+ case nexpr.kind:
208+ of nnkExprColonExpr:
209+ result .args.add (SignalArgDecl (name: nexpr[0 ].repr, typ: nexpr[1 ]))
210+ else :
211+ parseError (sig, errorMsg)
212+
181213proc parseType (ast: NimNode ): ObjectDecl =
182214 let definition = ast[0 ]
183215 let body = ast[^ 1 ]
184216 result = ObjectDecl (
185217 fields: newSeq [VarDecl ](),
218+ signals: newSeq [SignalDecl ](),
186219 methods: newSeq [MethodDecl ]()
187220 )
188221 (result .name, result .parentName) = extractNames (definition)
@@ -207,6 +240,10 @@ proc parseType(ast: NimNode): ObjectDecl =
207240 of nnkProcDef, nnkMethodDef:
208241 let meth = parseMethod (statement)
209242 result .methods.add (meth)
243+ of nnkCommand:
244+ if statement[0 ].strVal == " signal" :
245+ let sig = parseSignal (statement)
246+ result .signals.add (sig)
210247 of nnkCommentStmt:
211248 discard
212249 else :
@@ -619,6 +656,60 @@ N_NOINLINE(void, setStackBottom)(void* thestackbottom);
619656 argTypes, genSym (nskProc, " methFunc" ),
620657 hasReturnValue)))
621658
659+ template registerGodotSignalNoArgs (classNameLit, signalName) =
660+ var godotSignal = GodotSignal (
661+ name: signalName.toGodotString ())
662+ nativeScriptRegisterSignal (getNativeLibHandle (), classNameLit, godotSignal)
663+
664+ template initSignalArgumentParameters (argName, argTypeIdent, typeInfoIdent,
665+ godotStringIdent, godotVariantIdent)=
666+ var godotStringIdent = argName.toGodotString ()
667+ mixin godotTypeInfo
668+ const typeInfoIdent = when compiles (godotTypeInfo (argTypeIdent)):
669+ godotTypeInfo (argTypeIdent)
670+ else : GodotTypeInfo ()
671+ var godotVariantIdent:GodotVariant
672+ initGodotVariant (godotVariantIdent)
673+
674+ template deinitSignalArgumentParameters (godotStringIdent, godotVariantIdent)=
675+ godotStringIdent.deinit ()
676+ godotVariantIdent.deinit ()
677+
678+ template createSignalArgument (typeInfoIdent, godotStringIdent, godotVariantIdent) =
679+ GodotSignalArgument (name: godotStringIdent,
680+ typ: ord (typeInfoIdent.variantType),
681+ defaultValue: godotVariantIdent)
682+
683+ template registerGodotSignal (classNameLit, signalName, argCount, sigArgs) =
684+ var sigArgsArr = sigArgs
685+ var godotSignal = GodotSignal (
686+ name: signalName.toGodotString (),
687+ numArgs: argCount,
688+ args: addr (sigArgsArr[0 ]))
689+ nativeScriptRegisterSignal (getNativeLibHandle (), classNameLit, godotSignal)
690+
691+ for sig in obj.signals:
692+ if sig.args.len == 0 :
693+ result .add (getAst (
694+ registerGodotSignalNoArgs (classNameLit, sig.name)))
695+ else :
696+ var sigArgsParams:seq [(NimNode , NimNode , NimNode )]
697+ for arg in sig.args:
698+ var p = (genSym (nskConst, " typeInfo" ), genSym (nskVar, " godotString" ), genSym (nskVar, " godotVariant" ))
699+ sigArgsParams.add p
700+ result .add (getAst (
701+ initSignalArgumentParameters (arg.name, arg.typ, p[0 ], p[1 ], p[2 ])))
702+
703+ var sigArgs = newNimNode (nnkBracket)
704+ for p in sigArgsParams:
705+ sigArgs.add (getAst (
706+ createSignalArgument (p[0 ], p[1 ], p[2 ])))
707+ result .add (getAst (
708+ registerGodotSignal (classNameLit, sig.name, sig.args.len, sigArgs)))
709+ for p in sigArgsParams:
710+ result .add (getAst (
711+ deinitSignalArgumentParameters (p[1 ], p[2 ])))
712+
622713 if isRef:
623714 # add ref/unref for types inherited from Reference
624715 template registerRefIncDec (classNameLit) =
@@ -653,17 +744,27 @@ macro gdobj*(ast: varargs[untyped]): untyped =
653744 # # ## ``hintStr`` depends on the value of ``hint``, its format is
654745 # # ## described in ``GodotPropertyHint`` documentation.
655746 # #
747+ # # signal my_signal(amount:int, message:string)
748+ # # ## Defines a signal ``my_signal`` with parameters
749+ # #
656750 # # method ready*() =
657751 # # ## Exported methods are exported to Godot by default,
658752 # # ## and their Godot names are prefixed with ``_``
659753 # # ## (in this case ``_ready``)
660754 # # print("I am ready! myString is: " & self.myString)
661755 # #
756+ # # ## connect to the signal and then emit it
757+ # # discard self.connect("my_signal", self, "on_my_signal")
758+ # # self.emit_signal("my_signal", 123.toVariant, "hello godot".toVariant)
759+ # #
662760 # # proc myProc*() {.gdExport.} =
663761 # # ## Exported to Godot as ``my_proc``
664762 # # print("myProc is called! Incrementing myField.")
665763 # # inc self.myField
666764 # #
765+ # # proc on_my_signal(amount:int, message:string) {.gdExport.} =
766+ # # print("received my_signal " & amount & " " & message)
767+ # #
667768 # # If parent type is omitted, the type is inherited from ``Object``.
668769 # #
669770 # # ``tool`` specifier can be added to mark the type as an
0 commit comments