Add braille text wrap modes with continuation marks#20146
Merged
Conversation
12 tasks
f040151 to
e080142
Compare
Replaces boolean wordWrap config with BrailleTextWrapFlag feature flag (NONE, MARK_WORD_CUTS, AT_WORD_BOUNDARIES, AT_WORD_OR_SYLLABLE_BOUNDARIES). Adds profile upgrade step v22->v23, deprecated API bridge, GUI combo box, continuation mark (dots 7-8) when a word is cut mid-display, and _continuationRows tracking in BrailleBuffer. Syllable boundary mode is wired but its backend follows in a separate PR.
e080142 to
2ac4ae4
Compare
_continuationRows was a parallel list kept in sync with _windowRowBufferOffsets and joined by index at read time. Merge into list[tuple[int, int, bool]] to eliminate the sync burden and O(n) membership test. Also: extract _isMidWordCut helper (removes duplication), drop dead guard `end - start == numCols` (always true in that branch), fix redundant rindex call (second search [start, end) finds same index as first over [start, end+1) when lastSpaceIndex < end), delete stale wordWrap key in upgrade step 22→23 (matches pattern of all other renames).
Contributor
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Replaces the boolean wordWrap braille setting with a three-valued textWrap feature flag (Off / Show mark when words are cut / At word boundaries), and introduces a continuation marker (dots 7-8) shown in the last cell of a row when a word is cut across rows.
Changes:
- New
BrailleTextWrapFlagfeature flag enum, settings dialog combo box, and updated_calculateWindowRowBufferOffsets/_get_windowBrailleCellslogic to emit a continuation shape on cut rows. - Config schema bumped to version 23 with a profile upgrade step migrating
wordWrap→textWrap, and a deprecated-key link fromwordWraptotextWrap. - Updated user docs/changelog and added/updated unit tests.
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| source/config/featureFlagEnums.py | Adds BrailleTextWrapFlag enum with display strings. |
| source/config/configSpec.py | Bumps schema to 23, marks wordWrap deprecated, adds textWrap. |
| source/config/profileUpgradeSteps.py | Adds upgrade step 22→23 migrating wordWrap to textWrap. |
| source/config/init.py | Re-enables _linkDeprecatedValues and bridges wordWrap ⇄ textWrap. |
| source/braille.py | Adds CONTINUATION_SHAPE, mid-word cut handling, continuation flag in row offsets. |
| source/gui/settingsDialogs.py | Replaces word-wrap checkbox with a FeatureFlagCombo. |
| tests/unit/test_braille/test_calculateWindowRowBufferOffsets.py | Updates and extends tests for new wrap flag behavior. |
| tests/unit/test_braille/test_windowBrailleCells.py | New tests for continuation cell injection in window cells. |
| user_docs/en/userGuide.md | Documents the new Text wrap combo and modes. |
| user_docs/en/changes.md | Notes the new feature and deprecation. |
'word or syllable boundaries' was inaccurate; BrailleTextWrapFlag has no syllable option. Correct to 'at word boundaries'. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
seanbudd
reviewed
May 20, 2026
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
seanbudd
approved these changes
May 20, 2026
5 tasks
5 tasks
seanbudd
pushed a commit
that referenced
this pull request
May 25, 2026
) Closes #17010 Follow-up for #20146 and #20145. This is the last of three PRs replacing #19916. Summary of the issue: Word wrap is sometimes pretty aggressive, especially on shorter braille displays. The previous two PRs added the text wrap infrastructure and continuation marks; this PR adds the final mode that splits long words at syllable boundaries using hyphenation dictionaries. Description of user facing changes: A fourth option, At word or syllable boundaries, is added to the Text wrap combo box in braille settings. Like "At word boundaries", it avoids splitting words mid-way, but when a word is too long to fit on the display it additionally tries to split at a syllable boundary (using hyphenation dictionaries from the pyphen library) so less of the word spills onto the next row. NVDA marks the split with the continuation mark (braille dots 7-8). For locales without a pyphen dictionary, the mode falls back cleanly to word-boundary behaviour without any error. Description of developer facing changes: BrailleTextWrapFlag.AT_WORD_OR_SYLLABLE_BOUNDARIES member added to config.featureFlagEnums. Region._languageIndexes (dict[int, str]) tracks language-span boundaries within a braille region. Populated during _addFieldText and _addTextWithFields when format fields carry a language attribute or when field text is in a different language than the surrounding content. Region._getLanguageAtPos(pos) looks up the language at a raw-text offset using a bisect on the (always-ascending) keys of _languageIndexes. BrailleBuffer._getLanguageAtBufferPos(pos) delegates to the region that owns that braille cell. louisHelper.getTableLanguage(table) queries louis.getTableInfo for the "language" key and normalises the result, providing the default language for a region when no format-field language is known. Description of development approach: When AT_WORD_OR_SYLLABLE_BOUNDARIES is selected and a word straddles a row boundary, _calculateWindowRowBufferOffsets already finds the last space before the display edge. This PR adds a second pass: it looks up the full word (from that space to the next space), retrieves the language at the word's braille position, and calls textUtils.hyphenation.getHyphenPositions (introduced in #20145) to obtain candidate hyphen offsets. It then iterates the candidates from the end (closest to the display edge) and picks the first that falls within the current row, updating end accordingly and setting showContinuationMark. Language tracking in Region ensures that the correct pyphen dictionary is selected even when a braille region contains multilingual content (e.g. a paragraph with inline foreign phrases).
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:
Part of #17010. Split out from #19916 at reviewer request — this is the second of three PRs. The pyphen abstraction layer was shipped in #20145; the syllable-aware wrap mode follows in a separate PR.
Summary of the issue:
The braille word-wrap setting is a single boolean that gives users no control over how words are broken at the display edge. When a word is cut mid-way there is also no visual indication that the word continues on the next row.
Description of user facing changes:
The boolean "word wrap" checkbox in the braille settings has been replaced with a Text wrap combo box with three choices:
Existing config profiles with the old setting are automatically upgraded.
Description of developer facing changes:
BrailleTextWrapFlagfeature flag enum added toconfig.featureFlagEnumswith membersDEFAULT,NONE,MARK_WORD_CUTS,AT_WORD_BOUNDARIES.wordWrapboolean is deprecated and bridged bidirectionally totextWrapvia_linkDeprecatedValues, so add-ons reading or writing the old key keep working (with a deprecation warning).CONTINUATION_SHAPE = 0xC0(dots 7-8) constant added tobraille._WindowRowPositionsfrozen dataclass added tobrailleto hold the start/end buffer positions and continuation-mark flag for each row of the braille window, replacing the previous anonymous tuple.Description of development approach:
The continuation mark is unified: it consistently means "a word was cut here" regardless of mode, so readers get a predictable signal.
BrailleBuffer._calculateWindowRowBufferOffsetsis extended to implement all three modes. Each entry in_windowRowBufferOffsetsis a_WindowRowPositionsinstance whoseshowContinuationMarkfield records whether that row needs a continuation mark.BrailleBuffer._get_windowBrailleCellsreads that flag to insert the mark.BrailleBuffer._set_windowEndPosshort-circuits space-seeking forNONEandMARK_WORD_CUTSmodes (backwards scroll alignment).Testing strategy:
Unit tests cover:
_calculateWindowRowBufferOffsets(no cells, fits exactly, word cut, word too long for display, no space on line)._get_windowBrailleCells.wordWrap = True/False→ correcttextWrapflag values.Manual testing: loaded a pre-upgrade profile with
wordWrap = True/Falseand confirmed the upgrade writes the expectedtextWrapvalue and the braille settings panel shows the matching label.Known issues with pull request:
None.
Code Review Checklist: