Merge lines.love
[view.love.git] / reference.md
blob5f2d4d3f9e38a4106829bb681edadc92b473c620
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
9 * `App.screen`
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)
12     for details.
14 * `Version` -- the running version of LÖVE as a string, e.g. '11.4'.
15 * `Major_version` -- just the part before the period as an int, e.g. 11.
17 ## Functions that get automatically called
19 * `App.initialize_globals()` -- called before running each test and also
20   before the app starts up. As the name suggests, use this to initialize all
21   your global variables to something consistent. I also find it useful to be
22   able to see all my global variables in one place, and avoid defining
23   top-level variables anywhere else (unless they're constants and never going
24   to be modified).
26 * `App.initialize(arg)` -- called when app starts up after
27   `App.initialize_globals`. Provides in `arg` an array of words typed in if
28   you ran it from a terminal window.
29   (Based on [LÖVE](https://love2d.org/wiki/love.load).)
31 * `App.quit()` -- called before the app shuts down.
32   (Based on [LÖVE](https://love2d.org/wiki/love.quit).)
34 * `App.focus(start?)` -- called when the app starts or stops receiving
35   keypresses. `start?` will be `true` when app starts receiving keypresses and
36   `false` when keypresses move to another window.
37   (Based on [LÖVE](https://love2d.org/wiki/love.focus).)
39 * `App.resize(w,h)` -- called when you resize the app window. Provides new
40   window dimensions in `w` and `h`. Don't bother updating `App.screen.width`
41   and `App.screen.height`, that will happen automatically before calling
42   `App.resize`.
43   (Based on [LÖVE](https://love2d.org/wiki/love.resize))
45 * `App.filedropped(file)` -- called when a file icon is dragged and dropped on
46   the app window. Provides in `file` an object representing the file that was
47   dropped, that will respond to the following messages:
49     * `file:getFilename()` returning a string name
50     * `file:read()` returning the entire file contents in a single string
52   (Based on [LÖVE](https://love2d.org/wiki/love.filedropped).)
54 * `App.draw()` -- called to draw on the window, around 30 times a second.
55   (Based on [LÖVE](https://love2d.org/wiki/love.draw).)
57 * `App.update(dt)` -- called after every call to `App.draw`. Make changes to
58   your app's variables here rather than in `App.draw`. Provides in `dt` the
59   time since the previous call to `App.update`, which can be useful for things
60   like smooth animations.
61   (Based on [LÖVE](https://love2d.org/wiki/love.update).)
63 * `App.mousepressed(x,y, mouse_button)` -- called when you press down on a
64   mouse button. Provides in `x` and `y` the point on the screen at which the
65   click occurred, and in `mouse_button` an integer id of the mouse button
66   pressed.
67   `1` is the primary mouse button (the left button on a right-handed mouse),
68   `2` is the secondary button (the right button on a right-handed mouse),
69   and `3` is the middle button. Further buttons are mouse-dependent.
70   (Based on [LÖVE](https://love2d.org/wiki/love.mousepressed).)
72 * `App.mousereleased(x,y, mouse_button)` -- called when you release a mouse
73   button. Provides the same arguments as `App.mousepressed()` above.
74   (Based on [LÖVE](https://love2d.org/wiki/love.mousereleased).)
76 * `App.wheelmoved(dx,dy)` -- called when you use the scroll wheel on a mouse
77   that has it. Provides in `dx` and `dy` an indication of how fast the wheel
78   is being scrolled. Positive values for `dx` indicate movement to the right.
79   Positive values for `dy` indicate upward movement.
80   (Based on [LÖVE](https://love2d.org/wiki/love.wheelmoved).)
82 * `App.keychord_press(chord, key)` -- called when you press a key-combination.
83   Provides in `key` a string name for the key most recently pressed ([valid
84   values](https://love2d.org/wiki/KeyConstant)). Provides in `chord` a
85   string representation of the current key combination, consisting of the key
86   with the following prefixes:
87     * `C-` if one of the `ctrl` keys is pressed,
88     * `M-` if one of the `alt` keys is pressed,
89     * `S-` if one of the `shift` keys is pressed, and
90     * `s-` if the `windows`/`cmd`/`super` key is pressed.
92 * `App.textinput(t)` -- called when you press a key combination that yields
93   (roughly) a printable character. For example, `shift` and `a` pressed
94   together will call `App.textinput` with `A`.
95   (Based on [LÖVE](https://love2d.org/wiki/love.textinput).)
97 * `App.keyreleased(key)` -- called when you press a key on the keyboard.
98   Provides in `key` a string name for the key ([valid values](https://love2d.org/wiki/KeyConstant)).
99   (Based on [LÖVE](https://love2d.org/wiki/love.keyreleased), including other
100   variants.)
102 ## Functions you can call
104 Everything in the [LÖVE](https://love2d.org/wiki/Main_Page) and
105 [Lua](https://www.lua.org/manual/5.1/manual.html) guides is available to you,
106 but here's a brief summary of the most useful primitives. Some primitives have
107 new, preferred names under the `App` namespace, often because these variants
108 are more testable. If you run them within a test you'll be able to make
109 assertions on their side-effects.
111 ### regarding the app window
113 * `width, height, flags = App.screen.size()` -- returns the dimensions and
114   some properties of the app window.
115   (Based on [LÖVE](https://love2d.org/wiki/love.window.getMode).)
117 * `App.screen.resize(width, height, flags)` -- modify the size and properties
118   of the app window. The OS may or may not act on the request.
119   (Based on [LÖVE](https://love2d.org/wiki/love.window.setMode).)
121 * `x, y, displayindex = App.screen.position()` -- returns the coordinates and
122   monitor index (if you have more than one monitor) for the top-left corner of
123   the app window.
124   (Based on [LÖVE](https://love2d.org/wiki/love.window.getPosition).)
126 * `App.screen.move(x, y, displayindex)` -- moves the app window so its
127   top-left corner is at the specified coordinates of the specified monitor.
128   The OS may or may not act on the request.
129   (Based on [LÖVE](https://love2d.org/wiki/love.window.setPosition).)
131 ### drawing to the app window
133 * `App.screen.print(text, x,y)` -- print the given `text` in the current font
134   using the current color so its top-left corner is at the specified
135   coordinates of the app window.
136   (Based on [LÖVE](https://love2d.org/wiki/love.graphics.print).)
138 * `love.graphics.getFont()` -- returns a representation of the current font.
139   (From [LÖVE](https://love2d.org/wiki/love.graphics.getFont).)
141 * `love.graphics.setFont(font)` -- switches the current font to `font`.
142   (From [LÖVE](https://love2d.org/wiki/love.graphics.setFont).)
144 * `love.graphics.newFont(filename)` -- creates a font from the given font
145   file.
146   (From [LÖVE](https://love2d.org/wiki/love.graphics.newFont), including other
147   variants.)
149 * `App.width(text)` returns the width of `text` in pixels when rendered using
150   the current font.
151   (Based on [LÖVE](https://love2d.org/wiki/Font:getWidth).)
153 * `App.color(color)` -- sets the current color based on the fields `r`, `g`,
154   `b` and `a` (for opacity) of the table `color`.
155   (Based on [LÖVE](https://love2d.org/wiki/love.graphics.setColor).)
157 * `love.graphics.line(x1,y1, x2,y2)` -- draws a line from (`x1`,`y1`) to
158   (`x2`, `y2`) in the app window using the current color, clipping data for
159   negative coordinates and coordinates outside (`App.screen.width`,
160   `App.screen.height`)
161   (From [LÖVE](https://love2d.org/wiki/love.graphics.line), including other
162   variants.)
164 * `love.graphics.rectangle(mode, x, y, w, h)` -- draws a rectangle using the
165   current color, with a top-left corner at (`x`, `y`), with dimensions `width`
166   along the x axis and `height` along the y axis
167   (though check out https://love2d.org/wiki/love.graphics for ways to scale
168   and rotate shapes).
169   `mode` is a string, either `'line'` (to draw just the outline) and `'fill'`.
170   (From [LÖVE](https://love2d.org/wiki/love.graphics.circle), including other
171   variants.)
173 * `love.graphics.circle(mode, x, y, r)` -- draws a circle using the current
174   color, centered at (`x`, `y`) and with radius `r`.
175   `mode` is a string, either `'line'` and `'fill'`.
176   (From [LÖVE](https://love2d.org/wiki/love.graphics.circle), including other
177   variants.)
179 * `love.graphics.arc(mode, x, y, r, angle1, angle2)` -- draws an arc of a
180   circle using the current color, centered at (`x`, `y`) and with radius `r`.
181   `mode` is a string, either `'line'` and `'fill'`.
182   `angle1` and `angle2` are in [radians](https://en.wikipedia.org/wiki/Radian).
183   (From [LÖVE](https://love2d.org/wiki/love.graphics.circle), including other
184   variants.)
186 There's much more I could include here; check out [the LÖVE manual](https://love2d.org/wiki/love.graphics).
188 ### text editor primitives
190 The text-editor widget includes extremely thorough automated tests to give you
191 early warning if you break something.
193 * `state = edit.initialize_state(top, left, right, font_height, line_height)` --
194   returns an object that can be used to render an interactive editor widget
195   for text starting at `y=top` on the app window, between `x=left` and
196   `x=right`. Wraps long lines at word boundaries where possible, or in the
197   middle of words (no hyphenation yet) when it must.
199 * `edit.quit()` -- calling this ensures any final edits are flushed to disk
200   before the app exits.
202 * `edit.draw(state)` -- call this from `App.draw` to display the current
203   editor state on the app window as requested in the call to
204   `edit.initialize_state` that created `state`.
206 * `edit.update(state, dt)` -- call this from `App.update` to periodically
207   auto-save editor contents to disk.
209 * `edit.mouse_press(state, x,y, mouse_button)` and `edit.mouse_release(x,y,
210   mouse_button)` -- call these to position the cursor or select some text.
212 * `edit.mouse_wheel_move(state, dx,dy)` -- call this to scroll the editor in
213   response to a mouse wheel.
215 * `edit.keychord_press(state, chord, key)` and `edit.key_release(state, key)`
216   -- call these to perform some standard shortcuts: insert new lines,
217   backspace/delete, zoom in/out font size, cut/copy/paste to and from the
218   clipboard, undo/redo.
220 * `edit.text_input(state, t)` -- call this to insert keystrokes into the
221   buffer.
223 * `Text.redraw_all(state)` -- call this to clear and recompute any cached
224   state as the cursor moves and the buffer scrolls.
226 If you need more precise control, look at the comment at the top of
227 `edit.initialize_state` in edit.lua. In brief, the widget contains an array of
228 `lines`. Positions in the buffer are described in _schema-1_ locations
229 consisting of a `line` index and a code-point `pos`. We may also convert them
230 at times to _schema-2_ locations consisting of a `line`, `screen_line` and
231 `pos` that better indicates how long lines wrap. Schema-2 locations are never
232 persisted, just generated as needed from schema-1. Important schema-1
233 locations in the widget are `cursor1` describing where text is inserted or
234 deleted and `screen_top1` which specifies how far down the lines is currently
235 visible on screen.
237 Some constants that affect editor behavior:
238 * `Margin_top`, `Margin_left`, `Margin_right` are integers in pixel units that
239   affect where the editor is drawn on window (it always extends to bottom of
240   window as needed)
242 * Various color constants are represented as tables with r/g/b keys:
243   * `Text_color`, `Cursor_color`, `Highlight_color` for drawing text.
245 ### clickable buttons
247 There's a facility for rendering buttons and responding to events when they're
248 clicked. It requires setting up 3 things:
249   - a `state` table housing all buttons. Can be the same `state` variable the
250     text-editor widget uses, but doesn't have to be.
251   - specifying buttons to create in `state`. This must happen either directly
252     or indirectly within `App.draw`.
253   - responding to clicks on buttons in `state`. This must happen either
254     directly or indirectly within `App.mousepressed`.
256 The following facilities help set these things up:
258 * Clear `state` at the start of each frame:
260     ```
261     state.button_handlers = {}
262     ```
264   Don't forget to do this, or your app will get slower over time.
266 * `button` creates a single button. The syntax is:
268     ```
269     button(state, name, {x=..., y=..., w=..., h=..., bg={r,g,b},
270       icon = function({x=..., y=..., w=..., h=...}) ... end,
271       onpress1 = ...
272     })
273     ```
275   Call this either directly or indirectly from `App.draw`. It will assign a
276   rectangle with the given dimensions and trigger the provided (zero-arg)
277   `onpress1` callback when the primary mouse button is clicked within.
278   It will also optionally paint the rectangle with the specified background
279   color `bg` and a foreground described by the `icon` callback (which will
280   receive the same dimensions).
282   This way you can see everything about a button in one place. Create as many
283   buttons as you like within a single shared `state`.
285 * `mouse_press_consumed_by_any_button(state, x,y, mouse_button)`
287   Call this either directly or indirectly from `App.mousepressed`. It will
288   pass on a click to any button registered in `state`. It's also helpful to
289   ensure clicks on a button don't have other effects, so I prefer the
290   following boilerplate early in `mousepressed`:
292     ```
293     if mouse_press_consumed_by_any_button(state, x,y, mouse_button) then
294       return
295     end
296     ```
298 ### mouse primitives
300 * `App.mouse_move(x, y)` -- sets the current position of the mouse to (`x`,
301   `y`).
302   (Based on [LÖVE](https://love2d.org/wiki/love.mouse.setPosition).)
304 * `App.mouse_down(mouse_button)` -- returns `true` if the button
305   `mouse_button` is pressed. See `App.mousepressed` for `mouse_button` codes.
306   (Based on [LÖVE](https://love2d.org/wiki/love.mouse.isDown).)
308 * `App.mouse_x()` -- returns the x coordinate of the current position of the
309   mouse.
310   (Based on [LÖVE](https://love2d.org/wiki/love.mouse.getX).)
312 * `App.mouse_y()` -- returns the x coordinate of the current position of the
313   mouse.
314   (Based on [LÖVE](https://love2d.org/wiki/love.mouse.getY).)
316 ### keyboard primitives
318 * `App.is_cursor_movement(key)` -- return `true` if `key` is a cursor movement
319   key (arrow keys, page-up/down, home/end)
321 * `App.cmd_down()`, `App.ctrl_down`, `App.alt_down()`, `App.shift_down()` --
322   predicates for different modifier keys.
324 * `App.any_modifier_down()` -- returns `true` if any of the modifier keys is
325   currently pressed.
327 * `App.key_down(key)` -- returns `true` if the given key is currently pressed.
328   (Based on [LÖVE](https://love2d.org/wiki/love.keyboard.isDown).)
330 ### interacting with files
332 * `App.open_for_reading(filename)` -- returns a file handle that you can
333   [`read()`](https://www.lua.org/manual/5.1/manual.html#pdf-file:read) from.
334   Make sure `filename` is an absolute path so that your app can work reliably
335   by double-clicking on it.
336   (Based on [Lua](https://www.lua.org/manual/5.1/manual.html#pdf-io.open).)
338 * `App.open_for_writing(filename)` -- returns a file handle that you can
339   [`write()`](https://www.lua.org/manual/5.1/manual.html#pdf-file:write) to.
340   Make sure `filename` is an absolute path so that your app can work reliably
341   by double-clicking on it.
342   (Based on [Lua](https://www.lua.org/manual/5.1/manual.html#pdf-io.open).)
344 * `json.encode(obj)` -- returns a JSON string for an object `obj` that will
345   recreate `obj` when passed to `json.decode`. `obj` can be of most types but
346   has some exceptions.
347   (From [json.lua](https://github.com/rxi/json.lua).)
349 * `json.decode(obj)` -- turns a JSON string into a Lua object.
350   (From [json.lua](https://github.com/rxi/json.lua).)
352 * `App.files(dir)` -- returns an unsorted array of the files and directories
353   available under `dir`.
354   (From [LÖVE](https://love2d.org/wiki/love.filesystem.getDirectoryItems).]
356 * `App.file_info(filename)` -- returns some information about
357   `filename`, particularly whether it exists (non-`nil` return value) or not.
358   (From [LÖVE](https://love2d.org/wiki/love.filesystem.getInfo).]
360 * `App.mkdir(path)` -- creates a directory. Make sure `path` is absolute.
361   (From [LÖVE](https://love2d.org/wiki/love.filesystem.remove).]
363 * `App.remove(filename)` -- removes a file or empty directory. Definitely make
364   sure `filename` is an absolute path.
365   (From [LÖVE](https://love2d.org/wiki/love.filesystem.remove).]
367 There's much more I could include here; check out [the LÖVE manual](https://love2d.org/wiki/love.filesystem)
368 and [the Lua manual](https://www.lua.org/manual/5.1/manual.html#5.7).
370 ### desiderata
372 * `App.get_time()` -- returns the number of seconds elapsed since some
373   unspecified start time.
374   (Based on [LÖVE](https://love2d.org/wiki/love.timer.getTime).)
376 * `App.get_clipboard()` -- returns a string with the current clipboard
377   contents.
378   (Based on [LÖVE](https://love2d.org/wiki/love.system.getClipboardText).)
380 * `App.set_clipboard(text)` -- stores the string `text` in the clipboard.
381   (Based on [LÖVE](https://love2d.org/wiki/love.system.setClipboardText).)
383 * `array.find(arr, elem)` -- scan table `arr` for `elem` assuming it's
384   organized as an array (just numeric indices).
386 * `array.any(arr, f)` -- scan table `arr` for any elements satisfying
387   predicate `f`. Return first such element or `false` if none.
389 There's much more I could include here; check out [the LÖVE manual](https://love2d.org/wiki)
390 and [the Lua manual](https://www.lua.org/manual/5.1/manual.html).
392 ### writing tests
394 * `App.screen.init{width=.., height=..}` -- creates a fake screen for a test
396 * `App.screen.check(y, expected_contents, msg)` -- verifies text written to
397   the fake screen at `y`. This isn't very realistic; `y` must exactly match
398   what was displayed, and the expected contents show everything printed to
399   that `y` in chronological order, regardless of `x` coordinate. In spite of
400   these limitations, you can write lots of useful tests with this.
402 * `App.run_after_textinput(t)` -- mimics keystrokes resulting in `t` and then
403   draws one frame.
405 * `App.run_after_keychord(chord)` -- mimics keystrokes resulting in `chord`
406   and then draws one frame.
408 * `App.run_after_mouse_press(x,y, mouse_button)` -- mimics a mouse press down
409   followed by drawing a frame.
411 * `App.run_after_mouse_release(x,y, mouse_button)` -- mimics a mouse release
412   up followed by drawing a frame.
414 * `App.run_after_mouse_click(x,y, mouse_button)` -- mimics a mouse press down
415   and mouse release up followed by drawing a frame.
417 * `App.wait_fake_time(t)` -- simulates the passage of time for `App.getTime()`.