@@ -54,6 +54,8 @@ type TUI struct {
54
54
showTracker bool // Show tracker
55
55
showStatus bool // Show status line
56
56
sorting int // Sorting method - 0: none, 1 - by job group
57
+
58
+ screensize int // Lines per screen
57
59
}
58
60
59
61
func CreateTUI () TUI {
@@ -175,7 +177,6 @@ func (tui *TUI) SetHeader(header string) {
175
177
}
176
178
177
179
func (tui * TUI ) readInput () {
178
- // TODO: Find a way to read raw without ENTER
179
180
var b []byte = make ([]byte , 1 )
180
181
var p = make ([]byte , 3 ) // History, needed for special keys
181
182
for {
@@ -197,26 +198,22 @@ func (tui *TUI) readInput() {
197
198
tui .Update ()
198
199
}
199
200
} 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 ))
202
202
if tui .Model .offset < max {
203
203
tui .Model .offset ++
204
204
tui .Update ()
205
205
}
206
206
} else if p [2 ] == 27 && p [1 ] == 91 && p [0 ] == 72 { // home
207
207
tui .Model .offset = 0
208
208
} 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 ))
211
210
} 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 )
215
213
} 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 )
220
217
}
221
218
222
219
// Forward keypress to listener
@@ -287,16 +284,14 @@ func (tui *TUI) hideJob(job gopenqa.Job) bool {
287
284
}
288
285
289
286
// 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 )
292
289
for _ , job := range tui .Model .jobs {
293
290
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 ))
297
292
}
298
293
}
299
- tui . Model . printLines = len ( tui . Model . jobs )
294
+ return lines
300
295
}
301
296
302
297
func sortedKeys (vals map [string ]int ) []string {
@@ -311,7 +306,9 @@ func sortedKeys(vals map[string]int) []string {
311
306
return ret
312
307
}
313
308
314
- func (tui * TUI ) printJobsByGroup (width , height int ) int {
309
+ func (tui * TUI ) buildJobsScreenByGroup (width int ) []string {
310
+ lines := make ([]string , 0 )
311
+
315
312
// Determine active groups first
316
313
groups := make (map [int ][]gopenqa.Job , 0 )
317
314
for _ , job := range tui .Model .jobs {
@@ -327,14 +324,20 @@ func (tui *TUI) printJobsByGroup(width, height int) int {
327
324
grpIDs = append (grpIDs , k )
328
325
}
329
326
sort .Ints (grpIDs )
327
+
330
328
// Now print them sorted by group ID
331
- lines := make ([] string , 0 )
329
+ first := true
332
330
for _ , id := range grpIDs {
333
331
grp := tui .Model .jobGroups [id ]
334
332
jobs := groups [id ]
335
333
statC := make (map [string ]int , 0 )
336
334
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 ))
338
341
for _ , job := range jobs {
339
342
if ! tui .hideJob (job ) {
340
343
lines = append (lines , tui .formatJobLine (job , width ))
@@ -360,21 +363,7 @@ func (tui *TUI) printJobsByGroup(width, height int) int {
360
363
}
361
364
lines = append (lines , line )
362
365
}
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
378
367
}
379
368
380
369
func cut (text string , n int ) string {
@@ -384,26 +373,42 @@ func cut(text string, n int) string {
384
373
return text [:n ]
385
374
}
386
375
}
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
+ }
387
385
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
+ }
395
392
}
393
+ return lines [0 :0 ]
394
+ }
396
395
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 )
399
404
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" )
404
407
}
408
+ return lines
409
+ }
405
410
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 {
407
412
footer := make ([]string , 0 )
408
413
showStatus := tui .showStatus && tui .status != ""
409
414
showTracker := tui .showTracker && tui .tracker != ""
@@ -429,25 +434,81 @@ func (tui *TUI) Update() {
429
434
footer = append (footer , tui .tracker [:width ])
430
435
}
431
436
}
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 )
435
443
436
- // Job listing depends on selected sorting method
437
- remainingHeight -= 2 // printJobs and printJobsByGroup terminate with a newline
438
444
switch tui .sorting {
439
445
case 1 :
440
- tui .printJobsByGroup (width , remainingHeight )
441
- break
446
+ lines = append (lines , tui .buildJobsScreenByGroup (width )... )
442
447
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
445
463
}
446
464
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
447
508
if len (footer ) > 0 {
448
- fmt .Println ()
509
+ fmt .Println ("" )
449
510
for _ , line := range footer {
450
- fmt .Printf ( " \n %s" , line )
511
+ fmt .Println ( line )
451
512
}
452
513
}
453
514
}
@@ -516,6 +577,13 @@ func (tui *TUI) formatJobLine(job gopenqa.Job, width int) string {
516
577
}
517
578
}
518
579
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
+
519
587
// Full status line requires 89 characters (20+4+8+1+12+1+40+3) plus name
520
588
if width > 90 {
521
589
// Crop the name, if necessary
0 commit comments