package text import ( "log/slog" "github.com/egoist/mygo/ui" "git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/pixel" ) var more = pixel.MustSprite(` ##### .###. ..#.. `) type TypewriterOptions struct { // CPS is characters per second; zero means 20. CPS int Slot pixel.Slot Disabled bool } type TypewriterResult struct { Element *ui.Element // Done is true in the frame the whole text shows. Done bool } type typeKey struct{} type typeState struct { text string start, last int done bool } // Typewriter reveals s; a click or Enter shows the rest. func Typewriter(c *ui.Context, s string, o TypewriterOptions) TypewriterResult { k := pixel.K(c) cps := orDef("Typewriter CPS", o.CPS, 20) shell, base, st := pixel.Shell(c, func() *ui.Element { e := ui.Box(c).Role(ui.RoleText).Label(s).Disabled(o.Disabled) if !o.Disabled { e.Focusable() } return e }) ts := ui.Local(shell, typeKey{}, func() typeState { return typeState{start: -1} }) if ts.text != s { *ts = typeState{text: s, start: -1} } // Lines leave room for the mark; glyphs count, not runes. w, check := pixel.WrapWidth(c, shell) lines := pixel.Wrap(s, max(0, w-8), 0) total := 0 for _, l := range lines { total += len([]rune(l)) } res := TypewriterResult{Element: shell} if !ts.done { ts.last = total if !k.Reduce() { tick := k.Tick(c, cps) if ts.start < 0 { ts.start = tick } ts.last = min(total, tick-ts.start) } if base.Clicked() { ts.last = total } if ts.last == total { ts.done, res.Done = true, true slog.Debug("8bit: typewriter done", "glyphs", total) } } shown := ts.last if ts.done { shown = total } shell.FillWidth().Height(k.D(len(lines) * pixel.LineHeight)).Draw(func(p *ui.Painter, r ui.Rect) { check(r) g := k.Canvas(p, r) left, end := shown, 0 for i, l := range lines { lr := []rune(l) n := min(len(lr), max(0, left)) g.Text(0, i*pixel.LineHeight, string(lr[:n]), o.Slot, 1) left -= len(lr) end = pixel.Measure(string(lr[:n])) } if shown == total && g.Step(2)%2 == 0 { g.Sprite(end+3, len(lines)*pixel.LineHeight-6, more, 0, pixel.Accent, 1) } if st.Focus { g.FocusMark(0, 0, g.W, g.H) } }) return res }