@@ -58,10 +58,11 @@ export class AccessibilityManager extends Disposable {
58
58
@IRenderService private readonly _renderService : IRenderService
59
59
) {
60
60
super ( ) ;
61
- this . _accessibilityContainer = this . _coreBrowserService . mainDocument . createElement ( 'div' ) ;
61
+ const doc = this . _coreBrowserService . mainDocument ;
62
+ this . _accessibilityContainer = doc . createElement ( 'div' ) ;
62
63
this . _accessibilityContainer . classList . add ( 'xterm-accessibility' ) ;
63
64
64
- this . _rowContainer = this . _coreBrowserService . mainDocument . createElement ( 'div' ) ;
65
+ this . _rowContainer = doc . createElement ( 'div' ) ;
65
66
this . _rowContainer . setAttribute ( 'role' , 'list' ) ;
66
67
this . _rowContainer . classList . add ( 'xterm-accessibility-tree' ) ;
67
68
this . _rowElements = [ ] ;
@@ -75,10 +76,9 @@ export class AccessibilityManager extends Disposable {
75
76
this . _rowElements [ 0 ] . addEventListener ( 'focus' , this . _topBoundaryFocusListener ) ;
76
77
this . _rowElements [ this . _rowElements . length - 1 ] . addEventListener ( 'focus' , this . _bottomBoundaryFocusListener ) ;
77
78
78
- this . _refreshRowsDimensions ( ) ;
79
79
this . _accessibilityContainer . appendChild ( this . _rowContainer ) ;
80
80
81
- this . _liveRegion = this . _coreBrowserService . mainDocument . createElement ( 'div' ) ;
81
+ this . _liveRegion = doc . createElement ( 'div' ) ;
82
82
this . _liveRegion . classList . add ( 'live-region' ) ;
83
83
this . _liveRegion . setAttribute ( 'aria-live' , 'assertive' ) ;
84
84
this . _accessibilityContainer . appendChild ( this . _liveRegion ) ;
@@ -93,12 +93,12 @@ export class AccessibilityManager extends Disposable {
93
93
this . _rowContainer . classList . add ( 'debug' ) ;
94
94
95
95
// Use a `<div class="xterm">` container so that the css will still apply.
96
- this . _debugRootContainer = document . createElement ( 'div' ) ;
96
+ this . _debugRootContainer = doc . createElement ( 'div' ) ;
97
97
this . _debugRootContainer . classList . add ( 'xterm' ) ;
98
98
99
- this . _debugRootContainer . appendChild ( document . createTextNode ( '------start a11y------' ) ) ;
99
+ this . _debugRootContainer . appendChild ( doc . createTextNode ( '------start a11y------' ) ) ;
100
100
this . _debugRootContainer . appendChild ( this . _accessibilityContainer ) ;
101
- this . _debugRootContainer . appendChild ( document . createTextNode ( '------end a11y------' ) ) ;
101
+ this . _debugRootContainer . appendChild ( doc . createTextNode ( '------end a11y------' ) ) ;
102
102
103
103
this . _terminal . element . insertAdjacentElement ( 'afterend' , this . _debugRootContainer ) ;
104
104
} else {
@@ -115,9 +115,10 @@ export class AccessibilityManager extends Disposable {
115
115
this . register ( this . _terminal . onKey ( e => this . _handleKey ( e . key ) ) ) ;
116
116
this . register ( this . _terminal . onBlur ( ( ) => this . _clearLiveRegion ( ) ) ) ;
117
117
this . register ( this . _renderService . onDimensionsChange ( ( ) => this . _refreshRowsDimensions ( ) ) ) ;
118
- this . register ( addDisposableDomListener ( document , 'selectionchange' , ( ) => this . _handleSelectionChange ( ) ) ) ;
118
+ this . register ( addDisposableDomListener ( doc , 'selectionchange' , ( ) => this . _handleSelectionChange ( ) ) ) ;
119
119
this . register ( this . _coreBrowserService . onDprChange ( ( ) => this . _refreshRowsDimensions ( ) ) ) ;
120
120
121
+ this . _refreshRowsDimensions ( ) ;
121
122
this . _refreshRows ( ) ;
122
123
this . register ( toDisposable ( ( ) => {
123
124
if ( DEBUG ) {
@@ -192,6 +193,7 @@ export class AccessibilityManager extends Disposable {
192
193
}
193
194
element . setAttribute ( 'aria-posinset' , posInSet ) ;
194
195
element . setAttribute ( 'aria-setsize' , setSize ) ;
196
+ this . _alignRowWidth ( element ) ;
195
197
}
196
198
}
197
199
this . _announceCharacters ( ) ;
@@ -270,7 +272,7 @@ export class AccessibilityManager extends Disposable {
270
272
return ;
271
273
}
272
274
273
- const selection = document . getSelection ( ) ;
275
+ const selection = this . _coreBrowserService . mainDocument . getSelection ( ) ;
274
276
if ( ! selection ) {
275
277
return ;
276
278
}
@@ -389,19 +391,45 @@ export class AccessibilityManager extends Disposable {
389
391
this . _refreshRowDimensions ( element ) ;
390
392
return element ;
391
393
}
394
+
392
395
private _refreshRowsDimensions ( ) : void {
393
396
if ( ! this . _renderService . dimensions . css . cell . height ) {
394
397
return ;
395
398
}
396
- this . _accessibilityContainer . style . width = `${ this . _renderService . dimensions . css . canvas . width } px` ;
399
+ Object . assign ( this . _accessibilityContainer . style , {
400
+ width : `${ this . _renderService . dimensions . css . canvas . width } px` ,
401
+ fontSize : `${ this . _terminal . options . fontSize } px`
402
+ } ) ;
397
403
if ( this . _rowElements . length !== this . _terminal . rows ) {
398
404
this . _handleResize ( this . _terminal . rows ) ;
399
405
}
400
406
for ( let i = 0 ; i < this . _terminal . rows ; i ++ ) {
401
407
this . _refreshRowDimensions ( this . _rowElements [ i ] ) ;
408
+ this . _alignRowWidth ( this . _rowElements [ i ] ) ;
402
409
}
403
410
}
411
+
404
412
private _refreshRowDimensions ( element : HTMLElement ) : void {
405
413
element . style . height = `${ this . _renderService . dimensions . css . cell . height } px` ;
406
414
}
415
+
416
+ /**
417
+ * Scale the width of a row so that each of the character is (mostly) aligned
418
+ * with the actual rendering. This will allow the screen reader to draw
419
+ * selection outline at the correct position.
420
+ *
421
+ * On top of using the "monospace" font and correct font size, the scaling
422
+ * here is necessary to handle characters that are not covered by the font
423
+ * (e.g. CJK).
424
+ */
425
+ private _alignRowWidth ( element : HTMLElement ) : void {
426
+ element . style . transform = '' ;
427
+ const width = element . getBoundingClientRect ( ) . width ;
428
+ const lastColumn = this . _rowColumns . get ( element ) ?. slice ( - 1 ) ?. [ 0 ] ;
429
+ if ( ! lastColumn ) {
430
+ return ;
431
+ }
432
+ const targetWidth = lastColumn * this . _renderService . dimensions . css . cell . width ;
433
+ element . style . transform = `scaleX(${ targetWidth / width } )` ;
434
+ }
407
435
}
0 commit comments