qedit/ui/drawfunctions.go
Luke I. Wilson f64af6fa29 DrawStr: made more efficient and renders newline characters
Before, DrawStr would copy all bytes into a []rune slice, and later
copy the rune slice (by accident) in the for i, v := range loop. Now,
it uses a []byte slice and iterates with for i := range slice.
2021-04-02 13:30:37 -05:00

99 lines
3.2 KiB
Go

package ui
import (
"unicode/utf8"
"github.com/gdamore/tcell/v2"
)
// DrawRect renders a filled box at `x` and `y`, of size `width` and `height`.
// Will not call `Show()`.
func DrawRect(s tcell.Screen, x, y, width, height int, char rune, style tcell.Style) {
for col := x; col < x+width; col++ {
for row := y; row < y+height; row++ {
s.SetContent(col, row, char, nil, style)
}
}
}
// DrawStr will render each character of a string at `x` and `y`.
func DrawStr(s tcell.Screen, x, y int, str string, style tcell.Style) {
var col int
bytes := []byte(str)
var i int
for i < len(bytes) {
r, size := utf8.DecodeRune(bytes[i:])
if r == '\n' {
col = 0
y++
} else {
s.SetContent(x+col, y, r, nil, style)
}
i += size
col++
}
}
// DrawQuickCharStr renders a string very similar to how DrawStr works, but stylizes the
// quick char (the rune at `quickCharIdx`) with an underline. Returned is the number of
// columns that were drawn to the screen.
func DrawQuickCharStr(s tcell.Screen, x, y int, str string, quickCharIdx int, style tcell.Style) int {
var col int
var runeIdx int
bytes := []byte(str)
for i := 0; i < len(bytes); runeIdx++ { // i is a byte index
r, size := utf8.DecodeRune(bytes[i:])
sty := style
if runeIdx == quickCharIdx {
sty = style.Underline(true)
}
s.SetContent(x+col, y, r, nil, sty)
i += size
col++
}
return col // TODO: use mattn/runewidth
}
// DrawRectOutline draws only the outline of a rectangle, using `ul`, `ur`, `bl`, and `br`
// for the corner runes, and `hor` and `vert` for the horizontal and vertical runes, respectively.
func DrawRectOutline(s tcell.Screen, x, y, _width, _height int, ul, ur, bl, br, hor, vert rune, style tcell.Style) {
width := x + _width - 1 // Length across
height := y + _height - 1 // Length top-to-bottom
// Horizontals and verticals
for col := x + 1; col < width; col++ {
s.SetContent(col, y, hor, nil, style) // Top line
s.SetContent(col, height, hor, nil, style) // Bottom line
}
for row := y + 1; row < height; row++ {
s.SetContent(x, row, vert, nil, style) // Left line
s.SetContent(width, row, vert, nil, style) // Right line
}
// Corners
s.SetContent(x, y, ul, nil, style)
s.SetContent(width, y, ur, nil, style)
s.SetContent(x, height, bl, nil, style)
s.SetContent(width, height, br, nil, style)
}
// DrawRectOutlineDefault calls DrawRectOutline with the default edge runes.
func DrawRectOutlineDefault(s tcell.Screen, x, y, width, height int, style tcell.Style) {
DrawRectOutline(s, x, y, width, height, '┌', '┐', '└', '┘', '─', '│', style)
}
// DrawWindow draws a window-like object at x and y as the top-left corner. This window
// has an optional title. The Theme values "WindowHeader" and "Window" are used.
func DrawWindow(s tcell.Screen, x, y, width, height int, title string, theme *Theme) {
headerStyle := theme.GetOrDefault("WindowHeader")
DrawRect(s, x, y, width, 1, ' ', headerStyle) // Draw header background
DrawStr(s, x + width/2 - len(title)/2, y, title, headerStyle) // Draw header title
DrawRect(s, x, y+1, width, height-1, ' ', theme.GetOrDefault("Window")) // Draw body
}
// TODO: add DrawShadow(x, y, width, height int)