gioui.org@v0.6.1-0.20240506124620-7a9ce51988ce/layout/list_test.go (about) 1 // SPDX-License-Identifier: Unlicense OR MIT 2 3 package layout 4 5 import ( 6 "image" 7 "testing" 8 9 "gioui.org/f32" 10 "gioui.org/io/event" 11 "gioui.org/io/input" 12 "gioui.org/io/pointer" 13 "gioui.org/op" 14 ) 15 16 func TestListPositionExtremes(t *testing.T) { 17 var l List 18 gtx := Context{ 19 Ops: new(op.Ops), 20 Constraints: Exact(image.Pt(20, 10)), 21 } 22 const n = 3 23 layout := func(_ Context, idx int) Dimensions { 24 if idx < 0 || idx >= n { 25 t.Errorf("list index %d out of bounds [0;%d]", idx, n-1) 26 } 27 return Dimensions{} 28 } 29 l.Position.First = -1 30 l.Layout(gtx, n, layout) 31 l.Position.First = n + 1 32 l.Layout(gtx, n, layout) 33 } 34 35 func TestEmptyList(t *testing.T) { 36 var l List 37 gtx := Context{ 38 Ops: new(op.Ops), 39 Constraints: Exact(image.Pt(20, 10)), 40 } 41 dims := l.Layout(gtx, 0, nil) 42 if got, want := dims.Size, gtx.Constraints.Min; got != want { 43 t.Errorf("got %v; want %v", got, want) 44 } 45 } 46 47 func TestListScrollToEnd(t *testing.T) { 48 l := List{ 49 ScrollToEnd: true, 50 } 51 gtx := Context{ 52 Ops: new(op.Ops), 53 Constraints: Exact(image.Pt(20, 10)), 54 } 55 l.Layout(gtx, 1, func(gtx Context, idx int) Dimensions { 56 return Dimensions{ 57 Size: image.Pt(10, 10), 58 } 59 }) 60 if want, got := -10, l.Position.Offset; want != got { 61 t.Errorf("got offset %d, want %d", got, want) 62 } 63 } 64 65 func TestListPosition(t *testing.T) { 66 _s := func(e ...event.Event) []event.Event { return e } 67 r := new(input.Router) 68 gtx := Context{ 69 Ops: new(op.Ops), 70 Constraints: Constraints{ 71 Max: image.Pt(20, 10), 72 }, 73 Source: r.Source(), 74 } 75 el := func(gtx Context, idx int) Dimensions { 76 return Dimensions{Size: image.Pt(10, 10)} 77 } 78 for _, tc := range []struct { 79 label string 80 num int 81 scroll []event.Event 82 first int 83 count int 84 offset int 85 last int 86 }{ 87 {label: "no item", last: 20}, 88 {label: "1 visible 0 hidden", num: 1, count: 1, last: 10}, 89 {label: "2 visible 0 hidden", num: 2, count: 2}, 90 {label: "2 visible 1 hidden", num: 3, count: 2}, 91 {label: "3 visible 0 hidden small scroll", num: 3, count: 3, offset: 5, last: -5, 92 scroll: _s( 93 pointer.Event{ 94 Source: pointer.Mouse, 95 Buttons: pointer.ButtonPrimary, 96 Kind: pointer.Press, 97 Position: f32.Pt(0, 0), 98 }, 99 pointer.Event{ 100 Source: pointer.Mouse, 101 Kind: pointer.Scroll, 102 Scroll: f32.Pt(5, 0), 103 }, 104 pointer.Event{ 105 Source: pointer.Mouse, 106 Buttons: pointer.ButtonPrimary, 107 Kind: pointer.Release, 108 Position: f32.Pt(5, 0), 109 }, 110 )}, 111 {label: "3 visible 0 hidden small scroll 2", num: 3, count: 3, offset: 3, last: -7, 112 scroll: _s( 113 pointer.Event{ 114 Source: pointer.Mouse, 115 Buttons: pointer.ButtonPrimary, 116 Kind: pointer.Press, 117 Position: f32.Pt(0, 0), 118 }, 119 pointer.Event{ 120 Source: pointer.Mouse, 121 Kind: pointer.Scroll, 122 Scroll: f32.Pt(3, 0), 123 }, 124 pointer.Event{ 125 Source: pointer.Mouse, 126 Buttons: pointer.ButtonPrimary, 127 Kind: pointer.Release, 128 Position: f32.Pt(5, 0), 129 }, 130 )}, 131 {label: "2 visible 1 hidden large scroll", num: 3, count: 2, first: 1, 132 scroll: _s( 133 pointer.Event{ 134 Source: pointer.Mouse, 135 Buttons: pointer.ButtonPrimary, 136 Kind: pointer.Press, 137 Position: f32.Pt(0, 0), 138 }, 139 pointer.Event{ 140 Source: pointer.Mouse, 141 Kind: pointer.Scroll, 142 Scroll: f32.Pt(10, 0), 143 }, 144 pointer.Event{ 145 Source: pointer.Mouse, 146 Buttons: pointer.ButtonPrimary, 147 Kind: pointer.Release, 148 Position: f32.Pt(15, 0), 149 }, 150 )}, 151 } { 152 t.Run(tc.label, func(t *testing.T) { 153 gtx.Ops.Reset() 154 155 var list List 156 // Initialize the list. 157 list.Layout(gtx, tc.num, el) 158 // Generate the scroll events. 159 r.Frame(gtx.Ops) 160 r.Queue(tc.scroll...) 161 // Let the list process the events. 162 list.Layout(gtx, tc.num, el) 163 164 pos := list.Position 165 if got, want := pos.First, tc.first; got != want { 166 t.Errorf("List: invalid first position: got %v; want %v", got, want) 167 } 168 if got, want := pos.Count, tc.count; got != want { 169 t.Errorf("List: invalid number of visible children: got %v; want %v", got, want) 170 } 171 if got, want := pos.Offset, tc.offset; got != want { 172 t.Errorf("List: invalid first visible offset: got %v; want %v", got, want) 173 } 174 if got, want := pos.OffsetLast, tc.last; got != want { 175 t.Errorf("List: invalid last visible offset: got %v; want %v", got, want) 176 } 177 }) 178 } 179 } 180 181 func TestExtraChildren(t *testing.T) { 182 var l List 183 l.Position.First = 1 184 gtx := Context{ 185 Ops: new(op.Ops), 186 Constraints: Exact(image.Pt(10, 10)), 187 } 188 count := 0 189 const all = 3 190 l.Layout(gtx, all, func(gtx Context, idx int) Dimensions { 191 count++ 192 return Dimensions{Size: image.Pt(10, 10)} 193 }) 194 if count != all { 195 t.Errorf("laid out %d of %d children", count, all) 196 } 197 }