The trio the author decided on 2026-09-20 is now all built: def is CL's defparameter — its initialiser runs on every daemon re-run, unguarded, so an edited initialiser repaints the same storage on C-c C-c plus re-run — defonce (Clojure's name for CL's defvar, per the author) initialises once behind the .init~once. flag, and defconst stays the image. One parse arm reads both forms; the difference is Ast.reinit, carried to Tast.global's grerun. Emit.startup_plan gives a def no guard flag, and Check.check_global lifts every def initialiser — zero and literal included — into global/<n>, so the host's startup reaches it through the function cell and a re-evaluated def swaps it (Session's def_inits; Emit.redefinition declares the cell for a non-sibling target). The old defvar spelling is refused with the rename and both compiling spellings, and every program, test, doc and editor list is swept — except sand.flan, the author's live WIP, whose seven defvar lines are flagged in FIX.org and keep its three dependent tests red on this branch.
150 lines
6.6 KiB
Plaintext
150 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.
|
|
(defonce name [9 u8])
|
|
(defonce letter-count i32)
|
|
|
|
(defonce text-box rl/Rectangle)
|
|
(defonce mouse-on-text bool)
|
|
(defonce 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? :key-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/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 (.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)))))))
|