|
|
|
|
@ -85,9 +85,6 @@ textEditor = name \
|
|
|
|
|
newLines = updateAt state.cursorRow (_ \ before) state.lines;
|
|
|
|
|
newLines2 = insertAt (state.cursorRow + 1) after newLines;
|
|
|
|
|
|
|
|
|
|
_ = debug! "enter" { before = before, after = after, newLines2 = newLines2, newRow =
|
|
|
|
|
state.cursorRow + 1 };
|
|
|
|
|
|
|
|
|
|
{ state = state.{ lines = newLines2, cursorCol = 0, cursorRow = state.cursorRow + 1 }, emit = [] };
|
|
|
|
|
|
|
|
|
|
clampCursor = state \
|
|
|
|
|
@ -103,157 +100,184 @@ textEditor = name \
|
|
|
|
|
|
|
|
|
|
state.{ cursorRow = newRow2, cursorCol = newCol2 };
|
|
|
|
|
|
|
|
|
|
upArrow = state \ (
|
|
|
|
|
upArrow = state \
|
|
|
|
|
newState = clampCursor state.{ cursorRow = state.cursorRow - 1 };
|
|
|
|
|
{ state = newState, emit = [] });
|
|
|
|
|
{ state = newState, emit = [] };
|
|
|
|
|
|
|
|
|
|
downArrow = state \ (
|
|
|
|
|
downArrow = state \
|
|
|
|
|
newState = clampCursor state.{ cursorRow = state.cursorRow + 1 };
|
|
|
|
|
{ state = newState, emit = [] });
|
|
|
|
|
{ state = newState, emit = [] };
|
|
|
|
|
|
|
|
|
|
leftArrow = state \ (
|
|
|
|
|
leftArrow = state \
|
|
|
|
|
newState = clampCursor state.{ cursorCol = state.cursorCol - 1 };
|
|
|
|
|
{ state = newState, emit = [] });
|
|
|
|
|
{ state = newState, emit = [] };
|
|
|
|
|
|
|
|
|
|
rightArrow = state \ (
|
|
|
|
|
rightArrow = state \
|
|
|
|
|
newState = clampCursor state.{ cursorCol = state.cursorCol + 1 };
|
|
|
|
|
{ state = newState, emit = [] });
|
|
|
|
|
{ state = newState, emit = [] };
|
|
|
|
|
|
|
|
|
|
# todo: scrollHalfDown = state \ ();
|
|
|
|
|
# todo: scrollHalfUp = state \ ();
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
view = ctx \ ui.stateful {
|
|
|
|
|
focusable = True,
|
|
|
|
|
autoFocus = True,
|
|
|
|
|
|
|
|
|
|
key = "textEditor-" & name,
|
|
|
|
|
|
|
|
|
|
init = {
|
|
|
|
|
lines = lines,
|
|
|
|
|
cursorRow = 0,
|
|
|
|
|
cursorCol = 0,
|
|
|
|
|
scrollX = 0,
|
|
|
|
|
scrollY = 0,
|
|
|
|
|
undoStack = [],
|
|
|
|
|
redoStack = [],
|
|
|
|
|
mode = Normal # Normal | Insert | Visual
|
|
|
|
|
},
|
|
|
|
|
view = ctx \
|
|
|
|
|
|
|
|
|
|
update = state event \ event
|
|
|
|
|
| Scrolled delta \ (
|
|
|
|
|
newY = max 0 (state.scrollY + delta.deltaY);
|
|
|
|
|
newX = max 0 (state.scrollX + delta.deltaX);
|
|
|
|
|
{ state = state.{ scrollY = newY, scrollX = newX }, emit = [] })
|
|
|
|
|
|
|
|
|
|
| Key { key = "ArrowDown" } \ downArrow state
|
|
|
|
|
| Key { key = "j" } \ (state.mode
|
|
|
|
|
| Insert \ insertChar "j" state
|
|
|
|
|
| Normal \ downArrow state)
|
|
|
|
|
|
|
|
|
|
| Key { key = "ArrowUp" } \ upArrow state
|
|
|
|
|
| Key { key = "k" } \ (state.mode
|
|
|
|
|
| Insert \ insertChar "k" state
|
|
|
|
|
| Normal \ upArrow state)
|
|
|
|
|
|
|
|
|
|
| Key { key = "ArrowLeft" } \ leftArrow state
|
|
|
|
|
| Key { key = "h" } \ (state.mode
|
|
|
|
|
| Insert \ insertChar "h" state
|
|
|
|
|
| Normal \ leftArrow state)
|
|
|
|
|
|
|
|
|
|
| Key { key = "ArrowRight" } \ rightArrow state
|
|
|
|
|
| Key { key = "l" } \ (state.mode
|
|
|
|
|
| Insert \ insertChar "l" state
|
|
|
|
|
| Normal \ rightArrow state)
|
|
|
|
|
|
|
|
|
|
| Key { key = "i" } \ (state.mode
|
|
|
|
|
| Insert \ insertChar "i" state
|
|
|
|
|
| Normal \ insertMode state)
|
|
|
|
|
|
|
|
|
|
| Key { key = "u" } \ (state.mode
|
|
|
|
|
| Insert \ insertChar "u" state
|
|
|
|
|
| Normal \ undo state)
|
|
|
|
|
|
|
|
|
|
| Key { key = "r", ctrl = True } \ (state.mode
|
|
|
|
|
| Insert \ { state = state, emit = [] }
|
|
|
|
|
| Normal \ redo state)
|
|
|
|
|
|
|
|
|
|
| Key { key = "W", shift = True } \ (state.mode
|
|
|
|
|
| Insert \ insertChar "W" state
|
|
|
|
|
| Normal \ write state)
|
|
|
|
|
|
|
|
|
|
| Key { key = "W", shift = True } \ (state.mode
|
|
|
|
|
| Insert \ insertChar "W" state
|
|
|
|
|
| Normal \ write state)
|
|
|
|
|
|
|
|
|
|
| Key { key = "A", shift = True } \ (state.mode
|
|
|
|
|
| Insert \ insertChar "A" state
|
|
|
|
|
| Normal \ apply state)
|
|
|
|
|
|
|
|
|
|
| Key { key = "Escape" } \ escape state
|
|
|
|
|
|
|
|
|
|
| Key { key = "Backspace" } \ (state.mode
|
|
|
|
|
| Insert \ backspace state
|
|
|
|
|
| _ \ { state = state, emit = [] })
|
|
|
|
|
|
|
|
|
|
| Key { key = "Enter" } \ (state.mode
|
|
|
|
|
| Insert \ enter state
|
|
|
|
|
| _ \ { state = state, emit = [] })
|
|
|
|
|
|
|
|
|
|
# any other key
|
|
|
|
|
| Key { key = key, printable = True } \ (state.mode
|
|
|
|
|
| Insert \ insertChar key state
|
|
|
|
|
| _ \ { state = state, emit = [] })
|
|
|
|
|
|
|
|
|
|
| _ \ { state = state, emit = [] },
|
|
|
|
|
|
|
|
|
|
view = state emit \
|
|
|
|
|
scale = 2;
|
|
|
|
|
charH = 12;
|
|
|
|
|
charW = 5;
|
|
|
|
|
lineGap = 1;
|
|
|
|
|
charGap = 2;
|
|
|
|
|
lineH = charH * scale + lineGap;
|
|
|
|
|
scale = 2;
|
|
|
|
|
charH = 12;
|
|
|
|
|
charW = 5;
|
|
|
|
|
lineGap = 1;
|
|
|
|
|
charGap = 2;
|
|
|
|
|
lineH = charH * scale + lineGap;
|
|
|
|
|
|
|
|
|
|
autoScroll = state \
|
|
|
|
|
cursorY = state.cursorRow * lineH;
|
|
|
|
|
scrollY = (cursorY < state.scrollY
|
|
|
|
|
newScrollY = (cursorY < state.scrollY
|
|
|
|
|
| True \ cursorY
|
|
|
|
|
| False \ (cursorY + lineH > state.scrollY + ctx.h
|
|
|
|
|
| True \ cursorY + lineH - ctx.h
|
|
|
|
|
| False \ state.scrollY));
|
|
|
|
|
|
|
|
|
|
cursorX = state.cursorCol * charW * scale + charGap * state.cursorCol;
|
|
|
|
|
scrollX = (cursorX < state.scrollX
|
|
|
|
|
newScrollX = (cursorX < state.scrollX
|
|
|
|
|
| True \ cursorX
|
|
|
|
|
| False \ (cursorX + charW * scale > state.scrollX + ctx.w
|
|
|
|
|
| True \ cursorX + charW * scale - ctx.w
|
|
|
|
|
| False \ state.scrollX));
|
|
|
|
|
|
|
|
|
|
buffer = map (l \ text l) state.lines;
|
|
|
|
|
|
|
|
|
|
cursor = ui.positioned {
|
|
|
|
|
x = cursorX,
|
|
|
|
|
y = cursorY,
|
|
|
|
|
child = ui.rect { w = charW * scale, h = charH * scale, color = "rgba(255,255,255,0.5)" }
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
maxLineLen = fold (acc line \ max acc (len line)) 0 state.lines;
|
|
|
|
|
totalWidth = maxLineLen * charW * scale + maxLineLen * charGap;
|
|
|
|
|
totalHeight = len state.lines * lineH;
|
|
|
|
|
|
|
|
|
|
scrollable {
|
|
|
|
|
w = ctx.w,
|
|
|
|
|
h = ctx.h,
|
|
|
|
|
totalWidth = totalWidth,
|
|
|
|
|
totalHeight = totalHeight,
|
|
|
|
|
scrollX = scrollX,
|
|
|
|
|
scrollY = scrollY,
|
|
|
|
|
child = ui.stack {
|
|
|
|
|
children = [
|
|
|
|
|
ui.column {
|
|
|
|
|
gap = 1,
|
|
|
|
|
children = buffer
|
|
|
|
|
},
|
|
|
|
|
cursor
|
|
|
|
|
]
|
|
|
|
|
state.{ scrollY = newScrollY, cursorY = cursorY, scrollX = newScrollX, cursorX = cursorX };
|
|
|
|
|
|
|
|
|
|
withScroll = result \
|
|
|
|
|
{ state = autoScroll result.state, emit = result.emit };
|
|
|
|
|
|
|
|
|
|
ui.stateful {
|
|
|
|
|
focusable = True,
|
|
|
|
|
autoFocus = True,
|
|
|
|
|
|
|
|
|
|
key = "textEditor-" & name,
|
|
|
|
|
|
|
|
|
|
init = {
|
|
|
|
|
lines = lines,
|
|
|
|
|
cursorRow = 0,
|
|
|
|
|
cursorCol = 0,
|
|
|
|
|
scrollX = 0,
|
|
|
|
|
scrollY = 0,
|
|
|
|
|
undoStack = [],
|
|
|
|
|
redoStack = [],
|
|
|
|
|
mode = Normal # Normal | Insert | Visual
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
update = state event \ event
|
|
|
|
|
| Scrolled delta \ (
|
|
|
|
|
maxLineLen = fold (acc line \ max acc (len line)) 0 state.lines;
|
|
|
|
|
totalW = maxLineLen * charW * scale + maxLineLen * charGap;
|
|
|
|
|
totalH = len state.lines * lineH;
|
|
|
|
|
newX = max 0 (min (totalW - ctx.w) (state.scrollX + delta.deltaX));
|
|
|
|
|
newY = max 0 (min (totalH - ctx.h) (state.scrollY + delta.deltaY));
|
|
|
|
|
{ state = state.{ scrollY = newY, scrollX = newX }, emit = [] })
|
|
|
|
|
|
|
|
|
|
| Key { key = "ArrowDown" } \ withScroll (downArrow state)
|
|
|
|
|
| Key { key = "j" } \ (state.mode
|
|
|
|
|
| Insert \ insertChar "j" state
|
|
|
|
|
| Normal \ withScroll (downArrow state))
|
|
|
|
|
|
|
|
|
|
| Key { key = "ArrowUp" } \ withScroll (upArrow state)
|
|
|
|
|
| Key { key = "k" } \ (state.mode
|
|
|
|
|
| Insert \ insertChar "k" state
|
|
|
|
|
| Normal \ withScroll(upArrow state))
|
|
|
|
|
|
|
|
|
|
| Key { key = "ArrowLeft" } \ withScroll (leftArrow state)
|
|
|
|
|
| Key { key = "h" } \ (state.mode
|
|
|
|
|
| Insert \ insertChar "h" state
|
|
|
|
|
| Normal \ withScroll(leftArrow state))
|
|
|
|
|
|
|
|
|
|
| Key { key = "ArrowRight" } \ withScroll (rightArrow state)
|
|
|
|
|
| Key { key = "l" } \ (state.mode
|
|
|
|
|
| Insert \ insertChar "l" state
|
|
|
|
|
| Normal \ withScroll(rightArrow state))
|
|
|
|
|
|
|
|
|
|
| Key { key = "i" } \ (state.mode
|
|
|
|
|
| Insert \ insertChar "i" state
|
|
|
|
|
| Normal \ insertMode state)
|
|
|
|
|
|
|
|
|
|
| Key { key = "u" } \ (state.mode
|
|
|
|
|
| Insert \ insertChar "u" state
|
|
|
|
|
| Normal \ undo state)
|
|
|
|
|
|
|
|
|
|
| Key { key = "r", ctrl = True } \ (state.mode
|
|
|
|
|
| Insert \ { state = state, emit = [] }
|
|
|
|
|
| Normal \ redo state)
|
|
|
|
|
|
|
|
|
|
| Key { key = "W", shift = True } \ (state.mode
|
|
|
|
|
| Insert \ insertChar "W" state
|
|
|
|
|
| Normal \ write state)
|
|
|
|
|
|
|
|
|
|
| Key { key = "W", ctrl = True, shift = True } \ (state.mode
|
|
|
|
|
| Insert \ insertChar "W" state
|
|
|
|
|
| Normal \ write state)
|
|
|
|
|
|
|
|
|
|
| Key { key = "A", ctrl = True, shift = True } \ (state.mode
|
|
|
|
|
| Insert \ insertChar "A" state
|
|
|
|
|
| Normal \ apply state)
|
|
|
|
|
|
|
|
|
|
| Key { key = "d", ctrl = True } \ scrollHalfDown state
|
|
|
|
|
| Key { key = "u", ctrl = True } \ scrollHalfUp state
|
|
|
|
|
|
|
|
|
|
| Key { key = "Escape" } \ escape state
|
|
|
|
|
|
|
|
|
|
| Key { key = "Backspace" } \ (state.mode
|
|
|
|
|
| Insert \ backspace state
|
|
|
|
|
| _ \ { state = state, emit = [] })
|
|
|
|
|
|
|
|
|
|
| Key { key = "Enter" } \ (state.mode
|
|
|
|
|
| Insert \ enter state
|
|
|
|
|
| _ \ { state = state, emit = [] })
|
|
|
|
|
|
|
|
|
|
# any other key
|
|
|
|
|
| Key { key = key, printable = True } \ (state.mode
|
|
|
|
|
| Insert \ insertChar key state
|
|
|
|
|
| _ \ { state = state, emit = [] })
|
|
|
|
|
|
|
|
|
|
| _ \ { state = state, emit = [] },
|
|
|
|
|
|
|
|
|
|
view = state emit \
|
|
|
|
|
_ = debug! "here" state;
|
|
|
|
|
cursor = ui.positioned {
|
|
|
|
|
x = state.cursorX,
|
|
|
|
|
y = state.cursorY,
|
|
|
|
|
child = ui.rect { w = charW * scale, h = charH * scale, color = "rgba(255,255,255,0.5)" }
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
maxLineLen = fold (acc line \ max acc (len line)) 0 state.lines;
|
|
|
|
|
totalWidth = maxLineLen * charW * scale + maxLineLen * charGap;
|
|
|
|
|
totalHeight = len state.lines * lineH;
|
|
|
|
|
|
|
|
|
|
# buffer = map (l \ text l) state.lines;
|
|
|
|
|
# only render visible lines
|
|
|
|
|
startLine = max 0 (floor (state.scrollY / lineH));
|
|
|
|
|
visibleCount = floor (ctx.h / lineH) + 2;
|
|
|
|
|
endLine = min (len state.lines) (startLine + visibleCount);
|
|
|
|
|
visibleLines = slice state.lines startLine endLine;
|
|
|
|
|
buffer = mapWithIndex (l i \
|
|
|
|
|
ui.positioned {
|
|
|
|
|
x = 0,
|
|
|
|
|
y = (startLine + i) * lineH,
|
|
|
|
|
child = text l
|
|
|
|
|
}
|
|
|
|
|
) visibleLines;
|
|
|
|
|
|
|
|
|
|
scrollable {
|
|
|
|
|
w = ctx.w,
|
|
|
|
|
h = ctx.h,
|
|
|
|
|
totalWidth = totalWidth,
|
|
|
|
|
totalHeight = totalHeight,
|
|
|
|
|
scrollX = state.scrollX,
|
|
|
|
|
scrollY = state.scrollY,
|
|
|
|
|
onScroll = delta \ emit (Scrolled delta),
|
|
|
|
|
child = ui.stack {
|
|
|
|
|
children = [
|
|
|
|
|
...buffer,
|
|
|
|
|
cursor
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|