From 5d7d297050b1fc4e75b0082bfa557c881b273468 Mon Sep 17 00:00:00 2001 From: Dustin Swan Date: Sat, 21 Feb 2026 21:31:18 -0700 Subject: [PATCH] Allowing textInput to be controlled or uncontrolled. adding tab to palette to replace query text with selected item --- src/cg/03-ui-components.cg | 59 +++++++++++++++++++++----------------- src/cg/05-palette.cg | 7 ++++- 2 files changed, 39 insertions(+), 27 deletions(-) diff --git a/src/cg/03-ui-components.cg b/src/cg/03-ui-components.cg index e945517..ad105e5 100644 --- a/src/cg/03-ui-components.cg +++ b/src/cg/03-ui-components.cg @@ -168,51 +168,58 @@ textInput = config \ }, # update : State \ Event \ State - update = state event \ event + update = state event \ + s = (hasField "value" c + | True \ (c.value == state.text + | True \ state + | False \ state.{ text = c.value, cursorPos = len c.value, scrollOffset = 0 }) + | False \ state); + event | Key { key = key, printable = True, meta = False, ctrl = False } \ ( - newText = insertChar state.text state.cursorPos key; - newCursorPos = state.cursorPos + 1; - newScroll = calcScrollOffset newText newCursorPos state.scrollOffset c.w; - newState = state.{ text = newText, cursorPos = newCursorPos, scrollOffset = newScroll }; + newText = insertChar s.text s.cursorPos key; + newCursorPos = s.cursorPos + 1; + newScroll = calcScrollOffset newText newCursorPos s.scrollOffset c.w; + newState = s.{ text = newText, cursorPos = newCursorPos, scrollOffset = newScroll }; { state = newState, emit = [c.onChange newText] }) | Key { key = "ArrowLeft" } \ ( - newCursorPos = max 0 (state.cursorPos - 1); - newScroll = calcScrollOffset state.text newCursorPos state.scrollOffset c.w; - newState = state.{ text = state.text, cursorPos = newCursorPos, scrollOffset = newScroll }; + newCursorPos = max 0 (s.cursorPos - 1); + newScroll = calcScrollOffset s.text newCursorPos s.scrollOffset c.w; + newState = s.{ text = s.text, cursorPos = newCursorPos, scrollOffset = newScroll }; { state = newState, emit = [] }) | Key { key = "ArrowRight" } \ ( - newCursorPos = min (len state.text) (state.cursorPos + 1); - newScroll = calcScrollOffset state.text newCursorPos state.scrollOffset c.w; - newState = state.{ text = state.text, cursorPos = newCursorPos, scrollOffset = newScroll }; + newCursorPos = min (len s.text) (s.cursorPos + 1); + newScroll = calcScrollOffset s.text newCursorPos s.scrollOffset c.w; + newState = s.{ text = s.text, cursorPos = newCursorPos, scrollOffset = newScroll }; { state = newState, emit = [] }) | Key { key = "Backspace" } \ ( - newText = deleteChar state.text state.cursorPos; - newCursorPos = max 0 (state.cursorPos - 1); - newScroll = calcScrollOffset newText newCursorPos state.scrollOffset c.w; - newState = state.{ text = newText, cursorPos = newCursorPos, scrollOffset = newScroll }; + newText = deleteChar s.text s.cursorPos; + newCursorPos = max 0 (s.cursorPos - 1); + newScroll = calcScrollOffset newText newCursorPos s.scrollOffset c.w; + newState = s.{ text = newText, cursorPos = newCursorPos, scrollOffset = newScroll }; { state = newState, emit = [c.onChange newText] }) - | Key { key = "Enter" } \ ( - # newState = state.{ text = "", cursorPos = 0, scrollOffset = 0 }; - # do we blank it out here? wish parent could decide.. - { state = state, emit = [c.onSubmit state.text] }) + | Key { key = "Enter" } \ { state = s, emit = [c.onSubmit s.text] } | Clicked coords \ ( - newCursorPos = findCursorPos state.text coords.x state.scrollOffset 8; - newScroll = calcScrollOffset state.text newCursorPos state.scrollOffset c.w; - newState = state.{ text = state.text, cursorPos = newCursorPos, scrollOffset = newScroll }; + newCursorPos = findCursorPos s.text coords.x s.scrollOffset 8; + newScroll = calcScrollOffset s.text newCursorPos s.scrollOffset c.w; + newState = s.{ text = s.text, cursorPos = newCursorPos, scrollOffset = newScroll }; { state = newState, emit = [] }) | Focused \ { state = state.{ focused = True }, emit = [] } | Blurred \ { state = state.{ focused = False }, emit = [] } - # | Key { key = k } \ { state = state, emit = [\ c.onKeyDown k ] } - | _ \ { state = state, emit = [] }, + | SetText newText \ ( + newState = s.{ text = newText, cursorPos = len newText, scrollOffset = 0 }; + { state = newState, emit = [c.onChange newText] }) + | _ \ { state = s, emit = [] }, view = state emit \ - textBeforeCursor = slice state.text 0 state.cursorPos; + text = (hasField "value" c | True \ c.value | False \ state.text); + cursorPos = (text == state.text | True \ state.cursorPos | False \ len text); + textBeforeCursor = slice text 0 cursorPos; cursorX = ui.measureText textBeforeCursor; padding = 8; @@ -226,7 +233,7 @@ textInput = config \ ui.positioned { x = 8 - state.scrollOffset, y = 0, - child = ui.positioned { x = 0, y = 12, child = ui.text { content = state.text, color = c.color } } + child = ui.positioned { x = 0, y = 12, child = ui.text { content = text, color = c.color } } }, (state.focused diff --git a/src/cg/05-palette.cg b/src/cg/05-palette.cg index 9641731..3a4306f 100644 --- a/src/cg/05-palette.cg +++ b/src/cg/05-palette.cg @@ -97,6 +97,11 @@ palette = config \ (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 = [] }, @@ -118,7 +123,7 @@ palette = config \ children = [ textInput { key = "palette-query", - initialValue = paletteState.query, + value = paletteState.query, autoFocus = True, color = "white", backgroundColor = "rgba(0,0,0,0.2)",