Files
gio/io/router/semantic_test.go
T
Elias Naur 8a90074d04 io/semantic: add package for adding semantic descriptions to UI components
Software such as screen readers require semantic descriptions of user
interfaces to effectively present and interact with them. Package
semantic, combined with the existing package clip provide the operations
for Gio programs to describe themselves.

This change implements the semantic package and the routing changes for
accessing semantic trees; follow-ups add semantic information to widgets
and implement mapping semantic tree to platform representations.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2021-12-01 17:23:54 +01:00

121 lines
2.7 KiB
Go

// SPDX-License-Identifier: Unlicense OR MIT
package router
import (
"fmt"
"image"
"reflect"
"testing"
"gioui.org/f32"
"gioui.org/io/pointer"
"gioui.org/io/semantic"
"gioui.org/op"
"gioui.org/op/clip"
)
func TestSemanticTree(t *testing.T) {
var (
ops op.Ops
r Router
)
t1 := clip.Rect(image.Rect(0, 0, 75, 75)).Push(&ops)
semantic.DescriptionOp("child1").Add(&ops)
t1.Pop()
t2 := clip.Rect(image.Rect(25, 25, 100, 100)).Push(&ops)
semantic.DescriptionOp("child2").Add(&ops)
t2.Pop()
r.Frame(&ops)
tests := []struct {
x, y float32
desc string
}{
{24, 24, "child1"},
{50, 50, "child2"},
{100, 100, ""},
}
tree := r.AppendSemantics(nil)
verifyTree(t, 0, tree[0])
for _, test := range tests {
p := f32.Pt(test.x, test.y)
id, found := r.SemanticAt(p)
if !found {
t.Errorf("no semantic node at %v", p)
}
n, found := lookupNode(tree, id)
if !found {
t.Errorf("no id %d in semantic tree", id)
}
if got := n.Desc.Description; got != test.desc {
t.Errorf("got semantic description %s at %v, expected %s", got, p, test.desc)
}
}
// Verify stable IDs.
r.Frame(&ops)
tree2 := r.AppendSemantics(nil)
if !reflect.DeepEqual(tree, tree2) {
fmt.Println("First tree:")
printTree(0, tree[0])
fmt.Println("Second tree:")
printTree(0, tree2[0])
t.Error("same semantic description lead to differing trees")
}
}
func TestSemanticDescription(t *testing.T) {
var ops op.Ops
pointer.InputOp{Tag: new(int), Types: pointer.Press | pointer.Release}.Add(&ops)
semantic.DescriptionOp("description").Add(&ops)
semantic.LabelOp("label").Add(&ops)
semantic.Button.Add(&ops)
semantic.DisabledOp(true).Add(&ops)
semantic.SelectedOp(true).Add(&ops)
var r Router
r.Frame(&ops)
tree := r.AppendSemantics(nil)
got := tree[0].Desc
exp := SemanticDesc{
Class: 1,
Description: "description",
Label: "label",
Selected: true,
Disabled: true,
Gestures: ClickGesture,
Bounds: f32.Rectangle{Min: f32.Point{X: -1e+06, Y: -1e+06}, Max: f32.Point{X: 1e+06, Y: 1e+06}},
}
if got != exp {
t.Errorf("semantic description mismatch:\nGot: %+v\nWant: %+v", got, exp)
}
}
func lookupNode(tree []SemanticNode, id SemanticID) (SemanticNode, bool) {
for _, n := range tree {
if id == n.ID {
return n, true
}
}
return SemanticNode{}, false
}
func verifyTree(t *testing.T, parent SemanticID, n SemanticNode) {
t.Helper()
if n.ParentID != parent {
t.Errorf("node %d: got parent %d, want %d", n.ID, n.ParentID, parent)
}
for _, c := range n.Children {
verifyTree(t, n.ID, c)
}
}
func printTree(indent int, n SemanticNode) {
for i := 0; i < indent; i++ {
fmt.Print("\t")
}
fmt.Printf("%d: %+v\n", n.ID, n.Desc)
for _, c := range n.Children {
printTree(indent+1, c)
}
}