Template repo for tiny cross-platform apps that can be modified on phone, tablet or computer.
at main 234 lines 11 kB view raw view rendered
1# Building blocks for your Freewheeling Apps 2 3Freewheeling apps consist of 3 kinds of things: 4 - a small number of functions you can define that get automatically called 5 for you as appropriate, 6 - a wide variety of primitives that you can call but not modify, 7 - any other function you can define and call at will. 8 9The rest of this document will summarize what is available to you in the first 10two categories. 11 12## Variables you can set/read 13 14* `Disallow_error_recovery_on_key_release` -- set this when you detect 15 hard-to-recover-from errors. Right now I'm only aware of one such (see 16 driver.love): calls to `love.graphics.newCanvas` can fail at the SDL2 layer 17 if a computer doesn't have enough video RAM to perform them. In response, 18 set this variable before and unset after to prevent the system from 19 repeatedly retrying something that is unlikely to recover. In particular, 20 prevent settings from being saved to disk during an error state, thereby 21 perpetuating the error. 22 23 This variable is subject to removal if I can come up with a better approach, 24 but I'll document it here as long as I support it. Please 25 [let me know](http://akkartik.name/contact) if you find a new need to read 26 or write it. 27 28## Functions you can implement that will get automatically called 29 30* `on.initialize(arg)` -- called when app starts up. Provides in `arg` an 31 array of words typed in if you ran it from a terminal window. 32 (Based on [LÖVE](https://love2d.org/wiki/love.load).) 33 34* `on.draw()` -- called to draw on the window, around 30 times a second. 35 (Based on [LÖVE](https://love2d.org/wiki/love.draw).) 36 37* `on.update(dt)` -- called after every call to `on.draw`. Make changes to 38 your app's variables here rather than in `on.draw`. Provides in `dt` the 39 time since the previous call to `on.update`, which can be useful for things 40 like smooth animations. 41 (Based on [LÖVE](https://love2d.org/wiki/love.update).) 42 43* `on.mouse_press(x,y, mouse_button, is_touch, presses)` -- called when you 44 press down on a mouse button. Provides in `x` and `y` the point on the 45 screen at which the click occurred, and in `mouse_button` an integer id of 46 the mouse button pressed. 47 `1` is the primary mouse button (the left button on a right-handed mouse), 48 `2` is the secondary button (the right button on a right-handed mouse), 49 and `3` is the middle button. Further buttons are mouse-dependent. 50 (Based on [LÖVE](https://love2d.org/wiki/love.mousepressed).) 51 52* `on.mouse_release(x,y, mouse_button, is_touch, presses)` -- called when you 53 release a mouse button. Provides the same arguments as `on.mouse_press()` 54 above. 55 (Based on [LÖVE](https://love2d.org/wiki/love.mousereleased).) 56 57* `on.mouse_move(x,y, dx,dy, is_touch)` -- called when you move the mouse. 58 Provides in `x` and `y` the point on the screen at which the click occurred 59 and in `dx` and `dy` the amount moved since the previous call of 60 `mouse_move`. 61 (Based on [LÖVE](https://love2d.org/wiki/love.mousemoved).) 62 63* `on.mouse_wheel_move(dx,dy)` -- called when you use the scroll wheel on a 64 mouse that has it. Provides in `dx` and `dy` an indication of how fast the 65 wheel is being scrolled. Positive values for `dx` indicate movement to the 66 right. Positive values for `dy` indicate upward movement. 67 (Based on [LÖVE](https://love2d.org/wiki/love.wheelmoved).) 68 69* `on.touch_press(touch_id, x,y, dx,dy, pressure)` -- called when you touch a 70 multi-touch screen. Provides in `x` and `y` the point on the screen at which 71 the touch occurred. 72 (Based on [LÖVE](https://love2d.org/wiki/love.touchpressed).) 73 74* `on.touch_release(touch_id, x,y, dx,dy, pressure)` -- called when you 75 release a touch. Provides the same arguments as `on.touch_press()` above. 76 (Based on [LÖVE](https://love2d.org/wiki/love.touchreleased).) 77 78* `on.touch_move(touch_id, x,y, dx,dy, pressure)` -- called when you move any 79 finger on a multi-touch screen. Provides the same arguments as 80 `on.touch_press()` above, with `dx` and `dy` containing the amount moved 81 since the previous call to `touch_move`. 82 (Based on [LÖVE](https://love2d.org/wiki/love.touchmoved).) 83 84* `on.keychord_press(chord, key, scancode, is_repeat)` -- called when you 85 press a key-combination. Provides in `key` a string name for the key most 86 recently pressed ([valid values](https://love2d.org/wiki/KeyConstant)). 87 Provides in `chord` a string representation of the current key combination, 88 consisting of the key with the following prefixes: 89 * `C-` if one of the `ctrl` keys is pressed, 90 * `M-` if one of the `alt` keys is pressed, 91 * `S-` if one of the `shift` keys is pressed, and 92 * `s-` if the `windows`/`cmd`/`super` key is pressed. 93 94* `on.text_input(t)` -- called when you press a key combination that yields 95 (roughly) a printable character. For example, `shift` and `a` pressed 96 together will call `on.textinput` with `A`. 97 (Based on [LÖVE](https://love2d.org/wiki/love.textinput).) 98 99* `on.key_release(key, scancode)` -- called when you press a key on the 100 keyboard. Provides in `key` a string name for the key ([valid 101 values](https://love2d.org/wiki/KeyConstant)). 102 (Based on [LÖVE](https://love2d.org/wiki/love.keyreleased), including other 103 variants.) 104 105* `on.quit()` -- called before the app shuts down. 106 (Based on [LÖVE](https://love2d.org/wiki/love.quit).) 107 108* `on.save_settings()` -- called after on.quit and should return a table which 109 will be saved to disk. 110 111* `on.load_settings(settings)` -- called when app starts up, before 112 `on.initialize`. Provides in `settings` the table that was saved to disk the 113 last time the app shut down. 114 115* `on.focus(start?)` -- called when the app starts or stops receiving 116 keypresses. `start?` will be `true` when app starts receiving keypresses and 117 `false` when keypresses move to another window. 118 (Based on [LÖVE](https://love2d.org/wiki/love.focus).) 119 120* `on.resize(w,h)` -- called when you resize the app window. Provides new 121 window dimensions in `w` and `h`. 122 (Based on [LÖVE](https://love2d.org/wiki/love.resize)) 123 124* `on.code_change()` -- called when you make changes to the app using 125 [driver.love](https://git.sr.ht/~akkartik/driver.love), any time you hit 126 `f4` inside driver.love, after a definition is created or modified. 127 128* `on.mouse_focus(in_focus)` -- called when the mouse pointer moves on or off 129 the app window. `in_focus` will be `true` when mouse comes within the window 130 area, and `false` when it goes off. 131 (Based on [LÖVE](https://love2d.org/wiki/love.mousefocus).) 132 133## Functions you can call 134 135Everything in the [LÖVE](https://love2d.org/wiki/Main_Page) and 136[Lua](https://www.lua.org/manual/5.1/manual.html) guides is available to you. 137 138### text editor primitives 139 140* `editor = edit.new(top, left, right, bottom, font, font_height)` 141 -- returns an object that can be used to render an interactive editor widget 142 for text within the given bounds. Wraps long lines at word boundaries where 143 possible, or in the middle of words (no hyphenation yet) when it must. 144 145* `edit.resize(editor, w, h, right, bottom)` -- adjusts screen dimensions and 146 editor bounds. 147 148* `edit.load_file(editor, filename)` -- loads the `editor` state with lines 149 from `filename`. 150 151Make sure to wire up the following functions from corresponding `on.*` functions: 152* `edit.draw(editor, fg_color, hide_cursor, show_line_numbers, cursor_color)` 153 - `fg_color` is either nil or a 3-array of rgb or a 4-array of rgba. If it's 154 `nil`, the text will be syntax highlighted. 155 - `hide_cursor` is a boolean 156 - `show_line_numbers` is a boolean 157 - `cursor_color` is an optional color 158* `edit.keychord_press(editor, chord, key, scancode, is_repeat, readonly)` 159 - `key` and `scancode` are for the final key pressed in the `chord` 160 - `readonly` should probably be in sync with `hide_cursor` in `edit.draw` 161* `edit.text_input(editor, t)` 162* `edit.key_release(editor, key, scancode)` 163* `edit.mouse_press(editor, x,y, mouse_button, is_touch, presses)` 164* `edit.mouse_move(editor, x,y, dx,dy, is_touch)` 165* `edit.mouse_release(editor, x,y, mouse_button, is_touch, presses)` 166* `edit.mouse_wheel_move(editor, dx,dy)` 167* `edit.touch_press(editor, touch_id, x,y, dx,dy, pressure)` 168* `edit.touch_move(editor, touch_id, x,y, dx,dy, pressure)` 169* `edit.touch_release(editor, touch_id, x,y, dx,dy, pressure)` 170 171The `touch_*` functions above implement inertial scroll. You'll also need to 172wire up two additional functions for inertial scroll: 173* `edit.update_inertial_scroll(editor, dt)` -- from `on.update` 174* `edit.maybe_stop_inertial_scroll(editor)` -- from `on.mouse_press`, at a 175 higher priority than `edit.touch_press` or any other UI elements on screen. 176 Will return `true` if any inertial scroll was in progress and stopped. 177 178[Basic example showing how to wire together these calls into a live-modifiable 179text editor.](https://git.sr.ht/~akkartik/live-editor-mobile2-example) 180 181Other optional configuration: 182 183* `edit.update_font_settings(editor, font_height)` -- updates all state 184 dependent on font height. 185 186* `edit.save_to_disk(editor)` -- save current state to `editor.filename` 187 188* `edit.is_this_love_version_supported()` -- might provide early warning if 189 the version of LÖVE is too old. 190 191* `editor.indent_wrapped_lines = true` -- now after wrapping the line 192 continues from the same x coordinate as its starting character, rather than 193 the left margin. 194 195### The freewheeling protocol with the driver 196 197Freewheeling apps currently respond to the following commands from the driver: 198 * `QUIT` -- tells the current app to quit 199 * `RESTART` -- tells the current app to reinitialize after saving any settings 200 * `MANIFEST` -- requests a list of definitions the app knows about. The 201 app returns only definitions that were created using the freewheeling 202 framework and so can be modified and errors recovered from. 203 * `DEFAULT_MAP` -- requests a default map of the code. Everyone is free to 204 create their own "memory palace", but this is usually a good default to 205 start with. 206 * `GET <name>` -- requests the source code for definition `<name>`. 207 * `GET* <name> ...` -- requests source code for multiple definitions. 208 * `DELETE <name>` -- requests deletion of the definition `<name>`. Only 209 permitted for definitions created using the freewheeling framework, not 210 lower-level definitions. 211 * anything else -- is considered a new definition to be loaded into the 212 app. 213 214Commands may cause an error response, which is sent back to the driver. 215 216In addition, any _run-time_ errors caused as the app executes are also sent 217back to the driver. These don't need an explicit command from the driver. 218 219Some primitives available for complying with the protocol: 220 221* `live.receive_from_driver()` -- looks for a message from the driver, and 222 returns nil if there's nothing. 223 224* `live.send_to_driver(msg)` -- sends a message to the driver. 225 226* `live.send_run_time_error_to_driver(msg)` -- sends an error to the driver. 227 Automatically invoked by the LÖVE error handler, so you shouldn't need to 228 call this. 229 230* `live.get_cmd_from_buffer(buf)` -- helper to extract the first word from a 231 command. 232 233* `live.get_binding(name)` -- look up the repo for the source code for a 234 `name`.