170 lines
6 KiB
Text
170 lines
6 KiB
Text
@palette
|
|
|
|
paletteState = {
|
|
query = "",
|
|
focusedIndex = 0,
|
|
scrollOffset = 0,
|
|
opacity = 0.95,
|
|
scrollIndex = 0
|
|
};
|
|
|
|
palette = config \
|
|
windowHeight = 400;
|
|
windowWidth = 600;
|
|
results = take 100 (config.search paletteState.query);
|
|
nextSelectable = items index direction \
|
|
next = index + direction;
|
|
nth next items
|
|
| (Some (Section _)) \ nextSelectable items next direction
|
|
| (Some (Item _)) \ next
|
|
| None \ index;
|
|
effectiveIndex = nth paletteState.focusedIndex results
|
|
| (Some (Section _)) \ nextSelectable results paletteState.focusedIndex 1
|
|
| _ \ paletteState.focusedIndex;
|
|
dialogPadding = 0;
|
|
itemGap = 1;
|
|
textInputHeight = 40;
|
|
sectionHeight = 30;
|
|
contentWidth = windowWidth - (dialogPadding * 2);
|
|
contentHeight = windowHeight - (dialogPadding * 2);
|
|
listHeight = contentHeight - 40;
|
|
itemHeight = entry \
|
|
entry
|
|
| (Section _) \ sectionHeight
|
|
| (Item _) \ textInputHeight
|
|
| _ \ 0;
|
|
totalHeight = (sum (map itemHeight results)) + (itemGap * ((len results) - 1));
|
|
itemY = i \ (sum (map itemHeight (take i results))) + i;
|
|
onScroll = delta \ paletteState.scrollOffset := max 0 (min (totalHeight - listHeight) (paletteState.scrollOffset + delta.deltaY));
|
|
scrollTo = index \
|
|
y = itemY index;
|
|
h = unwrapOr 0 (nth index (map itemHeight results));
|
|
offset = paletteState.scrollOffset;
|
|
y < offset
|
|
| True \ y
|
|
| False \ ((y + h) > (offset + listHeight)
|
|
| True \ (y + h) - listHeight
|
|
| False \ offset);
|
|
onSelect = text \ batch [
|
|
paletteState.query := "",
|
|
paletteState.focusedIndex := 0,
|
|
paletteState.scrollOffset := 0,
|
|
config.onSelect text
|
|
];
|
|
paletteRow = config \
|
|
color = config.selected
|
|
| True \ "rgba(255,255,255,0.2)"
|
|
| False \ "transparent";
|
|
ui.clickable {
|
|
onClick = config.onClick,
|
|
child = ui.stack { children = [
|
|
ui.rect { w = config.w, h = config.h, color = color },
|
|
ui.positioned { x = 6, y = 12, child = text config.child }
|
|
] }
|
|
};
|
|
ui.stateful {
|
|
key = "palette",
|
|
focusable = False,
|
|
init = { },
|
|
update = state event \
|
|
event
|
|
| (Key {printable = True}) \ {
|
|
state = state,
|
|
emit = [
|
|
paletteState.focusedIndex := nextSelectable results 0 1,
|
|
paletteState.scrollIndex := 0
|
|
]
|
|
}
|
|
| (Key {key = "ArrowUp"}) \ (newIndex = nextSelectable results effectiveIndex (0 - 1);
|
|
{
|
|
state = state,
|
|
emit = [
|
|
paletteState.focusedIndex := newIndex,
|
|
paletteState.scrollOffset := scrollTo newIndex
|
|
]
|
|
})
|
|
| (Key {key = "ArrowDown"}) \ (newIndex = nextSelectable results effectiveIndex 1;
|
|
{
|
|
state = state,
|
|
emit = [
|
|
paletteState.focusedIndex := newIndex,
|
|
paletteState.scrollOffset := scrollTo newIndex
|
|
]
|
|
})
|
|
| (Key {key = "Enter"}) \ (nth effectiveIndex results
|
|
| (Some (Item data)) \ { state = state, emit = [onSelect data.label] }
|
|
| _ \ { state = state, emit = [] })
|
|
| (Key {key = "Tab"}) \ (newQuery = nth effectiveIndex results
|
|
| (Some (Item data)) \ data.label
|
|
| None \ paletteState.query;
|
|
{ state = state, emit = [paletteState.query := newQuery] })
|
|
| (Key {key = "Escape"}) \ { state = state, emit = [osState.palette.visible := False] }
|
|
| _ \ { state = state, emit = [] },
|
|
view = state emit \ ui.positioned {
|
|
x = (config.viewport.width - windowWidth) / 2,
|
|
y = (config.viewport.height - windowHeight) / 2,
|
|
child = ui.opacity {
|
|
opacity = paletteState.opacity,
|
|
child = ui.stack { children = [
|
|
ui.rect {
|
|
w = windowWidth,
|
|
h = windowHeight,
|
|
color = "#063351",
|
|
radius = 0,
|
|
strokeWidth = 1,
|
|
strokeColor = "#1A5F80"
|
|
},
|
|
ui.padding {
|
|
amount = dialogPadding,
|
|
child = ui.column {
|
|
gap = 0,
|
|
children = [
|
|
textInput {
|
|
key = "palette-query",
|
|
value = paletteState.query,
|
|
autoFocus = True,
|
|
color = "white",
|
|
backgroundColor = "rgba(0,0,0,0.2)",
|
|
w = contentWidth,
|
|
h = textInputHeight,
|
|
onChange = text \ batch [paletteState.query := text]
|
|
},
|
|
scrollable {
|
|
w = contentWidth,
|
|
h = listHeight,
|
|
totalHeight = totalHeight,
|
|
totalWidth = contentWidth,
|
|
scrollX = 0,
|
|
scrollY = paletteState.scrollOffset,
|
|
onScroll = onScroll,
|
|
child = ui.column {
|
|
gap = itemGap,
|
|
children = [...mapWithIndex (entry i \
|
|
entry
|
|
| (Section title) \ box {
|
|
w = contentWidth,
|
|
h = sectionHeight,
|
|
color = "transparent",
|
|
paddingLeft = 6,
|
|
paddingTop = 8,
|
|
child = renderText { content = title, color = "#bbb" }
|
|
}
|
|
| (Item data) \ paletteRow {
|
|
child = data.label,
|
|
w = contentWidth,
|
|
h = textInputHeight,
|
|
selected = effectiveIndex == i,
|
|
onClick = _ \ onSelect data.label
|
|
}
|
|
| _ \ empty) results]
|
|
}
|
|
}
|
|
]
|
|
}
|
|
}
|
|
] }
|
|
}
|
|
}
|
|
};
|
|
|
|
@
|