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 }