Letrec. index in stdlib. fixing NaNs when measuring things without gaps, w or h. more window management stuff in OS
This commit is contained in:
parent
db4abfa450
commit
5f592f40bf
5 changed files with 89 additions and 15 deletions
|
|
@ -16,6 +16,15 @@ mapWithIndex = f list \
|
|||
f2 = acc x \ { i = acc.i + 1, result = [...acc.result, f x acc.i] };
|
||||
(fold f2 init list).result;
|
||||
|
||||
# index : a \ List a \ Maybe Int
|
||||
index = x xs \
|
||||
go = i rest \ rest
|
||||
| [] \ None
|
||||
| [y, ...ys] \ (x == y
|
||||
| True \ Some i
|
||||
| False \ go (i + 1) ys);
|
||||
go 0 xs;
|
||||
|
||||
# filter : (a \ Bool) \ List a \ List a
|
||||
filter = f list \ list
|
||||
| [] \ []
|
||||
|
|
|
|||
|
|
@ -18,13 +18,25 @@ openWindow = title content \
|
|||
osState.palette.visible := False
|
||||
];
|
||||
|
||||
closeFocused = _ \
|
||||
i = osState.wm.focusedIndex;
|
||||
closeWindowByIndex = i \
|
||||
focused = osState.wm.focusedIndex;
|
||||
newFocused = (i < focused
|
||||
| True \ focused - 1
|
||||
| False \ (i == focused
|
||||
| True \ max 0 (focused - 1)
|
||||
| False \ focused));
|
||||
|
||||
batch [
|
||||
osState.windows := (take i osState.windows) & (drop (i + 1) osState.windows),
|
||||
osState.wm.focusedIndex := max 0 (osState.wm.focusedIndex - 1)
|
||||
osState.wm.focusedIndex := min newFocused (len osState.windows - 2)
|
||||
];
|
||||
|
||||
closeFocusedWindow = _ \ closeWindowByIndex osState.wm.focusedIndex;
|
||||
|
||||
closeWindowById = id \
|
||||
i = index id (map (w \ w.id) osState.windows);
|
||||
closeWindowByIndex (unwrapOr 0 i);
|
||||
|
||||
onSelect = input \
|
||||
result = eval input;
|
||||
result
|
||||
|
|
@ -33,7 +45,6 @@ onSelect = input \
|
|||
| Err msg \ openWindow "Error" (ui.text { content = msg, color = "red" });
|
||||
|
||||
renderWindow = window isActive \
|
||||
_ = debug "renderWindow" window.title;
|
||||
titleBarHeight = 30;
|
||||
ui.stack {
|
||||
children = [
|
||||
|
|
@ -45,8 +56,29 @@ renderWindow = window isActive \
|
|||
x = 0, y = 0,
|
||||
child = ui.stack {
|
||||
children = [
|
||||
# background
|
||||
ui.rect { w = window.width, h = titleBarHeight, color = (isActive | True \ "#a15f80" | False \ "#0f3348") },
|
||||
ui.positioned { x = 8, y = 8, child = ui.text { content = window.title, color = "white" } },
|
||||
ui.row {
|
||||
children = [
|
||||
# close button
|
||||
ui.clickable {
|
||||
event = closeWindowById window.id,
|
||||
child = ui.stack {
|
||||
children = [
|
||||
# button background
|
||||
ui.rect { w = titleBarHeight, h = titleBarHeight, color = "rgba(255,255,255,0.2)" },
|
||||
# button text
|
||||
ui.positioned {
|
||||
x = 9, y = 7, child = ui.text { content = "x" }
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
# title
|
||||
ui.positioned { x = 8, y = 8, child = ui.text { content = window.title, color = "white" } },
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
|
|
@ -63,12 +95,23 @@ renderWindow = window isActive \
|
|||
]
|
||||
};
|
||||
|
||||
windowComponent = config \ ui.stateful {
|
||||
key = "window-" & (show config.index),
|
||||
focusable = True,
|
||||
init = {},
|
||||
update = state event \ event
|
||||
| Focused \ { state = state, emit = [osState.wm.focusedIndex := config.index] }
|
||||
| ChildFocused \ { state = state, emit = [osState.wm.focusedIndex := config.index] }
|
||||
| _ \ { state = state, emit = [] },
|
||||
view = state \ renderWindow config.window config.isActive
|
||||
};
|
||||
|
||||
renderWindows = _ \
|
||||
windows = osState.windows;
|
||||
focused = osState.wm.focusedIndex;
|
||||
ui.row {
|
||||
gap = 1,
|
||||
children = mapWithIndex (w i \ renderWindow w (i == focused)) windows
|
||||
children = mapWithIndex (w i \ windowComponent { window = w, index = i, isActive = (i == focused) }) windows
|
||||
};
|
||||
|
||||
os = ui.stateful {
|
||||
|
|
@ -85,8 +128,8 @@ os = ui.stateful {
|
|||
| Key { key = "ArrowRight", meta = True } \
|
||||
{ state = state, emit = [osState.wm.focusedIndex := min (len osState.windows - 1) (osState.wm.focusedIndex + 1)] }
|
||||
| Key { key = "d", meta = True } \
|
||||
{ state = state, emit = [closeFocused 0] }
|
||||
| _ \ _ = debug "key" []; { state = state, emit = [] },
|
||||
{ state = state, emit = [closeFocusedWindow 0] }
|
||||
| _ \ { state = state, emit = [] },
|
||||
|
||||
view = state \
|
||||
ui.stack {
|
||||
|
|
|
|||
|
|
@ -71,8 +71,8 @@ export function compile(ast: AST, useStore = true, bound = new Set<string>(), to
|
|||
|
||||
case 'let':
|
||||
const newBound = new Set([...bound, ast.name]);
|
||||
return `((${sanitizeName(ast.name)}) =>
|
||||
${compile(ast.body, useStore, newBound, topLevel)})(${compile(ast.value, useStore, bound, topLevel)})`;
|
||||
return `(() => { let ${sanitizeName(ast.name)} = ${compile(ast.value, useStore, newBound, topLevel)};
|
||||
return ${compile(ast.body, useStore, newBound, topLevel)}; })()`;
|
||||
|
||||
case 'match':
|
||||
return compileMatch(ast, useStore, bound, topLevel);
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ export function runAppCompiled(canvas: HTMLCanvasElement, store: any) {
|
|||
|
||||
function setFocus(componentKey: string | null) {
|
||||
if (focusedComponentKey === componentKey) return;
|
||||
|
||||
const oldFocus = focusedComponentKey;
|
||||
focusedComponentKey = componentKey;
|
||||
|
||||
|
|
@ -36,6 +37,15 @@ export function runAppCompiled(canvas: HTMLCanvasElement, store: any) {
|
|||
if (componentKey && componentInstances.has(componentKey)) {
|
||||
handleComponentEvent(componentKey, { _tag: 'Focused' });
|
||||
}
|
||||
|
||||
// Notify ancestors
|
||||
if (componentKey) {
|
||||
for (const key of componentInstances.keys()) {
|
||||
if (key !== componentKey && componentKey.startsWith(key + '.')) {
|
||||
handleComponentEvent(key, { _tag: 'ChildFocused' });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function handleComponentEvent(componentKey: string, event: any) {
|
||||
|
|
|
|||
22
src/ui.ts
22
src/ui.ts
|
|
@ -88,8 +88,10 @@ function renderUI(ui: UIValue, ctx: CanvasRenderingContext2D, x: number, y: numb
|
|||
case 'row': {
|
||||
let offsetX = 0;
|
||||
for (const child of ui.children) {
|
||||
const size = measure(child);
|
||||
console.log("row child: ", child.kind, "width: ", size.width)
|
||||
renderUI(child, ctx, x + offsetX, y);
|
||||
offsetX += measure(child).width + ui.gap;
|
||||
offsetX += measure(child).width + (ui.gap || 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
@ -98,7 +100,7 @@ function renderUI(ui: UIValue, ctx: CanvasRenderingContext2D, x: number, y: numb
|
|||
let offsetY = 0;
|
||||
for (const child of ui.children) {
|
||||
renderUI(child, ctx, x, y + offsetY);
|
||||
offsetY += measure(child).height + ui.gap;
|
||||
offsetY += measure(child).height + (ui.gap || 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
@ -146,8 +148,16 @@ function renderUI(ui: UIValue, ctx: CanvasRenderingContext2D, x: number, y: numb
|
|||
}
|
||||
|
||||
export function measure(ui: UIValue): { width: number, height: number } {
|
||||
const result = _measure(ui);
|
||||
if (isNaN(result.width) || isNaN(result.height)) {
|
||||
console.warn('NaN from:', ui.kind, ui);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export function _measure(ui: UIValue): { width: number, height: number } {
|
||||
switch (ui.kind) {
|
||||
case 'rect': return { width: ui.w, height: ui.h };
|
||||
case 'rect': return { width: ui.w || 0, height: ui.h || 0 };
|
||||
|
||||
case 'text': return { width: ui.content.length * 10, height: 20 }; // TODO
|
||||
|
||||
|
|
@ -159,7 +169,7 @@ export function measure(ui: UIValue): { width: number, height: number } {
|
|||
totalWidth += size.width;
|
||||
maxHeight = Math.max(maxHeight, size.height);
|
||||
}
|
||||
totalWidth += ui.gap * (ui.children.length - 1);
|
||||
totalWidth += (ui.gap || 0) * (ui.children.length - 1);
|
||||
return { width: totalWidth, height: maxHeight };
|
||||
}
|
||||
|
||||
|
|
@ -171,7 +181,7 @@ export function measure(ui: UIValue): { width: number, height: number } {
|
|||
totalHeight += size.height;
|
||||
maxWidth = Math.max(maxWidth, size.width);
|
||||
}
|
||||
totalHeight += ui.gap * (ui.children.length - 1);
|
||||
totalHeight += (ui.gap || 0) * (ui.children.length - 1);
|
||||
return { width: maxWidth, height: totalHeight };
|
||||
}
|
||||
|
||||
|
|
@ -209,6 +219,8 @@ export function measure(ui: UIValue): { width: number, height: number } {
|
|||
|
||||
case 'stateful':
|
||||
throw new Error('Stateful components cannot be measured');
|
||||
|
||||
default: return { width: 0, height: 0 };
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue