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)) }