Well.. I created some sort of tui for GNU/Linux
This commit is contained in:
parent
db5136a33e
commit
544bf2b219
47
tui/consts.go
Normal file
47
tui/consts.go
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
package tui
|
||||||
|
|
||||||
|
const (
|
||||||
|
MY_SIGNAL_EXIT = iota
|
||||||
|
MY_SIGNAL_MESSAGE
|
||||||
|
MY_SIGNAL_CONNECT
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
cursorPosGeneralCenter cursorPosConfigValue = -1
|
||||||
|
cursorPosGeneralLeft cursorPosConfigValue = -2
|
||||||
|
cursorPosGeneralRight cursorPosConfigValue = -3
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
footerStart = "State: "
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *cursorPosConfigValue) isGeneral() bool {
|
||||||
|
switch *c {
|
||||||
|
case cursorPosGeneralCenter:
|
||||||
|
return true
|
||||||
|
case cursorPosGeneralLeft:
|
||||||
|
return true
|
||||||
|
case cursorPosGeneralRight:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
widgetPosGeneralCenter widgetPosConfigValue = -1
|
||||||
|
widgetPosGeneralLeftCenter widgetPosConfigValue = -2
|
||||||
|
widgetPosGeneralRightCenter widgetPosConfigValue = -3
|
||||||
|
)
|
||||||
|
|
||||||
|
func (w *widgetPosConfigValue) isGeneral() bool {
|
||||||
|
switch *w {
|
||||||
|
case widgetPosGeneralCenter:
|
||||||
|
return true
|
||||||
|
case widgetPosGeneralLeftCenter:
|
||||||
|
return true
|
||||||
|
case widgetPosGeneralRightCenter:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
101
tui/funcs.go
Normal file
101
tui/funcs.go
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
package tui
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"git.qowevisa.me/Qowevisa/gotell/env"
|
||||||
|
"git.qowevisa.me/Qowevisa/gotell/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetIntPercentFromData(a, b int) int {
|
||||||
|
return int(float64((a * 100)) / float64(b))
|
||||||
|
}
|
||||||
|
|
||||||
|
func SendMessageToConnectionEasy(msg *[]rune) (dataProcessHandler, dataT) {
|
||||||
|
return SendMessageToConnection, dataT{rawP: msg}
|
||||||
|
}
|
||||||
|
|
||||||
|
func SendMessageToConnection(t *TUI, data dataT) error {
|
||||||
|
if t.tlsConnection == nil {
|
||||||
|
return errors.WrapErr("t.tlsConnection", errors.NOT_SET)
|
||||||
|
}
|
||||||
|
if data.rawP == nil {
|
||||||
|
return errors.WrapErr("data.rawP", errors.NOT_SET)
|
||||||
|
}
|
||||||
|
message := string(*data.rawP)
|
||||||
|
n, err := t.tlsConnection.Write([]byte(message))
|
||||||
|
if err != nil {
|
||||||
|
return errors.WrapErr("t.tlsConnection.Write", err)
|
||||||
|
}
|
||||||
|
log.Printf("Successfully wrote %d bytes to connection; Message: %s", n, message)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// takes data from storage
|
||||||
|
func FE_ConnectTLS(t *TUI, data dataT) error {
|
||||||
|
log.Printf("Start of FE_ConnectTLS")
|
||||||
|
host, exist := t.storage[STORAGE_HOST_CONST]
|
||||||
|
if !exist {
|
||||||
|
errors.WrapErr("t.storage:host", errors.NOT_SET)
|
||||||
|
}
|
||||||
|
portStr, exist := t.storage[STORAGE_PORT_CONST]
|
||||||
|
if !exist {
|
||||||
|
errors.WrapErr("t.storage:host", errors.NOT_SET)
|
||||||
|
}
|
||||||
|
port, err := strconv.ParseInt(portStr, 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
errors.WrapErr("port.strconv.ParseInt", err)
|
||||||
|
}
|
||||||
|
loadingFileName := env.ServerFullchainFileName
|
||||||
|
cert, err := os.ReadFile(loadingFileName)
|
||||||
|
if err != nil {
|
||||||
|
errors.WrapErr("os.ReadFile", err)
|
||||||
|
}
|
||||||
|
log.Printf("Certificate %s loaded successfully!\n", loadingFileName)
|
||||||
|
//
|
||||||
|
roots := x509.NewCertPool()
|
||||||
|
if ok := roots.AppendCertsFromPEM(cert); !ok {
|
||||||
|
errors.WrapErr("client: failed to parse root certificate", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
config := &tls.Config{
|
||||||
|
RootCAs: roots,
|
||||||
|
}
|
||||||
|
conn, err := tls.Dial(
|
||||||
|
"tcp",
|
||||||
|
fmt.Sprintf("%s:%d", host, int(port)),
|
||||||
|
config,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return errors.WrapErr("tls.Dial", err)
|
||||||
|
}
|
||||||
|
// log.Printf("Set connection to %#v\n", conn)
|
||||||
|
t.tlsConnection = conn
|
||||||
|
if t.stateChannel == nil {
|
||||||
|
return errors.WrapErr("t.stateChannel", errors.NOT_INIT)
|
||||||
|
}
|
||||||
|
t.stateChannel <- "TLS Connected"
|
||||||
|
t.isConnected = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddToStorageEasy(key string, val *[]rune) (dataProcessHandler, dataT) {
|
||||||
|
// that's why I create wrapper around it.
|
||||||
|
// try to understand that, dear viewer!
|
||||||
|
return H_AddToStorage, dataT{rawP: val, op1: key}
|
||||||
|
}
|
||||||
|
|
||||||
|
func H_AddToStorage(t *TUI, data dataT) error {
|
||||||
|
log.Printf("Debug: %#v", data)
|
||||||
|
log.Printf("Adding to storage: %s = %s", data.op1, string(*data.rawP))
|
||||||
|
if t.storage == nil {
|
||||||
|
return errors.WrapErr("t.storage", errors.NOT_INIT)
|
||||||
|
}
|
||||||
|
t.storage[data.op1] = string(*data.rawP)
|
||||||
|
return nil
|
||||||
|
}
|
1
tui/input.go
Normal file
1
tui/input.go
Normal file
|
@ -0,0 +1 @@
|
||||||
|
package tui
|
111
tui/notification.go
Normal file
111
tui/notification.go
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
package tui
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"git.qowevisa.me/Qowevisa/gotell/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
func centerText(width int, text string) string {
|
||||||
|
emptyLen := width - len(text) - 2
|
||||||
|
leftEmptyLen := emptyLen / 2
|
||||||
|
return fmt.Sprintf(
|
||||||
|
"|%s%s%s|",
|
||||||
|
strings.Repeat(" ", leftEmptyLen),
|
||||||
|
text,
|
||||||
|
strings.Repeat(" ", emptyLen-leftEmptyLen),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func createNotification(message string) (notifier, error) {
|
||||||
|
title := "ERROR"
|
||||||
|
var buf string
|
||||||
|
width, height := UI.getSizes()
|
||||||
|
if width == 0 {
|
||||||
|
return notifier{}, errors.WrapErr("width", errors.NOT_INIT)
|
||||||
|
}
|
||||||
|
if height == 0 {
|
||||||
|
return notifier{}, errors.WrapErr("height", errors.NOT_INIT)
|
||||||
|
}
|
||||||
|
maxWidth := width / 3
|
||||||
|
maxHeight := 5
|
||||||
|
errMsgLen := len(message)
|
||||||
|
innerPart := maxWidth - 2
|
||||||
|
if errMsgLen <= innerPart {
|
||||||
|
maxWidth = errMsgLen + 2
|
||||||
|
} else {
|
||||||
|
for {
|
||||||
|
if errMsgLen <= innerPart {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
maxHeight++
|
||||||
|
errMsgLen -= innerPart
|
||||||
|
}
|
||||||
|
}
|
||||||
|
innerPart = maxWidth - 2
|
||||||
|
col := (width - maxWidth) / 2
|
||||||
|
row := (height - maxHeight) / 2
|
||||||
|
startCol := col
|
||||||
|
startRow := row
|
||||||
|
|
||||||
|
buf += getBufForMovingCursorTo(row, col)
|
||||||
|
buf += strings.Repeat("-", maxWidth)
|
||||||
|
|
||||||
|
row++
|
||||||
|
buf += getBufForMovingCursorTo(row, col)
|
||||||
|
buf += centerText(maxWidth, title)
|
||||||
|
|
||||||
|
row++
|
||||||
|
buf += getBufForMovingCursorTo(row, col)
|
||||||
|
buf += strings.Repeat("-", maxWidth)
|
||||||
|
|
||||||
|
startI := 0
|
||||||
|
endI := innerPart
|
||||||
|
for i := 3; i < maxHeight-1; i++ {
|
||||||
|
var tmp string
|
||||||
|
if endI > len(message) {
|
||||||
|
tmp = message[startI:]
|
||||||
|
} else {
|
||||||
|
tmp = message[startI:endI]
|
||||||
|
}
|
||||||
|
row++
|
||||||
|
buf += getBufForMovingCursorTo(row, col)
|
||||||
|
var spaces string
|
||||||
|
if innerPart > len(tmp) {
|
||||||
|
spaces = strings.Repeat(" ", innerPart-len(tmp))
|
||||||
|
}
|
||||||
|
buf += fmt.Sprintf("|%s%s|", tmp, spaces)
|
||||||
|
startI += innerPart
|
||||||
|
endI += innerPart
|
||||||
|
}
|
||||||
|
|
||||||
|
row++
|
||||||
|
buf += getBufForMovingCursorTo(row, col)
|
||||||
|
buf += strings.Repeat("-", maxWidth)
|
||||||
|
|
||||||
|
row++
|
||||||
|
buf += getBufForMovingCursorTo(row, col)
|
||||||
|
buf += centerText(maxWidth, "OK")
|
||||||
|
|
||||||
|
row++
|
||||||
|
buf += getBufForMovingCursorTo(row, col)
|
||||||
|
buf += strings.Repeat("-", maxWidth)
|
||||||
|
return notifier{
|
||||||
|
Row: startRow,
|
||||||
|
Col: startCol,
|
||||||
|
Width: maxWidth,
|
||||||
|
Height: row - startRow + 1,
|
||||||
|
Buf: buf,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *notifier) Clear() string {
|
||||||
|
var buf string
|
||||||
|
for i := 0; i < n.Height; i++ {
|
||||||
|
buf += getBufForMovingCursorTo(n.Row, n.Col)
|
||||||
|
buf += strings.Repeat(" ", n.Width)
|
||||||
|
n.Row++
|
||||||
|
}
|
||||||
|
return buf
|
||||||
|
}
|
6
tui/storage_consts.go
Normal file
6
tui/storage_consts.go
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
package tui
|
||||||
|
|
||||||
|
const (
|
||||||
|
STORAGE_HOST_CONST = "host"
|
||||||
|
STORAGE_PORT_CONST = "port"
|
||||||
|
)
|
30
tui/table.go
Normal file
30
tui/table.go
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
package tui
|
||||||
|
|
||||||
|
const (
|
||||||
|
CTRL_A = 1 << iota
|
||||||
|
CTRL_B
|
||||||
|
CTRL_C
|
||||||
|
CTRL_D
|
||||||
|
CTRL_E
|
||||||
|
CTRL_F
|
||||||
|
CTRL_G
|
||||||
|
CTRL_H
|
||||||
|
CTRL_I
|
||||||
|
CTRL_J
|
||||||
|
CTRL_K
|
||||||
|
CTRL_L
|
||||||
|
CTRL_M
|
||||||
|
CTRL_N
|
||||||
|
CTRL_O
|
||||||
|
CTRL_P
|
||||||
|
CTRL_Q
|
||||||
|
CTRL_R
|
||||||
|
CTRL_S
|
||||||
|
CTRL_T
|
||||||
|
CTRL_U
|
||||||
|
CTRL_V
|
||||||
|
CTRL_W
|
||||||
|
CTRL_X
|
||||||
|
CTRL_Y
|
||||||
|
CTRL_Z
|
||||||
|
)
|
110
tui/types.go
Normal file
110
tui/types.go
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
package tui
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"crypto/tls"
|
||||||
|
"os"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"golang.org/x/term"
|
||||||
|
)
|
||||||
|
|
||||||
|
type mySignal struct {
|
||||||
|
Type int
|
||||||
|
}
|
||||||
|
|
||||||
|
var UI TUI
|
||||||
|
|
||||||
|
type dataT struct {
|
||||||
|
raw string
|
||||||
|
rawP *[]rune
|
||||||
|
op1 string
|
||||||
|
op2 string
|
||||||
|
ops []string
|
||||||
|
}
|
||||||
|
|
||||||
|
type dataProcessHandler func(t *TUI, data dataT) error
|
||||||
|
|
||||||
|
type tuiPointPair struct {
|
||||||
|
startCol int
|
||||||
|
startRow int
|
||||||
|
endCol int
|
||||||
|
endRow int
|
||||||
|
}
|
||||||
|
|
||||||
|
type widget struct {
|
||||||
|
row int
|
||||||
|
col int
|
||||||
|
MinWidth int
|
||||||
|
MinHeight int
|
||||||
|
Width int
|
||||||
|
Height int
|
||||||
|
Title string
|
||||||
|
Input *[]rune
|
||||||
|
Handler dataProcessHandler
|
||||||
|
Data dataT
|
||||||
|
Next *widgetConfig
|
||||||
|
Finale dataProcessHandler
|
||||||
|
FinaleData dataT
|
||||||
|
percentPair tuiPointPair
|
||||||
|
snappedPair tuiPointPair
|
||||||
|
startupConfig widgetConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
type notifier struct {
|
||||||
|
Row int
|
||||||
|
Col int
|
||||||
|
Width int
|
||||||
|
Height int
|
||||||
|
Buf string
|
||||||
|
}
|
||||||
|
|
||||||
|
type widgetDraw struct {
|
||||||
|
Buf string
|
||||||
|
Row int
|
||||||
|
Col int
|
||||||
|
}
|
||||||
|
|
||||||
|
type cursorPosConfigValue int
|
||||||
|
type widgetPosConfigValue int
|
||||||
|
|
||||||
|
type widgetConfig struct {
|
||||||
|
MinWidth int
|
||||||
|
MinHeight int
|
||||||
|
Input *[]rune
|
||||||
|
CursorPosConfig cursorPosConfigValue
|
||||||
|
Title string
|
||||||
|
WidgetPosConfig widgetPosConfigValue
|
||||||
|
HasBorder bool
|
||||||
|
DataHandler dataProcessHandler
|
||||||
|
Data dataT
|
||||||
|
Next *widgetConfig
|
||||||
|
Finale dataProcessHandler
|
||||||
|
FinaleData dataT
|
||||||
|
}
|
||||||
|
|
||||||
|
type TUI struct {
|
||||||
|
width int
|
||||||
|
height int
|
||||||
|
cursorPosRow int
|
||||||
|
cursorPosCol int
|
||||||
|
writeMu sync.Mutex
|
||||||
|
sizeMutex sync.Mutex
|
||||||
|
oldState *term.State
|
||||||
|
input chan (rune)
|
||||||
|
printRunes chan (rune)
|
||||||
|
mySignals chan (mySignal)
|
||||||
|
osSignals chan (os.Signal)
|
||||||
|
errors chan (error)
|
||||||
|
readInputState chan (bool)
|
||||||
|
readEnterState chan (bool)
|
||||||
|
stateChannel chan (string)
|
||||||
|
widgets []*widget
|
||||||
|
widgetsMutext sync.Mutex
|
||||||
|
writer *bufio.Writer
|
||||||
|
isConnected bool
|
||||||
|
selectedWidget *widget
|
||||||
|
selectedNotifier *notifier
|
||||||
|
storage map[string]string
|
||||||
|
tlsConnection *tls.Conn
|
||||||
|
}
|
536
tui/ui.go
Normal file
536
tui/ui.go
Normal file
|
@ -0,0 +1,536 @@
|
||||||
|
package tui
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"sync"
|
||||||
|
"syscall"
|
||||||
|
"unicode"
|
||||||
|
|
||||||
|
"git.qowevisa.me/Qowevisa/gotell/errors"
|
||||||
|
"golang.org/x/term"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (t *TUI) init() error {
|
||||||
|
var err error
|
||||||
|
t.input = make(chan rune, 32)
|
||||||
|
t.printRunes = make(chan rune, 32)
|
||||||
|
t.widgets = make([]*widget, 8)
|
||||||
|
t.errors = make(chan error, 4)
|
||||||
|
t.mySignals = make(chan mySignal, 1)
|
||||||
|
t.osSignals = make(chan os.Signal, 1)
|
||||||
|
t.writer = bufio.NewWriter(os.Stdout)
|
||||||
|
t.storage = make(map[string]string)
|
||||||
|
t.readInputState = make(chan bool, 1)
|
||||||
|
t.readEnterState = make(chan bool, 1)
|
||||||
|
t.stateChannel = make(chan string, 1)
|
||||||
|
signal.Notify(t.osSignals, syscall.SIGWINCH)
|
||||||
|
err = t.setSizes()
|
||||||
|
if err != nil {
|
||||||
|
return errors.WrapErr("t.getSizes", err)
|
||||||
|
}
|
||||||
|
err = t.setTermToRaw()
|
||||||
|
if err != nil {
|
||||||
|
return errors.WrapErr("t.setTermToRaw", err)
|
||||||
|
}
|
||||||
|
err = t.setRoutines()
|
||||||
|
if err != nil {
|
||||||
|
return errors.WrapErr("t.setRoutines", err)
|
||||||
|
}
|
||||||
|
err = t.readRoutines()
|
||||||
|
if err != nil {
|
||||||
|
return errors.WrapErr("t.readRoutines", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TUI) exit() {
|
||||||
|
if t.oldState != nil {
|
||||||
|
term.Restore(int(os.Stdin.Fd()), t.oldState)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TUI) Run() error {
|
||||||
|
defer t.exit()
|
||||||
|
var err error
|
||||||
|
err = t.init()
|
||||||
|
if err != nil {
|
||||||
|
return errors.WrapErr("t.init", err)
|
||||||
|
}
|
||||||
|
//
|
||||||
|
if t.mySignals == nil {
|
||||||
|
return errors.WrapErr("t.signals", errors.NOT_INIT)
|
||||||
|
}
|
||||||
|
err = t.Draw()
|
||||||
|
if err != nil {
|
||||||
|
return errors.WrapErr("t.Draw", err)
|
||||||
|
}
|
||||||
|
for mySignal := range t.mySignals {
|
||||||
|
log.Printf("Receive signal: %#v\n", mySignal)
|
||||||
|
if mySignal.Type == MY_SIGNAL_EXIT {
|
||||||
|
t.errors <- t.clearScreen()
|
||||||
|
t.errors <- t.moveCursor(0, 0)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
switch mySignal.Type {
|
||||||
|
case MY_SIGNAL_CONNECT:
|
||||||
|
var host []rune
|
||||||
|
var port []rune
|
||||||
|
hostHandler, hostData := AddToStorageEasy("host", &host)
|
||||||
|
portHandler, portData := AddToStorageEasy("port", &port)
|
||||||
|
err := t.addWidget(widgetConfig{
|
||||||
|
Input: &host,
|
||||||
|
Title: "Host",
|
||||||
|
MinWidth: 16,
|
||||||
|
HasBorder: true,
|
||||||
|
WidgetPosConfig: widgetPosGeneralCenter,
|
||||||
|
CursorPosConfig: cursorPosGeneralCenter,
|
||||||
|
DataHandler: hostHandler,
|
||||||
|
Data: hostData,
|
||||||
|
Finale: nil,
|
||||||
|
Next: &widgetConfig{
|
||||||
|
Input: &port,
|
||||||
|
Title: "Port",
|
||||||
|
MinWidth: 8,
|
||||||
|
HasBorder: true,
|
||||||
|
WidgetPosConfig: widgetPosGeneralCenter,
|
||||||
|
CursorPosConfig: cursorPosGeneralCenter,
|
||||||
|
DataHandler: portHandler,
|
||||||
|
Data: portData,
|
||||||
|
Next: nil,
|
||||||
|
Finale: FE_ConnectTLS,
|
||||||
|
FinaleData: dataT{},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.errors <- errors.WrapErr("t.addWidget", err)
|
||||||
|
}
|
||||||
|
err = t.drawSelectedWidget()
|
||||||
|
if err != nil {
|
||||||
|
t.errors <- errors.WrapErr("t.drawSelectedWidget", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
case MY_SIGNAL_MESSAGE:
|
||||||
|
if t.isConnected {
|
||||||
|
var msg []rune
|
||||||
|
h, d := SendMessageToConnectionEasy(&msg)
|
||||||
|
err := t.addWidget(widgetConfig{
|
||||||
|
Input: &msg,
|
||||||
|
Title: "Message",
|
||||||
|
MinWidth: 20,
|
||||||
|
HasBorder: true,
|
||||||
|
WidgetPosConfig: widgetPosGeneralCenter,
|
||||||
|
CursorPosConfig: cursorPosGeneralCenter,
|
||||||
|
DataHandler: h,
|
||||||
|
Data: d,
|
||||||
|
Next: nil,
|
||||||
|
Finale: nil,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.errors <- errors.WrapErr("t.addWidget", err)
|
||||||
|
}
|
||||||
|
err = t.drawSelectedWidget()
|
||||||
|
if err != nil {
|
||||||
|
t.errors <- errors.WrapErr("t.drawSelectedWidget", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TUI) createNotification(text string) {
|
||||||
|
notifier, err := createNotification(text)
|
||||||
|
t.selectedNotifier = ¬ifier
|
||||||
|
t.errors <- err
|
||||||
|
t.errors <- t.write(notifier.Buf)
|
||||||
|
t.readEnterState <- true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TUI) setRoutines() error {
|
||||||
|
if t.errors == nil {
|
||||||
|
return errors.WrapErr("t.errors", errors.NOT_INIT)
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
for err := range t.errors {
|
||||||
|
if err != nil {
|
||||||
|
t.createNotification(err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
if t.input == nil {
|
||||||
|
return errors.WrapErr("t.input", errors.NOT_INIT)
|
||||||
|
}
|
||||||
|
if t.oldState == nil {
|
||||||
|
return errors.WrapErr("t.oldState", errors.NOT_INIT)
|
||||||
|
}
|
||||||
|
if t.readInputState == nil {
|
||||||
|
return errors.WrapErr("t.readInputState", errors.NOT_INIT)
|
||||||
|
}
|
||||||
|
if t.readEnterState == nil {
|
||||||
|
return errors.WrapErr("t.readEnterState", errors.NOT_INIT)
|
||||||
|
}
|
||||||
|
if t.stateChannel == nil {
|
||||||
|
return errors.WrapErr("t.stateChannel", errors.NOT_INIT)
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
for state := range t.stateChannel {
|
||||||
|
t.writeMu.Lock()
|
||||||
|
oldRow, oldCol := t.getCursorPos()
|
||||||
|
t.moveCursor(t.height, len(footerStart)+1)
|
||||||
|
t.write(state)
|
||||||
|
t.moveCursor(oldRow, oldCol)
|
||||||
|
t.writeMu.Unlock()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
var readInputMu sync.Mutex
|
||||||
|
var readEnterdMu sync.Mutex
|
||||||
|
readInput := false
|
||||||
|
readCommand := false
|
||||||
|
readEnter := false
|
||||||
|
go func() {
|
||||||
|
for newState := range t.readInputState {
|
||||||
|
readInputMu.Lock()
|
||||||
|
readInput = newState
|
||||||
|
readInputMu.Unlock()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
go func() {
|
||||||
|
for newState := range t.readEnterState {
|
||||||
|
readEnterdMu.Lock()
|
||||||
|
readEnter = newState
|
||||||
|
readEnterdMu.Unlock()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
go func() {
|
||||||
|
reader := bufio.NewReader(os.Stdin)
|
||||||
|
for {
|
||||||
|
r, _, err := reader.ReadRune()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
log.Printf("Read %#v rune\n", r)
|
||||||
|
if readEnter {
|
||||||
|
if r == 13 {
|
||||||
|
readEnter = false
|
||||||
|
if t.selectedNotifier != nil {
|
||||||
|
t.errors <- t.write(t.selectedNotifier.Clear())
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if readCommand {
|
||||||
|
switch r {
|
||||||
|
case 'q':
|
||||||
|
t.mySignals <- mySignal{
|
||||||
|
Type: MY_SIGNAL_EXIT,
|
||||||
|
}
|
||||||
|
case 'c':
|
||||||
|
t.mySignals <- mySignal{
|
||||||
|
Type: MY_SIGNAL_CONNECT,
|
||||||
|
}
|
||||||
|
readInputMu.Lock()
|
||||||
|
readInput = true
|
||||||
|
readInputMu.Unlock()
|
||||||
|
case 'm':
|
||||||
|
t.mySignals <- mySignal{
|
||||||
|
Type: MY_SIGNAL_MESSAGE,
|
||||||
|
}
|
||||||
|
readInputMu.Lock()
|
||||||
|
readInput = true
|
||||||
|
readInputMu.Unlock()
|
||||||
|
case 't':
|
||||||
|
t.createNotification("some notify")
|
||||||
|
readEnter = true
|
||||||
|
}
|
||||||
|
|
||||||
|
readCommand = false
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
//
|
||||||
|
if unicode.IsControl(r) {
|
||||||
|
switch r {
|
||||||
|
case CTRL_A:
|
||||||
|
readCommand = true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if readInput {
|
||||||
|
log.Printf("Send %c | %d to t.input", r, r)
|
||||||
|
t.input <- r
|
||||||
|
}
|
||||||
|
}
|
||||||
|
readInputMu.Lock()
|
||||||
|
if readInput {
|
||||||
|
switch r {
|
||||||
|
case 13:
|
||||||
|
t.input <- r
|
||||||
|
case 127:
|
||||||
|
t.input <- r
|
||||||
|
}
|
||||||
|
}
|
||||||
|
readInputMu.Unlock()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
//
|
||||||
|
if t.osSignals == nil {
|
||||||
|
return errors.WrapErr("t.osSignals", errors.NOT_INIT)
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
for sig := range t.osSignals {
|
||||||
|
log.Printf("Receive OS.signal: %#v\n", sig)
|
||||||
|
switch sig {
|
||||||
|
case syscall.SIGWINCH:
|
||||||
|
t.errors <- t.redraw()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
if t.input == nil {
|
||||||
|
return errors.WrapErr("t.input", errors.NOT_INIT)
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
for r := range t.input {
|
||||||
|
log.Printf("Read rune: %#v from t.input\n", r)
|
||||||
|
selWidget := t.selectedWidget
|
||||||
|
// Enter
|
||||||
|
if r == 13 {
|
||||||
|
log.Printf("Debug: selWidget: %#v ; selWidget.Input: %#v", selWidget, selWidget.Input)
|
||||||
|
if selWidget != nil && selWidget.Input != nil {
|
||||||
|
t.errors <- selWidget.Handler(t, selWidget.Data)
|
||||||
|
buf := selWidget.Clear()
|
||||||
|
t.writeMu.Lock()
|
||||||
|
err := t.write(buf)
|
||||||
|
t.writeMu.Unlock()
|
||||||
|
if err != nil {
|
||||||
|
t.errors <- errors.WrapErr("t.write", err)
|
||||||
|
}
|
||||||
|
if selWidget.Next != nil {
|
||||||
|
log.Printf("Seeing that widget.Next is not nil")
|
||||||
|
t.errors <- t.addWidget(*selWidget.Next)
|
||||||
|
t.errors <- t.drawSelectedWidget()
|
||||||
|
} else {
|
||||||
|
t.readInputState <- false
|
||||||
|
}
|
||||||
|
if selWidget.Finale != nil {
|
||||||
|
log.Printf("Seeing that widget.Finale is not nil")
|
||||||
|
t.errors <- selWidget.Finale(t, selWidget.FinaleData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
} else if r == 127 {
|
||||||
|
log.Printf("seeing r = 127")
|
||||||
|
sliceLen := len(*selWidget.Input)
|
||||||
|
log.Printf("sliceLen = %d\n", sliceLen)
|
||||||
|
if sliceLen > 0 {
|
||||||
|
log.Printf("sliceLen > 0")
|
||||||
|
*selWidget.Input = (*selWidget.Input)[:sliceLen-1]
|
||||||
|
t.writeMu.Lock()
|
||||||
|
t.errors <- t.moveCursor(t.cursorPosRow, t.cursorPosCol-1)
|
||||||
|
t.errors <- t.writeRune(' ')
|
||||||
|
t.errors <- t.moveCursor(t.cursorPosRow, t.cursorPosCol-1)
|
||||||
|
t.writeMu.Unlock()
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if selWidget != nil && selWidget.Input != nil {
|
||||||
|
log.Printf("t.input: append %#v to widget input\n", r)
|
||||||
|
*selWidget.Input = append(*selWidget.Input, r)
|
||||||
|
log.Printf("t.input: trying to write %#v", r)
|
||||||
|
t.writeMu.Lock()
|
||||||
|
err := t.writeRune(r)
|
||||||
|
t.writeMu.Unlock()
|
||||||
|
if err != nil {
|
||||||
|
t.errors <- err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TUI) readRoutines() error {
|
||||||
|
if t.input == nil {
|
||||||
|
return errors.WrapErr("t.input", errors.NOT_INIT)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TUI) Draw() error {
|
||||||
|
err := t.clearScreen()
|
||||||
|
if err != nil {
|
||||||
|
return errors.WrapErr("t.clearScreen", err)
|
||||||
|
}
|
||||||
|
err = t.drawFooter()
|
||||||
|
if err != nil {
|
||||||
|
return errors.WrapErr("t.drawFooter", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TUI) drawFooter() error {
|
||||||
|
t.writeMu.Lock()
|
||||||
|
defer t.writeMu.Unlock()
|
||||||
|
err := t.moveCursor(t.height, 0)
|
||||||
|
if err != nil {
|
||||||
|
return errors.WrapErr("t.moveCursor", err)
|
||||||
|
}
|
||||||
|
err = t.write(footerStart)
|
||||||
|
if err != nil {
|
||||||
|
return errors.WrapErr("t.write", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TUI) write(s string) error {
|
||||||
|
_, err := t.writer.WriteString(s)
|
||||||
|
if err != nil {
|
||||||
|
return errors.WrapErr("t.writer.WriteString", err)
|
||||||
|
}
|
||||||
|
err = t.writer.Flush()
|
||||||
|
if err != nil {
|
||||||
|
return errors.WrapErr("t.writer.Flush", err)
|
||||||
|
}
|
||||||
|
t.cursorPosCol += len(s)
|
||||||
|
if t.cursorPosCol > t.width {
|
||||||
|
t.cursorPosCol %= t.width
|
||||||
|
t.cursorPosRow++
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TUI) writeRune(r rune) error {
|
||||||
|
_, err := t.writer.WriteRune(r)
|
||||||
|
if err != nil {
|
||||||
|
return errors.WrapErr("t.writer.WriteRune", err)
|
||||||
|
}
|
||||||
|
err = t.writer.Flush()
|
||||||
|
if err != nil {
|
||||||
|
return errors.WrapErr("t.writer.Flush", err)
|
||||||
|
}
|
||||||
|
t.cursorPosCol++
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TUI) getCursorPos() (int, int) {
|
||||||
|
return t.cursorPosRow, t.cursorPosCol
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TUI) moveCursor(row, col int) error {
|
||||||
|
t.sizeMutex.Lock()
|
||||||
|
defer t.sizeMutex.Unlock()
|
||||||
|
if row > t.height {
|
||||||
|
return errors.WrapErr(fmt.Sprintf("row: %d; height: %d", row, t.height), errors.OUT_OF_BOUND)
|
||||||
|
}
|
||||||
|
if col > t.width {
|
||||||
|
return errors.WrapErr(fmt.Sprintf("col: %d; width: %d", col, t.width), errors.OUT_OF_BOUND)
|
||||||
|
}
|
||||||
|
log.Printf("t.cursorPosRow: %d ; t.cursorPosCol: %d\n", t.cursorPosRow, t.cursorPosCol)
|
||||||
|
log.Printf("trying to move to row: %d ; col %d\n", row, col)
|
||||||
|
_, err := t.writer.WriteString(fmt.Sprintf("\033[%d;%dH", row, col))
|
||||||
|
if err != nil {
|
||||||
|
return errors.WrapErr("t.writer.WriteString", err)
|
||||||
|
}
|
||||||
|
err = t.writer.Flush()
|
||||||
|
if err != nil {
|
||||||
|
return errors.WrapErr("t.writer.Flush", err)
|
||||||
|
}
|
||||||
|
t.cursorPosCol = col
|
||||||
|
log.Printf("t.cursorPosCol now is = %d\n", col)
|
||||||
|
t.cursorPosRow = row
|
||||||
|
log.Printf("t.cursorPosRow now is = %d\n", row)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TUI) clearScreen() error {
|
||||||
|
_, err := t.writer.WriteString("\033[2J")
|
||||||
|
if err != nil {
|
||||||
|
return errors.WrapErr("t.writer.WriteString", err)
|
||||||
|
}
|
||||||
|
err = t.writer.Flush()
|
||||||
|
if err != nil {
|
||||||
|
return errors.WrapErr("t.writer.Flush", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TUI) drawSelectedWidget() error {
|
||||||
|
wDraw, err := t.selectedWidget.Draw()
|
||||||
|
if err != nil {
|
||||||
|
return errors.WrapErr("t.selectedWidget.Draw", err)
|
||||||
|
}
|
||||||
|
t.writeMu.Lock()
|
||||||
|
err = t.write(wDraw.Buf)
|
||||||
|
t.writeMu.Unlock()
|
||||||
|
if err != nil {
|
||||||
|
return errors.WrapErr("t.write", err)
|
||||||
|
}
|
||||||
|
t.cursorPosRow = wDraw.Row
|
||||||
|
t.cursorPosCol = wDraw.Col
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creating and adding widget from config.
|
||||||
|
// Also sets created widget as selectedWidget
|
||||||
|
func (t *TUI) addWidget(config widgetConfig) error {
|
||||||
|
widget := &widget{}
|
||||||
|
err := widget.init(config)
|
||||||
|
if err != nil {
|
||||||
|
return errors.WrapErr("widget.init", err)
|
||||||
|
}
|
||||||
|
t.widgetsMutext.Lock()
|
||||||
|
defer t.widgetsMutext.Unlock()
|
||||||
|
t.widgets = append(t.widgets, widget)
|
||||||
|
t.selectedWidget = widget
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TUI) redraw() error {
|
||||||
|
var err error
|
||||||
|
err = t.setSizes()
|
||||||
|
if err != nil {
|
||||||
|
return errors.WrapErr("t.getSizes", err)
|
||||||
|
}
|
||||||
|
err = t.redrawWidgets()
|
||||||
|
if err != nil {
|
||||||
|
return errors.WrapErr("t.redrawWidgets", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TUI) setSizes() error {
|
||||||
|
w, h, err := term.GetSize(int(os.Stdin.Fd()))
|
||||||
|
if err != nil {
|
||||||
|
return errors.WrapErr("term.GetSize", err)
|
||||||
|
}
|
||||||
|
t.sizeMutex.Lock()
|
||||||
|
t.width = w
|
||||||
|
t.height = h
|
||||||
|
t.sizeMutex.Unlock()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TUI) getSizes() (int, int) {
|
||||||
|
t.sizeMutex.Lock()
|
||||||
|
defer t.sizeMutex.Unlock()
|
||||||
|
return t.width, t.height
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TUI) redrawWidgets() error {
|
||||||
|
if t.widgets == nil {
|
||||||
|
return errors.WrapErr("t.widgets", errors.NOT_INIT)
|
||||||
|
}
|
||||||
|
// TODO
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TUI) setTermToRaw() error {
|
||||||
|
oldState, err := term.MakeRaw(int(os.Stdin.Fd()))
|
||||||
|
if err != nil {
|
||||||
|
return errors.WrapErr("term.MakeRaw", err)
|
||||||
|
}
|
||||||
|
t.oldState = oldState
|
||||||
|
return nil
|
||||||
|
}
|
175
tui/widget.go
Normal file
175
tui/widget.go
Normal file
|
@ -0,0 +1,175 @@
|
||||||
|
package tui
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"git.qowevisa.me/Qowevisa/gotell/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// func (w *widget) init(startX, startY, endX, endY int) error {
|
||||||
|
// width, height := UI.getSizes()
|
||||||
|
//
|
||||||
|
// return nil
|
||||||
|
// }
|
||||||
|
|
||||||
|
func (w *widget) init(config widgetConfig) error {
|
||||||
|
width, height := UI.getSizes()
|
||||||
|
if width == 0 {
|
||||||
|
return errors.WrapErr("width", errors.NOT_INIT)
|
||||||
|
}
|
||||||
|
if height == 0 {
|
||||||
|
return errors.WrapErr("height", errors.NOT_INIT)
|
||||||
|
}
|
||||||
|
var startRow, startCol int
|
||||||
|
// I guess I really has to do it that way bc go doesn't like my C style
|
||||||
|
// of writting `-2 * config.HasBorder` or `-2 * (config.HasBorder == true)`
|
||||||
|
var factorIfHasBorder int
|
||||||
|
if config.HasBorder {
|
||||||
|
factorIfHasBorder = 1
|
||||||
|
} else {
|
||||||
|
factorIfHasBorder = 0
|
||||||
|
}
|
||||||
|
if config.WidgetPosConfig.isGeneral() {
|
||||||
|
switch config.WidgetPosConfig {
|
||||||
|
case widgetPosGeneralCenter:
|
||||||
|
startCol = (width - len(config.Title)) / 2
|
||||||
|
startRow = (height - 3 - 2*factorIfHasBorder) / 2
|
||||||
|
case widgetPosGeneralLeftCenter:
|
||||||
|
startCol = 0
|
||||||
|
startRow = (height - 3 - 2*factorIfHasBorder) / 2
|
||||||
|
case widgetPosGeneralRightCenter:
|
||||||
|
startCol = (width - len(config.Title) - 2*factorIfHasBorder)
|
||||||
|
startRow = (height - 3 - 2*factorIfHasBorder) / 2
|
||||||
|
default:
|
||||||
|
return errors.WrapErr(fmt.Sprintf("config.WidgetPosConfig: %d :", config.WidgetPosConfig), errors.NOT_HANDLED)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
snappedPair := tuiPointPair{
|
||||||
|
startCol: startCol,
|
||||||
|
startRow: startRow,
|
||||||
|
endCol: startCol + len(config.Title) + 2,
|
||||||
|
endRow: startRow + 5,
|
||||||
|
}
|
||||||
|
percentPair := tuiPointPair{
|
||||||
|
startCol: GetIntPercentFromData(snappedPair.startCol, width),
|
||||||
|
startRow: GetIntPercentFromData(snappedPair.startRow, height),
|
||||||
|
endCol: GetIntPercentFromData(snappedPair.endCol, width),
|
||||||
|
endRow: GetIntPercentFromData(snappedPair.endRow, height),
|
||||||
|
}
|
||||||
|
w.percentPair = percentPair
|
||||||
|
w.snappedPair = snappedPair
|
||||||
|
w.startupConfig = config
|
||||||
|
w.Input = config.Input
|
||||||
|
w.Handler = config.DataHandler
|
||||||
|
w.Data = config.Data
|
||||||
|
w.Title = config.Title
|
||||||
|
w.MinWidth = config.MinWidth
|
||||||
|
w.MinHeight = config.MinHeight
|
||||||
|
if w.MinWidth > len(config.Title) {
|
||||||
|
w.Width = w.MinWidth
|
||||||
|
} else {
|
||||||
|
if w.startupConfig.HasBorder {
|
||||||
|
w.Width = len(config.Title) + 2
|
||||||
|
} else {
|
||||||
|
w.Width = len(config.Title)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if config.HasBorder {
|
||||||
|
if config.MinHeight > 5 {
|
||||||
|
w.Height = config.MinHeight
|
||||||
|
} else {
|
||||||
|
w.Height = 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
w.Next = config.Next
|
||||||
|
w.Finale = config.Finale
|
||||||
|
w.FinaleData = config.FinaleData
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getBufForMovingCursorTo(row, col int) string {
|
||||||
|
return fmt.Sprintf("\033[%d;%dH", row, col)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *widget) moveToNextLine() string {
|
||||||
|
w.row++
|
||||||
|
return getBufForMovingCursorTo(w.row, w.col)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *widget) Draw() (widgetDraw, error) {
|
||||||
|
w.row = w.snappedPair.startRow
|
||||||
|
w.col = w.snappedPair.startCol
|
||||||
|
var buf string
|
||||||
|
title := w.startupConfig.Title
|
||||||
|
buf += getBufForMovingCursorTo(w.row, w.col)
|
||||||
|
if w.startupConfig.HasBorder {
|
||||||
|
buf += strings.Repeat("-", w.Width)
|
||||||
|
buf += w.moveToNextLine()
|
||||||
|
emptyLen := w.Width - len(title) - 2
|
||||||
|
firstHalf := (emptyLen) / 2
|
||||||
|
buf += fmt.Sprintf("|%s%s%s|",
|
||||||
|
strings.Repeat(" ", firstHalf),
|
||||||
|
title, strings.Repeat(" ",
|
||||||
|
emptyLen-firstHalf))
|
||||||
|
buf += w.moveToNextLine()
|
||||||
|
buf += strings.Repeat("-", w.Width)
|
||||||
|
buf += w.moveToNextLine()
|
||||||
|
buf += fmt.Sprintf("|%s%s|", string(*w.Input), strings.Repeat(" ", w.Width-2-len(*w.Input)))
|
||||||
|
buf += w.moveToNextLine()
|
||||||
|
buf += strings.Repeat("-", w.Width)
|
||||||
|
w.col++
|
||||||
|
w.row--
|
||||||
|
buf += getBufForMovingCursorTo(w.row, w.col)
|
||||||
|
} else {
|
||||||
|
buf += fmt.Sprintf("%s", title)
|
||||||
|
buf += w.moveToNextLine()
|
||||||
|
buf += strings.Repeat("-", w.Width)
|
||||||
|
buf += w.moveToNextLine()
|
||||||
|
buf += fmt.Sprintf("%s%s", string(*w.Input), strings.Repeat(" ", len(title)-len(*w.Input)))
|
||||||
|
buf += getBufForMovingCursorTo(w.row, w.col)
|
||||||
|
}
|
||||||
|
return widgetDraw{
|
||||||
|
Buf: buf,
|
||||||
|
Row: w.row,
|
||||||
|
Col: w.col,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *widget) Clear() string {
|
||||||
|
var buf string
|
||||||
|
w.row = w.snappedPair.startRow
|
||||||
|
w.col = w.snappedPair.startCol
|
||||||
|
title := w.startupConfig.Title
|
||||||
|
buf += getBufForMovingCursorTo(w.row, w.col)
|
||||||
|
if w.startupConfig.HasBorder {
|
||||||
|
buf += strings.Repeat(" ", w.Width)
|
||||||
|
buf += w.moveToNextLine()
|
||||||
|
buf += strings.Repeat(" ", w.Width)
|
||||||
|
buf += w.moveToNextLine()
|
||||||
|
buf += strings.Repeat(" ", w.Width)
|
||||||
|
buf += w.moveToNextLine()
|
||||||
|
maxClearInpLen := len(*w.Input) + 2
|
||||||
|
if w.Width > maxClearInpLen {
|
||||||
|
maxClearInpLen = w.Width
|
||||||
|
}
|
||||||
|
buf += fmt.Sprintf("%s", strings.Repeat(" ", maxClearInpLen))
|
||||||
|
buf += w.moveToNextLine()
|
||||||
|
buf += strings.Repeat(" ", w.Width)
|
||||||
|
w.row++
|
||||||
|
w.col--
|
||||||
|
buf += getBufForMovingCursorTo(0, 0)
|
||||||
|
} else {
|
||||||
|
buf += fmt.Sprintf("%s", strings.Repeat(" ", len(title)))
|
||||||
|
buf += w.moveToNextLine()
|
||||||
|
buf += strings.Repeat(" ", w.Width)
|
||||||
|
buf += w.moveToNextLine()
|
||||||
|
maxClearInpLen := len(*w.Input)
|
||||||
|
if w.Width > maxClearInpLen {
|
||||||
|
maxClearInpLen = w.Width
|
||||||
|
}
|
||||||
|
buf += fmt.Sprintf("%s", strings.Repeat(" ", maxClearInpLen))
|
||||||
|
buf += getBufForMovingCursorTo(0, 0)
|
||||||
|
}
|
||||||
|
return buf
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user