Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ jobs:
if: ${{ !matrix.emscripten }}
run: |
nim r tests/test_textboxes.nim
nim r -d:silkyTesting tests/test_inputs.nim
nim r tests/test_atlas_png.nim
nim r -d:silkyTesting tests/test_layout.nim
nim r -d:silkyTesting tests/test_layout_regressions.nim
Expand All @@ -74,6 +75,7 @@ jobs:
nim c ${{ matrix.build_flags }} examples/panels/panels.nim
nim c ${{ matrix.build_flags }} examples/the7gui/the7gui.nim
nim c ${{ matrix.build_flags }} examples/todomvc/todomvc.nim
nim c ${{ matrix.build_flags }} tests/manual_inputs.nim

- name: Compile examples (Emscripten)
if: matrix.emscripten
Expand Down
34 changes: 34 additions & 0 deletions docs/text_inputs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Text input

Construct Silky normally with `newSilky`. Text boxes activate text input when
clicked and keep it active between frames while focused. Clicking away, omitting
the focused field from a frame, or changing window focus ends text input. A
disabled field can select and copy text but cannot edit it.

Use `sk.buttonDown`, `sk.buttonPressed`, and `sk.buttonReleased` for application
controls. Keyboard keys in these views are false during text input, so typing
WASD cannot also move a character. Mouse buttons continue to work. Windy's raw
`window.buttonDown`, `window.buttonPressed`, and `window.buttonReleased` remain
unfiltered.

Creating Silky from a window automatically installs its input handlers. No
callback registration or input setup is needed. If window callbacks change,
Silky refreshes its handlers at the start of the next UI frame.

Application keyboard and rune callbacks are consumed during text input. Mouse
and focus callbacks still run. On entering text input, previously forwarded key
holds receive release callbacks so application controls do not remain stuck.
The corresponding physical releases are then consumed once.

Remove any manual `inputRunes` forwarding and `runeInputEnabled` assignments.
Silky manages rune input automatically. Each focused text box processes all
pending text edits in arrival order before drawing, using modifiers captured
with each event. There is no setup call or public queue to manage. Input beyond
the internal limit of 65,536 pending events raises `SilkyError`.

Native and Emscripten builds use the same Windy callbacks for text and modifier
state. No browser-specific input setup is needed.

Run `nim r tests/manual_inputs.nim` from the repository root to try single-line,
password, and multiline fields with a keyboard display. Hold keys outside the
fields to highlight them, then click a field and type to check suppression.
12 changes: 4 additions & 8 deletions examples/basicwindow/basicwindow.nim
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,6 @@ loadExtensions()

let sk = newSilky(window, "dist/atlas.png")

window.runeInputEnabled = true
window.onRune = proc(rune: Rune) =
sk.inputRunes.add(rune)

const
ElementOptions = ["Fire", "Water", "Earth", "Air"]
PowerOptions = ["Low", "Medium", "High"]
Expand Down Expand Up @@ -67,11 +63,11 @@ proc returnTest() =
characters "You will not see this."

window.onFrame = proc() =
if window.buttonPressed[KeyEqual] or
window.buttonPressed[NumpadAdd]:
if sk.buttonPressed[KeyEqual] or
sk.buttonPressed[NumpadAdd]:
sk.uiScale = min(sk.uiScale + 0.25'f, 4.0'f)
if window.buttonPressed[KeyMinus] or
window.buttonPressed[NumpadSubtract]:
if sk.buttonPressed[KeyMinus] or
sk.buttonPressed[NumpadSubtract]:
sk.uiScale = max(sk.uiScale - 0.25'f, 0.25'f)

sk.beginUI(window, window.size)
Expand Down
4 changes: 0 additions & 4 deletions examples/menu/menu.nim
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,6 @@ const BackgroundColor = parseHtmlColor("#808080").rgbx

let sk = newSilky(window, "dist/atlas.png")

window.runeInputEnabled = true
window.onRune = proc(rune: Rune) =
sk.inputRunes.add(rune)

window.onFrame = proc() =
sk.beginUI(window, window.size)
sk.clearScreen(BackgroundColor)
Expand Down
2 changes: 1 addition & 1 deletion examples/panels/panels.nim
Original file line number Diff line number Diff line change
Expand Up @@ -502,7 +502,7 @@ window.onFrame = proc() =
tint "#ffffff"

# Regenerate the layout when R is pressed.
if window.buttonPressed[KeyR]:
if sk.buttonPressed[KeyR]:
regenerate()

let ms = sk.avgFrameTime * 1000
Expand Down
4 changes: 0 additions & 4 deletions examples/the7gui/the7gui.nim
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,6 @@ sk.theme.buttonDownColor = rgbx(180, 180, 180, 255)
sk.theme.menuPopupHoverColor = parseHtmlColor("#3498DB").rgbx
sk.theme.menuPopupSelectedColor = parseHtmlColor("#2980B9").rgbx

window.runeInputEnabled = true
window.onRune = proc(rune: Rune) =
sk.inputRunes.add(rune)

var
showChallenges = true
showCounter = false
Expand Down
4 changes: 0 additions & 4 deletions examples/todomvc/todomvc.nim
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,6 @@ sk.theme.frameFocusColor = parseHtmlColor("#D5DBDB").rgbx
sk.theme.buttonHoverColor = rgbx(200, 200, 200, 255)
sk.theme.buttonDownColor = rgbx(180, 180, 180, 255)

window.runeInputEnabled = true
window.onRune = proc(rune: Rune) =
sk.inputRunes.add(rune)

var
showWindow = true
todos: seq[Todo]
Expand Down
2 changes: 1 addition & 1 deletion nimby.lock
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ nimsimd 1.3.2 https://github.com/guzba/nimsimd 3f6b2668ffb0867d0bf786a658b817763
bumpy 1.1.3 https://github.com/treeform/bumpy dc9a3d6d15680ec7f603959d65ba8ee4f1ddbc72
crunchy 0.1.11 https://github.com/guzba/crunchy 98eb6526982bb8aae8eec6e8781f4539fa19e049
bitty 0.1.4 https://github.com/treeform/bitty/ c40e81277c04befa37234cba5052f58648bef895
windy 0.5.0 https://github.com/treeform/windy 5971abb08f416cfb8a3603a3093503eb07f2f5c4
windy 0.5.0 https://github.com/treeform/windy d3d92f5decf22f98c61d198d6285427c83f67b60
opengl 1.2.9 https://github.com/nim-lang/opengl 8e2e098f82dc5eefd874488c37b5830233cd18f4
urlly 1.1.1 https://github.com/treeform/urlly 99784779f05649df25fd9c33003d8ef6de027345
ws 0.5.0 https://github.com/treeform/ws cbb8f763b436669392d10baec2a45778395395cc
Expand Down
2 changes: 1 addition & 1 deletion silky.nimble
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
version = "0.1.0"
version = "0.2.0"
author = "Andre von Houck"
description = "Silky immediate mode UI graphics."
license = "MIT"
Expand Down
8 changes: 6 additions & 2 deletions src/silky.nim
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import std/[tables]
from silky/common import SilkyError
export SilkyError

when defined(silkyTesting):
import silky/[semantics, atlas, widgets, textboxes, testing, dsl, menus, profiles]
export semantics, atlas, tables, textboxes, testing, dsl, menus, profiles
export semantics except textInputs
export atlas, tables, textboxes, testing, dsl, menus, profiles
export widgets except
button, checkBox, clickableIcon, dropDown, frame, group, h1text, icon,
iconButton, image, listBox, progressBar, radioButton, ribbon, scrubber, text
Expand All @@ -19,7 +22,8 @@ else:
not defined(useMetal4) and
not defined(useCpu):
export opengl
export windy, contexts, atlas, tables, textboxes, dsl, menus
export contexts except textInputs
export windy, atlas, tables, textboxes, dsl, menus
export widgets except
button, checkBox, clickableIcon, dropDown, frame, group, h1text, icon,
iconButton, image, listBox, progressBar, radioButton, ribbon, scrubber, text
2 changes: 2 additions & 0 deletions src/silky/common.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
type SilkyError* = object of CatchableError
## Reports invalid Silky state or input failures.
26 changes: 17 additions & 9 deletions src/silky/contexts.nim
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import
std/[algorithm, tables, unicode, times],
pixie, vmath, windy, bumpy,
silky/[atlas, clips, layout, profiles]
silky/[atlas, clips, layout, profiles],
silky/internal/inputs

export layout, profiles

Expand Down Expand Up @@ -86,7 +87,7 @@ type
padding*: float32 = 12
theme*: Theme = Theme()
cursor*: Cursor = Cursor(kind: ArrowCursor)
inputRunes*: seq[Rune]
inputs: TextInputs
mousePos*: Vec2
mouseDelta*: Vec2
mouseIdleTime*: float64
Expand Down Expand Up @@ -119,6 +120,10 @@ type
proc currentDrawLayer*(sk: Silky): int =
sk.drawer.currentLayer

proc textInputs*(sk: Silky): TextInputs =
## Provides internal text input state to the text widgets.
sk.inputs

proc pushLayer*(sk: Silky, layer: int) =
## Pushes a new rendering layer onto the stack.
sk.drawer.layerStack.add(sk.drawer.currentLayer)
Expand Down Expand Up @@ -299,6 +304,7 @@ proc resetInteractions*(sk: Silky) =

proc beginUiShared*(sk: Silky, window: Window, size: IVec2) =
## Starts a frame and updates the shared UI state.
sk.inputs.beginInputFrame(window)
beginProfileFrame()

sk.tooltipActive = sk.showTooltip
Expand Down Expand Up @@ -349,7 +355,7 @@ proc endUiShared*(sk: Silky) =
sk.popClipRect()
sk.frameTime = epochTime() - sk.frameStartTime
sk.avgFrameTime = (sk.avgFrameTime * 0.99) + (sk.frameTime * 0.01)
sk.inputRunes.setLen(0)
sk.inputs.endInputFrame(sk.window)
sk.inFrame = false
measurePop()
endProfileFrame()
Expand Down Expand Up @@ -696,12 +702,14 @@ proc newSilky*(
atlas: SilkyAtlas
): Silky {.measure.} =
## Creates a new Silky context and eagerly initializes its drawer.
## Automatically installs input handling on the window.
result = Silky()
result.image = image
result.atlas = atlas
result.builder = newAtlasBuilderFromAtlas(atlas, image)
result.window = window
result.drawer = newDrawer(window, image)
result.inputs = newTextInputs(window)

proc newSilky*(window: Window, atlasPngPath: string): Silky {.measure.} =
## Creates a new Silky from one atlas PNG file.
Expand Down Expand Up @@ -983,16 +991,16 @@ proc endUi*(sk: Silky) =
sk.endUiShared()

proc buttonDown*(sk: Silky): ButtonView =
## Returns a view that returns true if the selected button is down
sk.window.buttonDown
## Returns held buttons, excluding keyboard keys during text entry.
sk.inputs.filterButtons(sk.window.buttonDown)

proc buttonPressed*(sk: Silky): ButtonView =
## Returns a view that returns true the frame the selected button is pressed
sk.window.buttonPressed
## Returns pressed buttons, excluding keyboard keys during text entry.
sk.inputs.filterButtons(sk.window.buttonPressed)

proc buttonReleased*(sk: Silky): ButtonView =
## Returns a view that returns true the frame the selected button is released
sk.window.buttonReleased
## Returns released buttons, excluding keyboard keys during text entry.
sk.inputs.filterButtons(sk.window.buttonReleased)

when not defined(useDirectX) and
not defined(useVulkan) and
Expand Down
Loading
Loading