Skip to content

Commit 538b18c

Browse files
authoredJun 19, 2023
feat: signal gutterkeydown in keyboard accessibility mode (#5202)
* emit gutterkeydown event in keyboard a11y mode * switch emit to signal * emit hideGutterTooltip in keyboard gutter mode * remove unneeded methods * remove unused event import * refactoring * whitespace fix * refactor * renaming * fix * add test * extend test * add to ace.d.ts
1 parent 84e653e commit 538b18c

File tree

4 files changed

+111
-8
lines changed

4 files changed

+111
-8
lines changed
 

‎ace.d.ts

+1
Original file line numberDiff line numberDiff line change
@@ -863,6 +863,7 @@ export namespace Ace {
863863
on(name: 'mousewheel', callback: (e: any) => void): void;
864864
on(name: 'click', callback: (e: any) => void): void;
865865
on(name: 'guttermousedown', callback: (e: any) => void): void;
866+
on(name: 'gutterkeydown', callback: (e: any) => void): void;
866867

867868
onPaste(text: string, event: any): void;
868869

‎src/keyboard/gutter_handler.js

+63-5
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ class GutterKeyboardHandler {
3434
e.preventDefault();
3535

3636
if (e.keyCode === keys["escape"])
37-
this.annotationTooltip.hide();
37+
this.annotationTooltip.hideTooltip();
3838

3939
return;
4040
}
@@ -88,6 +88,16 @@ class GutterKeyboardHandler {
8888
}
8989

9090
// After here, foucs is on a gutter icon and we want to interact with them.
91+
this.$handleGutterKeyboardInteraction(e);
92+
93+
// Wait until folding is completed and then signal gutterkeydown to the editor.
94+
setTimeout(function() {
95+
// Signal to the editor that a key is pressed inside the gutter.
96+
this.editor._signal("gutterkeydown", new GutterKeyboardEvent(e, this));
97+
}.bind(this), 10);
98+
}
99+
100+
$handleGutterKeyboardInteraction(e) {
91101
// Prevent tabbing when interacting with the gutter icons.
92102
if (e.keyCode === keys["tab"]){
93103
e.preventDefault();
@@ -137,12 +147,14 @@ class GutterKeyboardHandler {
137147
if (e.keyCode === keys["left"]){
138148
e.preventDefault();
139149
this.$switchLane("annotation");
150+
return;
140151
}
141152

142153
// Try to switch from annotations to fold widgets.
143154
if (e.keyCode === keys["right"]){
144155
e.preventDefault();
145156
this.$switchLane("fold");
157+
return;
146158
}
147159

148160
if (e.keyCode === keys["enter"] || e.keyCode === keys["space"]){
@@ -198,7 +210,7 @@ class GutterKeyboardHandler {
198210
}
199211

200212
if (this.annotationTooltip.isOpen)
201-
this.annotationTooltip.hide();
213+
this.annotationTooltip.hideTooltip();
202214

203215
return;
204216
}
@@ -288,7 +300,6 @@ class GutterKeyboardHandler {
288300
var annotation = this.$getAnnotation(index);
289301

290302
annotation.classList.add(this.editor.renderer.keyboardFocusClassName);
291-
annotation.setAttribute("role", "button");
292303
annotation.focus();
293304
}
294305

@@ -303,7 +314,6 @@ class GutterKeyboardHandler {
303314
var annotation = this.$getAnnotation(index);
304315

305316
annotation.classList.remove(this.editor.renderer.keyboardFocusClassName);
306-
annotation.removeAttribute("role");
307317
annotation.blur();
308318
}
309319

@@ -423,4 +433,52 @@ class GutterKeyboardHandler {
423433
}
424434
}
425435

426-
exports.GutterKeyboardHandler = GutterKeyboardHandler;
436+
exports.GutterKeyboardHandler = GutterKeyboardHandler;
437+
438+
/*
439+
* Custom Ace gutter keyboard event
440+
*/
441+
class GutterKeyboardEvent {
442+
constructor(domEvent, gutterKeyboardHandler) {
443+
this.gutterKeyboardHandler = gutterKeyboardHandler;
444+
this.domEvent = domEvent;
445+
}
446+
447+
/**
448+
* Returns the key that was presssed.
449+
*
450+
* @return {string} the key that was pressed.
451+
*/
452+
getKey() {
453+
return keys.keyCodeToString(this.domEvent.keyCode);
454+
}
455+
456+
/**
457+
* Returns the row in the gutter that was focused after the keyboard event was handled.
458+
*
459+
* @return {number} the key that was pressed.
460+
*/
461+
getRow() {
462+
return this.gutterKeyboardHandler.$rowIndexToRow(this.gutterKeyboardHandler.activeRowIndex);
463+
}
464+
465+
/**
466+
* Returns whether focus is on the annotation lane after the keyboard event was handled.
467+
*
468+
* @return {boolean} true if focus was on the annotation lane after the keyboard event.
469+
*/
470+
isInAnnotationLane() {
471+
return this.gutterKeyboardHandler.activeLane === "annotation";
472+
}
473+
474+
/**
475+
* Returns whether focus is on the fold lane after the keyboard event was handled.
476+
*
477+
* @return {boolean} true if focus was on the fold lane after the keyboard event.
478+
*/
479+
isInFoldLane() {
480+
return this.gutterKeyboardHandler.activeLane === "fold";
481+
}
482+
}
483+
484+
exports.GutterKeyboardEvent = GutterKeyboardEvent;

‎src/keyboard/gutter_handler_test.js

+40
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,46 @@ module.exports = {
256256
assert.equal(document.activeElement, lines.cells[1].element.childNodes[2]);
257257
done();
258258
}, 20);
259+
},"test: should signal keyboard event" : function(done) {
260+
var editor = this.editor;
261+
var value = "x {" + "\n".repeat(50) + "}\n";
262+
value = value.repeat(50);
263+
editor.session.setMode(new Mode());
264+
editor.setOption("enableKeyboardAccessibility", true);
265+
editor.setValue(value, -1);
266+
editor.session.setAnnotations([{row: 0, column: 0, text: "error test", type: "error"}]);
267+
268+
var row, isAnnotation, isFold, key;
269+
editor.on("gutterkeydown", function(event) {
270+
row = event.getRow();
271+
isAnnotation = event.isInAnnotationLane();
272+
isFold = event.isInFoldLane();
273+
key = event.getKey();
274+
});
275+
276+
editor.renderer.$loop._flush();
277+
278+
var lines = editor.renderer.$gutterLayer.$lines;
279+
280+
// Set focus to the gutter div.
281+
editor.renderer.$gutter.focus();
282+
assert.equal(document.activeElement, editor.renderer.$gutter);
283+
284+
// Focus on the annotation.
285+
emit(keys["enter"]);
286+
287+
setTimeout(function() {
288+
emit(keys["left"]);
289+
assert.equal(document.activeElement, lines.cells[0].element.childNodes[2]);
290+
291+
setTimeout(function() {
292+
assert.equal(row, 0);
293+
assert.equal(isAnnotation, true);
294+
assert.equal(isFold, false);
295+
assert.equal(key, "left");
296+
done();
297+
}, 20);
298+
}, 20);
259299
},
260300

261301
tearDown : function() {

‎src/mouse/default_gutter_handler.js

+7-3
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,7 @@ function GutterHandler(mouseHandler) {
6969
if (tooltipTimeout)
7070
tooltipTimeout = clearTimeout(tooltipTimeout);
7171
if (tooltip.isOpen) {
72-
tooltip.hide();
73-
editor._signal("hideGutterTooltip", tooltip);
72+
tooltip.hideTooltip();
7473
editor.off("mousewheel", hideTooltip);
7574
}
7675
}
@@ -211,8 +210,13 @@ class GutterTooltip extends Tooltip {
211210
this.setTheme(this.editor.renderer.theme);
212211
}
213212

214-
this.editor._signal("showGutterTooltip", this);
215213
this.show();
214+
this.editor._signal("showGutterTooltip", this);
215+
}
216+
217+
hideTooltip() {
218+
this.hide();
219+
this.editor._signal("hideGutterTooltip", this);
216220
}
217221

218222
static annotationsToSummaryString(annotations) {

0 commit comments

Comments
 (0)
Please sign in to comment.