/
dot.go
169 lines (149 loc) · 2.86 KB
/
dot.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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
package frame
import (
"bytes"
"golang.org/x/image/font"
"image"
)
type Font struct {
font.Face
height int
}
func NewFont(face font.Face) *Font {
if face == nil {
panic("NewFont: nil font face")
}
return &Font{
Face: face,
}
}
func (f *Font) Height() int {
if f.Face == nil {
return 0
}
if f.height == 0 {
f.height = int(f.Metrics().Height>>6) + 1
}
return f.height
}
type Dot struct {
image.Point
origin image.Point
maxw int
font *Font
}
func NewDot(origin image.Point, maxw int, font *Font) *Dot {
if font == nil {
panic("NewDot: font is nil")
}
return &Dot{
Point: origin,
origin: origin,
maxw: maxw,
font: font,
}
}
func (d *Dot) advance(r rune) int {
dx, _ := d.font.GlyphAdvance(r)
return int(dx >> 6)
}
func (d *Dot) Advance(r rune) int {
if r == '\t' {
return d.advance(' ') * 4
}
return d.advance(r)
}
func (d *Dot) Visible(r rune) bool {
switch r {
case '\t', '\n':
return false
}
return true
}
func (d *Dot) Newline() {
d.X = d.origin.X
d.Y += d.Height()
}
func (d *Dot) Origin() image.Point {
return d.origin
}
// fits returns the number of pixels that would be advance
// if r were printed, or -1 if r doesn't fit on the line
func (d *Dot) fits(r rune) int {
adv := d.Advance(r)
if d.Width()+adv > d.maxw {
return -1
}
return adv
}
func (d *Dot) fitsbox(b *Box) int {
adv := b.Width()
if d.Width()+adv > d.maxw {
return -1
}
return adv
}
// Insert advances dot by the width of r, or starts a new
// line if r doesn't fit
func (d *Dot) Insert(r rune) image.Point {
if adv := d.fits(r); adv == -1 || r == rune('\n') {
d.Newline()
} else {
d.X += adv
}
return d.Point
}
func (d *Dot) InsertBox(b *Box) image.Point {
if adv := d.fitsbox(b); adv == -1 {
d.Newline()
} else {
d.X += adv
}
return d.Point
}
// IndexOf computes the index of the glyph containing pt
func (dot *Dot) indexOf(box *Box, pt image.Point) (i int) {
//defer func() { fmt.Printf("IndexOf: pt=%v i=%d (%c)\n", pt, i, f.s[i])}()
pt = dot.alignY(pt)
s := box.Bytes()
for i = 0; i < len(s); i++ {
switch {
case dot.Y < pt.Y:
// nothing special
case dot.Y == pt.Y:
// same line
if dot.X+dot.Advance(rune(s[i]))/2 >= pt.X {
return i
}
case dot.Y > pt.Y:
// advanced too far
if i-1 < 0 {
// bug fix for crash: happened when selecting
// and dragging all the way to the top
return i
}
if s[i-1] == '\n' {
// a hard newline
return i - 1
} else {
// line wrapped
return i
}
}
dot.Insert(rune(s[i]))
}
return i
}
// Width returns the amount of horizontal pixels covered by dot
// starting from the origin
func (d *Dot) Width() int {
return d.X - d.origin.X
}
func (d *Dot) Height() int {
return d.font.Height()
}
func nlpos(p []byte) (i int) {
return bytes.Index(p, NL)
}
func (d *Dot) alignY(pt image.Point) image.Point {
return alignY(d.Height(), d.Origin(), pt)
}