Skip to content

Commit

Permalink
Editor document fix getLines, line heihgt parity and json editor fix
Browse files Browse the repository at this point in the history
  • Loading branch information
Zekiah-A committed Mar 25, 2024
1 parent 77cb718 commit f45c5fc
Show file tree
Hide file tree
Showing 2 changed files with 118 additions and 76 deletions.
20 changes: 18 additions & 2 deletions Other/editor-test.html
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,12 @@ <h1>Editor Rendering lab</h1>
<option value="dark">Dark mode</option>
</select>
<button type="button" onclick="
const canvasStart = performance.now()
editor.renderCanvasData(editorCanvas)
console.log('Canvas render took:', performance.now() - canvasStart + 'ms')
const htmlStart = performance.now()
editor.renderHtmlData(editorHtml)
console.log('HTML render took:', performance.now() - htmlStart + 'ms')
">Re-render outputs</button>
<label>Editor views ABAB:</label>
<input type="range" min="0" max="1" value="1" oninput="
Expand All @@ -81,7 +85,7 @@ <h1>Editor Rendering lab</h1>
<div class="test">
<h4 style="margin: 0px;">Canvas Output:</h4>
<hr>
<canvas id="editorCanvas" width="1024"></canvas>
<canvas id="editorCanvas" height="360" width="1020"></canvas>
</div>
<div id="editorHtmlContainer" class="test">
<h4 style="margin: 0px;">Html Output:</h4>
Expand All @@ -92,7 +96,19 @@ <h4 style="margin: 0px;">Html Output:</h4>
<div class="test">
<h4 style="margin: 0px;">Live JSON data editor:</h4>
<hr>
<textarea id="editorJsonEdit" style="width: 680px; height: 360px;" placeholder="No test data available"></textarea>
<textarea id="editorJsonEdit" oninput="
try {
const newData = JSON.parse(this.value)
editor.data = newData
editor.renderCanvasData(editorCanvas)
editor.renderHtmlData(editorHtml)
editorJsonEdit.style.border = '1px solid green'
}
catch (e) {
console.log(e)
editorJsonEdit.style.border = '1px solid red'
}
" style="width: 680px; height: 360px;" placeholder="No test data available"></textarea>
</div>
</div>
</body>
Expand Down
174 changes: 100 additions & 74 deletions editor-document.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,85 +78,84 @@ class EditorDocument {
this.scale = scale
// These will be used at the root level or when no styles are specified by a document fragment
this.fontSize = fontSize * scale
this.colourHex = colourHex
}

renderHtmlData(root) {
root.innerHTML = ""
this.formatToHtml(root)
this.colourHex = colourHex || "#000"
}

formatToHtml(baseElement=document.createElement("div")) {
const root = baseElement
renderHtmlData(root) {
root.innerHTML = ""
this.formatToHtml(root)
}

formatToHtml(baseElement=document.createElement("div")) {
const root = baseElement
root.style.overflow = "hidden"
const cssFontSize = this.fontSize / this.scale

function renderFragment(data, html, _this) {
switch (data.type) {
case "fragment": {
const fragment = document.createElement("span")
fragment.style.lineHeight = 1
fragment.style.fontSize = `${cssFontSize}px`
fragment.style.fontFamily = "Arial, Helvetica, sans-serif"

for (let i = 0; i < data.styles.length; i++) {
const style = data.styles[i]
switch (style.type) {
case "bold":
fragment.style.fontWeight = "bold"
break
case "italic":
fragment.style.fontStyle = "italic"
break
case "colour":
fragment.style.color = style.hex
break
case "font":
fragment.style.fontFamily = style.name
fragment.style.fontSize = style.size
break
}
html.appendChild(fragment)
}

for (let i = 0; i < data.children.length; i++) {
const child = data.children[i]
renderFragment(child, fragment, _this)
}
html.appendChild(fragment)
break
}
case "text": {
const text = document.createElement("span")
text.textContent = data.content
html.appendChild(text)
break
}
case "newline": {
for (let i = 0; i < data.count; i++) {
const newLine = document.createElement("br")
html.appendChild(newLine)
}
break
}
}
}

function renderFragment(data, html, _this) {
switch (data.type) {
case "fragment": {
const fragment = document.createElement("span")
fragment.style.lineHeight = `${cssFontSize}px`
fragment.style.fontSize = `${cssFontSize}px`
fragment.style.fontFamily = "Arial, Helvetica, sans-serif"
fragment.style.display = "inline-block"
fragment.style.whiteSpace = "nowrap"

for (let i = 0; i < data.styles.length; i++) {
const style = data.styles[i]
switch (style.type) {
case "bold":
fragment.style.fontWeight = "bold"
break
case "italic":
fragment.style.fontStyle = "italic"
break
case "colour":
fragment.style.color = style.hex
break
case "font":
fragment.style.fontFamily = style.name
fragment.style.fontSize = style.size
break
}
html.appendChild(fragment)
}

for (let i = 0; i < data.children.length; i++) {
const child = data.children[i]
renderFragment(child, fragment, _this)
}
html.appendChild(fragment)
break
}
case "text": {
const text = document.createElement("span")
text.textContent = data.content
html.appendChild(text)
break
}
case "newline": {
for (let i = 0; i < data.count; i++) {
const newLine = document.createElement("br")
html.appendChild(newLine)
}
break
}
}
}
renderFragment(this.data, root, this)
return root
}

renderCanvasData(canvas, cursor = true) {
const context = canvas.getContext("2d")

let hasSelection = this.hasSelection()
let inStyle = false
let stylesStack = [] // Every style on this stack gets applied to a char
let lines = []
let currentLine = ""

// We precalculate how many lines the canvas will be
let preCalcHeight = Math.max(360, EditorDocument.getLines(this.data).length * (this.fontSize / this.scale) + this.fontSize)
canvas.style.height = preCalcHeight + "px"
canvas.height = preCalcHeight * this.scale
// We precalculate how many lines the canvas will be
let preCalcHeightCss = Math.max(360, this.getLines().length * (this.fontSize / this.scale) + this.fontSize)
canvas.style.height = preCalcHeightCss + "px"
canvas.height = preCalcHeightCss * this.scale
context.clearRect(0, 0, canvas.width, canvas.height)

let line = 1
Expand Down Expand Up @@ -332,7 +331,7 @@ class EditorDocument {
offsetY *= this.scale

const context = canvas.getContext("2d")
let lines = EditorDocument.getLines(this.data)
let lines = this.getLines()
let line = Math.floor(Math.min(lines.length - 1, offsetY / this.fontSize))

// TODO: currently we are assuming there are 0 styles, that is wrong
Expand Down Expand Up @@ -421,11 +420,11 @@ class EditorDocument {
}
else if (movement == positionMovements.up || movement == positionMovements.down) {
let textPosition = EditorDocument.toTextPosition(this.position, this.data)
let lines = EditorDocument.getLines(this.data);
let lines = this.getLines();
let linePosition = this.positionInLine(textPosition, lines)

// Current line is the line count up to this line
let currentLine = EditorDocument.getLines(this.data.slice(0, this.position)).length - 1
let currentLine = this.getLines().length - 1

let offset = movement == positionMovements.up ?
Math.max(0, lines[currentLine].slice(0, linePosition).length + lines[currentLine].slice(linePosition).length) :
Expand Down Expand Up @@ -470,17 +469,44 @@ class EditorDocument {
}

// Returns only text lines
static getLines(data) {
getLines() {
let lines = []
let currentLine = ""

function visitFragment(data, _this) {
switch (data.type) {
case "fragment": {
for (let i = 0; i < data.children.length; i++) {
const child = data.children[i]
visitFragment(child, _this)
}
break
}
case "text": {
currentLine += data.content
break
}
case "newline": {
lines.push(currentLine)
currentLine = ""
for (let i = 1; i < data.count; i++) {
lines.push(currentLine)
}
break
}
}
}
visitFragment(this.data, this)

let current = ""
let inStyle = false

for (let i = 0; i < data.length; i++) {
if (data[i] == '\uE000') {
for (let i = 0; i < this.data.length; i++) {
if (this.data[i] == '\uE000') {
inStyle = !inStyle
continue
}
if (data[i] == '\uE001') {
if (this.data[i] == '\uE001') {
lines.push(current)
current = ""
continue
Expand Down

0 comments on commit f45c5fc

Please sign in to comment.