From 76e803ff6c35c4c03cfeab7d31f25fe35f086ade Mon Sep 17 00:00:00 2001 From: Tom5521 Date: Mon, 22 Jul 2024 01:31:52 -0400 Subject: [PATCH] feat(pkg/gtools): Add FetchObjects --- go.mod | 2 +- internal/walk/main.go | 41 ++++++++++++++++++++++++++++++++++++ internal/walk/walk_test.go | 42 +++++++++++++++++++++++++++++++++++++ pkg/gtools/examples_test.go | 27 ++++++++++++++++++++++++ pkg/gtools/fetch.go | 26 +++++++++++++++++++++++ pkg/gtools/interfaces.go | 18 +++++++++++----- pkg/gtools/main.go | 6 ------ 7 files changed, 150 insertions(+), 12 deletions(-) create mode 100644 internal/walk/main.go create mode 100644 internal/walk/walk_test.go create mode 100644 pkg/gtools/examples_test.go create mode 100644 pkg/gtools/fetch.go diff --git a/go.mod b/go.mod index 53d64f0..569b2bf 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/Tom5521/gtk4tools -go 1.22.4 +go 1.22 require github.com/diamondburned/gotk4/pkg v0.3.0 diff --git a/internal/walk/main.go b/internal/walk/main.go new file mode 100644 index 0000000..b3655d1 --- /dev/null +++ b/internal/walk/main.go @@ -0,0 +1,41 @@ +package walk + +import "reflect" + +type Iteration func(reflect.Value, reflect.StructField) bool + +func Into(t any, action Iteration) { + value := reflect.ValueOf(t) + if v := value.Kind(); v == reflect.Pointer || v == reflect.Interface { + value = value.Elem() + } + + rtype := reflect.TypeOf(t) + if rtype.Kind() == reflect.Pointer { + rtype = rtype.Elem() + } + Struct(value, rtype, action) +} + +func Struct( + value reflect.Value, + str reflect.Type, + action Iteration, +) { + for i := range str.NumField() { + v := value.Field(i) + t := str.Field(i) + + if !t.IsExported() { + continue + } + + if t.Type.Kind() == reflect.Struct { + Struct(v, t.Type, action) + continue + } + if !action(v, t) { + break + } + } +} diff --git a/internal/walk/walk_test.go b/internal/walk/walk_test.go new file mode 100644 index 0000000..6f77dc5 --- /dev/null +++ b/internal/walk/walk_test.go @@ -0,0 +1,42 @@ +package walk_test + +import ( + "fmt" + "reflect" + "testing" + + "github.com/Tom5521/gtk4tools/internal/walk" +) + +type A struct { + F1 string + F2 bool + F3 int + B struct { + F1 string + C struct { + F1 string + } + } +} + +func TestUnmarshall(_ *testing.T) { + str := A{ + F1: "Meow in A", + F2: true, + F3: 20, + } + str.B.F1 = "Meow in B" + str.B.C.F1 = "Meow in C" + + walk.Into( + &str, + func(v reflect.Value, _ reflect.StructField) bool { + if v.String() == "Meow in C" { + v.Set(reflect.ValueOf(v.String() + " | Edited")) + } + fmt.Println(v) + return true + }, + ) +} diff --git a/pkg/gtools/examples_test.go b/pkg/gtools/examples_test.go new file mode 100644 index 0000000..d82e3db --- /dev/null +++ b/pkg/gtools/examples_test.go @@ -0,0 +1,27 @@ +package gtools_test + +import ( + "fmt" + + "github.com/Tom5521/gtk4tools/pkg/gtools" + "github.com/diamondburned/gotk4/pkg/gtk/v4" +) + +func ExampleFetchObjects() { + const xml = ` + + + + Hello World + +` + + type ui struct { + Window *gtk.Window `gtk:"window"` + } + + w := new(ui) + gtools.FetchObjects(w, xml) + + fmt.Println(w.Window.Title()) // Hello World +} diff --git a/pkg/gtools/fetch.go b/pkg/gtools/fetch.go new file mode 100644 index 0000000..af85631 --- /dev/null +++ b/pkg/gtools/fetch.go @@ -0,0 +1,26 @@ +package gtools + +import ( + "reflect" + + "github.com/Tom5521/gtk4tools/internal/walk" + "github.com/diamondburned/gotk4/pkg/gtk/v4" +) + +// What this function does is to obtain the tags of the provided structure +// and with the content of the tag call gtk.Builder.GetObject +// and assign it to its corresponding field, +// if the field has no tag it will be skipped and if it is another sub struct +// it will continue until it reaches the end. +func FetchObjects(str any, builder string) { + b := gtk.NewBuilderFromString(builder) + walk.Into(str, func(v reflect.Value, sf reflect.StructField) bool { + tag, ok := sf.Tag.Lookup("gtk") + if !ok { + return true + } + obj := b.GetObject(tag).Cast() + v.Set(reflect.ValueOf(obj)) + return true + }) +} diff --git a/pkg/gtools/interfaces.go b/pkg/gtools/interfaces.go index be49699..cbfadb8 100644 --- a/pkg/gtools/interfaces.go +++ b/pkg/gtools/interfaces.go @@ -5,6 +5,19 @@ import ( "github.com/diamondburned/gotk4/pkg/gtk/v4" ) +var _ Appender = (*gtk.Box)(nil) + +// It is simply an interface of a glib.Objector that has the append method. +type Appender interface { + glib.Objector + Append(gtk.Widgetter) +} + +var ( + _ ListItem = (*gtk.ListItem)(nil) + _ ListItem = (*gtk.ColumnViewCell)(nil) +) + type ListItem interface { glib.Objector @@ -25,8 +38,3 @@ type ListItem interface { SetFocusable(focusable bool) SetSelectable(selectable bool) } - -var ( - _ ListItem = (*gtk.ListItem)(nil) - _ ListItem = (*gtk.ColumnViewCell)(nil) -) diff --git a/pkg/gtools/main.go b/pkg/gtools/main.go index 84506c9..b0abdb5 100644 --- a/pkg/gtools/main.go +++ b/pkg/gtools/main.go @@ -13,12 +13,6 @@ func ToWidgetter[T gtk.Widgetter](items []T) []gtk.Widgetter { return widgets } -// It is simply an interface of a gtk.widgetter that has the append function. -type Appender interface { - gtk.Widgetter - Append(gtk.Widgetter) -} - func Append(parent Appender, widgets ...gtk.Widgetter) { for _, w := range widgets { parent.Append(w)