main df9bf83

position_hint_test.go RawHistoryFolder

1package vulkan
2
3import (
4	"github.com/bnema/neferwl/internal/ports"
5	"github.com/stretchr/testify/require"
6	"image"
7	"math"
8	"testing"
9)
10
11func TestPositionHintGeometryDamageAllocations(t *testing.T) {
12	for _, scale := range []float64{1, 1.5, 2} {
13		r := &Renderer{width: int(640 * scale), height: int(480 * scale)}
14		s := ports.Scene{OutputWidth: 640, OutputHeight: 480, Scale: scale, Border: ports.Border{Active: "#abcdef"}, PositionHint: ports.PositionHint{Opacity: 1, Columns: 2, Counts: [16]uint8{2, 1}, ActiveRow: 1}}
15		dmg := newDamage(&target{}, s, image.Rect(0, 0, r.width, r.height))
16		draws := r.draws(s, nil, dmg)
17		require.NotEmpty(t, draws)
18		w := sceneWalk{scale: scale}
19		b := s.PositionHint.Bounds(640, 480)
20		want := w.physRect(b.X, b.Y, b.W, b.H)
21		var area image.Rectangle
22		for _, d := range draws {
23			area = area.Union(image.Rect(int(d.pc.rect[0]), int(d.pc.rect[1]), int(d.pc.rect[2]), int(d.pc.rect[3])))
24		}
25		require.Equal(t, want, area)
26		require.Zero(t, testing.AllocsPerRun(100, func() { r.draws(s, nil, dmg) }))
27		old := s
28		old.PositionHint = ports.PositionHint{}
29		phys := func(b ports.Rect) image.Rectangle { return w.physRect(b.X, b.Y, b.W, b.H) }
30		got, ok := sceneDelta(old, s, phys, r.width*r.height)
31		require.True(t, ok)
32		require.Equal(t, want, got)
33		s.PositionHint.Opacity = 0.5
34		got, ok = sceneDelta(old, s, phys, r.width*r.height)
35		require.True(t, ok)
36		require.Equal(t, want, got)
37	}
38}
39
40func TestPositionHintDamageTransitions(t *testing.T) {
41	base := ports.Scene{Scale: 1, OutputWidth: 640, OutputHeight: 480, PositionHint: ports.PositionHint{Opacity: 1, Columns: 2, Counts: [16]uint8{2, 1}}}
42	phys := func(r ports.Rect) image.Rectangle { return image.Rect(r.X, r.Y, r.X+r.W, r.Y+r.H) }
43	bounds := func(s ports.Scene) image.Rectangle { return phys(s.PositionHint.Bounds(s.OutputWidth, s.OutputHeight)) }
44	t.Run("disappear", func(t *testing.T) {
45		cur := base
46		cur.PositionHint = ports.PositionHint{}
47		got, ok := sceneDelta(base, cur, phys, 640*480)
48		require.True(t, ok)
49		require.Equal(t, bounds(base), got)
50	})
51	t.Run("shape", func(t *testing.T) {
52		cur := base
53		cur.PositionHint.Columns = 3
54		cur.PositionHint.Counts[2] = 4
55		got, ok := sceneDelta(base, cur, phys, 640*480)
56		require.True(t, ok)
57		require.Equal(t, bounds(base).Union(bounds(cur)), got)
58	})
59	t.Run("fade", func(t *testing.T) {
60		cur := base
61		cur.PositionHint.Opacity = 0.5
62		got, ok := sceneDelta(base, cur, phys, 640*480)
63		require.True(t, ok)
64		require.Equal(t, bounds(base), got)
65	})
66	t.Run("scale", func(t *testing.T) {
67		cur := base
68		cur.Scale = 2
69		_, ok := sceneDelta(base, cur, phys, 640*480)
70		require.False(t, ok, "scale change requires full target redraw; geometry scaling is checked separately")
71	})
72}
73
74func TestPositionHintRoundedQuads(t *testing.T) {
75	for _, scale := range []float64{1, 2} {
76		r := &Renderer{width: int(640 * scale), height: int(480 * scale)}
77		h := hintGrid(3, 2)
78		s := ports.Scene{OutputWidth: 640, OutputHeight: 480, Scale: scale, PositionHint: h}
79		dmg := newDamage(&target{}, s, image.Rect(0, 0, r.width, r.height))
80		draws := r.draws(s, nil, dmg)
81		require.Len(t, draws, 1+6)
82		w := sceneWalk{scale: scale}
83		b := h.Bounds(640, 480)
84		pill := w.physRect(b.X, b.Y, b.W, b.H)
85		for i, d := range draws {
86			require.NotZero(t, d.pc.misc[1]&flagRounded, "draw %d", i)
87			require.Zero(t, d.pc.misc[1]&flagRing)
88			require.Equal(t, roundRect(image.Rect(int(d.pc.rect[0]), int(d.pc.rect[1]), int(d.pc.rect[2]), int(d.pc.rect[3]))), d.round)
89		}
90		require.Equal(t, pill, image.Rect(int(draws[0].pc.rect[0]), int(draws[0].pc.rect[1]), int(draws[0].pc.rect[2]), int(draws[0].pc.rect[3])))
91		require.Equal(t, float32(w.phys(ports.PositionHintRadius)), math.Float32frombits(draws[0].radii[0]))
92		// Active dot (col 1,row 1) is the larger one: radius = phys(active diameter)/2.
93		active := draws[1+1*2+1]
94		require.Equal(t, float32(w.phys(ports.PositionHintActiveDiameter)/2), math.Float32frombits(active.radii[0]))
95		other := draws[1]
96		require.Equal(t, float32(w.phys(ports.PositionHintDiameter)/2), math.Float32frombits(other.radii[0]))
97	}
98}
99
100func TestPositionHintRoundedOrient(t *testing.T) {
101	r := &Renderer{width: 640, height: 480}
102	s := ports.Scene{OutputWidth: 640, OutputHeight: 480, Scale: 1, PositionHint: hintGrid(2, 2)}
103	dmg := newDamage(&target{}, s, image.Rect(0, 0, r.width, r.height))
104	ds := r.draws(s, nil, dmg)
105	r.orient(ds, ports.BufferTransform(1))
106	for _, d := range ds {
107		require.Equal(t, roundRect(image.Rect(int(d.pc.rect[0]), int(d.pc.rect[1]), int(d.pc.rect[2]), int(d.pc.rect[3]))), d.round)
108	}
109}
110
111func TestHintRoundDraws(t *testing.T) {
112	bounds := image.Rect(0, 0, 100, 100)
113	for _, tc := range []struct {
114		name       string
115		rect       image.Rectangle
116		radius     int
117		wantDraws  int
118		wantRadius float32
119	}{
120		{"inside", image.Rect(10, 10, 50, 40), 6, 1, 6},
121		{"clipped fully off output", image.Rect(200, 200, 240, 230), 6, 0, 0},
122		{"clipped by bounds", image.Rect(90, 90, 130, 130), 6, 1, 6},
123		{"tiny pill clamps the radius", image.Rect(10, 10, 16, 14), 100, 1, 2},
124		{"zero radius is a plain fill", image.Rect(10, 10, 50, 40), 0, 1, 0},
125		{"negative radius is a plain fill", image.Rect(10, 10, 50, 40), -3, 1, 0},
126	} {
127		t.Run(tc.name, func(t *testing.T) {
128			w := sceneWalk{r: &Renderer{width: 100, height: 100}, alpha: 1, bounds: bounds}
129			w.hintRound(tc.rect, tc.radius, [3]uint8{1, 2, 3})
130			require.Len(t, w.draws, tc.wantDraws)
131			for _, d := range w.draws {
132				if tc.wantRadius == 0 {
133					require.Zero(t, d.pc.misc[1]&flagRounded)
134					continue
135				}
136				require.NotZero(t, d.pc.misc[1]&flagRounded)
137				require.Equal(t, roundRect(tc.rect), d.round, "the clip is the requested rect")
138				require.Equal(t, tc.wantRadius, math.Float32frombits(d.radii[0]))
139			}
140		})
141	}
142}
143
144func TestPositionHintRoundedRotatedOrient(t *testing.T) {
145	for transform := ports.BufferTransform(1); transform < 8; transform++ {
146		r := &Renderer{width: 640, height: 480}
147		sw, sh := r.sceneSize(transform)
148		s := ports.Scene{OutputWidth: sw, OutputHeight: sh, Scale: 1, Transform: transform, PositionHint: hintGrid(2, 2)}
149		ds := r.draws(s, nil, newDamage(&target{}, s, image.Rect(0, 0, sw, sh)))
150		require.Len(t, ds, 1+4)
151		sceneRects := make([]image.Rectangle, len(ds))
152		for i, d := range ds {
153			sceneRects[i] = image.Rect(int(d.pc.rect[0]), int(d.pc.rect[1]), int(d.pc.rect[2]), int(d.pc.rect[3]))
154		}
155		r.orient(ds, transform)
156		for i, d := range ds {
157			want := transform.RectToBuffer(sceneRects[i], sw, sh)
158			require.Equal(t, roundRect(want), d.round, "transform %d draw %d", transform, i)
159			require.NotZero(t, d.pc.misc[1]&flagRounded)
160		}
161	}
162}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 23

Tags 10

↑↓ moveenter openesc close