Cthulhu is no longer a fork. Now it is a screen reader written in Golang.
This commit is contained in:
1 parent
a998fe67de
commit
069fc674dc
708 files changed
+112482
-532
No files matched your search
@@ -0,0 +1,131 @@
|
||||
package atspi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"math"
|
||||
)
|
||||
|
||||
var ErrNoDocumentCaret = errors.New("document caret line unavailable")
|
||||
|
||||
type DocumentContent struct {
|
||||
Reference ObjectReference
|
||||
Snapshot Snapshot
|
||||
Start int
|
||||
End int
|
||||
Text string
|
||||
RawText string
|
||||
// Separator is display spacing before this object, outside its native range.
|
||||
Separator string
|
||||
Bounds Rectangle
|
||||
}
|
||||
|
||||
type DocumentLine struct {
|
||||
Context []Snapshot
|
||||
Document ObjectReference
|
||||
Contents []DocumentContent
|
||||
}
|
||||
|
||||
func (tree *AccessibleTree) DocumentLineAtOffset(ctx context.Context, reference ObjectReference, offset int) (DocumentLine, error) {
|
||||
document, err := tree.Document(ctx, reference)
|
||||
if err != nil {
|
||||
return DocumentLine{}, err
|
||||
}
|
||||
|
||||
caretIndex := -1
|
||||
if reference.Valid() {
|
||||
for index, node := range document.Nodes {
|
||||
if node.Reference == reference {
|
||||
caretIndex = index
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if caretIndex < 0 {
|
||||
for index, node := range document.Nodes {
|
||||
if node.Text.CaretKnown {
|
||||
caretIndex = index
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if caretIndex < 0 {
|
||||
return DocumentLine{}, ErrNoDocumentCaret
|
||||
}
|
||||
|
||||
caretNode := document.Nodes[caretIndex]
|
||||
if offset < 0 {
|
||||
offset = caretNode.Text.CaretOffset
|
||||
}
|
||||
if offset < 0 {
|
||||
return DocumentLine{}, ErrNoDocumentCaret
|
||||
}
|
||||
|
||||
caretRange, found := textRangeAtOffset(caretNode.Text, offset)
|
||||
if !found || !caretRange.Bounds.Valid() {
|
||||
return DocumentLine{}, ErrNoDocumentCaret
|
||||
}
|
||||
|
||||
ancestors := document.Context.Ancestors
|
||||
if len(ancestors) > 0 && ancestors[0].ID() == reference.ID() {
|
||||
ancestors = ancestors[1:]
|
||||
}
|
||||
line := DocumentLine{Document: document.Context.TopLevel, Context: ancestors}
|
||||
for _, node := range document.Nodes {
|
||||
if node.Hidden {
|
||||
continue
|
||||
}
|
||||
for _, textRange := range node.Text.Ranges {
|
||||
if !boundsAreOnSameLine(caretRange.Bounds, textRange.Bounds) {
|
||||
continue
|
||||
}
|
||||
if textRange.Text == "" {
|
||||
continue
|
||||
}
|
||||
line.Contents = append(line.Contents, DocumentContent{
|
||||
Reference: node.Reference,
|
||||
Snapshot: node.Snapshot,
|
||||
Start: textRange.Start,
|
||||
End: textRange.End,
|
||||
Text: textRange.Text,
|
||||
RawText: textRange.Text,
|
||||
Bounds: textRange.Bounds,
|
||||
})
|
||||
}
|
||||
}
|
||||
if len(line.Contents) == 0 {
|
||||
return DocumentLine{}, ErrNoDocumentCaret
|
||||
}
|
||||
return line, nil
|
||||
}
|
||||
|
||||
func textRangeAtOffset(text VisibleText, offset int) (TextRange, bool) {
|
||||
for _, textRange := range text.Ranges {
|
||||
if textRange.Start <= offset && offset < textRange.End {
|
||||
return textRange, true
|
||||
}
|
||||
}
|
||||
for _, textRange := range text.Ranges {
|
||||
if textRange.Start <= offset && offset <= textRange.End {
|
||||
return textRange, true
|
||||
}
|
||||
}
|
||||
return TextRange{}, false
|
||||
}
|
||||
|
||||
func boundsAreOnSameLine(first, second Rectangle) bool {
|
||||
if first == second {
|
||||
return true
|
||||
}
|
||||
if !first.Valid() || !second.Valid() {
|
||||
return false
|
||||
}
|
||||
top := max(first.Y, second.Y)
|
||||
bottom := min(first.Y+first.Height, second.Y+second.Height)
|
||||
if bottom <= top {
|
||||
return false
|
||||
}
|
||||
firstCenter := first.Y + first.Height/2
|
||||
secondCenter := second.Y + second.Height/2
|
||||
return math.Abs(float64(firstCenter-secondCenter)) <= 5
|
||||
}
|
||||
Reference in new issue
Block a user