mirror of
https://github.com/VladimirMarkelov/clui.git
synced 2025-04-28 13:48:50 +08:00
202 lines
3.8 KiB
Go
202 lines
3.8 KiB
Go
package clui
|
|
|
|
import (
|
|
"fmt"
|
|
xs "github.com/huandu/xstrings"
|
|
term "github.com/nsf/termbox-go"
|
|
)
|
|
|
|
// Represents an object visible content. Used by Composer to keep all screen info and by Window
|
|
type FrameBuffer struct {
|
|
buffer [][]term.Cell
|
|
w, h int
|
|
}
|
|
|
|
// Creates new buffer
|
|
func NewFrameBuffer(w, h int) *FrameBuffer {
|
|
if w < 5 || h < 5 {
|
|
panic(fmt.Sprintf("Invalid size: %vx%v.", w, h))
|
|
}
|
|
|
|
c := new(FrameBuffer)
|
|
c.SetSize(w, h)
|
|
|
|
return c
|
|
}
|
|
|
|
// Returns current width and height of a buffer
|
|
func (fb *FrameBuffer) Size() (int, int) {
|
|
return fb.w, fb.h
|
|
}
|
|
|
|
func (fb *FrameBuffer) SetSize(w, h int) {
|
|
if w == fb.w && h == fb.h {
|
|
return
|
|
}
|
|
|
|
if w < 5 || h < 5 {
|
|
panic(fmt.Sprintf("Invalid size: %vx%v.", w, h))
|
|
}
|
|
|
|
fb.w, fb.h = w, h
|
|
|
|
fb.buffer = make([][]term.Cell, h)
|
|
for i := 0; i < h; i++ {
|
|
fb.buffer[i] = make([]term.Cell, w)
|
|
}
|
|
}
|
|
|
|
// Fills buffers with color (the buffer is filled with spaces with defined background color)
|
|
func (fb *FrameBuffer) Clear(bg term.Attribute) {
|
|
s := term.Cell{Ch: ' ', Fg: ColorWhite, Bg: bg}
|
|
for y := 0; y < fb.h; y++ {
|
|
for x := 0; x < fb.w; x++ {
|
|
fb.buffer[y][x] = s
|
|
}
|
|
}
|
|
}
|
|
|
|
func (fb *FrameBuffer) FillRect(x, y, w, h int, s term.Cell) {
|
|
if x < 0 {
|
|
w += x
|
|
x = 0
|
|
}
|
|
if y < 0 {
|
|
h += y
|
|
y = 0
|
|
}
|
|
if x+w >= fb.w {
|
|
w = fb.w - x
|
|
}
|
|
if y+h >= fb.h {
|
|
h = fb.h - y
|
|
}
|
|
|
|
for yy := y; yy < y+h; yy++ {
|
|
for xx := x; xx < x+w; xx++ {
|
|
fb.buffer[yy][xx] = s
|
|
}
|
|
}
|
|
}
|
|
|
|
func (fb *FrameBuffer) Symbol(x, y int) (term.Cell, bool) {
|
|
if x >= fb.w || x < 0 || y >= fb.h || y < 0 {
|
|
return term.Cell{Ch: ' '}, false
|
|
} else {
|
|
return fb.buffer[y][x], true
|
|
}
|
|
}
|
|
|
|
func (fb *FrameBuffer) PutSymbol(x, y int, s term.Cell) bool {
|
|
if x < 0 || x >= fb.w || y < 0 || y >= fb.h {
|
|
return false
|
|
}
|
|
|
|
fb.buffer[y][x] = s
|
|
return true
|
|
}
|
|
|
|
func (fb *FrameBuffer) PutChar(x, y int, c rune, fg, bg term.Attribute) bool {
|
|
if x < 0 || x >= fb.w || y < 0 || y >= fb.h {
|
|
return false
|
|
}
|
|
|
|
fb.buffer[y][x] = term.Cell{Ch: c, Fg: fg, Bg: bg}
|
|
return true
|
|
}
|
|
|
|
// Draws text line on buffer
|
|
func (fb *FrameBuffer) PutText(x, y int, text string, fg, bg term.Attribute) {
|
|
width := fb.w
|
|
|
|
if (x < 0 && xs.Len(text) <= -x) || x >= fb.w || y < 0 || y >= fb.h {
|
|
return
|
|
}
|
|
|
|
if x < 0 {
|
|
xx := -x
|
|
x = 0
|
|
text = xs.Slice(text, xx, -1)
|
|
}
|
|
text = CutText(text, width)
|
|
|
|
dx := 0
|
|
for _, char := range text {
|
|
s := term.Cell{Ch: char, Fg: fg, Bg: bg}
|
|
fb.buffer[y][x+dx] = s
|
|
dx++
|
|
}
|
|
}
|
|
|
|
// Draws vertical text line on buffer
|
|
func (fb *FrameBuffer) PutTextVertical(x, y int, text string, fg, bg term.Attribute) {
|
|
height := fb.h
|
|
|
|
if (y < 0 && xs.Len(text) <= -y) || x >= fb.w || y > 0 || y >= fb.h {
|
|
return
|
|
}
|
|
|
|
if y < 0 {
|
|
yy := -y
|
|
y = 0
|
|
text = xs.Slice(text, yy, -1)
|
|
}
|
|
text = CutText(text, height)
|
|
|
|
dy := 0
|
|
for _, char := range text {
|
|
s := term.Cell{Ch: char, Fg: fg, Bg: bg}
|
|
fb.buffer[y+dy][x] = s
|
|
dy++
|
|
}
|
|
}
|
|
|
|
func (fb *FrameBuffer) DrawFrame(x, y, w, h int, fg, bg term.Attribute, frameChars string) {
|
|
if h < 1 || w < 1 {
|
|
return
|
|
}
|
|
|
|
if frameChars == "" {
|
|
frameChars = "─│┌┐└┘"
|
|
}
|
|
|
|
parts := []rune(frameChars)
|
|
if len(parts) < 6 {
|
|
panic("Invalid theme: single border")
|
|
}
|
|
|
|
H, V, UL, UR, DL, DR := parts[0], parts[1], parts[2], parts[3], parts[4], parts[5]
|
|
|
|
if h == 1 {
|
|
for xx := x; xx < x+w; xx++ {
|
|
fb.PutChar(xx, y, H, fg, bg)
|
|
}
|
|
return
|
|
}
|
|
if w == 1 {
|
|
for yy := y; yy < y+h; yy++ {
|
|
fb.PutChar(x, yy, V, fg, bg)
|
|
}
|
|
return
|
|
}
|
|
|
|
fb.PutChar(x, y, UL, fg, bg)
|
|
fb.PutChar(x, y+h-1, DL, fg, bg)
|
|
fb.PutChar(x+w-1, y, UR, fg, bg)
|
|
fb.PutChar(x+w-1, y+h-1, DR, fg, bg)
|
|
|
|
for xx := x + 1; xx < x+w-1; xx++ {
|
|
fb.PutChar(xx, y, H, fg, bg)
|
|
fb.PutChar(xx, y+h-1, H, fg, bg)
|
|
}
|
|
for yy := y + 1; yy < y+h-1; yy++ {
|
|
fb.PutChar(x, yy, V, fg, bg)
|
|
fb.PutChar(x+w-1, yy, V, fg, bg)
|
|
}
|
|
}
|
|
|
|
// x, y - absolute console coordinates
|
|
func (fb *FrameBuffer) SetCursorPos(x, y int) {
|
|
term.SetCursor(x, y)
|
|
}
|