github.com/Seikaijyu/gio@v0.0.1/io/router/key_test.go (about)

     1  // SPDX-License-Identifier: Unlicense OR MIT
     2  
     3  package router
     4  
     5  import (
     6  	"image"
     7  	"reflect"
     8  	"testing"
     9  
    10  	"github.com/Seikaijyu/gio/f32"
    11  	"github.com/Seikaijyu/gio/io/event"
    12  	"github.com/Seikaijyu/gio/io/key"
    13  	"github.com/Seikaijyu/gio/io/pointer"
    14  	"github.com/Seikaijyu/gio/op"
    15  	"github.com/Seikaijyu/gio/op/clip"
    16  )
    17  
    18  func TestKeyWakeup(t *testing.T) {
    19  	handler := new(int)
    20  	var ops op.Ops
    21  	key.InputOp{Tag: handler}.Add(&ops)
    22  
    23  	var r Router
    24  	// Test that merely adding a handler doesn't trigger redraw.
    25  	r.Frame(&ops)
    26  	if _, wake := r.WakeupTime(); wake {
    27  		t.Errorf("adding key.InputOp triggered a redraw")
    28  	}
    29  	// However, adding a handler queues a Focus(false) event.
    30  	if evts := r.Events(handler); len(evts) != 1 {
    31  		t.Errorf("no Focus event for newly registered key.InputOp")
    32  	}
    33  }
    34  
    35  func TestKeyMultiples(t *testing.T) {
    36  	handlers := make([]int, 3)
    37  	ops := new(op.Ops)
    38  	r := new(Router)
    39  
    40  	key.SoftKeyboardOp{Show: true}.Add(ops)
    41  	key.InputOp{Tag: &handlers[0]}.Add(ops)
    42  	key.FocusOp{Tag: &handlers[2]}.Add(ops)
    43  	key.InputOp{Tag: &handlers[1]}.Add(ops)
    44  
    45  	// The last one must be focused:
    46  	key.InputOp{Tag: &handlers[2]}.Add(ops)
    47  
    48  	r.Frame(ops)
    49  
    50  	assertKeyEvent(t, r.Events(&handlers[0]), false)
    51  	assertKeyEvent(t, r.Events(&handlers[1]), false)
    52  	assertKeyEvent(t, r.Events(&handlers[2]), true)
    53  	assertFocus(t, r, &handlers[2])
    54  	assertKeyboard(t, r, TextInputOpen)
    55  }
    56  
    57  func TestKeyStacked(t *testing.T) {
    58  	handlers := make([]int, 4)
    59  	ops := new(op.Ops)
    60  	r := new(Router)
    61  
    62  	key.InputOp{Tag: &handlers[0]}.Add(ops)
    63  	key.FocusOp{Tag: nil}.Add(ops)
    64  	key.SoftKeyboardOp{Show: false}.Add(ops)
    65  	key.InputOp{Tag: &handlers[1]}.Add(ops)
    66  	key.FocusOp{Tag: &handlers[1]}.Add(ops)
    67  	key.InputOp{Tag: &handlers[2]}.Add(ops)
    68  	key.SoftKeyboardOp{Show: true}.Add(ops)
    69  	key.InputOp{Tag: &handlers[3]}.Add(ops)
    70  
    71  	r.Frame(ops)
    72  
    73  	assertKeyEvent(t, r.Events(&handlers[0]), false)
    74  	assertKeyEvent(t, r.Events(&handlers[1]), true)
    75  	assertKeyEvent(t, r.Events(&handlers[2]), false)
    76  	assertKeyEvent(t, r.Events(&handlers[3]), false)
    77  	assertFocus(t, r, &handlers[1])
    78  	assertKeyboard(t, r, TextInputOpen)
    79  }
    80  
    81  func TestKeySoftKeyboardNoFocus(t *testing.T) {
    82  	ops := new(op.Ops)
    83  	r := new(Router)
    84  
    85  	// It's possible to open the keyboard
    86  	// without any active focus:
    87  	key.SoftKeyboardOp{Show: true}.Add(ops)
    88  
    89  	r.Frame(ops)
    90  
    91  	assertFocus(t, r, nil)
    92  	assertKeyboard(t, r, TextInputOpen)
    93  }
    94  
    95  func TestKeyRemoveFocus(t *testing.T) {
    96  	handlers := make([]int, 2)
    97  	ops := new(op.Ops)
    98  	r := new(Router)
    99  
   100  	// New InputOp with Focus and Keyboard:
   101  	key.InputOp{Tag: &handlers[0], Keys: "Short-Tab"}.Add(ops)
   102  	key.FocusOp{Tag: &handlers[0]}.Add(ops)
   103  	key.SoftKeyboardOp{Show: true}.Add(ops)
   104  
   105  	// New InputOp without any focus:
   106  	key.InputOp{Tag: &handlers[1], Keys: "Short-Tab"}.Add(ops)
   107  
   108  	r.Frame(ops)
   109  
   110  	// Add some key events:
   111  	event := event.Event(key.Event{Name: key.NameTab, Modifiers: key.ModShortcut, State: key.Press})
   112  	r.Queue(event)
   113  
   114  	assertKeyEvent(t, r.Events(&handlers[0]), true, event)
   115  	assertKeyEvent(t, r.Events(&handlers[1]), false)
   116  	assertFocus(t, r, &handlers[0])
   117  	assertKeyboard(t, r, TextInputOpen)
   118  
   119  	ops.Reset()
   120  
   121  	// Will get the focus removed:
   122  	key.InputOp{Tag: &handlers[0]}.Add(ops)
   123  
   124  	// Unchanged:
   125  	key.InputOp{Tag: &handlers[1]}.Add(ops)
   126  
   127  	// Remove focus by focusing on a tag that don't exist.
   128  	key.FocusOp{Tag: new(int)}.Add(ops)
   129  
   130  	r.Frame(ops)
   131  
   132  	assertKeyEventUnexpected(t, r.Events(&handlers[1]))
   133  	assertFocus(t, r, nil)
   134  	assertKeyboard(t, r, TextInputClose)
   135  
   136  	ops.Reset()
   137  
   138  	key.InputOp{Tag: &handlers[0]}.Add(ops)
   139  
   140  	key.InputOp{Tag: &handlers[1]}.Add(ops)
   141  
   142  	r.Frame(ops)
   143  
   144  	assertKeyEventUnexpected(t, r.Events(&handlers[0]))
   145  	assertKeyEventUnexpected(t, r.Events(&handlers[1]))
   146  	assertFocus(t, r, nil)
   147  	assertKeyboard(t, r, TextInputClose)
   148  
   149  	ops.Reset()
   150  
   151  	// Set focus to InputOp which already
   152  	// exists in the previous frame:
   153  	key.FocusOp{Tag: &handlers[0]}.Add(ops)
   154  	key.InputOp{Tag: &handlers[0]}.Add(ops)
   155  	key.SoftKeyboardOp{Show: true}.Add(ops)
   156  
   157  	// Remove focus.
   158  	key.InputOp{Tag: &handlers[1]}.Add(ops)
   159  	key.FocusOp{Tag: nil}.Add(ops)
   160  
   161  	r.Frame(ops)
   162  
   163  	assertKeyEventUnexpected(t, r.Events(&handlers[1]))
   164  	assertFocus(t, r, nil)
   165  	assertKeyboard(t, r, TextInputOpen)
   166  }
   167  
   168  func TestKeyFocusedInvisible(t *testing.T) {
   169  	handlers := make([]int, 2)
   170  	ops := new(op.Ops)
   171  	r := new(Router)
   172  
   173  	// Set new InputOp with focus:
   174  	key.FocusOp{Tag: &handlers[0]}.Add(ops)
   175  	key.InputOp{Tag: &handlers[0]}.Add(ops)
   176  	key.SoftKeyboardOp{Show: true}.Add(ops)
   177  
   178  	// Set new InputOp without focus:
   179  	key.InputOp{Tag: &handlers[1]}.Add(ops)
   180  
   181  	r.Frame(ops)
   182  
   183  	assertKeyEvent(t, r.Events(&handlers[0]), true)
   184  	assertKeyEvent(t, r.Events(&handlers[1]), false)
   185  	assertFocus(t, r, &handlers[0])
   186  	assertKeyboard(t, r, TextInputOpen)
   187  
   188  	ops.Reset()
   189  
   190  	//
   191  	// Removed first (focused) element!
   192  	//
   193  
   194  	// Unchanged:
   195  	key.InputOp{Tag: &handlers[1]}.Add(ops)
   196  
   197  	r.Frame(ops)
   198  
   199  	assertKeyEventUnexpected(t, r.Events(&handlers[0]))
   200  	assertKeyEventUnexpected(t, r.Events(&handlers[1]))
   201  	assertFocus(t, r, nil)
   202  	assertKeyboard(t, r, TextInputClose)
   203  
   204  	ops.Reset()
   205  
   206  	// Respawn the first element:
   207  	// It must receive one `Event{Focus: false}`.
   208  	key.InputOp{Tag: &handlers[0]}.Add(ops)
   209  
   210  	// Unchanged
   211  	key.InputOp{Tag: &handlers[1]}.Add(ops)
   212  
   213  	r.Frame(ops)
   214  
   215  	assertKeyEvent(t, r.Events(&handlers[0]), false)
   216  	assertKeyEventUnexpected(t, r.Events(&handlers[1]))
   217  	assertFocus(t, r, nil)
   218  	assertKeyboard(t, r, TextInputClose)
   219  
   220  }
   221  
   222  func TestNoOps(t *testing.T) {
   223  	r := new(Router)
   224  	r.Frame(nil)
   225  }
   226  
   227  func TestDirectionalFocus(t *testing.T) {
   228  	ops := new(op.Ops)
   229  	r := new(Router)
   230  	handlers := []image.Rectangle{
   231  		image.Rect(10, 10, 50, 50),
   232  		image.Rect(50, 20, 100, 80),
   233  		image.Rect(20, 26, 60, 80),
   234  		image.Rect(10, 60, 50, 100),
   235  	}
   236  
   237  	for i, bounds := range handlers {
   238  		cl := clip.Rect(bounds).Push(ops)
   239  		key.InputOp{Tag: &handlers[i]}.Add(ops)
   240  		cl.Pop()
   241  	}
   242  	r.Frame(ops)
   243  
   244  	r.MoveFocus(FocusLeft)
   245  	assertFocus(t, r, &handlers[0])
   246  	r.MoveFocus(FocusLeft)
   247  	assertFocus(t, r, &handlers[0])
   248  	r.MoveFocus(FocusRight)
   249  	assertFocus(t, r, &handlers[1])
   250  	r.MoveFocus(FocusRight)
   251  	assertFocus(t, r, &handlers[1])
   252  	r.MoveFocus(FocusDown)
   253  	assertFocus(t, r, &handlers[2])
   254  	r.MoveFocus(FocusDown)
   255  	assertFocus(t, r, &handlers[2])
   256  	r.MoveFocus(FocusLeft)
   257  	assertFocus(t, r, &handlers[3])
   258  	r.MoveFocus(FocusUp)
   259  	assertFocus(t, r, &handlers[0])
   260  
   261  	r.MoveFocus(FocusForward)
   262  	assertFocus(t, r, &handlers[1])
   263  	r.MoveFocus(FocusBackward)
   264  	assertFocus(t, r, &handlers[0])
   265  }
   266  
   267  func TestFocusScroll(t *testing.T) {
   268  	ops := new(op.Ops)
   269  	r := new(Router)
   270  	h := new(int)
   271  
   272  	parent := clip.Rect(image.Rect(1, 1, 14, 39)).Push(ops)
   273  	cl := clip.Rect(image.Rect(10, -20, 20, 30)).Push(ops)
   274  	key.InputOp{Tag: h}.Add(ops)
   275  	pointer.InputOp{
   276  		Tag:          h,
   277  		Kinds:        pointer.Scroll,
   278  		ScrollBounds: image.Rect(-100, -100, 100, 100),
   279  	}.Add(ops)
   280  	// Test that h is scrolled even if behind another handler.
   281  	pointer.InputOp{
   282  		Tag: new(int),
   283  	}.Add(ops)
   284  	cl.Pop()
   285  	parent.Pop()
   286  	r.Frame(ops)
   287  
   288  	r.MoveFocus(FocusLeft)
   289  	r.RevealFocus(image.Rect(0, 0, 15, 40))
   290  	evts := r.Events(h)
   291  	assertScrollEvent(t, evts[len(evts)-1], f32.Pt(6, -9))
   292  }
   293  
   294  func TestFocusClick(t *testing.T) {
   295  	ops := new(op.Ops)
   296  	r := new(Router)
   297  	h := new(int)
   298  
   299  	cl := clip.Rect(image.Rect(0, 0, 10, 10)).Push(ops)
   300  	key.InputOp{Tag: h}.Add(ops)
   301  	pointer.InputOp{
   302  		Tag:   h,
   303  		Kinds: pointer.Press | pointer.Release,
   304  	}.Add(ops)
   305  	cl.Pop()
   306  	r.Frame(ops)
   307  
   308  	r.MoveFocus(FocusLeft)
   309  	r.ClickFocus()
   310  	assertEventPointerTypeSequence(t, r.Events(h), pointer.Cancel, pointer.Press, pointer.Release)
   311  }
   312  
   313  func TestNoFocus(t *testing.T) {
   314  	r := new(Router)
   315  	r.MoveFocus(FocusForward)
   316  }
   317  
   318  func TestKeyRouting(t *testing.T) {
   319  	handlers := make([]int, 5)
   320  	ops := new(op.Ops)
   321  	macroOps := new(op.Ops)
   322  	r := new(Router)
   323  
   324  	rect := clip.Rect{Max: image.Pt(10, 10)}
   325  
   326  	macro := op.Record(macroOps)
   327  	key.InputOp{Tag: &handlers[0], Keys: "A"}.Add(ops)
   328  	cl1 := rect.Push(ops)
   329  	key.InputOp{Tag: &handlers[1], Keys: "B"}.Add(ops)
   330  	key.InputOp{Tag: &handlers[2], Keys: "A"}.Add(ops)
   331  	cl1.Pop()
   332  	cl2 := rect.Push(ops)
   333  	key.InputOp{Tag: &handlers[3]}.Add(ops)
   334  	key.InputOp{Tag: &handlers[4], Keys: "A"}.Add(ops)
   335  	cl2.Pop()
   336  	call := macro.Stop()
   337  	call.Add(ops)
   338  
   339  	r.Frame(ops)
   340  
   341  	A, B := key.Event{Name: "A"}, key.Event{Name: "B"}
   342  	r.Queue(A, B)
   343  
   344  	// With no focus, the events should traverse the final branch of the hit tree
   345  	// searching for handlers.
   346  	assertKeyEvent(t, r.Events(&handlers[4]), false, A)
   347  	assertKeyEvent(t, r.Events(&handlers[3]), false)
   348  	assertKeyEvent(t, r.Events(&handlers[2]), false)
   349  	assertKeyEvent(t, r.Events(&handlers[1]), false, B)
   350  	assertKeyEvent(t, r.Events(&handlers[0]), false)
   351  
   352  	r2 := new(Router)
   353  
   354  	call.Add(ops)
   355  	key.FocusOp{Tag: &handlers[3]}.Add(ops)
   356  	r2.Frame(ops)
   357  
   358  	r2.Queue(A, B)
   359  
   360  	// With focus, the events should traverse the branch of the hit tree
   361  	// containing the focused element.
   362  	assertKeyEvent(t, r2.Events(&handlers[4]), false)
   363  	assertKeyEvent(t, r2.Events(&handlers[3]), true)
   364  	assertKeyEvent(t, r2.Events(&handlers[2]), false)
   365  	assertKeyEvent(t, r2.Events(&handlers[1]), false)
   366  	assertKeyEvent(t, r2.Events(&handlers[0]), false, A)
   367  }
   368  
   369  func assertKeyEvent(t *testing.T, events []event.Event, expectedFocus bool, expectedInputs ...event.Event) {
   370  	t.Helper()
   371  	var evtFocus int
   372  	var evtKeyPress int
   373  	for _, e := range events {
   374  		switch ev := e.(type) {
   375  		case key.FocusEvent:
   376  			if ev.Focus != expectedFocus {
   377  				t.Errorf("focus is expected to be %v, got %v", expectedFocus, ev.Focus)
   378  			}
   379  			evtFocus++
   380  		case key.Event, key.EditEvent:
   381  			if len(expectedInputs) <= evtKeyPress {
   382  				t.Fatalf("unexpected key events")
   383  			}
   384  			if !reflect.DeepEqual(ev, expectedInputs[evtKeyPress]) {
   385  				t.Errorf("expected %v events, got %v", expectedInputs[evtKeyPress], ev)
   386  			}
   387  			evtKeyPress++
   388  		}
   389  	}
   390  	if evtFocus <= 0 {
   391  		t.Errorf("expected focus event")
   392  	}
   393  	if evtFocus > 1 {
   394  		t.Errorf("expected single focus event")
   395  	}
   396  	if evtKeyPress != len(expectedInputs) {
   397  		t.Errorf("expected key events")
   398  	}
   399  }
   400  
   401  func assertKeyEventUnexpected(t *testing.T, events []event.Event) {
   402  	t.Helper()
   403  	var evtFocus int
   404  	for _, e := range events {
   405  		switch e.(type) {
   406  		case key.FocusEvent:
   407  			evtFocus++
   408  		}
   409  	}
   410  	if evtFocus > 1 {
   411  		t.Errorf("unexpected focus event")
   412  	}
   413  }
   414  
   415  func assertFocus(t *testing.T, router *Router, expected event.Tag) {
   416  	t.Helper()
   417  	if got := router.key.queue.focus; got != expected {
   418  		t.Errorf("expected %v to be focused, got %v", expected, got)
   419  	}
   420  }
   421  
   422  func assertKeyboard(t *testing.T, router *Router, expected TextInputState) {
   423  	t.Helper()
   424  	if got := router.key.queue.state; got != expected {
   425  		t.Errorf("expected %v keyboard, got %v", expected, got)
   426  	}
   427  }