|
|
|
@ -142,6 +142,10 @@ type TextView struct {
|
|
|
|
|
sync.Mutex
|
|
|
|
|
*Box
|
|
|
|
|
|
|
|
|
|
// The size of the text area. If set to 0, the text view will use the entire
|
|
|
|
|
// available space.
|
|
|
|
|
width, height int
|
|
|
|
|
|
|
|
|
|
// The text buffer.
|
|
|
|
|
buffer []string
|
|
|
|
|
|
|
|
|
@ -152,6 +156,15 @@ type TextView struct {
|
|
|
|
|
// to be re-indexed.
|
|
|
|
|
index []*textViewIndex
|
|
|
|
|
|
|
|
|
|
// The label text shown, usually when part of a form.
|
|
|
|
|
label string
|
|
|
|
|
|
|
|
|
|
// The width of the text area's label.
|
|
|
|
|
labelWidth int
|
|
|
|
|
|
|
|
|
|
// The label style.
|
|
|
|
|
labelStyle tcell.Style
|
|
|
|
|
|
|
|
|
|
// The text alignment, one of AlignLeft, AlignCenter, or AlignRight.
|
|
|
|
|
align int
|
|
|
|
|
|
|
|
|
@ -206,8 +219,9 @@ type TextView struct {
|
|
|
|
|
// after punctuation characters.
|
|
|
|
|
wordWrap bool
|
|
|
|
|
|
|
|
|
|
// The (starting) color of the text.
|
|
|
|
|
textColor tcell.Color
|
|
|
|
|
// The (starting) style of the text. This also defines the background color
|
|
|
|
|
// of the main text element.
|
|
|
|
|
textStyle tcell.Style
|
|
|
|
|
|
|
|
|
|
// If set to true, the text color can be changed dynamically by piping color
|
|
|
|
|
// strings in square brackets to the text view.
|
|
|
|
@ -235,23 +249,66 @@ type TextView struct {
|
|
|
|
|
// An optional function which is called when one or more regions were
|
|
|
|
|
// highlighted.
|
|
|
|
|
highlighted func(added, removed, remaining []string)
|
|
|
|
|
|
|
|
|
|
// A callback function set by the Form class and called when the user leaves
|
|
|
|
|
// this form item.
|
|
|
|
|
finished func(tcell.Key)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// NewTextView returns a new text view.
|
|
|
|
|
func NewTextView() *TextView {
|
|
|
|
|
return &TextView{
|
|
|
|
|
Box: NewBox(),
|
|
|
|
|
labelStyle: tcell.StyleDefault.Foreground(Styles.SecondaryTextColor),
|
|
|
|
|
highlights: make(map[string]struct{}),
|
|
|
|
|
lineOffset: -1,
|
|
|
|
|
scrollable: true,
|
|
|
|
|
align: AlignLeft,
|
|
|
|
|
wrap: true,
|
|
|
|
|
textColor: Styles.PrimaryTextColor,
|
|
|
|
|
textStyle: tcell.StyleDefault.Background(Styles.PrimitiveBackgroundColor).Foreground(Styles.PrimaryTextColor),
|
|
|
|
|
regions: false,
|
|
|
|
|
dynamicColors: false,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SetLabel sets the text to be displayed before the text view.
|
|
|
|
|
func (t *TextView) SetLabel(label string) *TextView {
|
|
|
|
|
t.label = label
|
|
|
|
|
return t
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// GetLabel returns the text to be displayed before the text view.
|
|
|
|
|
func (t *TextView) GetLabel() string {
|
|
|
|
|
return t.label
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SetLabelWidth sets the screen width of the label. A value of 0 will cause the
|
|
|
|
|
// primitive to use the width of the label string.
|
|
|
|
|
func (t *TextView) SetLabelWidth(width int) *TextView {
|
|
|
|
|
t.labelWidth = width
|
|
|
|
|
return t
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SetSize sets the screen size of the main text element of the text view. This
|
|
|
|
|
// element is always located next to the label which is always located in the
|
|
|
|
|
// top left corner. If any of the values are 0 or larger than the available
|
|
|
|
|
// space, the available space will be used.
|
|
|
|
|
func (t *TextView) SetSize(rows, columns int) *TextView {
|
|
|
|
|
t.width = columns
|
|
|
|
|
t.height = rows
|
|
|
|
|
return t
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// GetFieldWidth returns this primitive's field width.
|
|
|
|
|
func (t *TextView) GetFieldWidth() int {
|
|
|
|
|
return t.width
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// GetFieldHeight returns this primitive's field height.
|
|
|
|
|
func (t *TextView) GetFieldHeight() int {
|
|
|
|
|
return t.height
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SetScrollable sets the flag that decides whether or not the text view is
|
|
|
|
|
// scrollable. If true, text is kept in a buffer and can be navigated. If false,
|
|
|
|
|
// the last line will always be visible.
|
|
|
|
@ -315,7 +372,16 @@ func (t *TextView) SetTextAlign(align int) *TextView {
|
|
|
|
|
// dynamically by sending color strings in square brackets to the text view if
|
|
|
|
|
// dynamic colors are enabled).
|
|
|
|
|
func (t *TextView) SetTextColor(color tcell.Color) *TextView {
|
|
|
|
|
t.textColor = color
|
|
|
|
|
t.textStyle = t.textStyle.Foreground(color)
|
|
|
|
|
return t
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SetTextStyle sets the initial style of the text (which can be changed
|
|
|
|
|
// dynamically by sending color strings in square brackets to the text view if
|
|
|
|
|
// dynamic colors are enabled). This style's background color also determines
|
|
|
|
|
// the background color of the main text element (even if empty).
|
|
|
|
|
func (t *TextView) SetTextStyle(style tcell.Style) *TextView {
|
|
|
|
|
t.textStyle = style
|
|
|
|
|
return t
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -427,6 +493,22 @@ func (t *TextView) SetHighlightedFunc(handler func(added, removed, remaining []s
|
|
|
|
|
return t
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SetFinishedFunc sets a callback invoked when the user leaves this form item.
|
|
|
|
|
func (t *TextView) SetFinishedFunc(handler func(key tcell.Key)) FormItem {
|
|
|
|
|
t.finished = handler
|
|
|
|
|
return t
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SetFormAttributes sets attributes shared by all form items.
|
|
|
|
|
func (t *TextView) SetFormAttributes(labelWidth int, labelColor, bgColor, fieldTextColor, fieldBgColor tcell.Color) FormItem {
|
|
|
|
|
t.labelWidth = labelWidth
|
|
|
|
|
t.backgroundColor = bgColor
|
|
|
|
|
t.labelStyle = t.labelStyle.Foreground(labelColor)
|
|
|
|
|
// We ignore the field background color because this is a read-only element.
|
|
|
|
|
t.textStyle = tcell.StyleDefault.Foreground(fieldTextColor).Background(bgColor)
|
|
|
|
|
return t
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ScrollTo scrolls to the specified row and column (both starting with 0).
|
|
|
|
|
func (t *TextView) ScrollTo(row, column int) *TextView {
|
|
|
|
|
if !t.scrollable {
|
|
|
|
@ -668,6 +750,13 @@ func (t *TextView) Focus(delegate func(p Primitive)) {
|
|
|
|
|
t.Lock()
|
|
|
|
|
defer t.Unlock()
|
|
|
|
|
t.Box.Focus(delegate)
|
|
|
|
|
|
|
|
|
|
// But if we're part of a form and not scrollable, there's nothing the user
|
|
|
|
|
// can do here so we're finished.
|
|
|
|
|
if t.finished != nil && !t.scrollable {
|
|
|
|
|
t.finished(-1)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// HasFocus returns whether or not this primitive has focus.
|
|
|
|
@ -1014,12 +1103,48 @@ func (t *TextView) Draw(screen tcell.Screen) {
|
|
|
|
|
t.Box.DrawForSubclass(screen, t)
|
|
|
|
|
t.Lock()
|
|
|
|
|
defer t.Unlock()
|
|
|
|
|
totalWidth, totalHeight := screen.Size()
|
|
|
|
|
|
|
|
|
|
// Get the available size.
|
|
|
|
|
x, y, width, height := t.GetInnerRect()
|
|
|
|
|
t.pageSize = height
|
|
|
|
|
|
|
|
|
|
// Draw label.
|
|
|
|
|
_, labelBg, _ := t.labelStyle.Decompose()
|
|
|
|
|
if t.labelWidth > 0 {
|
|
|
|
|
labelWidth := t.labelWidth
|
|
|
|
|
if labelWidth > width {
|
|
|
|
|
labelWidth = width
|
|
|
|
|
}
|
|
|
|
|
printWithStyle(screen, t.label, x, y, 0, labelWidth, AlignLeft, t.labelStyle, labelBg == tcell.ColorDefault)
|
|
|
|
|
x += labelWidth
|
|
|
|
|
width -= labelWidth
|
|
|
|
|
} else {
|
|
|
|
|
_, drawnWidth, _, _ := printWithStyle(screen, t.label, x, y, 0, width, AlignLeft, t.labelStyle, labelBg == tcell.ColorDefault)
|
|
|
|
|
x += drawnWidth
|
|
|
|
|
width -= drawnWidth
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// What's the space for the text element?
|
|
|
|
|
if t.width > 0 && t.width < width {
|
|
|
|
|
width = t.width
|
|
|
|
|
}
|
|
|
|
|
if t.height > 0 && t.height < height {
|
|
|
|
|
height = t.height
|
|
|
|
|
}
|
|
|
|
|
if width <= 0 {
|
|
|
|
|
return // No space left for the text area.
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Draw the text element if necessary.
|
|
|
|
|
_, bg, _ := t.textStyle.Decompose()
|
|
|
|
|
if bg != t.GetBackgroundColor() {
|
|
|
|
|
for row := 0; row < height; row++ {
|
|
|
|
|
for column := 0; column < width; column++ {
|
|
|
|
|
screen.SetContent(x+column, y+row, ' ', nil, t.textStyle)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If the width has changed, we need to reindex.
|
|
|
|
|
if width != t.lastWidth && t.wrap {
|
|
|
|
|
t.index = nil
|
|
|
|
@ -1101,10 +1226,9 @@ func (t *TextView) Draw(screen tcell.Screen) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Draw the buffer.
|
|
|
|
|
defaultStyle := tcell.StyleDefault.Foreground(t.textColor).Background(t.backgroundColor)
|
|
|
|
|
for line := t.lineOffset; line < len(t.index); line++ {
|
|
|
|
|
// Are we done?
|
|
|
|
|
if line-t.lineOffset >= height || y+line-t.lineOffset >= totalHeight {
|
|
|
|
|
if line-t.lineOffset >= height {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -1193,7 +1317,7 @@ func (t *TextView) Draw(screen tcell.Screen) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Mix the existing style with the new style.
|
|
|
|
|
style := overlayStyle(defaultStyle, foregroundColor, backgroundColor, attributes)
|
|
|
|
|
style := overlayStyle(t.textStyle, foregroundColor, backgroundColor, attributes)
|
|
|
|
|
|
|
|
|
|
// Do we highlight this character?
|
|
|
|
|
var highlighted bool
|
|
|
|
@ -1224,7 +1348,7 @@ func (t *TextView) Draw(screen tcell.Screen) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Stop at the right border.
|
|
|
|
|
if posX+screenWidth > width || x+posX >= totalWidth {
|
|
|
|
|
if posX+screenWidth > width {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -1266,6 +1390,9 @@ func (t *TextView) InputHandler() func(event *tcell.EventKey, setFocus func(p Pr
|
|
|
|
|
if t.done != nil {
|
|
|
|
|
t.done(key)
|
|
|
|
|
}
|
|
|
|
|
if t.finished != nil {
|
|
|
|
|
t.finished(key)
|
|
|
|
|
}
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|