diff options
Diffstat (limited to 'go-src')
| -rw-r--r-- | go-src/csi/csi.go | 90 | ||||
| -rw-r--r-- | go-src/utils/utils.go | 14 | ||||
| -rw-r--r-- | go-src/windows.go | 92 | ||||
| -rw-r--r-- | go-src/windowsHelpers.go | 70 |
4 files changed, 173 insertions, 93 deletions
diff --git a/go-src/csi/csi.go b/go-src/csi/csi.go new file mode 100644 index 0000000..100db59 --- /dev/null +++ b/go-src/csi/csi.go @@ -0,0 +1,90 @@ +package csi + +import ( + "fmt" + "os" + "os/exec" + "runtime" +) + +const ( + ansiCUUf = "\033[%vA" // Cursor (move) Up, formatted version (expects to be given a number) + ansiCUDf = "\033[%vB" // Cursor (move) Down, formated version (expects to be given a number) + ansiCUFf = "\033[%vC" // Cursor (move) Forward (to the right), formatted version (expects to be given a number) + + ansiCUU = "\033[A" // Cursor (move) Up + ansiCUD = "\033[B" // Cursor (move) Down + ansiCUF = "\033[C" // Cursor (move) Forward (to the right) + ansiCNL = "\033[E" // Cursor (move) Next Line + ansiED = "\033[2J" // Erase in Display (clears entire screen) + ansiEL = "\033[K" // Erase in Line (clears from cursor to end of line) + + ansiSCP = "\033[s" // Save Current Cursor Position + ansiRCP = "\033[u" // Restore Saved Cursor Position +) + +func MoveCursorUpN(times int) { + fmt.Printf(ansiCUUf, times) +} + +func MoveCursorDownN(times int) { + fmt.Printf(ansiCUDf, times) +} + +func MoveCursorRightN(times int) { + fmt.Printf(ansiCUFf, times) +} + +func MoveCursorUp() { + fmt.Print(ansiCUU) +} + +func MoveCursorDown() { + fmt.Print(ansiCUD) +} + +func MoveCursorRight() { + fmt.Printf(ansiCUF) +} + +func MoveCursorToBeginningOfLine() { + fmt.Print(ansiCNL) +} + +func ClearLine() { + fmt.Print(ansiEL) +} + +var blockClearing = false + +func ClearScreen() { + if blockClearing { + blockClearing = false + return + } + + if runtime.GOOS == "windows" { + cmd := exec.Command("cmd", "/c", "cls") + cmd.Stdout = os.Stdout + cmd.Run() + } else { + fmt.Println(ansiED) + } +} + +// The next time that ClearScreen is called, it won't clear the screen +func BlockClearScreenNextTime() { + blockClearing = true +} + +func EraseToEndOfLine() { + fmt.Print(ansiEL) +} + +func SaveCursorPosition() { + fmt.Print(ansiSCP) +} + +func RestoreCursorPosition() { + fmt.Print(ansiRCP) +} diff --git a/go-src/utils/utils.go b/go-src/utils/utils.go index 8d0c1c7..5676744 100644 --- a/go-src/utils/utils.go +++ b/go-src/utils/utils.go @@ -2,8 +2,6 @@ package utils import ( "os" - - ctfcmath "gitlab.com/Syndamia/ctfc/go-src/ctfcMath" ) // Repeats a rune given amount of times and returns the result as a string @@ -35,15 +33,3 @@ func AppendToFile(path string, value string) { allChatsFile.WriteString(value) allChatsFile.Close() } - -/* Pagination */ - -const PageSize = 15 - -func TotalPages(messageAmount int) int { - return ctfcmath.CeilDivInt(messageAmount, PageSize) -} - -func Paginate(page int, messages ...string) []string { - return messages[ctfcmath.MaxInt(len(messages)-PageSize*page, 0) : len(messages)-PageSize*(page-1)] -} diff --git a/go-src/windows.go b/go-src/windows.go index 584058a..b3af04e 100644 --- a/go-src/windows.go +++ b/go-src/windows.go @@ -4,14 +4,14 @@ import ( "strconv" "strings" + "gitlab.com/Syndamia/ctfc/go-src/csi" "gitlab.com/Syndamia/ctfc/go-src/ui" - "gitlab.com/Syndamia/ctfc/go-src/utils" ) type window func(...string) func StartupWindow(...string) { - clearScreen() + csi.ClearScreen() ui.NormalBox(true, "Login", "Register") input := ui.InputField("[L/R]") @@ -26,15 +26,19 @@ func StartupWindow(...string) { } func loginWindow(values ...string) { - inputs := formWindow("Login", StartupWindow, []formInput{ - {"Username", " or [B for Back to start page]", nil}, - {"Password", "", nil}, - }, values..., + // Takes form user input + inputs := formWindow("Login", StartupWindow, + []formInput{ + {"Username", " or [B for Back to start page]", nil}, + {"Password", "", nil}, + }, + values..., ) if len(inputs) == 0 { return } + // If login is successful, go to chats window, else show error if logInUser(inputs[0], inputs[1]) { defer chatsWindow() } else { @@ -43,11 +47,13 @@ func loginWindow(values ...string) { } func registerWindow(values ...string) { - inputs := formWindow("Register", StartupWindow, []formInput{ - {"Username", " [A-z, 0-9, *, ., _, -; at least 1 letter] or [B for Back to start page]", stringValidUsername}, - {"Password", " [5-40 characters; at least 1 number]", stringValidPassword}, - {"Name", " [2-60 characters]", stringValidName}, - }, values..., + inputs := formWindow("Register", StartupWindow, + []formInput{ + {"Username", " [A-z, 0-9, *, ., _, -; at least 1 letter] or [B for Back to start page]", stringValidUsername}, + {"Password", " [5-40 characters; at least 1 number]", stringValidPassword}, + {"Name", " [2-60 characters]", stringValidName}, + }, + values..., ) if len(inputs) == 0 { return @@ -63,7 +69,7 @@ func registerWindow(values ...string) { } func chatsWindow(values ...string) { - clearScreen() + csi.ClearScreen() ui.NormalBox(true, "Direct Messages", "Account", "Logout") allChats := getAllChats() @@ -83,7 +89,7 @@ func chatsWindow(values ...string) { inputAction{"H", chatsWindow, []string{"ShowHelp"}}, ) if !handled { - // if is number + // If user input is number, navigate to chat of given number, else show error if chatI, err := strconv.Atoi(input); chatI >= 0 && chatI <= len(allChats) && err == nil { defer chatWindow(strings.Split(allChats[chatI-1], " : ")[0]) } else { @@ -93,10 +99,12 @@ func chatsWindow(values ...string) { } func createChatWindow(values ...string) { - inputs := formWindow("Creating a new chat", chatsWindow, []formInput{ - {"Name", " [2-20 characters; A-z, 0-9, spaces, _, -] or [B for Back to chats page]", stringValidChatName}, - {"Description", " [0-30 characters]", stringValidChatDesc}, - }, values..., + inputs := formWindow("Creating a new chat", chatsWindow, + []formInput{ + {"Name", " [2-20 characters; A-z, 0-9, spaces, _, -] or [B for Back to chats page]", stringValidChatName}, + {"Description", " [0-30 characters]", stringValidChatDesc}, + }, + values..., ) if len(inputs) == 0 { return @@ -111,8 +119,10 @@ func createChatWindow(values ...string) { } func chatWindow(values ...string) { - clearScreen() + csi.ClearScreen() + // We determine page number by length of a string + // This method should be faster than having to cast to int all the time if len(values) == 1 { values = append(values, ".") } @@ -122,7 +132,9 @@ func chatWindow(values ...string) { ui.TextField(currChat.Name + " : " + currChat.Description) ui.TextField("Brought to you by " + currChat.Owner.Name + " (" + currChat.Owner.Username + ")") - for i := 0; i <= 16; i++ { + // One empty line to separate the "Brought to you by" line + // with the chat messages, and then empty line for each chat message + for i := 0; i <= pageSize+1; i++ { ui.EmptyLine() } @@ -139,25 +151,28 @@ func chatWindow(values ...string) { if !handled { switch input { case ">": - if len(values[1]) < utils.TotalPages(len(currChat.Messages)) { + // If possible, increment to the next page (adds a dot to the end of the string) + if len(values[1]) < totalPages(len(currChat.Messages)) { values[1] += "." } case "<": + // If possible, decrement to the previous page (removes a dot from the string) if len(values[1]) > 1 { values[1] = values[1][:len(values[1])-1] } - default: - values[1] = "." + default: // Send a message + values[1] = "." // Make sure to be at the first page, when sending a message + // Since "C" is a command, to just enter the letter C you'll need to input "\C" currChat.addMessage(strings.TrimPrefix(input, "\\"), loggedInUser.Username) } defer chatWindow(values...) } - lastLine = -1 + lastLine = -1 // Practically stops execution of the paginated subwindow routine } func logoutWindow(...string) { - clearScreen() + csi.ClearScreen() ui.NormalBox(true, "Are you sure you want to Log out of "+loggedInUser.Username+"?") ui.EmptyLine() @@ -172,6 +187,15 @@ func logoutWindow(...string) { } } +func showError(message string, callback window, callbackData ...string) { + csi.ClearScreen() + + ui.ErrorBox(message) + csi.BlockClearScreenNextTime() + + defer callback(callbackData...) +} + /* Form */ type formInput struct { @@ -182,23 +206,29 @@ type formInput struct { func formWindow(boxTitle string, backWindow window, formInputs []formInput, currentValues ...string) (userInputs []string) { userInputs = currentValues + // Go through all inputs for i, v := range formInputs[len(currentValues):] { - clearScreen() + csi.ClearScreen() ui.NormalBox(true, boxTitle) + // Show filled input fields for j, va := range userInputs { ui.InputFieldFilled(formInputs[j].name, va) } + // Wait for input on current input field input := ui.InputField(v.name + v.specification) + // Go to the "backWindow", if we're on the first input, we are given a back window, and the letter B is given if i == 0 && backWindow != nil && strings.ToLower(input) == "b" { defer backWindow() return } + // If we're validating input data, try to validate if v.validationFunc != nil { + // If input data isn't valid, show an error and the same screen if !v.validationFunc(input) { formCallback := func(...string) { defer formWindow(boxTitle, backWindow, formInputs, userInputs...) @@ -208,6 +238,7 @@ func formWindow(boxTitle string, backWindow window, formInputs []formInput, curr } } + // Add current input as a filled input userInputs = append(userInputs, input) } return @@ -238,14 +269,3 @@ func handleInputActions(input string, handleNav bool, ia ...inputAction) bool { } return false } - -/* Errors and Clear */ - -func showError(message string, callback window, callbackData ...string) { - clearScreen() - - ui.ErrorBox(message) - clearNext = false - - defer callback(callbackData...) -} diff --git a/go-src/windowsHelpers.go b/go-src/windowsHelpers.go index 13cb3c1..909a673 100644 --- a/go-src/windowsHelpers.go +++ b/go-src/windowsHelpers.go @@ -1,70 +1,54 @@ package ctfc import ( - "fmt" - "os" - "os/exec" - "runtime" "time" + "gitlab.com/Syndamia/ctfc/go-src/csi" + ctfcmath "gitlab.com/Syndamia/ctfc/go-src/ctfcMath" "gitlab.com/Syndamia/ctfc/go-src/ui" - "gitlab.com/Syndamia/ctfc/go-src/utils" ) +/* Pagination */ + +const pageSize = 15 + +func totalPages(messageAmount int) int { + return ctfcmath.CeilDivInt(messageAmount, pageSize) +} + +func paginate(page int, messages ...string) []string { + return messages[ctfcmath.MaxInt(len(messages)-pageSize*page, 0) : len(messages)-pageSize*(page-1)] +} + +// Must be run in a routine ( go routinePaginatedSubwindow(...) ) +// There must be empty lines for the subwindow func routinePaginatedSubwindow(messages *[]string, updateMessages func(*[]string), lastLine *int, page int) { for *lastLine > -1 { + // Update messages, if we've already shown the last message if *lastLine == len(*messages)-1 { updateMessages(messages) continue } *lastLine = len(*messages) - 1 - fmt.Print("\033[s") + csi.SaveCursorPosition() - moveCursorUp(utils.PageSize + 2) + csi.MoveCursorUpN(pageSize + 2) // +2 because of the input and pagination lines + csi.MoveCursorToBeginningOfLine() - pageMessages := utils.Paginate(page, *messages...) - for i := 0; i < utils.PageSize-len(pageMessages); i++ { - ui.EmptyLine() + pageMessages := paginate(page, *messages...) + // Leaves empty lines at the top, if there aren't enough messages to fill a full page + for i := 0; i < pageSize-len(pageMessages); i++ { + csi.MoveCursorDown() } for _, v := range pageMessages { - fmt.Print("\033[K") + csi.ClearLine() ui.TextField(v) } - ui.PageField(page, utils.TotalPages(len(*messages))) + ui.PageField(page, totalPages(len(*messages))) - fmt.Print("\033[u") + csi.RestoreCursorPosition() time.Sleep(500 * time.Millisecond) } } - -var clearNext = true - -func clearScreen() { - if !clearNext { - clearNext = true - return - } - - if runtime.GOOS == "windows" { - cmd := exec.Command("cmd", "/c", "cls") - cmd.Stdout = os.Stdout - cmd.Run() - } else { - fmt.Println("\033[2J") - } -} - -func moveCursorUp(times int) { - fmt.Printf("\033[%vA", times) - fmt.Print("\033[E") -} - -func moveCursorDown(times int) { - fmt.Printf("\033[%vB", times) -} - -func moveCursorRight(times int) { - fmt.Printf("\033[%vC", times) -} |
