~eliasnaur/gio

fe5878bc63fd41c40c202e8eff087bc3a37d50ef — Chris Waldon 5 months ago 5d1d1df
widget: track minWidth of editor for alignment

This commit extends the editor to keep track of its own minimum constraint
and to provide that value to the text shaper for the purpose of aligning
text. Without this, the shaper does not know how much of the width of the
editor to use for alignment purposes.

Signed-off-by: Chris Waldon <christopher.waldon.dev@gmail.com>
2 files changed, 55 insertions(+), 36 deletions(-)

M widget/editor.go
M widget/editor_test.go
M widget/editor.go => widget/editor.go +21 -16
@@ 52,21 52,21 @@ type Editor struct {
	// all characters are allowed.
	Filter string

	eventKey     int
	font         text.Font
	shaper       *text.Shaper
	textSize     fixed.Int26_6
	blinkStart   time.Time
	focused      bool
	rr           editBuffer
	maskReader   maskReader
	lastMask     rune
	maxWidth     int
	viewSize     image.Point
	valid        bool
	regions      []region
	dims         layout.Dimensions
	requestFocus bool
	eventKey           int
	font               text.Font
	shaper             *text.Shaper
	textSize           fixed.Int26_6
	blinkStart         time.Time
	focused            bool
	rr                 editBuffer
	maskReader         maskReader
	lastMask           rune
	maxWidth, minWidth int
	viewSize           image.Point
	valid              bool
	regions            []region
	dims               layout.Dimensions
	requestFocus       bool

	// offIndex is an index of rune index to byte offsets.
	offIndex []offEntry


@@ 547,10 547,15 @@ func (e *Editor) Layout(gtx layout.Context, lt *text.Shaper, font text.Font, siz
	if e.SingleLine {
		maxWidth = math.MaxInt
	}
	minWidth := gtx.Constraints.Min.X
	if maxWidth != e.maxWidth {
		e.maxWidth = maxWidth
		e.invalidate()
	}
	if minWidth != e.minWidth {
		e.minWidth = minWidth
		e.invalidate()
	}
	if lt != e.shaper {
		e.shaper = lt
		e.invalidate()


@@ 891,7 896,7 @@ func (e *Editor) layoutText(lt *text.Shaper) {
			Font:      e.font,
			PxPerEm:   e.textSize,
			Alignment: e.Alignment,
		}, 0, e.maxWidth, e.locale, r)
		}, e.minWidth, e.maxWidth, e.locale, r)
		for glyph, ok := it.processGlyph(lt.NextGlyph()); ok; glyph, ok = it.processGlyph(lt.NextGlyph()) {
			e.index.Glyph(glyph)
		}

M widget/editor_test.go => widget/editor_test.go +34 -20
@@ 115,7 115,7 @@ func TestEditorZeroDimensions(t *testing.T) {
func TestEditorConfigurations(t *testing.T) {
	gtx := layout.Context{
		Ops:         new(op.Ops),
		Constraints: layout.Exact(image.Pt(100, 100)),
		Constraints: layout.Exact(image.Pt(300, 300)),
		Locale:      english,
	}
	cache := text.NewShaper(gofont.Collection())


@@ 126,27 126,41 @@ func TestEditorConfigurations(t *testing.T) {

	// Ensure that both ends of the text are reachable in all permutations
	// of settings that influence layout.
	for _, lineMode := range []bool{true, false} {
	for _, singleLine := range []bool{true, false} {
		for _, alignment := range []text.Alignment{text.Start, text.Middle, text.End} {
			t.Run(fmt.Sprintf("SingleLine: %v Alignment: %v", lineMode, alignment), func(t *testing.T) {
				defer func() {
					if err := recover(); err != nil {
						t.Error(err)
			for _, zeroMin := range []bool{true, false} {
				t.Run(fmt.Sprintf("SingleLine: %v Alignment: %v ZeroMinConstraint: %v", singleLine, alignment, zeroMin), func(t *testing.T) {
					defer func() {
						if err := recover(); err != nil {
							t.Error(err)
						}
					}()
					if zeroMin {
						gtx.Constraints.Min = image.Point{}
					} else {
						gtx.Constraints.Min = gtx.Constraints.Max
					}
				}()
				e := new(Editor)
				e.SingleLine = lineMode
				e.Alignment = alignment
				e.SetText(sentence)
				e.SetCaret(0, 0)
				e.Layout(gtx, cache, font, fontSize, nil)
				e.SetCaret(runes, runes)
				e.Layout(gtx, cache, font, fontSize, nil)
				coords := e.CaretCoords()
				if int(coords.X) > gtx.Constraints.Max.X || int(coords.Y) > gtx.Constraints.Max.Y {
					t.Errorf("caret coordinates %v exceed constraints %v", coords, gtx.Constraints.Max)
				}
			})
					e := new(Editor)
					e.SingleLine = singleLine
					e.Alignment = alignment
					e.SetText(sentence)
					e.SetCaret(0, 0)
					dims := e.Layout(gtx, cache, font, fontSize, nil)
					if dims.Size.X < gtx.Constraints.Min.X || dims.Size.Y < gtx.Constraints.Min.Y {
						t.Errorf("expected min size %#+v, got %#+v", gtx.Constraints.Min, dims.Size)
					}
					coords := e.CaretCoords()
					if halfway := float32(gtx.Constraints.Min.X) * .5; !singleLine && alignment == text.Middle && !zeroMin && coords.X != halfway {
						t.Errorf("expected caret X to be %f, got %f", halfway, coords.X)
					}
					e.SetCaret(runes, runes)
					e.Layout(gtx, cache, font, fontSize, nil)
					coords = e.CaretCoords()
					if int(coords.X) > gtx.Constraints.Max.X || int(coords.Y) > gtx.Constraints.Max.Y {
						t.Errorf("caret coordinates %v exceed constraints %v", coords, gtx.Constraints.Max)
					}
				})
			}
		}
	}
}