github.com/utopiagio/gio@v0.0.8/io/input/key_test.go (about) 1 // SPDX-License-Identifier: Unlicense OR MIT 2 3 package input 4 5 import ( 6 "image" 7 "testing" 8 9 "github.com/utopiagio/gio/f32" 10 "github.com/utopiagio/gio/io/event" 11 "github.com/utopiagio/gio/io/key" 12 "github.com/utopiagio/gio/io/pointer" 13 "github.com/utopiagio/gio/op" 14 "github.com/utopiagio/gio/op/clip" 15 ) 16 17 func TestAllMatchKeyFilter(t *testing.T) { 18 r := new(Router) 19 r.Event(key.Filter{}) 20 ke := key.Event{Name: "A"} 21 r.Queue(ke) 22 // Catch-all gets all non-system events. 23 assertEventSequence(t, events(r, -1, key.Filter{}), ke) 24 25 r = new(Router) 26 r.Event(key.Filter{Name: "A"}) 27 r.Queue(SystemEvent{ke}) 28 if _, handled := r.WakeupTime(); !handled { 29 t.Errorf("system event was unexpectedly ignored") 30 } 31 // Only specific filters match system events. 32 assertEventSequence(t, events(r, -1, key.Filter{Name: "A"}), ke) 33 } 34 35 func TestInputHint(t *testing.T) { 36 r := new(Router) 37 if hint, changed := r.TextInputHint(); hint != key.HintAny || changed { 38 t.Fatal("unexpected hint") 39 } 40 ops := new(op.Ops) 41 h := new(int) 42 key.InputHintOp{Tag: h, Hint: key.HintEmail}.Add(ops) 43 r.Frame(ops) 44 if hint, changed := r.TextInputHint(); hint != key.HintAny || changed { 45 t.Fatal("unexpected hint") 46 } 47 r.Source().Execute(key.FocusCmd{Tag: h}) 48 if hint, changed := r.TextInputHint(); hint != key.HintEmail || !changed { 49 t.Fatal("unexpected hint") 50 } 51 } 52 53 func TestDeferred(t *testing.T) { 54 r := new(Router) 55 h := new(int) 56 f := []event.Filter{ 57 key.FocusFilter{Target: h}, 58 key.Filter{Name: "A"}, 59 } 60 // Provoke deferring by exhausting events for h. 61 events(r, -1, f...) 62 r.Source().Execute(key.FocusCmd{Tag: h}) 63 ke := key.Event{Name: "A"} 64 r.Queue(ke) 65 // All events are deferred at this point. 66 assertEventSequence(t, events(r, -1, f...)) 67 r.Frame(new(op.Ops)) 68 // But delivered after a frame. 69 assertEventSequence(t, events(r, -1, f...), key.FocusEvent{Focus: true}, ke) 70 } 71 72 func TestInputWakeup(t *testing.T) { 73 handler := new(int) 74 var ops op.Ops 75 // InputOps shouldn't trigger redraws. 76 event.Op(&ops, handler) 77 78 var r Router 79 // Reset events shouldn't either. 80 evts := events(&r, -1, key.FocusFilter{Target: new(int)}, key.Filter{Name: "A"}) 81 assertEventSequence(t, evts, key.FocusEvent{Focus: false}) 82 r.Frame(&ops) 83 if _, wake := r.WakeupTime(); wake { 84 t.Errorf("InputOp or the resetting FocusEvent triggered a wakeup") 85 } 86 // And neither does events that don't match anything. 87 r.Queue(key.SnippetEvent{}) 88 if _, handled := r.WakeupTime(); handled { 89 t.Errorf("a not-matching event triggered a wakeup") 90 } 91 // However, events that does match should trigger wakeup. 92 r.Queue(key.Event{Name: "A"}) 93 if _, handled := r.WakeupTime(); !handled { 94 t.Errorf("a key.Event didn't trigger redraw") 95 } 96 } 97 98 func TestKeyMultiples(t *testing.T) { 99 handlers := make([]int, 3) 100 r := new(Router) 101 r.Source().Execute(key.SoftKeyboardCmd{Show: true}) 102 for i := range handlers { 103 assertEventSequence(t, events(r, 1, key.FocusFilter{Target: &handlers[i]}), key.FocusEvent{Focus: false}) 104 } 105 r.Source().Execute(key.FocusCmd{Tag: &handlers[2]}) 106 assertEventSequence(t, events(r, -1, key.FocusFilter{Target: &handlers[2]}), key.FocusEvent{Focus: true}) 107 assertFocus(t, r, &handlers[2]) 108 109 assertKeyboard(t, r, TextInputOpen) 110 } 111 112 func TestKeySoftKeyboardNoFocus(t *testing.T) { 113 r := new(Router) 114 115 // It's possible to open the keyboard 116 // without any active focus: 117 r.Source().Execute(key.SoftKeyboardCmd{Show: true}) 118 119 assertFocus(t, r, nil) 120 assertKeyboard(t, r, TextInputOpen) 121 } 122 123 func TestKeyRemoveFocus(t *testing.T) { 124 handlers := make([]int, 2) 125 r := new(Router) 126 127 filters := func(h event.Tag) []event.Filter { 128 return []event.Filter{ 129 key.FocusFilter{Target: h}, 130 key.Filter{Focus: h, Name: key.NameTab, Required: key.ModShortcut}, 131 } 132 } 133 var all []event.Filter 134 for i := range handlers { 135 all = append(all, filters(&handlers[i])...) 136 } 137 assertEventSequence(t, events(r, len(handlers), all...), key.FocusEvent{}, key.FocusEvent{}) 138 r.Source().Execute(key.FocusCmd{Tag: &handlers[0]}) 139 r.Source().Execute(key.SoftKeyboardCmd{Show: true}) 140 141 evt := key.Event{Name: key.NameTab, Modifiers: key.ModShortcut, State: key.Press} 142 r.Queue(evt) 143 144 assertEventSequence(t, events(r, 2, filters(&handlers[0])...), key.FocusEvent{Focus: true}, evt) 145 assertFocus(t, r, &handlers[0]) 146 assertKeyboard(t, r, TextInputOpen) 147 148 // Frame removes focus from tags that don't filter for focus events nor mentioned in an InputOp. 149 r.Source().Execute(key.FocusCmd{Tag: new(int)}) 150 r.Frame(new(op.Ops)) 151 152 assertEventSequence(t, events(r, -1, filters(&handlers[1])...)) 153 assertFocus(t, r, nil) 154 assertKeyboard(t, r, TextInputClose) 155 156 // Set focus to InputOp which already 157 // exists in the previous frame: 158 r.Source().Execute(key.FocusCmd{Tag: &handlers[0]}) 159 assertFocus(t, r, &handlers[0]) 160 } 161 162 func TestKeyFocusedInvisible(t *testing.T) { 163 handlers := make([]int, 2) 164 ops := new(op.Ops) 165 r := new(Router) 166 167 for i := range handlers { 168 assertEventSequence(t, events(r, 1, key.FocusFilter{Target: &handlers[i]}), key.FocusEvent{Focus: false}) 169 } 170 171 // Set new InputOp with focus: 172 r.Source().Execute(key.FocusCmd{Tag: &handlers[0]}) 173 r.Source().Execute(key.SoftKeyboardCmd{Show: true}) 174 175 assertEventSequence(t, events(r, 1, key.FocusFilter{Target: &handlers[0]}), key.FocusEvent{Focus: true}) 176 assertFocus(t, r, &handlers[0]) 177 assertKeyboard(t, r, TextInputOpen) 178 179 // Frame will clear the focus because the handler is not visible. 180 r.Frame(ops) 181 182 for i := range handlers { 183 assertEventSequence(t, events(r, -1, key.FocusFilter{Target: &handlers[i]})) 184 } 185 assertFocus(t, r, nil) 186 assertKeyboard(t, r, TextInputClose) 187 188 r.Frame(ops) 189 r.Frame(ops) 190 191 ops.Reset() 192 193 // Respawn the first element: 194 // It must receive one `Event{Focus: false}`. 195 event.Op(ops, &handlers[0]) 196 197 assertEventSequence(t, events(r, -1, key.FocusFilter{Target: &handlers[0]}), key.FocusEvent{Focus: false}) 198 } 199 200 func TestNoOps(t *testing.T) { 201 r := new(Router) 202 r.Frame(nil) 203 } 204 205 func TestDirectionalFocus(t *testing.T) { 206 ops := new(op.Ops) 207 r := new(Router) 208 handlers := []image.Rectangle{ 209 image.Rect(10, 10, 50, 50), 210 image.Rect(50, 20, 100, 80), 211 image.Rect(20, 26, 60, 80), 212 image.Rect(10, 60, 50, 100), 213 } 214 215 for i, bounds := range handlers { 216 cl := clip.Rect(bounds).Push(ops) 217 event.Op(ops, &handlers[i]) 218 cl.Pop() 219 events(r, -1, key.FocusFilter{Target: &handlers[i]}) 220 } 221 r.Frame(ops) 222 223 r.MoveFocus(key.FocusLeft) 224 assertFocus(t, r, &handlers[0]) 225 r.MoveFocus(key.FocusLeft) 226 assertFocus(t, r, &handlers[0]) 227 r.MoveFocus(key.FocusRight) 228 assertFocus(t, r, &handlers[1]) 229 r.MoveFocus(key.FocusRight) 230 assertFocus(t, r, &handlers[1]) 231 r.MoveFocus(key.FocusDown) 232 assertFocus(t, r, &handlers[2]) 233 r.MoveFocus(key.FocusDown) 234 assertFocus(t, r, &handlers[2]) 235 r.MoveFocus(key.FocusLeft) 236 assertFocus(t, r, &handlers[3]) 237 r.MoveFocus(key.FocusUp) 238 assertFocus(t, r, &handlers[0]) 239 240 r.MoveFocus(key.FocusForward) 241 assertFocus(t, r, &handlers[1]) 242 r.MoveFocus(key.FocusBackward) 243 assertFocus(t, r, &handlers[0]) 244 } 245 246 func TestFocusScroll(t *testing.T) { 247 ops := new(op.Ops) 248 r := new(Router) 249 h := new(int) 250 251 filters := []event.Filter{ 252 key.FocusFilter{Target: h}, 253 pointer.Filter{ 254 Target: h, 255 Kinds: pointer.Scroll, 256 ScrollBounds: image.Rect(-100, -100, 100, 100), 257 }, 258 } 259 events(r, -1, filters...) 260 parent := clip.Rect(image.Rect(1, 1, 14, 39)).Push(ops) 261 cl := clip.Rect(image.Rect(10, -20, 20, 30)).Push(ops) 262 event.Op(ops, h) 263 // Test that h is scrolled even if behind another handler. 264 event.Op(ops, new(int)) 265 cl.Pop() 266 parent.Pop() 267 r.Frame(ops) 268 269 r.MoveFocus(key.FocusLeft) 270 r.RevealFocus(image.Rect(0, 0, 15, 40)) 271 evts := events(r, -1, filters...) 272 assertScrollEvent(t, evts[len(evts)-1], f32.Pt(6, -9)) 273 } 274 275 func TestFocusClick(t *testing.T) { 276 ops := new(op.Ops) 277 r := new(Router) 278 h := new(int) 279 280 filters := []event.Filter{ 281 key.FocusFilter{Target: h}, 282 pointer.Filter{ 283 Target: h, 284 Kinds: pointer.Press | pointer.Release | pointer.Cancel, 285 }, 286 } 287 assertEventPointerTypeSequence(t, events(r, -1, filters...), pointer.Cancel) 288 cl := clip.Rect(image.Rect(0, 0, 10, 10)).Push(ops) 289 event.Op(ops, h) 290 cl.Pop() 291 r.Frame(ops) 292 293 r.MoveFocus(key.FocusLeft) 294 r.ClickFocus() 295 296 assertEventPointerTypeSequence(t, events(r, -1, filters...), pointer.Press, pointer.Release) 297 } 298 299 func TestNoFocus(t *testing.T) { 300 r := new(Router) 301 r.MoveFocus(key.FocusForward) 302 } 303 304 func TestKeyRouting(t *testing.T) { 305 r := new(Router) 306 h := new(int) 307 A, B := key.Event{Name: "A"}, key.Event{Name: "B"} 308 // Register filters. 309 events(r, -1, key.Filter{Name: "A"}, key.Filter{Name: "B"}) 310 r.Frame(new(op.Ops)) 311 r.Queue(A, B) 312 // The handler is not focused, so only B is delivered. 313 assertEventSequence(t, events(r, -1, key.Filter{Focus: h, Name: "A"}, key.Filter{Name: "B"}), B) 314 r.Source().Execute(key.FocusCmd{Tag: h}) 315 // A is delivered to the focused handler. 316 assertEventSequence(t, events(r, -1, key.Filter{Focus: h, Name: "A"}, key.Filter{Name: "B"}), A) 317 } 318 319 func assertFocus(t *testing.T, router *Router, expected event.Tag) { 320 t.Helper() 321 if !router.Source().Focused(expected) { 322 t.Errorf("expected %v to be focused", expected) 323 } 324 } 325 326 func assertKeyboard(t *testing.T, router *Router, expected TextInputState) { 327 t.Helper() 328 if got := router.state().state; got != expected { 329 t.Errorf("expected %v keyboard, got %v", expected, got) 330 } 331 }