Merge branch 'master'
This commit is contained in:
commit
0d2e8c70ef
119
buffer.go
Normal file
119
buffer.go
Normal file
@ -0,0 +1,119 @@
|
||||
package buf2d
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Buffer is a 2-dimensional rune buffer
|
||||
type Buffer struct {
|
||||
data [][]fmt.Stringer
|
||||
width int
|
||||
height int
|
||||
parent *Buffer
|
||||
}
|
||||
|
||||
// NewBuffer makes a new buffer with the given dimensions
|
||||
func NewBuffer(width, height int) *Buffer {
|
||||
b := make([][]fmt.Stringer, height)
|
||||
for y := range b {
|
||||
b[y] = make([]fmt.Stringer, width)
|
||||
for x := range b[y] {
|
||||
b[y][x] = spaceStringer
|
||||
}
|
||||
}
|
||||
|
||||
return &Buffer{
|
||||
data: b,
|
||||
width: width,
|
||||
height: height,
|
||||
parent: nil,
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Buffer) x(x int) int {
|
||||
return limit(x, 0, b.width-1)
|
||||
}
|
||||
|
||||
func (b *Buffer) y(y int) int {
|
||||
return limit(y, 0, b.height-1)
|
||||
}
|
||||
|
||||
// Set sets the fmt.Stringer at position (x,y) to c
|
||||
func (b *Buffer) Set(x, y int, c fmt.Stringer) {
|
||||
b.data[b.y(y)][b.x(x)] = c
|
||||
}
|
||||
|
||||
// SetRune sets the fmt.Stringer at position (x,y) to rn
|
||||
func (b *Buffer) SetRune(x, y int, rn rune) {
|
||||
b.data[b.y(y)][b.x(x)] = newStringerFromRune(rn)
|
||||
}
|
||||
|
||||
// Get returns the fmt.Stringer at position (x,y)
|
||||
func (b *Buffer) Get(x, y int) fmt.Stringer {
|
||||
return b.data[y][x]
|
||||
}
|
||||
|
||||
// Size returns width and height of b
|
||||
func (b *Buffer) Size() (w, h int) {
|
||||
return b.width, b.height
|
||||
}
|
||||
|
||||
// Width returns the width of b
|
||||
func (b *Buffer) Width() int {
|
||||
return b.width
|
||||
}
|
||||
|
||||
// Height returns the height of b
|
||||
func (b *Buffer) Height() int {
|
||||
return b.height
|
||||
}
|
||||
|
||||
// ForEach calls f for every rune in this buffer
|
||||
func (b *Buffer) ForEach(f func(x, y int, c fmt.Stringer)) {
|
||||
for y, col := range b.data {
|
||||
for x, v := range col {
|
||||
f(x, y, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Buffer) String() string {
|
||||
s := new(strings.Builder)
|
||||
for ci, col := range b.data {
|
||||
for _, v := range col {
|
||||
s.WriteString(fmt.Sprintf("%v", v))
|
||||
}
|
||||
if ci != len(b.data)-1 {
|
||||
s.WriteRune('\n')
|
||||
}
|
||||
}
|
||||
return s.String()
|
||||
}
|
||||
|
||||
// Sub returns a buffer which is completely contained in this buffer
|
||||
// Modifying the main buffer or the sub buffer will modify the other one as well
|
||||
// This method can be used recursively
|
||||
// If the given dimensions don't fit in the parent buffer, it will be truncated
|
||||
func (b *Buffer) Sub(x, y, w, h int) *Buffer {
|
||||
// sanitize inputs
|
||||
x = limit(x, 0, b.width-1)
|
||||
y = limit(y, 0, b.height-1)
|
||||
w = limit(w, 1, b.width-x)
|
||||
h = limit(h, 1, b.height-y)
|
||||
|
||||
// make slice references
|
||||
data := make([][]fmt.Stringer, h)
|
||||
for dy := 0; dy < h; dy++ {
|
||||
col := b.data[y+dy]
|
||||
data[dy] = col[x : x+w]
|
||||
}
|
||||
|
||||
// make buffer
|
||||
return &Buffer{
|
||||
data: data,
|
||||
width: w,
|
||||
height: h,
|
||||
parent: b,
|
||||
}
|
||||
}
|
19
buffer_test.go
Normal file
19
buffer_test.go
Normal file
@ -0,0 +1,19 @@
|
||||
package buf2d
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSub(t *testing.T) {
|
||||
b := NewBuffer(10, 10)
|
||||
s := b.Sub(1, 1, b.Width()-1, b.Height()-1)
|
||||
b.SetRune(5, 1, 'a')
|
||||
s.SetRune(5, 5, 'b')
|
||||
b.WriteString("Hello world", 1, 2)
|
||||
|
||||
fmt.Println(b)
|
||||
fmt.Println(strings.Repeat("-", 20))
|
||||
fmt.Println(s)
|
||||
}
|
40
utils.go
Normal file
40
utils.go
Normal file
@ -0,0 +1,40 @@
|
||||
package buf2d
|
||||
|
||||
import "fmt"
|
||||
|
||||
func limit(v, min, max int) int {
|
||||
return getmax(getmin(v, max), min)
|
||||
}
|
||||
|
||||
func getmax(x, y int) int {
|
||||
if x > y {
|
||||
return x
|
||||
}
|
||||
return y
|
||||
}
|
||||
|
||||
func getmin(x, y int) int {
|
||||
if x < y {
|
||||
return x
|
||||
}
|
||||
return y
|
||||
}
|
||||
|
||||
type stringerImpl string
|
||||
|
||||
func (s *stringerImpl) String() string {
|
||||
return string(*s)
|
||||
}
|
||||
|
||||
func newStringer(str string) fmt.Stringer {
|
||||
var impl stringerImpl = stringerImpl(str)
|
||||
return &impl
|
||||
}
|
||||
|
||||
func newStringerFromRune(rn rune) fmt.Stringer {
|
||||
return newStringer(string(rn))
|
||||
}
|
||||
|
||||
var (
|
||||
spaceStringer = newStringer(" ")
|
||||
)
|
42
write_string.go
Normal file
42
write_string.go
Normal file
@ -0,0 +1,42 @@
|
||||
package buf2d
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// WriteString writes a whole string to the buffer at position (x,y)
|
||||
// no word wrap is applied at all. If the string does not fit, it will be truncated
|
||||
func (b *Buffer) WriteString(str string, x, y int) {
|
||||
dx := x
|
||||
for _, r := range str {
|
||||
if dx >= b.width {
|
||||
return
|
||||
}
|
||||
b.Set(dx, y, newStringerFromRune(r))
|
||||
dx++
|
||||
}
|
||||
}
|
||||
|
||||
// WriteMultiLineString writes a multi-line string to the buffer at position (x,y)
|
||||
// no word wrap is applied at all. If a line does not fit horizontally, it will be truncated
|
||||
// All lines which do not fit vertically will be truncated as well
|
||||
func (b *Buffer) WriteMultiLineString(str string, x, y int) {
|
||||
lines := strings.Split(str, "\n")
|
||||
for dy, line := range lines {
|
||||
if dy >= b.height {
|
||||
return
|
||||
}
|
||||
|
||||
b.WriteString(line, x, y+dy)
|
||||
}
|
||||
}
|
||||
|
||||
// Fill fills the whole buffer with c
|
||||
func (b *Buffer) Fill(c fmt.Stringer) {
|
||||
for _, col := range b.data {
|
||||
for ri := range col {
|
||||
col[ri] = c
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user