~eliasnaur/gio-patches

gio: ui/widget: password-mode for editor (io.Reader) v2 PROPOSED

Elias Naur: 1
 ui/widget: password-mode for editor (io.Reader)

 1 files changed, 57 insertions(+), 0 deletions(-)
Export patchset (mbox)
How do I use this?

Copy & paste the following snippet into your terminal to import this patchset into git:

curl -s https://lists.sr.ht/~eliasnaur/gio-patches/patches/9704/mbox | git am -3
Learn more about email & git

Re: [PATCH gio 0/1] [PATCH gio v2] ui/widget: password-mode for editor (io.Reader) Export this patch

Hi, and thank you for working on this.

Your approach seems to touch more of the Editor than I would expect,
and I would prefer to not have masks panic if SingleLine is false.
How about just a replacing io.Reader on top of the editor buffer?

Here's a rough sketch (it compiles but is untested):

diff --git widget/editor.go widget/editor.go
index 341b4cf..d4cef99 100644
--- widget/editor.go
@@ -4,6 +4,7 @@ package widget

import (
	"image"
	"io"
	"math"
	"strings"
	"time"
@@ -33,6 +34,9 @@ type Editor struct {
	// If not enabled, carriage returns are inserted as newlines in the text.
	Submit bool

	Mask rune

	maskReader   maskReader
	eventKey     int
	scale        int
	font         text.Font
@@ -65,6 +69,54 @@ type Editor struct {
	prevEvents int
}

type maskReader struct {
	// r is the underlying reader.
	r       io.Reader
	maskBuf [utf8.UTFMax]byte
	// mask is the utf-8 encoded mask rune.
	mask []byte
	// remaining is the number of mask bytes yet to write.
	remaining int
}

func (m *maskReader) reset(r io.Reader, mr rune) {
	m.r = r
	n := utf8.EncodeRune(m.maskBuf[:], mr)
	m.mask = m.maskBuf[:n]
}

// Read reads from the underlying reader and replaces every
// rune with the mask rune.
func (m *maskReader) Read(b []byte) (int, error) {
	var bytes int
	for len(b) > 0 {
		// Write remaining mask bytes.
		for m.remaining > 0 {
			mask := m.mask
			// Handle partial mask.
			if m.remaining < len(mask) {
				mask = mask[len(mask)-m.remaining:]
			}
			n := copy(b, mask)
			b = b[n:]
			bytes += n
			m.remaining -= n
		}
		// Read from underlying reader and count runes.
		n, err := m.r.Read(b)
		for n > 0 {
			_, size := utf8.DecodeRune(b)
			n -= size
			b = b[size:]
			m.remaining += len(m.mask)
		}
		if err != nil {
			return bytes, err
		}
	}
	return bytes, nil
}

type EditorEvent interface {
	isEditorEvent()
}
@@ -433,6 +485,11 @@ func (e *Editor) moveCoord(c unit.Converter, pos image.Point) {
func (e *Editor) layoutText(c unit.Converter, s text.Shaper, font text.Font) ([]text.Line, layout.Dimensions) {
	e.rr.Reset()
	opts := text.LayoutOptions{MaxWidth: e.maxWidth}
	var r io.Reader = &e.rr
	if e.Mask != 0 {
		e.maskReader.reset(r, e.Mask)
		r = &e.maskReader
	}
	lines, _ := s.Layout(c, font, &e.rr, opts)
	dims := linesDimens(lines)
	for i := 0; i < len(lines)-1; i++ {
View this thread in the archives