Skip to content

Commit f3c259d

Browse files
authored
Merge pull request os-autoinst#89 from grisu48/tui
Improve the screen behaviour of revtui
2 parents ee73434 + 656077a commit f3c259d

File tree

2 files changed

+131
-63
lines changed

2 files changed

+131
-63
lines changed

cmd/openqa-revtui/openqa-revtui.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import (
1111
"github.com/grisu48/gopenqa"
1212
)
1313

14-
const VERSION = "0.3.4"
14+
const VERSION = "0.4.0"
1515

1616
/* Group is a single configurable monitoring unit. A group contains all parameters that will be queried from openQA */
1717
type Group struct {
@@ -350,7 +350,6 @@ func parseProgramArgs() error {
350350
}
351351

352352
func printUsage() {
353-
// TODO: Write this
354353
fmt.Printf("Usage: %s [OPTIONS] [FLAVORS]\n", os.Args[0])
355354
fmt.Println("")
356355
fmt.Println("OPTIONS")
@@ -559,6 +558,7 @@ func tui_main(tui *TUI, instance gopenqa.Instance) error {
559558
}
560559
knownJobs = jobs
561560
tui.Model.Apply(knownJobs)
561+
fmt.Println("Initial fetching completed. Entering main loop ... ")
562562
tui.Start()
563563
tui.Update()
564564

cmd/openqa-revtui/tui.go

Lines changed: 129 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ type TUI struct {
5454
showTracker bool // Show tracker
5555
showStatus bool // Show status line
5656
sorting int // Sorting method - 0: none, 1 - by job group
57+
58+
screensize int // Lines per screen
5759
}
5860

5961
func CreateTUI() TUI {
@@ -175,7 +177,6 @@ func (tui *TUI) SetHeader(header string) {
175177
}
176178

177179
func (tui *TUI) readInput() {
178-
// TODO: Find a way to read raw without ENTER
179180
var b []byte = make([]byte, 1)
180181
var p = make([]byte, 3) // History, needed for special keys
181182
for {
@@ -197,26 +198,22 @@ func (tui *TUI) readInput() {
197198
tui.Update()
198199
}
199200
} else if p[1] == 91 && k == 66 { // Arrow down
200-
_, height := terminalSize()
201-
max := max(0, (tui.Model.printLines - height + 7))
201+
max := max(0, (tui.Model.printLines - tui.screensize))
202202
if tui.Model.offset < max {
203203
tui.Model.offset++
204204
tui.Update()
205205
}
206206
} else if p[2] == 27 && p[1] == 91 && p[0] == 72 { // home
207207
tui.Model.offset = 0
208208
} else if p[2] == 27 && p[1] == 91 && p[0] == 70 { // end
209-
_, height := terminalSize()
210-
tui.Model.offset = max(0, (tui.Model.printLines - height + 7))
209+
tui.Model.offset = max(0, (tui.Model.printLines - tui.screensize))
211210
} else if p[2] == 27 && p[1] == 91 && p[0] == 53 { // page up
212-
_, height := terminalSize()
213-
scroll := max(1, height-5)
214-
tui.Model.offset = max(0, tui.Model.offset-scroll)
211+
// Always leave one line overlap for better orientation
212+
tui.Model.offset = max(0, tui.Model.offset-tui.screensize+1)
215213
} else if p[2] == 27 && p[1] == 91 && p[0] == 54 { // page down
216-
_, height := terminalSize()
217-
scroll := max(1, height-5)
218-
max := max(0, (tui.Model.printLines - height + 7))
219-
tui.Model.offset = min(max, tui.Model.offset+scroll)
214+
max := max(0, (tui.Model.printLines - tui.screensize))
215+
// Always leave one line overlap for better orientation
216+
tui.Model.offset = min(max, tui.Model.offset+tui.screensize-1)
220217
}
221218

222219
// Forward keypress to listener
@@ -287,16 +284,14 @@ func (tui *TUI) hideJob(job gopenqa.Job) bool {
287284
}
288285

289286
// print all jobs unsorted
290-
func (tui *TUI) printJobs(width, height int) {
291-
line := 0
287+
func (tui *TUI) buildJobsScreen(width int) []string {
288+
lines := make([]string, 0)
292289
for _, job := range tui.Model.jobs {
293290
if !tui.hideJob(job) {
294-
if line++; line > tui.Model.offset {
295-
fmt.Println(tui.formatJobLine(job, width))
296-
}
291+
lines = append(lines, tui.formatJobLine(job, width))
297292
}
298293
}
299-
tui.Model.printLines = len(tui.Model.jobs)
294+
return lines
300295
}
301296

302297
func sortedKeys(vals map[string]int) []string {
@@ -311,7 +306,9 @@ func sortedKeys(vals map[string]int) []string {
311306
return ret
312307
}
313308

314-
func (tui *TUI) printJobsByGroup(width, height int) int {
309+
func (tui *TUI) buildJobsScreenByGroup(width int) []string {
310+
lines := make([]string, 0)
311+
315312
// Determine active groups first
316313
groups := make(map[int][]gopenqa.Job, 0)
317314
for _, job := range tui.Model.jobs {
@@ -327,14 +324,20 @@ func (tui *TUI) printJobsByGroup(width, height int) int {
327324
grpIDs = append(grpIDs, k)
328325
}
329326
sort.Ints(grpIDs)
327+
330328
// Now print them sorted by group ID
331-
lines := make([]string, 0)
329+
first := true
332330
for _, id := range grpIDs {
333331
grp := tui.Model.jobGroups[id]
334332
jobs := groups[id]
335333
statC := make(map[string]int, 0)
336334
hidden := 0
337-
lines = append(lines, fmt.Sprintf("\n===== %s ====================", grp.Name))
335+
if first {
336+
first = false
337+
} else {
338+
lines = append(lines, "")
339+
}
340+
lines = append(lines, fmt.Sprintf("===== %s ====================", grp.Name))
338341
for _, job := range jobs {
339342
if !tui.hideJob(job) {
340343
lines = append(lines, tui.formatJobLine(job, width))
@@ -360,21 +363,7 @@ func (tui *TUI) printJobsByGroup(width, height int) int {
360363
}
361364
lines = append(lines, line)
362365
}
363-
364-
// For scrolling, remember the total number of lines
365-
tui.Model.printLines = len(lines)
366-
367-
// Print relevant lines, taking the offset into consideration
368-
counter := 0
369-
for i := 0; i < height; i++ {
370-
if (tui.Model.offset + i) >= len(lines) {
371-
break
372-
} else {
373-
fmt.Println(lines[tui.Model.offset+i])
374-
counter++
375-
}
376-
}
377-
return counter
366+
return lines
378367
}
379368

380369
func cut(text string, n int) string {
@@ -384,26 +373,42 @@ func cut(text string, n int) string {
384373
return text[:n]
385374
}
386375
}
376+
func trimEmptyTail(lines []string) []string {
377+
// Crop empty elements at the end of the array
378+
for n := len(lines) - 1; n > 0; n-- {
379+
if lines[n] != "" {
380+
return lines[0 : n+1]
381+
}
382+
}
383+
return lines[0:0]
384+
}
387385

388-
/* Redraw screen */
389-
func (tui *TUI) Update() {
390-
tui.Model.mutex.Lock()
391-
defer tui.Model.mutex.Unlock()
392-
width, height := terminalSize()
393-
if width < 0 || height < 0 {
394-
return
386+
func trimEmptyHead(lines []string) []string {
387+
// Crop empty elements at the end of the array
388+
for i := 0; i < len(lines); i++ {
389+
if lines[i] != "" {
390+
return lines[i:]
391+
}
395392
}
393+
return lines[0:0]
394+
}
396395

397-
remainingHeight := height // remaining rows in the current terminal
398-
tui.Clear()
396+
func trimEmpty(lines []string) []string {
397+
lines = trimEmptyHead(lines)
398+
lines = trimEmptyTail(lines)
399+
return lines
400+
}
401+
402+
func (tui *TUI) buildHeader(width int) []string {
403+
lines := make([]string, 0)
399404
if tui.header != "" {
400-
fmt.Println(tui.header)
401-
fmt.Println("q:Quit r:Refresh h:Hide/Show jobs m:Toggle RabbitMQ tracker s:Switch sorting Arrows:Move up/down")
402-
fmt.Println()
403-
remainingHeight -= 4
405+
lines = append(lines, tui.header)
406+
lines = append(lines, "q:Quit r:Refresh h:Hide/Show jobs m:Toggle RabbitMQ tracker s:Switch sorting Arrows:Move up/down")
404407
}
408+
return lines
409+
}
405410

406-
// The footer is a bit more complex, build it here, so we know how many more rows are occupied
411+
func (tui *TUI) buildFooter(width int) []string {
407412
footer := make([]string, 0)
408413
showStatus := tui.showStatus && tui.status != ""
409414
showTracker := tui.showTracker && tui.tracker != ""
@@ -429,25 +434,81 @@ func (tui *TUI) Update() {
429434
footer = append(footer, tui.tracker[:width])
430435
}
431436
}
432-
if len(footer) > 0 {
433-
remainingHeight -= (len(footer) + 2)
434-
}
437+
return footer
438+
}
439+
440+
// Build the full screen
441+
func (tui *TUI) buildScreen(width int) []string {
442+
lines := make([]string, 0)
435443

436-
// Job listing depends on selected sorting method
437-
remainingHeight -= 2 // printJobs and printJobsByGroup terminate with a newline
438444
switch tui.sorting {
439445
case 1:
440-
tui.printJobsByGroup(width, remainingHeight)
441-
break
446+
lines = append(lines, tui.buildJobsScreenByGroup(width)...)
442447
default:
443-
tui.printJobs(width, remainingHeight)
444-
break
448+
lines = append(lines, tui.buildJobsScreen(width)...)
449+
}
450+
lines = trimEmpty(lines)
451+
452+
tui.Model.printLines = len(lines)
453+
return lines
454+
}
455+
456+
/* Redraw screen */
457+
func (tui *TUI) Update() {
458+
tui.Model.mutex.Lock()
459+
defer tui.Model.mutex.Unlock()
460+
width, height := terminalSize()
461+
if width < 0 || height < 0 {
462+
return
445463
}
446464

465+
// Header and footer are separate. We only scroll through the "screen"
466+
screen := tui.buildScreen(width)
467+
header := tui.buildHeader(width)
468+
footer := tui.buildFooter(width)
469+
470+
remainingLines := height
471+
tui.Clear()
472+
473+
// Print header
474+
if len(header) > 0 {
475+
header = append(header, "") // Add additional line after header
476+
477+
for _, line := range header {
478+
fmt.Println(line)
479+
remainingLines--
480+
if remainingLines <= 0 {
481+
return // crap. no need to continue
482+
}
483+
}
484+
}
485+
486+
// Reserve lines for footer, but spare footer if there is no space left
487+
if len(footer) > remainingLines {
488+
footer = make([]string, 0)
489+
} else {
490+
remainingLines -= (len(footer) + 1)
491+
}
492+
493+
// Print screen
494+
screensize := 0
495+
for elem := tui.Model.offset; remainingLines > 0; remainingLines-- {
496+
if elem >= len(screen) {
497+
fmt.Println("") // Fill screen with empty lines for alignment
498+
} else {
499+
//fmt.Println(strings.TrimSpace(screen[elem])) // XXX
500+
fmt.Println(screen[elem])
501+
elem++
502+
}
503+
screensize++
504+
}
505+
tui.screensize = screensize
506+
507+
// Print footer
447508
if len(footer) > 0 {
448-
fmt.Println()
509+
fmt.Println("")
449510
for _, line := range footer {
450-
fmt.Printf("\n%s", line)
511+
fmt.Println(line)
451512
}
452513
}
453514
}
@@ -516,6 +577,13 @@ func (tui *TUI) formatJobLine(job gopenqa.Job, width int) string {
516577
}
517578
}
518579

580+
// Crop the state field, if necessary
581+
if state == "timeout_exceeded" {
582+
state = "timeout"
583+
} else if len(state) > 12 {
584+
state = state[0:12]
585+
}
586+
519587
// Full status line requires 89 characters (20+4+8+1+12+1+40+3) plus name
520588
if width > 90 {
521589
// Crop the name, if necessary

0 commit comments

Comments
 (0)