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  }