compiling. interpreting was too slow
This commit is contained in:
parent
6edf592637
commit
2cd5a609bb
8 changed files with 650 additions and 21 deletions
227
src/runtime-compiled.ts
Normal file
227
src/runtime-compiled.ts
Normal file
|
|
@ -0,0 +1,227 @@
|
|||
// import type { UIValue } from './types';
|
||||
import { valueToUI } from './valueToUI-compiled';
|
||||
import { render, hitTest } from './ui';
|
||||
|
||||
type UIValue = any;
|
||||
|
||||
type App = {
|
||||
init: any;
|
||||
update: (state: any) => (event: any) => any;
|
||||
view: (state: any) => (viewport: any) => any;
|
||||
}
|
||||
|
||||
type ComponentInstance = {
|
||||
state: any;
|
||||
update: (state: any) => (event: any) => any;
|
||||
view: (state: any) => any;
|
||||
};
|
||||
|
||||
export function runAppCompiled(app: App, canvas: HTMLCanvasElement, rt: any) {
|
||||
let state = app.init;
|
||||
const componentInstances = new Map<string, ComponentInstance>();
|
||||
let focusedComponentKey: string | null = null;
|
||||
|
||||
function setupCanvas() {
|
||||
const dpr = window.devicePixelRatio || 1;
|
||||
canvas.width = window.innerWidth * dpr;
|
||||
canvas.height = window.innerHeight * dpr;
|
||||
canvas.style.width = window.innerWidth + 'px';
|
||||
canvas.style.height = window.innerHeight + 'px';
|
||||
}
|
||||
|
||||
setupCanvas();
|
||||
|
||||
function setFocus(componentKey: string | null) {
|
||||
if (focusedComponentKey === componentKey) return;
|
||||
const oldFocus = focusedComponentKey;
|
||||
focusedComponentKey = componentKey;
|
||||
|
||||
// Blur event to the previous
|
||||
if (oldFocus && componentInstances.has(oldFocus)) {
|
||||
handleComponentEvent(oldFocus, { _tag: 'Blurred' });
|
||||
}
|
||||
|
||||
// Focus event to the new
|
||||
if (componentKey && componentInstances.has(componentKey)) {
|
||||
handleComponentEvent(componentKey, { name: 'Focused' });
|
||||
}
|
||||
rerender();
|
||||
}
|
||||
|
||||
function handleComponentEvent(componentKey: string, event: any) {
|
||||
const instance = componentInstances.get(componentKey);
|
||||
if (!instance) return;
|
||||
|
||||
try {
|
||||
const result = instance.update(instance.state)(event);
|
||||
instance.state = result.state;
|
||||
|
||||
if (result.emit && Array.isArray(result.emit)) {
|
||||
for (const e of result.emit) {
|
||||
handleEvent(e);
|
||||
}
|
||||
}
|
||||
rerender();
|
||||
} catch(error) {
|
||||
console.error('Component event error:', error);
|
||||
}
|
||||
}
|
||||
|
||||
function expandStateful(ui: UIValue, path: number[]): UIValue {
|
||||
switch (ui.kind) {
|
||||
case 'stateful': {
|
||||
const fullKey = [...path, ui.key].join('.');
|
||||
let instance = componentInstances.get(fullKey);
|
||||
|
||||
if (!instance) {
|
||||
console.log('Creating stateful', fullKey);
|
||||
console.log('ui.init:', ui.init);
|
||||
instance = {
|
||||
state: ui.init,
|
||||
update: ui.update,
|
||||
view: ui.view
|
||||
};
|
||||
componentInstances.set(fullKey, instance);
|
||||
} else {
|
||||
// refresh closures, pick up new values
|
||||
instance.update = ui.update;
|
||||
instance.view = ui.view;
|
||||
}
|
||||
console.log('Instance state', instance.state);
|
||||
|
||||
const viewResult = instance.view(instance.state);
|
||||
let viewUI = valueToUI(viewResult);
|
||||
|
||||
if (ui.focusable) {
|
||||
viewUI = {
|
||||
kind: 'clickable',
|
||||
child: viewUI,
|
||||
event: { _tag: 'FocusAndClick', _0: fullKey }
|
||||
};
|
||||
}
|
||||
return expandStateful(viewUI, path);
|
||||
}
|
||||
|
||||
case 'stack':
|
||||
case 'row':
|
||||
case 'column': {
|
||||
return {
|
||||
...ui,
|
||||
children: ui.children.map((child: UIValue, i: number) =>
|
||||
expandStateful(child, [...path, i])
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
case 'clickable':
|
||||
case 'padding':
|
||||
case 'positioned':
|
||||
case 'opacity':
|
||||
case 'clip': {
|
||||
return {
|
||||
...ui,
|
||||
child: expandStateful((ui as any).child, [...path, 0])
|
||||
};
|
||||
}
|
||||
|
||||
default:
|
||||
// leaf nodes
|
||||
return ui;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function rerender() {
|
||||
const viewport = { width: window.innerWidth, height: window.innerHeight };
|
||||
|
||||
try {
|
||||
const uiValue = app.view(state)(viewport);
|
||||
const ui = valueToUI(uiValue);
|
||||
const expandedUI = expandStateful(ui, []);
|
||||
render(expandedUI, canvas);
|
||||
} catch (error) {
|
||||
console.error('Render error:', error);
|
||||
}
|
||||
}
|
||||
|
||||
function handleEvent(event: any) {
|
||||
if (!event || !event._tag) return;
|
||||
|
||||
if (event._tag === 'Batch' && event._0) {
|
||||
for (const e of event._0) {
|
||||
handleEvent(e);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (event._tag === 'FocusAndClick') {
|
||||
const componentKey = event._0;
|
||||
const coords = event._1;
|
||||
setFocus(componentKey);
|
||||
handleComponentEvent(componentKey, { _tag: 'Clicked', _0: coords });
|
||||
return;
|
||||
}
|
||||
|
||||
if (event._tag === 'Rebind') {
|
||||
rt.rebind(event._0, event._1, event._2);
|
||||
rerender();
|
||||
return;
|
||||
}
|
||||
|
||||
if (event._tag === 'Focus') {
|
||||
setFocus(event._0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (event._tag === 'NoOp')
|
||||
return;
|
||||
|
||||
state = app.update(state)(event);
|
||||
rerender();
|
||||
}
|
||||
|
||||
canvas.addEventListener('click', (e) => {
|
||||
const rect = canvas.getBoundingClientRect();
|
||||
const x = e.clientX - rect.left;
|
||||
const y = e.clientY - rect.top;
|
||||
|
||||
const hitResult = hitTest(x, y);
|
||||
if (hitResult) {
|
||||
const { event, relativeX, relativeY } = hitResult;
|
||||
|
||||
if (event._tag === 'FocusAndClick') {
|
||||
handleEvent({
|
||||
_tag: 'FocusAndClick',
|
||||
_0: event._0,
|
||||
_1: { x: Math.floor(relativeX), y: Math.floor(relativeY) }
|
||||
});
|
||||
} else {
|
||||
handleEvent(event);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
window.addEventListener('keydown', (e) => {
|
||||
let event: any;
|
||||
|
||||
if (e.key.length === 1 && !e.ctrlKey && !e.metaKey && !e.altKey) {
|
||||
event = { _tag: 'Char', _0: e.key };
|
||||
} else {
|
||||
event = { _tag: e.key };
|
||||
}
|
||||
|
||||
if (focusedComponentKey) {
|
||||
handleComponentEvent(focusedComponentKey, event);
|
||||
} else {
|
||||
handleEvent(event);
|
||||
}
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
window.addEventListener('resize', () => {
|
||||
setupCanvas();
|
||||
rerender();
|
||||
})
|
||||
|
||||
rerender();
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue