Things are working pretty well now

This commit is contained in:
2026-02-02 14:11:01 -06:00
parent f3c63fe95b
commit 486c07f2d4
9 changed files with 442 additions and 136 deletions

View File

@@ -43,7 +43,6 @@ type ScreenHome struct {
style tcell.Style style tcell.Style
menuLayout *w.TopMenuLayout menuLayout *w.TopMenuLayout
menu *w.Menu
openPdsEntry *w.Field openPdsEntry *w.Field
@@ -56,11 +55,6 @@ type ScreenHome struct {
status *wd.StatusBar status *wd.StatusBar
stPathBlock *wd.StatusBlock stPathBlock *wd.StatusBlock
pdsListingTypes []models.EntryType
pdsNSIDs []syntax.NSID
expandedEntries map[string]interface{}
recordIdsToNSIDs map[string]syntax.NSID
cli *w.Cli cli *w.Cli
doOpen bool doOpen bool
@@ -74,9 +68,6 @@ func (s *ScreenHome) Init(a *App) {
s.a, s.r = a, a.repo s.a, s.r = a, a.repo
s.style = a.style s.style = a.style
s.expandedEntries = make(map[string]interface{})
s.recordIdsToNSIDs = make(map[string]syntax.NSID)
s.openPdsEntry = w.NewField("home.openpds.field", s.style) s.openPdsEntry = w.NewField("home.openpds.field", s.style)
s.openPdsEntry.SetLabel("ID") s.openPdsEntry.SetLabel("ID")
s.openPdsEntry.SetActive(true) s.openPdsEntry.SetActive(true)
@@ -168,11 +159,14 @@ func (s *ScreenHome) HandleKey(ev *tcell.EventKey) bool {
s.toggleCli() s.toggleCli()
return true return true
} }
if s.doOpen { m := s.menuLayout.Menu()
if ev.Key() == tcell.KeyEscape { if ev.Key() == tcell.KeyEscape {
s.doOpen = false return s.menuLayout.HandleKey(ev)
return true } else if m.Active() {
return s.menuLayout.HandleKey(ev)
} }
if s.doOpen {
if ev.Key() == tcell.KeyCtrlU { if ev.Key() == tcell.KeyCtrlU {
s.openPdsEntry.SetValue("") s.openPdsEntry.SetValue("")
return true return true
@@ -183,6 +177,7 @@ func (s *ScreenHome) HandleKey(ev *tcell.EventKey) bool {
} }
return s.openPdsEntry.HandleKey(ev) return s.openPdsEntry.HandleKey(ev)
} }
return s.menuLayout.HandleKey(ev) return s.menuLayout.HandleKey(ev)
} }
func (s *ScreenHome) HandleTime(ev *tcell.EventTime) { func (s *ScreenHome) HandleTime(ev *tcell.EventTime) {
@@ -195,8 +190,9 @@ func (s *ScreenHome) Draw() {
s.menuLayout.SetStyle(s.style.Foreground(tcell.ColorGray)) s.menuLayout.SetStyle(s.style.Foreground(tcell.ColorGray))
} }
s.menuLayout.SetStyle(s.style) s.menuLayout.SetStyle(s.style)
m := s.menuLayout.Menu()
s.a.DrawWidget(s.menuLayout) s.a.DrawWidget(s.menuLayout)
if s.doOpen { if s.doOpen && !m.Active() {
s.a.DrawWidget(s.openPdsEntry) s.a.DrawWidget(s.openPdsEntry)
} }
@@ -214,10 +210,16 @@ func (s *ScreenHome) Log(t string, a ...any) {
func (s *ScreenHome) initMenu() { func (s *ScreenHome) initMenu() {
s.menuLayout.SetActive(true) s.menuLayout.SetActive(true)
var vimText = "Enable Vim Mode" wrk := "[ ]"
if viper.GetBool(data.KeyVimMode) { if viper.GetBool(data.KeyVimMode) {
vimText = "Disable Vim Mode" wrk = "[X]"
} }
vimText := fmt.Sprintf("%s Vim Mode", wrk)
wrk = "[ ]"
if viper.GetBool(data.KeyRecNmInfer) {
wrk = "[X]"
}
inferRecNm := fmt.Sprintf("%s Infer Record Names", wrk)
s.menuLayout.AddMenuItems( s.menuLayout.AddMenuItems(
s.menuLayout.CreateMenuItem("file", "File", nil, 'f', s.menuLayout.CreateMenuItem("file", "File", nil, 'f',
s.menuLayout.CreateMenuItem("file.openpds", "Open PDS", func() bool { s.menuLayout.CreateMenuItem("file.openpds", "Open PDS", func() bool {
@@ -246,6 +248,13 @@ func (s *ScreenHome) initMenu() {
s.update() s.update()
return true return true
}, 'v'), }, 'v'),
s.menuLayout.CreateMenuItem("settings.inferrecnm", inferRecNm, func() bool {
s.menuLayout.ToggleMenu()
viper.Set(data.KeyRecNmInfer, !viper.GetBool(data.KeyRecNmInfer))
viper.WriteConfig()
s.update()
return true
}, 'r'),
), ),
) )
} }
@@ -259,11 +268,17 @@ func (s *ScreenHome) update() {
s.pdsListing.SetVimMode(viper.GetBool(data.KeyVimMode)) s.pdsListing.SetVimMode(viper.GetBool(data.KeyVimMode))
s.jsonContent.SetVimMode(viper.GetBool(data.KeyVimMode)) s.jsonContent.SetVimMode(viper.GetBool(data.KeyVimMode))
vimMI := s.menuLayout.FindItem("settings.vimmode") vimMI := s.menuLayout.FindItem("settings.vimmode")
var vimText = "Enable Vim Mode" wrk := "[ ]"
if viper.GetBool(data.KeyVimMode) { if viper.GetBool(data.KeyVimMode) {
vimText = "Disable Vim Mode" wrk = "[X]"
} }
vimMI.SetLabel(vimText) vimMI.SetLabel(fmt.Sprintf("%s Vim Mode", wrk))
recNmInf := s.menuLayout.FindItem("settings.inferrecnm")
wrk = "[ ]"
if viper.GetBool(data.KeyRecNmInfer) {
wrk = "[X]"
}
recNmInf.SetLabel(fmt.Sprintf("%s Infer Record Names", wrk))
miReload := s.menuLayout.FindItem("file.reloadpds") miReload := s.menuLayout.FindItem("file.reloadpds")
if s.activePds == nil { if s.activePds == nil {
@@ -323,7 +338,6 @@ func (s *ScreenHome) cliGetPds(args ...string) bool {
s.doOpen = false s.doOpen = false
s.cli.Log("Retrieved: %s (%s)", pds.AtId, pds.Did) s.cli.Log("Retrieved: %s (%s)", pds.AtId, pds.Did)
s.activePds = pds s.activePds = pds
s.expandedEntries = make(map[string]interface{})
s.updatePdsListing() s.updatePdsListing()
n, err := s.pdsListing.GetActiveNode() n, err := s.pdsListing.GetActiveNode()
if err == nil && n != nil { if err == nil && n != nil {
@@ -346,7 +360,21 @@ func (s *ScreenHome) updatePdsListing() {
nsid := s.activePds.NSIDs[i] nsid := s.activePds.NSIDs[i]
rIds := s.activePds.GetRecordIdsFor(nsid) rIds := s.activePds.GetRecordIdsFor(nsid)
for j := range rIds { for j := range rIds {
c := wd.NewTreeNode(fmt.Sprintf("%s (%s)", "record", rIds[j]), rIds[j]) label := rIds[j]
if viper.GetBool(data.KeyRecNmInfer) {
if rec, ok := s.activePds.Records[rIds[j]]; ok {
for k := range rec {
if k == "name" || k == "title" || k == "label" {
if f, ok := rec[k].(string); ok {
label = fmt.Sprintf("%s (%s)", f, rIds[j])
break
}
}
}
}
}
c := wd.NewTreeNode(label, rIds[j])
t.AddChild(c) t.AddChild(c)
} }
s.pdsListing.Add(t) s.pdsListing.Add(t)
@@ -384,63 +412,18 @@ func (s *ScreenHome) updateStatusPathBlock(tn *wd.TreeNode) bool {
func (s *ScreenHome) updateJsonView(tn *wd.TreeNode) bool { func (s *ScreenHome) updateJsonView(tn *wd.TreeNode) bool {
// TODO: Update JSON View // TODO: Update JSON View
return true if tn.Depth() == 0 {
} nsid, err := syntax.ParseNSID(tn.Value())
/*
func (s *ScreenHome) o_selectPdsListingEntry(idx int, nm string) bool {
if !s.o_updateJsonView(idx, nm) {
return false
}
switch s.pdsListingTypes[idx] {
case models.TypeNSID:
// Expand the NSID
if _, ok := s.expandedEntries[nm]; ok {
delete(s.expandedEntries, nm)
} else {
s.expandedEntries[nm] = new(interface{})
}
s.updatePdsListing()
return true
case models.TypeRecord:
// If signed in and we can edit this, activate jsonContent
s.columns.ActivateWidget(s.jsonContent)
return true
}
return false
}
func (s *ScreenHome) o_updateJsonView(idx int, nm string) bool {
if len(s.pdsListingTypes) < idx {
s.Log("error finding pds listing type (idx: %d >= list length: %d", idx, len(s.pdsListingTypes))
return false
}
// Update the jsonContent with the list of records
switch s.pdsListingTypes[idx] {
case models.TypeNSID:
nsid, err := syntax.ParseNSID(nm)
if err != nil { if err != nil {
s.Log("error parsing NSID from %s: %w", nm, err) s.Log("error parsing NSID from %s: %w", tn.Value(), err)
return false return false
} }
recordIds := s.activePds.GetRecordIdsFor(nsid) recordIds := s.activePds.GetRecordIdsFor(nsid)
s.jsonContent.SetValue(recordIds) s.jsonContent.SetValue(recordIds)
return true return true
} else {
case models.TypeRecord: s.jsonContent.SetValue(s.activePds.Records[tn.Value()])
var nsid syntax.NSID
var ok bool
nm, _ := strings.CutPrefix(nm, "• ")
if nsid, ok = s.recordIdsToNSIDs[nm]; !ok {
s.Log("error finding NSID for record %s", nm)
} }
rId := fmt.Sprintf("%s/%s", nsid.String(), nm)
s.jsonContent.SetValue(s.activePds.Records[rId])
return true return true
}
return false
} }
*/

View File

@@ -27,4 +27,5 @@ const (
KeyDebug = "debug" KeyDebug = "debug"
KeyDataDir = "data" KeyDataDir = "data"
KeyVimMode = "vimMode" KeyVimMode = "vimMode"
KeyRecNmInfer = "inferRecNm"
) )

View File

@@ -138,7 +138,7 @@ func (p *Pds) unpack() error {
if err != nil { if err != nil {
return fmt.Errorf("error unmarshalling cbor (%s/%s): %w", sCol, sRKey, err) return fmt.Errorf("error unmarshalling cbor (%s/%s): %w", sCol, sRKey, err)
} }
p.Records[string(k)] = rec p.Records[sRKey] = rec
if !slices.Contains(p.NSIDs, col) { if !slices.Contains(p.NSIDs, col) {
p.NSIDs = append(p.NSIDs, col) p.NSIDs = append(p.NSIDs, col)

2
go.mod
View File

@@ -3,7 +3,7 @@ module git.bullercodeworks.com/brian/expds
go 1.25.1 go 1.25.1
require ( require (
git.bullercodeworks.com/brian/tcell-widgets v0.2.3 git.bullercodeworks.com/brian/tcell-widgets v0.3.2
github.com/bluesky-social/indigo v0.0.0-20260120225912-12d69fa4d209 github.com/bluesky-social/indigo v0.0.0-20260120225912-12d69fa4d209
github.com/gdamore/tcell v1.4.1 github.com/gdamore/tcell v1.4.1
github.com/ipfs/go-cid v0.4.1 github.com/ipfs/go-cid v0.4.1

6
go.sum
View File

@@ -10,6 +10,12 @@ git.bullercodeworks.com/brian/tcell-widgets v0.2.2 h1:DcZYyGMv5U/TGSAj8CpxfrYufP
git.bullercodeworks.com/brian/tcell-widgets v0.2.2/go.mod h1:3TlKbuGjY8nrKL5Qcp28h+KnEsXBl3iCwACTy79bdPg= git.bullercodeworks.com/brian/tcell-widgets v0.2.2/go.mod h1:3TlKbuGjY8nrKL5Qcp28h+KnEsXBl3iCwACTy79bdPg=
git.bullercodeworks.com/brian/tcell-widgets v0.2.3 h1:/57GmrSDru27lhkm13q/sh2xLryayvQcp7MrSpixIKo= git.bullercodeworks.com/brian/tcell-widgets v0.2.3 h1:/57GmrSDru27lhkm13q/sh2xLryayvQcp7MrSpixIKo=
git.bullercodeworks.com/brian/tcell-widgets v0.2.3/go.mod h1:3TlKbuGjY8nrKL5Qcp28h+KnEsXBl3iCwACTy79bdPg= git.bullercodeworks.com/brian/tcell-widgets v0.2.3/go.mod h1:3TlKbuGjY8nrKL5Qcp28h+KnEsXBl3iCwACTy79bdPg=
git.bullercodeworks.com/brian/tcell-widgets v0.3.0 h1:hvBaFvwgP29PtIushnrI68WIBiPqr3OExpJw65Vuq38=
git.bullercodeworks.com/brian/tcell-widgets v0.3.0/go.mod h1:3TlKbuGjY8nrKL5Qcp28h+KnEsXBl3iCwACTy79bdPg=
git.bullercodeworks.com/brian/tcell-widgets v0.3.1 h1:ehHpqSQTQpufVvyvQK5fHhUTd/f+LsrOVzKUJfulTEw=
git.bullercodeworks.com/brian/tcell-widgets v0.3.1/go.mod h1:3TlKbuGjY8nrKL5Qcp28h+KnEsXBl3iCwACTy79bdPg=
git.bullercodeworks.com/brian/tcell-widgets v0.3.2 h1:N2WdJmMhbQKXFaB2inbxtK9pjaj/WCY/O8s15uCJtOQ=
git.bullercodeworks.com/brian/tcell-widgets v0.3.2/go.mod h1:3TlKbuGjY8nrKL5Qcp28h+KnEsXBl3iCwACTy79bdPg=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=

271
widgets/field_content.go Normal file
View File

@@ -0,0 +1,271 @@
/*
Copyright © Brian Buller <brian@bullercodeworks.com>
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 widgets
import (
"encoding/json"
"fmt"
"strings"
wd "git.bullercodeworks.com/brian/tcell-widgets"
wh "git.bullercodeworks.com/brian/tcell-widgets/helpers"
"github.com/gdamore/tcell"
)
type FieldContent struct {
id string
valueString string
contents []string
style tcell.Style
x, y int
w, h int
border []rune
visible bool
active bool
keyMap *wd.KeyMap
editable bool
value any
activeNode []string
vimMode bool
cursor int
}
var _ wd.Widget = (*FieldContent)(nil)
func NewFieldContent(id string, style tcell.Style) *FieldContent {
ret := &FieldContent{}
ret.Init(id, style)
return ret
}
func (w *FieldContent) Init(id string, style tcell.Style) {
w.id = id
w.style = style
w.visible = true
w.keyMap = wd.NewKeyMap(
wd.NewKey(wd.BuildEK(tcell.KeyUp), func(_ *tcell.EventKey) bool { return w.MoveUp() }),
wd.NewKey(wd.BuildEK(tcell.KeyDown), func(_ *tcell.EventKey) bool { return w.MoveDown() }),
wd.NewKey(wd.BuildEK(tcell.KeyEnter), func(ev *tcell.EventKey) bool { return false }),
wd.NewKey(wd.BuildEK(tcell.KeyPgDn), func(_ *tcell.EventKey) bool { return w.PageDn() }),
wd.NewKey(wd.BuildEK(tcell.KeyPgUp), func(_ *tcell.EventKey) bool { return w.PageUp() }),
wd.NewKey(wd.BuildEKr('j'), func(ev *tcell.EventKey) bool {
if !w.vimMode {
return false
}
return w.MoveDown()
}),
wd.NewKey(wd.BuildEKr('k'), func(ev *tcell.EventKey) bool {
if !w.vimMode {
return false
}
return w.MoveUp()
}),
wd.NewKey(wd.BuildEKr('b'), func(ev *tcell.EventKey) bool {
if !w.vimMode {
return false
}
if ev.Modifiers()&tcell.ModCtrl != 0 {
return w.PageUp()
}
return false
}),
wd.NewKey(wd.BuildEKr('f'), func(ev *tcell.EventKey) bool {
if !w.vimMode {
return false
}
if ev.Modifiers()&tcell.ModCtrl != 0 {
return w.PageDn()
}
return false
}),
)
}
func (w *FieldContent) Id() string { return w.id }
func (w *FieldContent) HandleResize(ev *tcell.EventResize) { w.w, w.h = ev.Size() }
func (w *FieldContent) GetKeyMap() *wd.KeyMap { return w.keyMap }
func (w *FieldContent) SetKeyMap(km *wd.KeyMap) { w.keyMap = km }
func (w *FieldContent) HandleKey(ev *tcell.EventKey) bool {
return w.keyMap.Handle(ev)
}
func (w *FieldContent) HandleTime(ev *tcell.EventTime) {}
func (w *FieldContent) Draw(screen tcell.Screen) {
if !w.visible {
return
}
x, y := w.x, w.y
brdSz := 0
if len(w.border) > 0 {
brdSz = 2
wh.BorderFilled(x, y, x+w.w, y+w.h, w.border, w.style, screen)
}
x, y = x+1, y+1
h := w.h - brdSz
ln := len(w.contents)
st, ed := 0, ln-1
if ln == 0 {
return
}
if ln > w.h-2 {
mid := h / 2
if w.cursor < mid {
// contents need to start at 0
ed = h + 1
} else if w.cursor > ln-mid {
// contents need to begin at ln-h
st = ln - h + 1
} else {
st = w.cursor - mid
ed = st + h + 1
}
}
// ed cannot be higher than ln-1
if st < 0 {
st = 0
}
if ed > ln-1 {
ed = ln - 1
}
for i := st; i <= ed; i++ {
st := w.style
dim := true
if i == w.cursor {
dim = false
} else {
st = st.Foreground(tcell.ColorGreen)
}
txt := w.contents[i]
if len(txt) > w.w-brdSz && w.w-brdSz >= 0 {
txt = txt[:(w.w - brdSz)]
}
wh.DrawText(w.x, y, txt, st.Dim(dim).Bold(!dim), screen)
y++
}
}
func (w *FieldContent) SetStyle(s tcell.Style) { w.style = s }
func (w *FieldContent) Active() bool { return w.active }
func (w *FieldContent) SetActive(a bool) bool {
w.active = a
return w.active
}
func (w *FieldContent) Visible() bool { return w.visible }
func (w *FieldContent) SetVisible(a bool) { w.visible = a }
func (w *FieldContent) SetX(x int) { w.x = x }
func (w *FieldContent) SetY(y int) { w.y = y }
func (w *FieldContent) GetX() int { return w.x }
func (w *FieldContent) GetY() int { return w.y }
func (w *FieldContent) GetPos() wd.Coord { return wd.Coord{X: w.x, Y: w.y} }
func (w *FieldContent) SetPos(c wd.Coord) { w.x, w.y = c.X, c.Y }
func (w *FieldContent) SetW(x int) { w.w = x }
func (w *FieldContent) SetH(y int) { w.h = y }
func (w *FieldContent) GetW() int { return w.w }
func (w *FieldContent) GetH() int { return w.h }
func (w *FieldContent) WantW() int { return wh.Longest(w.contents) }
func (w *FieldContent) WantH() int { return len(w.contents) }
func (w *FieldContent) SetSize(c wd.Coord) { w.w, w.h = c.X, c.Y }
func (w *FieldContent) MinW() int { return wh.Longest(w.contents) }
func (w *FieldContent) MinH() int { return len(w.contents) }
func (w *FieldContent) SetValue(v any) error {
w.value = v
// Go ahead and try to build the json for v
bts, err := json.MarshalIndent(v, "", " ")
if err != nil {
return fmt.Errorf("error unmarshalling value: %w", err)
}
w.valueString = string(bts)
w.contents = strings.Split(w.valueString, "\n")
return nil
}
func (w *FieldContent) SetBorder(brd []rune) {
if len(brd) == 0 {
w.border = wh.BRD_SIMPLE
} else {
w.border = wh.ValidateBorder(brd)
}
}
func (w *FieldContent) SetEditable(v bool) { w.editable = v }
/*
func (w *FieldContent) SetFieldContent(txt string) {
w.text = txt
if strings.Contains(w.text, "\n") {
w.contents = strings.Split(w.text, "\n")
} else {
w.contents = []string{w.text}
}
}
func (w *FieldContent) GetFieldContent() string { return w.text }
func (w *FieldContent) GetContents() []string { return w.contents }
*/
func (w *FieldContent) SetVimMode(b bool) { w.vimMode = b }
func (w *FieldContent) MoveUp() bool {
w.cursor--
if w.cursor < 0 {
w.cursor = 0
}
return true
}
func (w *FieldContent) MoveDown() bool {
w.cursor++
if w.cursor >= len(w.contents) {
w.cursor = len(w.contents) - 1
}
return true
}
func (w *FieldContent) PageUp() bool {
w.cursor -= w.h
if w.cursor < 0 {
w.cursor = 0
}
return true
}
func (w *FieldContent) PageDn() bool {
w.cursor += w.h
if w.cursor > len(w.contents)-1 {
w.cursor = len(w.contents) - 1
}
return true
}
func (w *FieldContent) GetSelectedValue() string {
if w.cursor >= len(w.contents) {
w.cursor = len(w.contents) - 1
}
var ret string
ret = w.contents[w.cursor]
ret = ret[strings.Index(ret, "\": \"")+3:]
ret = ret[1 : len(ret)-1]
return ret
}

View File

@@ -43,7 +43,6 @@ type JsonContent struct {
border []rune border []rune
visible bool visible bool
active bool active bool
focusable bool
keyMap *wd.KeyMap keyMap *wd.KeyMap
editable bool editable bool
@@ -67,7 +66,6 @@ func (w *JsonContent) Init(id string, style tcell.Style) {
w.id = id w.id = id
w.style = style w.style = style
w.visible = true w.visible = true
w.focusable = false
w.keyMap = wd.NewKeyMap( w.keyMap = wd.NewKeyMap(
wd.NewKey(wd.BuildEK(tcell.KeyUp), func(_ *tcell.EventKey) bool { return w.MoveUp() }), wd.NewKey(wd.BuildEK(tcell.KeyUp), func(_ *tcell.EventKey) bool { return w.MoveUp() }),
wd.NewKey(wd.BuildEK(tcell.KeyDown), func(_ *tcell.EventKey) bool { return w.MoveDown() }), wd.NewKey(wd.BuildEK(tcell.KeyDown), func(_ *tcell.EventKey) bool { return w.MoveDown() }),
@@ -174,7 +172,10 @@ func (w *JsonContent) Draw(screen tcell.Screen) {
func (w *JsonContent) SetStyle(s tcell.Style) { w.style = s } func (w *JsonContent) SetStyle(s tcell.Style) { w.style = s }
func (w *JsonContent) Active() bool { return w.active } func (w *JsonContent) Active() bool { return w.active }
func (w *JsonContent) SetActive(a bool) { w.active = a } func (w *JsonContent) SetActive(a bool) bool {
w.active = a
return w.active
}
func (w *JsonContent) Visible() bool { return w.visible } func (w *JsonContent) Visible() bool { return w.visible }
func (w *JsonContent) SetVisible(a bool) { w.visible = a } func (w *JsonContent) SetVisible(a bool) { w.visible = a }
func (w *JsonContent) SetX(x int) { w.x = x } func (w *JsonContent) SetX(x int) { w.x = x }
@@ -190,8 +191,6 @@ func (w *JsonContent) GetH() int { return w.h }
func (w *JsonContent) WantW() int { return wh.Longest(w.contents) } func (w *JsonContent) WantW() int { return wh.Longest(w.contents) }
func (w *JsonContent) WantH() int { return len(w.contents) } func (w *JsonContent) WantH() int { return len(w.contents) }
func (w *JsonContent) SetSize(c wd.Coord) { w.w, w.h = c.X, c.Y } func (w *JsonContent) SetSize(c wd.Coord) { w.w, w.h = c.X, c.Y }
func (w *JsonContent) Focusable() bool { return w.focusable }
func (w *JsonContent) SetFocusable(b bool) { w.focusable = b }
func (w *JsonContent) MinW() int { return wh.Longest(w.contents) } func (w *JsonContent) MinW() int { return wh.Longest(w.contents) }
func (w *JsonContent) MinH() int { return len(w.contents) } func (w *JsonContent) MinH() int { return len(w.contents) }

View File

@@ -84,7 +84,7 @@ func (w *StatusBar) Draw(screen tcell.Screen) {
func (w *StatusBar) SetStyle(s tcell.Style) { w.style = s } func (w *StatusBar) SetStyle(s tcell.Style) { w.style = s }
func (w *StatusBar) Active() bool { return false } func (w *StatusBar) Active() bool { return false }
func (w *StatusBar) SetActive(a bool) {} func (w *StatusBar) SetActive(a bool) bool { return false }
func (w *StatusBar) Visible() bool { return w.visible } func (w *StatusBar) Visible() bool { return w.visible }
func (w *StatusBar) SetVisible(a bool) { w.visible = a } func (w *StatusBar) SetVisible(a bool) { w.visible = a }
func (w *StatusBar) Focusable() bool { return false } func (w *StatusBar) Focusable() bool { return false }

View File

@@ -29,6 +29,7 @@ type TreeBrowser struct {
cursor int cursor int
cursorWrap bool cursorWrap bool
nodes []*TreeNode nodes []*TreeNode
depthIndic string
onChange func(*TreeNode) bool onChange func(*TreeNode) bool
onSelect func(*TreeNode) bool onSelect func(*TreeNode) bool
@@ -49,6 +50,7 @@ func NewTreeBrowser(id string, s tcell.Style) *TreeBrowser {
func (w *TreeBrowser) Init(id string, style tcell.Style) { func (w *TreeBrowser) Init(id string, style tcell.Style) {
w.visible = true w.visible = true
w.focusable = true w.focusable = true
w.depthIndic = "• "
w.keyMap = t.NewKeyMap( w.keyMap = t.NewKeyMap(
t.NewKey(t.BuildEK(tcell.KeyUp), func(_ *tcell.EventKey) bool { return w.MoveUp() }), t.NewKey(t.BuildEK(tcell.KeyUp), func(_ *tcell.EventKey) bool { return w.MoveUp() }),
t.NewKey(t.BuildEK(tcell.KeyDown), func(_ *tcell.EventKey) bool { return w.MoveDown() }), t.NewKey(t.BuildEK(tcell.KeyDown), func(_ *tcell.EventKey) bool { return w.MoveDown() }),
@@ -134,18 +136,51 @@ func (w *TreeBrowser) Draw(screen tcell.Screen) {
} }
x, y = x+1, y+1 x, y = x+1, y+1
h := w.h - brdSz h := w.h - brdSz
for i := range w.list { ln := len(w.list)
th.DrawText(x, y, w.list[i], w.style.Reverse(i == w.cursor), screen) st, ed := 0, ln-1
y++ if ln == 0 {
if y > x+h { return
break
} }
if ln > w.h-2 {
mid := h / 2
if w.cursor < mid {
// Start drawing at 0
ed = h
} else if w.cursor > ln-mid {
// Start at ln-h+1
st = ln - h
} else {
st = w.cursor - mid
ed = st + h
}
}
// ed cannot be higher than ln - 1
if st < 0 {
st = 0
}
if ed > ln-1 {
ed = ln - 1
}
for i := st; i <= ed; i++ {
rev := false
if i == w.cursor {
rev = true
}
txt := w.list[i]
if len(txt) > w.w-brdSz && w.w-brdSz >= 0 {
txt = txt[:(w.w - brdSz)]
}
wh.DrawText(x, y, txt, dS.Reverse(rev), screen)
y += 1
} }
} }
func (w *TreeBrowser) SetStyle(s tcell.Style) { w.style = s } func (w *TreeBrowser) SetStyle(s tcell.Style) { w.style = s }
func (w *TreeBrowser) Active() bool { return w.active } func (w *TreeBrowser) Active() bool { return w.active }
func (w *TreeBrowser) SetActive(a bool) { w.active = a } func (w *TreeBrowser) SetActive(a bool) bool {
w.active = a
return w.active
}
func (w *TreeBrowser) Visible() bool { return w.visible } func (w *TreeBrowser) Visible() bool { return w.visible }
func (w *TreeBrowser) SetVisible(a bool) { w.visible = a } func (w *TreeBrowser) SetVisible(a bool) { w.visible = a }
func (w *TreeBrowser) Focusable() bool { return w.focusable } func (w *TreeBrowser) Focusable() bool { return w.focusable }
@@ -226,7 +261,7 @@ func (w *TreeBrowser) MoveUp() bool {
return false return false
} }
func (w *TreeBrowser) MoveDown() bool { func (w *TreeBrowser) MoveDown() bool {
if w.cursor <= len(w.list) { if w.cursor <= len(w.list)-2 {
w.cursor++ w.cursor++
if w.onChange != nil { if w.onChange != nil {
n, err := w.GetActiveNode() n, err := w.GetActiveNode()
@@ -278,6 +313,9 @@ func (w *TreeBrowser) Clear() {
w.UpdateList() w.UpdateList()
} }
func (w *TreeBrowser) Add(n *TreeNode) { func (w *TreeBrowser) Add(n *TreeNode) {
if n.depthIndic == "" {
n.depthIndic = w.depthIndic
}
w.nodes = append(w.nodes, n) w.nodes = append(w.nodes, n)
w.UpdateList() w.UpdateList()
} }
@@ -306,12 +344,14 @@ type TreeNode struct {
expanded bool expanded bool
parent *TreeNode parent *TreeNode
children []*TreeNode children []*TreeNode
depthIndic string
} }
func NewTreeNode(l, v string) *TreeNode { func NewTreeNode(l, v string) *TreeNode {
return &TreeNode{ return &TreeNode{
label: l, label: l,
value: v, value: v,
depthIndic: "• ",
} }
} }
func (tn *TreeNode) Depth() int { func (tn *TreeNode) Depth() int {
@@ -331,7 +371,7 @@ func (tn *TreeNode) GetLabelPath() []string {
return append(path, tn.Label()) return append(path, tn.Label())
} }
func (tn *TreeNode) getList() []string { func (tn *TreeNode) getList() []string {
pre := strings.Repeat("-", tn.Depth()) pre := strings.Repeat(tn.depthIndic, tn.Depth())
ret := []string{fmt.Sprintf("%s%s", pre, tn.label)} ret := []string{fmt.Sprintf("%s%s", pre, tn.label)}
if tn.expanded { if tn.expanded {
for i := range tn.children { for i := range tn.children {
@@ -353,9 +393,15 @@ func (tn *TreeNode) getVisibleNodeList() []*TreeNode {
func (tn *TreeNode) ToggleExpand() { tn.expanded = !tn.expanded } func (tn *TreeNode) ToggleExpand() { tn.expanded = !tn.expanded }
func (tn *TreeNode) AddChild(t *TreeNode, rest ...*TreeNode) { func (tn *TreeNode) AddChild(t *TreeNode, rest ...*TreeNode) {
if t.depthIndic == "" {
t.depthIndic = tn.depthIndic
}
t.parent = tn t.parent = tn
tn.children = append(tn.children, t) tn.children = append(tn.children, t)
for i := range rest { for i := range rest {
if rest[i].depthIndic == "" {
rest[i].depthIndic = tn.depthIndic
}
rest[i].parent = tn rest[i].parent = tn
tn.children = append(tn.children, rest[i]) tn.children = append(tn.children, rest[i])
} }