podman
176 строк · 3.3 Кб
1package goterm
2
3import (
4"bytes"
5"regexp"
6"strings"
7_ "unicode/utf8"
8)
9
10const DEFAULT_BORDER = "- │ ┌ ┐ └ ┘"
11
12// Box allows you to create independent parts of screen, with its own buffer and borders.
13// Can be used for creating modal windows
14//
15// Generates boxes likes this:
16// ┌--------┐
17// │hello │
18// │world │
19// │ │
20// └--------┘
21//
22type Box struct {
23Buf *bytes.Buffer
24
25Width int
26Height int
27
28// To get even padding: PaddingX ~= PaddingY*4
29PaddingX int
30PaddingY int
31
32// Should contain 6 border pieces separated by spaces
33//
34// Example border:
35// "- │ ┌ ┐ └ ┘"
36Border string
37
38Flags int // Not used now
39}
40
41// Create new Box.
42// Width and height can be relative:
43//
44// // Create box with 50% with of current screen and 10 lines height
45// box := tm.NewBox(50|tm.PCT, 10, 0)
46//
47func NewBox(width, height int, flags int) *Box {
48width, height = GetXY(width, height)
49
50box := new(Box)
51box.Buf = new(bytes.Buffer)
52box.Width = width
53box.Height = height
54box.Border = DEFAULT_BORDER
55box.PaddingX = 1
56box.PaddingY = 0
57box.Flags = flags
58
59return box
60}
61
62func (b *Box) Write(p []byte) (int, error) {
63return b.Buf.Write(p)
64}
65
66var ANSI_RE = regexp.MustCompile(`\\0\d+\[\d+(?:;\d+)?m`)
67
68// String renders Box
69func (b *Box) String() (out string) {
70borders := strings.Split(b.Border, " ")
71lines := strings.Split(b.Buf.String(), "\n")
72
73// Border + padding
74prefix := borders[1] + strings.Repeat(" ", b.PaddingX)
75suffix := strings.Repeat(" ", b.PaddingX) + borders[1]
76
77offset := b.PaddingY + 1 // 1 is border width
78
79// Content width without borders and padding
80contentWidth := b.Width - (b.PaddingX+1)*2
81for y := 0; y < b.Height; y++ {
82var line string
83
84switch {
85// Draw borders for first line
86case y == 0:
87line = borders[2] + strings.Repeat(borders[0], b.Width-2) + borders[3]
88
89// Draw borders for last line
90case y == (b.Height - 1):
91line = borders[4] + strings.Repeat(borders[0], b.Width-2) + borders[5]
92
93// Draw top and bottom padding
94case y <= b.PaddingY || y >= (b.Height-b.PaddingY):
95line = borders[1] + strings.Repeat(" ", b.Width-2) + borders[1]
96
97// Render content
98default:
99if len(lines) > y-offset {
100line = lines[y-offset]
101} else {
102line = ""
103}
104
105r := []rune(line)
106
107lastAnsii := ""
108withoutAnsii := []rune{}
109withOffset := []rune{}
110i := 0
111
112for {
113if i >= len(r) {
114break
115}
116
117if r[i] == 27 {
118lastAnsii = ""
119withOffset = append(withOffset, r[i])
120lastAnsii += string(r[i])
121i++
122for {
123
124i++
125if i > len(r) {
126break
127}
128
129withOffset = append(withOffset, r[i])
130lastAnsii += string(r[i])
131
132if r[i] == 'm' {
133i++
134break
135}
136}
137}
138
139if i >= len(r) {
140break
141}
142
143withoutAnsii = append(withoutAnsii, r[i])
144
145if len(withoutAnsii) <= contentWidth {
146withOffset = append(withOffset, r[i])
147}
148
149i++
150}
151
152if len(withoutAnsii) > contentWidth {
153// If line is too large limit it
154line = string(withOffset)
155} else {
156// If line is too small enlarge it by adding spaces
157line += strings.Repeat(" ", contentWidth-len(withoutAnsii))
158}
159
160if lastAnsii != "" {
161line += RESET
162}
163
164line = prefix + line + suffix
165}
166
167// Don't add newline for last element
168if y != b.Height-1 {
169line += "\n"
170}
171
172out += line
173}
174
175return out
176}
177