feat(gir): add support for detailed signals in code generation
Signals marked with detailed="1" in GIR now generate an additional
Connect{Signal}WithDetail(detail string, ...) method that allows
connecting with a signal detail string (e.g., "changed::key-name").
- Add Detailed field to SignalsTemplate struct
- Copy s.Detailed during signal template conversion in pass.go
- Update template to conditionally generate WithDetail methods
- Add conditional fmt import when detailed signals exist- commit
- 8b4a1dd9e2ca21d15243b61e36add55567e2fcc7
- author
- brice · · 2025-12-16 13:52 UTC
- parent
- 12c19f8
- signature
- unsigned
3 files · +50 -11
M internal/gir/pass/pass.go +6 -5
| @@ -333,11 +333,12 @@ func (p *Pass) writeGo(r types.Repository, gotemp *template.Template, dir string | |||
| 333 | 333 | signals := make([]types.SignalsTemplate, len(cls.Signals)) | |
| 334 | 334 | for i, s := range cls.Signals { | |
| 335 | 335 | signals[i] = types.SignalsTemplate{ | |
| 336 | - | Doc: s.Doc.StringSafe(), | |
| 337 | - | Name: util.DashToCamel(s.Name), | |
| 338 | - | CName: s.Name, | |
| 339 | - | Args: s.Parameters.Template(ns.Name, "", p.Types, false, types.ArgsFromCToGo), | |
| 340 | - | Ret: s.ReturnValue.Template(ns.Name, "", p.Types, false), | |
| 336 | + | Doc: s.Doc.StringSafe(), | |
| 337 | + | Name: util.DashToCamel(s.Name), | |
| 338 | + | CName: s.Name, | |
| 339 | + | Args: s.Parameters.Template(ns.Name, "", p.Types, false, types.ArgsFromCToGo), | |
| 340 | + | Ret: s.ReturnValue.Template(ns.Name, "", p.Types, false), | |
| 341 | + | Detailed: s.Detailed, | |
| 341 | 342 | } | |
| 342 | 343 | } | |
| 343 | 344 | receivers := make([]types.FuncTemplate, len(cls.Methods)) | |
| @@ -333,11 +333,12 @@ func (p *Pass) writeGo(r types.Repository, gotemp *template.Template, dir string | |||||
| 333 | signals := make([]types.SignalsTemplate, len(cls.Signals)) | 333 | signals := make([]types.SignalsTemplate, len(cls.Signals)) | ||
| 334 | for i, s := range cls.Signals { | 334 | for i, s := range cls.Signals { | ||
| 335 | signals[i] = types.SignalsTemplate{ | 335 | signals[i] = types.SignalsTemplate{ | ||
| 336 | - | Doc: s.Doc.StringSafe(), | 336 | + | Doc: s.Doc.StringSafe(), |
| 337 | - | Name: util.DashToCamel(s.Name), | 337 | + | Name: util.DashToCamel(s.Name), |
| 338 | - | CName: s.Name, | 338 | + | CName: s.Name, |
| 339 | - | Args: s.Parameters.Template(ns.Name, "", p.Types, false, types.ArgsFromCToGo), | 339 | + | Args: s.Parameters.Template(ns.Name, "", p.Types, false, types.ArgsFromCToGo), |
| 340 | - | Ret: s.ReturnValue.Template(ns.Name, "", p.Types, false), | 340 | + | Ret: s.ReturnValue.Template(ns.Name, "", p.Types, false), |
| 341 | + | Detailed: s.Detailed, | |||
| 341 | } | 342 | } | ||
| 342 | } | 343 | } | ||
| 343 | receivers := make([]types.FuncTemplate, len(cls.Methods)) | 344 | receivers := make([]types.FuncTemplate, len(cls.Methods)) | ||
M internal/gir/types/template.go +6 -5
| @@ -579,11 +579,12 @@ type InterfaceFuncTemplate struct { | |||
| 579 | 579 | } | |
| 580 | 580 | ||
| 581 | 581 | type SignalsTemplate struct { | |
| 582 | - | Doc string | |
| 583 | - | Name string | |
| 584 | - | CName string | |
| 585 | - | Args funcArgsTemplate | |
| 586 | - | Ret funcRetTemplate | |
| 582 | + | Doc string | |
| 583 | + | Name string | |
| 584 | + | CName string | |
| 585 | + | Args funcArgsTemplate | |
| 586 | + | Ret funcRetTemplate | |
| 587 | + | Detailed bool | |
| 587 | 588 | } | |
| 588 | 589 | ||
| 589 | 590 | type PropertyTemplate struct { | |
| @@ -579,11 +579,12 @@ type InterfaceFuncTemplate struct { | |||||
| 579 | } | 579 | } | ||
| 580 | 580 | ||||
| 581 | type SignalsTemplate struct { | 581 | type SignalsTemplate struct { | ||
| 582 | - | Doc string | 582 | + | Doc string |
| 583 | - | Name string | 583 | + | Name string |
| 584 | - | CName string | 584 | + | CName string |
| 585 | - | Args funcArgsTemplate | 585 | + | Args funcArgsTemplate |
| 586 | - | Ret funcRetTemplate | 586 | + | Ret funcRetTemplate |
| 587 | + | Detailed bool | |||
| 587 | } | 588 | } | ||
| 588 | 589 | ||||
| 589 | type PropertyTemplate struct { | 590 | type PropertyTemplate struct { | ||
M templates/go +38 -1
| @@ -4,22 +4,31 @@ package {{.PkgName}} | |||
| 4 | 4 | {{ $NotGObject := ne .PkgName "gobject" }} | |
| 5 | 5 | {{ $NotGLib := ne .PkgName "glib" }} | |
| 6 | 6 | {{ $HasSignals := false }} | |
| 7 | + | {{ $HasDetailedSignals := false }} | |
| 7 | 8 | {{range .Classes -}} | |
| 8 | 9 | {{if .Signals}} | |
| 9 | 10 | {{ $HasSignals = true }} | |
| 10 | 11 | {{end}} | |
| 12 | + | {{range .Signals}} | |
| 13 | + | {{if .Detailed}} | |
| 14 | + | {{ $HasDetailedSignals = true }} | |
| 15 | + | {{end}} | |
| 16 | + | {{end}} | |
| 11 | 17 | {{end}} | |
| 12 | 18 | {{ $HasCallbacks := or .HasReceiverCallbacks .HasFunctionCallbacks }} | |
| 13 | 19 | {{ $NeedsUnsafe := or .Records $HasSignals $HasCallbacks }} | |
| 14 | 20 | {{ $NeedsPurego := or .NeedsInit $HasSignals $HasCallbacks }} | |
| 15 | 21 | {{ $NeedsCore := or .NeedsInit .NeedsCore }} | |
| 16 | - | {{ $AnyImports := or $NeedsCore .Records $HasSignals $HasCallbacks }} | |
| 22 | + | {{ $AnyImports := or $NeedsCore .Records $HasSignals $HasCallbacks $HasDetailedSignals }} | |
| 17 | 23 | ||
| 18 | 24 | {{if $AnyImports}} | |
| 19 | 25 | import ( | |
| 20 | 26 | {{- if $NeedsUnsafe}} | |
| 21 | 27 | "unsafe" | |
| 22 | 28 | {{- end}} | |
| 29 | + | {{- if $HasDetailedSignals}} | |
| 30 | + | "fmt" | |
| 31 | + | {{- end}} | |
| 23 | 32 | {{- if .Records}} | |
| 24 | 33 | "structs" | |
| 25 | 34 | {{- end}} | |
| @@ -437,6 +446,34 @@ func (x *{{$outer.Name}}) Connect{{.Name}}(cb *func({{$outer.Name}} {{convc .Arg | |||
| 437 | 446 | {{if $NotGLib}}glib.{{end}}SaveCallback(cbPtr, cbRefPtr) | |
| 438 | 447 | return {{if $NotGObject}}gobject.{{end}}SignalConnect(x.GoPointer(), "{{.CName}}", cbRefPtr) | |
| 439 | 448 | } | |
| 449 | + | {{if .Detailed}} | |
| 450 | + | // Connect{{.Name}}WithDetail connects to the "{{.CName}}" signal with a detail string. | |
| 451 | + | // The detail is appended as "{{.CName}}::<detail>". | |
| 452 | + | func (x *{{$outer.Name}}) Connect{{.Name}}WithDetail(detail string, cb *func({{$outer.Name}} {{convc .Args.API.Types}}) {{.Ret.Value}}) uint32 { | |
| 453 | + | cbPtr := uintptr(unsafe.Pointer(cb)) | |
| 454 | + | signalName := fmt.Sprintf("{{.CName}}::%s", detail) | |
| 455 | + | if cbRefPtr, ok := {{if $NotGLib}}glib.{{end}}GetCallback(cbPtr); ok { | |
| 456 | + | return {{if $NotGObject}}gobject.{{end}}SignalConnect(x.GoPointer(), signalName, cbRefPtr) | |
| 457 | + | } | |
| 458 | + | ||
| 459 | + | fcb := func(clsPtr uintptr {{convc .Args.Pure.Full}}) {{.Ret.Raw}} { | |
| 460 | + | fa := {{$outer.Name}}{} | |
| 461 | + | fa.Ptr = clsPtr | |
| 462 | + | cbFn := *cb | |
| 463 | + | {{if .Ret.Class}} | |
| 464 | + | {{.Name}}Cls := cbFn(fa {{convc .Args.Pure.Call}}) | |
| 465 | + | return {{.Name}}Cls.Ptr | |
| 466 | + | {{else if .Ret.Value}} | |
| 467 | + | return cbFn(fa {{convc .Args.Pure.Call}}) | |
| 468 | + | {{else}} | |
| 469 | + | cbFn(fa {{convc .Args.Pure.Call}}) | |
| 470 | + | {{end}} | |
| 471 | + | } | |
| 472 | + | cbRefPtr := purego.NewCallback(fcb) | |
| 473 | + | {{if $NotGLib}}glib.{{end}}SaveCallback(cbPtr, cbRefPtr) | |
| 474 | + | return {{if $NotGObject}}gobject.{{end}}SignalConnect(x.GoPointer(), signalName, cbRefPtr) | |
| 475 | + | } | |
| 476 | + | {{end}} | |
| 440 | 477 | {{end}} | |
| 441 | 478 | ||
| 442 | 479 | {{range .Interfaces -}} | |
| @@ -4,22 +4,31 @@ package {{.PkgName}} | |||||
| 4 | {{ $NotGObject := ne .PkgName "gobject" }} | 4 | {{ $NotGObject := ne .PkgName "gobject" }} | ||
| 5 | {{ $NotGLib := ne .PkgName "glib" }} | 5 | {{ $NotGLib := ne .PkgName "glib" }} | ||
| 6 | {{ $HasSignals := false }} | 6 | {{ $HasSignals := false }} | ||
| 7 | + | {{ $HasDetailedSignals := false }} | |||
| 7 | {{range .Classes -}} | 8 | {{range .Classes -}} | ||
| 8 | {{if .Signals}} | 9 | {{if .Signals}} | ||
| 9 | {{ $HasSignals = true }} | 10 | {{ $HasSignals = true }} | ||
| 10 | {{end}} | 11 | {{end}} | ||
| 12 | + | {{range .Signals}} | |||
| 13 | + | {{if .Detailed}} | |||
| 14 | + | {{ $HasDetailedSignals = true }} | |||
| 15 | + | {{end}} | |||
| 16 | + | {{end}} | |||
| 11 | {{end}} | 17 | {{end}} | ||
| 12 | {{ $HasCallbacks := or .HasReceiverCallbacks .HasFunctionCallbacks }} | 18 | {{ $HasCallbacks := or .HasReceiverCallbacks .HasFunctionCallbacks }} | ||
| 13 | {{ $NeedsUnsafe := or .Records $HasSignals $HasCallbacks }} | 19 | {{ $NeedsUnsafe := or .Records $HasSignals $HasCallbacks }} | ||
| 14 | {{ $NeedsPurego := or .NeedsInit $HasSignals $HasCallbacks }} | 20 | {{ $NeedsPurego := or .NeedsInit $HasSignals $HasCallbacks }} | ||
| 15 | {{ $NeedsCore := or .NeedsInit .NeedsCore }} | 21 | {{ $NeedsCore := or .NeedsInit .NeedsCore }} | ||
| 16 | - | {{ $AnyImports := or $NeedsCore .Records $HasSignals $HasCallbacks }} | 22 | + | {{ $AnyImports := or $NeedsCore .Records $HasSignals $HasCallbacks $HasDetailedSignals }} |
| 17 | 23 | ||||
| 18 | {{if $AnyImports}} | 24 | {{if $AnyImports}} | ||
| 19 | import ( | 25 | import ( | ||
| 20 | {{- if $NeedsUnsafe}} | 26 | {{- if $NeedsUnsafe}} | ||
| 21 | "unsafe" | 27 | "unsafe" | ||
| 22 | {{- end}} | 28 | {{- end}} | ||
| 29 | + | {{- if $HasDetailedSignals}} | |||
| 30 | + | "fmt" | |||
| 31 | + | {{- end}} | |||
| 23 | {{- if .Records}} | 32 | {{- if .Records}} | ||
| 24 | "structs" | 33 | "structs" | ||
| 25 | {{- end}} | 34 | {{- end}} | ||
| @@ -437,6 +446,34 @@ func (x *{{$outer.Name}}) Connect{{.Name}}(cb *func({{$outer.Name}} {{convc .Arg | |||||
| 437 | {{if $NotGLib}}glib.{{end}}SaveCallback(cbPtr, cbRefPtr) | 446 | {{if $NotGLib}}glib.{{end}}SaveCallback(cbPtr, cbRefPtr) | ||
| 438 | return {{if $NotGObject}}gobject.{{end}}SignalConnect(x.GoPointer(), "{{.CName}}", cbRefPtr) | 447 | return {{if $NotGObject}}gobject.{{end}}SignalConnect(x.GoPointer(), "{{.CName}}", cbRefPtr) | ||
| 439 | } | 448 | } | ||
| 449 | + | {{if .Detailed}} | |||
| 450 | + | // Connect{{.Name}}WithDetail connects to the "{{.CName}}" signal with a detail string. | |||
| 451 | + | // The detail is appended as "{{.CName}}::<detail>". | |||
| 452 | + | func (x *{{$outer.Name}}) Connect{{.Name}}WithDetail(detail string, cb *func({{$outer.Name}} {{convc .Args.API.Types}}) {{.Ret.Value}}) uint32 { | |||
| 453 | + | cbPtr := uintptr(unsafe.Pointer(cb)) | |||
| 454 | + | signalName := fmt.Sprintf("{{.CName}}::%s", detail) | |||
| 455 | + | if cbRefPtr, ok := {{if $NotGLib}}glib.{{end}}GetCallback(cbPtr); ok { | |||
| 456 | + | return {{if $NotGObject}}gobject.{{end}}SignalConnect(x.GoPointer(), signalName, cbRefPtr) | |||
| 457 | + | } | |||
| 458 | + | ||||
| 459 | + | fcb := func(clsPtr uintptr {{convc .Args.Pure.Full}}) {{.Ret.Raw}} { | |||
| 460 | + | fa := {{$outer.Name}}{} | |||
| 461 | + | fa.Ptr = clsPtr | |||
| 462 | + | cbFn := *cb | |||
| 463 | + | {{if .Ret.Class}} | |||
| 464 | + | {{.Name}}Cls := cbFn(fa {{convc .Args.Pure.Call}}) | |||
| 465 | + | return {{.Name}}Cls.Ptr | |||
| 466 | + | {{else if .Ret.Value}} | |||
| 467 | + | return cbFn(fa {{convc .Args.Pure.Call}}) | |||
| 468 | + | {{else}} | |||
| 469 | + | cbFn(fa {{convc .Args.Pure.Call}}) | |||
| 470 | + | {{end}} | |||
| 471 | + | } | |||
| 472 | + | cbRefPtr := purego.NewCallback(fcb) | |||
| 473 | + | {{if $NotGLib}}glib.{{end}}SaveCallback(cbPtr, cbRefPtr) | |||
| 474 | + | return {{if $NotGObject}}gobject.{{end}}SignalConnect(x.GoPointer(), signalName, cbRefPtr) | |||
| 475 | + | } | |||
| 476 | + | {{end}} | |||
| 440 | {{end}} | 477 | {{end}} | ||
| 441 | 478 | ||||
| 442 | {{range .Interfaces -}} | 479 | {{range .Interfaces -}} | ||