Add a description column to the reading settings table under speech.
This commit is contained in:
1 parent
ffcea2efea
commit
41283a5d8d
7 files changed
+219
-46
No files matched your search
@@ -64,6 +64,7 @@ func flatReviewCommand(command input.Command) (review.FlatUnit, int) {
|
||||
|
||||
func presentFlatReviewResult(logger *slog.Logger, speech speechEnqueuer, result review.Result) {
|
||||
text := ""
|
||||
boundary := false
|
||||
if result.Err != nil {
|
||||
logger.Warn("flat review failed", "error", result.Err)
|
||||
switch {
|
||||
@@ -75,16 +76,24 @@ func presentFlatReviewResult(logger *slog.Logger, speech speechEnqueuer, result
|
||||
text = "Could not find current location."
|
||||
}
|
||||
} else if !result.Moved && (result.FlatStep != 0 || result.FlatUnit == review.WordAbove || result.FlatUnit == review.WordBelow) {
|
||||
// Python's sequential review stops silently at window boundaries.
|
||||
return
|
||||
if result.FlatUnit != review.Line {
|
||||
return
|
||||
}
|
||||
boundary = true
|
||||
if result.FlatStep < 0 {
|
||||
text = "Top of screen."
|
||||
} else {
|
||||
text = "Bottom of screen."
|
||||
}
|
||||
} else {
|
||||
text = presentation.FlatReviewText(result.FlatPosition.Text, result.FlatUnit == review.Character || result.FlatUnit == review.LineEnd)
|
||||
}
|
||||
request := speechRequest{Text: text, Role: "text", Cause: "flat-review", Character: result.Err == nil && (result.FlatUnit == review.Character || result.FlatUnit == review.LineEnd)}
|
||||
if result.Err == nil && result.FlatUnit == review.Line {
|
||||
hasContent := result.Err == nil && !boundary
|
||||
request := speechRequest{Text: text, Role: "text", Cause: "flat-review", Character: hasContent && (result.FlatUnit == review.Character || result.FlatUnit == review.LineEnd)}
|
||||
if hasContent && result.FlatUnit == review.Line {
|
||||
request.Line = &speechLine{Object: result.FlatPosition.Object, Raw: result.FlatPosition.Text}
|
||||
}
|
||||
if result.Err == nil {
|
||||
if hasContent {
|
||||
set_review_spelling(&request, result.FlatPosition.Text, result.FlatSpeech)
|
||||
}
|
||||
speech.Enqueue(request)
|
||||
|
||||
@@ -76,6 +76,14 @@ func TestProcessEventsPresentsFlatReviewThroughSpeechQueue(t *testing.T) {
|
||||
expect("Review field, text")
|
||||
worker.ReadFlat(review.Line, 0)
|
||||
expect("a 猫!oo")
|
||||
worker.ReadFlat(review.Line, -1)
|
||||
expect("Top of screen.")
|
||||
worker.ReadFlat(review.Line, 0)
|
||||
expect("a 猫!oo")
|
||||
worker.ReadFlat(review.Line, 1)
|
||||
expect("Bottom of screen.")
|
||||
worker.ReadFlat(review.Line, 0)
|
||||
expect("a 猫!oo")
|
||||
worker.ReadFlat(review.Character, 1)
|
||||
expect("space")
|
||||
worker.ReadFlat(review.Character, 1)
|
||||
@@ -122,11 +130,47 @@ func TestFlatReviewResultUsesNativeFocusAndRejectsOldPage(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestFlatReviewLineBoundaryFeedback(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
step int
|
||||
want string
|
||||
}{
|
||||
{"top", -1, "Top of screen."},
|
||||
{"bottom", 1, "Bottom of screen."},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
queue := &autocompleteRequestQueue{requests: make(chan speechRequest, 1)}
|
||||
result := review.Result{
|
||||
FlatUnit: review.Line, FlatStep: tc.step, FlatSpeech: review.FlatSpell,
|
||||
FlatPosition: review.FlatPosition{Text: " Edge line"},
|
||||
}
|
||||
for range 2 {
|
||||
presentFlatReviewResult(slog.New(slog.DiscardHandler), queue, result)
|
||||
select {
|
||||
case request := <-queue.requests:
|
||||
if request.Text != tc.want || request.Cause != "flat-review" {
|
||||
t.Fatalf("boundary request = %+v", request)
|
||||
}
|
||||
if request.Line != nil || request.Character || len(request.Spelling) != 0 {
|
||||
t.Fatalf("boundary retained content speech metadata: %+v", request)
|
||||
}
|
||||
default:
|
||||
t.Fatal("boundary was silent")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFlatReviewErrorsAndSilentBoundary(t *testing.T) {
|
||||
queue := &fakeSpeechQueue{spoken: make(chan string, 2)}
|
||||
logger := slog.New(slog.DiscardHandler)
|
||||
for _, result := range []review.Result{
|
||||
{FlatUnit: review.Line, FlatStep: 1},
|
||||
{FlatUnit: review.Word, FlatStep: -1},
|
||||
{FlatUnit: review.Word, FlatStep: 1},
|
||||
{FlatUnit: review.Character, FlatStep: -1},
|
||||
{FlatUnit: review.Character, FlatStep: 1},
|
||||
{FlatUnit: review.WordAbove},
|
||||
{FlatUnit: review.WordBelow},
|
||||
} {
|
||||
|
||||
@@ -79,17 +79,25 @@ func TestGUITableNormalizedFocusRetainsOwnerAndTitle(t *testing.T) {
|
||||
other.Source.BusName = ":1.99"
|
||||
adapter.events <- other
|
||||
expect_speech(second, 6, "checked, Break speech into chunks between pauses")
|
||||
// A third description cell participates in full-row speech, while
|
||||
// horizontal movement reads just that cell.
|
||||
description := "Reads numbers one digit at a time."
|
||||
rowText := "unchecked, Speak numbers as digits, " + description
|
||||
third := cell_event("/reading/2/state", "unchecked", rowText, 2, 0, atspi.SelectionChangedEvent)
|
||||
expect_speech(third, 7, rowText)
|
||||
descriptionCell := cell_event("/reading/2/description", description, rowText, 2, 2, atspi.ActiveDescendantEvent)
|
||||
expect_speech(descriptionCell, 8, description)
|
||||
button := atspi.NewSnapshot("/preferences/apply", "Apply", "button", "", "")
|
||||
button.BusName = window.BusName
|
||||
focus := atspi.NewEvent(atspi.FocusEvent, button)
|
||||
focus.Window = window
|
||||
expect_speech(focus, 7, "Apply, button")
|
||||
expect_speech(focus, 9, "Apply, button")
|
||||
adapter.events <- up // A table selection after leaving the table is noise.
|
||||
// Returning through a cell focus event establishes ownership and speaks
|
||||
// the table name again, without changing the user's row-reading option.
|
||||
first.OriginPath = first.Source.Path
|
||||
expect_speech(first, 8, "Reading settings, unchecked, Only speak displayed text")
|
||||
expect_speech(second, 9, "checked, Break speech into chunks between pauses")
|
||||
expect_speech(first, 10, "Reading settings, unchecked, Only speak displayed text")
|
||||
expect_speech(second, 11, "checked, Break speech into chunks between pauses")
|
||||
cancel()
|
||||
<-completed
|
||||
}
|
||||
@@ -4,46 +4,80 @@
|
||||
#include "native_internal.h"
|
||||
|
||||
static GtkListStore *reading_store;
|
||||
static const struct { const char *id; const char *label; } reading_rows[] = {
|
||||
{"only_speak_displayed_text", "Only speak displayed text"},
|
||||
{"automatically_read_title_changes", "Automatically read title changes"},
|
||||
{"automatically_read_clipboard_changes", "Automatically read clipboard changes"},
|
||||
{"phonetic_character_hints", "Phonetic character hints"},
|
||||
{"enable_pause_breaks", "Break speech into chunks between pauses"},
|
||||
{"speak_numbers_as_digits", "Speak numbers as digits"},
|
||||
{"speak_multicase_strings_as_words", "Speak multicase strings as words"},
|
||||
{"enable_indentation", "Enable indentation presentation"},
|
||||
{"speak_blank_lines", "Speak blank lines"},
|
||||
{"enable_mnemonic_speaking", "Speak object mnemonics"},
|
||||
{"enable_position_speaking", "Speak child position"},
|
||||
{"enable_tutorial_messages", "Speak tutorial messages"},
|
||||
{"speak_description", "Speak object descriptions"},
|
||||
{"messages_are_detailed", "System messages are detailed"},
|
||||
{"use_color_names", "Speak colors as names"},
|
||||
{"speak_misspelled_indicator", "Speak misspelled-word indicator"},
|
||||
{"speak_context_blockquote", "Announce block quotes during navigation"},
|
||||
{"speak_context_list", "Announce lists during navigation"},
|
||||
{"speak_context_table", "Announce tables during navigation"},
|
||||
{"speak_context_panel", "Announce panels during navigation"},
|
||||
{"speak_context_landmark", "Announce landmarks during navigation"},
|
||||
{"speak_context_non_landmark_form", "Announce forms during navigation"},
|
||||
{"read_full_row_in_gui_table", "Speak full row in GUI tables"},
|
||||
{"read_full_row_in_document_table", "Speak full row in document tables"},
|
||||
{"read_full_row_in_spreadsheet", "Speak full row in spreadsheets"},
|
||||
{"present_tooltips", "Present tooltips"},
|
||||
{"enable_mouse_review", "Speak object under mouse"},
|
||||
{"say_all_context_blockquote", "Announce block quotes in Say All"},
|
||||
{"say_all_context_list", "Announce lists in Say All"},
|
||||
{"say_all_context_table", "Announce tables in Say All"},
|
||||
{"say_all_context_panel", "Announce panels in Say All"},
|
||||
{"say_all_context_non_landmark_form", "Announce forms in Say All"},
|
||||
{"say_all_context_landmark", "Announce landmarks in Say All"},
|
||||
static const struct { const char *id; const char *label; const char *description; } reading_rows[] = {
|
||||
{"only_speak_displayed_text", "Only speak displayed text",
|
||||
"Limits reading to displayed names, text and values. Omits extra information such as control types, states and container announcements."},
|
||||
{"automatically_read_title_changes", "Automatically read title changes",
|
||||
"Announces changes to the active window's title, such as a new page title in a browser."},
|
||||
{"automatically_read_clipboard_changes", "Automatically read clipboard changes",
|
||||
"Automatically reads new text when the clipboard contents change, such as after copying text."},
|
||||
{"phonetic_character_hints", "Phonetic character hints",
|
||||
"When navigating characters with Left or Right, adds a phonetic name after a 300 ms pause, for example, a then alpha. Further input cancels the hint."},
|
||||
{"enable_pause_breaks", "Break speech into chunks between pauses",
|
||||
"Separates parts of an announcement with pauses, such as between a control's name, type and state."},
|
||||
{"speak_numbers_as_digits", "Speak numbers as digits",
|
||||
"Reads numbers one digit at a time, for example, 123 as one two three."},
|
||||
{"speak_multicase_strings_as_words", "Speak multicase strings as words",
|
||||
"Separates words where a lowercase letter is followed by an uppercase letter, for example, saveFile as save File."},
|
||||
{"enable_indentation", "Enable indentation presentation",
|
||||
"Reports changes in a line's leading spaces and tabs using the selected indentation presentation mode."},
|
||||
{"speak_blank_lines", "Speak blank lines",
|
||||
"Says blank when navigating to an empty line, instead of leaving it silent."},
|
||||
{"enable_mnemonic_speaking", "Speak object mnemonics",
|
||||
"Announces a control's access key when the application provides one, such as Alt+O for an Open button."},
|
||||
{"enable_position_speaking", "Speak child position",
|
||||
"Announces an item's position within a group when available, for example, 2 of 5."},
|
||||
{"enable_tutorial_messages", "Speak tutorial messages",
|
||||
"Adds brief instructions for using the focused control, such as Press Space to toggle for a checkbox."},
|
||||
{"speak_description", "Speak object descriptions",
|
||||
"Reads additional descriptive text supplied by the application for a control or window."},
|
||||
{"messages_are_detailed", "System messages are detailed",
|
||||
"Reads a notification's full message when available. When disabled, reads just its summary when one is provided."},
|
||||
{"use_color_names", "Speak colors as names",
|
||||
"Uses color names when reporting text colors, instead of their numeric color values."},
|
||||
{"speak_misspelled_indicator", "Speak misspelled-word indicator",
|
||||
"Says misspelled when reading text that the application's spell checker has marked as incorrect."},
|
||||
{"speak_context_blockquote", "Announce block quotes during navigation",
|
||||
"Announces when you enter or leave a block quote while navigating text."},
|
||||
{"speak_context_list", "Announce lists during navigation",
|
||||
"Announces when you enter or leave a list while navigating."},
|
||||
{"speak_context_table", "Announce tables during navigation",
|
||||
"Announces when you enter or leave a data table while navigating."},
|
||||
{"speak_context_panel", "Announce panels during navigation",
|
||||
"Announces when you enter or leave a panel or group of controls while navigating."},
|
||||
{"speak_context_landmark", "Announce landmarks during navigation",
|
||||
"Announces when you enter or leave a landmark, such as a page's main content or navigation region."},
|
||||
{"speak_context_non_landmark_form", "Announce forms during navigation",
|
||||
"Announces when you enter or leave a form that is not identified as a landmark."},
|
||||
{"read_full_row_in_gui_table", "Speak full row in GUI tables",
|
||||
"Reads all cells in a row when moving between rows in application tables, including this settings table. Left and Right read individual cells."},
|
||||
{"read_full_row_in_document_table", "Speak full row in document tables",
|
||||
"Reads the whole row when navigating rows in a document or web page table. Moving between columns reads individual cells."},
|
||||
{"read_full_row_in_spreadsheet", "Speak full row in spreadsheets",
|
||||
"Reads the whole row when navigating rows in a spreadsheet. Moving between columns reads individual cells."},
|
||||
{"present_tooltips", "Present tooltips",
|
||||
"Reads tooltip text when the application displays it."},
|
||||
{"enable_mouse_review", "Speak object under mouse",
|
||||
"Reads the object under the mouse pointer as you move the mouse."},
|
||||
{"say_all_context_blockquote", "Announce block quotes in Say All",
|
||||
"Announces entry into and exit from block quotes during continuous reading with Say All. This is separate from navigation announcements."},
|
||||
{"say_all_context_list", "Announce lists in Say All",
|
||||
"Announces entry into and exit from lists during continuous reading with Say All. This is separate from navigation announcements."},
|
||||
{"say_all_context_table", "Announce tables in Say All",
|
||||
"Announces entry into and exit from data tables during continuous reading with Say All. This is separate from navigation announcements."},
|
||||
{"say_all_context_panel", "Announce panels in Say All",
|
||||
"Announces entry into and exit from panels or groups of controls during continuous reading with Say All. This is separate from navigation announcements."},
|
||||
{"say_all_context_non_landmark_form", "Announce forms in Say All",
|
||||
"Announces entry into and exit from forms that are not landmarks during continuous reading with Say All. This is separate from navigation announcements."},
|
||||
{"say_all_context_landmark", "Announce landmarks in Say All",
|
||||
"Announces entry into and exit from landmarks, such as main content or navigation regions, during continuous reading with Say All. This is separate from navigation announcements."},
|
||||
};
|
||||
|
||||
enum {
|
||||
READING_ID,
|
||||
READING_LABEL,
|
||||
READING_ENABLED,
|
||||
READING_DESCRIPTION,
|
||||
READING_N_COLUMNS
|
||||
};
|
||||
static void name_reading_toggle_cells(void);
|
||||
@@ -94,7 +128,7 @@ static void name_reading_toggle_cells(void) {
|
||||
}
|
||||
|
||||
void prefs_init_reading_settings(void) {
|
||||
reading_store = gtk_list_store_new(READING_N_COLUMNS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN);
|
||||
reading_store = gtk_list_store_new(READING_N_COLUMNS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_STRING);
|
||||
for (unsigned int i = 0; i < G_N_ELEMENTS(reading_rows); i++) {
|
||||
GtkTreeIter iter;
|
||||
gtk_list_store_append(reading_store, &iter);
|
||||
@@ -102,6 +136,7 @@ void prefs_init_reading_settings(void) {
|
||||
READING_ID, reading_rows[i].id,
|
||||
READING_LABEL, reading_rows[i].label,
|
||||
READING_ENABLED, FALSE,
|
||||
READING_DESCRIPTION, reading_rows[i].description,
|
||||
-1);
|
||||
}
|
||||
|
||||
@@ -118,6 +153,12 @@ void prefs_init_reading_settings(void) {
|
||||
"Enabled", toggle_renderer, "active", READING_ENABLED, NULL);
|
||||
gtk_tree_view_append_column(view, enabled_column);
|
||||
gtk_tree_view_append_column(view, setting_column);
|
||||
GtkCellRenderer *descriptionRenderer = gtk_cell_renderer_text_new();
|
||||
g_object_set(descriptionRenderer, "wrap-mode", PANGO_WRAP_WORD_CHAR, "wrap-width", 280, NULL);
|
||||
GtkTreeViewColumn *descriptionColumn = gtk_tree_view_column_new_with_attributes(
|
||||
"Description", descriptionRenderer, "text", READING_DESCRIPTION, NULL);
|
||||
gtk_tree_view_column_set_expand(descriptionColumn, TRUE);
|
||||
gtk_tree_view_append_column(view, descriptionColumn);
|
||||
g_signal_connect(view, "key-press-event", G_CALLBACK(reading_key_press), NULL);
|
||||
name_reading_toggle_cells();
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user