func (fr *Frame) PointToCoord(p int) image.Point {
	pp := p - fr.Top
	if pp < 0 {
		var r fixed.Point26_6
		if len(fr.glyphs) == 0 {
			r = fr.ins
		} else {
			r = fr.glyphs[0].p
		}
		return image.Point{r.X.Floor(), r.Y.Floor()}
	} else if pp < len(fr.glyphs) {
		r := fr.glyphs[pp].p
		return image.Point{r.X.Floor(), r.Y.Floor()}
	} else if (pp == len(fr.glyphs)) && (len(fr.glyphs) > 0) {
		r := fr.glyphs[pp-1].p
		return image.Point{(r.X + fr.glyphs[pp-1].width).Floor(), r.Y.Floor()}
	} else {
		return image.Point{fr.R.Min.X + 2, fr.R.Min.Y + 2}
	}
}
Converts rune index into a graphical coordinate
package textframe
import (
	"fmt"
	"image"
	"image/draw"
	"math"
	"runtime"
	"time"
	"github.com/aarzilli/yacco/otat"
	"github.com/aarzilli/yacco/util"
	"github.com/golang/freetype"
	"golang.org/x/image/font"
	"golang.org/x/image/math/fixed"
	"golang.org/x/mobile/event/key"
	"golang.org/x/mobile/event/mouse"
)
type Redrawable interface {
	Redraw(flush bool)
}
const debugRedraw = false
const optiStats = false
type FrameScrollFn func(scrollDir int, n int)
type ExpandSelectionFn func(kind, start, end int) (int, int)
const (
	HF_MARKSOFTWRAP uint32 = 1 << iota
	HF_TRUNCATE             
	HF_NOVERTSTOP           
	HF_AUTOINDENT_SOFTWRAP
)
type Frame struct {
	Font            font.Face
	otatm           *otat.Machine
	Hackflags       uint32
	B               draw.Image       
	R               image.Rectangle  
	VisibleTick     bool
	Colors          [][]image.Uniform
	TabWidth        int
	Flush           func(...image.Rectangle)
	Scroll          FrameScrollFn
	ExpandSelection ExpandSelectionFn
	Top             int
	Tabs            []int
	margin            fixed.Int26_6
	minimumDragForSel int
	Offset            int
	Sel      util.Sel
	SelColor int
	PMatch   util.Sel
	glyphs   []glyph
	ins      fixed.Point26_6
	lastFull int
	redrawOpt struct {
		drawnVisibleTick bool
		drawnSel         util.Sel
		drawnPMatch      util.Sel
		selColor         int
		reloaded         bool
		scrollStart      int
		scrollEnd        int
	}
	scrubGlyph image.Alpha
	debugRedraw bool
	leftMargin, rightMargin fixed.Int26_6
}
type glyph struct {
	r        rune
	fakerune bool
	width    fixed.Int26_6
	widthy   fixed.Int26_6
	p        fixed.Point26_6
	color    uint8
}
func (fr *Frame) Init(margin int) error {
	fr.margin = fixed.I(margin)
	fr.glyphs = []glyph{}
	fr.Offset = 0
	fr.SelColor = 0
	if fr.TabWidth == 0 {
		fr.TabWidth = 8
	}
	if len(fr.Colors) < 2 {
		return fmt.Errorf("Not enough color lines")
	}
	for i, _ := range fr.Colors {
		if len(fr.Colors[i]) < 2 {
			return fmt.Errorf("Not enough colors in line %d (%d)", i, len(fr.Colors[i]))
		}
	}
	_, _, _, xw, _ := fr.Font.Glyph(fixed.P(0, 0), 'x')
	fr.minimumDragForSel = xw.Floor()
	if fr.minimumDragForSel <= 5 {
		fr.minimumDragForSel = 5
	}
	fr.Clear()
	if mf, ok := fr.Font.(*util.Multiface); ok {
		fr.otatm = mf.Otatm
	} else {
		fr.otatm = otat.Dummy()
	}
	return nil
}
func (fr *Frame) Invalidate() {
	fr.redrawOpt.reloaded = true
	if mf, ok := fr.Font.(*util.Multiface); ok {
		fr.otatm = mf.Otatm
	} else {
		fr.otatm = otat.Dummy()
	}
}
func (fr *Frame) BytesSize() uintptr {
	n := cap(fr.glyphs)
	return uintptr(n * 31)
}
func (fr *Frame) initialInsPoint() fixed.Point26_6 {
	p := fixed.P(fr.R.Min.X+fr.Offset, fr.R.Min.Y+fr.Font.Metrics().Ascent.Floor())
	p.X += fr.margin
	return p
}
func (fr *Frame) Clear() {
	fr.ins = fr.initialInsPoint()
	fr.glyphs = fr.glyphs[:0]
	fr.lastFull = 0
	fr.redrawOpt.reloaded = true
	fr.redrawOpt.scrollStart = -1
	fr.redrawOpt.scrollEnd = -1
}
func (fr *Frame) Insert(r1, r2 []rune) (limit image.Point) {
	fr.redrawOpt.reloaded = true
	lh := fr.Font.Metrics().Height
	prevRune, hasPrev := rune(0), false
	fr.rightMargin = fixed.I(fr.R.Max.X) - fr.margin
	fr.leftMargin = fixed.I(fr.R.Min.X) + fr.margin
	bottom := fixed.I(fr.R.Max.Y) + lh
	_, _, _, spaceWidth, _ := fr.Font.Glyph(fixed.P(0, 0), ' ')
	tabWidth := spaceWidth * fixed.Int26_6(fr.TabWidth)
	limit.X = fr.ins.X.Floor()
	limit.Y = fr.ins.Y.Floor()
	runesIdx := 0
	fr.otatm.Reset(func() (rune, bool) {
		if runesIdx < len(r1) {
			r := r1[runesIdx]
			runesIdx++
			return r, true
		}
		if i := runesIdx - len(r1); i < len(r2) {
			runesIdx++
			return r2[i], true
		}
		return 0, false
	})
	autoindentMargin := fixed.Int26_6(0)
	for fr.otatm.Next() {
		i, glyphidx, crune := fr.otatm.Glyph()
		if crune == 0 {
			if i < len(r1) {
				crune = r1[i]
			} else {
				crune = r2[i-len(r1)]
			}
		}
		if fr.ins.Y > bottom && (fr.Hackflags&HF_NOVERTSTOP == 0) {
			return
		}
		if fr.ins.Y < fixed.I(fr.R.Max.Y) {
			fr.lastFull = len(fr.glyphs)
		}
		switch crune {
		case '\n':
			g := glyph{
				r:        crune,
				fakerune: true,
				p:        fr.ins,
				color:    1,
				width:    fixed.I(fr.R.Max.X) - fr.ins.X - fr.margin,
				widthy:   lh,
			}
			fr.glyphs = append(fr.glyphs, g)
			fr.ins.X = fr.leftMargin
			fr.ins.Y += lh
			prevRune, hasPrev = ' ', true
			autoindentMargin = 0
		case '\t':
			var toNextCell fixed.Int26_6
			if fr.Tabs != nil {
				for i := range fr.Tabs {
					t := fixed.I(fr.Tabs[i]) + fr.leftMargin
					if fr.ins.X+spaceWidth/2 < t {
						toNextCell = t - fr.ins.X
						break
					}
				}
			} else {
				toNextCell = tabWidth - ((fr.ins.X - fr.leftMargin) % tabWidth)
				if toNextCell <= spaceWidth/2 {
					toNextCell += tabWidth
				}
			}
			g := glyph{
				r:        crune,
				fakerune: true,
				p:        fr.ins,
				color:    1,
				width:    toNextCell,
			}
			fr.glyphs = append(fr.glyphs, g)
			fr.ins.X += g.width
			prevRune, hasPrev = ' ', true
		default:
			width, _ := fr.Font.GlyphAdvance(crune)
			kerning := fixed.I(0)
			if hasPrev {
				kerning = fr.Font.Kern(prevRune, crune)
				fr.ins.X += kerning
			}
			if fr.Hackflags&HF_TRUNCATE == 0 {
				if fr.ins.X+width > fr.rightMargin {
					if autoindentMargin != 0 && (fr.Hackflags&HF_AUTOINDENT_SOFTWRAP != 0) {
						fr.ins.X = autoindentMargin + fr.margin
					} else {
						fr.ins.X = fr.leftMargin
					}
					fr.ins.Y += lh
				}
			}
			if autoindentMargin == 0 && (fr.Hackflags&HF_AUTOINDENT_SOFTWRAP != 0) {
				autoindentMargin = fr.ins.X
			}
			g := glyph{
				r:        glyphidx,
				fakerune: false,
				p:        fr.ins,
				color:    1,
				width:    width,
			}
			fr.glyphs = append(fr.glyphs, g)
			fr.ins.X += g.width
			prevRune, hasPrev = crune, true
		}
		if x := fr.ins.X.Floor(); x > limit.X {
			limit.X = x
		}
		if y := fr.ins.Y.Floor(); y > limit.Y {
			limit.Y = y
		}
	}
	if fr.ins.Y < fixed.I(fr.R.Max.Y) {
		fr.lastFull = len(fr.glyphs)
	}
	return
}
func (fr *Frame) RefreshColors(colors []uint8) {
	for i := range fr.glyphs {
		fr.glyphs[i].color = colors[i]
	}
}
func abs(n int) int {
	if n < 0 {
		return -n
	}
	return n
}
func (fr *Frame) Select(idx, kind int, button mouse.Button, startPos image.Point, events <-chan interface{}) *mouse.Event {
	if events == nil {
		return nil
	}
	if (idx < 0) || (idx >= len(fr.Colors)-1) {
		for ei := range events {
			switch e := ei.(type) {
			case mouse.Event:
				if e.Direction == mouse.DirRelease {
					return &e
				}
			}
		}
	}
	fr.PMatch.E = fr.PMatch.S
	fr.SelColor = idx
	fix := fr.Sel.S
	var autoscrollTicker *time.Ticker
	var autoscrollChan <-chan time.Time
	stopAutoscroll := func() {
		if autoscrollTicker != nil {
			autoscrollTicker.Stop()
			autoscrollTicker = nil
		}
	}
	var lastPos image.Point
	started := false
	r := fr.R
	r.Max.Y -= 2
	for {
		runtime.Gosched()
		select {
		case ei := <-events:
			switch e := ei.(type) {
			case mouse.Event:
				if e.Direction == mouse.DirRelease {
					stopAutoscroll()
					return &e
				}
				where := image.Point{int(e.X), int(e.Y)}
				if !started && abs(where.X-startPos.X) < fr.minimumDragForSel && abs(where.Y-startPos.Y) < fr.minimumDragForSel {
					continue
				}
				started = true
				lastPos = where
				if where.In(r) {
					stopAutoscroll()
					p := fr.CoordToPoint(where)
					fr.SetSelect(idx, kind, fix, p)
					fr.Redraw(true, nil)
				} else {
					if autoscrollTicker == nil {
						autoscrollTicker = time.NewTicker(100 * time.Millisecond)
						autoscrollChan = autoscrollTicker.C
					}
				}
			}
		case <-autoscrollChan:
			if (lastPos.X >= fr.R.Min.X) && (lastPos.X <= fr.R.Max.X) {
				mid := (fr.R.Min.Y + fr.R.Max.Y) / 2
				var sd int
				if lastPos.Y < mid {
					sd = -1
				}
				if lastPos.Y >= mid {
					sd = +1
				}
				fr.Scroll(sd, 1)
				if sd < 0 {
					fr.SetSelect(idx, kind, fr.Top, fix)
				} else if sd > 0 {
					fr.SetSelect(idx, kind, len(fr.glyphs)+fr.Top, fix)
				}
				fr.Redraw(true, nil)
			}
		}
	}
}
func (fr *Frame) SetSelect(idx, kind, start, end int) {
	if (idx < 0) || (idx >= len(fr.Colors)) {
		idx = 0
	}
	fr.SelColor = idx
	if start >= end {
		temp := start
		start = end
		end = temp
	}
	if fr.ExpandSelection != nil {
		nstart, nend := fr.ExpandSelection(kind, start, end)
		fr.Sel.S = nstart
		fr.Sel.E = nend
	} else {
		fr.Sel.S = start
		fr.Sel.E = end
	}
}
func (fr *Frame) CoordToPoint(coord image.Point) int {
	if !coord.In(fr.R) {
		return -1
	}
	ftcoord := freetype.Pt(coord.X, coord.Y)
	lh := fr.Font.Metrics().Height
	fm := fr.Font.Metrics()
	for i, g := range fr.glyphs {
		if g.p.Y+fm.Descent < ftcoord.Y {
			continue
		} else if (g.p.Y - lh) > ftcoord.Y {
			return i + fr.Top
		} else if ftcoord.X < g.p.X {
			return i + fr.Top
		} else if g.r == '\n' {
			return i + fr.Top
		} else if (ftcoord.X >= g.p.X) && (ftcoord.X <= g.p.X+g.width) {
			return i + fr.Top
		}
	}
	return fr.Top + len(fr.glyphs)
}