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" 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 lines []string 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 var ch rune 48 if x < 0 || y < 0 || y >= len(m.lines) || x >= len(m.lines[y]) { 49 return ch, m.style, nil, 1 50 } 51 // XXX: extend this to support combining and full width chars 52 return rune(m.lines[y][x]), m.style, nil, 1 53} 54 55func (m *linesModel) GetBounds() (int, int) { 56 return m.width, m.height 57} 58 59func (m *linesModel) limitCursor() { 60 if m.x > m.width-1 { 61 m.x = m.width - 1 62 } 63 if m.y > m.height-1 { 64 m.y = m.height - 1 65 } 66 if m.x < 0 { 67 m.x = 0 68 } 69 if m.y < 0 { 70 m.y = 0 71 } 72} 73 74func (m *linesModel) SetCursor(x, y int) { 75 m.x = x 76 m.y = y 77 m.limitCursor() 78} 79 80func (m *linesModel) MoveCursor(x, y int) { 81 m.x += x 82 m.y += y 83 m.limitCursor() 84} 85 86func (m *linesModel) GetCursor() (int, int, bool, bool) { 87 return m.x, m.y, m.cursor, !m.hide 88} 89 90// SetLines sets the content text to display. 91func (ta *TextArea) SetLines(lines []string) { 92 ta.Init() 93 m := ta.model 94 m.width = 0 95 m.height = len(lines) 96 m.lines = append([]string{}, lines...) 97 for _, l := range lines { 98 if len(l) > m.width { 99 m.width = len(l) 100 } 101 } 102 ta.CellView.SetModel(m) 103} 104 105func (ta *TextArea) SetStyle(style tcell.Style) { 106 ta.model.style = style 107 ta.CellView.SetStyle(style) 108} 109 110// EnableCursor enables a soft cursor in the TextArea. 111func (ta *TextArea) EnableCursor(on bool) { 112 ta.Init() 113 ta.model.cursor = on 114} 115 116// HideCursor hides or shows the cursor in the TextArea. 117// If on is true, the cursor is hidden. Note that a cursor is only 118// shown if it is enabled. 119func (ta *TextArea) HideCursor(on bool) { 120 ta.Init() 121 ta.model.hide = on 122} 123 124// SetContent is used to set the textual content, passed as a 125// single string. Lines within the string are delimited by newlines. 126func (ta *TextArea) SetContent(text string) { 127 ta.Init() 128 lines := strings.Split(strings.Trim(text, "\n"), "\n") 129 ta.SetLines(lines) 130} 131 132// Init initializes the TextArea. 133func (ta *TextArea) Init() { 134 ta.once.Do(func() { 135 lm := &linesModel{lines: []string{}, width: 0} 136 ta.model = lm 137 ta.CellView.Init() 138 ta.CellView.SetModel(lm) 139 }) 140} 141 142// NewTextArea creates a blank TextArea. 143func NewTextArea() *TextArea { 144 ta := &TextArea{} 145 ta.Init() 146 return ta 147} 148