@ -7,6 +7,7 @@ import (
@@ -7,6 +7,7 @@ import (
"strings"
"github.com/gdamore/tcell"
runewidth "github.com/mattn/go-runewidth"
)
// Text alignment within a box.
@ -90,40 +91,69 @@ func init() {
@@ -90,40 +91,69 @@ func init() {
// not exceeding that box. "align" is one of AlignLeft, AlignCenter, or
// AlignRight. The screen's background color will not be changed.
//
// Returns the number of actual runes printed.
func Print ( screen tcell . Screen , text string , x , y , maxWidth , align int , color tcell . Color ) int {
// Returns the number of actual runes printed and the actual width used for the
// printed runes.
func Print ( screen tcell . Screen , text string , x , y , maxWidth , align int , color tcell . Color ) ( int , int ) {
// We deal with runes, not with bytes.
runes := [ ] rune ( text )
if maxWidth < 0 {
return 0
return 0 , 0
}
// AlignCenter is split into two parts .
// AlignCenter is a special case .
if align == AlignCenter {
half := len ( runes ) / 2
halfWidth := maxWidth / 2
return Print ( screen , string ( runes [ : half ] ) , x , y , halfWidth , AlignRight , color ) +
Print ( screen , string ( runes [ half : ] ) , x + halfWidth , y , maxWidth - halfWidth , AlignLeft , color )
width := runewidth . StringWidth ( text )
if width == maxWidth {
// Use the exact space.
return Print ( screen , text , x , y , maxWidth , AlignLeft , color )
} else if width < maxWidth {
// We have more space than we need.
half := ( maxWidth - width ) / 2
return Print ( screen , text , x + half , y , maxWidth - half , AlignLeft , color )
} else {
// Chop off runes until we have a perfect fit.
var start , choppedLeft , choppedRight int
ru := runes
for len ( ru ) > 0 && width - choppedLeft - choppedRight > maxWidth {
leftWidth := runewidth . RuneWidth ( ru [ 0 ] )
rightWidth := runewidth . RuneWidth ( ru [ len ( ru ) - 1 ] )
if choppedLeft < choppedRight {
start ++
choppedLeft += leftWidth
ru = ru [ 1 : ]
} else {
choppedRight += rightWidth
ru = ru [ : len ( ru ) - 1 ]
}
}
return Print ( screen , string ( ru ) , x , y , maxWidth , AlignLeft , color )
}
}
// Draw text.
drawn := 0
drawnWidth := 0
for pos , ch := range runes {
if pos >= maxWidth {
chWidth := runewidth . RuneWidth ( ch )
if drawnWidth + chWidth > maxWidth {
break
}
finalX := x + pos
finalX := x + drawnWidth
if align == AlignRight {
ch = runes [ len ( runes ) - 1 - pos ]
finalX = x + maxWidth - 1 - pos
finalX = x + maxWidth - chWidth - drawnWidth
}
_ , _ , style , _ := screen . GetContent ( finalX , y )
style = style . Foreground ( color )
screen . SetContent ( finalX , y , ch , nil , style )
for offset := 0 ; offset < chWidth ; offset ++ {
// To avoid undesired effects, we place the same character in all cells.
screen . SetContent ( finalX + offset , y , ch , nil , style )
}
drawn ++
drawnWidth += chWidth
}
return drawn
return drawn , drawnWidth
}
// PrintSimple prints white text to the screen at the given position.
@ -132,8 +162,8 @@ func PrintSimple(screen tcell.Screen, text string, x, y int) {
@@ -132,8 +162,8 @@ func PrintSimple(screen tcell.Screen, text string, x, y int) {
}
// WordWrap splits a text such that each resulting line does not exceed the
// given width. Possible split points are after commas, dots, dashes, and any
// whitespace. Whitespace at split points will be dropped.
// given screen width. Possible split points are after commas, dots, dashes,
// and any whitespace. Whitespace at split points will be dropped.
//
// Text is always split at newline characters ('\n').
func WordWrap ( text string , width int ) ( lines [ ] string ) {
@ -146,6 +176,8 @@ func WordWrap(text string, width int) (lines []string) {
@@ -146,6 +176,8 @@ func WordWrap(text string, width int) (lines []string) {
text = strings . TrimSpace ( text )
for pos , ch := range text {
chWidth := runewidth . RuneWidth ( ch )
if ! evaluatingCandidate && x >= width {
// We've exceeded the width, we must split.
if candidate >= 0 {
@ -190,8 +222,8 @@ func WordWrap(text string, width int) (lines []string) {
@@ -190,8 +222,8 @@ func WordWrap(text string, width int) (lines []string) {
countAfterCandidate = 0
}
}
x ++
countAfterCandidate ++
x += chWidth
countAfterCandidate += chWidth
}
// Process remaining text.