145 lines
3.5 KiB
Go
145 lines
3.5 KiB
Go
package cui
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"strings"
|
|
)
|
|
|
|
// screenInit records whether or not the screen has been initialized
|
|
// this is used to prevent more than one screen from being used
|
|
var screenInit bool = false
|
|
|
|
// Screen represent the top level abstraction for a cui application.
|
|
// It takes up the full width and height of the terminal window and
|
|
// holds the various Windows and MsgBars for the application as well
|
|
// as a record of which window is active for control purposes.
|
|
type Screen struct {
|
|
Height int
|
|
Width int
|
|
Windows []*Window
|
|
Activewindow int
|
|
Bars []*MsgBar
|
|
}
|
|
|
|
// AddWindow adds a new window to the Screen struct in question
|
|
func (s *Screen) AddWindow(r1, c1, r2, c2 int, scroll, border, show bool) {
|
|
w := Window{box{r1, c1, r2, c2}, scroll, 0, []string{}, border, false, show, 1}
|
|
s.Windows = append(s.Windows, &w)
|
|
}
|
|
|
|
// AddMsgBar adds a new MsgBar to the Screen struct in question
|
|
func (s *Screen) AddMsgBar(row int, title, msg string, showTitle bool) {
|
|
b := MsgBar{row, title, msg, showTitle}
|
|
s.Bars = append(s.Bars, &b)
|
|
}
|
|
|
|
// DrawAllWindows loops over every window in the Screen struct and
|
|
// draws it to screen in index order (smallest to largest)
|
|
func (s Screen) DrawAllWindows() {
|
|
for _, w := range s.Windows {
|
|
if w.Show {
|
|
w.DrawWindow()
|
|
}
|
|
}
|
|
MoveCursorTo(s.Height-1, 1)
|
|
}
|
|
|
|
// Clear removes all content from the interior of the screen
|
|
func (s Screen) Clear() {
|
|
for i := 0; i <= s.Height; i++ {
|
|
MoveCursorTo(i, 0)
|
|
Clear("line")
|
|
}
|
|
}
|
|
|
|
// Clears message/error/command area
|
|
func (s *Screen) ClearCommandArea() {
|
|
MoveCursorTo(s.Height-1, 1)
|
|
Clear("line")
|
|
MoveCursorTo(s.Height, 1)
|
|
Clear("line")
|
|
MoveCursorTo(s.Height-1, 1)
|
|
}
|
|
|
|
// ReflashScreen checks for a screen resize and resizes windows if
|
|
// needed then redraws the screen. It takes a bool to decide whether
|
|
// to redraw the full screen or just the content. On a resize
|
|
// event, the full screen will always be redrawn.
|
|
func (s *Screen) ReflashScreen(clearScreen bool) {
|
|
s.DrawAllWindows()
|
|
|
|
if clearScreen {
|
|
s.DrawMsgBars()
|
|
s.ClearCommandArea()
|
|
}
|
|
}
|
|
|
|
// DrawMsgBars draws all MsgBars present in the Screen struct.
|
|
// All MsgBars are looped over and drawn in index order (sm - lg).
|
|
func (s *Screen) DrawMsgBars() {
|
|
for _, bar := range s.Bars {
|
|
fmt.Print("\033[7m")
|
|
var buf bytes.Buffer
|
|
title := bar.title
|
|
if len(bar.title) > s.Width {
|
|
title = string(bar.title[:s.Width-3]) + "..."
|
|
}
|
|
_, _ = buf.WriteString(title)
|
|
msg := bar.message
|
|
if len(bar.message) > s.Width-len(title) {
|
|
msg = string(bar.message[:s.Width-len(title)-3]) + "..."
|
|
}
|
|
_, _ = buf.WriteString(msg)
|
|
if buf.Len() < s.Width {
|
|
wsLength := s.Width - buf.Len()
|
|
_,_ = buf.WriteString(strings.Repeat(" ", wsLength))
|
|
}
|
|
|
|
MoveCursorTo(bar.row, 1)
|
|
fmt.Print(buf.String())
|
|
fmt.Print("\033[0m")
|
|
|
|
}
|
|
}
|
|
|
|
// GetSize retrieves the terminal size and sets the Screen
|
|
// width and height to that size
|
|
func (s *Screen) GetSize() {
|
|
cmd := exec.Command("stty", "size")
|
|
cmd.Stdin = os.Stdin
|
|
out, err := cmd.Output()
|
|
if err != nil {
|
|
fmt.Println("Fatal error: Unable to retrieve terminal size")
|
|
os.Exit(1)
|
|
}
|
|
var h, w int
|
|
fmt.Sscan(string(out), &h, &w)
|
|
s.Height = h
|
|
s.Width = w
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
// NewScreen is a constructor function that returns a pointer
|
|
// to a Screen struct
|
|
func NewScreen() *Screen {
|
|
if screenInit {
|
|
fmt.Println("Fatal error: Cannot create multiple screens")
|
|
os.Exit(1)
|
|
}
|
|
var s Screen
|
|
s.GetSize()
|
|
for i := 0; i < s.Height; i++ {
|
|
fmt.Println()
|
|
}
|
|
|
|
SetCharMode()
|
|
|
|
Clear("screen")
|
|
screenInit = true
|
|
return &s
|
|
}
|