border.go

  1package uv
  2
  3// NormalBorder returns a standard-type border with a normal weight and 90
  4// degree corners.
  5func NormalBorder() Border {
  6	return Border{
  7		Top:         Side{Content: "─"},
  8		Bottom:      Side{Content: "─"},
  9		Left:        Side{Content: "│"},
 10		Right:       Side{Content: "│"},
 11		TopLeft:     Side{Content: "┌"},
 12		TopRight:    Side{Content: "┐"},
 13		BottomLeft:  Side{Content: "└"},
 14		BottomRight: Side{Content: "┘"},
 15	}
 16}
 17
 18// RoundedBorder returns a border with rounded corners.
 19func RoundedBorder() Border {
 20	return Border{
 21		Top:         Side{Content: "─"},
 22		Bottom:      Side{Content: "─"},
 23		Left:        Side{Content: "│"},
 24		Right:       Side{Content: "│"},
 25		TopLeft:     Side{Content: "╭"},
 26		TopRight:    Side{Content: "╮"},
 27		BottomLeft:  Side{Content: "╰"},
 28		BottomRight: Side{Content: "╯"},
 29	}
 30}
 31
 32// BlockBorder returns a border that takes the whole block.
 33func BlockBorder() Border {
 34	return Border{
 35		Top:         Side{Content: "█"},
 36		Bottom:      Side{Content: "█"},
 37		Left:        Side{Content: "█"},
 38		Right:       Side{Content: "█"},
 39		TopLeft:     Side{Content: "█"},
 40		TopRight:    Side{Content: "█"},
 41		BottomLeft:  Side{Content: "█"},
 42		BottomRight: Side{Content: "█"},
 43	}
 44}
 45
 46// OuterHalfBlockBorder returns a half-block border that sits outside the frame.
 47func OuterHalfBlockBorder() Border {
 48	return Border{
 49		Top:         Side{Content: "▀"},
 50		Bottom:      Side{Content: "▄"},
 51		Left:        Side{Content: "▌"},
 52		Right:       Side{Content: "▐"},
 53		TopLeft:     Side{Content: "▛"},
 54		TopRight:    Side{Content: "▜"},
 55		BottomLeft:  Side{Content: "▙"},
 56		BottomRight: Side{Content: "▟"},
 57	}
 58}
 59
 60// InnerHalfBlockBorder returns a half-block border that sits inside the frame.
 61func InnerHalfBlockBorder() Border {
 62	return Border{
 63		Top:         Side{Content: "▄"},
 64		Bottom:      Side{Content: "▀"},
 65		Left:        Side{Content: "▐"},
 66		Right:       Side{Content: "▌"},
 67		TopLeft:     Side{Content: "▗"},
 68		TopRight:    Side{Content: "▖"},
 69		BottomLeft:  Side{Content: "▝"},
 70		BottomRight: Side{Content: "▘"},
 71	}
 72}
 73
 74// ThickBorder returns a border that's thicker than the one returned by
 75// NormalBorder.
 76func ThickBorder() Border {
 77	return Border{
 78		Top:         Side{Content: "━"},
 79		Bottom:      Side{Content: "━"},
 80		Left:        Side{Content: "┃"},
 81		Right:       Side{Content: "┃"},
 82		TopLeft:     Side{Content: "┏"},
 83		TopRight:    Side{Content: "┓"},
 84		BottomLeft:  Side{Content: "┗"},
 85		BottomRight: Side{Content: "┛"},
 86	}
 87}
 88
 89// DoubleBorder returns a border comprised of two thin strokes.
 90func DoubleBorder() Border {
 91	return Border{
 92		Top:         Side{Content: "═"},
 93		Bottom:      Side{Content: "═"},
 94		Left:        Side{Content: "║"},
 95		Right:       Side{Content: "║"},
 96		TopLeft:     Side{Content: "╔"},
 97		TopRight:    Side{Content: "╗"},
 98		BottomLeft:  Side{Content: "╚"},
 99		BottomRight: Side{Content: "╝"},
100	}
101}
102
103// HiddenBorder returns a border that renders as a series of single-cell
104// spaces. It's useful for cases when you want to remove a standard border but
105// maintain layout positioning. This said, you can still apply a background
106// color to a hidden border.
107func HiddenBorder() Border {
108	return Border{
109		Top:         Side{Content: " "},
110		Bottom:      Side{Content: " "},
111		Left:        Side{Content: " "},
112		Right:       Side{Content: " "},
113		TopLeft:     Side{Content: " "},
114		TopRight:    Side{Content: " "},
115		BottomLeft:  Side{Content: " "},
116		BottomRight: Side{Content: " "},
117	}
118}
119
120// MarkdownBorder return a table border in markdown style.
121func MarkdownBorder() Border {
122	return Border{
123		Left:        Side{Content: "|"},
124		Right:       Side{Content: "|"},
125		TopLeft:     Side{Content: "|"},
126		TopRight:    Side{Content: "|"},
127		BottomLeft:  Side{Content: "|"},
128		BottomRight: Side{Content: "|"},
129	}
130}
131
132// ASCIIBorder returns a table border with ASCII characters.
133func ASCIIBorder() Border {
134	return Border{
135		Top:         Side{Content: "-"},
136		Bottom:      Side{Content: "-"},
137		Left:        Side{Content: "|"},
138		Right:       Side{Content: "|"},
139		TopLeft:     Side{Content: "+"},
140		TopRight:    Side{Content: "+"},
141		BottomLeft:  Side{Content: "+"},
142		BottomRight: Side{Content: "+"},
143	}
144}
145
146// Side represents a single border side with its properties.
147type Side struct {
148	Content string
149	Style
150	Link
151}
152
153// Border represents a border with its properties.
154type Border struct {
155	Top         Side
156	Bottom      Side
157	Left        Side
158	Right       Side
159	TopLeft     Side
160	TopRight    Side
161	BottomLeft  Side
162	BottomRight Side
163}
164
165// Style returns a new [Border] with the given style applied to all [Side]s.
166func (b Border) Style(style Style) Border {
167	b.Top.Style = style
168	b.Bottom.Style = style
169	b.Left.Style = style
170	b.Right.Style = style
171	b.TopLeft.Style = style
172	b.TopRight.Style = style
173	b.BottomLeft.Style = style
174	b.BottomRight.Style = style
175	return b
176}
177
178// Link returns a new [Border] with the given link applied to all [Side]s.
179func (b Border) Link(link Link) Border {
180	b.Top.Link = link
181	b.Bottom.Link = link
182	b.Left.Link = link
183	b.Right.Link = link
184	b.TopLeft.Link = link
185	b.TopRight.Link = link
186	b.BottomLeft.Link = link
187	b.BottomRight.Link = link
188	return b
189}
190
191// Draw draws the border around the given component.
192func (b *Border) Draw(scr Screen, area Rectangle) {
193	for y := area.Min.Y; y < area.Max.Y; y++ {
194		for x := area.Min.X; x < area.Max.X; x++ {
195			var cell *Cell
196			switch {
197			case y == area.Min.Y && x == area.Min.X:
198				cell = borderCell(scr, &b.TopLeft)
199			case y == area.Min.Y && x == area.Max.X-1:
200				cell = borderCell(scr, &b.TopRight)
201			case y == area.Max.Y-1 && x == area.Min.X:
202				cell = borderCell(scr, &b.BottomLeft)
203			case y == area.Max.Y-1 && x == area.Max.X-1:
204				cell = borderCell(scr, &b.BottomRight)
205			case y == area.Min.Y:
206				cell = borderCell(scr, &b.Top)
207			case y == area.Max.Y-1:
208				cell = borderCell(scr, &b.Bottom)
209			case x == area.Min.X:
210				cell = borderCell(scr, &b.Left)
211			case x == area.Max.X-1:
212				cell = borderCell(scr, &b.Right)
213			default:
214				continue
215			}
216			if cell == nil {
217				continue
218			}
219			scr.SetCell(x, y, cell)
220		}
221	}
222}
223
224func borderCell(scr Screen, b *Side) *Cell {
225	c := NewCell(scr.WidthMethod(), b.Content)
226	if c != nil {
227		c.Style = b.Style
228		c.Link = b.Link
229	}
230	return c
231}