package ctfc import ( "strings" "time" "gitlab.com/Syndamia/ctfc/go-src/csi" ctfcmath "gitlab.com/Syndamia/ctfc/go-src/ctfcMath" "gitlab.com/Syndamia/ctfc/go-src/ui" ) /* 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, customLinesBelow int, numbered bool) { for *lastLine > -2 { // Update messages, if we've already shown the last message if *lastLine == len(*messages)-1 { updateMessages(messages) continue } *lastLine = len(*messages) - 1 csi.SaveCursorPosition() csi.MoveCursorUpN(pageSize + 1 + customLinesBelow) csi.MoveCursorToBeginningOfLine() pageMessages := paginate(page, *messages...) // Leaves empty lines at the top, if there aren't enough messages to fill a full page // Works on the assumption that there are ui.EmptyLine(), where messages would be printed for i := 0; i < pageSize-len(pageMessages); i++ { csi.MoveCursorDown() } if numbered { ui.NumberedFields(pageMessages...) } else { ui.TextFields(pageMessages...) } ui.PageField(page, totalPages(len(*messages))) csi.RestoreCursorPosition() time.Sleep(500 * time.Millisecond) } } func initPaginatedValues(defaultLength int, values *[]string) { if len(*values) == defaultLength { *values = append(append(*values, "."), "") } } /* Input actions */ type inputAction struct { value string execute window args []string } func handleInputActions(input string, handleNav bool, ia ...inputAction) func() { if handleNav { ia = append(ia, inputAction{"C", chatsWindow, nil}, inputAction{"D", chatsWindow, nil}, inputAction{"A", chatsWindow, nil}, inputAction{"L", logoutWindow, nil}, ) } for _, v := range ia { if strings.ToLower(input) == strings.ToLower(v.value) { return func() { v.execute(v.args...) } } } return nil } /* Form */ type formInput struct { name string specification string validationFunc func(string) bool } 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):] { 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...) } defer showError(invalidValueFor(v.name), formCallback) return } } // Add current input as a filled input userInputs = append(userInputs, input) } return } /* Error */ func showError(message string, callback window, callbackData ...string) { csi.ClearScreen() ui.ErrorBox(message) csi.BlockClearScreenNextTime() defer callback(callbackData...) }