Files
ZacharyZhang-NY 51af14709d Add code components 366-385; ScrollArea Follow, editor sync
A terminal with history, a Go-colored code block, a log viewer that
follows, a hex viewer, a memory map, a JSON tree, a line diff, a GAME
OVER crash screen, an FPS meter, profiler bars, breakpoints,
variables, command history, a shortcut list, a debug grid, a pixel
editor that exports string art, a palette editor, a sprite sheet, a
glyph map and a 6502 register view.

layout.ScrollArea takes Follow, and ChatLog now uses it. data.TreeNode
takes a Value drawn in its Slot. pixel gains Has, and Sprite.Art
writes MustSprite art. pixel.Editor takes the caller's value at each
input event, so a value changed between frames is never typed onto.
2026-10-12 07:18:30 -04:00

78 lines
1.9 KiB
Go

package code
import (
"encoding/json"
"fmt"
"io"
"strconv"
"strings"
"github.com/egoist/mygo/ui"
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/data"
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/pixel"
)
type JSONTreeOptions struct {
// Width is in dots; zero means 200.
Width int
Disabled bool
}
type JSONTreeResult = data.TreeResult
// JSONTree shows JSON as a tree; values colored by type.
func JSONTree(c *ui.Context, text, doc string, selected *string, o JSONTreeOptions) JSONTreeResult {
dec := json.NewDecoder(strings.NewReader(doc))
dec.UseNumber()
root := node(dec, "")
if _, err := dec.Token(); err != io.EOF {
panic(fmt.Sprintf("code: JSONTree: after the value, %v", err))
}
// An empty container or lone value is its own row.
roots := root.Children
if len(roots) == 0 {
roots = []data.TreeNode{root}
}
return data.Tree(c, text, roots, selected, data.TreeOptions{Width: pixel.Or("JSONTree Width", o.Width, 200), Disabled: o.Disabled})
}
// node reads the next value under label; bad JSON panics.
func node(dec *json.Decoder, label string) data.TreeNode {
tok, err := dec.Token()
if err != nil {
panic(fmt.Sprintf("code: JSONTree: %v", err))
}
n := data.TreeNode{Label: label, Slot: pixel.Faint}
switch v := tok.(type) {
case json.Delim:
end := "]"
if v == '{' {
end = "}"
}
for i := 0; dec.More(); i++ {
key := strconv.Itoa(i)
if v == '{' {
t, _ := dec.Token()
key = t.(string)
}
n.Children = append(n.Children, node(dec, key))
}
dec.Token()
n.Value = fmt.Sprintf("%c%d%s", v, len(n.Children), end)
// An empty object or array still opens nothing.
if n.Children == nil {
n.Children = []data.TreeNode{}
}
case string:
n.Value, n.Slot = strconv.QuoteToGraphic(v), pixel.Good
case json.Number:
n.Value, n.Slot = v.String(), pixel.Warn
case bool:
n.Value, n.Slot = strconv.FormatBool(v), pixel.Info
case nil:
n.Value = "null"
}
return n
}