Support nav mode pgup/pgdown/end/home, check boundaries

This commit is contained in:
Daniel Imms
2018-01-16 11:01:45 -08:00
parent 222c42f7ea
commit 3fa4b4941f
4 changed files with 66 additions and 23 deletions
+40 -23
View File
@@ -211,7 +211,7 @@ export class AccessibilityManager implements IDisposable {
class NavigationMode implements IDisposable { class NavigationMode implements IDisposable {
private _activeItemId: string; private _activeItemId: string;
private _isNavigationModeActive: boolean = false; private _isNavigationModeActive: boolean = false;
private _navigationModeFocusedRow: number; private _absoluteFocusedRow: number;
private _disposables: IDisposable[] = []; private _disposables: IDisposable[] = [];
@@ -243,13 +243,14 @@ class NavigationMode implements IDisposable {
} }
public enter(): void { public enter(): void {
// TODO: Should entering navigation mode send ydisp to ybase?
this._isNavigationModeActive = true; this._isNavigationModeActive = true;
this._accessibilityManager.announce('Entered line navigation mode'); this._accessibilityManager.announce('Entered line navigation mode');
this._rowContainer.tabIndex = 0; this._rowContainer.tabIndex = 0;
this._rowContainer.setAttribute('role', 'menu'); this._rowContainer.setAttribute('role', 'menu');
this._rowContainer.setAttribute('aria-activedescendant', this._activeItemId); this._rowContainer.setAttribute('aria-activedescendant', this._activeItemId);
this._rowContainer.focus(); this._rowContainer.focus();
this._navigateToElement(this._terminal.buffer.y); this._navigateToElement(this._terminal.buffer.ydisp + this._terminal.buffer.y);
} }
public leave(): void { public leave(): void {
@@ -280,10 +281,16 @@ class NavigationMode implements IDisposable {
public onKeyUp(e: KeyboardEvent): boolean { public onKeyUp(e: KeyboardEvent): boolean {
return this._onKey(e, e => { return this._onKey(e, e => {
switch (e.keyCode) { if (this._isNavigationModeActive) {
case 27: return this._onEscape(e); switch (e.keyCode) {
case 38: return this._onArrowUp(e); case 27: return this._onEscape(e);
case 40: return this._onArrowDown(e); case 33: return this._onPageUp(e);
case 34: return this._onPageDown(e);
case 35: return this._onEnd(e);
case 36: return this._onHome(e);
case 38: return this._onArrowUp(e);
case 40: return this._onArrowDown(e);
}
} }
return false; return false;
}); });
@@ -302,35 +309,45 @@ class NavigationMode implements IDisposable {
} }
private _onArrowUp(e: KeyboardEvent): boolean { private _onArrowUp(e: KeyboardEvent): boolean {
// TODO: Jump up/down to next non-blank row return this._focusRow(this._absoluteFocusedRow - 1);
this._navigateToElement(this._navigationModeFocusedRow - 1);
this._rowContainer.focus();
return true;
} }
private _onArrowDown(e: KeyboardEvent): boolean { private _onArrowDown(e: KeyboardEvent): boolean {
this._navigateToElement(this._navigationModeFocusedRow + 1); return this._focusRow(this._absoluteFocusedRow + 1);
}
private _onPageUp(e: KeyboardEvent): boolean {
return this._focusRow(this._absoluteFocusedRow - this._terminal.rows);
}
private _onPageDown(e: KeyboardEvent): boolean {
return this._focusRow(this._absoluteFocusedRow + this._terminal.rows);
}
private _onHome(e: KeyboardEvent): boolean {
return this._focusRow(0);
}
private _onEnd(e: KeyboardEvent): boolean {
return this._focusRow(this._terminal.buffer.lines.length - 1);
}
private _focusRow(row: number): boolean {
this._navigateToElement(row);
this._rowContainer.focus(); this._rowContainer.focus();
return true; return true;
} }
private _navigateToElement(row: number): void { private _navigateToElement(absoluteRow: number): void {
if (row < 0) { absoluteRow = this._terminal.scrollToRow(absoluteRow);
if (this._terminal.buffer.ydisp > 0) {
this._terminal.scrollLines(-1);
row = 0;
// TODO: This doesn't reliably read the element, maybe a new row needs to be inserted at the top?
// Exit early since the same element is focused
return;
}
}
// TODO: Store this state // TODO: Store this state
const selected = document.querySelector('#' + this._activeItemId); const selected = document.querySelector('#' + this._activeItemId);
if (selected) { if (selected) {
selected.removeAttribute('id'); selected.removeAttribute('id');
} }
this._navigationModeFocusedRow = row; this._absoluteFocusedRow = absoluteRow;
const selectedElement = this._rowElements[row]; const selectedElement = this._rowElements[absoluteRow - this._terminal.buffer.ydisp];
selectedElement.id = this._activeItemId; selectedElement.id = this._activeItemId;
} }
} }
+1
View File
@@ -56,6 +56,7 @@ export interface ITerminal extends ILinkifierAccessor, IBufferAccessor, IElement
*/ */
handler(data: string): void; handler(data: string): void;
scrollLines(disp: number, suppressScrollEvent?: boolean): void; scrollLines(disp: number, suppressScrollEvent?: boolean): void;
scrollToRow(row: number): number;
cancel(ev: Event, force?: boolean): boolean | void; cancel(ev: Event, force?: boolean): boolean | void;
log(text: string): void; log(text: string): void;
reset(): void; reset(): void;
+22
View File
@@ -1169,6 +1169,28 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT
this.refresh(0, this.rows - 1); this.refresh(0, this.rows - 1);
} }
/**
* Scroll the viewport to an absolute row in the buffer.
* @param absoluteRow The absolute row in the buffer to scroll to.
* @returns The actual absolute row that was scrolled to (including boundary checked).
*/
public scrollToRow(absoluteRow: number): number {
// Ensure value is valid
absoluteRow = Math.max(Math.min(absoluteRow, this.buffer.lines.length - 1), 0);
// Move viewport as necessary
const relativeRow = absoluteRow - this.buffer.ydisp;
let scrollAmount = 0;
if (relativeRow < 0) {
scrollAmount = relativeRow;
} else if (relativeRow >= this.rows) {
scrollAmount = relativeRow - this.rows + 1;
}
this.scrollLines(scrollAmount);
return absoluteRow;
}
/** /**
* Scroll the display of the terminal by a number of pages. * Scroll the display of the terminal by a number of pages.
* @param {number} pageCount The number of pages to scroll (negative scrolls up). * @param {number} pageCount The number of pages to scroll (negative scrolls up).
+3
View File
@@ -48,6 +48,9 @@ export class MockTerminal implements ITerminal {
scrollLines(disp: number, suppressScrollEvent: boolean): void { scrollLines(disp: number, suppressScrollEvent: boolean): void {
throw new Error('Method not implemented.'); throw new Error('Method not implemented.');
} }
scrollToRow(absoluteRow: number): number {
throw new Error('Method not implemented.');
}
cancel(ev: Event, force?: boolean): void { cancel(ev: Event, force?: boolean): void {
throw new Error('Method not implemented.'); throw new Error('Method not implemented.');
} }