forked from aykevl/tinygl
-
Notifications
You must be signed in to change notification settings - Fork 0
/
text.go
146 lines (125 loc) · 3.82 KB
/
text.go
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
144
145
146
package tinygl
import (
"github.com/aykevl/tinygl/font"
"tinygo.org/x/drivers/pixel"
)
type TextAlign uint8
const (
AlignCenter TextAlign = iota
AlignLeft
)
type Text[T pixel.Color] struct {
Rect[T]
text string
font font.Font
textX int16
textY int16
textWidth int16
extra uint16 // alignment, padding
color T
}
func NewText[T pixel.Color](font font.Font, foreground, background T, text string) *Text[T] {
t := MakeText(font, foreground, background, text)
return &t
}
// MakeText returns a new initialized Rect object. This is mostly useful to
// initialize an embedded Text struct in a custom object. If you want a
// standalone text object, use NewText.
func MakeText[T pixel.Color](font font.Font, foreground, background T, text string) Text[T] {
t := Text[T]{
text: text,
font: font,
Rect: MakeRect(background),
}
t.color = foreground
// Calculate bounding box for the text.
outerWidth := font.LineWidth(text)
t.textWidth = int16(outerWidth)
return t
}
// MinSize returns the minimal size of the text label.
func (t *Text[T]) MinSize() (width, height int) {
padHorizontal, padVertical := t.padding()
width = int(t.textWidth) + padHorizontal*2
height = int(t.font.Height()) + padVertical*2
return
}
// SetText changes the text for this text label.
func (t *Text[T]) SetText(text string) {
if t.text != text {
t.text = text
outerWidth := t.font.LineWidth(text)
if int(t.textWidth) != outerWidth {
t.textWidth = int16(outerWidth)
t.RequestLayout()
}
t.RequestUpdate()
}
}
func (t *Text[T]) SetAlign(align TextAlign) {
t.extra = (t.extra &^ 0x0003) | uint16(align)
t.RequestUpdate()
}
func (t *Text[T]) align() TextAlign {
return TextAlign(t.extra & 0x0003)
}
// Set horizontal and vertical padding in screen pixels. The padding must be a
// positive integer that is less than 128.
func (t *Text[T]) SetPadding(horizontal, vertical int) {
t.extra = (t.extra & 0x0003) | uint16(horizontal&0x7f)<<2 | uint16(vertical&0x7f)<<9
t.RequestLayout()
t.RequestUpdate()
}
func (t *Text[T]) padding() (horizontal, vertical int) {
horizontal = (int(t.extra) >> 2) & 0x7f
vertical = int(t.extra) >> 9
return
}
// SetBackground changes the background color of the text.
func (t *Text[T]) SetBackground(background T) {
t.background = background
t.RequestUpdate()
}
// SetColor sets the text color.
func (t *Text[T]) SetColor(color T) {
t.color = color
t.RequestUpdate()
}
func (t *Text[T]) Layout(width, height int) {
switch t.align() {
case AlignLeft:
padHorizontal, _ := t.padding()
t.textX = int16(padHorizontal)
default: // AlignCenter
t.textX = int16((width - int(t.textWidth)) / 2)
}
t.textY = int16((height-t.font.Height())/2 + t.font.Ascent())
t.flags &^= flagNeedsLayout
}
func (t *Text[T]) Update(screen *Screen[T], displayX, displayY, displayWidth, displayHeight, x, y int) {
if t.flags&flagNeedsUpdate == 0 {
return // nothing to do
}
// Draw text in the center of the provided area.
paintText(screen, displayX, displayY, displayWidth, displayHeight, displayX+int(t.textX)-x, displayY+int(t.textY)-y, t.text, t.font, t.background, t.color)
}
func paintText[T pixel.Color](screen *Screen[T], x, y, width, height, textX, textY int, text string, f font.Font, background, foreground T) {
linesPerChunk := screen.buffer.Len() / width
if linesPerChunk > height {
linesPerChunk = height
}
lineStart := 0
// Note: it may be more efficient to draw text left to right rather than
// downwards, drawing only the glyphs that are part of that area.
for lineStart < height {
lines := linesPerChunk
if lineStart+lines > height {
lines = height - lineStart
}
subimg := screen.buffer.Rescale(width, lines)
subimg.FillSolidColor(background)
font.Draw(f, text, textX-x, textY-y-lineStart, foreground, subimg)
screen.Send(x, y+lineStart, subimg)
lineStart += linesPerChunk
}
}