Skip to content

Commit

Permalink
feat: signal gutterkeydown in keyboard accessibility mode (#5202)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
akoreman committed Jun 19, 2023
1 parent 84e653e commit 538b18c
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 8 deletions.
1 change: 1 addition & 0 deletions ace.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -863,6 +863,7 @@ export namespace Ace {
on(name: 'mousewheel', callback: (e: any) => void): void;
on(name: 'click', callback: (e: any) => void): void;
on(name: 'guttermousedown', callback: (e: any) => void): void;
on(name: 'gutterkeydown', callback: (e: any) => void): void;

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

Expand Down
68 changes: 63 additions & 5 deletions src/keyboard/gutter_handler.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class GutterKeyboardHandler {
e.preventDefault();

if (e.keyCode === keys["escape"])
this.annotationTooltip.hide();
this.annotationTooltip.hideTooltip();

return;
}
Expand Down Expand Up @@ -88,6 +88,16 @@ class GutterKeyboardHandler {
}

// After here, foucs is on a gutter icon and we want to interact with them.
this.$handleGutterKeyboardInteraction(e);

// Wait until folding is completed and then signal gutterkeydown to the editor.
setTimeout(function() {
// Signal to the editor that a key is pressed inside the gutter.
this.editor._signal("gutterkeydown", new GutterKeyboardEvent(e, this));
}.bind(this), 10);
}

$handleGutterKeyboardInteraction(e) {
// Prevent tabbing when interacting with the gutter icons.
if (e.keyCode === keys["tab"]){
e.preventDefault();
Expand Down Expand Up @@ -137,12 +147,14 @@ class GutterKeyboardHandler {
if (e.keyCode === keys["left"]){
e.preventDefault();
this.$switchLane("annotation");
return;
}

// Try to switch from annotations to fold widgets.
if (e.keyCode === keys["right"]){
e.preventDefault();
this.$switchLane("fold");
return;
}

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

if (this.annotationTooltip.isOpen)
this.annotationTooltip.hide();
this.annotationTooltip.hideTooltip();

return;
}
Expand Down Expand Up @@ -288,7 +300,6 @@ class GutterKeyboardHandler {
var annotation = this.$getAnnotation(index);

annotation.classList.add(this.editor.renderer.keyboardFocusClassName);
annotation.setAttribute("role", "button");
annotation.focus();
}

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

annotation.classList.remove(this.editor.renderer.keyboardFocusClassName);
annotation.removeAttribute("role");
annotation.blur();
}

Expand Down Expand Up @@ -423,4 +433,52 @@ class GutterKeyboardHandler {
}
}

exports.GutterKeyboardHandler = GutterKeyboardHandler;
exports.GutterKeyboardHandler = GutterKeyboardHandler;

/*
* Custom Ace gutter keyboard event
*/
class GutterKeyboardEvent {
constructor(domEvent, gutterKeyboardHandler) {
this.gutterKeyboardHandler = gutterKeyboardHandler;
this.domEvent = domEvent;
}

/**
* Returns the key that was presssed.
*
* @return {string} the key that was pressed.
*/
getKey() {
return keys.keyCodeToString(this.domEvent.keyCode);
}

/**
* Returns the row in the gutter that was focused after the keyboard event was handled.
*
* @return {number} the key that was pressed.
*/
getRow() {
return this.gutterKeyboardHandler.$rowIndexToRow(this.gutterKeyboardHandler.activeRowIndex);
}

/**
* Returns whether focus is on the annotation lane after the keyboard event was handled.
*
* @return {boolean} true if focus was on the annotation lane after the keyboard event.
*/
isInAnnotationLane() {
return this.gutterKeyboardHandler.activeLane === "annotation";
}

/**
* Returns whether focus is on the fold lane after the keyboard event was handled.
*
* @return {boolean} true if focus was on the fold lane after the keyboard event.
*/
isInFoldLane() {
return this.gutterKeyboardHandler.activeLane === "fold";
}
}

exports.GutterKeyboardEvent = GutterKeyboardEvent;
40 changes: 40 additions & 0 deletions src/keyboard/gutter_handler_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,46 @@ module.exports = {
assert.equal(document.activeElement, lines.cells[1].element.childNodes[2]);
done();
}, 20);
},"test: should signal keyboard event" : function(done) {
var editor = this.editor;
var value = "x {" + "\n".repeat(50) + "}\n";
value = value.repeat(50);
editor.session.setMode(new Mode());
editor.setOption("enableKeyboardAccessibility", true);
editor.setValue(value, -1);
editor.session.setAnnotations([{row: 0, column: 0, text: "error test", type: "error"}]);

var row, isAnnotation, isFold, key;
editor.on("gutterkeydown", function(event) {
row = event.getRow();
isAnnotation = event.isInAnnotationLane();
isFold = event.isInFoldLane();
key = event.getKey();
});

editor.renderer.$loop._flush();

var lines = editor.renderer.$gutterLayer.$lines;

// Set focus to the gutter div.
editor.renderer.$gutter.focus();
assert.equal(document.activeElement, editor.renderer.$gutter);

// Focus on the annotation.
emit(keys["enter"]);

setTimeout(function() {
emit(keys["left"]);
assert.equal(document.activeElement, lines.cells[0].element.childNodes[2]);

setTimeout(function() {
assert.equal(row, 0);
assert.equal(isAnnotation, true);
assert.equal(isFold, false);
assert.equal(key, "left");
done();
}, 20);
}, 20);
},

tearDown : function() {
Expand Down
10 changes: 7 additions & 3 deletions src/mouse/default_gutter_handler.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,7 @@ function GutterHandler(mouseHandler) {
if (tooltipTimeout)
tooltipTimeout = clearTimeout(tooltipTimeout);
if (tooltip.isOpen) {
tooltip.hide();
editor._signal("hideGutterTooltip", tooltip);
tooltip.hideTooltip();
editor.off("mousewheel", hideTooltip);
}
}
Expand Down Expand Up @@ -211,8 +210,13 @@ class GutterTooltip extends Tooltip {
this.setTheme(this.editor.renderer.theme);
}

this.editor._signal("showGutterTooltip", this);
this.show();
this.editor._signal("showGutterTooltip", this);
}

hideTooltip() {
this.hide();
this.editor._signal("hideGutterTooltip", this);
}

static annotationsToSummaryString(annotations) {
Expand Down

0 comments on commit 538b18c

Please sign in to comment.