summaryrefslogtreecommitdiffstats
path: root/pkg/common/draw.go
blob: b5b21fe9eb19fedd51a6c8301ce04c3cf52c419f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
package common

import (
	"fmt"
	"io"
	"os"
	"strings"
)

// Style is a specific style
type Style int

// Styles
const (
	StyleDoubleLine = iota
	StyleSingleLine
	StyleDashedLine
	StyleNoLine
)

// NewPen creates a new pen
func NewPen(style Style, color int) *Pen {
	bgcolor := 49
	if os.Getenv("CLICOLOR") == "0" {
		color = 0
		bgcolor = 0
	}
	return &Pen{
		style:   style,
		color:   color,
		bgcolor: bgcolor,
	}
}

type styleDef struct {
	cornerTL string
	cornerTR string
	cornerBL string
	cornerBR string
	lineH    string
	lineV    string
}

var styleDefs = []styleDef{
	{"\u2554", "\u2557", "\u255a", "\u255d", "\u2550", "\u2551"},
	{"\u256d", "\u256e", "\u2570", "\u256f", "\u2500", "\u2502"},
	{"\u250c", "\u2510", "\u2514", "\u2518", "\u254c", "\u254e"},
	{" ", " ", " ", " ", " ", " "},
}

// Pen struct
type Pen struct {
	style   Style
	color   int
	bgcolor int
}

// Drawing struct
type Drawing struct {
	buf   *strings.Builder
	width int
}

func (p *Pen) drawTopBars(buf io.Writer, labels ...string) {
	style := styleDefs[p.style]
	for _, label := range labels {
		bar := strings.Repeat(style.lineH, len(label)+2)
		fmt.Fprintf(buf, " ")
		fmt.Fprintf(buf, "\x1b[%d;%dm", p.color, p.bgcolor)
		fmt.Fprintf(buf, "%s%s%s", style.cornerTL, bar, style.cornerTR)
		fmt.Fprintf(buf, "\x1b[%dm", 0)
	}
	fmt.Fprintf(buf, "\n")
}
func (p *Pen) drawBottomBars(buf io.Writer, labels ...string) {
	style := styleDefs[p.style]
	for _, label := range labels {
		bar := strings.Repeat(style.lineH, len(label)+2)
		fmt.Fprintf(buf, " ")
		fmt.Fprintf(buf, "\x1b[%d;%dm", p.color, p.bgcolor)
		fmt.Fprintf(buf, "%s%s%s", style.cornerBL, bar, style.cornerBR)
		fmt.Fprintf(buf, "\x1b[%dm", 0)
	}
	fmt.Fprintf(buf, "\n")
}
func (p *Pen) drawLabels(buf io.Writer, labels ...string) {
	style := styleDefs[p.style]
	for _, label := range labels {
		fmt.Fprintf(buf, " ")
		fmt.Fprintf(buf, "\x1b[%d;%dm", p.color, p.bgcolor)
		fmt.Fprintf(buf, "%s %s %s", style.lineV, label, style.lineV)
		fmt.Fprintf(buf, "\x1b[%dm", 0)
	}
	fmt.Fprintf(buf, "\n")
}

// DrawArrow between boxes
func (p *Pen) DrawArrow() *Drawing {
	drawing := &Drawing{
		buf:   new(strings.Builder),
		width: 1,
	}
	fmt.Fprintf(drawing.buf, "\x1b[%dm", p.color)
	fmt.Fprintf(drawing.buf, "\u2b07")
	fmt.Fprintf(drawing.buf, "\x1b[%dm", 0)
	return drawing
}

// DrawBoxes to draw boxes
func (p *Pen) DrawBoxes(labels ...string) *Drawing {
	width := 0
	for _, l := range labels {
		width += len(l) + 2 + 2 + 1
	}
	drawing := &Drawing{
		buf:   new(strings.Builder),
		width: width,
	}
	p.drawTopBars(drawing.buf, labels...)
	p.drawLabels(drawing.buf, labels...)
	p.drawBottomBars(drawing.buf, labels...)

	return drawing
}

// Draw to writer
func (d *Drawing) Draw(writer io.Writer, centerOnWidth int) {
	padSize := (centerOnWidth - d.GetWidth()) / 2
	if padSize < 0 {
		padSize = 0
	}
	for _, l := range strings.Split(d.buf.String(), "\n") {
		if len(l) > 0 {
			padding := strings.Repeat(" ", padSize)
			fmt.Fprintf(writer, "%s%s\n", padding, l)
		}
	}
}

// GetWidth of drawing
func (d *Drawing) GetWidth() int {
	return d.width
}