Cleaning up UI primitives. fixing parser bugs. still struggling to make a command palette lol

master
Dustin Swan 3 weeks ago
parent 2d687b5d38
commit da97f53729
Signed by: dustinswan
GPG Key ID: 30D46587E2100467

@ -468,7 +468,8 @@ export const builtins: { [name: string]: Value } = {
name: 'debug', name: 'debug',
arity: 2, arity: 2,
fn: (label, value) => { fn: (label, value) => {
expectString(label, 'debug'); const str = expectString(label, 'debug');
console.log(str, value);
return value; return value;
} }
} }

@ -1,5 +1,4 @@
import type { Env } from './env' import type { Env } from './env'
import type { Value } from './types'
import { evaluate } from './interpreter' import { evaluate } from './interpreter'
import { tokenize } from './lexer' import { tokenize } from './lexer'
import { Parser } from './parser' import { Parser } from './parser'

@ -1,7 +1,6 @@
osState = { osState = {
query = "", query = "",
debug = "DEBUG", focusedPaletteIndex = 0
selectedPaletteIndex = 0
}; };
init = { init = {
@ -11,7 +10,23 @@ init = {
update = state event \ event update = state event \ event
| _ \ state; | _ \ state;
listRow = child selected \
Clickable {
event = osState.query := "",
child = Stack {
children = [
Rect { w = 300, h = 40, strokeWidth = 2, strokeColor = "black", color = "pink" }
]
}
};
# listRow = child selected \
# pre = (selected | True \ "*" | _ \ "");
# Text { content = pre & " " & child };
view = state viewport \ view = state viewport \
_ = debug "focusedPalletIndex" osState.focusedPaletteIndex;
Positioned { Positioned {
x = 30, x = 30,
y = 30, y = 30,
@ -26,13 +41,18 @@ view = state viewport \
h = 40, h = 40,
onChange = text \ osState.query := text, onChange = text \ osState.query := text,
onKeyDown = key \ key onKeyDown = key \ key
| ArrowUp \ osState.selectedPaletteIndex := max 0 (osState.selectedPaletteIndex + 1) | ArrowUp \ osState.focusedPaletteIndex := max 0 (osState.focusedPaletteIndex - 1)
| ArrowDown \ osState.selectedPaletteIndex := osState.selectedPaletteIndex - 1 | ArrowDown \ osState.focusedPaletteIndex := (osState.focusedPaletteIndex + 1)
| _ \ NoOp
}, },
Text { content = osState.debug, x = 8, y = 16 }, Clip {
Column { w = 300,
gap = 10, h = 300,
children = map (t \ Text { content = t, x = 8, y = 16 }) (storeSearch osState.query) child = Column {
gap = 10,
# children = mapWithIndex (t i \ Text { content = str i & " " & t }) (storeSearch osState.query)
children = mapWithIndex (t i \ listRow t (osState.focusedPaletteIndex == i)) (storeSearch osState.query)
}
} }
] ]
} }

@ -168,7 +168,7 @@ export class Parser {
if (this.current().kind == 'colon-equals') { if (this.current().kind == 'colon-equals') {
const token = this.current(); const token = this.current();
this.advance(); this.advance();
const value = this.parseExpression(); const value = this.parseExpressionNoMatch();
return { kind: 'rebind', target: expr, value, ...this.getPos(token) }; return { kind: 'rebind', target: expr, value, ...this.getPos(token) };
} }

@ -154,6 +154,10 @@ export function runApp(app: App, canvas: HTMLCanvasElement, source: string, env:
view: ui.view view: ui.view
}; };
componentInstances.set(fullKey, instance); componentInstances.set(fullKey, instance);
} else {
// refresh closures, pick up new values
instance.update = ui.update;
instance.view = ui.view;
} }
if (instance.view.kind !== 'closure') if (instance.view.kind !== 'closure')

@ -3,6 +3,12 @@ map = f list \ list
| [] \ [] | [] \ []
| [x, ...xs] \ [f x, ...map f xs]; | [x, ...xs] \ [f x, ...map f xs];
# mapWithIndex : (a \ Number \ b) \ List a \ List b
mapWithIndex = f list \
init = { i = 0, result = [] };
f2 = acc x \ { i = acc.i + 1, result = [...acc.result, f x acc.i] };
(fold f2 init list).result;
# filter : (a \ Bool) \ List a \ List a # filter : (a \ Bool) \ List a \ List a
filter = f list \ list filter = f list \ list
| [] \ [] | [] \ []

@ -53,8 +53,8 @@ export type NativeFunction = {
} }
export type UIValue = export type UIValue =
| { kind: 'rect', w: number, h: number, color: string, radius?: number } | { kind: 'rect', w: number, h: number, color?: string, strokeColor?: string, strokeWidth?: number, radius?: number }
| { kind: 'text', content: string, x: number, y: number } | { kind: 'text', content: string }
| { kind: 'row', children: UIValue[], gap: number } | { kind: 'row', children: UIValue[], gap: number }
| { kind: 'column', children: UIValue[], gap: number } | { kind: 'column', children: UIValue[], gap: number }
| { kind: 'clickable', child: UIValue, event: Value } | { kind: 'clickable', child: UIValue, event: Value }

@ -115,7 +115,7 @@ textInput = config \ Stateful {
Positioned { Positioned {
x = 8 - state.scrollOffset, x = 8 - state.scrollOffset,
y = 8, y = 8,
child = Text { content = state.text, x = 0, y = 17 } child = Positioned { x = 0, y = 17, child = Text { content = state.text } }
}, },
(state.focused (state.focused

@ -27,7 +27,7 @@ export function render(ui: UIValue, canvas: HTMLCanvasElement) {
function renderUI(ui: UIValue, ctx: CanvasRenderingContext2D, x: number, y: number) { function renderUI(ui: UIValue, ctx: CanvasRenderingContext2D, x: number, y: number) {
switch (ui.kind) { switch (ui.kind) {
case 'rect': { case 'rect': {
ctx.fillStyle = ui.color; ctx.fillStyle = ui.color || 'transparent';
if (ui.radius && ui.radius > 0) { if (ui.radius && ui.radius > 0) {
const r = Math.min(ui.radius, ui.w / 2, ui.h / 2); const r = Math.min(ui.radius, ui.w / 2, ui.h / 2);
@ -43,8 +43,21 @@ function renderUI(ui: UIValue, ctx: CanvasRenderingContext2D, x: number, y: numb
ctx.arcTo(x, y, x + r, y, r); ctx.arcTo(x, y, x + r, y, r);
ctx.closePath(); ctx.closePath();
ctx.fill(); ctx.fill();
if (ui.strokeColor && ui.strokeWidth) {
ctx.strokeStyle = ui.strokeColor;
ctx.lineWidth = ui.strokeWidth;
ctx.stroke();
}
} else { } else {
ctx.fillRect(x, y, ui.w, ui.h); ctx.fillRect(x, y, ui.w, ui.h);
if (ui.strokeColor && ui.strokeWidth) {
ctx.strokeStyle = ui.strokeColor;
ctx.lineWidth = ui.strokeWidth;
ctx.strokeRect(x, y, ui.w, ui.h);;
}
} }
break; break;
@ -53,7 +66,7 @@ function renderUI(ui: UIValue, ctx: CanvasRenderingContext2D, x: number, y: numb
case 'text': case 'text':
ctx.fillStyle = 'black'; ctx.fillStyle = 'black';
ctx.font = '16px "SF Mono", "Monaco", "Menlo", "Consolas", "Courier New", monospace'; ctx.font = '16px "SF Mono", "Monaco", "Menlo", "Consolas", "Courier New", monospace';
ctx.fillText(ui.content, x + ui.x, y + ui.y); ctx.fillText(ui.content, x, y);
break; break;
case 'row': { case 'row': {

@ -12,29 +12,29 @@ export function valueToUI(value: Value): UIValue {
switch (value.name) { switch (value.name) {
case 'Rect': { case 'Rect': {
const { w, h, color, radius } = fields; const { w, h, color, radius, strokeColor, strokeWidth } = fields;
if (w.kind !== 'int' || h.kind !== 'int' || color.kind !== 'string') if (w.kind !== 'int' || h.kind !== 'int')
throw new Error('Invalid Rect fields'); throw new Error('Invalid Rect fields');
return { return {
kind: 'rect', kind: 'rect',
w: w.value, w: w.value,
h: h.value, h: h.value,
color: color.value, color: color && color.kind === 'string' ? color.value : undefined,
strokeColor: strokeColor && strokeColor.kind === 'string' ? strokeColor.value : undefined,
strokeWidth: strokeWidth && strokeWidth.kind === 'int' ? strokeWidth.value : undefined,
radius: radius && radius.kind === 'int' ? radius.value : 0 radius: radius && radius.kind === 'int' ? radius.value : 0
}; };
} }
case 'Text': { case 'Text': {
const x = fields.x;
const y = fields.y;
const content = fields.content; const content = fields.content;
if (content.kind !== 'string' || x.kind !== 'int' || y.kind !== 'int') if (content.kind !== 'string')
throw new Error('Invalid Text fields'); throw new Error('Invalid Text fields');
return { kind: 'text', x: x.value, y: y.value, content: content.value }; return { kind: 'text', content: content.value };
} }
case 'Column': { case 'Column': {

Loading…
Cancel
Save