1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
|
package cursor
import (
"os"
"strings"
)
// Area displays content which can be updated on the fly.
// You can use this to create live output, charts, dropdowns, etc.
type Area struct {
height int
writer Writer
cursor *Cursor
cursorPosY int
}
// NewArea returns a new Area.
func NewArea() Area {
return Area{
height: 0,
writer: os.Stdout,
cursor: cursor,
cursorPosY: 0,
}
}
// WithWriter sets the custom writer.
func (area Area) WithWriter(writer Writer) Area {
area.writer = writer
area.cursor = area.cursor.WithWriter(writer)
return area
}
// Clear clears the content of the Area.
func (area *Area) Clear() {
// Initialize writer if not done yet
if area.writer == nil {
area.writer = os.Stdout
}
if area.height > 0 {
area.Bottom()
area.ClearLinesUp(area.height)
area.StartOfLine()
} else {
area.StartOfLine()
area.cursor.ClearLine()
}
}
// Update overwrites the content of the Area and adjusts its height based on content.
func (area *Area) Update(content string) {
area.Clear()
area.writeArea(content)
area.cursorPosY = 0
area.height = strings.Count(content, "\n")
}
// Up moves the cursor of the area up one line.
func (area *Area) Up(n int) {
if n > 0 {
if area.cursorPosY+n > area.height {
n = area.height - area.cursorPosY
}
area.cursor.Up(n)
area.cursorPosY += n
}
}
// Down moves the cursor of the area down one line.
func (area *Area) Down(n int) {
if n > 0 {
if area.cursorPosY-n < 0 {
n = area.height - area.cursorPosY
}
area.cursor.Down(n)
area.cursorPosY -= n
}
}
// Bottom moves the cursor to the bottom of the terminal.
// This is done by calculating how many lines were moved by Up and Down.
func (area *Area) Bottom() {
if area.cursorPosY > 0 {
area.Down(area.cursorPosY)
area.cursorPosY = 0
}
}
// Top moves the cursor to the top of the area.
// This is done by calculating how many lines were moved by Up and Down.
func (area *Area) Top() {
if area.cursorPosY < area.height {
area.Up(area.height - area.cursorPosY)
area.cursorPosY = area.height
}
}
// StartOfLine moves the cursor to the start of the current line.
func (area *Area) StartOfLine() {
area.cursor.HorizontalAbsolute(0)
}
// StartOfLineDown moves the cursor down by n lines, then moves to cursor to the start of the line.
func (area *Area) StartOfLineDown(n int) {
area.Down(n)
area.StartOfLine()
}
// StartOfLineUp moves the cursor up by n lines, then moves to cursor to the start of the line.
func (area *Area) StartOfLineUp(n int) {
area.Up(n)
area.StartOfLine()
}
// UpAndClear moves the cursor up by n lines, then clears the line.
func (area *Area) UpAndClear(n int) {
area.Up(n)
area.cursor.ClearLine()
}
// DownAndClear moves the cursor down by n lines, then clears the line.
func (area *Area) DownAndClear(n int) {
area.Down(n)
area.cursor.ClearLine()
}
// Move moves the cursor relative by x and y.
func (area *Area) Move(x, y int) {
if x > 0 {
area.cursor.Right(x)
} else if x < 0 {
area.cursor.Left(-x)
}
if y > 0 {
area.Up(y)
} else if y < 0 {
area.Down(-y)
}
}
// ClearLinesUp clears n lines upwards from the current position and moves the cursor.
func (area *Area) ClearLinesUp(n int) {
area.StartOfLine()
area.cursor.ClearLine()
for i := 0; i < n; i++ {
area.UpAndClear(1)
}
}
// ClearLinesDown clears n lines downwards from the current position and moves the cursor.
func (area *Area) ClearLinesDown(n int) {
area.StartOfLine()
area.cursor.ClearLine()
for i := 0; i < n; i++ {
area.DownAndClear(1)
}
}
|