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  }