diff options
| -rw-r--r-- | gui/layout/split.go | 16 | ||||
| -rw-r--r-- | gui/text/concurrent_face.go (renamed from gui/widget/concurrent_face.go) | 2 | ||||
| -rw-r--r-- | gui/text/text.go (renamed from gui/widget/text.go) | 6 | ||||
| -rw-r--r-- | gui/widget/widget.go | 73 | ||||
| -rw-r--r-- | main.go | 84 | ||||
| -rw-r--r-- | ui.go | 17 |
6 files changed, 171 insertions, 27 deletions
diff --git a/gui/layout/split.go b/gui/layout/split.go index db04225..455d74b 100644 --- a/gui/layout/split.go +++ b/gui/layout/split.go @@ -1,6 +1,10 @@ package layout -import "fmt" +import ( + "fmt" + + "volute/gui/text" +) // SplitFunc represents a way to split a space among a number of elements. // The length of the returned slice must be equal to the number of elements. @@ -24,3 +28,13 @@ func EvenSplit(elements int, width int) []int { } return ret } + +func TextRowSplit(elements int, space int) []int { + bounds := make([]int, elements) + height := text.Size("1").Y + for i := 0; i < elements && space > 0; i++ { + bounds[i] = min(height, space) + space -= bounds[i] + } + return bounds +} diff --git a/gui/widget/concurrent_face.go b/gui/text/concurrent_face.go index 98db572..c865251 100644 --- a/gui/widget/concurrent_face.go +++ b/gui/text/concurrent_face.go @@ -1,4 +1,4 @@ -package widget +package text import ( "image" diff --git a/gui/widget/text.go b/gui/text/text.go index 1b40096..dfe60e1 100644 --- a/gui/widget/text.go +++ b/gui/text/text.go @@ -1,4 +1,4 @@ -package widget +package text import ( "log" @@ -38,12 +38,12 @@ func init() { face = &concurrentFace{sync.Mutex{}, fce} } -func TextSize(text string) image.Point { +func Size(text string) image.Point { bounds := textBounds([]byte(text), font.Drawer{Face: face}) return image.Point{bounds.Max.X - bounds.Min.X + 2*PAD, bounds.Max.Y - bounds.Min.Y + 2*PAD} } -func drawText(text []byte, dst draw.Image, r image.Rectangle, fg, bg color.Color) { +func Draw(text []byte, dst draw.Image, r image.Rectangle, fg, bg color.Color) { drawer := font.Drawer{ Src: &image.Uniform{fg}, Face: face, diff --git a/gui/widget/widget.go b/gui/widget/widget.go index 2e7c671..3fd9637 100644 --- a/gui/widget/widget.go +++ b/gui/widget/widget.go @@ -11,6 +11,8 @@ import ( "image/draw" "volute/gui" + "volute/gui/layout" + "volute/gui/text" "volute/gui/win" ) @@ -23,12 +25,71 @@ var ( interpolator = xdraw.ApproxBiLinear ) -func Label(text string, r image.Rectangle, env gui.Env, wg *sync.WaitGroup) { +type Node[T any] struct { + Label string + Value T + Children []Node[T] + + expanded bool +} + +func Tree[T any](trees []Node[T], r image.Rectangle, focus FocusSlave, mux *gui.Mux, wg *sync.WaitGroup) { + defer wg.Done() + + var nodes []string + for _, root := range trees { + nodes = append(nodes, flatten(root, 0)...) + } + + bounds := layout.Grid{ + Rows: populate(make([]int, len(nodes)), 1), + Background: color.Gray{255}, + Gap: 1, + Split: layout.EvenSplit, + SplitRows: layout.TextRowSplit, + Margin: 0, + Border: 0, + BorderColor: color.Gray{16}, + Flip: false, + }.Lay(r) + for i := range nodes { + wg.Add(1) + go Label(nodes[i], bounds[i], mux.MakeEnv(), wg) + } + + /* + globalFocus := focus; + localFocus := NewFocusMaster([]int{1, 1, 1}); + defer localFocus.Close() + */ + // TODO +} + +func flatten[T any](root Node[T], depth int) []string { + indent := string(populate(make([]byte, 2*depth), ' ')) + nodes := []string{indent + root.Label} + root.expanded = true // TODO: remove me + if root.expanded { + for _, c := range root.Children { + nodes = append(nodes, flatten(c, depth+1)...) + } + } + return nodes +} + +func populate[T any](arr []T, v T) []T { + for i := range arr { + arr[i] = v + } + return arr +} + +func Label(str string, r image.Rectangle, env gui.Env, wg *sync.WaitGroup) { defer wg.Done() defer close(env.Draw()) redraw := func(drw draw.Image) image.Rectangle { - drawText([]byte(text), drw, r, BLACK, WHITE) + text.Draw([]byte(str), drw, r, BLACK, WHITE) return r } @@ -93,12 +154,12 @@ Loop: } } -func inputDraw(text []byte, focused bool, r image.Rectangle) func(draw.Image) image.Rectangle { +func inputDraw(str []byte, focused bool, r image.Rectangle) func(draw.Image) image.Rectangle { return func(drw draw.Image) image.Rectangle { if focused { - drawText(text, drw, r, GREEN, FOCUS_COLOR) + text.Draw(str, drw, r, GREEN, FOCUS_COLOR) } else { - drawText(text, drw, r, GREEN, WHITE) + text.Draw(str, drw, r, GREEN, WHITE) } return r } @@ -128,7 +189,7 @@ Loop: func outputDraw(v float64, r image.Rectangle) func(draw.Image) image.Rectangle { return func(drw draw.Image) image.Rectangle { - drawText([]byte(fmt.Sprintf("%.3f", v)), drw, r, BLACK, WHITE) + text.Draw([]byte(fmt.Sprintf("%.3f", v)), drw, r, BLACK, WHITE) return r } } @@ -2,12 +2,16 @@ package main import ( "fmt" - "image" "os" "sync" + "image" + _ "image/jpeg" + _ "image/png" + "github.com/faiface/mainthread" "volute/gui" + "volute/gui/text" "volute/gui/widget" "volute/gui/win" ) @@ -28,7 +32,7 @@ func run() { wg := new(sync.WaitGroup) defer wg.Wait() - focus := widget.NewFocusMaster([]int{1, POINTS, POINTS, POINTS, POINTS}) + focus := widget.NewFocusMaster([]int{1, POINTS, POINTS, POINTS, POINTS, 1}) defer focus.Close() displacementChan := make(chan uint) @@ -63,15 +67,89 @@ func run() { wg, ) + compressors := []widget.Node[string]{ + { + Label: "BorgWarner", + Value: "bw", + Children: []widget.Node[string]{ + { + Label: "EFR", + Value: "efr", + Children: []widget.Node[string]{ + { + Label: "6258", + Value: "6258", + Children: nil, + }, { + Label: "7064", + Value: "7064", + Children: nil, + }, + }, + }, { + Label: "K", + Value: "k", + Children: []widget.Node[string]{ + { + Label: "03", + Value: "03", + Children: nil, + }, { + Label: "04", + Value: "04", + Children: nil, + }, + }, + }, + }, + }, { + Label: "Garrett", + Value: "garrett", + Children: []widget.Node[string]{ + { + Label: "G", + Value: "g", + Children: []widget.Node[string]{ + { + Label: "25-550", + Value: "25-550", + Children: nil, + }, + }, + }, + }, + }, + } + wg.Add(1) + go widget.Tree( + compressors, + image.Rect(text.PAD, 125, 250, HEIGHT-text.PAD), + focus.Slave(5, 0), + mux, + wg, + ) + imChan := make(chan image.Image) defer close(imChan) wg.Add(1) go widget.Image( imChan, - image.Rect(0, 200, 100, 300), + image.Rect(250+text.PAD, 125, WIDTH-text.PAD, HEIGHT-text.PAD), mux.MakeEnv(), wg, ) + f, err := os.Open("compressor_maps/borgwarner/efr/8374.jpg") + if err != nil { + fmt.Fprintln(os.Stderr, err) + return + } + im, _, err := image.Decode(f) + if err != nil { + fmt.Fprintln(os.Stderr, err) + return + } + f.Close() + imChan <- im for i := 0; i < POINTS; i++ { wg.Add(1) @@ -7,6 +7,7 @@ import ( "volute/gui" "volute/gui/layout" + "volute/gui/text" "volute/gui/widget" ) @@ -23,7 +24,7 @@ func spawnWidgets( Background: color.Gray{255}, Gap: 1, Split: split, - SplitRows: splitRows, + SplitRows: layout.TextRowSplit, Margin: 0, Border: 0, BorderColor: color.Gray{16}, @@ -96,8 +97,8 @@ func spawnWidgets( func split(elements int, space int) []int { bounds := make([]int, elements) widths := []int{ - widget.TextSize(WIDEST_LABEL).X, - widget.TextSize("123456").X, + text.Size(WIDEST_LABEL).X, + text.Size("123456").X, } for i := 0; i < elements && space > 0; i++ { bounds[i] = min(widths[min(i, len(widths)-1)], space) @@ -105,13 +106,3 @@ func split(elements int, space int) []int { } return bounds } - -func splitRows(elements int, space int) []int { - bounds := make([]int, elements) - height := widget.TextSize("1").Y - for i := 0; i < elements && space > 0; i++ { - bounds[i] = min(height, space) - space -= bounds[i] - } - return bounds -} |