128 lines
3.7 KiB
Go
128 lines
3.7 KiB
Go
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
|
|
}
|