From 964fd51438d7f4b431e9745c2be3874c206bc287 Mon Sep 17 00:00:00 2001 From: Trevor Slocum Date: Mon, 5 Jul 2021 12:52:52 -0700 Subject: [PATCH] Add TextView.SetVerticalAlign Resolves #68. --- CHANGELOG | 1 + demos/presentation/textview.go | 1 + textview.go | 42 ++++++++++++++++++++++++++++------ util.go | 12 +++++++++- 4 files changed, 48 insertions(+), 8 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index a2c96fe..5022795 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,7 @@ v1.5.6 (WIP) - Add TrueColorTags option and do not use TrueColor tag values by default - Add TextView.SetHighlightForegroundColor and TextView.SetHighlightBackgroundColor +- Add TextView.SetVerticalAlign - Add Button.SetCursorRune (cursors are now shown within Buttons when focused by default) - Add CheckBox.SetCursorRune (cursors are now shown within CheckBoxes when focused by default) - Add DropDown.SetAlwaysDrawDropDownSymbol (DropDown symbols are now shown only when focused by default) diff --git a/demos/presentation/textview.go b/demos/presentation/textview.go index f737ab1..6d27d98 100644 --- a/demos/presentation/textview.go +++ b/demos/presentation/textview.go @@ -32,6 +32,7 @@ const textView1 = `[green]func[white] [yellow]main[white]() { // TextView1 demonstrates the basic text view. func TextView1(nextSlide func()) (title string, info string, content cview.Primitive) { textView := cview.NewTextView() + textView.SetVerticalAlign(cview.AlignBottom) textView.SetTextColor(tcell.ColorYellow.TrueColor()) textView.SetDoneFunc(func(key tcell.Key) { nextSlide() diff --git a/textview.go b/textview.go index 8cd4537..b95bf62 100644 --- a/textview.go +++ b/textview.go @@ -119,9 +119,12 @@ type TextView struct { // If set to true, the buffer will be reindexed each time it is modified. reindex bool - // The text alignment, one of AlignLeft, AlignCenter, or AlignRight. + // The horizontal text alignment, one of AlignLeft, AlignCenter, or AlignRight. align int + // The vertical text alignment, one of AlignTop, AlignMiddle, or AlignBottom. + valign VerticalAlignment + // Information about visible regions as of the last call to Draw(). regionInfos []*textViewRegion @@ -227,6 +230,7 @@ func NewTextView() *TextView { scrollBarVisibility: ScrollBarAuto, scrollBarColor: Styles.ScrollBarColor, align: AlignLeft, + valign: AlignTop, wrap: true, textColor: Styles.PrimaryTextColor, highlightForeground: Styles.PrimitiveBackgroundColor, @@ -291,8 +295,8 @@ func (t *TextView) SetWordWrap(wrapOnWords bool) { t.wordWrap = wrapOnWords } -// SetTextAlign sets the text alignment within the text view. This must be -// either AlignLeft, AlignCenter, or AlignRight. +// SetTextAlign sets the horizontal alignment of the text. This must be either +// AlignLeft, AlignCenter, or AlignRight. func (t *TextView) SetTextAlign(align int) { t.Lock() defer t.Unlock() @@ -303,6 +307,18 @@ func (t *TextView) SetTextAlign(align int) { t.align = align } +// SetVerticalAlign sets the vertical alignment of the text. This must be +// either AlignTop, AlignMiddle, or AlignBottom. +func (t *TextView) SetVerticalAlign(valign VerticalAlignment) { + t.Lock() + defer t.Unlock() + + if t.valign != valign { + t.index = nil + } + t.valign = valign +} + // SetTextColor sets the initial color of the text (which can be changed // dynamically by sending color strings in square brackets to the text view if // dynamic colors are enabled). @@ -1118,6 +1134,16 @@ func (t *TextView) Draw(screen tcell.Screen) { } } + // Calculate offset to apply vertical alignment + verticalOffset := 0 + if len(t.index) < height { + if t.valign == AlignMiddle { + verticalOffset = (height - len(t.index)) / 2 + } else if t.valign == AlignBottom { + verticalOffset = height - len(t.index) + } + } + // Draw the buffer. defaultStyle := tcell.StyleDefault.Foreground(t.textColor).Background(t.backgroundColor) for line := t.lineOffset; line < len(t.index); line++ { @@ -1160,8 +1186,10 @@ func (t *TextView) Draw(screen tcell.Screen) { posX = 0 } + drawAtY := y + line - t.lineOffset + verticalOffset + // Print the line. - if y+line-t.lineOffset >= 0 { + if drawAtY >= 0 { var colorPos, regionPos, escapePos, tagOffset, skipped int iterateString(string(strippedText), func(main rune, comb []rune, textPos, textWidth, screenPos, screenWidth int) bool { // Process tags. @@ -1203,7 +1231,7 @@ func (t *TextView) Draw(screen tcell.Screen) { } // Mix the existing style with the new style. - _, _, existingStyle, _ := screen.GetContent(x+posX, y+line-t.lineOffset) + _, _, existingStyle, _ := screen.GetContent(x+posX, drawAtY) _, background, _ := existingStyle.Decompose() style := overlayStyle(background, defaultStyle, foregroundColor, backgroundColor, attributes) @@ -1250,9 +1278,9 @@ func (t *TextView) Draw(screen tcell.Screen) { // Draw the character. for offset := screenWidth - 1; offset >= 0; offset-- { if offset == 0 { - screen.SetContent(x+posX+offset, y+line-t.lineOffset, main, comb, style) + screen.SetContent(x+posX+offset, drawAtY, main, comb, style) } else { - screen.SetContent(x+posX+offset, y+line-t.lineOffset, ' ', nil, style) + screen.SetContent(x+posX+offset, drawAtY, ' ', nil, style) } } diff --git a/util.go b/util.go index cd0b514..bc405e1 100644 --- a/util.go +++ b/util.go @@ -21,13 +21,23 @@ var TrueColorTags = false // value of color, ColorDefault, results in default terminal colors. var ColorUnset = tcell.ColorSpecial | 108 -// Text alignment within a box. +// Horizontal alignment within a box. const ( AlignLeft = iota AlignCenter AlignRight ) +// VerticalAlignment represents vertical alignment. +type VerticalAlignment int + +// Vertical alignment within a box. +const ( + AlignTop VerticalAlignment = iota + AlignMiddle + AlignBottom +) + // Common regular expressions. var ( colorPattern = regexp.MustCompile(`\[([a-zA-Z]+|#[0-9a-zA-Z]{6}|\-)?(:([a-zA-Z]+|#[0-9a-zA-Z]{6}|\-)?(:([bdilrsu]+|\-)?)?)?\]`)