Files

90 lines
2.2 KiB
Go

// Copyright (c) 2026 Storm Dragon
// Authors: Storm Dragon, Angus Kola
// SPDX-License-Identifier: GPL-3.0-or-later
package app
import (
"fmt"
"strings"
"git.stormux.org/storm/cthulhu/internal/atspi"
"git.stormux.org/storm/cthulhu/internal/sound"
)
type speechLine struct {
Object atspi.ObjectID
Raw string
}
type indentationData struct {
signature string
columns int
}
type indentationHistory map[atspi.ObjectID]indentationData
func indentation_data(line string) indentationData {
var data indentationData
for _, value := range line {
switch value {
case ' ', '\u00a0':
data.columns++
value = ' '
case '\t':
data.columns += 4 - data.columns%4
default:
return data
}
data.signature += string(value)
}
return data
}
func (worker *speechWorker) line_text(text string, line *speechLine) string {
if line == nil || worker.reading == nil {
return text
}
data := indentation_data(line.Raw)
previous, known := worker.indentation[line.Object]
if worker.indentation == nil || len(worker.indentation) >= 128 && !known {
worker.indentation = make(indentationHistory)
}
worker.indentation[line.Object] = data
settings := worker.reading()
if !settings.EnableIndentation || settings.OnlySpeakDisplayedText || known && previous.signature == data.signature {
return text
}
mode := settings.IndentationPresentationMode
if (mode == "beeps" || mode == "both") && worker.indentationCue != nil {
frequency := min(2700, 200+data.columns*30)
if data.columns > previous.columns {
frequency += 50
} else if data.columns < previous.columns {
frequency = max(100, frequency-50)
}
worker.indentationCue(sound.Cue{Name: sound.IndentationCueName(frequency)})
}
if mode == "beeps" {
return text
}
word := "columns"
if data.columns == 1 {
word = "column"
}
return strings.TrimSuffix(fmt.Sprintf("indentation %d %s, %s", data.columns, word, text), ", ")
}
func document_speech_line(line atspi.DocumentLine) *speechLine {
if len(line.Contents) == 0 {
return nil
}
content := line.Contents[0]
if content.Snapshot.Role == "password text" {
return nil
}
raw := content.RawText
if raw == "" {
raw = content.Text
}
return &speechLine{Object: content.Snapshot.ID(), Raw: raw}
}