1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
|
package filetree
import (
"strings"
"github.com/gdamore/tcell/v2"
"rickub.com/turbo-editors/turbo-core/theme"
"rickub.com/turbo-editors/turbo-core/ui"
)
// Draw paints the visible rows and the scroll bar beside them.
func (v *View) Draw(p *ui.Painter, th *theme.Theme) {
area := p.Sub(v.Bounds())
area.Clear(th.Style(theme.KeyTreeText))
rows := v.tree.Rows()
v.scrollToSelection(len(rows), area.Size().H)
width := max(area.Size().W-1, 0)
for line := range area.Size().H {
index := v.top + line
if index >= len(rows) {
break
}
v.drawRow(area, line, width, rows[index], index == v.selected, th)
}
ui.DrawVScrollBar(area, area.Size().W-1, 0, area.Size().H,
v.top, area.Size().H, len(rows),
th.Style(theme.KeyScrollBar), th.Style(theme.KeyScrollBarThumb))
}
// drawRow paints one entry: its indentation, its marker and its name.
func (v *View) drawRow(p *ui.Painter, line, width int, row Row, selected bool, th *theme.Theme) {
style := v.styleFor(row.Node, selected, th)
if selected {
p.HLine(0, line, width, ' ', style)
}
p.TextLimited(0, line, width, label(row), style)
}
// label renders one row as the text to draw.
//
// A file is indented by a marker's width as well, so that names line up in a
// column whatever their neighbours are.
func label(row Row) string {
return strings.Repeat(indent, row.Depth) + marker(row.Node) + row.Node.Name()
}
// marker returns the glyph that says whether a directory is open, closed, or
// not a directory at all.
func marker(node *Node) string {
switch {
case !node.IsDir():
return fileMarker
case node.Expanded():
return openMarker
default:
return closedMarker
}
}
// styleFor picks the colour of one row.
//
// The highlight is dimmed when the tree does not have the focus, the same way
// a list box dims its own, so that a screenful of windows still says where the
// keyboard is.
func (v *View) styleFor(node *Node, selected bool, th *theme.Theme) tcell.Style {
switch {
case selected && v.Focused():
return th.Style(theme.KeyTreeSelected)
case selected:
return th.Style(theme.KeyTreeUnfocused)
case node.IsDir():
return th.Style(theme.KeyTreeDirectory)
default:
return th.Style(theme.KeyTreeText)
}
}
// scrollToSelection moves the window of visible rows just enough to keep the
// highlight on screen.
func (v *View) scrollToSelection(total, height int) {
if height <= 0 {
return
}
if v.selected < v.top {
v.top = v.selected
}
if v.selected >= v.top+height {
v.top = v.selected - height + 1
}
v.top = min(max(v.top, 0), max(total-height, 0))
}
|