1// Copyright 2016 The Tcell Authors 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use file except in compliance with the License. 5// You may obtain a copy of the license at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package views 16 17import ( 18 "strings" 19 "sync" 20 21 "github.com/gdamore/tcell/v2" 22) 23 24// TextArea is a pannable 2 dimensional text widget. It wraps both 25// the view and the model for text in a single, convenient widget. 26// Text is provided as an array of strings, each of which represents 27// a single line to display. All text in the TextArea has the same 28// style. An optional soft cursor is available. 29type TextArea struct { 30 model *linesModel 31 once sync.Once 32 CellView 33} 34 35type linesModel struct { 36 runes [][]rune 37 width int 38 height int 39 x int 40 y int 41 hide bool 42 cursor bool 43 style tcell.Style 44} 45 46func (m *linesModel) GetCell(x, y int) (rune, tcell.Style, []rune, int) { 47 if x < 0 || y < 0 || y >= m.height || x >= len(m.runes[y]) { 48 return 0, m.style, nil, 1 49 } 50 // XXX: extend this to support combining and full width chars 51 return m.runes[y][x], m.style, nil, 1 52} 53 54func (m *linesModel) GetBounds() (int, int) { 55 return m.width, m.height 56} 57 58func (m *linesModel) limitCursor() { 59 if m.x > m.width-1 { 60 m.x = m.width - 1 61 } 62 if m.y > m.height-1 { 63 m.y = m.height - 1 64 } 65 if m.x < 0 { 66 m.x = 0 67 } 68 if m.y < 0 { 69 m.y = 0 70 } 71} 72 73func (m *linesModel) SetCursor(x, y int) { 74 m.x = x 75 m.y = y 76 m.limitCursor() 77} 78 79func (m *linesModel) MoveCursor(x, y int) { 80 m.x += x 81 m.y += y 82 m.limitCursor() 83} 84 85func (m *linesModel) GetCursor() (int, int, bool, bool) { 86 return m.x, m.y, m.cursor, !m.hide 87} 88 89// SetLines sets the content text to display. 90func (ta *TextArea) SetLines(lines []string) { 91 ta.Init() 92 m := ta.model 93 m.width =0 94 95 // extend slice before using m.runes[row] to avoid panic 96 slice := make([][]rune, len(lines)) 97 m.runes = slice 98 99 for row, line := range lines { 100 for _, ch := range line { 101 m.runes[row] = append(m.runes[row], ch) 102 } 103 if len(m.runes[row]) > m.width { 104 m.width = len(m.runes[row]) 105 } 106 } 107 108 m.height = len(m.runes) 109 110 ta.CellView.SetModel(m) 111} 112 113func (ta *TextArea) SetStyle(style tcell.Style) { 114 ta.model.style = style 115 ta.CellView.SetStyle(style) 116} 117 118// EnableCursor enables a soft cursor in the TextArea. 119func (ta *TextArea) EnableCursor(on bool) { 120 ta.Init() 121 ta.model.cursor = on 122} 123 124// HideCursor hides or shows the cursor in the TextArea. 125// If on is true, the cursor is hidden. Note that a cursor is only 126// shown if it is enabled. 127func (ta *TextArea) HideCursor(on bool) { 128 ta.Init() 129 ta.model.hide = on 130} 131 132// SetContent is used to set the textual content, passed as a 133// single string. Lines within the string are delimited by newlines. 134func (ta *TextArea) SetContent(text string) { 135 ta.Init() 136 lines := strings.Split(strings.Trim(text, "\n"), "\n") 137 ta.SetLines(lines) 138} 139 140// Init initializes the TextArea. 141func (ta *TextArea) Init() { 142 ta.once.Do(func() { 143 lm := &linesModel{runes: [][]rune{}, width: 0} 144 ta.model = lm 145 ta.CellView.Init() 146 ta.CellView.SetModel(lm) 147 }) 148} 149 150// NewTextArea creates a blank TextArea. 151func NewTextArea() *TextArea { 152 ta := &TextArea{} 153 ta.Init() 154 return ta 155} 156