aboutsummaryrefslogtreecommitdiffstats
path: root/Godeps/_workspace/src/github.com/obscuren/qml/qml.go
diff options
context:
space:
mode:
Diffstat (limited to 'Godeps/_workspace/src/github.com/obscuren/qml/qml.go')
-rw-r--r--Godeps/_workspace/src/github.com/obscuren/qml/qml.go1109
1 files changed, 0 insertions, 1109 deletions
diff --git a/Godeps/_workspace/src/github.com/obscuren/qml/qml.go b/Godeps/_workspace/src/github.com/obscuren/qml/qml.go
deleted file mode 100644
index 1fa8f8ad4..000000000
--- a/Godeps/_workspace/src/github.com/obscuren/qml/qml.go
+++ /dev/null
@@ -1,1109 +0,0 @@
-package qml
-
-// #include <stdlib.h>
-//
-// #include "capi.h"
-//
-import "C"
-
-import (
- "errors"
- "fmt"
- "gopkg.in/qml.v1/gl/glbase"
- "image"
- "image/color"
- "io"
- "io/ioutil"
- "os"
- "path/filepath"
- "reflect"
- "strings"
- "sync"
- "unsafe"
-)
-
-// Engine provides an environment for instantiating QML components.
-type Engine struct {
- Common
- values map[interface{}]*valueFold
- destroyed bool
-
- imageProviders map[string]*func(imageId string, width, height int) image.Image
-}
-
-var engines = make(map[unsafe.Pointer]*Engine)
-
-// NewEngine returns a new QML engine.
-//
-// The Destory method must be called to finalize the engine and
-// release any resources used.
-func NewEngine() *Engine {
- engine := &Engine{values: make(map[interface{}]*valueFold)}
- RunMain(func() {
- engine.addr = C.newEngine(nil)
- engine.engine = engine
- engine.imageProviders = make(map[string]*func(imageId string, width, height int) image.Image)
- engines[engine.addr] = engine
- stats.enginesAlive(+1)
- })
- return engine
-}
-
-func (e *Engine) assertValid() {
- if e.destroyed {
- panic("engine already destroyed")
- }
-}
-
-// Destroy finalizes the engine and releases any resources used.
-// The engine must not be used after calling this method.
-//
-// It is safe to call Destroy more than once.
-func (e *Engine) Destroy() {
- if !e.destroyed {
- RunMain(func() {
- if !e.destroyed {
- e.destroyed = true
- C.delObjectLater(e.addr)
- if len(e.values) == 0 {
- delete(engines, e.addr)
- } else {
- // The engine reference keeps those values alive.
- // The last value destroyed will clear it.
- }
- stats.enginesAlive(-1)
- }
- })
- }
-}
-
-// Load loads a new component with the provided location and with the
-// content read from r. The location informs the resource name for
-// logged messages, and its path is used to locate any other resources
-// referenced by the QML content.
-//
-// Once a component is loaded, component instances may be created from
-// the resulting object via its Create and CreateWindow methods.
-func (e *Engine) Load(location string, r io.Reader) (Object, error) {
- var cdata *C.char
- var cdatalen C.int
-
- qrc := strings.HasPrefix(location, "qrc:")
- if qrc {
- if r != nil {
- return nil, fmt.Errorf("cannot load qrc resource while providing data: %s", location)
- }
- } else {
- data, err := ioutil.ReadAll(r)
- if err != nil {
- return nil, err
- }
- if colon, slash := strings.Index(location, ":"), strings.Index(location, "/"); colon == -1 || slash <= colon {
- if filepath.IsAbs(location) {
- location = "file:///" + filepath.ToSlash(location)
- } else {
- dir, err := os.Getwd()
- if err != nil {
- return nil, fmt.Errorf("cannot obtain absolute path: %v", err)
- }
- location = "file:///" + filepath.ToSlash(filepath.Join(dir, location))
- }
- }
-
- // Workaround issue #84 (QTBUG-41193) by not refering to an existent file.
- if s := strings.TrimPrefix(location, "file:///"); s != location {
- if _, err := os.Stat(filepath.FromSlash(s)); err == nil {
- location = location + "."
- }
- }
-
- cdata, cdatalen = unsafeBytesData(data)
- }
-
- var err error
- cloc, cloclen := unsafeStringData(location)
- comp := &Common{engine: e}
- RunMain(func() {
- // TODO The component's parent should probably be the engine.
- comp.addr = C.newComponent(e.addr, nilPtr)
- if qrc {
- C.componentLoadURL(comp.addr, cloc, cloclen)
- } else {
- C.componentSetData(comp.addr, cdata, cdatalen, cloc, cloclen)
- }
- message := C.componentErrorString(comp.addr)
- if message != nilCharPtr {
- err = errors.New(strings.TrimRight(C.GoString(message), "\n"))
- C.free(unsafe.Pointer(message))
- }
- })
- if err != nil {
- return nil, err
- }
- return comp, nil
-}
-
-// LoadFile loads a component from the provided QML file.
-// Resources referenced by the QML content will be resolved relative to its path.
-//
-// Once a component is loaded, component instances may be created from
-// the resulting object via its Create and CreateWindow methods.
-func (e *Engine) LoadFile(path string) (Object, error) {
- if strings.HasPrefix(path, "qrc:") {
- return e.Load(path, nil)
- }
- // TODO Test this.
- f, err := os.Open(path)
- if err != nil {
- return nil, err
- }
- defer f.Close()
- return e.Load(path, f)
-}
-
-// LoadString loads a component from the provided QML string.
-// The location informs the resource name for logged messages, and its
-// path is used to locate any other resources referenced by the QML content.
-//
-// Once a component is loaded, component instances may be created from
-// the resulting object via its Create and CreateWindow methods.
-func (e *Engine) LoadString(location, qml string) (Object, error) {
- return e.Load(location, strings.NewReader(qml))
-}
-
-// Context returns the engine's root context.
-func (e *Engine) Context() *Context {
- e.assertValid()
- var ctx Context
- ctx.engine = e
- RunMain(func() {
- ctx.addr = C.engineRootContext(e.addr)
- })
- return &ctx
-}
-
-// TODO ObjectOf is probably still worth it, but turned out unnecessary
-// for GL functionality. Test it properly before introducing it.
-
-// ObjectOf returns the QML Object representation of the provided Go value
-// within the e engine.
-//func (e *Engine) ObjectOf(value interface{}) Object {
-// // TODO Would be good to preserve identity on the Go side. See unpackDataValue as well.
-// return &Common{
-// engine: e,
-// addr: wrapGoValue(e, value, cppOwner),
-// }
-//}
-
-// Painter is provided to Paint methods on Go types that have displayable content.
-type Painter struct {
- engine *Engine
- obj Object
- glctxt glbase.Context
-}
-
-// Object returns the underlying object being painted.
-func (p *Painter) Object() Object {
- return p.obj
-}
-
-// GLContext returns the OpenGL context for this painter.
-func (p *Painter) GLContext() *glbase.Context {
- return &p.glctxt
-}
-
-// AddImageProvider registers f to be called when an image is requested by QML code
-// with the specified provider identifier. It is a runtime error to register the same
-// provider identifier multiple times.
-//
-// The imgId provided to f is the requested image source, with the "image:" scheme
-// and provider identifier removed. For example, with an image image source of
-// "image://myprovider/icons/home.ext", the respective imgId would be "icons/home.ext".
-//
-// If either the width or the height parameters provided to f are zero, no specific
-// size for the image was requested. If non-zero, the returned image should have the
-// the provided size, and will be resized if the returned image has a different size.
-//
-// See the documentation for more details on image providers:
-//
-// http://qt-project.org/doc/qt-5.0/qtquick/qquickimageprovider.html
-//
-func (e *Engine) AddImageProvider(prvId string, f func(imgId string, width, height int) image.Image) {
- if _, ok := e.imageProviders[prvId]; ok {
- panic(fmt.Sprintf("engine already has an image provider with id %q", prvId))
- }
- e.imageProviders[prvId] = &f
- cprvId, cprvIdLen := unsafeStringData(prvId)
- RunMain(func() {
- qprvId := C.newString(cprvId, cprvIdLen)
- defer C.delString(qprvId)
- C.engineAddImageProvider(e.addr, qprvId, unsafe.Pointer(&f))
- })
-}
-
-//export hookRequestImage
-func hookRequestImage(imageFunc unsafe.Pointer, cid *C.char, cidLen, cwidth, cheight C.int) unsafe.Pointer {
- f := *(*func(imgId string, width, height int) image.Image)(imageFunc)
-
- id := unsafeString(cid, cidLen)
- width := int(cwidth)
- height := int(cheight)
-
- img := f(id, width, height)
-
- var cimage unsafe.Pointer
-
- rect := img.Bounds()
- width = rect.Max.X - rect.Min.X
- height = rect.Max.Y - rect.Min.Y
- cimage = C.newImage(C.int(width), C.int(height))
-
- var cbits []byte
- cbitsh := (*reflect.SliceHeader)((unsafe.Pointer)(&cbits))
- cbitsh.Data = (uintptr)((unsafe.Pointer)(C.imageBits(cimage)))
- cbitsh.Len = width * height * 4 // RGBA
- cbitsh.Cap = cbitsh.Len
-
- i := 0
- for y := 0; y < height; y++ {
- for x := 0; x < width; x++ {
- r, g, b, a := img.At(x, y).RGBA()
- *(*uint32)(unsafe.Pointer(&cbits[i])) = (a>>8)<<24 | (r>>8)<<16 | (g>>8)<<8 | (b >> 8)
- i += 4
- }
- }
- return cimage
-}
-
-// Context represents a QML context that can hold variables visible
-// to logic running within it.
-type Context struct {
- Common
-}
-
-// SetVar makes the provided value available as a variable with the
-// given name for QML code executed within the c context.
-//
-// If value is a struct, its exported fields are also made accessible to
-// QML code as attributes of the named object. The attribute name in the
-// object has the same name of the Go field name, except for the first
-// letter which is lowercased. This is conventional and enforced by
-// the QML implementation.
-//
-// The engine will hold a reference to the provided value, so it will
-// not be garbage collected until the engine is destroyed, even if the
-// value is unused or changed.
-func (ctx *Context) SetVar(name string, value interface{}) {
- cname, cnamelen := unsafeStringData(name)
- RunMain(func() {
- var dvalue C.DataValue
- packDataValue(value, &dvalue, ctx.engine, cppOwner)
-
- qname := C.newString(cname, cnamelen)
- defer C.delString(qname)
-
- C.contextSetProperty(ctx.addr, qname, &dvalue)
- })
-}
-
-// SetVars makes the exported fields of the provided value available as
-// variables for QML code executed within the c context. The variable names
-// will have the same name of the Go field names, except for the first
-// letter which is lowercased. This is conventional and enforced by
-// the QML implementation.
-//
-// The engine will hold a reference to the provided value, so it will
-// not be garbage collected until the engine is destroyed, even if the
-// value is unused or changed.
-func (ctx *Context) SetVars(value interface{}) {
- RunMain(func() {
- C.contextSetObject(ctx.addr, wrapGoValue(ctx.engine, value, cppOwner))
- })
-}
-
-// Var returns the context variable with the given name.
-func (ctx *Context) Var(name string) interface{} {
- cname, cnamelen := unsafeStringData(name)
-
- var dvalue C.DataValue
- RunMain(func() {
- qname := C.newString(cname, cnamelen)
- defer C.delString(qname)
-
- C.contextGetProperty(ctx.addr, qname, &dvalue)
- })
- return unpackDataValue(&dvalue, ctx.engine)
-}
-
-// Spawn creates a new context that has ctx as a parent.
-func (ctx *Context) Spawn() *Context {
- var result Context
- result.engine = ctx.engine
- RunMain(func() {
- result.addr = C.contextSpawn(ctx.addr)
- })
- return &result
-}
-
-// Object is the common interface implemented by all QML types.
-//
-// See the documentation of Common for details about this interface.
-type Object interface {
- Common() *Common
- Addr() uintptr
- TypeName() string
- Interface() interface{}
- Set(property string, value interface{})
- Property(name string) interface{}
- Int(property string) int
- Int64(property string) int64
- Float64(property string) float64
- Bool(property string) bool
- String(property string) string
- Color(property string) color.RGBA
- Object(property string) Object
- Map(property string) *Map
- List(property string) *List
- ObjectByName(objectName string) Object
- Call(method string, params ...interface{}) interface{}
- Create(ctx *Context) Object
- CreateWindow(ctx *Context) *Window
- Destroy()
- On(signal string, function interface{})
-}
-
-// List holds a QML list which may be converted to a Go slice of an
-// appropriate type via Convert.
-//
-// In the future this will also be able to hold a reference
-// to QML-owned maps, so they can be mutated in place.
-type List struct {
- // In the future this will be able to hold a reference to QML-owned
- // lists, so they can be mutated.
- data []interface{}
-}
-
-// Len returns the number of elements in the list.
-func (l *List) Len() int {
- return len(l.data)
-}
-
-// Convert allocates a new slice and copies the list content into it,
-// performing type conversions as possible, and then assigns the result
-// to the slice pointed to by sliceAddr.
-// Convert panics if the list values are not compatible with the
-// provided slice.
-func (l *List) Convert(sliceAddr interface{}) {
- toPtr := reflect.ValueOf(sliceAddr)
- if toPtr.Kind() != reflect.Ptr || toPtr.Type().Elem().Kind() != reflect.Slice {
- panic(fmt.Sprintf("List.Convert got a sliceAddr parameter that is not a slice address: %#v", sliceAddr))
- }
- err := convertAndSet(toPtr.Elem(), reflect.ValueOf(l), reflect.Value{})
- if err != nil {
- panic(err.Error())
- }
-}
-
-// Map holds a QML map which may be converted to a Go map of an
-// appropriate type via Convert.
-//
-// In the future this will also be able to hold a reference
-// to QML-owned maps, so they can be mutated in place.
-type Map struct {
- data []interface{}
-}
-
-// Len returns the number of pairs in the map.
-func (m *Map) Len() int {
- return len(m.data) / 2
-}
-
-// Convert allocates a new map and copies the content of m property to it,
-// performing type conversions as possible, and then assigns the result to
-// the map pointed to by mapAddr. Map panics if m contains values that
-// cannot be converted to the type of the map at mapAddr.
-func (m *Map) Convert(mapAddr interface{}) {
- toPtr := reflect.ValueOf(mapAddr)
- if toPtr.Kind() != reflect.Ptr || toPtr.Type().Elem().Kind() != reflect.Map {
- panic(fmt.Sprintf("Map.Convert got a mapAddr parameter that is not a map address: %#v", mapAddr))
- }
- err := convertAndSet(toPtr.Elem(), reflect.ValueOf(m), reflect.Value{})
- if err != nil {
- panic(err.Error())
- }
-}
-
-// Common implements the common behavior of all QML objects.
-// It implements the Object interface.
-type Common struct {
- addr unsafe.Pointer
- engine *Engine
-}
-
-var _ Object = (*Common)(nil)
-
-// CommonOf returns the Common QML value for the QObject at addr.
-//
-// This is meant for extensions that integrate directly with the
-// underlying QML logic.
-func CommonOf(addr unsafe.Pointer, engine *Engine) *Common {
- return &Common{addr, engine}
-}
-
-// Common returns obj itself.
-//
-// This provides access to the underlying *Common for types that
-// embed it, when these are used via the Object interface.
-func (obj *Common) Common() *Common {
- return obj
-}
-
-// TypeName returns the underlying type name for the held value.
-func (obj *Common) TypeName() string {
- var name string
- RunMain(func() {
- name = C.GoString(C.objectTypeName(obj.addr))
- })
- return name
-}
-
-// Addr returns the QML object address.
-//
-// This is meant for extensions that integrate directly with the
-// underlying QML logic.
-func (obj *Common) Addr() uintptr {
- return uintptr(obj.addr)
-}
-
-// Interface returns the underlying Go value that is being held by
-// the object wrapper.
-//
-// It is a runtime error to call Interface on values that are not
-// backed by a Go value.
-func (obj *Common) Interface() interface{} {
- var result interface{}
- var cerr *C.error
- RunMain(func() {
- var fold *valueFold
- if cerr = C.objectGoAddr(obj.addr, (*unsafe.Pointer)(unsafe.Pointer(&fold))); cerr == nil {
- result = fold.gvalue
- }
- })
- cmust(cerr)
- return result
-}
-
-// Set changes the named object property to the given value.
-func (obj *Common) Set(property string, value interface{}) {
- cproperty := C.CString(property)
- defer C.free(unsafe.Pointer(cproperty))
- var cerr *C.error
- RunMain(func() {
- var dvalue C.DataValue
- packDataValue(value, &dvalue, obj.engine, cppOwner)
- cerr = C.objectSetProperty(obj.addr, cproperty, &dvalue)
- })
- cmust(cerr)
-}
-
-// Property returns the current value for a property of the object.
-// If the property type is known, type-specific methods such as Int
-// and String are more convenient to use.
-// Property panics if the property does not exist.
-func (obj *Common) Property(name string) interface{} {
- cname := C.CString(name)
- defer C.free(unsafe.Pointer(cname))
-
- var dvalue C.DataValue
- var found C.int
- RunMain(func() {
- found = C.objectGetProperty(obj.addr, cname, &dvalue)
- })
- if found == 0 {
- panic(fmt.Sprintf("object does not have a %q property", name))
- }
- return unpackDataValue(&dvalue, obj.engine)
-}
-
-// Int returns the int value of the named property.
-// Int panics if the property cannot be represented as an int.
-func (obj *Common) Int(property string) int {
- switch value := obj.Property(property).(type) {
- case int64:
- return int(value)
- case int:
- return value
- case uint64:
- return int(value)
- case uint32:
- return int(value)
- case uintptr:
- return int(value)
- case float32:
- return int(value)
- case float64:
- return int(value)
- default:
- panic(fmt.Sprintf("value of property %q cannot be represented as an int: %#v", property, value))
- }
-}
-
-// Int64 returns the int64 value of the named property.
-// Int64 panics if the property cannot be represented as an int64.
-func (obj *Common) Int64(property string) int64 {
- switch value := obj.Property(property).(type) {
- case int64:
- return value
- case int:
- return int64(value)
- case uint64:
- return int64(value)
- case uint32:
- return int64(value)
- case uintptr:
- return int64(value)
- case float32:
- return int64(value)
- case float64:
- return int64(value)
- default:
- panic(fmt.Sprintf("value of property %q cannot be represented as an int64: %#v", property, value))
- }
-}
-
-// Float64 returns the float64 value of the named property.
-// Float64 panics if the property cannot be represented as float64.
-func (obj *Common) Float64(property string) float64 {
- switch value := obj.Property(property).(type) {
- case int64:
- return float64(value)
- case int:
- return float64(value)
- case uint64:
- return float64(value)
- case uint32:
- return float64(value)
- case uintptr:
- return float64(value)
- case float32:
- return float64(value)
- case float64:
- return value
- default:
- panic(fmt.Sprintf("value of property %q cannot be represented as a float64: %#v", property, value))
- }
-}
-
-// Bool returns the bool value of the named property.
-// Bool panics if the property is not a bool.
-func (obj *Common) Bool(property string) bool {
- value := obj.Property(property)
- if b, ok := value.(bool); ok {
- return b
- }
- panic(fmt.Sprintf("value of property %q is not a bool: %#v", property, value))
-}
-
-// String returns the string value of the named property.
-// String panics if the property is not a string.
-func (obj *Common) String(property string) string {
- value := obj.Property(property)
- if s, ok := value.(string); ok {
- return s
- }
- panic(fmt.Sprintf("value of property %q is not a string: %#v", property, value))
-}
-
-// Color returns the RGBA value of the named property.
-// Color panics if the property is not a color.
-func (obj *Common) Color(property string) color.RGBA {
- value := obj.Property(property)
- c, ok := value.(color.RGBA)
- if !ok {
- panic(fmt.Sprintf("value of property %q is not a color: %#v", property, value))
- }
- return c
-}
-
-// Object returns the object value of the named property.
-// Object panics if the property is not a QML object.
-func (obj *Common) Object(property string) Object {
- value := obj.Property(property)
- object, ok := value.(Object)
- if !ok {
- panic(fmt.Sprintf("value of property %q is not a QML object: %#v", property, value))
- }
- return object
-}
-
-// List returns the list value of the named property.
-// List panics if the property is not a list.
-func (obj *Common) List(property string) *List {
- value := obj.Property(property)
- m, ok := value.(*List)
- if !ok {
- panic(fmt.Sprintf("value of property %q is not a QML list: %#v", property, value))
- }
- return m
-}
-
-// Map returns the map value of the named property.
-// Map panics if the property is not a map.
-func (obj *Common) Map(property string) *Map {
- value := obj.Property(property)
- m, ok := value.(*Map)
- if !ok {
- panic(fmt.Sprintf("value of property %q is not a QML map: %#v", property, value))
- }
- return m
-}
-
-// ObjectByName returns the Object value of the descendant object that
-// was defined with the objectName property set to the provided value.
-// ObjectByName panics if the object is not found.
-func (obj *Common) ObjectByName(objectName string) Object {
- cname, cnamelen := unsafeStringData(objectName)
- var dvalue C.DataValue
- var object Object
- RunMain(func() {
- qname := C.newString(cname, cnamelen)
- defer C.delString(qname)
- C.objectFindChild(obj.addr, qname, &dvalue)
- // unpackDataValue will also initialize the Go type, if necessary.
- value := unpackDataValue(&dvalue, obj.engine)
- if dvalue.dataType == C.DTGoAddr {
- datap := unsafe.Pointer(&dvalue.data)
- fold := (*(**valueFold)(datap))
- if fold.init.IsValid() {
- panic("internal error: custom Go type not initialized")
- }
- object = &Common{fold.cvalue, fold.engine}
- } else {
- object, _ = value.(Object)
- }
- })
- if object == nil {
- panic(fmt.Sprintf("cannot find descendant with objectName == %q", objectName))
- }
- return object
-}
-
-// Call calls the given object method with the provided parameters.
-// Call panics if the method does not exist.
-func (obj *Common) Call(method string, params ...interface{}) interface{} {
- if len(params) > len(dataValueArray) {
- panic("too many parameters")
- }
- cmethod, cmethodLen := unsafeStringData(method)
- var result C.DataValue
- var cerr *C.error
- RunMain(func() {
- for i, param := range params {
- packDataValue(param, &dataValueArray[i], obj.engine, jsOwner)
- }
- cerr = C.objectInvoke(obj.addr, cmethod, cmethodLen, &result, &dataValueArray[0], C.int(len(params)))
- })
- cmust(cerr)
- return unpackDataValue(&result, obj.engine)
-}
-
-// Create creates a new instance of the component held by obj.
-// The component instance runs under the ctx context. If ctx is nil,
-// it runs under the same context as obj.
-//
-// The Create method panics if called on an object that does not
-// represent a QML component.
-func (obj *Common) Create(ctx *Context) Object {
- if C.objectIsComponent(obj.addr) == 0 {
- panic("object is not a component")
- }
- var root Common
- root.engine = obj.engine
- RunMain(func() {
- ctxaddr := nilPtr
- if ctx != nil {
- ctxaddr = ctx.addr
- }
- root.addr = C.componentCreate(obj.addr, ctxaddr)
- })
- return &root
-}
-
-// CreateWindow creates a new instance of the component held by obj,
-// and creates a new window holding the instance as its root object.
-// The component instance runs under the ctx context. If ctx is nil,
-// it runs under the same context as obj.
-//
-// The CreateWindow method panics if called on an object that
-// does not represent a QML component.
-func (obj *Common) CreateWindow(ctx *Context) *Window {
- if C.objectIsComponent(obj.addr) == 0 {
- panic("object is not a component")
- }
- var win Window
- win.engine = obj.engine
- RunMain(func() {
- ctxaddr := nilPtr
- if ctx != nil {
- ctxaddr = ctx.addr
- }
- win.addr = C.componentCreateWindow(obj.addr, ctxaddr)
- })
- return &win
-}
-
-// Destroy finalizes the value and releases any resources used.
-// The value must not be used after calling this method.
-func (obj *Common) Destroy() {
- // TODO We might hook into the destroyed signal, and prevent this object
- // from being used in post-destruction crash-prone ways.
- RunMain(func() {
- if obj.addr != nilPtr {
- C.delObjectLater(obj.addr)
- obj.addr = nilPtr
- }
- })
-}
-
-var connectedFunction = make(map[*interface{}]bool)
-
-// On connects the named signal from obj with the provided function, so that
-// when obj next emits that signal, the function is called with the parameters
-// the signal carries.
-//
-// The provided function must accept a number of parameters that is equal to
-// or less than the number of parameters provided by the signal, and the
-// resepctive parameter types must match exactly or be conversible according
-// to normal Go rules.
-//
-// For example:
-//
-// obj.On("clicked", func() { fmt.Println("obj got a click") })
-//
-// Note that Go uses the real signal name, rather than the one used when
-// defining QML signal handlers ("clicked" rather than "onClicked").
-//
-// For more details regarding signals and QML see:
-//
-// http://qt-project.org/doc/qt-5.0/qtqml/qml-qtquick2-connections.html
-//
-func (obj *Common) On(signal string, function interface{}) {
- funcv := reflect.ValueOf(function)
- funct := funcv.Type()
- if funcv.Kind() != reflect.Func {
- panic("function provided to On is not a function or method")
- }
- if funct.NumIn() > C.MaxParams {
- panic("function takes too many arguments")
- }
- csignal, csignallen := unsafeStringData(signal)
- var cerr *C.error
- RunMain(func() {
- cerr = C.objectConnect(obj.addr, csignal, csignallen, obj.engine.addr, unsafe.Pointer(&function), C.int(funcv.Type().NumIn()))
- if cerr == nil {
- connectedFunction[&function] = true
- stats.connectionsAlive(+1)
- }
- })
- cmust(cerr)
-}
-
-//export hookSignalDisconnect
-func hookSignalDisconnect(funcp unsafe.Pointer) {
- before := len(connectedFunction)
- delete(connectedFunction, (*interface{})(funcp))
- if before == len(connectedFunction) {
- panic("disconnecting unknown signal function")
- }
- stats.connectionsAlive(-1)
-}
-
-//export hookSignalCall
-func hookSignalCall(enginep unsafe.Pointer, funcp unsafe.Pointer, args *C.DataValue) {
- engine := engines[enginep]
- if engine == nil {
- panic("signal called after engine was destroyed")
- }
- funcv := reflect.ValueOf(*(*interface{})(funcp))
- funct := funcv.Type()
- numIn := funct.NumIn()
- var params [C.MaxParams]reflect.Value
- for i := 0; i < numIn; i++ {
- arg := (*C.DataValue)(unsafe.Pointer(uintptr(unsafe.Pointer(args)) + uintptr(i)*dataValueSize))
- param := reflect.ValueOf(unpackDataValue(arg, engine))
- if paramt := funct.In(i); param.Type() != paramt {
- // TODO Provide a better error message when this fails.
- param = param.Convert(paramt)
- }
- params[i] = param
- }
- funcv.Call(params[:numIn])
-}
-
-func cerror(cerr *C.error) error {
- err := errors.New(C.GoString((*C.char)(unsafe.Pointer(cerr))))
- C.free(unsafe.Pointer(cerr))
- return err
-}
-
-func cmust(cerr *C.error) {
- if cerr != nil {
- panic(cerror(cerr).Error())
- }
-}
-
-// TODO Signal emitting support for go values.
-
-// Window represents a QML window where components are rendered.
-type Window struct {
- Common
-}
-
-// Show exposes the window.
-func (win *Window) Show() {
- RunMain(func() {
- C.windowShow(win.addr)
- })
-}
-
-// Hide hides the window.
-func (win *Window) Hide() {
- RunMain(func() {
- C.windowHide(win.addr)
- })
-}
-
-// PlatformId returns the window's platform id.
-//
-// For platforms where this id might be useful, the value returned will
-// uniquely represent the window inside the corresponding screen.
-func (win *Window) PlatformId() uintptr {
- var id uintptr
- RunMain(func() {
- id = uintptr(C.windowPlatformId(win.addr))
- })
- return id
-}
-
-// Root returns the root object being rendered.
-//
-// If the window was defined in QML code, the root object is the window itself.
-func (win *Window) Root() Object {
- var obj Common
- obj.engine = win.engine
- RunMain(func() {
- obj.addr = C.windowRootObject(win.addr)
- })
- return &obj
-}
-
-// Wait blocks the current goroutine until the window is closed.
-func (win *Window) Wait() {
- // XXX Test this.
- var m sync.Mutex
- m.Lock()
- RunMain(func() {
- // TODO Must be able to wait for the same Window from multiple goroutines.
- // TODO If the window is not visible, must return immediately.
- waitingWindows[win.addr] = &m
- C.windowConnectHidden(win.addr)
- })
- m.Lock()
-}
-
-var waitingWindows = make(map[unsafe.Pointer]*sync.Mutex)
-
-//export hookWindowHidden
-func hookWindowHidden(addr unsafe.Pointer) {
- m, ok := waitingWindows[addr]
- if !ok {
- panic("window is not waiting")
- }
- delete(waitingWindows, addr)
- m.Unlock()
-}
-
-// Snapshot returns an image with the visible contents of the window.
-// The main GUI thread is paused while the data is being acquired.
-func (win *Window) Snapshot() image.Image {
- // TODO Test this.
- var cimage unsafe.Pointer
- RunMain(func() {
- cimage = C.windowGrabWindow(win.addr)
- })
- defer C.delImage(cimage)
-
- // This should be safe to be done out of the main GUI thread.
- var cwidth, cheight C.int
- C.imageSize(cimage, &cwidth, &cheight)
-
- var cbits []byte
- cbitsh := (*reflect.SliceHeader)((unsafe.Pointer)(&cbits))
- cbitsh.Data = (uintptr)((unsafe.Pointer)(C.imageConstBits(cimage)))
- cbitsh.Len = int(cwidth * cheight * 8) // ARGB
- cbitsh.Cap = cbitsh.Len
-
- image := image.NewRGBA(image.Rect(0, 0, int(cwidth), int(cheight)))
- l := int(cwidth * cheight * 4)
- for i := 0; i < l; i += 4 {
- var c uint32 = *(*uint32)(unsafe.Pointer(&cbits[i]))
- image.Pix[i+0] = byte(c >> 16)
- image.Pix[i+1] = byte(c >> 8)
- image.Pix[i+2] = byte(c)
- image.Pix[i+3] = byte(c >> 24)
- }
- return image
-}
-
-// 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{})
-
-// LoadResources registers all resources in the provided resources collection,
-// making them available to be loaded by any Engine and QML file.
-// Registered resources are made available under "qrc:///some/path", where
-// "some/path" is the path the resource was added with.
-func LoadResources(r *Resources) {
- var base unsafe.Pointer
- if len(r.sdata) > 0 {
- base = *(*unsafe.Pointer)(unsafe.Pointer(&r.sdata))
- } else if len(r.bdata) > 0 {
- base = *(*unsafe.Pointer)(unsafe.Pointer(&r.bdata))
- }
- tree := (*C.char)(unsafe.Pointer(uintptr(base)+uintptr(r.treeOffset)))
- name := (*C.char)(unsafe.Pointer(uintptr(base)+uintptr(r.nameOffset)))
- data := (*C.char)(unsafe.Pointer(uintptr(base)+uintptr(r.dataOffset)))
- C.registerResourceData(C.int(r.version), tree, name, data)
-}
-
-// UnloadResources unregisters all previously registered resources from r.
-func UnloadResources(r *Resources) {
- var base unsafe.Pointer
- if len(r.sdata) > 0 {
- base = *(*unsafe.Pointer)(unsafe.Pointer(&r.sdata))
- } else if len(r.bdata) > 0 {
- base = *(*unsafe.Pointer)(unsafe.Pointer(&r.bdata))
- }
- tree := (*C.char)(unsafe.Pointer(uintptr(base)+uintptr(r.treeOffset)))
- name := (*C.char)(unsafe.Pointer(uintptr(base)+uintptr(r.nameOffset)))
- data := (*C.char)(unsafe.Pointer(uintptr(base)+uintptr(r.dataOffset)))
- C.unregisterResourceData(C.int(r.version), tree, name, data)
-}