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
}
Inserts text into the frame, returns the maximum X and Y used
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
}