Improve keyboard navigation and scrolling
MODIFY
lib/project/results-view.js
lib/project/results-view.js
@@ -29,9 +29,9 @@ class ResultsView {
this.subscriptions = new CompositeDisposable(
atom.config.observe('editor.fontFamily', this.fontFamilyChanged.bind(this)),
- this.model.onDidAddResult(this.addResult.bind(this)),
- this.model.onDidRemoveResult(this.removeResult.bind(this)),
- this.model.onDidClearSearchState(this.clear.bind(this)),
+ this.model.onDidAddResult(this.didAddResult.bind(this)),
+ this.model.onDidRemoveResult(this.didRemoveResult.bind(this)),
+ this.model.onDidClearSearchState(this.didClearSearchState.bind(this)),
this.model.getFindOptions().onDidChangeReplacePattern(() => etch.update(this)),
atom.commands.add(this.element, {
@@ -56,8 +56,8 @@ class ResultsView {
this.subscriptions.dispose();
}
- clear() {
- this.selectedResultIndex = -1;
+ didClearSearchState() {
+ this.selectedResultIndex = 0;
this.selectedMatchIndex = -1;
etch.update(this);
}
@@ -70,7 +70,7 @@ class ResultsView {
}
return $.div(
- {className: 'results-view'},
+ {className: 'results-view focusable-panel', tabIndex: '-1'},
$.ol(
{
@@ -101,7 +101,8 @@ class ResultsView {
this.pathDetailsHeight != null ?
$(ListView, {
- className: 'list-tree has-collapsable-children focusable-panel',
+ ref: 'listView',
+ className: 'list-tree has-collapsable-children',
itemComponent: ResultView,
heightForItem: this.heightForSearchResult,
items: this.model.getPaths().map((filePath, i) => {
@@ -140,11 +141,11 @@ class ResultsView {
}
}
- addResult() {
+ didAddResult() {
etch.update(this);
}
- removeResult({filePath}) {
+ didRemoveResult() {
etch.update(this);
}
@@ -178,11 +179,12 @@ class ResultsView {
} else {
this.selectedMatchIndex = 0;
}
+
+ this.scrollToSelectedMatch();
etch.update(this);
}
moveToTop() {
- this.element.scrollTop = 0;
this.selectFirstResult();
}
@@ -193,40 +195,37 @@ class ResultsView {
} else {
this.selectedMatchIndex = this.model.getResultAt(this.selectedResultIndex).matches.length - 1;
}
- this.element.scrollTop = this.element.scrollHeight - this.element.offsetHeight;
+
+ this.scrollToSelectedMatch();
etch.update(this);
}
pageUp() {
- const pathDetailsElement = this.element.querySelector('.path-details');
- const matchElement = this.element.querySelector('.path:not(.collapsed) .search-result');
- const pathDetailsHeight = pathDetailsElement ? pathDetailsElement.offsetHeight : 0;
- const matchHeight = matchElement ? matchElement.offsetHeight : 0;
+ if (this.refs.listView) {
+ const position = this.positionOfSelectedResult(this.pathDetailsHeight, this.matchHeight);
- const position = this.positionOfSelectedResult(pathDetailsHeight, matchHeight);
- this.element.scrollTop -= this.element.offsetHeight;
+ this.setScrollTop(this.getScrollTop() - this.refs.listView.element.clientHeight);
this.selectResultAtPosition(
- position - this.element.offsetHeight,
+ position - this.refs.listView.element.clientHeight,
pathDetailsHeight,
matchHeight
);
}
+ }
pageDown() {
- const pathDetailsElement = this.element.querySelector('.path-details');
- const matchElement = this.element.querySelector('.path:not(.collapsed) .search-result');
- const pathDetailsHeight = pathDetailsElement ? pathDetailsElement.offsetHeight : 0;
- const matchHeight = matchElement ? matchElement.offsetHeight : 0;
-
- const position = this.positionOfSelectedResult(pathDetailsHeight, matchHeight);
- const newScrollTop = this.element.scrollTop + this.element.offsetHeight;
- this.element.scrollTop = newScrollTop;
+ if (this.refs.listView) {
+ const position = this.positionOfSelectedResult(this.pathDetailsHeight, this.matchHeight);
+ const newScrollTop = this.getScrollTop() + this.refs.listView.element.clientHeight;
+
+ this.setScrollTop(newScrollTop);
this.selectResultAtPosition(
- position + this.element.offsetHeight,
+ position + this.refs.listView.element.clientHeight,
pathDetailsHeight,
matchHeight
);
}
+ }
positionOfSelectedResult(pathDetailsHeight, matchHeight) {
let result = 0;
@@ -245,6 +244,7 @@ class ResultsView {
}
selectResultAtPosition(position, pathDetailsHeight, matchHeight) {
+ if (this.refs.listView) {
this.selectedResultIndex = this.model.getPathCount() - 1;
this.selectedMatchIndex = this.collapsedResultIndices[this.selectedResultIndex] ?
-1 : this.getResultAt(this.selectedResultIndex).matches.length - 1;
@@ -274,16 +274,17 @@ class ResultsView {
}
}
- if (this.element.scrollTop > top) {
- this.element.scrollTop = top;
+ if (this.getScrollTop() > top) {
+ this.setScrollTop(top);
}
- if (this.element.scrollTop < bottom - this.element.offsetHeight) {
- this.element.scrollTop = bottom - this.element.offsetHeight;
+ if (this.getScrollTop() < bottom - this.refs.listView.clientHeight) {
+ this.setScrollTop(bottom - this.refs.listView.clientHeight);
}
etch.update(this);
}
+ }
moveDown() {
if (this.selectedResultIndex === -1) {
@@ -291,7 +292,6 @@ class ResultsView {
return;
}
- this.userMovedSelection = true;
const selectedResult = this.model.getResultAt(this.selectedResultIndex);
if (this.selectedMatchIndex < selectedResult.matches.length - 1 &&
!this.collapsedResultIndices[this.selectedResultIndex]) {
@@ -302,6 +302,7 @@ class ResultsView {
}
this.scrollToSelectedMatch();
+ etch.update(this);
}
moveUp() {
@@ -310,7 +311,6 @@ class ResultsView {
return;
}
- this.userMovedSelection = true;
if (this.selectedMatchIndex >= 0) {
this.selectedMatchIndex--;
} else if (this.selectedResultIndex > 0) {
@@ -323,13 +323,15 @@ class ResultsView {
}
}
- etch.update(this).then(this.scrollToSelectedMatch.bind(this));
+ this.scrollToSelectedMatch();
+ etch.update(this);
}
expandResult() {
if (this.selectedResultIndex !== -1 && this.selectedMatchIndex == -1) {
this.collapsedResultIndices[this.selectedResultIndex] = false;
this.selectedMatchIndex = 0;
+ etch.update(this);
}
}
@@ -337,6 +339,7 @@ class ResultsView {
if (this.selectedResultIndex !== -1) {
this.collapsedResultIndices[this.selectedResultIndex] = true;
this.selectedMatchIndex = -1;
+ etch.update(this);
}
}
@@ -381,46 +384,50 @@ class ResultsView {
expandAllResults() {
this.collapsedResultIndices = new Array(this.model.getPathCount());
this.collapsedResultIndices.fill(false);
- this.element.scrollTop = 0
- etch.update(this)
+ this.setScrollTop(0);
+ etch.update(this);
}
collapseAllResults() {
this.collapsedResultIndices = new Array(this.model.getPaths().length);
this.collapsedResultIndices.fill(true);
- this.element.scrollTop = 0
- etch.update(this)
+ this.setScrollTop(0);
+ etch.update(this);
}
scrollToSelectedMatch() {
- const resultElement = this.element.children[this.selectedResultIndex];
- if (!resultElement) return;
+ if (this.refs.listView) {
+ const top = this.positionOfSelectedResult(this.pathDetailsHeight, this.matchHeight);
+ const bottom = top + this.matchHeight;
- let element;
- if (this.selectedMatchIndex === -1) {
- element = resultElement.querySelector('.path-details');
- } else {
- element = resultElement.querySelectorAll('.search-result')[this.selectedMatchIndex];
+ if (bottom > this.getScrollTop() + this.refs.listView.element.clientHeight) {
+ this.setScrollTop(bottom - this.refs.listView.element.clientHeight);
+ } else if (top < this.getScrollTop()) {
+ this.setScrollTop(top);
+ }
+ }
}
- const top = element.offsetTop;
- const bottom = top + element.offsetHeight;
+ scrollToBottom() {
+ this.setScrollTop(this.getScrollHeight());
+ }
- if (bottom > this.element.scrollTop + this.element.offsetHeight) {
- this.element.scrollTop = bottom - this.element.offsetHeight;
+ scrollToTop() {
+ this.setScrollTop(0);
}
- if (top < this.element.scrollTop) {
- this.element.scrollTop = top;
+ setScrollTop (scrollTop) {
+ if (this.refs.listView) {
+ this.refs.listView.element.scrollTop = scrollTop;
}
}
- scrollToBottom() {
- this.element.scrollTop = this.element.scrollHeight;
+ getScrollTop () {
+ return this.refs.listView ? this.refs.listView.element.scrollTop : 0;
}
- scrollToTop() {
- this.element.scrollTop = 0;
+ getScrollHeight () {
+ return this.refs.listView ? this.refs.listView.element.scrollHeight : 0;
}
selectedResultView() {

Improve keyboard navigation and scrolling