util.go (4808B)
1 package tview 2 3 import ( 4 "math" 5 "os" 6 "regexp" 7 8 "github.com/gdamore/tcell/v2" 9 ) 10 11 // Text alignment within a box. Also used to align images. 12 const ( 13 AlignLeft = iota 14 AlignCenter 15 AlignRight 16 AlignTop = 0 17 AlignBottom = 2 18 ) 19 20 var ( 21 // Regular expression used to escape style/region tags. 22 escapePattern = regexp.MustCompile(`(\[[a-zA-Z0-9_,;: \-\."#]+\[*)\]`) 23 24 // Regular expression used to unescape escaped style/region tags. 25 unescapePattern = regexp.MustCompile(`(\[[a-zA-Z0-9_,;: \-\."#]+\[*)\[\]`) 26 27 // The number of colors available in the terminal. 28 availableColors = 256 29 ) 30 31 // Package initialization. 32 func init() { 33 // Determine the number of colors available in the terminal. 34 info, err := tcell.LookupTerminfo(os.Getenv("TERM")) 35 if err == nil { 36 availableColors = info.Colors 37 } 38 } 39 40 // Print prints text onto the screen into the given box at (x,y,maxWidth,1), 41 // not exceeding that box. "align" is one of AlignLeft, AlignCenter, or 42 // AlignRight. The screen's background color will not be changed. 43 // 44 // You can change the colors and text styles mid-text by inserting a style tag. 45 // See the package description for details. 46 // 47 // Returns the number of actual bytes of the text printed (including style tags) 48 // and the actual width used for the printed runes. 49 func Print(screen tcell.Screen, text string, x, y, maxWidth, align int, color tcell.Color) (int, int) { 50 start, end, width := printWithStyle(screen, text, x, y, 0, maxWidth, align, tcell.StyleDefault.Foreground(color), true) 51 return end - start, width 52 } 53 54 // printWithStyle works like [Print] but it takes a style instead of just a 55 // foreground color. The skipWidth parameter specifies the number of cells 56 // skipped at the beginning of the text. It returns the start index, end index 57 // (exclusively), and screen width of the text actually printed. If 58 // maintainBackground is "true", the existing screen background is not changed 59 // (i.e. the style's background color is ignored). 60 func printWithStyle(screen tcell.Screen, text string, x, y, skipWidth, maxWidth, align int, style tcell.Style, maintainBackground bool) (start, end, printedWidth int) { 61 totalWidth, totalHeight := screen.Size() 62 if maxWidth <= 0 || len(text) == 0 || y < 0 || y >= totalHeight { 63 return 0, 0, 0 64 } 65 66 // If we don't overwrite the background, we use the default color. 67 if maintainBackground { 68 style = style.Background(tcell.ColorDefault) 69 } 70 71 // Skip beginning and measure width. 72 var textWidth int 73 state := &stepState{ 74 unisegState: -1, 75 style: style, 76 } 77 newState := *state 78 str := text 79 for len(str) > 0 { 80 _, str, state = step(str, state, stepOptionsStyle) 81 if skipWidth > 0 { 82 skipWidth -= state.Width() 83 text = str 84 newState = *state 85 start += state.GrossLength() 86 } else { 87 textWidth += state.Width() 88 } 89 } 90 state = &newState 91 92 // Reduce all alignments to AlignLeft. 93 if align == AlignRight { 94 // Chop off characters on the left until it fits. 95 for len(text) > 0 && textWidth > maxWidth { 96 _, text, state = step(text, state, stepOptionsStyle) 97 textWidth -= state.Width() 98 start += state.GrossLength() 99 } 100 x, maxWidth = x+maxWidth-textWidth, textWidth 101 } else if align == AlignCenter { 102 // Chop off characters on the left until it fits. 103 subtracted := (textWidth - maxWidth) / 2 104 for len(text) > 0 && subtracted > 0 { 105 _, text, state = step(text, state, stepOptionsStyle) 106 subtracted -= state.Width() 107 textWidth -= state.Width() 108 start += state.GrossLength() 109 } 110 if textWidth < maxWidth { 111 x, maxWidth = x+maxWidth/2-textWidth/2, textWidth 112 } 113 } 114 115 // Draw left-aligned text. 116 end = start 117 rightBorder := x + maxWidth 118 for len(text) > 0 && x < rightBorder && x < totalWidth { 119 var c string 120 c, text, state = step(text, state, stepOptionsStyle) 121 if c == "" { 122 break // We don't care about the style at the end. 123 } 124 width := state.Width() 125 126 if width > 0 { 127 finalStyle := state.Style() 128 if maintainBackground { 129 _, backgroundColor, _ := finalStyle.Decompose() 130 if backgroundColor == tcell.ColorDefault { 131 _, _, existingStyle, _ := screen.GetContent(x, y) 132 _, background, _ := existingStyle.Decompose() 133 finalStyle = finalStyle.Background(background) 134 } 135 } 136 for offset := width - 1; offset >= 0; offset-- { 137 // To avoid undesired effects, we populate all cells. 138 runes := []rune(c) 139 if offset == 0 { 140 screen.SetContent(x+offset, y, runes[0], runes[1:], finalStyle) 141 } else { 142 screen.SetContent(x+offset, y, ' ', nil, finalStyle) 143 } 144 } 145 } 146 147 x += width 148 end += state.GrossLength() 149 printedWidth += width 150 } 151 152 return 153 } 154 155 // PrintSimple prints white text to the screen at the given position. 156 func PrintSimple(screen tcell.Screen, text string, x, y int) { 157 Print(screen, text, x, y, math.MaxInt32, AlignLeft, Styles.PrimaryTextColor) 158 }