Skip to content

Commit

Permalink
Generic bind funcs
Browse files Browse the repository at this point in the history
  • Loading branch information
jgillich committed Aug 15, 2024
1 parent ce72802 commit e82e6e0
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 44 deletions.
6 changes: 6 additions & 0 deletions go.work
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
go 1.22.0

use (
.
./pkg
)
74 changes: 74 additions & 0 deletions pkg/core/glib/bind.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package glib

import (
"reflect"
"sync"
"unsafe"

"github.com/diamondburned/gotk4/pkg/core/gbox"
)

// #include <glib.h>
// #include <glib-object.h>
// #include "glib.go.h"
import "C"

var bindingNames sync.Map // map[reflect.Type]C.GQuark

// Associate value with object
func Bind[T any](obj Objector, value T) {
object := BaseObject(obj)
name := bindingName[T]()

ptr := C.gpointer(gbox.Assign(value))

C.g_object_set_data_full(object.native(), (*C.gchar)(name), ptr, (*[0]byte)(C._gotk4_data_destroy))
}

// Disassociate value from object
func Unbind[T any](obj Objector) {
name := bindingName[T]()

ptr := C.g_object_steal_data(BaseObject(obj).native(), (*C.gchar)(name))
defer gbox.Delete(uintptr(ptr))
}

// Obtain value associated with object
func Bounded[T any](obj Objector) *T {
name := bindingName[T]()

ptr := C.g_object_get_data(BaseObject(obj).native(), name)

value, ok := gbox.Get(uintptr(ptr)).(T)
if !ok {
return nil
}

return &value
}

func bindingName[T any]() *C.gchar {
t := reflect.TypeFor[T]()

if v, ok := bindingNames.Load(t); ok {
quark := v.(C.GQuark)
return C.g_quark_to_string(quark)
}

name := "_gotk4_" + t.String()

nameC := C.CString(name)
defer C.free(unsafe.Pointer(nameC))

quark := C.g_quark_from_string(nameC)
if v, lost := bindingNames.LoadOrStore(t, quark); lost {
quark = v.(C.GQuark)
}

return C.g_quark_to_string(quark)
}

//export _gotk4_data_destroy
func _gotk4_data_destroy(ptr C.gpointer) {
gbox.Delete(uintptr(ptr))
}
38 changes: 0 additions & 38 deletions pkg/core/glib/glib.go
Original file line number Diff line number Diff line change
Expand Up @@ -589,9 +589,6 @@ type Objector interface {
NotifyProperty(string, func()) SignalHandle
ObjectProperty(string) interface{}
SetObjectProperty(string, interface{})
ObjectData(string) interface{}
SetObjectData(string, interface{})
StealObjectData(name string) interface{}
FreezeNotify()
ThawNotify()
StopEmission(string)
Expand Down Expand Up @@ -871,41 +868,6 @@ func (v *Object) NotifyProperty(property string, f func()) SignalHandle {
)
}

// Gets a named field from the objects table of associations
func (v *Object) ObjectData(name string) interface{} {
cstr := C.CString(name)
defer C.free(unsafe.Pointer(cstr))

ptr := C.g_object_get_data(v.native(), (*C.gchar)(cstr))
runtime.KeepAlive(v)

return gbox.Get(uintptr(ptr))
}

// Each object carries around a table of associations from strings to pointers. This function lets you set an association.
func (v *Object) SetObjectData(name string, value interface{}) {
cstr := C.CString(name)
defer C.free(unsafe.Pointer(cstr))

ptr := C.gpointer(gbox.Assign(value))

C.g_object_set_data(v.native(), (*C.gchar)(cstr), ptr)
runtime.KeepAlive(v)
}

// Remove a specified datum from the object’s data associations
func (v *Object) StealObjectData(name string) interface{} {
cstr := C.CString(name)
defer C.free(unsafe.Pointer(cstr))

ptr := C.g_object_steal_data(v.native(), (*C.gchar)(cstr))
defer gbox.Delete(uintptr(ptr))

runtime.KeepAlive(v)

return gbox.Get(uintptr(ptr))
}

// FreezeNotify increases the freeze count on object. If the freeze count is
// non-zero, the emission of “notify” signals on object is stopped. The signals
// are queued until the freeze count is decreased to zero. Duplicate
Expand Down
2 changes: 2 additions & 0 deletions pkg/core/glib/glib.go.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,4 +109,6 @@ static void init_i18n(const char *domain, const char *dir) {

static const char *localize(const char *string) { return _(string); }

extern void _gotk4_data_destroy(gpointer ptr);

#endif
18 changes: 12 additions & 6 deletions pkg/core/test/glib_test.go
Original file line number Diff line number Diff line change
@@ -1,21 +1,27 @@
package test

import (
"runtime"
"testing"

"github.com/diamondburned/gotk4/pkg/gtk/v4"
"github.com/diamondburned/gotk4/pkg/core/glib"
"github.com/diamondburned/gotk4/pkg/gio/v2"
)

func TestObjectData(t *testing.T) {
gtk.Init()
testObjectData(t)

label := gtk.NewLabel("label")
runtime.GC()
}

func testObjectData(t *testing.T) {
app := gio.NewApplication("foo.bar", gio.ApplicationFlagsNone)

label.SetObjectData("foo", "bar")
glib.Bind(app, "foo")

if label.ObjectData("foo") != "bar" {
if value := glib.Bounded[string](app); value == nil || *value != "foo" {
t.Fatal("returned data did not match expected data")
}

label.StealObjectData("foo")
glib.Unbind[string](app)
}

0 comments on commit e82e6e0

Please sign in to comment.