mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-01 23:55:39 +00:00
cd3b4561cf
The existing implementation cannot remove the focus of some widget, doesn't have an option to focus without display the on-screen keyboard and it automatically focuses the first InputOp, aggressively. That change aims to make possible: remove focus from any widget. Add focus without displaying the on-screen-keyboard/soft keyboard. Don't automatically focus any widget. Don't recover focus when the widget is visible again. Fixes gio#180. Signed-off-by: Inkeliz <inkeliz@inkeliz.com>
312 lines
7.0 KiB
Go
312 lines
7.0 KiB
Go
// SPDX-License-Identifier: Unlicense OR MIT
|
|
|
|
package router
|
|
|
|
import (
|
|
"reflect"
|
|
"testing"
|
|
|
|
"gioui.org/io/event"
|
|
"gioui.org/io/key"
|
|
"gioui.org/op"
|
|
)
|
|
|
|
func TestKeyMultiples(t *testing.T) {
|
|
handlers := make([]int, 3)
|
|
ops := new(op.Ops)
|
|
r := new(Router)
|
|
|
|
key.SoftKeyboardOp{Show: true}.Add(ops)
|
|
key.InputOp{Tag: &handlers[0]}.Add(ops)
|
|
key.FocusOp{Focus: true}.Add(ops)
|
|
key.InputOp{Tag: &handlers[1]}.Add(ops)
|
|
|
|
// The last one must be focused:
|
|
key.InputOp{Tag: &handlers[2]}.Add(ops)
|
|
|
|
r.Frame(ops)
|
|
|
|
assertKeyEvent(t, r.Events(&handlers[0]), false)
|
|
assertKeyEvent(t, r.Events(&handlers[1]), false)
|
|
assertKeyEvent(t, r.Events(&handlers[2]), true)
|
|
assertFocus(t, r, &handlers[2])
|
|
assertKeyboard(t, r, TextInputOpen)
|
|
}
|
|
|
|
func TestKeyStacked(t *testing.T) {
|
|
handlers := make([]int, 4)
|
|
ops := new(op.Ops)
|
|
r := new(Router)
|
|
|
|
s := op.Push(ops)
|
|
key.InputOp{Tag: &handlers[0]}.Add(ops)
|
|
// FocusOp must not overwrite the
|
|
// FocusOp{Focus: true}.
|
|
key.FocusOp{Focus: false}.Add(ops)
|
|
s.Pop()
|
|
s = op.Push(ops)
|
|
key.SoftKeyboardOp{Show: false}.Add(ops)
|
|
key.InputOp{Tag: &handlers[1]}.Add(ops)
|
|
key.FocusOp{Focus: true}.Add(ops)
|
|
s.Pop()
|
|
s = op.Push(ops)
|
|
key.InputOp{Tag: &handlers[2]}.Add(ops)
|
|
// SoftwareKeyboardOp will open the keyboard,
|
|
// overwriting `SoftKeyboardOp{Show: false}`.
|
|
key.SoftKeyboardOp{Show: true}.Add(ops)
|
|
s.Pop()
|
|
s = op.Push(ops)
|
|
key.SoftKeyboardOp{Show: false}.Add(ops)
|
|
key.InputOp{Tag: &handlers[3]}.Add(ops)
|
|
// FocusOp must not overwrite the
|
|
// FocusOp{Focus: true}.
|
|
key.FocusOp{Focus: false}.Add(ops)
|
|
s.Pop()
|
|
|
|
r.Frame(ops)
|
|
|
|
assertKeyEvent(t, r.Events(&handlers[0]), false)
|
|
assertKeyEvent(t, r.Events(&handlers[1]), true)
|
|
assertKeyEvent(t, r.Events(&handlers[2]), false)
|
|
assertKeyEvent(t, r.Events(&handlers[3]), false)
|
|
assertFocus(t, r, &handlers[1])
|
|
assertKeyboard(t, r, TextInputOpen)
|
|
}
|
|
|
|
func TestKeySoftKeyboardNoFocus(t *testing.T) {
|
|
ops := new(op.Ops)
|
|
r := new(Router)
|
|
|
|
// It's possible to open the keyboard
|
|
// without any active focus:
|
|
key.SoftKeyboardOp{Show: true}.Add(ops)
|
|
|
|
r.Frame(ops)
|
|
|
|
assertFocus(t, r, nil)
|
|
assertKeyboard(t, r, TextInputOpen)
|
|
}
|
|
|
|
func TestKeyRemoveFocus(t *testing.T) {
|
|
handlers := make([]int, 2)
|
|
ops := new(op.Ops)
|
|
r := new(Router)
|
|
|
|
// New InputOp with Focus and Keyboard:
|
|
s := op.Push(ops)
|
|
key.InputOp{Tag: &handlers[0]}.Add(ops)
|
|
key.FocusOp{Focus: true}.Add(ops)
|
|
key.SoftKeyboardOp{Show: true}.Add(ops)
|
|
s.Pop()
|
|
|
|
// New InputOp without any focus:
|
|
s = op.Push(ops)
|
|
key.InputOp{Tag: &handlers[1]}.Add(ops)
|
|
s.Pop()
|
|
|
|
r.Frame(ops)
|
|
|
|
// Add some key events:
|
|
event := event.Event(key.Event{Name: key.NameTab, Modifiers: key.ModShortcut, State: key.Press})
|
|
r.Add(event)
|
|
|
|
assertKeyEvent(t, r.Events(&handlers[0]), true, event)
|
|
assertKeyEvent(t, r.Events(&handlers[1]), false)
|
|
assertFocus(t, r, &handlers[0])
|
|
assertKeyboard(t, r, TextInputOpen)
|
|
|
|
ops.Reset()
|
|
|
|
// Will get the focus removed:
|
|
s = op.Push(ops)
|
|
key.InputOp{Tag: &handlers[0]}.Add(ops)
|
|
s.Pop()
|
|
|
|
// Unchanged:
|
|
s = op.Push(ops)
|
|
key.InputOp{Tag: &handlers[1]}.Add(ops)
|
|
s.Pop()
|
|
|
|
// Removing any Focus:
|
|
s = op.Push(ops)
|
|
key.FocusOp{Focus: false}.Add(ops)
|
|
s.Pop()
|
|
|
|
r.Frame(ops)
|
|
|
|
assertKeyEvent(t, r.Events(&handlers[0]), false)
|
|
assertKeyEventUnexpected(t, r.Events(&handlers[1]))
|
|
assertFocus(t, r, nil)
|
|
assertKeyboard(t, r, TextInputClose)
|
|
|
|
ops.Reset()
|
|
|
|
s = op.Push(ops)
|
|
key.InputOp{Tag: &handlers[0]}.Add(ops)
|
|
s.Pop()
|
|
|
|
// Setting Focus without InputOp:
|
|
s = op.Push(ops)
|
|
key.FocusOp{Focus: true}.Add(ops)
|
|
s.Pop()
|
|
|
|
s = op.Push(ops)
|
|
key.InputOp{Tag: &handlers[1]}.Add(ops)
|
|
s.Pop()
|
|
|
|
r.Frame(ops)
|
|
|
|
assertKeyEventUnexpected(t, r.Events(&handlers[0]))
|
|
assertKeyEventUnexpected(t, r.Events(&handlers[1]))
|
|
assertFocus(t, r, nil)
|
|
assertKeyboard(t, r, TextInputKeep)
|
|
|
|
ops.Reset()
|
|
|
|
// Set focus to InputOp which already
|
|
// exists in the previous frame:
|
|
s = op.Push(ops)
|
|
key.FocusOp{Focus: true}.Add(ops)
|
|
key.InputOp{Tag: &handlers[0]}.Add(ops)
|
|
key.SoftKeyboardOp{Show: true}.Add(ops)
|
|
s.Pop()
|
|
|
|
// Tries to remove focus:
|
|
// It must not overwrite the previous `FocusOp`.
|
|
s = op.Push(ops)
|
|
key.InputOp{Tag: &handlers[1]}.Add(ops)
|
|
key.FocusOp{Focus: false}.Add(ops)
|
|
s.Pop()
|
|
|
|
r.Frame(ops)
|
|
|
|
assertKeyEvent(t, r.Events(&handlers[0]), true)
|
|
assertKeyEventUnexpected(t, r.Events(&handlers[1]))
|
|
assertFocus(t, r, &handlers[0])
|
|
assertKeyboard(t, r, TextInputOpen)
|
|
}
|
|
|
|
func TestKeyFocusedInvisible(t *testing.T) {
|
|
handlers := make([]int, 2)
|
|
ops := new(op.Ops)
|
|
r := new(Router)
|
|
|
|
// Set new InputOp with focus:
|
|
s := op.Push(ops)
|
|
key.FocusOp{Focus: true}.Add(ops)
|
|
key.InputOp{Tag: &handlers[0]}.Add(ops)
|
|
key.SoftKeyboardOp{Show: true}.Add(ops)
|
|
s.Pop()
|
|
|
|
// Set new InputOp without focus:
|
|
s = op.Push(ops)
|
|
key.InputOp{Tag: &handlers[1]}.Add(ops)
|
|
s.Pop()
|
|
|
|
r.Frame(ops)
|
|
|
|
assertKeyEvent(t, r.Events(&handlers[0]), true)
|
|
assertKeyEvent(t, r.Events(&handlers[1]), false)
|
|
assertFocus(t, r, &handlers[0])
|
|
assertKeyboard(t, r, TextInputOpen)
|
|
|
|
ops.Reset()
|
|
|
|
//
|
|
// Removed first (focused) element!
|
|
//
|
|
|
|
// Unchanged:
|
|
s = op.Push(ops)
|
|
key.InputOp{Tag: &handlers[1]}.Add(ops)
|
|
s.Pop()
|
|
|
|
r.Frame(ops)
|
|
|
|
assertKeyEventUnexpected(t, r.Events(&handlers[0]))
|
|
assertKeyEventUnexpected(t, r.Events(&handlers[1]))
|
|
assertFocus(t, r, nil)
|
|
assertKeyboard(t, r, TextInputClose)
|
|
|
|
ops.Reset()
|
|
|
|
// Respawn the first element:
|
|
// It must receive one `Event{Focus: false}`.
|
|
s = op.Push(ops)
|
|
key.InputOp{Tag: &handlers[0]}.Add(ops)
|
|
s.Pop()
|
|
|
|
// Unchanged
|
|
s = op.Push(ops)
|
|
key.InputOp{Tag: &handlers[1]}.Add(ops)
|
|
s.Pop()
|
|
|
|
r.Frame(ops)
|
|
|
|
assertKeyEvent(t, r.Events(&handlers[0]), false)
|
|
assertKeyEventUnexpected(t, r.Events(&handlers[1]))
|
|
assertFocus(t, r, nil)
|
|
assertKeyboard(t, r, TextInputKeep)
|
|
|
|
}
|
|
|
|
func assertKeyEvent(t *testing.T, events []event.Event, expected bool, expectedInputs ...event.Event) {
|
|
t.Helper()
|
|
var evtFocus int
|
|
var evtKeyPress int
|
|
for _, e := range events {
|
|
switch ev := e.(type) {
|
|
case key.FocusEvent:
|
|
if ev.Focus != expected {
|
|
t.Errorf("focus is expected to be %v, got %v", expected, ev.Focus)
|
|
}
|
|
evtFocus++
|
|
case key.Event, key.EditEvent:
|
|
if len(expectedInputs) <= evtKeyPress {
|
|
t.Errorf("unexpected key events")
|
|
}
|
|
if !reflect.DeepEqual(ev, expectedInputs[evtKeyPress]) {
|
|
t.Errorf("expected %v events, got %v", expectedInputs[evtKeyPress], ev)
|
|
}
|
|
evtKeyPress++
|
|
}
|
|
}
|
|
if evtFocus <= 0 {
|
|
t.Errorf("expected focus event")
|
|
}
|
|
if evtFocus > 1 {
|
|
t.Errorf("expected single focus event")
|
|
}
|
|
if evtKeyPress != len(expectedInputs) {
|
|
t.Errorf("expected key events")
|
|
}
|
|
}
|
|
|
|
func assertKeyEventUnexpected(t *testing.T, events []event.Event) {
|
|
t.Helper()
|
|
var evtFocus int
|
|
for _, e := range events {
|
|
switch e.(type) {
|
|
case key.FocusEvent:
|
|
evtFocus++
|
|
}
|
|
}
|
|
if evtFocus > 1 {
|
|
t.Errorf("unexpected focus event")
|
|
}
|
|
}
|
|
|
|
func assertFocus(t *testing.T, router *Router, expected event.Tag) {
|
|
t.Helper()
|
|
if router.kqueue.focus != expected {
|
|
t.Errorf("expected %v to be focused, got %v", expected, router.kqueue.focus)
|
|
}
|
|
}
|
|
|
|
func assertKeyboard(t *testing.T, router *Router, expected TextInputState) {
|
|
t.Helper()
|
|
if router.kqueue.state != expected {
|
|
t.Errorf("expected %v keyboard, got %v", expected, router.kqueue.state)
|
|
}
|
|
}
|