We have Refs! Emit and Update event to update refs in the 'store'.

master
Dustin Swan 1 day ago
parent a39c80fc74
commit 1af838126e
Signed by: dustinswan
GPG Key ID: 30D46587E2100467

@ -472,5 +472,39 @@ export const builtins: { [name: string]: NativeFunction } = {
console.log(`[DEBUG] ${l}: `, value); console.log(`[DEBUG] ${l}: `, value);
return value; return value;
} }
} },
'store': {
kind: 'native',
name: 'store',
arity: 1,
fn: (initialValue) => {
return { kind: 'ref', value: initialValue };
}
},
'get': {
kind: 'native',
name: 'get',
arity: 1,
fn: (ref) => {
if (ref.kind !== 'ref')
throw new Error('get expects a Ref');
return ref.value;
}
},
'update': {
kind: 'native',
name: 'update',
arity: 2,
fn: (ref, transformFn) => {
return {
kind: 'constructor',
name: 'Update',
args: [ref, transformFn]
};
}
},
} }

@ -11,13 +11,18 @@ import designTokensCode from './design-tokens.cg?raw';
import uiComponentsCode from './ui-components.cg?raw'; import uiComponentsCode from './ui-components.cg?raw';
import textInputCode from './textinput-test.cg?raw'; import textInputCode from './textinput-test.cg?raw';
import refTest from './test-ref.cg?raw';
// import testCode from './test.cg?raw'; // import testCode from './test.cg?raw';
// import counterApp from './counter.cg?raw'; // import counterApp from './counter.cg?raw';
const canvas = document.createElement('canvas') as HTMLCanvasElement; const canvas = document.createElement('canvas') as HTMLCanvasElement;
document.body.appendChild(canvas); document.body.appendChild(canvas);
const cgCode = stdlibCode + '\n' + designTokensCode + '\n' + uiComponentsCode + '\n' + textInputCode; const cgCode = stdlibCode + '\n' +
designTokensCode + '\n' +
uiComponentsCode + '\n' +
textInputCode + '\n'
// refTest + '\n';
try { try {
const tokens = tokenize(cgCode); const tokens = tokenize(cgCode);

@ -77,6 +77,9 @@ export function runApp(app: App, canvas: HTMLCanvasElement, source: string) {
handleEvent(event); handleEvent(event);
} }
} }
rerender();
} catch(error) { } catch(error) {
if (error instanceof CGError) { if (error instanceof CGError) {
console.error(error.format()); console.error(error.format());
@ -120,7 +123,7 @@ export function runApp(app: App, canvas: HTMLCanvasElement, source: string) {
child: viewUI, child: viewUI,
event: { event: {
kind: 'constructor', kind: 'constructor',
name: 'Focus', name: 'FocusAndClick',
args: [{ kind: 'string', value: fullKey }] args: [{ kind: 'string', value: fullKey }]
} }
}; };
@ -201,9 +204,41 @@ export function runApp(app: App, canvas: HTMLCanvasElement, source: string) {
} }
function handleEvent(event: Value) { function handleEvent(event: Value) {
if (event.kind === 'constructor' && event.name === 'Focus') { if (event.kind === 'constructor' && event.name === 'FocusAndClick') {
if (event.args.length > 0 && event.args[0].kind === 'string') { if (event.args.length === 2 && event.args[0].kind === 'string') {
setFocus(event.args[0].value); const componentKey = event.args[0].value;
const coords = event.args[1];
setFocus(componentKey);
handleComponentEvent(componentKey, {
kind: 'constructor',
name: 'Clicked',
args: [coords]
});
return;
}
}
if (event.kind === 'constructor' && event.name === 'Update') {
if (event.args.length === 2) {
const ref = event.args[0];
const transformFn = event.args[1];
if (ref.kind !== 'ref')
throw new Error('Update event expects a Ref')
if (transformFn.kind !== 'closure')
throw new Error('Update event expects a Ref')
const callEnv = new Map(transformFn.env);
callEnv.set(transformFn.params[0], ref.value);
const newValue = evaluate(transformFn.body, callEnv, source);
ref.value = newValue;
rerender();
return; return;
} }
} }
@ -240,19 +275,22 @@ export function runApp(app: App, canvas: HTMLCanvasElement, source: string) {
if (hitResult) { if (hitResult) {
const { event, relativeX, relativeY } = hitResult; const { event, relativeX, relativeY } = hitResult;
if (event.kind === 'constructor' && event.name === 'Focus') { if (event.kind === 'constructor' && (event.name === 'Focus' || event.name === 'Update')) {
handleEvent(event); handleEvent(event);
} else if (event.kind === 'constructor') { } else if (event.kind === 'constructor' && event.name === 'FocusAndClick') {
const eventWithCoords: Value = { const eventWithCoords: Value = {
kind: 'constructor', kind: 'constructor',
name: event.name, name: event.name,
args: [{ args: [
kind: 'record', event.args[0],
fields: { {
x: { kind: 'int', value: Math.floor(relativeX) }, kind: 'record',
y: { kind: 'int', value: Math.floor(relativeY) }, fields: {
x: { kind: 'int', value: Math.floor(relativeX) },
y: { kind: 'int', value: Math.floor(relativeY) },
}
} }
}] ]
}; };
handleEvent(eventWithCoords); handleEvent(eventWithCoords);
} else { } else {

@ -0,0 +1,17 @@
counter = store 0;
view = state viewport \
currentCount = get counter;
Column {
gap = 10,
children = [
Text { content = str currentCount, x = 30, y = 30 },
Clickable {
event = update counter (n \ n + 1),
child = Rect { w = 100, h = 40, color = "blue" }
}
]
};
{ init = {}, update = state event \ state, view = view }

@ -46,6 +46,11 @@ export type NativeFunction = {
fn: (...args: Value[]) => Value fn: (...args: Value[]) => Value
} }
export type RefValue = {
kind: 'ref'
value: Value
}
export type UIValue = export type UIValue =
| { kind: 'rect', w: number, h: number, color: string, radius?: number } | { kind: 'rect', w: number, h: number, color: string, radius?: number }
| { kind: 'text', content: string, x: number, y: number } | { kind: 'text', content: string, x: number, y: number }
@ -59,4 +64,4 @@ export type UIValue =
| { kind: 'stack', children: UIValue[] } | { kind: 'stack', children: UIValue[] }
| { kind: 'stateful', key: string, focusable: boolean, init: Value, update: Value, view: Value } | { kind: 'stateful', key: string, focusable: boolean, init: Value, update: Value, view: Value }
export type Value = IntValue | FloatValue | StringValue | Closure | ListValue | RecordValue | ConstructorValue | NativeFunction; export type Value = IntValue | FloatValue | StringValue | Closure | ListValue | RecordValue | ConstructorValue | NativeFunction | RefValue;

@ -68,14 +68,15 @@ textInput = config \ Stateful {
newCursorPos = max 0 (state.cursorPos - 1); newCursorPos = max 0 (state.cursorPos - 1);
newScroll = calcScrollOffset state.text newCursorPos state.scrollOffset 284; newScroll = calcScrollOffset state.text newCursorPos state.scrollOffset 284;
newState = state.{ text = state.text, cursorPos = newCursorPos, scrollOffset = newScroll }; newState = state.{ text = state.text, cursorPos = newCursorPos, scrollOffset = newScroll };
return { state = newState, emit = [] } _ = debug "ArrowLeft" [];
{ state = newState, emit = [] }
) )
| ArrowRight \ ( | ArrowRight \ (
newCursorPos = min (len state.text) (state.cursorPos + 1); newCursorPos = min (len state.text) (state.cursorPos + 1);
newScroll = calcScrollOffset state.text newCursorPos state.scrollOffset 284; newScroll = calcScrollOffset state.text newCursorPos state.scrollOffset 284;
newState = state.{ text = state.text, cursorPos = newCursorPos, scrollOffset = newScroll }; newState = state.{ text = state.text, cursorPos = newCursorPos, scrollOffset = newScroll };
return { state = newState, emit = [] } { state = newState, emit = [] }
) )
| Backspace \ ( | Backspace \ (
@ -86,19 +87,18 @@ textInput = config \ Stateful {
{ state = newState, emit = [config.onChange newText] } { state = newState, emit = [config.onChange newText] }
) )
| Char c \ ( | Char c \
_ = debug c; _ = debug c;
newText = insertChar state.text state.cursorPos c; newText = insertChar state.text state.cursorPos c;
newCursorPos = state.cursorPos + 1; newCursorPos = state.cursorPos + 1;
newScroll = calcScrollOffset newText newCursorPos state.scrollOffset 284; newScroll = calcScrollOffset newText newCursorPos state.scrollOffset 284;
newState = state.{ text = newText, cursorPos = newCursorPos, scrollOffset = newScroll }; newState = state.{ text = newText, cursorPos = newCursorPos, scrollOffset = newScroll };
{ state = newState, emit = [config.onChange newText] } { state = newState, emit = [config.onChange newText] }
)
| Clicked coords \ ( | Clicked coords \ (
newCursorPos = findCursorPos state.text coords.x state.scrollOffset 8; newCursorPos = findCursorPos state.text coords.x state.scrollOffset 8;
newScroll = calcScrollOffset state.text newCursorPos state.scrollOffset 284; newScroll = calcScrollOffset state.text newCursorPos state.scrollOffset 284;
newSatte = state.{ text = state.text, cursorPos = newCursorPos, scrollOffset = newScroll }; newState = state.{ text = state.text, cursorPos = newCursorPos, scrollOffset = newScroll };
{ state = newState, emit = [] } { state = newState, emit = [] }
) )

Loading…
Cancel
Save