Fix problems when scolling + wrapped lines
This commit is contained in:
parent
c30b9ece8f
commit
609f062803
5 changed files with 82 additions and 20 deletions
|
|
@ -772,10 +772,14 @@ var Document = function(text, mode) {
|
||||||
this.$useWrapMode = false;
|
this.$useWrapMode = false;
|
||||||
this.setUseWrapMode = function(useWrapMode) {
|
this.setUseWrapMode = function(useWrapMode) {
|
||||||
var _self = this;
|
var _self = this;
|
||||||
|
|
||||||
function computeWrapData(e) {
|
function computeWrapData(e) {
|
||||||
var lines = _self.lines, wrapData = _self.$wrapData;
|
var lines = _self.lines, wrapData = _self.$wrapData;
|
||||||
var wrapLimit = _self.$wrapLimit;
|
var wrapLimit = _self.$wrapLimit;
|
||||||
|
|
||||||
|
// Remove lines that are no longer there.
|
||||||
|
wrapData.splice(lines.length, wrapData.length - lines.length);
|
||||||
|
|
||||||
if (!e.data.lastRow) {
|
if (!e.data.lastRow) {
|
||||||
e.data.lastRow = _self.lines.length - 1;
|
e.data.lastRow = _self.lines.length - 1;
|
||||||
}
|
}
|
||||||
|
|
@ -857,6 +861,21 @@ var Document = function(text, mode) {
|
||||||
return docColumn;
|
return docColumn;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.screenToDocumentRow = function(row) {
|
||||||
|
if (!this.$useWrapMode) {
|
||||||
|
return row;
|
||||||
|
}
|
||||||
|
|
||||||
|
var wrapData = this.$wrapData, linesCount = this.lines.length;
|
||||||
|
var docRow = 0;
|
||||||
|
while (docRow < linesCount && row >= wrapData[docRow].length + 1) {
|
||||||
|
row -= wrapData[docRow].length + 1;
|
||||||
|
docRow ++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return docRow;
|
||||||
|
};
|
||||||
|
|
||||||
this.screenToDocumentPosition = function(row, column) {
|
this.screenToDocumentPosition = function(row, column) {
|
||||||
if (!this.$useWrapMode) {
|
if (!this.$useWrapMode) {
|
||||||
return {
|
return {
|
||||||
|
|
@ -903,7 +922,7 @@ var Document = function(text, mode) {
|
||||||
return screenColumn;
|
return screenColumn;
|
||||||
};
|
};
|
||||||
|
|
||||||
this.documentToScreenPosition = function(row, column) {
|
this.documentToScreenRow = function(row) {
|
||||||
if (!this.$useWrapMode) {
|
if (!this.$useWrapMode) {
|
||||||
return {
|
return {
|
||||||
row: row,
|
row: row,
|
||||||
|
|
@ -919,19 +938,27 @@ var Document = function(text, mode) {
|
||||||
for (row = 0; row < wrapData.length; row ++) {
|
for (row = 0; row < wrapData.length; row ++) {
|
||||||
screenRow += wrapData[row].length + 1;
|
screenRow += wrapData[row].length + 1;
|
||||||
}
|
}
|
||||||
return {
|
return screenRow;
|
||||||
row: screenRow,
|
|
||||||
column: 0
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var i = 0; i < row; i++) {
|
for (var i = 0; i < row; i++) {
|
||||||
screenRow += wrapData[i].length + 1;
|
screenRow += wrapData[i].length + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return screenRow;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.documentToScreenPosition = function(row, column) {
|
||||||
|
if (!this.$useWrapMode) {
|
||||||
|
return {
|
||||||
|
row: row,
|
||||||
|
column: this.documentToScreenColumn(row, column)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var screenRow = this.documentToScreenRow(row);
|
||||||
var screenColumn = column;
|
var screenColumn = column;
|
||||||
var wrapRowData = wrapData[row];
|
var wrapRowData = this.$wrapData[row];
|
||||||
for (var split = 0; split < wrapRowData.length; split++) {
|
for (var split = 0; wrapRowData && split < wrapRowData.length; split++) {
|
||||||
if (column > wrapRowData[split]) {
|
if (column > wrapRowData[split]) {
|
||||||
screenColumn = column - wrapRowData[split];
|
screenColumn = column - wrapRowData[split];
|
||||||
screenRow ++;
|
screenRow ++;
|
||||||
|
|
@ -946,6 +973,18 @@ var Document = function(text, mode) {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.getScreenLength = function() {
|
||||||
|
if (!this.$useWrapMode) {
|
||||||
|
return this.getLength();
|
||||||
|
}
|
||||||
|
|
||||||
|
var screenRows = 0;
|
||||||
|
for (var row = 0; row < this.$wrapData.length; row++) {
|
||||||
|
screenRows += this.$wrapData[row].length + 1;
|
||||||
|
}
|
||||||
|
return screenRows;
|
||||||
|
}
|
||||||
|
|
||||||
}).call(Document.prototype)
|
}).call(Document.prototype)
|
||||||
|
|
||||||
exports.Document = Document;
|
exports.Document = Document;
|
||||||
|
|
|
||||||
|
|
@ -100,7 +100,7 @@ var Cursor = function(parentEl) {
|
||||||
}, 1000);
|
}, 1000);
|
||||||
};
|
};
|
||||||
|
|
||||||
this.getPixelPosition = function() {
|
this.getPixelPosition = function(onScreen) {
|
||||||
if (!this.config || !this.position) {
|
if (!this.config || !this.position) {
|
||||||
return {
|
return {
|
||||||
left : 0,
|
left : 0,
|
||||||
|
|
@ -111,7 +111,8 @@ var Cursor = function(parentEl) {
|
||||||
var pos = this.doc.documentToScreenPosition(this.position.row,
|
var pos = this.doc.documentToScreenPosition(this.position.row,
|
||||||
this.position.column);
|
this.position.column);
|
||||||
var cursorLeft = Math.round(pos.column * this.config.characterWidth);
|
var cursorLeft = Math.round(pos.column * this.config.characterWidth);
|
||||||
var cursorTop = pos.row * this.config.lineHeight;
|
var cursorTop = (pos.row - (onScreen ? this.config.firstRowScreen : 0)) *
|
||||||
|
this.config.lineHeight;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
left : cursorLeft,
|
left : cursorLeft,
|
||||||
|
|
@ -125,7 +126,7 @@ var Cursor = function(parentEl) {
|
||||||
|
|
||||||
this.config = config;
|
this.config = config;
|
||||||
|
|
||||||
this.pixelPos = this.getPixelPosition();
|
this.pixelPos = this.getPixelPosition(true);
|
||||||
|
|
||||||
this.cursor.style.left = this.pixelPos.left + "px";
|
this.cursor.style.left = this.pixelPos.left + "px";
|
||||||
this.cursor.style.top = this.pixelPos.top + "px";
|
this.cursor.style.top = this.pixelPos.top + "px";
|
||||||
|
|
|
||||||
|
|
@ -102,6 +102,10 @@ var Marker = function(parentEl) {
|
||||||
this.element.innerHTML = html.join("");
|
this.element.innerHTML = html.join("");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.$getTop = function(row, layerConfig) {
|
||||||
|
return (row - layerConfig.firstRowScreen) * layerConfig.lineHeight;
|
||||||
|
};
|
||||||
|
|
||||||
this.drawTextMarker = function(stringBuilder, range, clazz, layerConfig) {
|
this.drawTextMarker = function(stringBuilder, range, clazz, layerConfig) {
|
||||||
// selection start
|
// selection start
|
||||||
var row = range.start.row;
|
var row = range.start.row;
|
||||||
|
|
@ -125,7 +129,7 @@ var Marker = function(parentEl) {
|
||||||
// from selection start to the end of the line
|
// from selection start to the end of the line
|
||||||
var height = layerConfig.lineHeight;
|
var height = layerConfig.lineHeight;
|
||||||
var width = Math.round(layerConfig.width - (range.start.column * layerConfig.characterWidth));
|
var width = Math.round(layerConfig.width - (range.start.column * layerConfig.characterWidth));
|
||||||
var top = (range.start.row - layerConfig.firstRow) * layerConfig.lineHeight;
|
var top = this.$getTop(range.start.row, layerConfig);
|
||||||
var left = Math.round(range.start.column * layerConfig.characterWidth);
|
var left = Math.round(range.start.column * layerConfig.characterWidth);
|
||||||
|
|
||||||
stringBuilder.push(
|
stringBuilder.push(
|
||||||
|
|
@ -137,7 +141,7 @@ var Marker = function(parentEl) {
|
||||||
);
|
);
|
||||||
|
|
||||||
// from start of the last line to the selection end
|
// from start of the last line to the selection end
|
||||||
var top = (range.end.row - layerConfig.firstRow) * layerConfig.lineHeight;
|
var top = this.$getTop(range.start.end, layerConfig);
|
||||||
var width = Math.round(range.end.column * layerConfig.characterWidth);
|
var width = Math.round(range.end.column * layerConfig.characterWidth);
|
||||||
|
|
||||||
stringBuilder.push(
|
stringBuilder.push(
|
||||||
|
|
@ -151,7 +155,7 @@ var Marker = function(parentEl) {
|
||||||
var height = (range.end.row - range.start.row - 1) * layerConfig.lineHeight;
|
var height = (range.end.row - range.start.row - 1) * layerConfig.lineHeight;
|
||||||
if (height < 0)
|
if (height < 0)
|
||||||
return;
|
return;
|
||||||
var top = (range.start.row + 1 - layerConfig.firstRow) * layerConfig.lineHeight;
|
var top = this.$getTop(range.start.row + 1, layerConfig);
|
||||||
|
|
||||||
stringBuilder.push(
|
stringBuilder.push(
|
||||||
"<div class='", clazz, "' style='",
|
"<div class='", clazz, "' style='",
|
||||||
|
|
@ -164,7 +168,7 @@ var Marker = function(parentEl) {
|
||||||
this.drawSingleLineMarker = function(stringBuilder, range, clazz, layerConfig) {
|
this.drawSingleLineMarker = function(stringBuilder, range, clazz, layerConfig) {
|
||||||
var height = layerConfig.lineHeight;
|
var height = layerConfig.lineHeight;
|
||||||
var width = Math.round((range.end.column - range.start.column) * layerConfig.characterWidth);
|
var width = Math.round((range.end.column - range.start.column) * layerConfig.characterWidth);
|
||||||
var top = (range.start.row - layerConfig.firstRow) * layerConfig.lineHeight;
|
var top = this.$getTop(range.start.row, layerConfig);
|
||||||
var left = Math.round(range.start.column * layerConfig.characterWidth);
|
var left = Math.round(range.start.column * layerConfig.characterWidth);
|
||||||
|
|
||||||
stringBuilder.push(
|
stringBuilder.push(
|
||||||
|
|
|
||||||
|
|
@ -96,15 +96,15 @@ var Text = function(parentEl) {
|
||||||
if (!this.$measureNode) {
|
if (!this.$measureNode) {
|
||||||
var measureNode = this.$measureNode = document.createElement("div");
|
var measureNode = this.$measureNode = document.createElement("div");
|
||||||
var style = measureNode.style;
|
var style = measureNode.style;
|
||||||
|
|
||||||
style.width = style.height = "auto";
|
style.width = style.height = "auto";
|
||||||
style.left = style.top = "-1000px";
|
style.left = style.top = "-1000px";
|
||||||
|
|
||||||
style.visibility = "hidden";
|
style.visibility = "hidden";
|
||||||
style.position = "absolute";
|
style.position = "absolute";
|
||||||
style.overflow = "visible";
|
style.overflow = "visible";
|
||||||
style.whiteSpace = "nowrap";
|
style.whiteSpace = "nowrap";
|
||||||
|
|
||||||
// in FF 3.6 monospace fonts can have a fixed sub pixel width.
|
// in FF 3.6 monospace fonts can have a fixed sub pixel width.
|
||||||
// that's why we have to measure many characters
|
// that's why we have to measure many characters
|
||||||
// Note: characterWidth can be a float!
|
// Note: characterWidth can be a float!
|
||||||
|
|
@ -151,6 +151,12 @@ var Text = function(parentEl) {
|
||||||
this.updateLines = function(config, firstRow, lastRow) {
|
this.updateLines = function(config, firstRow, lastRow) {
|
||||||
console.log("layer.text.updateLines", firstRow, lastRow);
|
console.log("layer.text.updateLines", firstRow, lastRow);
|
||||||
this.$computeTabString();
|
this.$computeTabString();
|
||||||
|
// Due to wrap line changes there can be new lines if e.g.
|
||||||
|
// the line to updated wrapped in the meantime.
|
||||||
|
if (this.config.lastRow != config.lastRow ||
|
||||||
|
this.config.firstRow != config.firstRow) {
|
||||||
|
this.scrollLines(config);
|
||||||
|
}
|
||||||
this.config = config;
|
this.config = config;
|
||||||
|
|
||||||
var first = Math.max(firstRow, config.firstRow);
|
var first = Math.max(firstRow, config.firstRow);
|
||||||
|
|
@ -273,6 +279,7 @@ var Text = function(parentEl) {
|
||||||
};
|
};
|
||||||
|
|
||||||
this.$renderLine = function(stringBuilder, row, tokens) {
|
this.$renderLine = function(stringBuilder, row, tokens) {
|
||||||
|
console.log("layer.text.renderLine", row);
|
||||||
// if (this.$showInvisibles) {
|
// if (this.$showInvisibles) {
|
||||||
// var self = this;
|
// var self = this;
|
||||||
// var spaceRe = /[\v\f \u00a0\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u2028\u2029\u3000]+/g;
|
// var spaceRe = /[\v\f \u00a0\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u2028\u2029\u3000]+/g;
|
||||||
|
|
|
||||||
|
|
@ -326,7 +326,7 @@ var VirtualRenderer = function(container, theme) {
|
||||||
};
|
};
|
||||||
|
|
||||||
this.$updateScrollBar = function() {
|
this.$updateScrollBar = function() {
|
||||||
this.scrollBar.setInnerHeight(this.doc.getLength() * this.lineHeight);
|
this.scrollBar.setInnerHeight(this.doc.getScreenLength() * this.lineHeight);
|
||||||
this.scrollBar.setScrollTop(this.scrollTop);
|
this.scrollBar.setScrollTop(this.scrollTop);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -399,12 +399,23 @@ var VirtualRenderer = function(container, theme) {
|
||||||
|
|
||||||
var lineCount = Math.ceil(minHeight / this.lineHeight);
|
var lineCount = Math.ceil(minHeight / this.lineHeight);
|
||||||
var firstRow = Math.max(0, Math.round((this.scrollTop - offset) / this.lineHeight));
|
var firstRow = Math.max(0, Math.round((this.scrollTop - offset) / this.lineHeight));
|
||||||
var lastRow = Math.max(0, Math.min(this.lines.length, firstRow + lineCount) - 1);
|
//var lastRow = Math.max(0, Math.min(this.lines.length, firstRow + lineCount) - 1);
|
||||||
|
var lastRow = firstRow + lineCount - 1;
|
||||||
|
|
||||||
|
// Add support for wrapped lines.
|
||||||
|
var lineHeight = { lineHeight: this.lineHeight };
|
||||||
|
firstRow = this.doc.screenToDocumentRow(firstRow);
|
||||||
|
var firstRowScreen = this.doc.documentToScreenRow(firstRow);
|
||||||
|
lastRow = Math.min(this.doc.screenToDocumentRow(lastRow), this.doc.lines.length - 1);
|
||||||
|
offset = this.scrollTop % this.doc.getRowHeight(lineHeight, firstRow);
|
||||||
|
console.log("renderer.computeLayerConfig", firstRow, lastRow);
|
||||||
|
|
||||||
|
|
||||||
var layerConfig = this.layerConfig = {
|
var layerConfig = this.layerConfig = {
|
||||||
width : longestLine,
|
width : longestLine,
|
||||||
padding : this.$padding,
|
padding : this.$padding,
|
||||||
firstRow : firstRow,
|
firstRow : firstRow,
|
||||||
|
firstRowScreen: firstRowScreen,
|
||||||
lastRow : lastRow,
|
lastRow : lastRow,
|
||||||
lineHeight : this.lineHeight,
|
lineHeight : this.lineHeight,
|
||||||
characterWidth : this.characterWidth,
|
characterWidth : this.characterWidth,
|
||||||
|
|
@ -542,7 +553,7 @@ var VirtualRenderer = function(container, theme) {
|
||||||
};
|
};
|
||||||
|
|
||||||
this.scrollToY = function(scrollTop) {
|
this.scrollToY = function(scrollTop) {
|
||||||
var maxHeight = this.lines.length * this.lineHeight - this.$size.scrollerHeight;
|
var maxHeight = this.doc.getScreenLength() * this.lineHeight - this.$size.scrollerHeight;
|
||||||
var scrollTop = Math.max(0, Math.min(maxHeight, scrollTop));
|
var scrollTop = Math.max(0, Math.min(maxHeight, scrollTop));
|
||||||
|
|
||||||
if (this.scrollTop !== scrollTop) {
|
if (this.scrollTop !== scrollTop) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue