Files
rdu/tui.go
tom.chivert 8efc6d8c6d
build / build (push) Successful in 32s
slightly optimized cpu usage
2024-08-20 19:41:00 +02:00

306 lines
6.0 KiB
Go

package main
import (
"os"
"path/filepath"
"strings"
"time"
"unicode"
"github.com/gdamore/tcell/v2"
)
var (
pos int = 0
)
func drawTextLine(screen tcell.Screen, x, y int, s string, style tcell.Style) {
for _, r := range s {
screen.SetContent(x, y, r, nil, style)
x++
}
}
func drawLine(screen tcell.Screen, x, y, w int, style tcell.Style) {
for i := 0; i < w; i++ {
screen.SetContent(x, y, ' ', nil, style)
x++
}
}
func getNameSize(i Item, w int) (string, string) {
name := ""
size := ""
c := w / 2
if i.isDir {
name = "/" + filepath.Base(i.dir.path)
size = alignRight(6, sizeConv(i.dir.size))
} else {
name = i.file.name
size = alignRight(6, sizeConv(i.file.size))
}
if len(name) > c-14 && w > 60 {
name = name[:c-14] + "..."
} else if len(name) > w-14 {
name = name[:w-14] + "..."
}
return name, size
}
func (d Dir) draw(screen tcell.Screen, items []Item) {
title := "rdu - another disk usage utility"
mainStyle := tcell.StyleDefault.Foreground(tcell.NewHexColor(0xFFFFFF))
titleStyle := tcell.StyleDefault.Foreground(tcell.NewHexColor(0x000000)).Background(tcell.NewHexColor(0xFFFFFF)).Bold(true)
footStyle := tcell.StyleDefault.Foreground(tcell.NewHexColor(0x000000)).Background(tcell.NewHexColor(0xFFFFFF))
infoStyle := mainStyle.Foreground(tcell.NewHexColor(0xF012BE)).Bold(true)
screen.Fill(' ', mainStyle)
w, h := screen.Size()
c := w / 2
i := 0
if pos > h-4 {
i += pos - (h - 4)
}
l := 1
drawLine(screen, 0, 0, w, titleStyle)
drawTextLine(screen, c-len(title)/2, 0, title, titleStyle)
drawLine(screen, 0, h-1, w, footStyle)
drawTextLine(screen, 1, h-1, "Current directory size:", titleStyle)
drawTextLine(screen, 25, h-1, sizeConv(d.size), footStyle)
drawTextLine(screen, c+1, h-1, "Total scanned files:", titleStyle)
drawTextLine(screen, c+22, h-1, itoa(count), footStyle)
drawTextLine(screen, w-6, h-1, time.Now().Format("15:04"), footStyle)
path := d.path
for len(path) > c-6 {
tmp := strings.Split(path, "/")
tmp = tmp[1:]
path = strings.Join(tmp, "/")
if path == "" {
path = "/"
}
}
if len(d.path) > c-6 {
path = ".../" + path
}
drawTextLine(screen, c/2-len(path)/2, 1, path, mainStyle.Bold(true))
l++
for i < len(items) {
style := mainStyle
if i == pos {
style = infoStyle
if items[i].isDir && w > 60 {
next := getItems(*items[i].dir)
if next == nil {
continue
}
next = sortItems(next)
for j := 0; j < len(next); j++ {
if next[j].isDir {
name, size := getNameSize(next[j], w)
drawTextLine(screen, c+1, j+2, size, mainStyle)
drawTextLine(screen, c+10, j+2, name, mainStyle)
} else {
name, size := getNameSize(next[j], w)
drawTextLine(screen, c+1, j+2, size, mainStyle)
drawTextLine(screen, c+10, j+2, name, mainStyle)
}
if j >= h-4 {
break
}
}
}
}
if items[i].file != nil || items[i].dir != nil {
if items[i].isDir {
name, size := getNameSize(items[i], w)
drawTextLine(screen, 0, l, size, style)
drawTextLine(screen, 10, l, name, style)
} else {
name, size := getNameSize(items[i], w)
drawTextLine(screen, 0, l, size, style)
drawTextLine(screen, 10, l, name, style)
}
i++
l++
}
if l >= h-1 {
break
}
}
for i := 1; i < h-1; i++ {
drawTextLine(screen, c, i, "|", mainStyle)
}
}
func (d Dir) handle(event tcell.Event, items []Item) string {
switch event := event.(type) {
case *tcell.EventKey:
if event.Key() == tcell.KeyLeft || event.Key() == tcell.KeyEscape {
if d.prev != nil {
return "prev"
}
return ""
}
if event.Key() == tcell.KeyRight || event.Key() == tcell.KeyEnter {
if items == nil {
return ""
}
if !items[pos].isDir {
return ""
}
return "next"
}
if event.Key() == tcell.KeyUp {
pos -= 1
if pos < 0 {
pos = len(items) - 1
}
return "up"
}
if event.Key() == tcell.KeyDown {
pos += 1
if pos >= len(items) {
pos = 0
}
return "down"
}
if event.Key() == tcell.KeyPgUp {
pos -= 10
if pos < 0 {
pos = 0
}
return "up"
}
if event.Key() == tcell.KeyPgDn {
pos += 10
if pos >= len(items) {
pos = len(items) - 1
}
return "down"
}
if event.Key() == tcell.KeyCtrlC {
return "quit"
}
if event.Key() == tcell.KeyHome {
pos = 0
return "up"
}
if event.Key() == tcell.KeyEnd {
pos = len(items) - 1
return "down"
}
if event.Key() != tcell.KeyRune {
return ""
}
switch unicode.ToLower(event.Rune()) {
case 'q':
return "quit"
case 'r':
return "refresh"
case 'h':
return "help"
}
}
return ""
}
func show(d Dir) {
screen, err := tcell.NewScreen()
if err != nil {
printerr(err)
os.Exit(1)
}
err = screen.Init()
if err != nil {
printerr(err)
os.Exit(1)
}
defer screen.Fini()
events := make(chan tcell.Event)
go func() {
for {
events <- screen.PollEvent()
time.Sleep(time.Millisecond * 20)
if screen.HasPendingEvent() {
screen.PollEvent()
}
}
}()
curr := &d
items := sortItems(getItems(*curr))
screen.Clear()
curr.draw(screen, items)
screen.Show()
refresh := time.Tick(time.Second * 10)
for {
select {
case event := <-events:
switch event := event.(type) {
case *tcell.EventResize:
screen.Clear()
curr.draw(screen, items)
screen.Sync()
default:
action := curr.handle(event, items)
if action == "quit" {
screen.Fini()
return
}
if action == "prev" {
items = sortItems(getItems(*curr.prev))
pos = getPos(curr.path, items)
curr = curr.prev
screen.Clear()
curr.draw(screen, items)
screen.Show()
continue
}
if action == "next" {
items[pos].dir.prev = curr
curr = items[pos].dir
pos = 0
items = sortItems(getItems(*curr))
screen.Clear()
curr.draw(screen, items)
screen.Show()
continue
}
if action != "" {
screen.Clear()
curr.draw(screen, items)
screen.Show()
}
}
case <-refresh:
screen.Clear()
curr.draw(screen, items)
screen.Show()
}
}
}