The original thread for it contained a demo program and some rather detailed notes on all the parameters - which of course I have no record of and is probably lost forever.
falcon was never
really intended for public consumption (originally it was part of a long-abandoned menuing system, then it got picked up for InForm) so I think there's a few versions of it floating around; for reference I have attached my copy of it, and it's what I'll base my notes below on.
DECLARE LIBRARY "falcon"
SUB uprint_extra (BYVAL x&, BYVAL y&, BYVAL chars%&, BYVAL length%&, BYVAL kern&, BYVAL do_render&, txt_width&, BYVAL charpos%&, charcount&, BYVAL colour~&, BYVAL max_width&)
FUNCTION uprint (BYVAL x&, BYVAL y&, chars$, BYVAL txt_len&, BYVAL colour~&, BYVAL max_width&)
FUNCTION uprintwidth (chars$, BYVAL txt_len&, BYVAL max_width&)
FUNCTION uheight& ()
FUNCTION falcon_uspacing& ALIAS uspacing ()
FUNCTION uascension& ()
END DECLARE
Strings are expected to be UTF-8 encoded.
uprint_extra is the main function:
- x&, y& : coordinates to render at
- chars%& : offset to text (i.e. _OFFSET(text$))
- length%& : string length in bytes
- kern& : boolean value to enable/disable kerninig
- do_render& : boolean value to enable/disable actually printing text. Disabling this is useful for doing size calculations with variable-width fonts
- txt_width& (output) : returns the horizontal size of the rendered text, in pixels
- charpos%& (output) : Declare an array REDIM charpos(LEN(text$)) AS LONG and pass _OFFSET(charpos()). Then charpos(1) is the location of the end of the first character, charpos(2) the end of the second character, and so on. Locations are horizontal pixel distances from the left boundary.
- charcount& (output) : The number of characters actually printed
- colour~& : Font colour. Use _RGB32().
- max_width& : The maximum width in pixels the text should have. If there is not enough room, the text is truncated to however many characters will fit.
The uprint function differs only in that it takes a string argument for the text, and has the defaults:
- kern = true
- do_render = true
It returns the txt_width parameter described above. Other output values are not available.
The uprintwidth function takes only the text data and the maximum print width, and returns the actual width the text would have if printed.
The other functions are font metrics, and my memory's a little shaky. You might need to experiment a little:
uheight& is the vertical distance between baselines, in pixels.
uascension& is the distance from the baseline to the top. Lets you find the position of the baseline.
uspacing& ¯\_(ツ)_/¯
Finally, here's a cute textbox I found that uses the charpos() array to draw a cursor:
DECLARE LIBRARY "falcon"
SUB uprint_extra (BYVAL x&, BYVAL y&, BYVAL chars%&, BYVAL length%&, BYVAL kern&, BYVAL do_render&, txt_width&, BYVAL charpos%&, charcount&, BYVAL colour~&, BYVAL max_width&)
FUNCTION uprint (BYVAL x&, BYVAL y&, chars$, BYVAL txt_len&, BYVAL colour~&, BYVAL max_width&)
FUNCTION uprintwidth (chars$, BYVAL txt_len&, BYVAL max_width&)
FUNCTION uheight& ()
FUNCTION falcon_uspacing& ALIAS uspacing ()
FUNCTION uascension& ()
END DECLARE
TYPE textbox_t
shadow_screen AS LONG
hardware_screen AS LONG
box_x AS LONG
box_y AS LONG
box_width AS LONG
box_height AS LONG
cursor_offset AS LONG
cursorx AS LONG
cursory AS LONG
cursor_image AS LONG
END TYPE
DEFLNG A-Z
CONST FONTFILE = "cyberbit.ttf"
CONST BOX_X = 60
CONST BOX_Y = 90
CONST BOX_WIDTH = 520
CONST BOX_HEIGHT = 300
SCREEN _NEWIMAGE(640, 480, 32)
DIM textbox AS textbox_t
create_textbox _LOADFONT(FONTFILE, 16), BOX_X, BOX_Y, BOX_WIDTH, BOX_HEIGHT, textbox
cursor_image = create_cursor
text$ = "Quis sunt vel ut dolor sed natus. Autem odit eius sapiente nostrum velit blanditiis eius. Aut earum sed est."
textcopy$ = text$
textbox.cursor_offset = 10
_DISPLAYORDER _HARDWARE
typeset_textbox textbox, textcopy$
DO
_LIMIT 50
k$ = INKEY$
IF k$ <> "" THEN
SELECT CASE k$
CASE CHR$(8)
IF textbox.cursor_offset > 0 THEN
IF textbox.cursor_offset = LEN(text$) THEN
text$ = LEFT$(text$, LEN(text$) - 1)
ELSE
text$ = LEFT$(text$, textbox.cursor_offset - 1) + MID$(text$, textbox.cursor_offset + 1)
END IF
textbox.cursor_offset = textbox.cursor_offset - 1
END IF
CASE CHR$(0) + "M"
IF textbox.cursor_offset < LEN(text$) THEN textbox.cursor_offset = textbox.cursor_offset + 1
CASE CHR$(0) + "K"
IF textbox.cursor_offset > 0 THEN textbox.cursor_offset = textbox.cursor_offset - 1
CASE CHR$(0) + "G"
textbox.cursor_offset = 0
CASE CHR$(0) + "O"
textbox.cursor_offset = LEN(text$)
CASE ELSE
IF textbox.cursor_offset = LEN(text$) THEN
text$ = text$ + k$
ELSE
text$ = LEFT$(text$, textbox.cursor_offset) + k$ + MID$(text$, textbox.cursor_offset + 1)
END IF
textbox.cursor_offset = textbox.cursor_offset + 1
END SELECT
textcopy$ = text$
typeset_textbox textbox, textcopy$
END IF
render_textbox textbox
_DISPLAY
LOOP
SUB render_textbox (textbox AS textbox_t)
_PUTIMAGE (textbox.box_x, textbox.box_y), textbox.hardware_screen
_PUTIMAGE (textbox.box_x + textbox.cursorx, textbox.box_y + textbox.cursory), textbox.cursor_image
END SUB
SUB bench (t0!, t1!, t2!)
STATIC n, a1!, a2!
a1! = (a1! * n + (t1! - t0!)) / (n + 1)
a2! = (a2! * n + (t2! - t1!)) / (n + 1)
n = n + 1
_TITLE STR$(a1!) + " " + STR$(a2!)
END SUB
FUNCTION create_cursor
image = _NEWIMAGE(1, uheight&, 32)
olddest = _DEST
_DEST image
LINE (0, 0)-(0, uheight&), _RGB32(255, 0, 0)
_DEST olddest
create_cursor = _COPYIMAGE(image, 33)
_FREEIMAGE image
END FUNCTION
SUB typeset_textbox (textbox AS textbox_t, s$)
LINE (textbox.box_x, textbox.box_y)-STEP(textbox.box_width, textbox.box_height), _RGB32(255, 255, 255), BF
olddest = _DEST
_DEST textbox.shadow_screen
LINE (0, 0)-(BOX_WIDTH, BOX_HEIGHT), _RGB32(255, 255, 255), BF
REDIM charpos(LEN(s$)) AS LONG
cursor_offset = textbox.cursor_offset
DO
uprint_extra 0, vertical_offset, _OFFSET(s$), LEN(s$), -1, -1, txt_width&, _OFFSET(charpos()), charcount&, _RGB32(0, 0, 0), BOX_WIDTH
'Line ended partway through a word?
IF LEN(s$) > charcount& THEN
IF MID$(s$, charcount&, 1) <> " " AND MID$(s$, charcount& + 1, 1) <> " " THEN
'Find end of previous word
charcount& = reverse_find_word_end(s$, charcount&)
END IF
END IF
newwidth& = charpos&(charcount&)
LINE (newwidth& + 1, vertical_offset)-(BOX_WIDTH, vertical_offset + uheight& - 1), _RGB32(255, 255, 255), BF
IF NOT cursor_found AND cursor_offset <= charcount& THEN
textbox.cursorx = charpos(cursor_offset)
textbox.cursory = vertical_offset
cursor_found = -1
END IF
vertical_offset = vertical_offset + uheight&
IF LEN(s$) = charcount& THEN
EXIT DO
ELSE
'remove leading spaces
'WARNING: This part will mean the cursor offsets are no longer contiguous; it will break on line ends and get positions wrong
'Need to decide on a behaviour to make them both work
'FOR i = charcount& + 1 TO LEN(s$)
' IF MID$(s$, i, 1) <> " " THEN EXIT FOR
'NEXT i
'IF i > LEN(s$) THEN EXIT DO 'turns out the entire remaining text was spaces
's$ = MID$(s$, i)
s$ = MID$(s$, charcount& + 1)
'Keep cursor offset relative to our ever-shrinking string
cursor_offset = cursor_offset - charcount&
END IF
LOOP
IF textbox.hardware_screen <> 0 THEN _FREEIMAGE textbox.hardware_screen
textbox.hardware_screen = _COPYIMAGE(textbox.shadow_screen, 33)
_DEST olddest
END SUB
SUB create_textbox (fonthandle, box_x, box_y, box_width, box_height, textbox AS textbox_t)
textbox.shadow_screen = _NEWIMAGE(box_width, box_height, 32)
textbox.box_x = box_x
textbox.box_y = box_y
textbox.box_width = box_width
textbox.box_height = box_height
textbox.cursor_image = create_cursor
olddest = _DEST
_DEST textbox.shadow_screen
_FONT fonthandle
_DEST olddest
END SUB
'Finds the end of the last complete word in the substring of s$ from start to length&
FUNCTION reverse_find_word_end (s$, length&)
FOR i = length& TO 2 STEP -1
'Looking for a space proceeded by a non-space
IF MID$(s$, i, 1) = " " AND MID$(s$, i - 1, 1) <> " " THEN
reverse_find_word_end = i - 1
EXIT FUNCTION
END IF
NEXT i
'What to do if there's no location meeting this condition?
END FUNCTION