shithub: xui

Download patch

ref: 00d144fb31327e3e7181f669c58d6fdbe0c9485e
parent: 0c44bd66e976d09c7aad8c6081d8b590832cea46
author: Philip Silva <philip.silva@protonmail.com>
date: Thu Jun 5 13:59:03 EDT 2025

field cursor

--- a/cmd/hello/hello.go
+++ b/cmd/hello/hello.go
@@ -80,7 +80,7 @@
 	}, nil)
 	btn.Margin = x.Space(5, 10)
 
-	fl := field.New(x, image.ZP, " ", x.Rect(0,0, 150, 50))
+	fl := field.New(x, image.ZP, "", x.Rect(0,0, 150, 50))
 	fl.Margin = x.Space(5, 10)
 	b2 := box.New([]element.Interface{
 		l,
--- a/field/field.go
+++ b/field/field.go
@@ -35,6 +35,9 @@
 	Text string
 	color.Colorset
 
+	// Position of the cursor
+	Pos int
+
 	textImg *memdraw.Image
 	borderImg *memdraw.Image
 	hoverImg *memdraw.Image
@@ -76,16 +79,32 @@
 			f.cb(tev, f.cbUserData)
 		}
 	case keyboard.Event:
-		log.Printf("key pressed: %+v", tev)
+		log.Printf("key pressed: %+v %d % x", tev, tev, tev)
 
 		switch tev.Key {
 		case Backspace:
-			if len(f.Text) > 0 {
-				f.Text = f.Text[:len(f.Text)-1]
+			if f.Pos > 0 && len(f.Text) > 0 {
+				f.Text = f.Text[:f.Pos-1] + f.Text[f.Pos:]
 			}
+			f.Pos -= 1
+		case draw.KeyDelete:
+			if f.Pos < len(f.Text) {
+				f.Text = f.Text[:f.Pos] + f.Text[f.Pos+1:]
+			}
+		case draw.KeyLeft:
+			f.Pos -= 1
+		case draw.KeyRight:
+			f.Pos += 1
 		default:
 			f.Text += string([]byte{byte(tev.Key)})
+			f.Pos = len(f.Text)
 		}
+		if f.Pos < 0 {
+			f.Pos = 0
+		}
+		if f.Pos > len(f.Text) {
+			f.Pos = len(f.Text)
+		}
 		//log.Printf("event: call updateTextImgs")
 		f.updateTextImgs()
 	}
@@ -138,7 +157,12 @@
 	f.hoverImg.Draw(rr, f.textImg, image.ZP, color.EmptyMask, image.ZP, draw.SoverD)
 	geom.DrawRoundedBorder(f.hoverImg, f.Rectangle, f.Colorset.Hover.Border)
 
-	geom.DrawCursor(f.hoverImg, r, f.textImg, f.Colorset.Hover.Border)
+	textPart, err := font.String(f.Text[:f.Pos])
+	if err == nil {
+		geom.DrawCursor(f.hoverImg, r, textPart.R, f.Colorset.Hover.Border)
+	} else {
+		log.Printf("font string sub text: %v", err)
+	}
 }
 
 func (f Field) Focus() {
--- a/field/field_test.go
+++ b/field/field_test.go
@@ -9,17 +9,34 @@
 	"testing"
 )
 
-func TestRender(t *testing.T) {
+var rField = image.Rect(0, 0, 300, 100)
+
+func newField(t *testing.T) *Field {
 	memdraw.Init()
 	rScreen := image.Rect(0, 0, 800, 600)
-	rField := image.Rect(0, 0, 300, 100)
 	img, err := memdraw.AllocImage(rScreen, draw.ABGR32)
 	if err != nil {
 		t.Fail()
 	}
 	memdraw.FillColor(img, draw.White)
-	f := New(&xuitest.Xui{}, image.ZP, "", rField)
+	return New(&xuitest.Xui{}, image.ZP, "", rField)
+}
 
+func TestEvent(t *testing.T) {
+	f := newField(t)
+	f.Text = "Text"
+	f.Pos = 2
+	f.Event(keyboard.Event{Key: Backspace})
+	if f.Text != "Txt" {
+		t.Fatalf("Text=%s", f.Text)
+	}
+	if f.Pos != 1 {
+		t.Fatalf("Pos=%d", f.Pos)
+	}
+}
+
+func TestRender(t *testing.T) {
+	f := newField(t)
 	for i := 0; i < 20; i++ {
 		f.Event(keyboard.Event{Key: 'a'})
 		img := f.Render()
@@ -32,4 +49,4 @@
 			t.Fatalf("bbox outside field rect")
 		}
 	}
-}
\ No newline at end of file
+}
--- a/internal/geom/lines.go
+++ b/internal/geom/lines.go
@@ -48,9 +48,9 @@
 	memdraw.Line(img, image.Pt(x1-radius, y0), image.Pt(x0+radius, y0), 0, 0, 0, color, image.ZP, draw.SoverD)
 }
 
-func DrawCursor(dst *memdraw.Image, bounds image.Rectangle, text *memdraw.Image, color *memdraw.Image) {
-	h := text.R.Dy()/2
-	p1 := text.R.Max.Add(image.Pt(h/5, -h/2))
+func DrawCursor(dst *memdraw.Image, bounds, textR image.Rectangle, color *memdraw.Image) {
+	h := textR.Dy()/2
+	p1 := textR.Max.Add(image.Pt(h/5, -h/2))
 	p0 := p1.Add(image.Pt(0, -h))
 	if p0.X >= bounds.Dx() {
 		return
--