package stone // Color represents all the different colors a cell can be. type Color uint8 const ( ColorBackground Color = 0x0 ColorForeground Color = 0x1 ColorRed Color = 0x2 ColorOrange Color = 0x3 ColorYellow Color = 0x4 ColorGreen Color = 0x5 ColorBlue Color = 0x6 ColorPurple Color = 0x7 ) // 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 two dimensional text buffer that stores a grid of cells, as well // as information stating whether each cell is clean or dirty. Cells are dirty // by default, are only clean when marked as clean, and become dirty again when // they are altered in some way. type Buffer struct { content []Cell clean []bool 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 } // 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 } // 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 } index := x + y * buffer.width buffer.clean[index] = buffer.content[index].color == color buffer.content[index].color = color } // 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) buffer.clean = make([]bool, width * height) for index := 0; index < len(buffer.content); index ++ { buffer.content[index].color = ColorForeground } } // 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 } index := x + y * buffer.width buffer.clean[index] = buffer.content[index].style == style buffer.content[index].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 } index := x + y * buffer.width buffer.clean[index] = buffer.content[index].content == content buffer.content[index].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 } // Clean returns whether or not the cell at the specified x and y coordinates is // clean. func (buffer *Buffer) 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 *Buffer) MarkClean (x, y int) { if buffer.isOutOfBounds(x, y) { return } buffer.clean[x + y * buffer.width] = true }