From f5ca35ce27315ccd6e18eb8d7bdd7bee78644679 Mon Sep 17 00:00:00 2001 From: Roland Singer Date: Thu, 24 Jan 2019 15:36:14 +0100 Subject: [PATCH] new listmodel syntax. Also added code gen support to list models --- internal/build/template_go.go | 1 + listmodel.go | 93 +++++++++++-------- object.go | 18 +++- samples/hello_world/main.go | 1 - samples/hello_world/qml/app.qml | 10 -- samples/list_model/.gitignore | 5 + samples/list_model/main.go | 36 +++++++ .../model => list_model}/model.go | 17 ++-- samples/list_model/qml/app.qml | 22 +++++ 9 files changed, 145 insertions(+), 58 deletions(-) create mode 100644 samples/list_model/.gitignore create mode 100644 samples/list_model/main.go rename samples/{hello_world/model => list_model}/model.go (90%) create mode 100644 samples/list_model/qml/app.qml diff --git a/internal/build/template_go.go b/internal/build/template_go.go index 22b34ec..ebd8862 100644 --- a/internal/build/template_go.go +++ b/internal/build/template_go.go @@ -89,6 +89,7 @@ func init() { func (_v *{{$struct.Name}}) GmlInit() { goPtr := pointer.Save(_v) + _v.GmlObject_SetGoPointer(goPtr) _v.GmlObject_SetPointer(unsafe.Pointer(C.{{$struct.CBaseName}}_new(goPtr))) runtime.SetFinalizer(_v, func(_v *{{$struct.Name}}) { C.{{$struct.CBaseName}}_free((C.{{$struct.CBaseName}})(_v.GmlObject_Pointer())) diff --git a/listmodel.go b/listmodel.go index 1449219..acf17c1 100644 --- a/listmodel.go +++ b/listmodel.go @@ -50,6 +50,10 @@ func init() { C.gml_list_model_init() } +type listModelInitializer interface { + initListModel(handler ListModelHandler) +} + type ListModelHandler interface { RowCount() int Data(row int) interface{} @@ -58,75 +62,86 @@ type ListModelHandler interface { type ListModel struct { Object - freed bool - lm C.gml_list_model - ptr unsafe.Pointer - + ptr C.gml_list_model handler ListModelHandler } -func NewListModel(handler ListModelHandler) *ListModel { - lm := &ListModel{handler: handler} +func InitListModel(m interface{}) { + lmi, ok := m.(listModelInitializer) + if !ok { + panic(fmt.Errorf("failed to assert to list model")) + } - lm.ptr = pointer.Save(lm) - lm.lm = C.gml_list_model_new(lm.ptr) - lm.GmlObject_SetPointer(unsafe.Pointer(lm.lm)) + handler, ok := m.(ListModelHandler) + if !ok { + panic(fmt.Errorf("failed to assert to list model handler")) + } - // Always free the C++ value. - runtime.SetFinalizer(lm, freeListModel) + lmi.initListModel(handler) +} + +func (lm *ListModel) initListModel(handler ListModelHandler) { + lm.handler = handler + + // If signals, slots or properties are defined on the model handler, + // then use the generated C++ type by calling GmlInit... + // Otherwise this is a standalone ListModel. + switch ht := lm.handler.(type) { + case objectInitializer: + ht.GmlInit() + lm.ptr = (C.gml_list_model)(lm.GmlObject_Pointer()) + + default: + handlerGoPtr := pointer.Save(lm.handler) + lm.GmlObject_SetGoPointer(handlerGoPtr) + + lm.ptr = C.gml_list_model_new(handlerGoPtr) + lm.GmlObject_SetPointer(unsafe.Pointer(lm.ptr)) + + // Always cleanup. + runtime.SetFinalizer(lm, func(lm *ListModel) { + C.gml_list_model_free(lm.ptr) + pointer.Unref(handlerGoPtr) + }) + } // Check if something failed. - // This should never happen is signalizes a fatal error. - if lm.lm == nil { + // This should never happen. It signalizes a fatal error. + if lm.ptr == nil { panic(fmt.Errorf("failed to create gml list model: C pointer is nil")) } - - return lm -} - -func (lm *ListModel) Free() { - freeListModel(lm) -} - -func freeListModel(lm *ListModel) { - if lm.freed { - return - } - lm.freed = true - C.gml_list_model_free(lm.lm) - pointer.Unref(lm.ptr) } func (lm *ListModel) Reset(dataModifier func()) { RunMain(func() { // Begin the reset operation. - C.gml_list_model_begin_reset_model(lm.lm) + C.gml_list_model_begin_reset_model(lm.ptr) // Perform the data modifications. dataModifier() // End the reset operation. - C.gml_list_model_end_reset_model(lm.lm) + C.gml_list_model_end_reset_model(lm.ptr) }) } func (lm *ListModel) Insert(row, count int, dataModifier func()) { RunMain(func() { // Begin the insert operation. - C.gml_list_model_begin_insert_rows(lm.lm, C.int(row), C.int(count)) + C.gml_list_model_begin_insert_rows(lm.ptr, C.int(row), C.int(count)) // Perform the data modification. dataModifier() // End the insert operation. - C.gml_list_model_end_insert_rows(lm.lm) + C.gml_list_model_end_insert_rows(lm.ptr) }) } func (lm *ListModel) Move(row, count, dstRow int, dataModifier func()) { RunMain(func() { // Begin the move operation. - C.gml_list_model_begin_move_rows(lm.lm, C.int(row), C.int(count), C.int(dstRow)) + C.gml_list_model_begin_move_rows(lm.ptr, C.int(row), C.int(count), C.int(dstRow)) // Perform the data modification. dataModifier() // End the move operation. - C.gml_list_model_end_move_rows(lm.lm) + C.gml_list_model_end_move_rows(lm.ptr) }) } @@ -135,18 +150,18 @@ func (lm *ListModel) Reload(row, count int, dataModifier func()) { // Perform the data modification. dataModifier() // Signal the changed operation. - C.gml_list_model_rows_data_changed(lm.lm, C.int(row), C.int(count)) + C.gml_list_model_rows_data_changed(lm.ptr, C.int(row), C.int(count)) }) } func (lm *ListModel) Remove(row, count int, dataModifier func()) { RunMain(func() { // Begin the remove operation. - C.gml_list_model_begin_remove_rows(lm.lm, C.int(row), C.int(count)) + C.gml_list_model_begin_remove_rows(lm.ptr, C.int(row), C.int(count)) // Perform the data modification. dataModifier() // End the remove operation. - C.gml_list_model_end_remove_rows(lm.lm) + C.gml_list_model_end_remove_rows(lm.ptr) }) } @@ -156,12 +171,12 @@ func (lm *ListModel) Remove(row, count int, dataModifier func()) { //export gml_list_model_row_count_go_slot func gml_list_model_row_count_go_slot(goPtr unsafe.Pointer) C.int { - return C.int((pointer.Restore(goPtr)).(*ListModel).handler.RowCount()) + return C.int((pointer.Restore(goPtr)).(ListModelHandler).RowCount()) } //export gml_list_model_data_go_slot func gml_list_model_data_go_slot(goPtr unsafe.Pointer, row C.int) C.gml_variant { - data := (pointer.Restore(goPtr)).(*ListModel).handler.Data(int(row)) + data := (pointer.Restore(goPtr)).(ListModelHandler).Data(int(row)) v := ToVariant(data) // Release, because C++ is handling memory. diff --git a/object.go b/object.go index 9140c94..0b60df8 100644 --- a/object.go +++ b/object.go @@ -35,12 +35,17 @@ import ( "unsafe" ) +type objectInitializer interface { + GmlInit() +} + type objectGetter interface { GmlObject() *Object } type Object struct { - ptr unsafe.Pointer + ptr unsafe.Pointer + goPtr unsafe.Pointer } func (o *Object) GmlObject_Pointer() unsafe.Pointer { @@ -54,6 +59,17 @@ func (o *Object) GmlObject_SetPointer(ptr unsafe.Pointer) { o.ptr = ptr } +func (o *Object) GmlObject_GoPointer() unsafe.Pointer { + if o.goPtr == nil { + panic(fmt.Errorf("gml.Object go pointer is nil: did you call GmlInit()?")) + } + return o.goPtr +} + +func (o *Object) GmlObject_SetGoPointer(goPtr unsafe.Pointer) { + o.goPtr = goPtr +} + func (o *Object) GmlObject() *Object { return o } diff --git a/samples/hello_world/main.go b/samples/hello_world/main.go index d3c911d..8c73f95 100644 --- a/samples/hello_world/main.go +++ b/samples/hello_world/main.go @@ -29,7 +29,6 @@ package main import ( "github.com/desertbit/gml" - _ "github.com/desertbit/gml/samples/hello_world/model" ) func main() { diff --git a/samples/hello_world/qml/app.qml b/samples/hello_world/qml/app.qml index b635921..b6ab6fb 100644 --- a/samples/hello_world/qml/app.qml +++ b/samples/hello_world/qml/app.qml @@ -15,14 +15,4 @@ ApplicationWindow { font.pointSize: 24 font.bold: true } - - ListView { - anchors.fill: parent - delegate: Text { - color: "red" - text: display - } - model: m - Component.onCompleted: console.log(m.get(5)) - } } \ No newline at end of file diff --git a/samples/list_model/.gitignore b/samples/list_model/.gitignore new file mode 100644 index 0000000..c898a7e --- /dev/null +++ b/samples/list_model/.gitignore @@ -0,0 +1,5 @@ +/build +/list_model +gml_gen* +*.qrc + diff --git a/samples/list_model/main.go b/samples/list_model/main.go new file mode 100644 index 0000000..8c73f95 --- /dev/null +++ b/samples/list_model/main.go @@ -0,0 +1,36 @@ +/* + * GML - Go QML + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Roland Singer + * Copyright (c) 2019 Sebastian Borchers + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package main + +import ( + "github.com/desertbit/gml" +) + +func main() { + gml.ExecExit("qrc:/qml/app.qml") +} diff --git a/samples/hello_world/model/model.go b/samples/list_model/model.go similarity index 90% rename from samples/hello_world/model/model.go rename to samples/list_model/model.go index 47c21f8..7ab7e2e 100644 --- a/samples/hello_world/model/model.go +++ b/samples/list_model/model.go @@ -25,19 +25,23 @@ * SOFTWARE. */ -package model +package main import ( + "log" "strconv" "github.com/desertbit/gml" - "github.com/rs/zerolog/log" ) +//#############// +//### Slots ###// +//#############// + var M *model type model struct { - *gml.ListModel + gml.ListModel _ struct { get func(row int) string `gml:"slot"` @@ -54,12 +58,11 @@ func (m *model) Data(row int) interface{} { func init() { M = &model{} - M.ListModel = gml.NewListModel(M) - M.GmlInit() + gml.InitListModel(M) err := gml.SetContextProperty("m", M) if err != nil { - log.Fatal().Err(err).Msg("init") + log.Fatalln("failed to set model context property") } } @@ -68,5 +71,5 @@ func init() { //#############// func (m *model) get(row int) string { - return "this is a test, lol" + return "slot get called" } diff --git a/samples/list_model/qml/app.qml b/samples/list_model/qml/app.qml new file mode 100644 index 0000000..8f5ff97 --- /dev/null +++ b/samples/list_model/qml/app.qml @@ -0,0 +1,22 @@ +import QtQuick 2.11 +import QtQuick.Layouts 1.3 +import QtQuick.Controls 2.4 + +ApplicationWindow { + id: window + width: 300 + height: 300 + visible: true + + ListView { + anchors.fill: parent + delegate: Text { + color: "red" + text: display + font.pointSize: 24 + font.bold: true + } + model: m + Component.onCompleted: console.log(m.get(5)) + } +} \ No newline at end of file