diff --git a/src/main.ts b/src/main.ts index 9f7e92d..915463c 100644 --- a/src/main.ts +++ b/src/main.ts @@ -25,6 +25,7 @@ console.log(ast); const env: Env = new Map(Object.entries(builtins)); const appRecord = evaluate(ast, env); +console.log("appRecord", appRecord); if (appRecord.kind !== 'record') throw new Error('Expected record'); diff --git a/src/textinput-test.cg b/src/textinput-test.cg index 4fc642c..ab03d03 100644 --- a/src/textinput-test.cg +++ b/src/textinput-test.cg @@ -1,78 +1,48 @@ -# Helpers - -insertChar = text pos char \ - before = slice text 0 pos; - after = slice text pos (len text); - before & char & after; - -deleteChar = text pos \ - (pos == 0 - | True \ text +routeKeyToFocused = state event \ + (state.focusedInput == "email" + | True \ + newInputState = textInput2.update state.email event; + state.{ email = newInputState } | False \ - (before = slice text 0 (pos - 1); - after = slice text pos (len text); - before & after)); - -# test app + newInputState = textInput2.update state.password event; + state.{ password = newInputState }); init = { - text = "hello world", - cursorPos = 5 + focusedInput = "email", + email = textInput2.init "", + password = textInput2.init "" }; update = state event \ event - | ArrowLeft \ state.{ cursorPos = max 0 (state.cursorPos - 1) } - | ArrowRight \ state.{ cursorPos = min (len state.text) (state.cursorPos + 1) } - | Backspace \ { - text = deleteChar state.text state.cursorPos, - cursorPos = max 0 (state.cursorPos - 1) - } - | Char c \ { - text = insertChar state.text state.cursorPos c, - cursorPos = state.cursorPos + 1 - } + | FocusEmail \ state.{ focusedInput = "email" } + | FocusPassword \ state.{ focusedInput = "password" } + | ArrowLeft \ routeKeyToFocused state ArrowLeft + | ArrowRight \ routeKeyToFocused state ArrowRight + | Backspace \ routeKeyToFocused state Backspace + | Char c \ routeKeyToFocused state (Char c) | _ \ state; view = state viewport \ - # charWidth = 9.65; - # cursorX = state.cursorPos * charWidth; - textBeforeCursor = slice state.text 0 state.cursorPos; - cursorX = measureText textBeforeCursor; - - Column { - gap = 20, - children = [ - Text { - content = "Text: " & state.text & " | Cursor: " & str(state.cursorPos), - x = 0, - y = 20 - }, - - # Text Input Component - Stack { - children = [ - Rect { w = 300, h = 40, color = "white", radius = 4 }, - - # Text content - Positioned { - x = 8, - y = 8, - child = Text { content = state.text, x = 0, y = 17 } - }, - - # Cursor - Positioned { - x = 8 + cursorX, - y = 8, - child = Rect { - w = 2, - h = 24, - color = "black" - } - } - ] - } - ] - }; + Positioned { + x = 30, + y = 30, + child = Column { + gap = 10, + children = [ + (textInput2.view state.email) { + focused = state.focusedInput == "email", + onFocus = FocusEmail, + w = 300, + h = 40 + }, + (textInput2.view state.password) { + focused = state.focusedInput == "password", + onFocus = FocusPassword, + w = 300, + h = 40 + } + ] + } + }; { init = init, update = update, view = view } diff --git a/src/ui-components.cg b/src/ui-components.cg index d1a4616..1f27e80 100644 --- a/src/ui-components.cg +++ b/src/ui-components.cg @@ -9,3 +9,90 @@ button = config \ ] } }; + +insertChar = text pos char \ + before = slice text 0 pos; + after = slice text pos (len text); + before & char & after; + +deleteChar = text pos \ + (pos == 0 + | True \ text + | False \ + (before = slice text 0 (pos - 1); + after = slice text pos (len text); + before & after)); + +calcScrollOffset = text cursorPos scrollOffset inputWidth \ + textBeforeCursor = slice text 0 cursorPos; + cursorX = measureText textBeforeCursor; + (cursorX < scrollOffset + | True \ max 0 (cursorX - 20) + | False \ + (cursorX > (scrollOffset + inputWidth) + | True \ cursorX - inputWidth + 20 + | False \ scrollOffset)); + +textInput2 = { + # init : String \ State + init = text \ { text = text, cursorPos = 0, scrollOffset = 0 }, + + # update : State \ Event \ State + update = state event \ event + | ArrowLeft \ ( + newCursorPos = max 0 (state.cursorPos - 1); + newScroll = calcScrollOffset state.text newCursorPos state.scrollOffset 284; + { text = state.text, cursorPos = newCursorPos, scrollOffset = newScroll } + ) + + | ArrowRight \ ( + newCursorPos = min (len state.text) (state.cursorPos + 1); + newScroll = calcScrollOffset state.text newCursorPos state.scrollOffset 284; + { text = state.text, cursorPos = newCursorPos, scrollOffset = newScroll } + ) + + | Backspace \ ( + newText = deleteChar state.text state.cursorPos; + newCursorPos = max 0 (state.cursorPos - 1); + newScroll = calcScrollOffset newText newCursorPos state.scrollOffset 284; + { text = newText, cursorPos = newCursorPos, scrollOffset = newScroll } + ) + + | Char c \ ( + newText = insertChar state.text state.cursorPos c; + newCursorPos = state.cursorPos + 1; + newScroll = calcScrollOffset newText newCursorPos state.scrollOffset 284; + { text = newText, cursorPos = newCursorPos, scrollOffset = newScroll } + ) + + | _ \ state, + + view = state config \ + textBeforeCursor = slice state.text 0 state.cursorPos; + cursorX = measureText textBeforeCursor; + padding = 8; + + Clickable { + event = config.onFocus, + child = + Stack { + children = [ + Rect { w = config.w, h = config.h, color = "rgba(240,240,240,0.9)", radius = 4 }, + + Positioned { + x = 8 - state.scrollOffset, + y = 8, + child = Text { content = state.text, x = 0, y = 17 } + }, + + (config.focused + | True \ Positioned { + x = 8 + cursorX - state.scrollOffset, + y = 8, + child = Rect { w = 2, h = 24, color = "black" } + } + | _ \ Rect { w = 0, h = 0, color = "transparent" }) + ] + } + } +};