Bypass the rendering pipeline for highlighting to reduce (eliminate) latency

Fixes #15
This commit is contained in:
Ian Gulliver
2019-07-14 02:26:00 +00:00
parent a4630ba92a
commit f3823812c7
18 changed files with 96 additions and 123 deletions

View File

@@ -47,19 +47,18 @@ class Architype {
this.addDefaultEntries();
}
this.observer_ = new MutationObserver(e => { this.onChange(e); });
this.observer2_ = new MutationObserver(e => { this.snapshot(e); });
this.observer_ = new MutationObserver(e => { this.onChange(); });
this.observer2_ = new MutationObserver(e => { this.snapshot(false); });
this.observe();
this.saveAndRender();
history.replaceState('first', null, '#' + btoa(this.serializedStr_));
this.render();
this.snapshot(true);
}
observe() {
this.observer_.observe(this.editorElem_, {
attributes: true,
attributeFilter: ['data-arch-refresh'],
attributeFilter: ['data-arch-render'],
childList: true,
subtree: true,
});
@@ -130,7 +129,7 @@ class Architype {
this.editor_.clear();
this.unserialize(ser);
this.observe();
this.saveAndRender();
this.render();
}
onHashChange() {
@@ -145,19 +144,25 @@ class Architype {
onChange() {
++this.generation_;
this.saveAndRender();
this.render();
}
snapshot() {
history.pushState(null, null, '#' + btoa(this.serializedStr_));
this.first_ = false;
}
saveAndRender() {
snapshot(first) {
this.serialized_ = this.serialize();
this.startRender();
this.serializedStr_ = JSON.stringify(this.serialized_);
localStorage.setItem('currentState', this.serializedStr_);
this.first_ = first || false;
let hash = '#' + btoa(this.serializedStr_);
if (first) {
history.replaceState('first', null, hash);
} else {
history.pushState(null, null, hash);
}
}
render() {
this.serialized_ = this.serialize();
this.startRender();
}
addDefaultEntries() {

View File

@@ -196,10 +196,8 @@ class Editor extends List {
case '`':
if (!this.container_.parentElement.xArchObj) {
for (let entry of this.queryEntries('.highlight')) {
entry.getElement().classList.toggle('highlight', false);
entry.setHighlight(false);
}
this.container_.setAttribute('data-arch-refresh', '');
this.container_.setAttribute('data-arch-snapshot', '');
e.stopPropagation();
e.preventDefault();
return;

View File

@@ -13,6 +13,12 @@ class EditorEntryBase extends ListenUtils {
this.elem_.xArchObj = this;
}
serialize(base) {
base.id = this.getId();
base.highlight = this.elem_.classList.contains('highlight');
return base;
}
remove() {
if (document.activeElement == this.elem_ ||
document.activeElement == document.body) {
@@ -42,11 +48,31 @@ class EditorEntryBase extends ListenUtils {
return this.elem_.id;
}
setHighlight(highlight) {
this.elem_.classList.toggle('highlight', highlight);
for (let elem of document.getElementsByClassName('grid-' + this.getId())) {
elem.classList.toggle('highlight', highlight);
}
// Do NOT refresh: this bypasses the rendering pipeline
this.elem_.setAttribute('data-arch-snapshot', '');
}
toggleHighlight() {
this.setHighlight(!this.elem_.classList.contains('highlight'));
}
onElemFocus() {
this.elem_.scrollIntoView({block: 'nearest'});
}
onKeyDown() {
onKeyDown(e) {
switch (e.key) {
case ' ':
this.toggleHighlight();
e.stopPropagation();
e.preventDefault();
break;
}
}
afterDomAdd() {

View File

@@ -29,13 +29,11 @@ class EditorGroup extends EditorEntryBase {
}
serialize() {
return {
return super.serialize({
type: 'group',
id: this.getId(),
label: this.getLabel(),
members: this.nodes_.serialize(EditorNode),
highlight: this.elem_.classList.contains('highlight'),
};
});
}
getNodes() {
@@ -57,10 +55,6 @@ class EditorGroup extends EditorEntryBase {
}
}
setHighlight(highlight) {
this.elem_.classList.toggle('highlight', highlight);
}
onKeyDown(e) {
super.onKeyDown(e);
@@ -72,14 +66,6 @@ class EditorGroup extends EditorEntryBase {
e.stopPropagation();
e.preventDefault();
break;
case ' ':
this.elem_.classList.toggle('highlight');
this.elem_.setAttribute('data-arch-refresh', '');
this.elem_.setAttribute('data-arch-snapshot', '');
e.stopPropagation();
e.preventDefault();
break;
}
}

View File

@@ -117,10 +117,9 @@ class EditorHelp extends EditorEntryBase {
}
serialize() {
return {
return super.serialize({
type: 'help',
id: this.getId(),
};
});
}
static unserialize(ser) {

View File

@@ -21,7 +21,7 @@ class EditorInputBase extends EditorEntryBase {
}
serialize(base) {
base.id = this.getId();
super.serialize(base);
base.label = this.getLabel();
return base;
}
@@ -41,7 +41,7 @@ class EditorInputBase extends EditorEntryBase {
}
onInput() {
this.elem_.setAttribute('data-arch-refresh', '');
this.elem_.setAttribute('data-arch-render', '');
}
onBlur() {

View File

@@ -9,25 +9,13 @@ class EditorLabel extends EditorInputBase {
serialize() {
return super.serialize({
type: 'label',
id: this.getId(),
});
}
onKeyDown(e) {
super.onKeyDown(e);
switch (e.key) {
case ' ':
// We don't support highlighting, but stop propagation
e.stopPropagation();
e.preventDefault();
break;
}
}
static unserialize(ser) {
let label = new EditorLabel(ser.id);
label.setLabel(ser.label);
label.setHighlight(ser.highlight);
return label.getElement();
}
}

View File

@@ -26,14 +26,12 @@ class EditorLink extends EditorEntryBase {
}
serialize() {
return {
return super.serialize({
type: 'link',
id: this.getId(),
label: this.getLabel(),
from: this.getFrom().serialize(),
to: this.getTo().serialize(),
highlight: this.elem_.classList.contains('highlight'),
};
});
}
getFrom() {
@@ -59,10 +57,6 @@ class EditorLink extends EditorEntryBase {
}
}
setHighlight(highlight) {
this.elem_.classList.toggle('highlight', highlight);
}
flip() {
let entries = this.nodes_.getEntries(EditorNode);
let fromElem = entries[0].getElement();
@@ -91,14 +85,6 @@ class EditorLink extends EditorEntryBase {
e.preventDefault();
break;
case ' ':
this.elem_.classList.toggle('highlight');
this.elem_.setAttribute('data-arch-refresh', '');
this.elem_.setAttribute('data-arch-snapshot', '');
e.stopPropagation();
e.preventDefault();
break;
case 'f':
this.flip();
e.stopPropagation();

View File

@@ -9,14 +9,9 @@ class EditorNode extends EditorInputBase {
serialize() {
return super.serialize({
type: 'node',
highlight: this.elem_.classList.contains('highlight'),
});
}
setHighlight(highlight) {
this.elem_.classList.toggle('highlight', highlight);
}
isSoft() {
// Nested nodes are presumed to be references to other nodes if they exist
for (let iter = this.elem_.parentElement; iter; iter = iter.parentElement) {
@@ -27,20 +22,6 @@ class EditorNode extends EditorInputBase {
return false;
}
onKeyDown(e) {
super.onKeyDown(e);
switch (e.key) {
case ' ':
this.elem_.classList.toggle('highlight');
this.elem_.setAttribute('data-arch-snapshot', '');
this.onInput();
e.stopPropagation();
e.preventDefault();
break;
}
}
static unserialize(ser) {
let node = new EditorNode(ser.id);
node.setLabel(ser.label);

View File

@@ -28,7 +28,6 @@ class GraphGroup {
let group = new GraphGroup();
group.id = item.id;
group.label = item.label;
group.highlight = item.highlight;
group.nodeLabels = new Set();
for (let member of item.members) {
if (member.label == '') {

View File

@@ -11,13 +11,11 @@ class GraphLink {
to: to,
id: this.id,
label: this.label,
highlight: this.highlight,
});
to.linksIn.push({
from: from,
id: this.id,
label: this.label,
highlight: this.highlight,
});
}
}
@@ -35,7 +33,6 @@ class GraphLink {
link.label = item.label;
link.fromLabel = item.from.label;
link.toLabel = item.to.label;
link.highlight = item.highlight;
if (link.fromLabel == '' || link.toLabel == '') {
return null;
}

View File

@@ -48,7 +48,6 @@ class GraphNode {
node.id = item.id;
node.label = item.label;
node.soft = soft;
node.highlight = item.highlight;
return node;
}
}

42
Grid.js
View File

@@ -22,7 +22,7 @@ class Grid {
break;
case 'arrow':
this.drawArrow(step.id, step.pos, step.cls, step.highlight);
this.drawArrow(step.id, step.pos, step.cls);
break;
case 'graphLabel':
@@ -30,12 +30,11 @@ class Grid {
break;
case 'group':
this.drawGroup(step.id, step.min, step.max, step.label,
step.highlight);
this.drawGroup(step.id, step.min, step.max, step.label);
break;
case 'line':
this.drawLine(step.id, step.pos, step.cls, step.highlight);
this.drawLine(step.id, step.pos, step.cls);
break;
case 'linkLabel':
@@ -43,7 +42,7 @@ class Grid {
break;
case 'node':
this.drawNode(step.id, step.label, step.pos, step.highlight);
this.drawNode(step.id, step.label, step.pos);
break;
}
}
@@ -60,14 +59,14 @@ class Grid {
size[0] + ')))';
}
drawArrow(id, pos, cls, highlight) {
drawArrow(id, pos, cls) {
let svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
this.container_.appendChild(svg);
svg.classList.add('gridArrow');
svg.classList.add('grid-' + id);
this.maybeHighlight(svg, id);
svg.style.gridColumn = pos[0] + 1;
svg.style.gridRow = pos[1] + 1;
svg.classList.toggle('highlight', highlight);
let use = document.createElementNS('http://www.w3.org/2000/svg', 'use');
svg.appendChild(use);
@@ -79,26 +78,29 @@ class Grid {
this.container_.appendChild(elem);
elem.classList.add('gridGraphLabel');
elem.classList.add('grid-' + id);
this.maybeHighlight(elem, id);
elem.style.gridColumn = (min[0] + 1) + ' / ' + (max[0] + 2);
elem.style.gridRow = (min[1] + 1) + ' / ' + (max[1] + 2);
elem.innerText = label;
this.toSize_.push(elem);
}
drawGroup(id, min, max, label, highlight) {
drawGroup(id, min, max, label) {
let group = document.createElement('div');
this.container_.appendChild(group);
group.classList.add('gridGroup');
group.classList.add('grid-' + id);
this.maybeHighlight(group, id);
group.style.gridColumn = (min[0] + 1) + ' / ' + (max[0] + 2);
group.style.gridRow = (min[1] + 1) + ' / ' + (max[1] + 2);
group.classList.toggle('highlight', highlight);
if (label != '') {
// TODO: split this into its own draw step type
let labelNode = document.createElement('div');
this.container_.appendChild(labelNode);
labelNode.classList.add('gridGroupLabel');
labelNode.classList.add('grid-' + id);
this.maybeHighlight(labelNode, id);
labelNode.innerText = label;
labelNode.style.gridColumn = (min[0] + 1) + ' / ' + (max[0] + 2);
labelNode.style.gridRow = min[1] + 1;
@@ -106,14 +108,14 @@ class Grid {
}
}
drawLine(id, pos, cls, highlight) {
drawLine(id, pos, cls) {
let svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
this.container_.appendChild(svg);
svg.classList.add('gridLines');
svg.classList.add('grid-' + id);
this.maybeHighlight(svg, id);
svg.style.gridColumn = pos[0] + 1;
svg.style.gridRow = pos[1] + 1;
svg.classList.toggle('highlight', highlight);
let use = document.createElementNS('http://www.w3.org/2000/svg', 'use');
svg.appendChild(use);
@@ -125,27 +127,35 @@ class Grid {
this.container_.appendChild(elem);
elem.classList.add('gridLinkLabel');
elem.classList.add('grid-' + id);
this.maybeHighlight(elem, id);
elem.innerText = label;
elem.style.gridColumn = pos[0] + 1;
elem.style.gridRow = pos[1] + 1;
this.toSize_.push(elem);
}
drawNode(id, label, pos, highlight) {
drawNode(id, label, pos) {
let node = document.createElement('div');
this.container_.appendChild(node);
node.classList.add('gridNode');
node.classList.add('grid-' + id);
this.maybeHighlight(node, id);
node.innerText = label;
node.classList.toggle('highlight', highlight);
node.style.gridColumn = pos[0] + 1;
node.style.gridRow = pos[1] + 1;
this.toSize_.push(node);
}
node.addEventListener('click', () => {
maybeHighlight(elem, id) {
let source = document.getElementById(id);
if (!source) {
return;
}
elem.classList.toggle('highlight', source.classList.contains('highlight'));
elem.addEventListener('click', () => {
let editorElem = document.getElementById(id);
editorElem.classList.toggle('highlight');
editorElem.setAttribute('data-arch-refresh', '');
editorElem.xArchObj.toggleHighlight();
});
}

View File

@@ -227,7 +227,6 @@ class Layout {
to: link.to,
id: link.id,
label: link.label,
highlight: link.highlight,
});
}
}
@@ -240,7 +239,6 @@ class Layout {
for (let link of links) {
this.links_.push(
new LayoutLink(link.from, link.to, link.id, link.label,
link.highlight,
this.nodesByPos_, this.linksByPos_,
this.labelsByPos_));
}

View File

@@ -92,7 +92,6 @@ class LayoutGroup {
max: max,
id: this.graphGroup_.id,
label: this.graphGroup_.label,
highlight: this.graphGroup_.highlight,
};
}

View File

@@ -1,10 +1,9 @@
class LayoutLink {
constructor(from, to, id, label, highlight, nodesByPos, linksByPos, labelsByPos) {
constructor(from, to, id, label, nodesByPos, linksByPos, labelsByPos) {
this.from_ = from;
this.to_ = to;
this.id_ = id;
this.label_ = label;
this.highlight_ = highlight;
this.nodesByPos_ = nodesByPos;
this.linksByPos_ = linksByPos;
this.labelsByPos_ = labelsByPos;
@@ -229,7 +228,6 @@ class LayoutLink {
id: this.id_,
pos: Array.from(this.path[0]),
cls: 's' + this.getOutPoint(this.path[0], this.path[1]),
highlight: this.highlight_,
});
for (let i = 1; i < this.path.length - 1; ++i) {
@@ -240,7 +238,6 @@ class LayoutLink {
id: this.id_,
pos: Array.from(this.path[i]),
cls: `i${inPoint}o${outPoint}`,
highlight: this.highlight_,
});
}
@@ -252,7 +249,6 @@ class LayoutLink {
id: this.id_,
pos: Array.from(this.path[this.path.length - 1]),
cls: 's' + endInPoint,
highlight: this.highlight_,
});
steps.push({
@@ -260,7 +256,6 @@ class LayoutLink {
id: this.id_,
pos: Array.from(this.path[this.path.length - 1]),
cls: 'a' + endInPoint,
highlight: this.highlight_,
});
if (this.labelPos_) {

View File

@@ -21,7 +21,6 @@ class LayoutNode {
to: nodesByGraphNode.get(link.to),
id: link.id,
label: link.label,
highlight: link.highlight,
});
}
}
@@ -161,7 +160,6 @@ class LayoutNode {
pos: this.pos,
id: this.graphNode_.id,
label: this.graphNode_.label,
highlight: this.graphNode_.highlight,
};
}
}

View File

@@ -141,6 +141,14 @@ body {
background-color: var(--label);
}
.editor li.label.highlight {
background: repeating-linear-gradient(
-45deg,
transparent 0 10px,
rgba(255,0,0,0.3) 10px 20px
), var(--label);
}
.editor li.help {
padding: 10px;
font-size: 16px;
@@ -260,6 +268,10 @@ body {
text-align: center;
}
.gridGraphLabel.highlight {
color: var(--focus);
}
.gridGroup {
width: 100%;
height: 100%;
@@ -283,14 +295,12 @@ body {
overflow: hidden;
overflow-wrap: anywhere;
z-index: 1;
pointer-events: none;
}
.gridLines {
width: 100%;
height: 100%;
z-index: 2;
pointer-events: none;
--line-color: var(--line);
}
@@ -307,7 +317,6 @@ body {
z-index: 3;
border-radius: 4px;
padding: 3px;
pointer-events: none;
}
.gridArrow {