Support nav mode pgup/pgdown/end/home, check boundaries
This commit is contained in:
+40
-23
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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).
|
||||||
|
|||||||
@@ -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.');
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user