box.gno
2.23 Kb · 69 lines
1package art
2
3// BoxStyle is the six runes a rectangular border needs.
4type BoxStyle struct {
5 TL, TR, BL, BR rune
6 H, V rune
7}
8
9// The border styles worth having. ASCII is the one that survives a consumer
10// with no Unicode: everything else here is box-drawing, which is fine in a
11// terminal and in a markdown code fence but not in, say, a plain-ASCII log.
12var (
13 Light = BoxStyle{TL: '┌', TR: '┐', BL: '└', BR: '┘', H: '─', V: '│'}
14 Heavy = BoxStyle{TL: '┏', TR: '┓', BL: '┗', BR: '┛', H: '━', V: '┃'}
15 Double = BoxStyle{TL: '╔', TR: '╗', BL: '╚', BR: '╝', H: '═', V: '║'}
16 Round = BoxStyle{TL: '╭', TR: '╮', BL: '╰', BR: '╯', H: '─', V: '│'}
17 ASCII = BoxStyle{TL: '+', TR: '+', BL: '+', BR: '+', H: '-', V: '|'}
18)
19
20// Box draws a w by h border in the pen's colours, leaving the interior
21// untouched. w or h under 2 draws nothing: there is no border with no inside.
22func (c *Canvas) Box(x, y, w, h int, s BoxStyle) {
23 if w < 2 || h < 2 {
24 return
25 }
26 c.HLine(x+1, y, w-2, s.H)
27 c.HLine(x+1, y+h-1, w-2, s.H)
28 c.VLine(x, y+1, h-2, s.V)
29 c.VLine(x+w-1, y+1, h-2, s.V)
30 c.Put(x, y, s.TL)
31 c.Put(x+w-1, y, s.TR)
32 c.Put(x, y+h-1, s.BL)
33 c.Put(x+w-1, y+h-1, s.BR)
34}
35
36// Frame returns a NEW canvas two cells wider and taller than c, with c blitted
37// into the middle and a border around it. A non-empty title is written into the
38// top edge starting two cells in, and is truncated rather than allowed to run
39// over the corner.
40//
41// It returns a new canvas rather than growing this one because a Canvas has a
42// fixed size by design: every index in this package is computed from W, and a
43// resize in place would invalidate any offset a caller was holding.
44func (c *Canvas) Frame(title string, s BoxStyle) *Canvas {
45 out := NewCanvas(c.W+2, c.H+2)
46 out.FG, out.BG = c.FG, c.BG
47 out.Box(0, 0, out.W, out.H, s)
48 out.Blit(1, 1, c)
49 if title != "" {
50 // The top edge has W-2 usable cells and the title starts at x=2, so it
51 // gets W-4 of them. Anything less than one cell means no room at all.
52 room := out.W - 4
53 if room > 0 {
54 out.Write(2, 0, truncRunes(title, room))
55 }
56 }
57 return out
58}
59
60func truncRunes(s string, n int) string {
61 i := 0
62 for idx := range s {
63 if i == n {
64 return s[:idx]
65 }
66 i++
67 }
68 return s
69}