package stone // Color represents all the different colors a cell can be. type Color uint8 const ( ColorBackground Color = 0x0 ColorSelection Color = 0x1 ColorForeground Color = 0x2 ColorApplication Color = 0x3 ) // Style contains styling information about cells. These properties can be or'd // together to combine them. type Style uint8 const ( StyleNormal Style = iota StyleBold Style = iota >> 1 StyleItalic StyleBoldItalic Style = StyleBold | StyleItalic ) // Cell is a grid-aligned rune in a buffer with associated styling and color // informaiton. type Cell struct { color Color style Style content rune } // Color returns the cell's color. func (cell Cell) Color (color Color) { color = cell.color return } // Style returns the styling information associated with the cell func (cell Cell) Style (style Style) { style = cell.style return } // Rune returns the rune in the cell func (cell Cell) Rune () (content rune) { content = cell.content return } // Buffer is a basic grid of cells. type Buffer struct { content []Cell width int height int Dot struct { X int Y int } } func (buffer *Buffer) isOutOfBounds (x, y int) (outOfBounds bool) { outOfBounds = x < 0 || y < 0 || x >= buffer.width || y >= buffer.height return } // Size returns the width and height of the buffer. func (buffer *Buffer) Size () (width, height int) { width = buffer.width height = buffer.height return } // SetSize sets the width and height of the buffer. This clears all data in the // buffer. If the width or height is negative, this method does nothing. func (buffer *Buffer) SetSize (width, height int) { if width < 0 || height < 0 { return } buffer.width = width buffer.height = height buffer.content = make([]Cell, width * height) } // Cell returns the cell at the specified x and y coordinates. If the // coordinates are out of bounds, this method will return a blank cell. func (buffer *Buffer) Cell (x, y int) (cell Cell) { if buffer.isOutOfBounds(x, y) { return } cell = buffer.content[x + y * buffer.width] return } // SetColor sets the color of the cell at the specified x and y coordinates. func (buffer *Buffer) SetColor (x, y int, color Color) { if buffer.isOutOfBounds(x, y) { return } buffer.content[x + y * buffer.width].color = color } // SetStyle sets the style of the cell at the specified x and y coordinates. func (buffer *Buffer) SetStyle (x, y int, style Style) { if buffer.isOutOfBounds(x, y) { return } buffer.content[x + y * buffer.width].style = style } // SetRune sets the rune of the cell at the specified x and y coordinates. func (buffer *Buffer) SetRune (x, y int, content rune) { if buffer.isOutOfBounds(x, y) { return } buffer.content[x + y * buffer.width].content = content } // Write writes data stored in a byte slice to the buffer at the current dot // position. This makes Buffer an io.Writer. func (buffer *Buffer) Write (bytes []byte) (bytesWritten int, err error) { text := string(bytes) bytesWritten = len(bytes) for _, character := range text { buffer.SetRune(buffer.Dot.X, buffer.Dot.Y, character) buffer.Dot.X ++ if buffer.Dot.X > buffer.width { break } } return } // ResetDot is a convenience method to reset the dot to the buffer origin point // (0, 0). func (buffer *Buffer) ResetDot () { buffer.Dot.X = 0 buffer.Dot.Y = 0 } // DamageBuffer is a special buffer that keeps track of damage information. // Cells are dirty by default, are only clean when marked as clean, and become // dirty again when they are altered in some way. type DamageBuffer struct { Buffer clean []bool } // SetSize sets the width and height of the buffer. This clears all data in the // buffer. If the width or height is negative, this method does nothing. func (buffer *DamageBuffer) SetSize (width, height int) { if width < 0 || height < 0 { return } buffer.Buffer.SetSize(width, height) buffer.clean = make([]bool, width * height) } // SetColor sets the color of the cell at the specified x and y coordinates. func (buffer *DamageBuffer) SetColor (x, y int, color Color) { if buffer.isOutOfBounds(x, y) { return } index := x + y * buffer.width buffer.clean[index] = buffer.content[index].color == color buffer.Buffer.SetColor(x, y, color) } // SetStyle sets the style of the cell at the specified x and y coordinates. func (buffer *DamageBuffer) SetStyle (x, y int, style Style) { if buffer.isOutOfBounds(x, y) { return } index := x + y * buffer.width buffer.clean[index] = buffer.content[index].style == style buffer.Buffer.SetStyle(x, y, style) } // SetRune sets the rune of the cell at the specified x and y coordinates. func (buffer *DamageBuffer) SetRune (x, y int, content rune) { if buffer.isOutOfBounds(x, y) { return } index := x + y * buffer.width buffer.clean[index] = buffer.content[index].content == content buffer.Buffer.SetRune(x, y, content) } // Write writes data stored in a byte slice to the buffer at the current dot // position. This makes DamageBuffer an io.Writer. func (buffer *DamageBuffer) Write (bytes []byte) (bytesWritten int, err error) { text := string(bytes) bytesWritten = len(bytes) for _, character := range text { buffer.SetRune(buffer.Dot.X, buffer.Dot.Y, character) buffer.Dot.X ++ if buffer.Dot.X > buffer.width { break } } return } // Clean returns whether or not the cell at the specified x and y coordinates is // clean. func (buffer *DamageBuffer) Clean (x, y int) (clean bool) { if buffer.isOutOfBounds(x, y) { return } clean = buffer.clean[x + y * buffer.width] return } // MarkClean marks the cell at the specified x and y coordinates as clean. func (buffer *DamageBuffer) MarkClean (x, y int) { if buffer.isOutOfBounds(x, y) { return } buffer.clean[x + y * buffer.width] = true }