github.com/grahambrereton-form3/tilt@v0.10.18/internal/rty/canvas.go (about)

     1  package rty
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/gdamore/tcell"
     7  )
     8  
     9  // Canvases hold content.
    10  
    11  type Canvas interface {
    12  	Size() (int, int)
    13  	SetContent(x int, y int, mainc rune, combc []rune, style tcell.Style) error
    14  	Close() (int, int)
    15  	GetContent(x, y int) (mainc rune, combc []rune, style tcell.Style, width int, err error)
    16  }
    17  
    18  func totalHeight(canvases []Canvas) int {
    19  	total := 0
    20  	for _, c := range canvases {
    21  		_, h := c.Size()
    22  		total += h
    23  	}
    24  	return total
    25  }
    26  
    27  // Implementations below
    28  type cell struct {
    29  	ch    rune
    30  	style tcell.Style
    31  }
    32  
    33  type TempCanvas struct {
    34  	width  int
    35  	height int
    36  	cells  [][]cell
    37  	style  tcell.Style
    38  }
    39  
    40  var _ Canvas = &TempCanvas{}
    41  
    42  func newTempCanvas(width, height int, style tcell.Style) *TempCanvas {
    43  	c := &TempCanvas{width: width, height: height}
    44  	if height != GROW {
    45  		c.cells = make([][]cell, height)
    46  		for i := 0; i < height; i++ {
    47  			c.cells[i] = c.makeRow()
    48  		}
    49  	}
    50  	return c
    51  }
    52  
    53  func (c *TempCanvas) Size() (int, int) {
    54  	return c.width, c.height
    55  }
    56  
    57  func (c *TempCanvas) Close() (int, int) {
    58  	if c.height == GROW {
    59  		c.height = len(c.cells)
    60  	}
    61  	return c.width, c.height
    62  }
    63  
    64  func (c *TempCanvas) makeRow() []cell {
    65  	row := make([]cell, c.width)
    66  	for i := 0; i < c.width; i++ {
    67  		row[i].style = c.style
    68  	}
    69  	return row
    70  }
    71  
    72  func (c *TempCanvas) SetContent(x int, y int, mainc rune, combc []rune, style tcell.Style) error {
    73  	if mainc == 0 {
    74  		mainc = ' '
    75  	}
    76  	if x < 0 || x >= c.width || y < 0 || y >= c.height {
    77  		return fmt.Errorf("cell %v,%v outside canvas %v,%v", x, y, c.width, c.height)
    78  	}
    79  
    80  	for y >= len(c.cells) {
    81  		c.cells = append(c.cells, c.makeRow())
    82  	}
    83  
    84  	c.cells[y][x] = cell{ch: mainc, style: style}
    85  	return nil
    86  }
    87  
    88  func (c *TempCanvas) GetContent(x, y int) (mainc rune, combc []rune, style tcell.Style, width int, err error) {
    89  	if x < 0 || x >= c.width || y < 0 || y >= c.height {
    90  		return 0, nil, 0, 0, fmt.Errorf("cell %d, %d outside bounds %d, %d", x, y, c.width, c.height)
    91  	}
    92  
    93  	if y >= len(c.cells) {
    94  		return 0, nil, tcell.StyleDefault, 1, nil
    95  	}
    96  
    97  	cell := c.cells[y][x]
    98  	return cell.ch, nil, cell.style, 1, nil
    99  }
   100  
   101  type SubCanvas struct {
   102  	del       Canvas
   103  	startX    int
   104  	startY    int
   105  	width     int
   106  	height    int
   107  	highWater int
   108  	style     tcell.Style
   109  	needsFill bool
   110  }
   111  
   112  func newSubCanvas(del Canvas, startX int, startY int, width int, height int, style tcell.Style) (*SubCanvas, error) {
   113  	_, delHeight := del.Size()
   114  	if height == GROW && delHeight != GROW {
   115  		return nil, fmt.Errorf("can't create a growing subcanvas from a non-growing subcanvas")
   116  	}
   117  
   118  	needsFill := true
   119  	delSubCanvas, ok := del.(*SubCanvas)
   120  	if ok {
   121  		// If this is a subcanvas of a subcanvas with the exact same style (or with
   122  		// only the foreground different), we already reset the canvas to the
   123  		// current style. No need to re-fill.
   124  		needsFill = style.Foreground(tcell.ColorDefault) !=
   125  			delSubCanvas.style.Foreground(tcell.ColorDefault)
   126  	}
   127  
   128  	r := &SubCanvas{
   129  		del:       del,
   130  		startX:    startX,
   131  		startY:    startY,
   132  		width:     width,
   133  		height:    height,
   134  		highWater: -1,
   135  		style:     style,
   136  		needsFill: needsFill,
   137  	}
   138  	if needsFill {
   139  		err := r.fill(-1)
   140  		if err != nil {
   141  			return nil, err
   142  		}
   143  	}
   144  	return r, nil
   145  }
   146  
   147  func (c *SubCanvas) Size() (int, int) {
   148  	return c.width, c.height
   149  }
   150  
   151  func (c *SubCanvas) Close() (int, int) {
   152  	if c.height == GROW {
   153  		c.height = c.highWater + 1
   154  	}
   155  	return c.width, c.height
   156  }
   157  
   158  func (c *SubCanvas) SetContent(x int, y int, mainc rune, combc []rune, style tcell.Style) error {
   159  	if mainc == 0 {
   160  		mainc = ' '
   161  	}
   162  	if x < 0 || x >= c.width || y < 0 || y >= c.height {
   163  		return fmt.Errorf("coord %d,%d is outside bounds %d,%d", x, y, c.width, c.height)
   164  	}
   165  
   166  	if c.height == GROW && y > c.highWater {
   167  		oldHighWater := c.highWater
   168  		c.highWater = y
   169  		if c.needsFill {
   170  			err := c.fill(oldHighWater)
   171  			if err != nil {
   172  				return err
   173  			}
   174  		}
   175  	}
   176  	return c.del.SetContent(c.startX+x, c.startY+y, mainc, combc, style)
   177  }
   178  
   179  func (c *SubCanvas) fill(lastFilled int) error {
   180  	startY := lastFilled + 1
   181  	maxY := c.height
   182  	if maxY == GROW {
   183  		maxY = c.highWater + 1
   184  	}
   185  	for y := startY; y < maxY; y++ {
   186  		for x := 0; x < c.width; x++ {
   187  			if err := c.del.SetContent(c.startX+x, c.startY+y, ' ', nil, c.style); err != nil {
   188  				return err
   189  			}
   190  		}
   191  	}
   192  
   193  	return nil
   194  }
   195  
   196  func (c *SubCanvas) GetContent(x int, y int) (rune, []rune, tcell.Style, int, error) {
   197  	return c.del.GetContent(x, y)
   198  }
   199  
   200  type ScreenCanvas struct {
   201  	del tcell.Screen
   202  }
   203  
   204  var _ Canvas = &ScreenCanvas{}
   205  
   206  func newScreenCanvas(del tcell.Screen) *ScreenCanvas {
   207  	return &ScreenCanvas{del: del}
   208  }
   209  
   210  func (c *ScreenCanvas) Size() (int, int) {
   211  	return c.del.Size()
   212  }
   213  
   214  func (c *ScreenCanvas) SetContent(x int, y int, mainc rune, combc []rune, style tcell.Style) error {
   215  	if mainc == 0 {
   216  		mainc = ' '
   217  	}
   218  	c.del.SetContent(x, y, mainc, combc, style)
   219  	return nil
   220  }
   221  
   222  func (c *ScreenCanvas) Close() (int, int) {
   223  	return c.del.Size()
   224  }
   225  
   226  func (c *ScreenCanvas) GetContent(x, y int) (mainc rune, combc []rune, style tcell.Style, width int, err error) {
   227  	mainc, combc, style, width = c.del.GetContent(x, y)
   228  	return mainc, combc, style, width, nil
   229  }