For speech rate in settings up/down move by 10, left/right move by 1. Easier to get settings right without having to hold doown the arrow keys lol.

This commit is contained in:
Storm Dragon committed 2026-10-28 15:44:41 -04:00
1 parent ea5d26f56d
commit 088f706cb4
13 files changed
+81 -10

No files matched your search

+1
View File
@@ -144,6 +144,7 @@ Space toggles checkboxes; arrows change selections and numeric values. Activate
The menu includes speech, sound, reading, keyboard, clipboard, and other
settings. Speech rate, pitch, and volume use a scale from -100 to 100.
For speech rate, Up and Down change the value by 10; Left and Right change it by 1.
Backend, device, keyboard layout, sound theme, and other launch-only changes
take effect the next time Fenrir starts.
+1
View File
@@ -239,6 +239,7 @@ Activate \f[B]OK\f[R] to save, or \f[B]Cancel\f[R] or Escape to discard
the form\(cqs changes.
.PP
Speech rate, pitch, and volume use a scale from \-100 to 100.
For speech rate, Up and Down change the value by 10; Left and Right change it by 1.
Sound volume is between 0.0 and 1.0.
Backend, device, keyboard layout, sound theme, and other launch\-only
settings take effect on the next start.
+2 -1
View File
@@ -164,7 +164,8 @@ options. The form captures its navigation keys until it closes.
* Activate *OK* to save, or *Cancel* or Escape to discard the form’s
changes.
Speech rate, pitch, and volume use a scale from -100 to 100. Sound
Speech rate, pitch, and volume use a scale from -100 to 100. For speech
rate, Up and Down change the value by 10; Left and Right change it by 1. Sound
volume is between 0.0 and 1.0. Backend, device, keyboard layout, sound
theme, and other launch-only settings take effect on the next start.
+2 -1
View File
@@ -233,7 +233,8 @@ The form captures its navigation keys until it closes.
- Arrows change list selections and numeric values.
- Activate **OK** to save, or **Cancel** or Escape to discard the form's changes.
Speech rate, pitch, and volume use a scale from -100 to 100. Sound volume is
Speech rate, pitch, and volume use a scale from -100 to 100. For speech rate,
Up and Down change the value by 10; Left and Right change it by 1. Sound volume is
between 0.0 and 1.0. Backend, device, keyboard layout, sound theme, and other
launch-only settings take effect on the next start.
+2 -1
View File
@@ -156,7 +156,8 @@ options. The form captures its navigation keys until it closes.
- Activate OK to save, or Cancel or Escape to discard the form’s
changes.
Speech rate, pitch, and volume use a scale from -100 to 100. Sound
Speech rate, pitch, and volume use a scale from -100 to 100. For speech
rate, Up and Down change the value by 10; Left and Right change it by 1. Sound
volume is between 0.0 and 1.0. Backend, device, keyboard layout, sound
theme, and other launch-only settings take effect on the next start.
+1 -1
View File
@@ -95,7 +95,7 @@ func TestSettingsMenuCancelSaveAndReopen(t *testing.T) {
settings, _, err := config.Load(path, nil)
want := 40
if action == "save" {
want = 41
want = 50
}
if err != nil || settings.Presentation.Speech.Rate != want {
t.Fatal("wrong persistence", settings, err)
+1 -1
View File
@@ -20,7 +20,7 @@ func admit_menu_save(t *testing.T, a *application) {
receive_ui(t, a.ui)
a.ui.handle(interaction.Key{Name: "tab"})
a.ui.handle(interaction.Key{Name: "tab"})
a.ui.handle(interaction.Key{Name: "up"}) // Rate 24 -> 25.
a.ui.handle(interaction.Key{Name: "right"}) // Rate 24 -> 25.
a.ui.action("save", a.ui.session)
if !a.saver.busy || a.ui.session != nil {
t.Fatal("menu save was not admitted")
+1
View File
@@ -37,6 +37,7 @@ type Control struct {
Checked bool
Number float64
Min, Max, Step float64
UpDownStep float64 // Zero uses the default step of 1 for Up and Down.
Text string
Items []string
Selected int
+10 -2
View File
@@ -54,6 +54,14 @@ func New_session(form Form) (*Session, error) {
return nil, fmt.Errorf("invalid control %q", control.ID)
}
controlIDs[control.ID] = true
if control.Kind == Number {
if control.UpDownStep == 0 {
control.UpDownStep = 1
}
if math.IsNaN(control.UpDownStep) || math.IsInf(control.UpDownStep, 0) || control.UpDownStep < 0 {
return nil, fmt.Errorf("invalid vertical step %q", control.ID)
}
}
if control.Kind == Number && (math.IsNaN(control.Number) || math.IsInf(control.Number, 0) || math.IsNaN(control.Min) || math.IsInf(control.Min, 0) || math.IsNaN(control.Max) || math.IsInf(control.Max, 0) || math.IsNaN(control.Step) || math.IsInf(control.Step, 0) || control.Min > control.Max || control.Step <= 0 || control.Number < control.Min || control.Number > control.Max) {
return nil, fmt.Errorf("invalid range %q", control.ID)
}
@@ -244,9 +252,9 @@ func (s *Session) Handle(key Key) Result {
delta := 0.0
switch key.Name {
case "up":
delta = 1
delta = control.UpDownStep
case "down":
delta = -1
delta = -control.UpDownStep
case "right":
delta = control.Step
case "left":
+14 -1
View File
@@ -17,7 +17,20 @@
package interaction
import "testing"
import (
"math"
"testing"
)
func TestInvalidNumberUpDownStepRejected(t *testing.T) {
for _, step := range []float64{-1, math.NaN(), math.Inf(1), math.Inf(-1)} {
form := test_form()
form.Tabs[0].Controls[1].UpDownStep = step
if _, err := New_session(form); err == nil {
t.Fatalf("invalid Up/Down step %v accepted", step)
}
}
}
func test_form() Form {
return Form{Type: TabForm, Title: "Settings", Tabs: []Tab{
+1 -1
View File
@@ -28,7 +28,7 @@ func TestApplicationSettingsOnlyExposeRestorableControls(t *testing.T) {
session.Handle(interaction.Key{Name: "tab"})
session.Handle(interaction.Key{Name: "up"})
saved, err := Application_settings(session, base)
if err != nil || saved.Presentation.Speech.Rate != base.Presentation.Speech.Rate+1 || saved.Keyboard.Layout != base.Keyboard.Layout {
if err != nil || saved.Presentation.Speech.Rate != base.Presentation.Speech.Rate+10 || saved.Keyboard.Layout != base.Keyboard.Layout {
t.Fatal(saved, err)
}
}
+3
View File
@@ -132,6 +132,9 @@ func make_control(definition config.Definition, settings config.Settings, discov
control.Number = value.(float64)
}
control.Min, control.Max, control.Step = definition.Min, definition.Max, definition.Step
if definition.Path == "presentation.speech.rate" {
control.UpDownStep = 10
}
case reflect.String:
control.Kind, control.Text = interaction.Entry, value.(string)
case reflect.Slice:
+42 -1
View File
@@ -19,6 +19,7 @@ package preferences
import (
"reflect"
"strconv"
"strings"
"testing"
@@ -52,11 +53,51 @@ func TestSettingsFormCoversSchemaAndReturnsIsolatedDraft(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if draft.Presentation.Speech.Enabled || draft.Presentation.Speech.Rate != 1 || !base.Presentation.Speech.Enabled || base.Presentation.Speech.Rate != 0 {
if draft.Presentation.Speech.Enabled || draft.Presentation.Speech.Rate != 10 || !base.Presentation.Speech.Enabled || base.Presentation.Speech.Rate != 0 {
t.Fatal("draft isolation", draft, base)
}
}
func TestSpeechRateArrowSteps(t *testing.T) {
for _, application := range []bool{false, true} {
for _, trial := range []struct {
start int
key string
want int
}{
{-3, "up", 7}, {-3, "down", -13},
{-3, "right", -2}, {-3, "left", -4},
{96, "up", 100}, {-96, "down", -100},
{100, "up", 100}, {-100, "down", -100},
{100, "right", 100}, {-100, "left", -100},
{100, "down", 90}, {-100, "up", -90},
{100, "left", 99}, {-100, "right", -99},
} {
base := config.Defaults()
base.Presentation.Speech.Rate = trial.start
session, err := new_session(base, []string{"presentation"}, MenuTitle, application)
if err != nil {
t.Fatal(err)
}
session.Handle(interaction.Key{Name: "tab"}) // Speech enabled.
session.Handle(interaction.Key{Name: "tab"}) // Rate.
result := session.Handle(interaction.Key{Name: trial.key})
if want := "Rate " + strconv.Itoa(trial.want); result.Speech != want {
t.Fatalf("application=%v, rate=%d, key=%s: speech=%q, want %q", application, trial.start, trial.key, result.Speech, want)
}
var draft config.Settings
if application {
draft, err = Application_settings(session, base)
} else {
draft, err = Settings(session, base)
}
if err != nil || draft.Presentation.Speech.Rate != trial.want || base.Presentation.Speech.Rate != trial.start {
t.Fatalf("application=%v, trial=%+v: draft rate=%d, base rate=%d, err=%v", application, trial, draft.Presentation.Speech.Rate, base.Presentation.Speech.Rate, err)
}
}
}
}
func TestReadingModeIsConstrainedAndSavedInDraft(t *testing.T) {
base := config.Defaults()
session, err := New_session(base, []string{"reading"})