Issue #56. Slight improvement in layout algorithm. Still doesn't handle > 2 columns well, but it's more correct and lays the foundation for a correct solution. It's clear, however, that a full table model parser will require a substantial rewrite and, possibly, a change in the layout syntax. That would add substantial complexity, so maybe for some future effort. I'll fix the basic column layout (rowspans at the edges), and then shelve it.

This commit is contained in:
Sean E. Russell 2020-02-20 08:19:15 -06:00
parent 66ad0f8785
commit 7b09a00230
5 changed files with 59 additions and 21 deletions

View File

@ -423,7 +423,6 @@ func getLayout(conf gotop.Config) io.Reader {
case "battery":
return strings.NewReader(batteryUI)
default:
log.Printf("layout = %s", conf.Layout)
fp := filepath.Join(conf.ConfigDir, conf.Layout)
fin, err := os.Open(fp)
if err != nil {

View File

@ -31,17 +31,17 @@ var widgetNames []string = []string{"cpu", "disk", "mem", "temp", "net", "procs"
func Layout(wl layout, c gotop.Config) (*MyGrid, error) {
rowDefs := wl.Rows
uiRows := make([]ui.GridItem, 0)
uiRows := make([][]interface{}, 0)
numRows := countNumRows(wl.Rows)
var uiRow ui.GridItem
var uiRow []interface{}
for len(rowDefs) > 0 {
uiRow, rowDefs = processRow(c, numRows, rowDefs)
uiRows = append(uiRows, uiRow)
}
rgs := make([]interface{}, 0)
rh := 1.0 / float64(len(uiRows))
for _, ur := range uiRows {
ur.HeightRatio = ur.HeightRatio / float64(numRows)
rgs = append(rgs, ur)
rgs = append(rgs, ui.NewRow(rh, ur...))
}
grid := &MyGrid{ui.NewGrid(), nil, nil}
grid.Set(rgs...)
@ -58,11 +58,10 @@ func Layout(wl layout, c gotop.Config) (*MyGrid, error) {
// if there's a row span widget in the row; in this case, it'll consume as many
// rows as the largest row span object in the row, and produce an uber-row
// containing all that stuff. It returns a slice without the consumed elements.
func processRow(c gotop.Config, numRows int, rowDefs [][]widgetRule) (ui.GridItem, [][]widgetRule) {
// FIXME: 3\:A 2\:B\nC should stop consuming rows when all columns are full
func processRow(c gotop.Config, numRows int, rowDefs [][]widgetRule) ([]interface{}, [][]widgetRule) {
// Recursive function #3. See the comment in deepFindProc.
if len(rowDefs) < 1 {
return ui.GridItem{}, [][]widgetRule{}
return nil, [][]widgetRule{}
}
// The height of the tallest widget in this row; the number of rows that
// will be consumed, and the overall height of the row that will be
@ -87,6 +86,7 @@ func processRow(c gotop.Config, numRows int, rowDefs [][]widgetRule) (ui.GridIte
columns = append(columns, make([]interface{}, 0))
}
colHeights := make([]int, numCols)
outer:
for i, row := range processing {
// A definition may fill up the columns before all rows are consumed,
// e.g. wid1/2 wid2/2. This block checks for that and, if it occurs,
@ -102,16 +102,25 @@ func processRow(c gotop.Config, numRows int, rowDefs [][]widgetRule) (ui.GridIte
rowDefs = append(processing[i:], rowDefs...)
break
}
// Not all rows have been consumed, so go ahead and place the row's widgets in columns
for _, wid := range row {
for j, ch := range colHeights {
if ch+wid.Height <= maxHeight {
widget := makeWidget(c, wid)
columns[j] = append(columns[j], ui.NewRow(float64(wid.Height)/float64(maxHeight), widget))
colHeights[j] += wid.Height
// Not all rows have been consumed, so go ahead and place the row's
// widgets in columns
for w, widg := range row {
placed := false
for k := w; k < len(colHeights); k++ { // there are enough columns
ch := colHeights[k]
if ch+widg.Height <= maxHeight {
widget := makeWidget(c, widg)
columns[k] = append(columns[k], ui.NewRow(float64(widg.Height)/float64(maxHeight), widget))
colHeights[k] += widg.Height
placed = true
break
}
}
// If all columns are full, break out, return the row, and continue processing
if !placed {
rowDefs = processing[i:]
break outer
}
}
}
var uiColumns []interface{}
@ -121,7 +130,7 @@ func processRow(c gotop.Config, numRows int, rowDefs [][]widgetRule) (ui.GridIte
}
}
return ui.NewRow(1.0/float64(numRows), uiColumns...), rowDefs
return uiColumns, rowDefs
}
type Metric interface {

View File

@ -101,6 +101,33 @@ func TestParsing(t *testing.T) {
assert.Equal(t, 1, l.Rows[1][0].Height)
assert.Equal(t, 1.0, l.Rows[1][0].Weight)
}},
{"cpu/2 mem/1 6:procs\n3:temp/1 2:disk/2\npower\nnet procs", func(l layout) {
assert.Equal(t, 4, len(l.Rows))
// First row
assert.Equal(t, 3, len(l.Rows[0]))
assert.Equal(t, 1, l.Rows[0][0].Height)
assert.Equal(t, 0.5, l.Rows[0][0].Weight)
assert.Equal(t, 1, l.Rows[0][1].Height)
assert.Equal(t, 0.25, l.Rows[0][1].Weight)
assert.Equal(t, 6, l.Rows[0][2].Height)
assert.Equal(t, 0.25, l.Rows[0][2].Weight)
// Second row
assert.Equal(t, 2, len(l.Rows[1]))
assert.Equal(t, 3, l.Rows[1][0].Height)
assert.Equal(t, 1/3.0, l.Rows[1][0].Weight)
assert.Equal(t, 2, l.Rows[1][1].Height)
assert.Equal(t, 2/3.0, l.Rows[1][1].Weight)
// Third row
assert.Equal(t, 1, len(l.Rows[2]))
assert.Equal(t, 1, l.Rows[2][0].Height)
assert.Equal(t, 1.0, l.Rows[2][0].Weight)
// Fourth row
assert.Equal(t, 2, len(l.Rows[3]))
assert.Equal(t, 1, l.Rows[3][0].Height)
assert.Equal(t, 0.5, l.Rows[3][0].Weight)
assert.Equal(t, 1, l.Rows[3][1].Height)
assert.Equal(t, 0.5, l.Rows[3][1].Weight)
}},
}
for _, tc := range tests {

View File

@ -1,5 +1,4 @@
cpu/2 mem/1
3:temp/1 2:disk/2
power
power
net procs

View File

@ -0,0 +1,4 @@
cpu/2 mem/1 6:procs/2
3:temp/1 2:disk/2
power
net procs