Skip to content

Commit 72d7eaa

Browse files
committed
WIP
1 parent 1908a3f commit 72d7eaa

File tree

3 files changed

+167
-38
lines changed

3 files changed

+167
-38
lines changed

tree/node.go

Lines changed: 61 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,21 @@ type Node struct {
2828
opts itemOptions
2929
}
3030

31+
// Indicator returns the rendered open/closed symbol
32+
func (t *Node) Indicator() string {
33+
char := ""
34+
if t.open {
35+
char = t.opts.openCharacter
36+
} else {
37+
char = t.opts.closedCharacter
38+
}
39+
if char != "" {
40+
char += " "
41+
}
42+
43+
return t.opts.styles.OpenIndicatorStyle.Render(char)
44+
}
45+
3146
// IsSelected returns whether this item is selected.
3247
func (t *Node) IsSelected() bool {
3348
return t.yOffset == t.opts.treeYOffset
@@ -80,37 +95,51 @@ type itemOptions struct {
8095

8196
// Used to print the Node's tree.
8297
func (t *Node) String() string {
83-
s := t.opts.styles.OpenIndicatorStyle
84-
if t.open {
85-
return s.Render(t.opts.openCharacter+" ") + t.tree.String()
86-
}
87-
return s.Render(t.opts.closedCharacter+" ") + t.tree.String()
98+
return t.tree.String()
8899
}
89100

90101
func (t *Node) getStyle() lipgloss.Style {
91102
s := t.opts.styles
92-
if t.yOffset == t.opts.treeYOffset {
103+
if t.yOffset == t.opts.treeYOffset && s.selectedNodeFunc != nil {
93104
return s.selectedNodeFunc(Nodes{t}, 0)
94-
} else if t.yOffset == 0 {
105+
} else if t.yOffset == 0 && s.rootNodeFunc != nil {
95106
return s.rootNodeFunc(Nodes{t}, 0)
96-
} else if t.isRoot {
107+
} else if t.isRoot && s.parentNodeFunc != nil {
97108
return s.parentNodeFunc(Nodes{t}, 0)
109+
} else if s.nodeFunc != nil {
110+
return s.nodeFunc(Nodes{t}, 0)
111+
}
112+
113+
return lipgloss.NewStyle()
114+
}
115+
116+
func (t *Node) getEnumeratorStyle() lipgloss.Style {
117+
s := t.opts.styles
118+
if t.yOffset == t.opts.treeYOffset && s.selectedEnumeratorFunc != nil {
119+
return s.selectedEnumeratorFunc(Nodes{t}, 0)
120+
} else if s.enumeratorFunc != nil {
121+
return s.enumeratorFunc(Nodes{t}, 0)
122+
}
123+
124+
return lipgloss.NewStyle()
125+
}
126+
127+
func (t *Node) getIndenterStyle() lipgloss.Style {
128+
s := t.opts.styles
129+
if s.indenterFunc != nil {
130+
return s.indenterFunc(Nodes{t}, 0)
98131
}
99132

100-
return s.nodeFunc(Nodes{t}, 0)
133+
return lipgloss.NewStyle()
101134
}
102135

103136
// Value returns the root name of this node.
104137
func (t *Node) Value() string {
105-
s := t.opts.styles
106138
ns := t.getStyle()
107139
v := ns.Render(t.tree.Value())
108140

109141
if t.isRoot {
110-
if t.open {
111-
return s.OpenIndicatorStyle.Render(t.opts.openCharacter+" ") + v
112-
}
113-
return s.OpenIndicatorStyle.Render(t.opts.closedCharacter+" ") + v
142+
return lipgloss.JoinHorizontal(lipgloss.Top, t.Indicator(), v)
114143
}
115144

116145
// Leaf.
@@ -145,7 +174,8 @@ func (t *Node) ChildNodes() []*Node {
145174

146175
// AllNodes returns all descendant nodes as a flat list.
147176
func (t *Node) AllNodes() []*Node {
148-
res := []*Node{t}
177+
res := make([]*Node, 0)
178+
res = append(res, t)
149179
children := t.tree.Children()
150180
for i := 0; i < children.Length(); i++ {
151181
child := children.At(i)
@@ -259,8 +289,14 @@ func (t *Node) EnumeratorStyle(style lipgloss.Style) *Node {
259289
// }
260290
// return lipgloss.NewStyle().Foreground(dimColor)
261291
// })
262-
func (t *Node) EnumeratorStyleFunc(f func(children ltree.Children, i int) lipgloss.Style) *Node {
263-
t.tree.EnumeratorStyleFunc(f)
292+
func (t *Node) EnumeratorStyleFunc(f StyleFunc) *Node {
293+
t.tree.EnumeratorStyleFunc(func(children ltree.Children, i int) lipgloss.Style {
294+
c := make(Nodes, children.Length())
295+
for i := 0; i < children.Length(); i++ {
296+
c[i] = children.At(i).(*Node)
297+
}
298+
return f(c, i)
299+
})
264300
return t
265301
}
266302

@@ -282,8 +318,14 @@ func (t *Node) IndenterStyle(style lipgloss.Style) *Node {
282318
// }
283319
// return lipgloss.NewStyle().Foreground(dimColor)
284320
// })
285-
func (t *Node) IndenterStyleFunc(f func(children ltree.Children, i int) lipgloss.Style) *Node {
286-
t.tree.IndenterStyleFunc(f)
321+
func (t *Node) IndenterStyleFunc(f StyleFunc) *Node {
322+
t.tree.IndenterStyleFunc(func(children ltree.Children, i int) lipgloss.Style {
323+
c := make(Nodes, children.Length())
324+
for i := 0; i < children.Length(); i++ {
325+
c[i] = children.At(i).(*Node)
326+
}
327+
return f(c, i)
328+
})
287329
return t
288330
}
289331

tree/styles.go

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,18 @@ type Styles struct {
2929

3030
CursorStyle lipgloss.Style
3131

32-
EnumeratorStyle lipgloss.Style
33-
IndenterStyle lipgloss.Style
32+
enumeratorFunc StyleFunc
33+
EnumeratorStyle lipgloss.Style
34+
EnumeratorStyleFunc StyleFunc
35+
36+
selectedEnumeratorFunc StyleFunc
37+
SelectedEnumeratorStyle lipgloss.Style
38+
SelectedEnumeratorStyleFunc StyleFunc
39+
40+
indenterFunc StyleFunc
41+
IndenterStyle lipgloss.Style
42+
IndenterStyleFunc StyleFunc
43+
3444
OpenIndicatorStyle lipgloss.Style
3545
}
3646

@@ -68,7 +78,20 @@ func DefaultStyles(isDark bool) (s Styles) {
6878
s.CursorStyle = lipgloss.NewStyle().PaddingRight(1).Foreground(lipgloss.Color("212")).Bold(true)
6979

7080
s.EnumeratorStyle = lipgloss.NewStyle().Foreground(verySubduedColor)
81+
s.enumeratorFunc = func(children Nodes, i int) lipgloss.Style {
82+
return s.EnumeratorStyle
83+
}
84+
85+
s.SelectedEnumeratorStyle = s.EnumeratorStyle
86+
s.selectedEnumeratorFunc = func(children Nodes, i int) lipgloss.Style {
87+
return s.SelectedEnumeratorStyle
88+
}
89+
7190
s.IndenterStyle = lipgloss.NewStyle().Foreground(verySubduedColor)
91+
s.indenterFunc = func(children Nodes, i int) lipgloss.Style {
92+
return s.IndenterStyle
93+
}
94+
7295
s.OpenIndicatorStyle = lipgloss.NewStyle().Foreground(subduedColor)
7396

7497
return s

tree/tree.go

Lines changed: 81 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@
33
package tree
44

55
import (
6+
"fmt"
67
"strings"
78

89
tea "charm.land/bubbletea/v2"
910
"charm.land/lipgloss/v2"
1011
ltree "charm.land/lipgloss/v2/tree"
12+
"github.com/charmbracelet/x/ansi"
1113

1214
"charm.land/bubbles/v2/help"
1315
"charm.land/bubbles/v2/key"
@@ -245,6 +247,7 @@ func (m *Model) SetCursorCharacter(character string) {
245247
// SetNodes sets the tree to the given root node.
246248
func (m *Model) SetNodes(t *Node) {
247249
m.root = t
250+
m.root.value = t.Value()
248251
if m.enumerator != nil {
249252
m.root.Enumerator(*m.enumerator)
250253
}
@@ -254,6 +257,7 @@ func (m *Model) SetNodes(t *Node) {
254257
m.setAttributes()
255258
m.updateStyles()
256259
m.updateViewport(0)
260+
m.setRootStyles(m.styles)
257261
}
258262

259263
// SetAdditionalFullHelpKeys sets additional key mappings for the full help view.
@@ -366,7 +370,7 @@ func (m *Model) updateViewport(movement int) {
366370
lipgloss.JoinHorizontal(
367371
lipgloss.Top,
368372
cursor,
369-
m.styles.TreeStyle.Render(m.root.String()),
373+
m.styles.TreeStyle.Width(m.width).MaxWidth(m.width).Render(m.root.String()),
370374
),
371375
)
372376

@@ -418,20 +422,51 @@ func (m *Model) SetStyles(styles Styles) {
418422
return styles.RootNodeStyle
419423
}
420424
}
425+
if styles.EnumeratorStyleFunc != nil {
426+
styles.enumeratorFunc = styles.EnumeratorStyleFunc
427+
} else {
428+
styles.enumeratorFunc = func(_ Nodes, _ int) lipgloss.Style {
429+
return styles.EnumeratorStyle
430+
}
431+
}
432+
if styles.SelectedEnumeratorStyleFunc != nil {
433+
styles.selectedEnumeratorFunc = styles.SelectedEnumeratorStyleFunc
434+
} else {
435+
styles.selectedEnumeratorFunc = func(_ Nodes, _ int) lipgloss.Style {
436+
return styles.SelectedEnumeratorStyle
437+
}
438+
}
439+
if styles.IndenterStyleFunc != nil {
440+
styles.indenterFunc = styles.IndenterStyleFunc
441+
} else {
442+
styles.indenterFunc = func(_ Nodes, _ int) lipgloss.Style {
443+
return styles.IndenterStyle
444+
}
445+
}
421446

447+
m.setRootStyles(styles)
448+
m.styles = styles
449+
450+
// call SetSize as it takes into account width/height of the styles frame sizes
451+
m.SetSize(m.width, m.height)
452+
m.updateViewport(0)
453+
}
454+
455+
func (m *Model) setRootStyles(styles Styles) {
422456
if m.root != nil {
423-
m.root.EnumeratorStyle(styles.EnumeratorStyle)
424-
m.root.IndenterStyle(styles.IndenterStyle)
457+
m.root.EnumeratorStyleFunc(func(children Nodes, i int) lipgloss.Style {
458+
child := children.At(i)
459+
return child.getEnumeratorStyle()
460+
})
461+
m.root.IndenterStyleFunc(func(children Nodes, i int) lipgloss.Style {
462+
child := children.At(i)
463+
return child.getIndenterStyle()
464+
})
425465
m.root.ItemStyleFunc(func(children Nodes, i int) lipgloss.Style {
426466
child := children.At(i)
427467
return child.getStyle()
428468
})
429469
}
430-
431-
m.styles = styles
432-
// call SetSize as it takes into account width/height of the styles frame sizes
433-
m.SetSize(m.width, m.height)
434-
m.updateViewport(0)
435470
}
436471

437472
// SetShowHelp shows or hides the help view.
@@ -464,8 +499,10 @@ func (m *Model) SetHeight(height int) {
464499
func (m *Model) SetSize(width, height int) {
465500
m.width = width
466501
m.height = height
467-
m.root.tree.Width(width - lipgloss.Width(m.cursorView()) -
468-
m.styles.TreeStyle.GetHorizontalFrameSize())
502+
if m.root != nil {
503+
m.root.tree.Width(width - lipgloss.Width(m.cursorView()) -
504+
m.styles.TreeStyle.GetHorizontalFrameSize())
505+
}
469506

470507
m.viewport.SetWidth(width)
471508
hv := 0
@@ -589,6 +626,11 @@ func (m *Model) YOffset() int {
589626
return m.yOffset
590627
}
591628

629+
func (m *Model) SetYOffset(yoffset int) {
630+
movement := yoffset - m.yOffset
631+
m.updateViewport(movement)
632+
}
633+
592634
// Node returns the item at the given yoffset.
593635
func (m *Model) Node(yoffset int) *Node {
594636
return findNode(m.root, yoffset)
@@ -602,26 +644,42 @@ func (m *Model) NodeAtCurrentOffset() *Node {
602644
// Enumerator sets the enumerator for the tree.
603645
func (m *Model) Enumerator(enumerator ltree.Enumerator) *Model {
604646
m.enumerator = &enumerator
605-
m.root.Enumerator(enumerator)
647+
if m.root != nil {
648+
m.root.Enumerator(enumerator)
649+
}
606650
return m
607651
}
608652

609653
// Indenter sets the indenter for the tree.
610654
func (m *Model) Indenter(indenter ltree.Indenter) *Model {
611655
m.indenter = &indenter
612-
m.root.Indenter(indenter)
656+
if m.root != nil {
657+
m.root.Indenter(indenter)
658+
}
613659
return m
614660
}
615661

616662
// Since the selected node changes, we need to capture m.yOffset in the
617663
// style function's closure again.
618664
func (m *Model) updateStyles() {
665+
opts := m.getItemOpts()
619666
if m.root != nil {
620-
m.root.RootStyle(m.rootStyle())
667+
m.root.opts = *opts
668+
rs := m.rootStyle()
669+
m.root.RootStyle(rs)
670+
671+
root := ""
672+
switch val := m.root.value.(type) {
673+
case fmt.Stringer:
674+
root = rs.Render(val.String())
675+
case string:
676+
root = rs.Render(ansi.Strip(val))
677+
}
678+
root = lipgloss.JoinHorizontal(lipgloss.Left, m.root.Indicator(), root)
679+
m.root.tree.SetValue(root)
621680
}
622681

623682
items := m.AllNodes()
624-
opts := m.getItemOpts()
625683
for _, item := range items {
626684
item.opts = *opts
627685
}
@@ -637,11 +695,17 @@ func (m *Model) getItemOpts() *itemOptions {
637695
}
638696

639697
func (m *Model) rootStyle() lipgloss.Style {
640-
if m.root.yOffset == m.yOffset {
641-
return m.styles.selectedNodeFunc(Nodes{m.root}, 0)
698+
nodes := Nodes{m.root}
699+
if m.root.yOffset == m.yOffset && m.styles.selectedNodeFunc != nil {
700+
return m.styles.rootNodeFunc(nodes, 0).Inherit(
701+
m.styles.selectedNodeFunc(Nodes{m.root}, 0))
702+
}
703+
704+
if m.styles.rootNodeFunc != nil {
705+
return m.styles.rootNodeFunc(Nodes{m.root}, 0)
642706
}
643707

644-
return m.styles.rootNodeFunc(Nodes{m.root}, 0)
708+
return lipgloss.NewStyle()
645709
}
646710

647711
// findNode starts a DFS search for the node with the given yOffset

0 commit comments

Comments
 (0)