diff --git a/src/neuroglancer/ui/annotations.ts b/src/neuroglancer/ui/annotations.ts index 8249a0b8b..eb821374e 100644 --- a/src/neuroglancer/ui/annotations.ts +++ b/src/neuroglancer/ui/annotations.ts @@ -349,9 +349,30 @@ export class AnnotationLayerView extends Tab { this.virtualList.element.addEventListener('mouseleave', () => { this.displayState.hoverState.value = undefined; }); - + const changeSelectedIndex = (offset: number) => { + const selectedIndex = this.getSelectedAnnotationIndex(); + if (selectedIndex === undefined) return; + const nextAnnotation = this.listElements[selectedIndex+offset]; + if (nextAnnotation) { + const {state, annotation} = nextAnnotation; + this.layer.selectAnnotation(state, annotation.id, true); + this.moveToAnnotation(annotation, state); + } + }; + this.virtualList.element.addEventListener('action:select-previous-annotation', event => { + event.stopPropagation(); + event.preventDefault(); + changeSelectedIndex(-1); + }); + this.virtualList.element.addEventListener('action:select-next-annotation', event => { + event.stopPropagation(); + event.preventDefault(); + changeSelectedIndex(1); + }); const bindings = getDefaultAnnotationListBindings(); this.registerDisposer(new MouseEventBinder(this.virtualList.element, bindings)); + this.registerDisposer(new KeyboardEventBinder(this.virtualList.element, bindings)); + this.virtualList.element.tabIndex = -1; this.virtualList.element.title = bindings.describe(); this.registerDisposer(this.displayState.hoverState.changed.add(() => this.updateHoverView())); this.registerDisposer( @@ -369,6 +390,17 @@ export class AnnotationLayerView extends Tab { this.updateSelectionView(); } + private getSelectedAnnotationIndex() { + const {previousSelectedState: state} = this; + if (state === undefined) return; + const {annotationLayerState, annotationId} = state; + const attached = this.attachedAnnotationStates.get(annotationLayerState); + if (attached === undefined) return; + const index = attached.idToIndex.get(annotationId); + if (index === undefined) return; + return attached.listOffset + index; + } + private getRenderedAnnotationListElement( state: AnnotationLayerState, id: AnnotationId, scrollIntoView: boolean = false): HTMLElement |undefined { @@ -661,6 +693,18 @@ export class AnnotationLayerView extends Tab { this.updateSelectionView(); } + private moveToAnnotation(annotation: Annotation, state: AnnotationLayerState) { + const chunkTransform = state.chunkTransform.value as ChunkTransformParameters; + const {layerRank} = chunkTransform; + const chunkPosition = new Float32Array(layerRank); + const layerPosition = new Float32Array(layerRank); + getCenterPosition(chunkPosition, annotation); + matrix.transformPoint( + layerPosition, chunkTransform.chunkToLayerTransform, layerRank + 1, chunkPosition, + layerRank); + setLayerPosition(this.layer, chunkTransform, layerPosition); + } + private makeAnnotationListElement(annotation: Annotation, state: AnnotationLayerState) { const chunkTransform = state.chunkTransform.value as ChunkTransformParameters; const element = document.createElement('div'); @@ -757,14 +801,7 @@ export class AnnotationLayerView extends Tab { element.addEventListener('action:move-to-annotation', event => { event.stopPropagation(); event.preventDefault(); - const {layerRank} = chunkTransform; - const chunkPosition = new Float32Array(layerRank); - const layerPosition = new Float32Array(layerRank); - getCenterPosition(chunkPosition, annotation); - matrix.transformPoint( - layerPosition, chunkTransform.chunkToLayerTransform, layerRank + 1, chunkPosition, - layerRank); - setLayerPosition(this.layer, chunkTransform, layerPosition); + this.moveToAnnotation(annotation, state); }); const selectionState = this.selectedAnnotationState.value; diff --git a/src/neuroglancer/ui/default_input_event_bindings.ts b/src/neuroglancer/ui/default_input_event_bindings.ts index 022972ada..150873819 100644 --- a/src/neuroglancer/ui/default_input_event_bindings.ts +++ b/src/neuroglancer/ui/default_input_event_bindings.ts @@ -68,6 +68,8 @@ export function getDefaultAnnotationListBindings() { { 'click0': 'pin-annotation', 'mousedown2': 'move-to-annotation', + 'arrowup': 'select-previous-annotation', + 'arrowdown': 'select-next-annotation', }, {parents: [[getDefaultSelectBindings(), 0]]}); }