forked from VladimirMarkelov/clui
-
Notifications
You must be signed in to change notification settings - Fork 0
/
tableview.go
130 lines (115 loc) · 4.2 KB
/
tableview.go
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
/*
* Demo includes:
* - How to use OnBeforeDraw event
* - a simple example of "DBCache" for faster drawing
*/
package main
import (
"fmt"
ui "github.com/VladimirMarkelov/clui"
)
// number of columns in a table
const columnInTable = 6
// dbCache for data from DB. It always caches the whole table row, so it does not
// use firstCol and colCount values from OnBeforeDraw event. But you can do more
// granular storage to minimize memory usage by cache
// dbCache is quite dumb: if it detects that topRow or the number of visible rows
// is changed it invalidates the cache and reloads all the data from new row span.
// In real application, it would be good to make it smarter, e.g:
// - if rowCount descreased and firstRow does not change - the cache is valid,
// and redundant rereading data can be skipped
// - usually visible area changes by 1 row, so performance-wise the cache can
// shift row slice and read only new rows
// - etc
type dbCache struct {
firstRow int // previous first visible row
rowCount int // previous visible row count
data [][]string // cache - contains at least 'rowCount' rows from DB
}
// cache data from a new row span
// It imitates a random data by selecting values from predefined arrays. Sizes
// of all arrays should be different to make TableView data look more random
func (d *dbCache) preload(firstRow, rowCount int) {
if firstRow == d.firstRow && rowCount == d.rowCount {
// fast path: view area is the same, return immediately
return
}
// slow path: refill cache
fNames := []string{"Jack", "Alisa", "Richard", "Paul", "Nicole", "Steven", "Jane"}
lNames := []string{"Smith", "Catcher", "Stone", "White", "Black"}
posts := []string{"Engineer", "Manager", "Janitor", "Driver"}
deps := []string{"IT", "Financial", "Support"}
salary := []int{40000, 38000, 41000, 32000}
d.data = make([][]string, rowCount, rowCount)
for i := 0; i < rowCount; i++ {
absIndex := firstRow + i
d.data[i] = make([]string, columnInTable, columnInTable)
d.data[i][0] = fNames[absIndex%len(fNames)]
d.data[i][1] = lNames[absIndex%len(lNames)]
d.data[i][2] = fmt.Sprintf("%08d", 100+absIndex)
d.data[i][3] = posts[absIndex%len(posts)]
d.data[i][4] = deps[absIndex%len(deps)]
d.data[i][5] = fmt.Sprintf("%d k/year", salary[absIndex%len(salary)]/1000)
}
// do not forget to save the last values
d.firstRow = firstRow
d.rowCount = rowCount
}
// returns the cell value for a given col and row. Col and row are absolute
// value. But cache keeps limited number of rows to minimize memory usage.
// So, the position of the value of the cell should be calculated
// To simplify, the function just returns empty string if the cell is not
// cached. It is unlikely but can happen
func (d *dbCache) value(row, col int) string {
rowId := row - d.firstRow
if rowId >= len(d.data) {
return ""
}
rowValues := d.data[rowId]
if col >= len(rowValues) {
return ""
}
return rowValues[col]
}
var (
view *ui.Window
)
func createView() *ui.TableView {
view = ui.AddWindow(0, 0, 10, 7, "TableView Preload Demo")
bch := ui.CreateTableView(view, 35, 12, 1)
ui.ActivateControl(view, bch)
return bch
}
func mainLoop() {
// Every application must create a single Composer and
// call its intialize method
ui.InitLibrary()
defer ui.DeinitLibrary()
cache := &dbCache{firstRow: -1}
b := createView()
b.SetShowLines(true)
b.SetShowRowNumber(true)
b.SetRowCount(25)
cols := []ui.Column{
ui.Column{Title: "First Name", Width: 10, Alignment: ui.AlignLeft},
ui.Column{Title: "Last Name", Width: 12, Alignment: ui.AlignLeft},
ui.Column{Title: "ID", Width: 12, Alignment: ui.AlignRight},
ui.Column{Title: "Post", Width: 12, Alignment: ui.AlignLeft},
ui.Column{Title: "Department", Width: 15, Alignment: ui.AlignLeft},
ui.Column{Title: "Salary", Width: 12, Alignment: ui.AlignRight},
}
b.SetColumns(cols)
b.OnBeforeDraw(func(col, row, colCnt, rowCnt int) {
cache.preload(row, rowCnt)
l, t, w, h := b.VisibleArea()
view.SetTitle(fmt.Sprintf("Caching: %d:%d - %dx%d", l, t, w, h))
})
b.OnDrawCell(func(info *ui.ColumnDrawInfo) {
info.Text = cache.value(info.Row, info.Col)
})
// start event processing loop - the main core of the library
ui.MainLoop()
}
func main() {
mainLoop()
}