1 # Some useful building blocks
3 Apps can be composed of a wide variety of building blocks that you
4 can use in your functions, including a small number of functions that get
5 automatically called for you as appropriate.
7 ## Variables you can read
10 * `width` and `height` -- integer dimensions for the app window in pixels.
11 * `flags` -- some properties of the app window. See [`flags` in `love.graphics.getMode`](https://love2d.org/wiki/love.window.getMode)
14 ## Functions that get automatically called
16 * `App.initialize_globals()` -- called before running each test and also
17 before the app starts up. As the name suggests, use this to initialize all
18 your global variables to something consistent. I also find it useful to be
19 able to see all my global variables in one place, and avoid defining
20 top-level variables anywhere else (unless they're constants and never going
23 * `App.initialize(arg)` -- called when app starts up after
24 `App.initialize_globals`. Provides in `arg` an array of words typed in if
25 you ran it from a terminal window.
26 (Based on [LÖVE](https://love2d.org/wiki/love.load).)
28 * `App.quit()` -- called before the app shuts down.
29 (Based on [LÖVE](https://love2d.org/wiki/love.quit).)
31 * `App.focus(start?)` -- called when the app starts or stops receiving
32 keypresses. `start?` will be `true` when app starts receiving keypresses and
33 `false` when keypresses move to another window.
34 (Based on [LÖVE](https://love2d.org/wiki/love.focus).)
36 * `App.resize(w,h)` -- called when you resize the app window. Provides new
37 window dimensions in `w` and `h`. Don't bother updating `App.screen.width`
38 and `App.screen.height`, that will happen automatically before calling
40 (Based on [LÖVE](https://love2d.org/wiki/love.resize))
42 * `App.filedropped(file)` -- called when a file icon is dragged and dropped on
43 the app window. Provides in `file` an object representing the file that was
44 dropped, that will respond to the following messages:
46 * `file:getFilename()` returning a string name
47 * `file:read()` returning the entire file contents in a single string
49 (Based on [LÖVE](https://love2d.org/wiki/love.filedropped).)
51 * `App.draw()` -- called to draw on the window, around 30 times a second.
52 (Based on [LÖVE](https://love2d.org/wiki/love.draw).)
54 * `App.update(dt)` -- called after every call to `on.draw`. Make changes to
55 your app's variables here rather than in `on.draw`. Provides in `dt` the
56 time since the previous call to `on.update`, which can be useful for things
57 like smooth animations.
58 (Based on [LÖVE](https://love2d.org/wiki/love.update).)
60 * `App.mousepressed(x,y, mouse_button)` -- called when you press down on a
61 mouse button. Provides in `x` and `y` the point on the screen at which the
62 click occurred, and in `mouse_button` an integer id of the mouse button
64 `1` is the primary mouse button (the left button on a right-handed mouse),
65 `2` is the secondary button (the right button on a right-handed mouse),
66 and `3` is the middle button. Further buttons are mouse-dependent.
67 (Based on [LÖVE](https://love2d.org/wiki/love.mousepressed).)
69 * `App.mousereleased(x,y, mouse_button)` -- called when you release a mouse
70 button. Provides the same arguments as `on.mouse_press()` above.
71 (Based on [LÖVE](https://love2d.org/wiki/love.mousereleased).)
73 * `App.wheelmoved(dx,dy)` -- called when you use the scroll wheel on a mouse
74 that has it. Provides in `dx` and `dy` an indication of how fast the wheel
75 is being scrolled. Positive values for `dx` indicate movement to the right.
76 Positive values for `dy` indicate upward movement.
77 (Based on [LÖVE](https://love2d.org/wiki/love.wheelmoved).)
79 * `App.keychord_press(chord, key)` -- called when you press a key-combination.
80 Provides in `key` a string name for the key most recently pressed ([valid
81 values](https://love2d.org/wiki/KeyConstant)). Provides in `chord` a
82 string representation of the current key combination, consisting of the key
83 with the following prefixes:
84 * `C-` if one of the `ctrl` keys is pressed,
85 * `M-` if one of the `alt` keys is pressed,
86 * `S-` if one of the `shift` keys is pressed, and
87 * `s-` if the `windows`/`cmd`/`super` key is pressed.
89 * `App.textinput(t)` -- called when you press a key combination that yields
90 (roughly) a printable character. For example, `shift` and `a` pressed
91 together will call `on.textinput` with `A`.
92 (Based on [LÖVE](https://love2d.org/wiki/love.textinput).)
94 * `App.keyrelease(key)` -- called when you press a key on the keyboard.
95 Provides in `key` a string name for the key ([valid values](https://love2d.org/wiki/KeyConstant)).
96 (Based on [LÖVE](https://love2d.org/wiki/love.keyreleased), including other
99 ## Functions you can call
101 Everything in the [LÖVE](https://love2d.org/wiki/Main_Page) and
102 [Lua](https://www.lua.org/manual/5.1/manual.html) guides is available to you,
103 but here's a brief summary of the most useful primitives. Some primitives have
104 new, preferred names under the `App` namespace, often because these variants
105 are more testable. If you run them within a test you'll be able to make
106 assertions on their side-effects.
108 ### regarding the app window
110 * `width, height, flags = App.screen.size()` -- returns the dimensions and
111 some properties of the app window.
112 (Based on [LÖVE](https://love2d.org/wiki/love.window.getMode).)
114 * `App.screen.resize(width, height, flags)` -- modify the size and properties
115 of the app window. The OS may or may not act on the request.
116 (Based on [LÖVE](https://love2d.org/wiki/love.window.setMode).)
118 * `x, y, displayindex = App.screen.position()` -- returns the coordinates and
119 monitor index (if you have more than one monitor) for the top-left corner of
121 (Based on [LÖVE](https://love2d.org/wiki/love.window.getPosition).)
123 * `App.screen.move(x, y, displayindex)` -- moves the app window so its
124 top-left corner is at the specified coordinates of the specified monitor.
125 The OS may or may not act on the request.
126 (Based on [LÖVE](https://love2d.org/wiki/love.window.setPosition).)
128 ### drawing to the app window
130 * `App.screen.print(text, x,y)` -- print the given `text` in the current font
131 using the current color so its top-left corner is at the specified
132 coordinates of the app window.
133 (Based on [LÖVE](https://love2d.org/wiki/love.graphics.print).)
135 * `love.graphics.getFont()` -- returns a representation of the current font.
136 (From [LÖVE](https://love2d.org/wiki/love.graphics.getFont).)
138 * `love.graphics.setFont(font)` -- switches the current font to `font`.
139 (From [LÖVE](https://love2d.org/wiki/love.graphics.setFont).)
141 * `love.graphics.newFont(filename)` -- creates a font from the given font
143 (From [LÖVE](https://love2d.org/wiki/love.graphics.newFont), including other
146 * `App.width(text)` returns the width of `text` in pixels when rendered using
148 (Based on [LÖVE](https://love2d.org/wiki/Font:getWidth).)
150 * `App.color(color)` -- sets the current color based on the fields `r`, `g`,
151 `b` and `a` (for opacity) of the table `color`.
152 (Based on [LÖVE](https://love2d.org/wiki/love.graphics.setColor).)
154 * `love.graphics.line(x1,y1, x2,y2)` -- draws a line from (`x1`,`y1`) to
155 (`x2`, `y2`) in the app window using the current color, clipping data for
156 negative coordinates and coordinates outside (`App.screen.width`,
158 (From [LÖVE](https://love2d.org/wiki/love.graphics.line), including other
161 * `love.graphics.rectangle(mode, x, y, w, h)` -- draws a rectangle using the
162 current color, with a top-left corner at (`x`, `y`), with dimensions `width`
163 along the x axis and `height` along the y axis
164 (though check out https://love2d.org/wiki/love.graphics for ways to scale
166 `mode` is a string, either `'line'` (to draw just the outline) and `'fill'`.
167 (From [LÖVE](https://love2d.org/wiki/love.graphics.circle), including other
170 * `love.graphics.circle(mode, x, y, r)` -- draws a circle using the current
171 color, centered at (`x`, `y`) and with radius `r`.
172 `mode` is a string, either `'line'` and `'fill'`.
173 (From [LÖVE](https://love2d.org/wiki/love.graphics.circle), including other
176 * `love.graphics.arc(mode, x, y, r, angle1, angle2)` -- draws an arc of a
177 circle using the current color, centered at (`x`, `y`) and with radius `r`.
178 `mode` is a string, either `'line'` and `'fill'`.
179 `angle1` and `angle2` are in [radians](https://en.wikipedia.org/wiki/Radian).
180 (From [LÖVE](https://love2d.org/wiki/love.graphics.circle), including other
183 There's much more I could include here; check out [the LÖVE manual](https://love2d.org/wiki/love.graphics).
185 ### text editor primitives
187 The text-editor widget includes extremely thorough automated tests to give you
188 early warning if you break something.
190 * `state = edit.initialize_state(top, left, right, font_height, line_height)` --
191 returns an object that can be used to render an interactive editor widget
192 for text and line drawings starting at `y=top` on the app window, between
193 `x=left` and `x=right`. Wraps long lines at word boundaries where possible,
194 or in the middle of words (no hyphenation yet) when it must.
196 * `edit.quit()` -- calling this ensures any final edits are flushed to disk
197 before the app exits.
199 * `edit.draw(state)` -- call this from `App.draw` to display the current
200 editor state on the app window as requested in the call to
201 `edit.initialize_state` that created `state`.
203 * `edit.update(state, dt)` -- call this from `App.update` to periodically
204 auto-save editor contents to disk.
206 * `edit.mouse_press(state, x,y, mouse_button)` and `edit.mouse_release(x,y,
207 mouse_button)` -- call these to position the cursor or select some text.
209 * `edit.mouse_wheel_move(state, dx,dy)` -- call this to scroll the editor in
210 response to a mouse wheel.
212 * `edit.keychord_press(state, chord, key)` and `edit.key_release(state, key)`
213 -- call these to perform some standard shortcuts: insert new lines,
214 backspace/delete, zoom in/out font size, cut/copy/paste to and from the
215 clipboard, undo/redo.
217 * `edit.text_input(state, t)` -- call this to insert keystrokes into the
220 * `Text.redraw_all(state)` -- call this to clear and recompute any cached
221 state as the cursor moves and the buffer scrolls.
223 If you need more precise control, look at the comment at the top of
224 `edit.initialize_state` in edit.lua. In brief, the widget contains an array of
225 `lines`. Positions in the buffer are described in _schema-1_ locations
226 consisting of a `line` index and a code-point `pos`. We may also convert them
227 at times to _schema-2_ locations consisting of a `line`, `screen_line` and
228 `pos` that better indicates how long lines wrap. Schema-2 locations are never
229 persisted, just generated as needed from schema-1. Important schema-1
230 locations in the widget are `cursor1` describing where text is inserted or
231 deleted and `screen_top1` which specifies how far down the lines is currently
236 * `App.mouse_move(x, y)` -- sets the current position of the mouse to (`x`,
238 (Based on [LÖVE](https://love2d.org/wiki/love.mouse.setPosition).)
240 * `App.mouse_down(mouse_button)` -- returns `true` if the button
241 `mouse_button` is pressed. See `on.mouse_press` for `mouse_button` codes.
242 (Based on [LÖVE](https://love2d.org/wiki/love.mouse.isDown).)
244 * `App.mouse_x()` -- returns the x coordinate of the current position of the
246 (Based on [LÖVE](https://love2d.org/wiki/love.mouse.getX).)
248 * `App.mouse_y()` -- returns the x coordinate of the current position of the
250 (Based on [LÖVE](https://love2d.org/wiki/love.mouse.getY).)
252 ### keyboard primitives
254 * `App.modifier_down(key)` -- returns `true` if the given key (doesn't
255 actually have to be just a modifier) is currently pressed.
256 (Based on [LÖVE](https://love2d.org/wiki/love.keyboard.isDown).)
258 ### interacting with files
260 * `App.open_for_reading(filename)` -- returns a file handle that you can
261 [`read()`](https://www.lua.org/manual/5.1/manual.html#pdf-file:read) from.
262 Make sure `filename` is an absolute path so that your app can work reliably
263 by double-clicking on it.
264 (Based on [Lua](https://www.lua.org/manual/5.1/manual.html#pdf-io.open).)
266 * `App.open_for_writing(filename)` -- returns a file handle that you can
267 [`write()`](https://www.lua.org/manual/5.1/manual.html#pdf-file:write) to.
268 Make sure `filename` is an absolute path so that your app can work reliably
269 by double-clicking on it.
270 (Based on [Lua](https://www.lua.org/manual/5.1/manual.html#pdf-io.open).)
272 * `json.encode(obj)` -- returns a JSON string for an object `obj` that will
273 recreate `obj` when passed to `json.decode`. `obj` can be of most types but
275 (From [json.lua](https://github.com/rxi/json.lua).)
277 * `json.decode(obj)` -- turns a JSON string into a Lua object.
278 (From [json.lua](https://github.com/rxi/json.lua).)
280 * `love.filesystem.getDirectoryItems(dir)` -- returns an unsorted array of the
281 files and directories available under `dir`. `dir` must be relative to
282 [LÖVE's save directory](https://love2d.org/wiki/love.filesystem.getSaveDirectory).
283 There is no easy, portable way in Lua/LÖVE to list directories outside the
285 (From [LÖVE](https://love2d.org/wiki/love.filesystem.getDirectoryItems).]
287 * `love.filesystem.getInfo(filename)` -- returns some information about
288 `filename`, particularly whether it exists (non-`nil` return value) or not.
289 `filename` must be relative to [LÖVE's save directory](https://love2d.org/wiki/love.filesystem.getSaveDirectory).
290 (From [LÖVE](https://love2d.org/wiki/love.filesystem.getInfo).]
292 * `os.remove(filename)` -- removes a file or empty directory. Definitely make
293 sure `filename` is an absolute path.
294 (From [Lua](https://www.lua.org/manual/5.1/manual.html#pdf-os.remove).)
296 There's much more I could include here; check out [the LÖVE manual](https://love2d.org/wiki/love.filesystem)
297 and [the Lua manual](https://www.lua.org/manual/5.1/manual.html#5.7).
301 * `App.getTime()` -- returns the number of seconds elapsed since some
302 unspecified start time.
303 (Based on [LÖVE](https://love2d.org/wiki/love.timer.getTime).)
305 * `App.getClipboardText()` -- returns a string with the current clipboard
307 (Based on [LÖVE](https://love2d.org/wiki/love.system.getClipboardText).)
309 * `App.setClipboardText(text)` -- stores the string `text` in the clipboard.
310 (Based on [LÖVE](https://love2d.org/wiki/love.system.setClipboardText).)
312 There's much more I could include here; check out [the LÖVE manual](https://love2d.org/wiki)
313 and [the Lua manual](https://www.lua.org/manual/5.1/manual.html).
317 * `App.screen.init{width=.., height=..}` -- creates a fake screen for a test
319 * `App.screen.check(y, expected_contents, msg)` -- verifies text written to
320 the fake screen at `y`. This isn't very realistic; `y` must exactly match
321 what was displayed, and the expected contents show everything printed to
322 that `y` in chronological order, regardless of `x` coordinate. In spite of
323 these limitations, you can write lots of useful tests with this.
325 * `App.run_after_textinput(t)` -- mimics keystrokes resulting in `t` and then
328 * `App.run_after_keychord(chord)` -- mimics keystrokes resulting in `chord`
329 and then draws one frame.
331 * `App.run_after_mouse_press(x,y, mouse_button)` -- mimics a mouse press down
332 followed by drawing a frame.
334 * `App.run_after_mouse_release(x,y, mouse_button)` -- mimics a mouse release
335 up followed by drawing a frame.
337 * `App.run_after_mouse_click(x,y, mouse_button)` -- mimics a mouse press down
338 and mouse release up followed by drawing a frame.
340 * `App.wait_fake_time(t)` -- simulates the passage of time for `App.getTime()`.