diff --git a/go.mod b/go.mod index 1fb3f88..5a841d5 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,7 @@ require ( ) require ( - github.com/go-gtk/gtk4 v0.5.0 + github.com/go-gtk/gtk4 v0.6.0 github.com/go-macos/virtualdisplay v0.3.0 ) diff --git a/go.sum b/go.sum index 310f0f9..7646de4 100644 --- a/go.sum +++ b/go.sum @@ -20,6 +20,8 @@ github.com/go-gfx/gfx v0.19.0 h1:nA2Eg8Aq2kytR5Hrm/eQ6618w4vT0+Ao8BMZiuYh84c= github.com/go-gfx/gfx v0.19.0/go.mod h1:VAK6hgCgkhT3j3ek2K7G8zDfkCPD3RCF45UbcRlCV8Q= github.com/go-gtk/gtk4 v0.5.0 h1:97/WDULZ3cVIYldSIPT9FV7GBkEf4cOMunF7nsIODNk= github.com/go-gtk/gtk4 v0.5.0/go.mod h1:jjaA29sFQTpy2D3N+FrnxMFZq07mG48ahgQoEEECpPA= +github.com/go-gtk/gtk4 v0.6.0 h1:piXUsvHo1FWJtgEz5ogowyvXlMskchAbiryeNJPKEmw= +github.com/go-gtk/gtk4 v0.6.0/go.mod h1:jjaA29sFQTpy2D3N+FrnxMFZq07mG48ahgQoEEECpPA= github.com/go-icons/iconoir v0.2.0 h1:I5AnDEIPgYfawTmwzemDRJjlYCUbFHuS8GJXrjADhXc= github.com/go-icons/iconoir v0.2.0/go.mod h1:Td/M2vy4xdVv4aOudAskV0nXR9Wv0MlhWvyRIzPBz5U= github.com/go-images/images v0.0.0-20260831115433-23d959d868e3 h1:SinmtXWpA4a30ZQ5A9QI9YrsYvytmLGeTRVbXqqEezw= diff --git a/internal/gtk/gtk_live_linux_test.go b/internal/gtk/gtk_live_linux_test.go index 64bb9dc..2176e8d 100644 --- a/internal/gtk/gtk_live_linux_test.go +++ b/internal/gtk/gtk_live_linux_test.go @@ -165,3 +165,30 @@ func TestGTKBackendMoreWidgets(t *testing.T) { t.Error("segmented: the \"two\" segment should be active") } } + +// TestGTKBackendList proves NativeList builds a GtkListBox whose rows are the +// Items and whose selection follows Number. +func TestGTKBackendList(t *testing.T) { + win, err := Open("gtk list test", 320, 240, nil, 1) + if err != nil { + t.Skipf("no GTK display: %v", err) + } + defer win.Close() + surf := toolkit.NewSurface(func() ([]byte, int, int) { return make([]byte, 320*240*4), 320, 240 }) + surf.Controls = func() []toolkit.NativeControl { + return []toolkit.NativeControl{{ + Kind: toolkit.NativeList, Key: "lst", Visible: true, + Rect: toolkit.Rect{X: 10, Y: 10, W: 200, H: 160}, + Items: []string{"red", "green", "blue"}, Number: 2, OnNumber: func(float64) {}, + }} + } + win.root = surf + win.frame() + lc := win.native["lst"] + if lc == nil { + t.Fatal("no list control created") + } + if got := lc.widget.SelectedRow(); got != 2 { + t.Errorf("selected row = %d, want 2 (blue)", got) + } +} diff --git a/internal/gtk/native_linux.go b/internal/gtk/native_linux.go index 754633e..3dc1e1f 100644 --- a/internal/gtk/native_linux.go +++ b/internal/gtk/native_linux.go @@ -162,6 +162,14 @@ func (w *Window) applySpec(lc *liveControl, spec toolkit.NativeControl) { lc.widget.SetColorHex(spec.Text) lc.lastText = spec.Text } + case toolkit.NativeList: + // Push the selected row when the app moved it. (Changing the row STRINGS + // after creation needs a new Key so the control is rebuilt — GtkListBox has + // no clear-all in the binding; the reconcile handles that path.) + if spec.Number != lc.lastNum { + lc.widget.SelectRow(int(spec.Number)) + lc.lastNum = spec.Number + } } w.fixed.Move(lc.widget, w.pt(spec.Rect.X), w.pt(spec.Rect.Y)) @@ -319,6 +327,26 @@ func (w *Window) makeControl(spec toolkit.NativeControl) *liveControl { lc.onText(lc.lastText) } }) + case toolkit.NativeList: + // A single-column selectable list (the GTK peer of cocoa's NSTableView). + // Items are the rows, Number the selected index, reported via OnNumber. + lc.items = spec.Items + lb := gtk4.ListBoxNew() + for _, it := range spec.Items { + lb.ListBoxAppendText(it) + } + if i := int(spec.Number); i >= 0 && i < len(spec.Items) { + lb.SelectRow(i) + } + lb.Connect("row-selected", func() { + if i := lb.SelectedRow(); i >= 0 { + lc.lastNum = float64(i) + if lc.onNumber != nil { + lc.onNumber(lc.lastNum) + } + } + }) + lc.widget = lb case toolkit.NativeSegmented: // GTK has no segmented control; compose one from linked toggle buttons in a // horizontal box (the go-gtk primitives). The selected segment's title is the