extract rendering code
This commit is contained in:
parent
7e462ec372
commit
bf3266a83a
1 changed files with 181 additions and 121 deletions
300
editor.html
300
editor.html
|
|
@ -11,14 +11,18 @@
|
||||||
|
|
||||||
#container {
|
#container {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
border: 1px solid black;
|
||||||
width: 600px;
|
width: 600px;
|
||||||
|
height: 400px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#canvas {
|
#container.focus {
|
||||||
|
border: 1px solid #327fbd;;
|
||||||
|
}
|
||||||
|
|
||||||
|
.canvas {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
border: 1px solid black;
|
width: 600px;
|
||||||
margin: 4px;
|
|
||||||
width: 590px;
|
|
||||||
height: 400px;
|
height: 400px;
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
|
@ -26,10 +30,6 @@
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
#canvas.focus {
|
|
||||||
border: 1px solid #327fbd;;
|
|
||||||
}
|
|
||||||
|
|
||||||
.composition {
|
.composition {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
|
|
@ -55,7 +55,6 @@
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
<div id="container">
|
<div id="container">
|
||||||
<div id="canvas"></div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script type="text/javascript" charset="utf-8">
|
<script type="text/javascript" charset="utf-8">
|
||||||
|
|
@ -229,13 +228,13 @@ function KeyBinding(element, host)
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
function Editor(containerId, canvasId)
|
function Editor(renderer)
|
||||||
{
|
{
|
||||||
this.container = document.getElementById(containerId);
|
var container = renderer.getContainerElement();
|
||||||
this.canvas = document.getElementById(canvasId);
|
this.renderer = renderer;
|
||||||
|
|
||||||
var textInput = new TextInput(container, this);
|
var textInput = new TextInput(container, this);
|
||||||
new KeyBinding(this.container, this);
|
new KeyBinding(container, this);
|
||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
addListener(container, "mousedown", function(e) {
|
addListener(container, "mousedown", function(e) {
|
||||||
|
|
@ -244,16 +243,6 @@ function Editor(containerId, canvasId)
|
||||||
return preventDefault(e);
|
return preventDefault(e);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.measureSizes();
|
|
||||||
|
|
||||||
this.composition = document.createElement("div");
|
|
||||||
this.composition.className = "composition";
|
|
||||||
this.composition.style.height = this.lineHeight + "px";
|
|
||||||
|
|
||||||
this.cursor = document.createElement("div");
|
|
||||||
this.cursor.className = "cursor";
|
|
||||||
this.cursor.style.height = this.lineHeight + "px";
|
|
||||||
|
|
||||||
this.row = 0;
|
this.row = 0;
|
||||||
this.col = 0;
|
this.col = 0;
|
||||||
this.lines = [""];
|
this.lines = [""];
|
||||||
|
|
@ -261,103 +250,30 @@ function Editor(containerId, canvasId)
|
||||||
this.draw();
|
this.draw();
|
||||||
}
|
}
|
||||||
|
|
||||||
Editor.prototype = {
|
Editor.prototype =
|
||||||
|
{
|
||||||
measureSizes : function()
|
|
||||||
{
|
|
||||||
var measureNode = document.createElement("div");
|
|
||||||
var style = measureNode.style;
|
|
||||||
style.width = style.height = "auto";
|
|
||||||
style.left = style.top = "-1000px";
|
|
||||||
style.visibility = "hidden";
|
|
||||||
style.position = "absolute";
|
|
||||||
style.overflow = "visible";
|
|
||||||
|
|
||||||
measureNode.innerHTML = "X<br>X";
|
|
||||||
this.canvas.appendChild(measureNode);
|
|
||||||
|
|
||||||
this.lineHeight = Math.round(measureNode.offsetHeight / 2);
|
|
||||||
this.characterWidth = measureNode.offsetWidth;
|
|
||||||
|
|
||||||
this.canvas.removeChild(measureNode);
|
|
||||||
},
|
|
||||||
|
|
||||||
draw : function()
|
draw : function()
|
||||||
{
|
{
|
||||||
if (this.cursor.parentNode) {
|
this.renderer.draw(this.lines);
|
||||||
this.canvas.removeChild(this.cursor);
|
this.renderer.updateCursor(this.row, this.col);
|
||||||
}
|
|
||||||
|
|
||||||
// TODO HACK
|
|
||||||
var longestLine = this.canvas.clientWidth;
|
|
||||||
for (var i=0; i < this.lines.length; i++) {
|
|
||||||
longestLine = Math.max(longestLine, (this.lines[i].length * this.characterWidth));
|
|
||||||
}
|
|
||||||
|
|
||||||
var html = [];
|
|
||||||
for (var i=0; i < this.lines.length; i++)
|
|
||||||
{
|
|
||||||
html.push(
|
|
||||||
"<div class='line ",
|
|
||||||
i % 2 == 0 ? "even" : "odd",
|
|
||||||
"' style='height:" + this.lineHeight + "px;",
|
|
||||||
"width:", longestLine, "px'>",
|
|
||||||
this.lines[i].
|
|
||||||
replace(/&/g, "&").
|
|
||||||
replace(/</g, "<").
|
|
||||||
replace(/\s/g, " "),
|
|
||||||
"</div>"
|
|
||||||
);
|
|
||||||
};
|
|
||||||
this.canvas.innerHTML = html.join("");
|
|
||||||
|
|
||||||
this.updateCursor();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
updateCursor : function()
|
updateCursor : function() {
|
||||||
{
|
this.renderer.updateCursor(this.row, this.col);
|
||||||
var left = this.col * this.characterWidth;
|
|
||||||
var top = this.row * this.lineHeight;
|
|
||||||
|
|
||||||
this.cursor.style.left = left + "px";
|
|
||||||
this.cursor.style.top = top + "px";
|
|
||||||
|
|
||||||
this.canvas.appendChild(this.cursor);
|
|
||||||
|
|
||||||
if (this.canvas.scrollLeft > left) {
|
|
||||||
this.canvas.scrollLeft = left;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.canvas.scrollLeft + this.canvas.clientWidth < left + this.characterWidth) {
|
|
||||||
this.canvas.scrollLeft = left + this.characterWidth - this.canvas.clientWidth;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.canvas.scrollTop > top) {
|
|
||||||
this.canvas.scrollTop = top;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.canvas.scrollTop + this.canvas.clientHeight < top + this.lineHeight) {
|
|
||||||
this.canvas.scrollTop = top + this.lineHeight - this.canvas.clientHeight;
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
onFocus : function() {
|
onFocus : function() {
|
||||||
this.canvas.className = "focus";
|
this.renderer.visualizeFocus();
|
||||||
},
|
},
|
||||||
|
|
||||||
onBlur : function() {
|
onBlur : function() {
|
||||||
this.canvas.className = "";
|
this.renderer.visualizeBlur();
|
||||||
},
|
},
|
||||||
|
|
||||||
placeCursorToMouse : function(pageX, pageY)
|
placeCursorToMouse : function(pageX, pageY)
|
||||||
{
|
{
|
||||||
var canvas = this.canvas;
|
var pos = this.renderer.screenToTextCoordinates(pageX, pageY);
|
||||||
var canvasPos = canvas.getBoundingClientRect();
|
this.moveTo(pos.row, pos.column);
|
||||||
|
|
||||||
var row = Math.floor((pageY + canvas.scrollTop - canvasPos.top) / this.lineHeight);
|
|
||||||
var col = Math.floor((pageX + canvas.scrollLeft - canvasPos.left) / this.characterWidth);
|
|
||||||
|
|
||||||
this.moveTo(row, col)
|
|
||||||
},
|
},
|
||||||
|
|
||||||
onTextInput: function(text)
|
onTextInput: function(text)
|
||||||
|
|
@ -443,25 +359,16 @@ Editor.prototype = {
|
||||||
|
|
||||||
onCompositionStart : function()
|
onCompositionStart : function()
|
||||||
{
|
{
|
||||||
setText(this.composition, "");
|
this.renderer.showComposition(this.row, this.col);
|
||||||
|
|
||||||
this.composition.style.left = (this.col * this.characterWidth) + "px";
|
|
||||||
this.composition.style.top = (this.row * this.lineHeight) + "px";
|
|
||||||
|
|
||||||
this.onTextInput(" ");
|
this.onTextInput(" ");
|
||||||
|
|
||||||
this.canvas.appendChild(this.composition);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
onCompositionUpdate : function(text) {
|
onCompositionUpdate : function(text) {
|
||||||
setText(this.composition, text);
|
this.renderer.setCompositionText(text);
|
||||||
},
|
},
|
||||||
|
|
||||||
onCompositionEnd : function() {
|
onCompositionEnd : function() {
|
||||||
if (this.composition.parentNode) {
|
this.renderer.hideComposition();
|
||||||
this.canvas.removeChild(this.composition);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.removeLeft();
|
this.removeLeft();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -473,7 +380,8 @@ Editor.prototype = {
|
||||||
this.moveBy(1, 0);
|
this.moveBy(1, 0);
|
||||||
},
|
},
|
||||||
|
|
||||||
moveLeft : function() {
|
moveLeft : function()
|
||||||
|
{
|
||||||
if (this.col == 0) {
|
if (this.col == 0) {
|
||||||
if (this.row > 0) {
|
if (this.row > 0) {
|
||||||
this.moveTo(this.row-1, this.lines[this.row-1].length);
|
this.moveTo(this.row-1, this.lines[this.row-1].length);
|
||||||
|
|
@ -513,7 +421,159 @@ Editor.prototype = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
new Editor("container", "canvas", "text");
|
|
||||||
|
function DumbRenderer(containerId)
|
||||||
|
{
|
||||||
|
this.container = document.getElementById(containerId);
|
||||||
|
this.canvas = document.createElement("div");
|
||||||
|
this.canvas.className = "canvas";
|
||||||
|
this.container.appendChild(this.canvas);
|
||||||
|
|
||||||
|
this._measureSizes();
|
||||||
|
|
||||||
|
this.composition = document.createElement("div");
|
||||||
|
this.composition.className = "composition";
|
||||||
|
this.composition.style.height = this.lineHeight + "px";
|
||||||
|
|
||||||
|
this.cursor = document.createElement("div");
|
||||||
|
this.cursor.className = "cursor";
|
||||||
|
this.cursor.style.height = this.lineHeight + "px";
|
||||||
|
}
|
||||||
|
|
||||||
|
DumbRenderer.prototype =
|
||||||
|
{
|
||||||
|
getContainerElement : function() {
|
||||||
|
return this.container;
|
||||||
|
},
|
||||||
|
|
||||||
|
_measureSizes : function()
|
||||||
|
{
|
||||||
|
var measureNode = document.createElement("div");
|
||||||
|
var style = measureNode.style;
|
||||||
|
style.width = style.height = "auto";
|
||||||
|
style.left = style.top = "-1000px";
|
||||||
|
style.visibility = "hidden";
|
||||||
|
style.position = "absolute";
|
||||||
|
style.overflow = "visible";
|
||||||
|
|
||||||
|
measureNode.innerHTML = "X<br>X";
|
||||||
|
this.canvas.appendChild(measureNode);
|
||||||
|
|
||||||
|
this.lineHeight = Math.round(measureNode.offsetHeight / 2);
|
||||||
|
this.characterWidth = measureNode.offsetWidth;
|
||||||
|
|
||||||
|
this.canvas.removeChild(measureNode);
|
||||||
|
},
|
||||||
|
|
||||||
|
draw : function(lines)
|
||||||
|
{
|
||||||
|
// TODO HACK
|
||||||
|
var longestLine = this.canvas.clientWidth;
|
||||||
|
for (var i=0; i < lines.length; i++) {
|
||||||
|
longestLine = Math.max(longestLine, (lines[i].length * this.characterWidth));
|
||||||
|
}
|
||||||
|
|
||||||
|
var html = [];
|
||||||
|
for (var i=0; i < lines.length; i++)
|
||||||
|
{
|
||||||
|
html.push(
|
||||||
|
"<div class='line ",
|
||||||
|
i % 2 == 0 ? "even" : "odd",
|
||||||
|
"' style='height:" + this.lineHeight + "px;",
|
||||||
|
"width:", longestLine, "px'>",
|
||||||
|
lines[i].
|
||||||
|
replace(/&/g, "&").
|
||||||
|
replace(/</g, "<").
|
||||||
|
replace(/\s/g, " "),
|
||||||
|
"</div>"
|
||||||
|
);
|
||||||
|
};
|
||||||
|
this.canvas.innerHTML = html.join("");
|
||||||
|
|
||||||
|
this.canvas.appendChild(this.cursor)
|
||||||
|
},
|
||||||
|
|
||||||
|
updateCursor : function(row, column)
|
||||||
|
{
|
||||||
|
var left = column * this.characterWidth;
|
||||||
|
var top = row * this.lineHeight;
|
||||||
|
|
||||||
|
this.cursor.style.left = left + "px";
|
||||||
|
this.cursor.style.top = top + "px";
|
||||||
|
|
||||||
|
this.canvas.appendChild(this.cursor);
|
||||||
|
|
||||||
|
if (this.canvas.scrollLeft > left) {
|
||||||
|
this.canvas.scrollLeft = left;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.canvas.scrollLeft + this.canvas.clientWidth < left + this.characterWidth) {
|
||||||
|
this.canvas.scrollLeft = left + this.characterWidth - this.canvas.clientWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.canvas.scrollTop > top) {
|
||||||
|
this.canvas.scrollTop = top;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.canvas.scrollTop + this.canvas.clientHeight < top + this.lineHeight) {
|
||||||
|
this.canvas.scrollTop = top + this.lineHeight - this.canvas.clientHeight;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
screenToTextCoordinates : function(pageX, pageY)
|
||||||
|
{
|
||||||
|
var canvas = this.canvas;
|
||||||
|
var canvasPos = canvas.getBoundingClientRect();
|
||||||
|
|
||||||
|
if (pageX < canvasPos.left || pageX > canvasPos.right) {
|
||||||
|
row = null;
|
||||||
|
} else {
|
||||||
|
var row = Math.floor((pageY + canvas.scrollTop - canvasPos.top) / this.lineHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pageY < canvasPos.top || pageY > canvasPos.bottom) {
|
||||||
|
col = null;
|
||||||
|
} else {
|
||||||
|
var col = Math.floor((pageX + canvas.scrollLeft - canvasPos.left) / this.characterWidth);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
row: row,
|
||||||
|
column: col
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
visualizeFocus : function() {
|
||||||
|
this.container.className = "focus";
|
||||||
|
},
|
||||||
|
|
||||||
|
visualizeBlur : function() {
|
||||||
|
this.container.className = "";
|
||||||
|
},
|
||||||
|
|
||||||
|
showComposition : function(row, column)
|
||||||
|
{
|
||||||
|
setText(this.composition, "");
|
||||||
|
|
||||||
|
this.composition.style.left = (column * this.characterWidth+1) + "px";
|
||||||
|
this.composition.style.top = (row * this.lineHeight+1) + "px";
|
||||||
|
|
||||||
|
this.container.appendChild(this.composition);
|
||||||
|
},
|
||||||
|
|
||||||
|
setCompositionText : function(text) {
|
||||||
|
setText(this.composition, text);
|
||||||
|
},
|
||||||
|
|
||||||
|
hideComposition : function() {
|
||||||
|
if (this.composition.parentNode) {
|
||||||
|
this.container.removeChild(this.composition);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
new Editor(new DumbRenderer("container"), "text");
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue