github.com/xyproto/orbiton/v2@v2.65.12-0.20240516144430-e10a419274ec/position.go (about) 1 package main 2 3 import ( 4 "errors" 5 "sync" 6 7 "github.com/xyproto/vt100" 8 ) 9 10 // Position represents a position on the screen, including how far down the view has scrolled 11 type Position struct { 12 mut *sync.RWMutex // for the position 13 sx int // the position of the cursor in the current scrollview 14 sy int // the position of the cursor in the current scrollview 15 offsetX int // how far one has scrolled along the X axis 16 offsetY int // how far one has scrolled along the Y axis 17 scrollSpeed int // how many lines to scroll, when scrolling up and down 18 savedX int // for smart down cursor movement 19 } 20 21 // NewPosition returns a new Position struct 22 func NewPosition(scrollSpeed int) *Position { 23 return &Position{&sync.RWMutex{}, 0, 0, 0, 0, scrollSpeed, 0} 24 } 25 26 // Copy will create a new Position struct that is a copy of this one 27 func (p *Position) Copy() *Position { 28 return &Position{p.mut, p.sx, p.sy, p.offsetX, p.offsetY, p.scrollSpeed, p.savedX} 29 } 30 31 // ScreenX returns the screen X position in the current view 32 func (p *Position) ScreenX() int { 33 var x int 34 p.mut.RLock() 35 x = p.sx 36 p.mut.RUnlock() 37 return x 38 } 39 40 // ScreenY returns the screen Y position in the current view 41 func (p *Position) ScreenY() int { 42 var y int 43 p.mut.RLock() 44 y = p.sy 45 p.mut.RUnlock() 46 return y 47 } 48 49 // OffsetX returns the X scroll offset for the current view 50 func (p *Position) OffsetX() int { 51 var x int 52 p.mut.RLock() 53 x = p.offsetX 54 p.mut.RUnlock() 55 return x 56 } 57 58 // OffsetY returns the Y scroll offset for the current view 59 func (p *Position) OffsetY() int { 60 var y int 61 p.mut.RLock() 62 y = p.offsetY 63 p.mut.RUnlock() 64 return y 65 } 66 67 // SetX will set the screen X position 68 func (p *Position) SetX(c *vt100.Canvas, x int) { 69 p.mut.Lock() 70 defer p.mut.Unlock() 71 72 p.sx = x 73 w := 80 // default width 74 if c != nil { 75 w = int(c.W()) 76 } 77 if x < w { 78 p.offsetX = 0 79 } else { 80 p.offsetX = (x - w) + 1 81 p.sx -= p.offsetX 82 } 83 } 84 85 // SetY will set the screen Y position 86 func (p *Position) SetY(y int) { 87 p.mut.Lock() 88 defer p.mut.Unlock() 89 90 p.sy = y 91 } 92 93 // DecY will decrease Y by 1 94 func (p *Position) DecY() { 95 p.mut.Lock() 96 defer p.mut.Unlock() 97 98 p.sy-- 99 if p.sy < 0 { 100 p.sy = 0 101 } 102 } 103 104 // IncY will increase Y by 1 105 func (p *Position) IncY(c *vt100.Canvas) { 106 p.mut.Lock() 107 defer p.mut.Unlock() 108 109 h := 25 // default height 110 if c != nil { 111 h = int(c.H()) 112 } 113 114 p.sy++ 115 if p.sy > (h - 1) { 116 p.sy = (h - 1) 117 } 118 } 119 120 // SetOffsetX will set the screen X scrolling offset 121 func (p *Position) SetOffsetX(offsetX int) { 122 p.mut.Lock() 123 defer p.mut.Unlock() 124 125 p.offsetX = offsetX 126 } 127 128 // SetOffsetY will set the screen Y scrolling offset 129 func (p *Position) SetOffsetY(offsetY int) { 130 p.mut.Lock() 131 defer p.mut.Unlock() 132 133 p.offsetY = offsetY 134 } 135 136 // Up will move the cursor up 137 func (p *Position) Up() error { 138 p.mut.Lock() 139 defer p.mut.Unlock() 140 if p.sy <= 0 { 141 return errors.New("already at the top of the canvas") 142 } 143 p.sy-- 144 return nil 145 } 146 147 // Down will move the cursor down 148 func (p *Position) Down(c *vt100.Canvas) error { 149 p.mut.Lock() 150 defer p.mut.Unlock() 151 h := 25 // default height 152 if c != nil { 153 h = int(c.H()) 154 } 155 if p.sy >= h-1 { 156 return errors.New("already at the bottom of the canvas") 157 } 158 p.sy++ 159 return nil 160 } 161 162 // AtStartOfScreenLine returns true if the position is at the very start of the line, regardless of whitespace and scrolling 163 func (p *Position) AtStartOfScreenLine() bool { 164 p.mut.RLock() 165 defer p.mut.RUnlock() 166 167 return p.sx == 0 168 } 169 170 // AtStartOfTheLine returns true if the position is at the very start of the line, and the line is not scrolled 171 func (p *Position) AtStartOfTheLine() bool { 172 p.mut.RLock() 173 defer p.mut.RUnlock() 174 175 return p.sx == 0 && p.offsetX == 0 176 } 177 178 // LineIndex returns the current line index this position is at 179 func (p *Position) LineIndex() LineIndex { 180 p.mut.RLock() 181 defer p.mut.RUnlock() 182 183 return LineIndex(p.offsetY + p.sy) 184 } 185 186 // LineNumber returns the current line number this Position is at 187 func (p *Position) LineNumber() LineNumber { 188 p.mut.RLock() 189 defer p.mut.RUnlock() 190 191 return LineIndex(p.offsetY + p.sy).LineNumber() 192 } 193 194 // ColNumber returns the current column number this Position is at 195 func (p *Position) ColNumber() ColNumber { 196 p.mut.RLock() 197 defer p.mut.RUnlock() 198 199 return ColIndex(p.offsetX + p.sx).ColNumber() 200 } 201 202 // Right will move the cursor to the right, if possible. 203 // It will not move the cursor up or down. 204 func (p *Position) Right(c *vt100.Canvas) { 205 p.mut.Lock() 206 defer p.mut.Unlock() 207 208 w := 80 // default width 209 if c != nil { 210 w = int(c.Width()) 211 } 212 if p.sx < (w - 1) { 213 p.sx++ 214 } else { 215 p.sx = 0 216 p.offsetX += (w - 1) 217 } 218 } 219 220 // Left will move the cursor to the left, if possible. 221 // It will not move the cursor up or down. 222 func (p *Position) Left() { 223 p.mut.Lock() 224 defer p.mut.Unlock() 225 226 if p.sx > 0 { 227 p.sx-- 228 } 229 }