Three kinds of Flan face over the generated set, which stays honest to C because that is what makes it checkable against the header. A slice where C takes a pointer and a count: the eleven vector-array drawing calls, all eleven rather than the three anybody calls, since a subset has its hole where the next caller looks. Each guards the empty slice, which is the part a hand-written call site gets wrong rather than merely writes out -- raylib takes a count of 0 happily, but taking the address of element 0 of an empty slice is out of bounds before raylib is reached. An Option where C signals with a sentinel: get-key-pressed and get-char-pressed, raylib's two input queues, both of which say "empty" with 0. What that buys is in text-input-box.flan, which read the queue in two places -- once to prime the loop, once at the bottom of the body -- and now reads it in one. Both of those use the `name` directive in bindings, so the generated declaration keeps the symbol and gives up the name: nothing about the C signature is hand-written and the generated half keeps its agreement-by-construction with the header. An enum where the header says int: key-up?, key-pressed-repeat?, mouse-button-up?. These are NOT wrappers -- a C enum parameter has an int's ABI, so the hand-written declare-c with the Flan type is the whole fix. They were holes in families whose other halves already took a Key, so (rl/key-down? :space) compiled and (rl/key-up? :space) did not. Not built: with-drawing and with-mode-2d. A macro cannot live in a package -- the expander collects defmacros from the prelude and from the file being compiled, and one in an imported package is refused by name. test/programs/pkg-macro.flan is that refusal. And vendor/raylib/vector.flan, which is raymath written in Flan because raymath is static inline and has no symbol to bind. A file of its own, split on declare-c and not on "idiomatic": there is not one declaration in it, so it is not part of the surface the header check reads, and raylib.flan is 1300 lines already. clamp and lerp are deliberately absent -- the prelude has both, and a second lerp would not even be the same function, since the prelude writes (1-t)a + tb where raymath writes a + t*(b - a). Examples: the identical eight-expression box-around helper in core-3d-picking and models-box-collisions is a half-extent subtracted and added. shapes-following-eyes keeps its measurement and gives up one line to v2-sub, which is the honest size of the gap in a file that is nothing but vector maths.
152 lines
6.6 KiB
Plaintext
152 lines
6.6 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 — key-down?, 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.flan, "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 defenum. raylib's
|
|
;;;; header says `int cursor` and means one of eleven MOUSE_CURSOR_ values, so
|
|
;;;; the Flan face is `MouseCursor` — hand-written in raylib.flan 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.flan")
|
|
|
|
(defconst screen-width 800)
|
|
(defconst screen-height 450)
|
|
|
|
(defconst max-input-chars 9)
|
|
|
|
;; No +1: see the header comment. There is no NUL to leave room for.
|
|
(defvar name [9 u8])
|
|
(defvar letter-count i32)
|
|
|
|
(defvar text-box rl/Rectangle)
|
|
(defvar mouse-on-text bool)
|
|
(defvar frames-counter i32)
|
|
|
|
(defn main [] ()
|
|
(rl/init-window screen-width screen-height
|
|
"raylib [text] example - input box")
|
|
(defer (rl/close-window))
|
|
|
|
(set name (array max-input-chars u8))
|
|
(set letter-count 0)
|
|
|
|
(set text-box (rl/Rectangle {.x (- (/ (f32 screen-width) 2.0) 100.0)
|
|
.y 180.0 .width 225.0 .height 50.0}))
|
|
(set mouse-on-text false)
|
|
(set frames-counter 0)
|
|
|
|
(rl/set-target-fps 60)
|
|
|
|
(until (rl/window-should-close?)
|
|
;; Update
|
|
(set mouse-on-text
|
|
(rl/collision-point-rec? (rl/get-mouse-position) text-box))
|
|
|
|
(if mouse-on-text
|
|
(do
|
|
(rl/set-mouse-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 (set 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)
|
|
(when (and (>= key 32) (<= key 125)
|
|
(< letter-count max-input-chars))
|
|
(set (at name letter-count) (u8 key))
|
|
(set letter-count (+ letter-count 1))))))
|
|
|
|
(when (rl/key-pressed? :backspace)
|
|
(set letter-count (- letter-count 1))
|
|
(when (< letter-count 0) (set letter-count 0))))
|
|
(rl/set-mouse-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
|
|
(set frames-counter (+ frames-counter 1))
|
|
(set frames-counter 0))
|
|
|
|
;; Draw
|
|
(rl/begin-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 (.x text-box)) (i32 (.y text-box))
|
|
(i32 (.width text-box)) (i32 (.height text-box))
|
|
(if mouse-on-text rl/red rl/darkgray))
|
|
|
|
(let [typed (string (slice name 0 letter-count))]
|
|
(rl/draw-text typed (+ (i32 (.x text-box)) 5) (+ (i32 (.y text-box)) 8)
|
|
40 rl/maroon)
|
|
|
|
;; The C's TextFormat("INPUT CHARS: %i/%i", ...), reassembled out of
|
|
;; examples/digits.flan 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]
|
|
(set x (+ x (d/draw-piece "INPUT CHARS: " x 250 20 rl/darkgray)))
|
|
(set x (+ x (d/draw-int letter-count x 250 20 rl/darkgray)))
|
|
(set x (+ x (d/draw-piece "/" x 250 20 rl/darkgray)))
|
|
(d/draw-int max-input-chars x 250 20 rl/darkgray))
|
|
|
|
(when mouse-on-text
|
|
(if (< letter-count max-input-chars)
|
|
;; Twenty frames on, twenty frames off, at the pen position after
|
|
;; whatever has been typed.
|
|
(when (= (% (/ frames-counter 20) 2) 0)
|
|
(rl/draw-text "_"
|
|
(+ (i32 (.x text-box)) 8 (rl/measure-text typed 40))
|
|
(+ (i32 (.y text-box)) 12) 40 rl/maroon))
|
|
(rl/draw-text "Press BACKSPACE to delete chars..." 230 300 20
|
|
rl/gray))))
|
|
|
|
(rl/end-drawing)))
|