Repository: incubator-zeppelin Updated Branches: refs/heads/master f3de6cd7e -> 6ba4cf96a
ZEPPELIN-401 Improve autoscroll Addresses https://issues.apache.org/jira/browse/ZEPPELIN-401 This PR improves autoscroll behavior when using keyboard navigation (up/down key) * When cursor closes to edge of top/bottom of notebook, autoscroll triggers * Autoscroll will scroll notebook to position cursor to the middle of browser window * When last paragraph is executed, a new paragraph is inserted after. now notebook autoscrolls to newly inserted paragraph. Author: Lee moon soo <[email protected]> This patch had conflicts when merged, resolved by Committer: Lee moon soo <[email protected]> Closes #400 from Leemoonsoo/improve_scroll and squashes the following commits: e1170b4 [Lee moon soo] Do not skip paragraph only hides result when move cursor 3d6a3e1 [Lee moon soo] fix style and condition for tail last paragraph 53d48ac [Lee moon soo] Remove unncessary console.log 9229bd2 [Lee moon soo] Uncomment cursor force positioning 46055f5 [Lee moon soo] Replace hardcoded value 71be96b [Lee moon soo] tail last paragraph ca865fa [Lee moon soo] Moving focus of paragraph by keyboard reset cursor to the beginning (or ending) position of the editor 39108a0 [Lee moon soo] Scroll to cursor position Project: http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/commit/6ba4cf96 Tree: http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/tree/6ba4cf96 Diff: http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/diff/6ba4cf96 Branch: refs/heads/master Commit: 6ba4cf96aecad6a316d6c74f9a336a20585044c6 Parents: f3de6cd Author: Lee moon soo <[email protected]> Authored: Sun Nov 8 21:00:26 2015 +0900 Committer: Lee moon soo <[email protected]> Committed: Tue Nov 10 21:47:27 2015 +0900 ---------------------------------------------------------------------- .../src/app/notebook/notebook.controller.js | 8 +- .../notebook/paragraph/paragraph.controller.js | 78 +++++++++++++++++++- 2 files changed, 80 insertions(+), 6 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/6ba4cf96/zeppelin-web/src/app/notebook/notebook.controller.js ---------------------------------------------------------------------- diff --git a/zeppelin-web/src/app/notebook/notebook.controller.js b/zeppelin-web/src/app/notebook/notebook.controller.js index d8ee83f..dc986a9 100644 --- a/zeppelin-web/src/app/notebook/notebook.controller.js +++ b/zeppelin-web/src/app/notebook/notebook.controller.js @@ -313,8 +313,8 @@ angular.module('zeppelinWebApp').controller('NotebookCtrl', function($scope, $ro } } else { var p = $scope.note.paragraphs[i]; - if (!p.config.hide && !p.config.editorHide && !p.config.tableHide) { - $scope.$broadcast('focusParagraph', $scope.note.paragraphs[i].id); + if (!p.config.hide && !p.config.editorHide) { + $scope.$broadcast('focusParagraph', $scope.note.paragraphs[i].id, -1); break; } } @@ -331,8 +331,8 @@ angular.module('zeppelinWebApp').controller('NotebookCtrl', function($scope, $ro } } else { var p = $scope.note.paragraphs[i]; - if (!p.config.hide && !p.config.editorHide && !p.config.tableHide) { - $scope.$broadcast('focusParagraph', $scope.note.paragraphs[i].id); + if (!p.config.hide && !p.config.editorHide) { + $scope.$broadcast('focusParagraph', $scope.note.paragraphs[i].id, 0); break; } } http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/6ba4cf96/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js ---------------------------------------------------------------------- diff --git a/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js b/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js index 63c30c4..0ba155f 100644 --- a/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js +++ b/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js @@ -167,6 +167,7 @@ angular.module('zeppelinWebApp') var oldGraphMode = $scope.getGraphMode(); var newGraphMode = $scope.getGraphMode(data.paragraph); var resultRefreshed = (data.paragraph.dateFinished !== $scope.paragraph.dateFinished); + var statusChanged = (data.paragraph.status !== $scope.paragraph.status); //console.log("updateParagraph oldData %o, newData %o. type %o -> %o, mode %o -> %o", $scope.paragraph, data, oldType, newType, oldGraphMode, newGraphMode); @@ -223,7 +224,14 @@ angular.module('zeppelinWebApp') } else if (newType === 'ANGULAR' && resultRefreshed) { $scope.renderAngular(); } + + if (statusChanged || resultRefreshed) { + // when last paragraph runs, zeppelin automatically appends new paragraph. + // this broadcast will focus to the newly inserted paragraph + $rootScope.$broadcast('scrollToCursor'); + } } + }); $scope.isRunning = function() { @@ -586,12 +594,15 @@ angular.module('zeppelinWebApp') } else { var numRows; var currentRow; + if (keyCode === 38 || (keyCode === 80 && e.ctrlKey)) { // UP numRows = $scope.editor.getSession().getLength(); currentRow = $scope.editor.getCursorPosition().row; if (currentRow === 0) { // move focus to previous paragraph $scope.$emit('moveFocusToPreviousParagraph', $scope.paragraph.id); + } else { + $scope.scrollToCursor($scope.paragraph.id, -1); } } else if (keyCode === 40 || (keyCode === 78 && e.ctrlKey)) { // DOWN numRows = $scope.editor.getSession().getLength(); @@ -599,6 +610,8 @@ angular.module('zeppelinWebApp') if (currentRow === numRows-1) { // move focus to next paragraph $scope.$emit('moveFocusToNextParagraph', $scope.paragraph.id); + } else { + $scope.scrollToCursor($scope.paragraph.id, 1); } } } @@ -615,6 +628,54 @@ angular.module('zeppelinWebApp') editor.resize(); }; + $rootScope.$on('scrollToCursor', function(event) { + $scope.scrollToCursor($scope.paragraph.id, 0); + }); + + /** scrollToCursor if it is necessary + * when cursor touches scrollTriggerEdgeMargin from the top (or bottom) of the screen, it autoscroll to place cursor around 1/3 of screen height from the top (or bottom) + * paragraphId : paragraph that has active cursor + * lastCursorMove : 1(down), 0, -1(up) last cursor move event + **/ + $scope.scrollToCursor = function(paragraphId, lastCursorMove) { + if (!$scope.editor.isFocused()) { + // only make sense when editor is focused + return; + } + var lineHeight = $scope.editor.renderer.lineHeight; + var headerHeight = 103; // menubar, notebook titlebar + var scrollTriggerEdgeMargin = 50; + + var documentHeight = angular.element(document).height(); + var windowHeight = angular.element(window).height(); // actual viewport height + + var scrollPosition = angular.element(document).scrollTop(); + var editorPosition = angular.element('#'+paragraphId+'_editor').offset(); + var position = $scope.editor.getCursorPosition(); + var lastCursorPosition = $scope.editor.renderer.$cursorLayer.getPixelPosition(position, true); + + var calculatedCursorPosition = editorPosition.top + lastCursorPosition.top + 16*lastCursorMove; + + var scrollTargetPos; + if (calculatedCursorPosition < scrollPosition + headerHeight + scrollTriggerEdgeMargin) { + scrollTargetPos = calculatedCursorPosition - headerHeight - ((windowHeight-headerHeight)/3); + if (scrollTargetPos < 0) { + scrollTargetPos = 0; + } + } else if(calculatedCursorPosition > scrollPosition + scrollTriggerEdgeMargin + windowHeight - headerHeight) { + scrollTargetPos = calculatedCursorPosition - headerHeight - ((windowHeight-headerHeight)*2/3); + + if (scrollTargetPos > documentHeight) { + scrollTargetPos = documentHeight; + } + } + angular.element('body').scrollTo(scrollTargetPos, {duration:200}); + }; + + var setEditorHeight = function(id, height) { + angular.element('#' + id).height(height.toString() + 'px'); + }; + $scope.getEditorValue = function() { return $scope.editor.getValue(); }; @@ -653,10 +714,23 @@ angular.module('zeppelinWebApp') } }); - $scope.$on('focusParagraph', function(event, paragraphId) { + $scope.$on('focusParagraph', function(event, paragraphId, cursorPos) { if ($scope.paragraph.id === paragraphId) { + // focus editor $scope.editor.focus(); - $('body').scrollTo('#'+paragraphId+'_editor', 300, {offset:-60}); + + // move cursor to the first row (or the last row) + var row; + if (cursorPos >= 0) { + row = cursorPos; + var column = 0; + $scope.editor.gotoLine(row, 0); + } else { + row = $scope.editor.session.getLength() - 1; + $scope.editor.gotoLine(row + 1, 0); + } + + $scope.scrollToCursor($scope.paragraph.id, 0); } });
