Add multi routing gesture and select with routing command#20028
Draft
LeonarddeR wants to merge 22 commits intonvaccess:masterfrom
Draft
Add multi routing gesture and select with routing command#20028LeonarddeR wants to merge 22 commits intonvaccess:masterfrom
LeonarddeR wants to merge 22 commits intonvaccess:masterfrom
Conversation
…ccess#20001) `braille.BrailleDisplayGesture.routingIndex` (single int) was too narrow: "routing" excluded non-routing cell-addressed gestures (e.g. Handy Tech Active Tactile Control), and simultaneous routing key presses had unspecified behavior (last index won). Core API (source/braille.py): - New `cellIndexes: list[int]` canonical attribute for any cell-addressed gesture (routing, touch, ATC, ...). - `BrailleDisplayGesture.idForCellCount(n)` helper returning `"routing"` for <=1 cell, `"multiRouting"` for >1. - `_get_routingIndex`/`_set_routingIndex` deprecation shim using the `AutoPropertyObject` pattern: getter returns first cell, setter wraps into a single-element list. Warning gated on `NVDAState._allowDeprecatedAPI()`. Scripts (source/globalCommands.py): - `script_braille_routeTo` and `script_braille_reportFormatting` now read `cellIndexes[0]`. - New `script_braille_selectToCell` (unassigned by default) selects text between first and last pressed routing cells when bound to a `multiRouting` gesture. Driver migration (source/brailleDisplayDrivers/*): all 22 drivers migrated from `self.routingIndex = X` to `self.cellIndexes = [X]`. Multi-routing emission added where the protocol already reports simultaneous presses: - baum: iterate bitmap of routing keys - handyTech: aggregate routing key codes from pressed-key set - albatross: aggregate primary routing range indexes - nlseReaderZoomax: iterate routing keys bitmap - seikantk: emit single gesture with sorted indexes instead of one gesture per key Remote compat (source/_remoteClient/*): - Sender serializes `cellIndexes` plus legacy `routingIndex` (first cell) for old peers. - Receiver normalizes legacy `routingIndex` into `cellIndexes` before attribute assignment, avoiding a deprecation warning. brailleViewer: `brailleViewerInputGesture.py` sets `cellIndexes`. Tests (tests/unit/test_braille/test_brailleDisplayDrivers.py): added coverage for default `cellIndexes`, `idForCellCount`, deprecated `routingIndex` getter/setter round-trip, and `multiRouting` identifier regex validity. Docs (user_docs/en/changes.md): new-feature entry for multi-routing selection, developer change for `cellIndexes`/`multiRouting`/helper, deprecation entry for `routingIndex`.
- cellIndexes default is now None instead of empty list, matching the previous routingIndex = None convention. - Replace #: style comments with docstrings for ID_ROUTING, ID_MULTI_ROUTING, and cellIndexes. - Update deprecated routingIndex setter to return None (not []). - Update tests to expect None default and None on clear. Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
- idForCellCount now accepts an optional baseName parameter (default "routing"). For count > 1, prepends "multi" with the first character uppercased: "routing" → "multiRouting", "secondRouting" → "multiSecondRouting", "route" → "multiRoute". - Albatross driver: collect all routing ranges into a dict keyed by range name, then merge into a flat cellIndexes list preserving duplicates for cross-row simultaneous presses. - Alva driver: same aggregation pattern for "routing" and "secondRouting" ranges, replacing the previous overwrite behavior. - Added test_idForCellCount_custom_baseName covering secondRouting, route, and upperRouting base names. Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
These class constants on BrailleDisplayGesture are superseded by the idForCellCount helper which dynamically builds the gesture id for any routing range name. Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
…orCellCount - brailliantB: aggregate routing keys into list, support multi-press - hidBrailleStandard: collect routing keys with prefix, support multi-press producing routerSet1_multiRouterKey for simultaneous presses - Switch all drivers from braille.BrailleDisplayGesture.idForCellCount to self.idForCellCount for consistency Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
The end position of the selection no longer includes the character at the last routing key, making the selection range exclusive. Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
Make the deprecated routingIndex getter and the remote client legacy field use max(cellIndexes) consistently, so single-cell behavior is preserved while multi-cell gestures resolve to the highest index. Also guard script_braille_selectToCell against cellIndexes being None. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Restore backwards compatibility for callers that pass a single int to InputGestureRouting, normalizing it to a one-element list. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Caller (_handleRouting) already passes a fresh sorted list, and the single-int compat path constructs a new list. No need to copy again. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds default bindings for the new braille_selectToCell command on drivers that emit a multiRouting gesture: ALVA, Albatross, Baum, Brailliant B, Handy Tech, HID Braille, NLS eReader Zoomax, and Seika Notetaker. Updates the user-facing changelog to list these drivers explicitly. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace 'braille cells' with 'braille routing keys' in the script description, error message and changelog entry to match the actual input concept. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Allows subclasses to override the helper if they need a different naming convention while still letting drivers call self.idForCellCount. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Previously, when NVDAState._allowDeprecatedAPI() returned False, the routingIndex getter and setter still returned/applied the value and only suppressed the log message. That defeated the purpose of the flag, which is meant to prove a code path is free of deprecated API usage. Now raise AttributeError instead, mirroring the pattern used elsewhere (e.g. addonHandler._pickleToJsonMigration). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Pull request overview
This PR enhances NVDA’s braille input model to support gestures that address multiple braille cells at once (e.g., multiple routing keys pressed simultaneously), and adds a new built-in command to select text directly from the braille display using multi-routing.
Changes:
- Added
cellIndexes(list of addressed cell indexes) tobraille.BrailleDisplayGesture, deprecatedroutingIndex, and introducedBrailleDisplayGesture.idForCellCount(...)for consistent gesture ids. - Updated in-tree braille display drivers (and Braille Viewer) to populate
cellIndexesand emitmultiRouting(and related) ids where appropriate; boundmultiRoutingto the new selection command on supported drivers. - Added
script_braille_selectToCelland propagatedcellIndexesacross NVDA Remote while retaining legacyroutingIndexcompatibility.
Reviewed changes
Copilot reviewed 28 out of 28 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| user_docs/en/changes.md | Documents the new multi-routing gesture, selection command, and the cellIndexes API/deprecation. |
| tests/unit/test_braille/test_brailleDisplayDrivers.py | Adds unit tests for cellIndexes, idForCellCount, and the routingIndex compatibility shim. |
| source/globalCommands.py | Updates routing/formatting scripts to use cellIndexes; adds braille_selectToCell command. |
| source/brailleViewer/brailleViewerInputGesture.py | Migrates Braille Viewer routing gesture to cellIndexes. |
| source/brailleDisplayDrivers/seikantk.py | Aggregates routing indexes into cellIndexes, emits multi ids, and binds multiRouting to selection. |
| source/brailleDisplayDrivers/seika.py | Migrates routing gesture to cellIndexes. |
| source/brailleDisplayDrivers/papenmeier_serial.py | Migrates routing handling to cellIndexes while preserving existing ids. |
| source/brailleDisplayDrivers/papenmeier.py | Migrates routing gesture to cellIndexes. |
| source/brailleDisplayDrivers/nlseReaderZoomax.py | Collects multiple routing keys into cellIndexes, uses idForCellCount, binds selection. |
| source/brailleDisplayDrivers/nattiqbraille.py | Migrates routing gesture to cellIndexes. |
| source/brailleDisplayDrivers/lilli.py | Migrates routing gesture to cellIndexes. |
| source/brailleDisplayDrivers/hims.py | Migrates routing gesture to cellIndexes. |
| source/brailleDisplayDrivers/hidBrailleStandard.py | Aggregates routing keys into cellIndexes, emits routerSet1_multiRouterKey, binds selection. |
| source/brailleDisplayDrivers/hedoProfiLine.py | Migrates routing gesture to cellIndexes. |
| source/brailleDisplayDrivers/hedoMobilLine.py | Migrates routing gesture to cellIndexes. |
| source/brailleDisplayDrivers/handyTech.py | Aggregates routing indexes, uses idForCellCount, binds selection. |
| source/brailleDisplayDrivers/freedomScientific.py | Migrates routing gesture to cellIndexes. |
| source/brailleDisplayDrivers/eurobraille/gestures.py | Migrates routing gesture to cellIndexes. |
| source/brailleDisplayDrivers/ecoBraille.py | Migrates routing gesture to cellIndexes. |
| source/brailleDisplayDrivers/brltty.py | Migrates routing gesture to cellIndexes. |
| source/brailleDisplayDrivers/brailliantB.py | Aggregates routing indexes, uses idForCellCount, binds selection. |
| source/brailleDisplayDrivers/brailleNote.py | Migrates routing gesture to cellIndexes. |
| source/brailleDisplayDrivers/baum.py | Aggregates routing indexes, uses idForCellCount, binds selection. |
| source/brailleDisplayDrivers/alva.py | Supports multiple routing ranges via per-range aggregation and idForCellCount; binds selection. |
| source/brailleDisplayDrivers/albatross/gestures.py | Supports multiple routing ranges via aggregation and idForCellCount; binds selection. |
| source/braille.py | Introduces cellIndexes, adds idForCellCount, and implements deprecated routingIndex shim. |
| source/_remoteClient/session.py | Propagates cellIndexes over Remote while providing legacy routingIndex. |
| source/_remoteClient/input.py | Normalizes legacy routingIndex into cellIndexes to avoid triggering deprecation warnings. |
The previous name suggested a single destination cell, while the command actually selects a range between two routing keys. Update all driver gesture maps and the changelog to match. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Drop the redundant 'Requires a display that reports simultaneous routing key presses' sentence. The input gestures dialog already shows the bound gesture, and the wording is now consistent with sibling braille script descriptions. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The selection is half-open: it includes the cell at the first pressed routing key but stops at (does not include) the cell at the last pressed routing key. 'Between' was misleading. Use 'from the first up to the last' instead, both in the script description and in the changelog entry. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Link to issue number:
Closes #20001
Summary of the issue:
NVDA's
BrailleDisplayGestureonly exposed a singleroutingIndex, so braille displays that report multiple simultaneously pressed routing keys could not bind a meaningful "multi routing" gesture. There was also no way for a user to select a range of text directly from the braille display.Description of user facing changes:
script_braille_selectRange). The selection is half-open: it starts at the cell under the first pressed routing key and ends just before the cell under the last pressed routing key. On drivers that emitmultiRoutingnatively, the gesture is bound to this command out of the box; users of other displays can bind it manually if they wish.multiRouting→braille_selectRangebound by default: ALVA, Albatross, Baum (and compatible), HumanWare Brailliant BI/B series, Handy Tech, NLS eReader Zoomax, Seika Notetaker, Standard HID Braille displays.Description of developer facing changes:
braille.BrailleDisplayGestureexposescellIndexes: list[int] | None. The single-valuedroutingIndexis now a deprecated property that falls back tomax(cellIndexes)for compatibility. WhenNVDAState._allowDeprecatedAPI()returnsFalsethe getter and setter raiseAttributeError.BrailleDisplayGesture.idForCellCount(count, baseName="routing")(classmethod) builds the canonical id ("routing"/"multiRouting"/"multiSecondRouting").cellIndexesis not limited to routing keys; touch-sensitive cells (e.g. Handy Tech ATC) can reuse it._remoteClientpropagatescellIndexeswhile keepingroutingIndexpopulated for older peers.seikantk.InputGestureRouting.__init__now accepts a list of indexes; a single int is still accepted for backwards compatibility.Description of development approach:
cellIndexesand a deprecation shim forroutingIndexonBrailleDisplayGesture.self.routingIndex = xtoself.cellIndexes = [x](or to a collected list where the protocol reports several keys at once).idForCellCountso drivers with multiple routing ranges (e.g. ALVArouting+secondRouting, HIDrouterSet1collections, Albatross dual-row) yield ids likemultiRouting,multiSecondRouting,routerSet1_multiRouterKey.script_braille_selectRangeinglobalCommandsusinggetTextInfoForWindowPos+setEndPoint("endToEnd")+updateSelection(). Guarded againstcellIndexesbeingNone/short and againstNotImplementedErrorfrom the focused control.multiRouting→braille_selectRangein the gesture map of every driver whose driver-side implementation now reports simultaneous routing keys.Testing strategy:
Unit tests added in
tests/unit/test_braille/test_brailleDisplayDrivers.pycovering gesture id construction andcellIndexespopulation for multi-key presses.Manual testing performed on the following drivers (all updated to emit
multiRouting):Per-driver multi-routing support matrix
multiRouting→braille_selectRangeboundmultiSecondRouting)routerSet1_multiRouterKey)Known issues with pull request:
multiRoutingbecause their current driver-side implementation does not aggregate simultaneous routing key presses — not necessarily a hard protocol limitation. Adding support for any of them is a follow-up that only requires collecting indexes intocellIndexesand usingidForCellCount.script_braille_selectRangerelies onTextInfo.updateSelection(); controls that raiseNotImplementedError(some Win32 console-style targets) report a localized "Selection not supported here" message.Code Review Checklist:
cellIndexes,idForCellCount, deprecation entry)routingIndexgetter/setter preserved with deprecation warning under_allowDeprecatedAPI();seikantk.InputGestureRoutingaccepts legacyint.)