From cd42d284fd680c1e079f62837dfd992045f77ba8 Mon Sep 17 00:00:00 2001 From: Fabian Jakobs Date: Mon, 1 Aug 2011 18:47:37 +0200 Subject: [PATCH] move resize handling in the window model --- lib/ace/editor.js | 162 ++++-------------------------- lib/ace/editor_navigation_test.js | 63 +++++++----- lib/ace/model/window.js | 112 +++++++++++++++++++-- lib/ace/view/window_view.js | 78 +++++++------- lib/ace/view/window_view_mock.js | 7 -- lib/ace/window_controller.js | 12 ++- 6 files changed, 207 insertions(+), 227 deletions(-) diff --git a/lib/ace/editor.js b/lib/ace/editor.js index 78e5f3aa..d0ce2554 100644 --- a/lib/ace/editor.js +++ b/lib/ace/editor.js @@ -142,7 +142,8 @@ var Editor = function(windowView, buffer) { }; this.resize = function() { - this.renderer.onResize(); + //this.renderer.onResize(); + this.windowController.resize(); }; this.setTheme = function(theme) { @@ -196,117 +197,6 @@ var Editor = function(windowView, buffer) { this._dispatchEvent("blur"); }; -/* this.onDocumentChange = function(e) { - var delta = e.data; - var range = delta.range; - - if (range.start.row == range.end.row && delta.action != "insertLines" && delta.action != "removeLines") - var lastRow = range.end.row; - else - lastRow = Infinity; - this.renderer.updateLines(range.start.row, lastRow); - - // update cursor because tab characters can influence the cursor position - this.renderer.updateCursor(); - }; - - this.onTokenizerUpdate = function(e) { - var rows = e.data; - this.renderer.updateLines(rows.first, rows.last); - }; - - this.onCursorChange = function(e) { - this.renderer.updateCursor(); - - if (!this.$blockScrolling) { - this.windowModel.scrollCursorIntoView(); - } - - // move text input over the cursor - // this is required for iOS and IME - this.renderer.moveTextAreaToCursor(this.textInput.getElement()); - - this.$highlightBrackets(); - this.$updateHighlightActiveLine(); - }; - - this.$updateHighlightActiveLine = function() { - var session = this.getSession(); - - if (session.$highlightLineMarker) { - session.removeMarker(session.$highlightLineMarker); - } - session.$highlightLineMarker = null; - - if (this.getHighlightActiveLine() && (this.getSelectionStyle() != "line" || !this.selection.isMultiLine())) { - var cursor = this.getCursorPosition(), - foldLine = this.session.getFoldLine(cursor.row); - var range; - if (foldLine) { - range = new Range(foldLine.start.row, 0, foldLine.end.row + 1, 0); - } else { - range = new Range(cursor.row, 0, cursor.row+1, 0); - } - session.$highlightLineMarker = session.addMarker(range, "ace_active_line", "background"); - } - }; - - this.onSelectionChange = function(e) { - var session = this.getSession(); - - if (session.$selectionMarker) { - session.removeMarker(session.$selectionMarker); - } - session.$selectionMarker = null; - - if (!this.selection.isEmpty()) { - var range = this.selection.getRange(); - var style = this.getSelectionStyle(); - session.$selectionMarker = session.addMarker(range, "ace_selection", style); - } else { - this.$updateHighlightActiveLine(); - } - - if (this.$highlightSelectedWord) - this.session.getMode().highlightSelection(this); - }; - - this.onChangeFrontMarker = function() { - this.renderer.updateFrontMarkers(); - }; - - this.onChangeBackMarker = function() { - this.renderer.updateBackMarkers(); - }; - - this.onChangeBreakpoint = function() { - this.renderer.setBreakpoints(this.session.getBreakpoints()); - }; - - this.onChangeAnnotation = function() { - this.renderer.setAnnotations(this.session.getAnnotations()); - }; - - this.onChangeMode = function() { - this.renderer.updateText() - }; - - this.onChangeWrapLimit = function() { - this.renderer.updateFull(); - }; - - this.onChangeWrapMode = function() { - this.renderer.onResize(true); - }; - - this.onChangeFold = function() { - // Update the active line marker as due to folding changes the current - // line range on the screen might have changed. - this.$updateHighlightActiveLine(); - // TODO: This might be too much updating. Okay for now. - this.renderer.updateFull(); - };*/ - this.getCopyText = function() { var text = ""; if (!this.selection.isEmpty()) @@ -816,7 +706,6 @@ var Editor = function(windowView, buffer) { this.renderer.hideComposition(); }; - this.getFirstVisibleRow = function() { return this.windowModel.getFirstVisibleRow(); }; @@ -826,22 +715,19 @@ var Editor = function(windowView, buffer) { }; this.isRowVisible = function(row) { - return (row >= this.getFirstVisibleRow() && row <= this.getLastVisibleRow()); + return this.windowModel.isRowVisible(row); }; this.$getVisibleRowCount = function() { - return this.windowModel.getScrollBottomRow() - this.windowModel.getScrollTopRow() + 1; + return this.windowModel.getVisibleRowCount(); }; this.$getPageDownRow = function() { - return this.windowModel.getScrollBottomRow(); + return this.windowModel.getPageDownRow(); }; this.$getPageUpRow = function() { - var firstRow = this.windowModel.getScrollTopRow(); - var lastRow = this.windowModel.getScrollBottomRow(); - - return firstRow - (lastRow - firstRow); + return this.windowModel.getPageUpRow(); }; this.selectPageDown = function() { @@ -900,58 +786,44 @@ var Editor = function(windowView, buffer) { }; this.centerSelection = function() { - var range = this.getSelectionRange(); - var line = Math.floor(range.start.row + (range.end.row - range.start.row) / 2); - this.windowModel.scrollToLine(line, true); + this.windowModel.centerSelection(); }; this.getCursorPosition = function() { - return this.selection.getCursor(); + return this.windowModel.getCursorPosition(); }; this.getCursorPositionScreen = function() { - return this.session.documentToScreenPosition(this.getCursorPosition()); + return this.windowModel.getCursorPositionScreen(); }; this.getSelectionRange = function() { - return this.selection.getRange(); + return this.windowModel.getSelectionRange(); }; - this.selectAll = function() { - this.$blockScrolling += 1; - this.selection.selectAll(); - this.$blockScrolling -= 1; + this.windowModel.selectAll(); }; this.clearSelection = function() { - this.selection.clearSelection(); + this.windowModel.clearSelection(); }; this.moveCursorTo = function(row, column) { - this.selection.moveCursorTo(row, column); + this.windowModel.moveCursorTo(row, column); }; this.moveCursorToPosition = function(pos) { - this.selection.moveCursorToPosition(pos); + this.windowModel.moveCursorToPosition(pos); }; this.gotoLine = function(lineNumber, column) { - this.selection.clearSelection(); - - this.$blockScrolling += 1; - this.moveCursorTo(lineNumber-1, column || 0); - this.$blockScrolling -= 1; - - if (!this.isRowVisible(this.getCursorPosition().row)) { - this.scrollToLine(lineNumber, true); - } - }, + this.windowModel.gotoLine(lineNumber, column); + }; this.navigateTo = function(row, column) { - this.clearSelection(); - this.moveCursorTo(row, column); + this.windowModel.navigateTo(row, column); }; this.navigateUp = function(times) { diff --git a/lib/ace/editor_navigation_test.js b/lib/ace/editor_navigation_test.js index 86c95166..f1475f75 100644 --- a/lib/ace/editor_navigation_test.js +++ b/lib/ace/editor_navigation_test.js @@ -43,6 +43,7 @@ if (typeof process !== "undefined") { define(function(require, exports, module) { var Buffer = require("ace/model/buffer").Buffer; +var Window = require("ace/model/window").Window; var Editor = require("ace/editor").Editor; var MockRenderer = require("ace/view/window_view_mock").MockRenderer; var assert = require("ace/test/assertions"); @@ -78,38 +79,48 @@ module.exports = { assert.equal(editor.getFirstVisibleRow(), 0); }, - "test: goto hidden line should scroll the line into the middle of the viewport" : function() { - var editor = new Editor(new MockRenderer(), this.createBuffer(200, 5)); + ">test: goto hidden line should scroll the line into the middle of the viewport" : function() { + //var editor = new Editor(new MockRenderer(), this.createBuffer(200, 5)); + var win = new Window(); + win.setBuffer(this.createBuffer(200, 5)); + win.setSizes({ + heigth: 410, + width: 640, + scrollerHeight: 410, + scrollerWidth: 600 + }); + win.setComputedCharacterSize({width: 10, height: 20}); + //win.updateLayerConfig(); - editor.navigateTo(0, 0); - editor.gotoLine(101); - assert.position(editor.getCursorPosition(), 100, 0); - assert.equal(editor.getFirstVisibleRow(), 90); + win.navigateTo(0, 0); + win.gotoLine(101); + assert.position(win.getCursorPosition(), 100, 0); + assert.equal(win.getFirstVisibleRow(), 90); - editor.navigateTo(100, 0); - editor.gotoLine(11); - assert.position(editor.getCursorPosition(), 10, 0); - assert.equal(editor.getFirstVisibleRow(), 0); + win.navigateTo(100, 0); + win.gotoLine(11); + assert.position(win.getCursorPosition(), 10, 0); + assert.equal(win.getFirstVisibleRow(), 0); - editor.navigateTo(100, 0); - editor.gotoLine(6); - assert.position(editor.getCursorPosition(), 5, 0); - assert.equal(0, editor.getFirstVisibleRow(), 0); + win.navigateTo(100, 0); + win.gotoLine(6); + assert.position(win.getCursorPosition(), 5, 0); + assert.equal(0, win.getFirstVisibleRow(), 0); - editor.navigateTo(100, 0); - editor.gotoLine(1); - assert.position(editor.getCursorPosition(), 0, 0); - assert.equal(editor.getFirstVisibleRow(), 0); + win.navigateTo(100, 0); + win.gotoLine(1); + assert.position(win.getCursorPosition(), 0, 0); + assert.equal(win.getFirstVisibleRow(), 0); - editor.navigateTo(0, 0); - editor.gotoLine(191); - assert.position(editor.getCursorPosition(), 190, 0); - assert.equal(editor.getFirstVisibleRow(), 180); + win.navigateTo(0, 0); + win.gotoLine(191); + assert.position(win.getCursorPosition(), 190, 0); + assert.equal(win.getFirstVisibleRow(), 180); - editor.navigateTo(0, 0); - editor.gotoLine(196); - assert.position(editor.getCursorPosition(), 195, 0); - assert.equal(editor.getFirstVisibleRow(), 180); + win.navigateTo(0, 0); + win.gotoLine(196); + assert.position(win.getCursorPosition(), 195, 0); + assert.equal(win.getFirstVisibleRow(), 180); }, "test: goto visible line should only move the cursor and not scroll": function() { diff --git a/lib/ace/model/window.js b/lib/ace/model/window.js index 45e72351..06a69bee 100644 --- a/lib/ace/model/window.js +++ b/lib/ace/model/window.js @@ -77,6 +77,7 @@ var Window = exports.Window = function(theme) { this.scrollLeft = 0; this.scrollTop = 0; + this._blockScrolling = 0; this.showInvisibles = false; this.showPrintMargin = true; @@ -94,6 +95,31 @@ var Window = exports.Window = function(theme) { oop.implement(this, EventEmitter); + // VIEWPORT SIZE + + this.setSizes = function(size) { + if (this.size.width !== size.width || this.size.scrollerWidth !== size.scrollerWidth) { + this.size.width = size.width; + this.size.scrollerWidth = size.scrollerWidth; + this.size.gutterWidth = size.gutterWidth; + this._emit("changeWidth"); + } + + if (this.size.height !== size.height || this.size.scrollerHeight !== size.scrollerHeight) { + this.size.height = size.height; + this.size.scrollerHeight = size.scrollerHeight; + this._emit("changeHeight"); + } + }; + + this.setComputedCharacterSize = function(size) { + if (this.charSize.height == size.height && this.charSize.width == size.width) + return; + + this.charSize = size; + this._emit("changeCharacterSize") + }; + // VIEWPORT COMPUTATIONS this.getFirstVisibleRow = function() { @@ -113,6 +139,25 @@ var Window = exports.Window = function(theme) { return this.layerConfig.firstRow - 1 + flint; }; + this.isRowVisible = function(row) { + return (row >= this.getFirstVisibleRow() && row <= this.getLastVisibleRow()); + }; + + this.getVisibleRowCount = function() { + return this.getScrollBottomRow() - this.getScrollTopRow() + 1; + }; + + this.getPageDownRow = function() { + return this.getScrollBottomRow(); + }; + + this.getPageUpRow = function() { + var firstRow = this.getScrollTopRow(); + var lastRow = this.getScrollBottomRow(); + + return firstRow - (lastRow - firstRow); + }; + this.getCursorPixelPosition = function(onScreen) { if (!this.buffer) { return { @@ -140,6 +185,62 @@ var Window = exports.Window = function(theme) { this.getRowHeight = function(row) { return this.buffer.getRowLength(row) * this.charSize.height; }; + + // SELECTION HANDLING + + this.centerSelection = function() { + var range = this.getSelectionRange(); + var line = Math.floor(range.start.row + (range.end.row - range.start.row) / 2); + this.scrollToLine(line, true); + }; + + this.getCursorPosition = function() { + return this.selection.getCursor(); + }; + + this.getCursorPositionScreen = function() { + return this.buffer.documentToScreenPosition(this.getCursorPosition()); + }; + + this.getSelectionRange = function() { + return this.selection.getRange(); + }; + + this.selectAll = function() { + this._blockScrolling += 1; + this.selection.selectAll(); + this._blockScrolling -= 1; + }; + + this.clearSelection = function() { + this.selection.clearSelection(); + }; + + this.moveCursorTo = function(row, column) { + this.selection.moveCursorTo(row, column); + }; + + this.moveCursorToPosition = function(pos) { + this.selection.moveCursorToPosition(pos); + }; + + // NAVIGATION + + this.gotoLine = function(lineNumber, column) { + this.selection.clearSelection(); + + this._blockScrolling += 1; + this.moveCursorTo(lineNumber-1, column || 0); + this._blockScrolling -= 1; + + if (!this.isRowVisible(this.getCursorPosition().row)) + this.scrollToLine(lineNumber, true); + }; + + this.navigateTo = function(row, column) { + this.clearSelection(); + this.moveCursorTo(row, column); + }; // SCROLLING @@ -204,6 +305,9 @@ var Window = exports.Window = function(theme) { if (this.size.scrollerHeight === 0) return; + if (this._blockScrolling) + return; + var pos = this.getCursorPixelPosition(); var left = pos.left; @@ -340,14 +444,6 @@ var Window = exports.Window = function(theme) { return this.horizScrollAlwaysVisible; }; - this.setComputedCharacterSize = function(size) { - if (this.charSize.height == size.height && this.charSize.width == size.width) - return; - - this.charSize = size; - this._emit("changeCharacterSize") - }; - this.setSelectionStyle = function(style) { if (this.selectionStyle === style) return; diff --git a/lib/ace/view/window_view.js b/lib/ace/view/window_view.js index 86b39060..c25d559f 100644 --- a/lib/ace/view/window_view.js +++ b/lib/ace/view/window_view.js @@ -176,45 +176,47 @@ var WindowView = function(windowModel, container) { this.$textLayer.checkForSizeChanges(); }; - /** - * Triggers resize of the editor - */ - this.onResize = function(force) { - if (!this.scroller || !this.container || !this.model.buffer) - return; - - var changes = this.CHANGE_SIZE; - var size = this.model.size; - - var height = dom.getInnerHeight(this.container); - if (force || size.height != height) { - size.height = height; - - this.scroller.style.height = height + "px"; - size.scrollerHeight = this.scroller.clientHeight; - this.scrollBar.setHeight(size.scrollerHeight); - - if (this.model.buffer) { - this.model.scrollToY(this.model.scrollTop); - changes = changes | this.CHANGE_FULL; - } - } - + this.measureSizes = function() { var width = dom.getInnerWidth(this.container); - if (force || size.width != width) { - size.width = width; - - var gutterWidth = this.model.showGutter ? this.$gutter.offsetWidth : 0; - this.scroller.style.left = gutterWidth + "px"; - size.scrollerWidth = Math.max(0, width - gutterWidth - this.scrollBar.getWidth()) - this.scroller.style.width = size.scrollerWidth + "px"; - - if (this.model.buffer.getUseWrapMode() && this.adjustWrapLimit() || force) - changes = changes | this.CHANGE_FULL; + var gutterWidth = this.model.showGutter ? this.$gutter.offsetWidth : 0; + return { + width: width, + height: dom.getInnerHeight(this.container), + scrollerHeight: this.scroller.clientHeight, + scrollerWidth: Math.max(0, width - gutterWidth - this.scrollBar.getWidth()), + gutterWidth: gutterWidth } + }; + this.updateHeight = function() { + var size = this.model.size; + + this.scroller.style.height = size.height + "px"; + this.scrollBar.setHeight(size.scrollerHeight); + + var changes = this.CHANGE_SIZE; + if (this.model.buffer) { + this.model.scrollToY(this.model.scrollTop); + changes = changes | this.CHANGE_FULL; + } this.$loop.schedule(changes); }; + + this.updateWidth = function() { + var size = this.model.size; + + var changes = this.CHANGE_SIZE; + + this.scroller.style.left = size.gutterWidth + "px"; + this.scroller.style.width = size.scrollerWidth + "px"; + + // TODO refactor + // move to the model + if (this.model.buffer.getUseWrapMode() && this.adjustWrapLimit()) + changes = changes | this.CHANGE_FULL; + + this.$loop.schedule(changes); + } this.adjustWrapLimit = function(){ var availableWidth = this.model.size.scrollerWidth - this.model.padding * 2; @@ -238,7 +240,7 @@ var WindowView = function(windowModel, container) { this.updateShowGutter = function(){ this.$gutter.style.display = this.model.showGutter ? "block" : "none"; - this.onResize(true); + this._emit("resize"); }; this.updatePrintMargin = function() { @@ -302,7 +304,7 @@ var WindowView = function(windowModel, container) { this.updateCharacterSize = function() { this.updatePrintMargin(); - this.onResize(true); + this._emit("resize"); this.$loop.schedule(this.CHANGE_FULL); }; @@ -449,7 +451,7 @@ var WindowView = function(windowModel, container) { // Horizontal scrollbar visibility may have changed, which changes // the client height of the scroller if (horizScrollChanged) - this.onResize(true); + this._emit("resize"); }; this.$updateLines = function() { @@ -619,7 +621,7 @@ var WindowView = function(windowModel, container) { // force re-measure of the gutter width if (this.model.size) { this.model.size.width = 0; - this.onResize(); + this._emit("resize"); } }; diff --git a/lib/ace/view/window_view_mock.js b/lib/ace/view/window_view_mock.js index fb0f3220..d93a9d93 100644 --- a/lib/ace/view/window_view_mock.js +++ b/lib/ace/view/window_view_mock.js @@ -98,11 +98,4 @@ WindowViewMock.prototype.getSession = function(session) { WindowViewMock.prototype[name] = function() {}; }) -WindowViewMock.prototype.textToScreenCoordinates = function() { - return { - pageX: 0, - pageY: 0 - } -}; - }); diff --git a/lib/ace/window_controller.js b/lib/ace/window_controller.js index f30e7267..4303532e 100644 --- a/lib/ace/window_controller.js +++ b/lib/ace/window_controller.js @@ -52,16 +52,24 @@ var WindowController = exports.WindowController = function(model, view) { model.on("changeCharacterSize", view.updateCharacterSize.bind(view)); model.on("changeScrollLeft", view.updateScrollLeft.bind(view)); model.on("changeScrollTop", view.updateScrollTop.bind(view)); + model.on("changeWidth", view.updateWidth.bind(view)); + model.on("changeHeight", view.updateHeight.bind(view)); model.on("changeSelectionStyle", this.onSelectionChange.bind(this)); model.on("changeHighlightActiveLine", this._updateHighlightActiveLine.bind(this)); model.on("changeHighlightSelectedWord", this.onChangeHighlightSelectedWord.bind(this)); model.on("changeBuffer", this.onChangeBuffer.bind(this)); + + view.on("resize", this.resize.bind(this)); }; (function() { + this.resize = function() { + this.model.setSizes(this.view.measureSizes()); + }; + this.onChangeHighlightSelectedWord = function() { var buffer = this.model.buffer; if (this.model.shouldHighlight) @@ -193,9 +201,7 @@ var WindowController = exports.WindowController = function(model, view) { this.onCursorChange = function(e) { this.view.updateCursor(); - - if (!this._blockScrolling) - this.model.scrollCursorIntoView(); + this.model.scrollCursorIntoView(); // move text input over the cursor // this is required for iOS and IME