Files
aego/ui/richtext/cull_gltest_test.go
sanspie 9e20b59d00
GitHub / windows amd64 native GitHub-hosted native CI: success
GitHub / darwin amd64 native GitHub-hosted native CI: success
GitHub / linux amd64 native GitHub-hosted native CI: success
ci-platforms / darwin amd64 smoke (push) Skipped
ci-platforms / windows amd64 smoke (push) Skipped
ci-platforms / darwin arm64 smoke (push) Skipped
ci / build (amd64, linux, ubuntu-24.04) (push) Failing after 9m5s
github-native / gitea-pending (push) Skipped
GitHub / darwin arm64 native GitHub-hosted native CI: failure
github-native / darwin amd64 native (push) Skipped
github-native / linux amd64 native (push) Skipped
github-native / windows amd64 native (push) Skipped
github-native / darwin arm64 native (push) Skipped
github-native / gitea-result (push) Skipped
ci / checks (push) Failing after 47m1s
ci / release (push) Skipped
editor: cull clipped agent transcript lines
2026-11-10 10:03:23 +03:00

127 lines
3.7 KiB
Go

//go:build aego_gltest
package richtext
import (
"fmt"
"reflect"
"strings"
"testing"
"aego/core/mathx"
"aego/gpu"
"aego/platform/input"
"aego/render/text"
"aego/render/texture"
"aego/ui/core"
"aego/ui/draw"
"aego/ui/theme"
)
func clipTestFont() *text.Font {
f := text.NewFont(14, 1, 18, 14)
whole := texture.Whole(gpu.TextureHandle{Index: 1, Gen: 1}, 16, 16)
for r := rune(32); r < 127; r++ {
f.SetGlyph(r, text.FontGlyph{Region: whole, Size: mathx.Vec2{X: 8, Y: 14}, Advance: 8})
}
return f
}
func drawClipItems(ui *core.Context, rt *Text, r, clip mathx.Rect, prefix string) []draw.TestDrawnItem {
var in input.State
ui.Begin(&in, mathx.Vec2{X: 400, Y: r.H + 20}, 1)
ui.PushClip(clip)
rt.Draw(ui, ui.ID("rich"), r)
ui.PopClip()
list := ui.End()
return list.TestDrawnItems(mathx.Rect{W: 400, H: r.H + 20}, ui.Font(), prefix, 0, 0)
}
func TestCullClippedKeepsVisibleLinesAndDropsOffscreenCommands(t *testing.T) {
ui := core.New(clipTestFont())
ui.Theme = theme.Editor()
var md strings.Builder
for i := range 40 {
fmt.Fprintf(&md, "row%02d\n\n", i)
}
var rt Text
rt.Set(Parse(md.String()))
r := mathx.Rect{W: 220}
r.H = rt.Measure(ui, r.W)
for _, y := range []float32{0, 61, r.H / 2, r.H - 99} {
clip := mathx.Rect{Y: y, W: r.W, H: 99}
rt.CullClipped = false
old := drawClipItems(ui, &rt, r, clip, "row")
rt.CullClipped = true
now := drawClipItems(ui, &rt, r, clip, "row")
visible := func(items []draw.TestDrawnItem) []draw.TestDrawnItem {
var out []draw.TestDrawnItem
for _, v := range items {
if v.Bounds.Y+v.Bounds.H > clip.Y && v.Bounds.Y < clip.Y+clip.H {
out = append(out, v)
}
}
return out
}
if !reflect.DeepEqual(visible(old), visible(now)) {
t.Fatalf("visible text or positions changed at y=%v: old=%+v new=%+v", y, visible(old), visible(now))
}
if len(now) >= len(old) {
t.Fatalf("y=%v did not cull: old=%d new=%d", y, len(old), len(now))
}
}
}
func TestCullClippedRetainsOverhangingGlyphs(t *testing.T) {
f := clipTestFont()
whole := texture.Whole(gpu.TextureHandle{Index: 1, Gen: 1}, 16, 16)
f.SetGlyph('A', text.FontGlyph{Region: whole, Size: mathx.Vec2{X: 8, Y: 100}, Advance: 8})
f.SetGlyph('Z', text.FontGlyph{Region: whole, Offset: mathx.Vec2{Y: -100}, Size: mathx.Vec2{X: 8, Y: 110}, Advance: 8})
ui := core.New(f)
ui.Theme = theme.Editor()
var rt Text
rt.CullClipped = true
rt.Set(Parse("A\n\n" + strings.Repeat("row\n\n", 12) + "Z"))
r := mathx.Rect{W: 220}
r.H = rt.Measure(ui, r.W)
lines := rt.Doc().Lines
pad := 2 * ui.Theme.Padding
for _, tc := range []struct {
prefix string
y float32
}{
{"A", pad + lines[0].Y + 70},
{"Z", pad + lines[len(lines)-1].Y - 70},
} {
items := drawClipItems(ui, &rt, r, mathx.Rect{Y: tc.y, W: r.W, H: 10}, tc.prefix)
if len(items) != 1 {
t.Fatalf("%s glyph crossing the clip was dropped: %+v", tc.prefix, items)
}
}
}
func TestCullClippedRetainsRunButtonOutsideItsCodeLine(t *testing.T) {
ui := core.New(clipTestFont())
ui.Theme = theme.Editor()
ui.Theme.RowHeight = 100
var rt Text
rt.RunButtons = true
rt.Set(Parse("```aes run\nrun_me()\n```\n\n" + strings.Repeat("row\n\n", 8)))
r := mathx.Rect{W: 220}
r.H = rt.Measure(ui, r.W)
line := rt.Doc().Lines[0]
if !line.Run {
t.Fatal("runnable code line not found")
}
buttonY := 2*ui.Theme.Padding + line.Y - CodePad + 4
textY := buttonY + (ui.Theme.RowHeight+2-ui.LineHeight())/2
clip := mathx.Rect{Y: textY, W: r.W, H: ui.LineHeight()}
rt.CullClipped = false
old := drawClipItems(ui, &rt, r, clip, "Run")
rt.CullClipped = true
now := drawClipItems(ui, &rt, r, clip, "Run")
if len(old) != 1 || !reflect.DeepEqual(old, now) {
t.Fatalf("button in parent clip was culled: old=%+v new=%+v", old, now)
}
}