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,127 @@
|
||||
package presentation
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"git.stormux.org/storm/cthulhu/internal/atspi"
|
||||
"git.stormux.org/storm/cthulhu/internal/sound"
|
||||
)
|
||||
|
||||
// DocumentLineText mirrors the web script's content presentation: join the
|
||||
// displayed text for each same-line object and speak common role/state details.
|
||||
func DocumentLineText(line atspi.DocumentLine) string {
|
||||
return DocumentLineTextWithOptions(line, DefaultOptions())
|
||||
}
|
||||
|
||||
func DocumentLineTextWithOptions(line atspi.DocumentLine, options Options) string {
|
||||
return DocumentLineSpeechWithOptions(line, options).Text
|
||||
}
|
||||
|
||||
func DocumentLineSpeechWithOptions(line atspi.DocumentLine, options Options) Speech {
|
||||
reading := reading_options(options)
|
||||
var parts []string
|
||||
seen := make(map[atspi.ObjectID]bool)
|
||||
type rowKey struct {
|
||||
bus, path string
|
||||
row int
|
||||
}
|
||||
rows := make(map[rowKey]bool)
|
||||
for _, content := range line.Contents {
|
||||
text := strings.TrimSpace(content.Text)
|
||||
if content.Snapshot.Role == "table cell" && content.Snapshot.TableRowText != "" && FullRowEnabled(content.Snapshot, options) {
|
||||
key := rowKey{content.Snapshot.BusName, content.Snapshot.TablePath, content.Snapshot.TableRowIndex}
|
||||
if key.path != "" {
|
||||
if rows[key] {
|
||||
continue
|
||||
}
|
||||
rows[key] = true
|
||||
}
|
||||
text = content.Snapshot.TableRowText
|
||||
}
|
||||
if text == "" {
|
||||
text = strings.TrimSpace(content.Snapshot.Name)
|
||||
}
|
||||
appendPart(&parts, text)
|
||||
if reading.OnlySpeakDisplayedText {
|
||||
continue
|
||||
}
|
||||
id := content.Snapshot.ID()
|
||||
if seen[id] {
|
||||
continue
|
||||
}
|
||||
_, roleCue := roleSoundCue(content.Snapshot, options)
|
||||
_, stateCue := stateSoundCue(content.Snapshot, options)
|
||||
role := lineRole(content.Snapshot)
|
||||
if options.SpeechVerbosityLevel == 0 && (role == "icon" || role == "canvas") {
|
||||
role = ""
|
||||
}
|
||||
if role != "" && !((roleCue || stateCue) && options.RoleSoundPresentation == "sound_only") {
|
||||
appendPart(&parts, role)
|
||||
}
|
||||
states := controlStates(content.Snapshot)
|
||||
if stateCue && options.RoleSoundPresentation == "sound_only" {
|
||||
states = removeReplacedState(states)
|
||||
}
|
||||
for _, state := range states {
|
||||
appendPart(&parts, state)
|
||||
}
|
||||
appendPart(&parts, controlValue(content.Snapshot))
|
||||
if reading.SpeakMisspelledIndicator && content.Snapshot.Misspelled {
|
||||
appendPart(&parts, "misspelled")
|
||||
}
|
||||
seen[id] = true
|
||||
}
|
||||
if len(parts) == 0 {
|
||||
if !options.SpeakBlankLines {
|
||||
return Speech{}
|
||||
}
|
||||
return Speech{Text: "blank"}
|
||||
}
|
||||
return SpeechWithOptions(parts, options)
|
||||
}
|
||||
|
||||
// A text object can contribute several ranges to a visual line. Present its
|
||||
// role and state sounds once, in document order.
|
||||
func DocumentLineSoundCues(line atspi.DocumentLine, options Options) []sound.Cue {
|
||||
var cues []sound.Cue
|
||||
seen := make(map[atspi.ObjectID]bool)
|
||||
for _, content := range line.Contents {
|
||||
id := content.Snapshot.ID()
|
||||
if !seen[id] {
|
||||
cues = append(cues, SoundCues(content.Snapshot, options)...)
|
||||
seen[id] = true
|
||||
}
|
||||
}
|
||||
return cues
|
||||
}
|
||||
|
||||
func appendPart(parts *[]string, part string) {
|
||||
if part == "" {
|
||||
return
|
||||
}
|
||||
*parts = append(*parts, part)
|
||||
}
|
||||
|
||||
// Keep this deliberately small: line reading presents displayed text plus
|
||||
// the role/state details Python's web generator keeps for common content.
|
||||
func lineRole(snapshot atspi.Snapshot) string {
|
||||
switch snapshot.Role {
|
||||
case "heading":
|
||||
if snapshot.HeadingLevel > 0 {
|
||||
return fmt.Sprintf("heading level %d", snapshot.HeadingLevel)
|
||||
}
|
||||
return "heading"
|
||||
case "link":
|
||||
if snapshot.Visited {
|
||||
return "visited link"
|
||||
}
|
||||
return "link"
|
||||
case "footer", "form", "label", "menu item", "paragraph", "section",
|
||||
"redundant object", "unknown", "table cell", "text", "static",
|
||||
"content suggestion", "list item", "list", "document frame",
|
||||
"document web", "alert", "row header", "toggle button":
|
||||
return ""
|
||||
}
|
||||
return snapshot.Role
|
||||
}
|
||||
Reference in new issue
Block a user