detached 8b4a1dd

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
333333 signals := make([]types.SignalsTemplate, len(cls.Signals))
334334 for i, s := range cls.Signals {
335335 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,
341342 }
342343 }
343344 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 {
579579 }
580580
581581 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
587588 }
588589
589590 type PropertyTemplate struct {
@@ -579,11 +579,12 @@ type InterfaceFuncTemplate struct {
579 }579 }
580 580
581 type SignalsTemplate struct {581 type SignalsTemplate struct {
582- Doc string582+ Doc string
583- Name string583+ Name string
584- CName string584+ CName string
585- Args funcArgsTemplate585+ Args funcArgsTemplate
586- Ret funcRetTemplate586+ 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}}
44 {{ $NotGObject := ne .PkgName "gobject" }}
55 {{ $NotGLib := ne .PkgName "glib" }}
66 {{ $HasSignals := false }}
7+{{ $HasDetailedSignals := false }}
78 {{range .Classes -}}
89 {{if .Signals}}
910 {{ $HasSignals = true }}
1011 {{end}}
12+ {{range .Signals}}
13+ {{if .Detailed}}
14+ {{ $HasDetailedSignals = true }}
15+ {{end}}
16+ {{end}}
1117 {{end}}
1218 {{ $HasCallbacks := or .HasReceiverCallbacks .HasFunctionCallbacks }}
1319 {{ $NeedsUnsafe := or .Records $HasSignals $HasCallbacks }}
1420 {{ $NeedsPurego := or .NeedsInit $HasSignals $HasCallbacks }}
1521 {{ $NeedsCore := or .NeedsInit .NeedsCore }}
16-{{ $AnyImports := or $NeedsCore .Records $HasSignals $HasCallbacks }}
22+{{ $AnyImports := or $NeedsCore .Records $HasSignals $HasCallbacks $HasDetailedSignals }}
1723
1824 {{if $AnyImports}}
1925 import (
2026 {{- if $NeedsUnsafe}}
2127 "unsafe"
2228 {{- end}}
29+{{- if $HasDetailedSignals}}
30+ "fmt"
31+{{- end}}
2332 {{- if .Records}}
2433 "structs"
2534 {{- end}}
@@ -437,6 +446,34 @@ func (x *{{$outer.Name}}) Connect{{.Name}}(cb *func({{$outer.Name}} {{convc .Arg
437446 {{if $NotGLib}}glib.{{end}}SaveCallback(cbPtr, cbRefPtr)
438447 return {{if $NotGObject}}gobject.{{end}}SignalConnect(x.GoPointer(), "{{.CName}}", cbRefPtr)
439448 }
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}}
440477 {{end}}
441478
442479 {{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 -}}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 1

Tags 102

↑↓ moveenter openesc close