// Copyright (c) 2026 Storm Dragon // SPDX-License-Identifier: GPL-3.0-or-later package reader import ( "git.stormux.org/storm/fenrir/internal/i18n" "git.stormux.org/storm/fenrir/internal/presentation" "git.stormux.org/storm/fenrir/internal/screen" "strings" "time" ) func (r *Reader) Configure_attributes(mode, format string) { // Configuration is the saved/quick-menu choice. Reapplying unchanged // settings must not overwrite the shortcut's temporary session state. if mode != r.attributeConfigured { r.attributeConfigured, r.attributeMode = mode, mode if mode != "" && mode != "none" { r.attributeResumeMode = mode } } r.attributeFormat = format r.review.Configure_attributes(format) } func (r *Reader) toggle_attributes() string { if r.attributeMode == "" || r.attributeMode == "none" { r.attributeMode = r.attributeResumeMode if r.attributeMode == "" { r.attributeMode = "beeps" } return i18n.T("announcement of attributes enabled") } r.attributeResumeMode, r.attributeMode = r.attributeMode, "none" return i18n.T("announcement of attributes disabled") } func (r *Reader) attribute_feedback(announce bool) *presentation.Message { attribute, valid := r.review.Attribute() if !valid { r.attributeKnown = false return nil } changed := r.attributeKnown && attribute != r.lastAttribute r.lastAttribute, r.attributeKnown = attribute, true if !announce || !changed || r.attributeMode == "" || r.attributeMode == "none" { return nil } // Presence tones describe emphasis; returning to defaults is described by // speech modes, but must not play a misleading has-attribute cue. if attribute.Default() && r.attributeMode == "beeps" { return nil } message := presentation.Attribute_message(attribute, r.attributeMode, r.attributeFormat) if attribute.Default() { message.Cue = "" } if message.Cue == "" && strings.TrimSpace(message.Text) == "" { return nil } return &message } func (r *Reader) automatic_attributes(previous, next screen.Snapshot, switched bool) *presentation.Message { if switched { r.attributeKnown = false } announce := !switched && next.Active && r.automatic && r.cursorEcho && r.trackingMode == "cursor" && !r.review.Active() && r.region_state().allows(next.X, next.Y, next.Cols, true, true) if announce && (previous.X != next.X || previous.Y != next.Y) { announce = !r.applicationInputAt.IsZero() && time.Since(r.applicationInputAt) < 300*time.Millisecond } return r.attribute_feedback(announce) }