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 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185
|
package tui
import (
"path/filepath"
"slices"
"github.com/dundee/gdu/v5/pkg/device"
"github.com/dundee/gdu/v5/pkg/fs"
"github.com/rivo/tview"
)
var (
barFullRune = "\u2588"
barPartRunes = map[int]string{
0: " ",
1: "\u258F",
2: "\u258E",
3: "\u258D",
4: "\u258C",
5: "\u258B",
6: "\u258A",
7: "\u2589",
}
)
func getDeviceUsagePart(item *device.Device, useOld bool) string {
part := int(float64(item.Size-item.Free) / float64(item.Size) * 100.0)
if useOld {
return getUsageGraphOld(part)
}
return getUsageGraph(part)
}
func getUsageGraph(part int) string {
graph := " "
whole := part / 10
for i := 0; i < whole; i++ {
graph += barFullRune
}
partWidth := (part % 10) * 8 / 10
if part < 100 {
graph += barPartRunes[partWidth]
}
for i := 0; i < 10-whole-1; i++ {
graph += " "
}
graph += "\u258F"
return graph
}
func getUsageGraphOld(part int) string {
part /= 10
graph := "["
for i := 0; i < 10; i++ {
if part > i {
graph += "#"
} else {
graph += " "
}
}
graph += "]"
return graph
}
func modal(p tview.Primitive, width, height int) tview.Primitive {
return tview.NewFlex().
AddItem(nil, 0, 1, false).
AddItem(tview.NewFlex().SetDirection(tview.FlexRow).
AddItem(nil, 0, 1, false).
AddItem(p, height, 1, true).
AddItem(nil, 0, 1, false), width, 1, true).
AddItem(nil, 0, 1, false)
}
// CollapsedPath represents a directory chain that can be collapsed into a single display entry.
// For example, if directory "a" contains only directory "b", and "b" contains only "c",
// this represents the collapsed path "a/b/c" that allows direct navigation to the deepest directory.
type CollapsedPath struct {
DisplayName string // The display name shown in the UI (e.g., "a/b/c")
DeepestDir fs.Item // The actual deepest directory item
Segments []string // Individual path segments of the collapsed chain
}
// findCollapsiblePath checks if the given directory item has a single subdirectory chain
// and returns a CollapsedPath if it can be collapsed
func findCollapsiblePath(item fs.Item) *CollapsedPath {
if item == nil || !item.IsDir() {
return nil
}
var segments []string
current := item
for {
// Collect files to check count and types
var files []fs.Item
for file := range current.GetFiles(fs.SortByName, fs.SortAsc) {
files = append(files, file)
}
if len(files) > 1 {
break
}
// Count directories and files separately
var subdirs []fs.Item
var fileCount int
for _, file := range files {
if file.IsDir() {
subdirs = append(subdirs, file)
} else {
fileCount++
}
}
// Only collapse if there's exactly one subdirectory AND no files
if len(subdirs) != 1 || fileCount > 0 {
break
}
// Add this segment to the path
// nolint:staticcheck // the result is used
segments = append(segments, subdirs[0].GetName())
current = subdirs[0]
}
// Only create collapsed path if we have at least one collapsible segment
if len(segments) == 0 {
return nil
}
return &CollapsedPath{
DisplayName: filepath.Join(slices.Concat([]string{item.GetName()}, segments)...),
DeepestDir: current,
Segments: segments,
}
}
// findCollapsedParent checks if the current directory is the deepest directory
// in a collapsed path, and returns the appropriate parent to navigate to
func findCollapsedParent(currentDir fs.Item) fs.Item {
if currentDir == nil {
return nil
}
if currentDir.GetParent() == nil {
return nil
}
// Check if current directory is part of a single-child chain going up
current := currentDir
var chainParent fs.Item
// Walk up the parent chain
for current.GetParent() != nil {
parent := current.GetParent()
// Count files in parent
fileCount := 0
for range parent.GetFiles(fs.SortByName, fs.SortAsc) {
fileCount++
if fileCount > 1 {
break
}
}
// If parent has more than one item, this is where the collapsed chain starts
if fileCount > 1 {
chainParent = parent
break
}
// Move up the chain
current = parent
}
// If we found a chain parent (meaning current dir is part of a collapsed path),
// return it, otherwise return the normal parent
if chainParent != nil {
return chainParent
}
return currentDir.GetParent()
}
|