We have Refs! Emit and Update event to update refs in the 'store'.
This commit is contained in:
parent
a39c80fc74
commit
1af838126e
6 changed files with 119 additions and 20 deletions
|
|
@ -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: [
|
||||||
|
event.args[0],
|
||||||
|
{
|
||||||
kind: 'record',
|
kind: 'record',
|
||||||
fields: {
|
fields: {
|
||||||
x: { kind: 'int', value: Math.floor(relativeX) },
|
x: { kind: 'int', value: Math.floor(relativeX) },
|
||||||
y: { kind: 'int', value: Math.floor(relativeY) },
|
y: { kind: 'int', value: Math.floor(relativeY) },
|
||||||
}
|
}
|
||||||
}]
|
}
|
||||||
|
]
|
||||||
};
|
};
|
||||||
handleEvent(eventWithCoords);
|
handleEvent(eventWithCoords);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
17
src/test-ref.cg
Normal file
17
src/test-ref.cg
Normal file
|
|
@ -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…
Add table
Add a link
Reference in a new issue