forked from go-qml/qml
-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathqmltypes.go
135 lines (121 loc) · 4.36 KB
/
qmltypes.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
package qml
// #include <stdlib.h>
//
// #include "capi.h"
//
import "C"
import (
"fmt"
"reflect"
"unsafe"
)
// TypeSpec holds the specification of a QML type that is backed by Go logic.
//
// The type specification must be registered with the RegisterTypes function
// before it will be visible to QML code, as in:
//
// qml.RegisterTypes("GoExtensions", 1, 0, []qml.TypeSpec{{
// Init: func(p *Person, obj qml.Object) {},
// }})
//
// See the package documentation for more details.
//
type TypeSpec struct {
// Init must be set to a function that is called when QML code requests
// the creation of a new value of this type. The provided function must
// have the following type:
//
// func(value *CustomType, object qml.Object)
//
// Where CustomType is the custom type being registered. The function will
// be called with a newly created *CustomType and its respective qml.Object.
Init interface{}
// Name optionally holds the identifier the type is known as within QML code,
// when the registered extension module is imported. If not specified, the
// name of the Go type provided as the first argument of Init is used instead.
Name string
// Singleton defines whether a single instance of the type should be used
// for all accesses, as a singleton value. If true, all properties of the
// singleton value are directly accessible under the type name.
Singleton bool
private struct{} // Force use of fields by name.
}
var types []*TypeSpec
// RegisterTypes registers the provided list of type specifications for use
// by QML code. To access the registered types, they must be imported from the
// provided location and major.minor version numbers.
//
// For example, with a location "GoExtensions", major 4, and minor 2, this statement
// imports all the registered types in the module's namespace:
//
// import GoExtensions 4.2
//
// See the documentation on QML import statements for details on these:
//
// http://qt-project.org/doc/qt-5.0/qtqml/qtqml-syntax-imports.html
//
func RegisterTypes(location string, major, minor int, types []TypeSpec) {
for i := range types {
err := registerType(location, major, minor, &types[i])
if err != nil {
panic(err)
}
}
}
func registerType(location string, major, minor int, spec *TypeSpec) error {
// Copy and hold a reference to the spec data.
localSpec := *spec
f := reflect.ValueOf(localSpec.Init)
ft := f.Type()
if ft.Kind() != reflect.Func {
return fmt.Errorf("TypeSpec.Init must be a function, got %#v", localSpec.Init)
}
if ft.NumIn() != 2 {
return fmt.Errorf("TypeSpec.Init's function must accept two arguments: %s", ft)
}
firstArg := ft.In(0)
if firstArg.Kind() != reflect.Ptr || firstArg.Elem().Kind() == reflect.Ptr {
return fmt.Errorf("TypeSpec.Init's function must take a pointer type as the second argument: %s", ft)
}
if ft.In(1) != typeObject {
return fmt.Errorf("TypeSpec.Init's function must take qml.Object as the second argument: %s", ft)
}
customType := typeInfo(reflect.New(firstArg.Elem()).Interface())
if localSpec.Name == "" {
localSpec.Name = firstArg.Elem().Name()
if localSpec.Name == "" {
panic("cannot determine registered type name; please provide one explicitly")
}
}
var err error
RunMain(func() {
cloc := C.CString(location)
cname := C.CString(localSpec.Name)
cres := C.int(0)
if localSpec.Singleton {
cres = C.registerSingleton(cloc, C.int(major), C.int(minor), cname, customType, unsafe.Pointer(&localSpec))
} else {
cres = C.registerType(cloc, C.int(major), C.int(minor), cname, customType, unsafe.Pointer(&localSpec))
}
// It doesn't look like it keeps references to these, but it's undocumented and unclear.
C.free(unsafe.Pointer(cloc))
C.free(unsafe.Pointer(cname))
if cres == -1 {
err = fmt.Errorf("QML engine failed to register type; invalid type location or name?")
} else {
types = append(types, &localSpec)
}
})
return err
}
// RegisterConverter registers the convereter function to be called when a
// value with the provided type name is obtained from QML logic. The function
// must return the new value to be used in place of the original value.
func RegisterConverter(typeName string, converter func(engine *Engine, obj Object) interface{}) {
if converter == nil {
delete(converters, typeName)
} else {
converters[typeName] = converter
}
}
var converters = make(map[string]func(engine *Engine, obj Object) interface{})