134 lines
6.2 KiB
Plaintext
134 lines
6.2 KiB
Plaintext
;;;; raylib [text] example - input box
|
|
;;;;
|
|
;;;; examples/text/text_input_box.c. Two things in this file exist nowhere
|
|
;;;; else in the corpus.
|
|
;;;;
|
|
;;;; The first is get-char-pressed. Everything ported so far reads input as
|
|
;;;; state — is-key-down, is-key-pressed, the mouse position — and this is the
|
|
;;;; only example that drains a *queue*: raylib buffers the characters the
|
|
;;;; platform produced since the last frame, already through the keyboard
|
|
;;;; layout and the dead keys, and hands them back one at a time until it is
|
|
;;;; empty. The loop that empties it is the point of the example, and a
|
|
;;;; program that read the queue once per frame instead would silently drop
|
|
;;;; the second character of a fast pair.
|
|
;;;;
|
|
;;;; raylib says "empty" by answering 0, and rl/get-char-pressed is now a
|
|
;;;; Flan wrapper that says it with None instead — see raylib.fln, "Draining
|
|
;;;; raylib's two input queues". What that buys is visible below: the C
|
|
;;;; shape, which this file had, reads the queue in *two* places, once to
|
|
;;;; prime the loop and once at the bottom of the body, and a `> 0` in
|
|
;;;; between that a reader has to know raylib's convention to trust. The
|
|
;;;; Option shape reads it in one place, and the case where there is no
|
|
;;;; character is a branch the checker knows about rather than a comparison.
|
|
;;;;
|
|
;;;; The second is set-mouse-cursor, which needed a new enum. raylib's
|
|
;;;; header says `int cursor` and means one of eleven MOUSE_CURSOR_ values, so
|
|
;;;; the Flan face is `MouseCursor` — hand-written in raylib.fln beside the
|
|
;;;; other cursor calls, excluded from the generated half, and mapped in
|
|
;;;; `bindings` so the eleven members are checked against the header. See the
|
|
;;;; comment there: this call is made on every frame off a hover test, and the
|
|
;;;; failure an i32 would allow is not a crash but a pointer in the wrong
|
|
;;;; shape, which nothing reports.
|
|
;;;;
|
|
;;;; The text buffer, and what it says about strings. The C keeps a
|
|
;;;; `char name[MAX_INPUT_CHARS + 1]` and maintains the NUL itself; here it is
|
|
;;;; a `[9 u8]` with no terminator, and every use is `(string (slice name 0
|
|
;;;; letter-count))`. A Flan string is a pointer and a length, and the shim
|
|
;;;; that calls DrawText copies it and NUL-terminates the copy
|
|
;;;; (lib/shim.ml) — so the extra byte the C needs has no counterpart here and
|
|
;;;; the buffer is exactly as long as the characters it can hold. The two
|
|
;;;; draws and the measure all take the same slice, so they cannot disagree
|
|
;;;; about where the text ends.
|
|
;;;;
|
|
;;;; Dropped from the C: its IsAnyKeyPressed helper, which is defined at the
|
|
;;;; bottom of the file and called from nowhere.
|
|
|
|
import rl "vendor:raylib"
|
|
|
|
import d "digits.fln"
|
|
|
|
const screen-width = 800
|
|
const screen-height = 450
|
|
|
|
const max-input-chars = 9
|
|
|
|
;; No +1: see the header comment. There is no NUL to leave room for.
|
|
once name: [9 u8]
|
|
once letter-count: i32
|
|
|
|
once text-box: rl/Rectangle
|
|
once mouse-on-text: bool
|
|
once frames-counter: i32
|
|
|
|
fn main() -> ()
|
|
rl/init-window(screen-width, screen-height,
|
|
"raylib [text] example - input box")
|
|
defer rl/close-window()
|
|
name = array(max-input-chars, u8)
|
|
letter-count = 0
|
|
text-box = rl/Rectangle{.x f32(screen-width) / 2.0 - 100.0, .y 180.0, .width 225.0, .height 50.0}
|
|
mouse-on-text = false
|
|
frames-counter = 0
|
|
rl/set-target-fps(60)
|
|
until rl/window-should-close()
|
|
;; Update
|
|
mouse-on-text = rl/check-collision-point-rec(rl/get-mouse-position(),
|
|
text-box)
|
|
if mouse-on-text
|
|
rl/set-mouse-cursor(:cursor-ibeam)
|
|
;; Drain the character queue: more than one character can arrive in
|
|
;; a single frame — holding a key with the platform's repeat on is
|
|
;; the ordinary way that happens — and None is the end of it.
|
|
let draining = true
|
|
while draining
|
|
match rl/get-char-pressed()
|
|
None -> draining = false
|
|
;; 32..125 is the printable ASCII range the C accepts. Anything
|
|
;; outside it — an accented letter, a control character — is
|
|
;; dropped rather than stored, because the buffer is bytes and a
|
|
;; codepoint above 127 would need more than one of them.
|
|
Some(key) ->
|
|
if key >= 32 and key <= 125 and letter-count < max-input-chars
|
|
name[letter-count] = u8(key)
|
|
letter-count += 1
|
|
if rl/is-key-pressed(:key-backspace)
|
|
letter-count -= 1
|
|
if letter-count < 0
|
|
letter-count = 0
|
|
else
|
|
rl/set-mouse-cursor(:cursor-default)
|
|
;; The blink phase only runs while the box has focus, so the underscore is
|
|
;; always visible on the frame the pointer arrives.
|
|
if mouse-on-text then frames-counter += 1 else frames-counter = 0
|
|
;; Draw
|
|
rl/with-drawing:
|
|
rl/clear-background(rl/raywhite)
|
|
rl/draw-text("PLACE MOUSE OVER INPUT BOX!", 240, 140, 20, rl/gray)
|
|
rl/draw-rectangle-rec(text-box, rl/lightgray)
|
|
rl/draw-rectangle-lines(i32(text-box.x), i32(text-box.y),
|
|
i32(text-box.width), i32(text-box.height),
|
|
if mouse-on-text then rl/red else rl/darkgray)
|
|
let typed = str(slice(name, 0, letter-count))
|
|
rl/draw-text(typed, i32(text-box.x) + 5, i32(text-box.y) + 8, 40,
|
|
rl/maroon)
|
|
;; The C's TextFormat("INPUT CHARS: %i/%i", ...), reassembled out of
|
|
;; examples/digits.fln the way the other ports do it. `typed` is a view
|
|
;; of `name` and not of the runtime's shared format buffer, so it
|
|
;; survives the two numbers being formatted — which a value from
|
|
;; i64->bytes would not.
|
|
let x = 315
|
|
x += d/draw-piece("INPUT CHARS: ", x, 250, 20, rl/darkgray)
|
|
x += d/draw-int(letter-count, x, 250, 20, rl/darkgray)
|
|
x += d/draw-piece("/", x, 250, 20, rl/darkgray)
|
|
d/draw-int(max-input-chars, x, 250, 20, rl/darkgray)
|
|
if mouse-on-text
|
|
if letter-count < max-input-chars
|
|
;; Twenty frames on, twenty frames off, at the pen position after
|
|
;; whatever has been typed.
|
|
if frames-counter / 20 % 2 == 0
|
|
rl/draw-text("_", i32(text-box.x) + 8 + rl/measure-text(typed, 40),
|
|
i32(text-box.y) + 12, 40, rl/maroon)
|
|
else
|
|
rl/draw-text("Press BACKSPACE to delete chars...", 230, 300, 20,
|
|
rl/gray)
|