summary refs log tree commit diff stats
path: root/README
diff options
context:
space:
mode:
authorhut <hut@lavabit.com>2010-08-28 21:27:43 +0200
committerhut <hut@lavabit.com>2010-08-28 21:27:43 +0200
commit0df1d653deb7a9a8ca30f012475891e339b4449f (patch)
treecc56d9916bb66029280c8fd2a1d96ca976202aca /README
parentbd33933dd1cb708a5e708b361c1842190a1c9e4b (diff)
downloadranger-0df1d653deb7a9a8ca30f012475891e339b4449f.tar.gz
Changed version number to 1.2 (testing) to adhere with versioning scheme
Diffstat (limited to 'README')
-rw-r--r--README4
1 files changed, 2 insertions, 2 deletions
diff --git a/README b/README
index babcfbbb..912b8718 100644
--- a/README
+++ b/README
@@ -1,5 +1,5 @@
-Ranger v.1.1.2
-==============
+Ranger v.1.2
+============
 
 Ranger is a free console file manager that gives you greater flexibility
 and a good overview of your files without having to leave your *nix console.
139' href='#n139'>139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438
# Some useful building blocks

Apps can be composed of a wide variety of building blocks that you
can use in your functions, including a small number of functions that get
automatically called for you as appropriate.

## Variables you can read

* `App.screen`
  * `width` and `height` -- integer dimensions for the app window in pixels.
  * `flags` -- some properties of the app window. See [`flags` in `love.graphics.getMode`](https://love2d.org/wiki/love.window.getMode)
    for details.

* `Version` -- the running version of LÖVE as a string, e.g. '11.4'.
* `Major_version` -- just the part before the period as an int, e.g. 11.

## Functions that get automatically called

* `App.initialize_globals()` -- called before running each test and also
  before the app starts up. As the name suggests, use this to initialize all
  your global variables to something consistent. I also find it useful to be
  able to see all my global variables in one place, and avoid defining
  top-level variables anywhere else (unless they're constants and never going
  to be modified).

* `App.initialize(arg)` -- called when app starts up after
  `App.initialize_globals`. Provides in `arg` an array of words typed in if
  you ran it from a terminal window.
  (Based on [LÖVE](https://love2d.org/wiki/love.load).)

* `App.quit()` -- called before the app shuts down.
  (Based on [LÖVE](https://love2d.org/wiki/love.quit).)

* `App.focus(start?)` -- called when the app starts or stops receiving
  keypresses. `start?` will be `true` when app starts receiving keypresses and
  `false` when keypresses move to another window.
  (Based on [LÖVE](https://love2d.org/wiki/love.focus).)

* `App.resize(w,h)` -- called when you resize the app window. Provides new
  window dimensions in `w` and `h`. Don't bother updating `App.screen.width`
  and `App.screen.height`, that will happen automatically before calling
  `App.resize`.
  (Based on [LÖVE](https://love2d.org/wiki/love.resize))

* `App.filedropped(file)` -- called when a file icon is dragged and dropped on
  the app window. Provides in `file` an object representing the file that was
  dropped, that will respond to the following messages:

    * `file:getFilename()` returning a string name
    * `file:read()` returning the entire file contents in a single string

  (Based on [LÖVE](https://love2d.org/wiki/love.filedropped).)

* `App.draw()` -- called to draw on the window, around 30 times a second.
  (Based on [LÖVE](https://love2d.org/wiki/love.draw).)

* `App.update(dt)` -- called after every call to `App.draw`. Make changes to
  your app's variables here rather than in `App.draw`. Provides in `dt` the
  time since the previous call to `App.update`, which can be useful for things
  like smooth animations.
  (Based on [LÖVE](https://love2d.org/wiki/love.update).)

* `App.mousepressed(x,y, mouse_button)` -- called when you press down on a
  mouse button. Provides in `x` and `y` the point on the screen at which the
  click occurred, and in `mouse_button` an integer id of the mouse button
  pressed.
  `1` is the primary mouse button (the left button on a right-handed mouse),
  `2` is the secondary button (the right button on a right-handed mouse),
  and `3` is the middle button. Further buttons are mouse-dependent.
  (Based on [LÖVE](https://love2d.org/wiki/love.mousepressed).)

* `App.mousereleased(x,y, mouse_button)` -- called when you release a mouse
  button. Provides the same arguments as `App.mousepressed()` above.
  (Based on [LÖVE](https://love2d.org/wiki/love.mousereleased).)

* `App.mousemoved(x,y, dx,dy, is_touch)` -- called any time the mouse moves.
  (Based on [LÖVE](https://love2d.org/wiki/love.mousemoved).)

* `App.wheelmoved(dx,dy)` -- called when you use the scroll wheel on a mouse
  that has it. Provides in `dx` and `dy` an indication of how fast the wheel
  is being scrolled. Positive values for `dx` indicate movement to the right.
  Positive values for `dy` indicate upward movement.
  (Based on [LÖVE](https://love2d.org/wiki/love.wheelmoved).)

* `App.mousefocus(in_focus)` -- called when the mouse pointer moves on or off
  the app window.
  (Based on [LÖVE](https://love2d.org/wiki/love.mousefocus).)

* `App.keychord_press(chord, key)` -- called when you press a key-combination.
  Provides in `key` a string name for the key most recently pressed ([valid
  values](https://love2d.org/wiki/KeyConstant)). Provides in `chord` a
  string representation of the current key combination, consisting of the key
  with the following prefixes:
    * `C-` if one of the `ctrl` keys is pressed,
    * `M-` if one of the `alt` keys is pressed,
    * `S-` if one of the `shift` keys is pressed, and
    * `s-` if the `windows`/`cmd`/`super` key is pressed.

* `App.textinput(t)` -- called when you press a key combination that yields
  (roughly) a printable character. For example, `shift` and `a` pressed
  together will call `App.textinput` with `A`.
  (Based on [LÖVE](https://love2d.org/wiki/love.textinput).)

* `App.keyreleased(key)` -- called when you press a key on the keyboard.
  Provides in `key` a string name for the key ([valid values](https://love2d.org/wiki/KeyConstant)).
  (Based on [LÖVE](https://love2d.org/wiki/love.keyreleased), including other
  variants.)

## Functions you can call

Everything in the [LÖVE](https://love2d.org/wiki/Main_Page) and
[Lua](https://www.lua.org/manual/5.1/manual.html) guides is available to you,
but here's a brief summary of the most useful primitives. Some primitives have
new, preferred names under the `App` namespace, often because these variants
are more testable. If you run them within a test you'll be able to make
assertions on their side-effects.

### regarding the app window

* `width, height, flags = App.screen.size()` -- returns the dimensions and
  some properties of the app window.
  (Based on [LÖVE](https://love2d.org/wiki/love.window.getMode).)

* `App.screen.resize(width, height, flags)` -- modify the size and properties
  of the app window. The OS may or may not act on the request.
  (Based on [LÖVE](https://love2d.org/wiki/love.window.setMode).)

* `x, y, displayindex = App.screen.position()` -- returns the coordinates and
  monitor index (if you have more than one monitor) for the top-left corner of
  the app window.
  (Based on [LÖVE](https://love2d.org/wiki/love.window.getPosition).)

* `App.screen.move(x, y, displayindex)` -- moves the app window so its
  top-left corner is at the specified coordinates of the specified monitor.
  The OS may or may not act on the request.
  (Based on [LÖVE](https://love2d.org/wiki/love.window.setPosition).)

### drawing to the app window

* `App.screen.print(text, x,y)` -- print the given `text` in the current font
  using the current color so its top-left corner is at the specified
  coordinates of the app window.
  (Based on [LÖVE](https://love2d.org/wiki/love.graphics.print).)

* `love.graphics.getFont()` -- returns a representation of the current font.
  (From [LÖVE](https://love2d.org/wiki/love.graphics.getFont).)

* `love.graphics.setFont(font)` -- switches the current font to `font`.
  (From [LÖVE](https://love2d.org/wiki/love.graphics.setFont).)

* `love.graphics.newFont(filename)` -- creates a font from the given font
  file.
  (From [LÖVE](https://love2d.org/wiki/love.graphics.newFont), including other
  variants.)

* `App.width(text)` returns the width of `text` in pixels when rendered using
  the current font.
  (Based on [LÖVE](https://love2d.org/wiki/Font:getWidth).)

* `App.color(color)` -- sets the current color based on the fields `r`, `g`,
  `b` and `a` (for opacity) of the table `color`.
  (Based on [LÖVE](https://love2d.org/wiki/love.graphics.setColor).)

* `love.graphics.line(x1,y1, x2,y2)` -- draws a line from (`x1`,`y1`) to
  (`x2`, `y2`) in the app window using the current color, clipping data for
  negative coordinates and coordinates outside (`App.screen.width`,
  `App.screen.height`)
  (From [LÖVE](https://love2d.org/wiki/love.graphics.line), including other
  variants.)

* `love.graphics.rectangle(mode, x, y, w, h)` -- draws a rectangle using the
  current color, with a top-left corner at (`x`, `y`), with dimensions `width`
  along the x axis and `height` along the y axis
  (though check out https://love2d.org/wiki/love.graphics for ways to scale
  and rotate shapes).
  `mode` is a string, either `'line'` (to draw just the outline) and `'fill'`.
  (From [LÖVE](https://love2d.org/wiki/love.graphics.circle), including other
  variants.)

* `love.graphics.circle(mode, x, y, r)` -- draws a circle using the current
  color, centered at (`x`, `y`) and with radius `r`.
  `mode` is a string, either `'line'` and `'fill'`.
  (From [LÖVE](https://love2d.org/wiki/love.graphics.circle), including other
  variants.)

* `love.graphics.arc(mode, x, y, r, angle1, angle2)` -- draws an arc of a
  circle using the current color, centered at (`x`, `y`) and with radius `r`.
  `mode` is a string, either `'line'` and `'fill'`.
  `angle1` and `angle2` are in [radians](https://en.wikipedia.org/wiki/Radian).
  (From [LÖVE](https://love2d.org/wiki/love.graphics.circle), including other
  variants.)

There's much more I could include here; check out [the LÖVE manual](https://love2d.org/wiki/love.graphics).

### text editor primitives

The text-editor widget includes extremely thorough automated tests to give you
early warning if you break something.

* `state = edit.initialize_state(top, left, right, font, font_height, line_height)`
  -- returns an object that can be used to render an interactive editor widget
  for text and line drawings starting at `y=top` on the app window, between
  `x=left` and `x=right`. Wraps long lines at word boundaries where possible,
  or in the middle of words (no hyphenation yet) when it must.

* `edit.quit()` -- calling this ensures any final edits are flushed to disk
  before the app exits.

* `edit.draw(state)` -- call this from `App.draw` to display the current
  editor state on the app window as requested in the call to
  `edit.initialize_state` that created `state`.

* `edit.mouse_press(state, x,y, mouse_button)` and `edit.mouse_release(x,y,
  mouse_button)` -- call these to position the cursor or select some text.

* `edit.mouse_wheel_move(state, dx,dy)` -- call this to scroll the editor in
  response to a mouse wheel.

* `edit.keychord_press(state, chord, key)` and `edit.key_release(state, key)`
  -- call these to perform some standard shortcuts: insert new lines,
  backspace/delete, zoom in/out font size, cut/copy/paste to and from the
  clipboard, undo/redo.

* `edit.text_input(state, t)` -- call this to insert keystrokes into the
  buffer.

* `Text.redraw_all(state)` -- call this to clear and recompute any cached
  state as the cursor moves and the buffer scrolls.

* `edit.update(state, dt)` -- call this from `App.update` to periodically
  auto-save editor contents to disk.

* `edit.quit(state)` -- call this from `App.quit` to ensure any final edits
  get saved before quitting.

If you need more precise control, look at the comment at the top of
`edit.initialize_state` in edit.lua. In brief, the widget contains an array of
`lines`. Positions in the buffer are described in _schema-1_ locations
consisting of a `line` index and a code-point `pos`. We may also convert them
at times to _schema-2_ locations consisting of a `line`, `screen_line` and
`pos` that better indicates how long lines wrap. Schema-2 locations are never
persisted, just generated as needed from schema-1. Important schema-1
locations in the widget are `cursor1` describing where text is inserted or
deleted and `screen_top1` which specifies how far down the lines is currently
visible on screen.

Some constants that affect editor behavior:
* `Margin_top`, `Margin_left`, `Margin_right` are integers in pixel units that
  affect where the editor is drawn on window (it always extends to bottom of
  window as needed)
* `Drawing_padding_top` and `Drawing_padding_bottom` affect spacing around
  drawings.

* Various color constants are represented as tables with r/g/b keys:
  * `Text_color`, `Cursor_color`, `Highlight_color` for drawing text.
  * `Stroke_color`, `Current_stroke_color` for line drawings.
  * `Icon_color` affects the color of the little mode icon on the top right of
    a drawing.
  * `Current_name_background_color` manages the color when naming points using
    `ctrl+n`.
  * `Focus_stroke_color` affects the color of a point or line when you hover
    over it.
  * `Help_color` and `Help_background_color` affect the color of online help
    within line drawings.

### clickable buttons

There's a facility for rendering buttons and responding to events when they're
clicked. It requires setting up 3 things:
  - a `state` table housing all buttons. Can be the same `state` variable the
    text-editor widget uses, but doesn't have to be.
  - specifying buttons to create in `state`. This must happen either directly
    or indirectly within `App.draw`.
  - responding to clicks on buttons in `state`. This must happen either
    directly or indirectly within `App.mousepressed`.

The following facilities help set these things up:

* Clear `state` at the start of each frame:

    ```
    state.button_handlers = {}
    ```

  Don't forget to do this, or your app will get slower over time.

* `button` creates a single button. The syntax is:

    ```
    button(state, name, {x=..., y=..., w=..., h=..., bg={r,g,b},
      icon = function({x=..., y=..., w=..., h=...}) ... end,
      onpress1 = ...
    })
    ```

  Call this either directly or indirectly from `App.draw`. It will assign a
  rectangle with the given dimensions and trigger the provided (zero-arg)
  `onpress1` callback when the primary mouse button is clicked within.
  It will also optionally paint the rectangle with the specified background
  color `bg` and a foreground described by the `icon` callback (which will
  receive the same dimensions).

  This way you can see everything about a button in one place. Create as many
  buttons as you like within a single shared `state`.

* `mouse_press_consumed_by_any_button(state, x,y, mouse_button)`

  Call this either directly or indirectly from `App.mousepressed`. It will
  pass on a click to any button registered in `state`. It's also helpful to
  ensure clicks on a button don't have other effects, so I prefer the
  following boilerplate early in `mousepressed`:

    ```
    if mouse_press_consumed_by_any_button(state, x,y, mouse_button) then
      return
    end
    ```

### mouse primitives

* `App.mouse_move(x, y)` -- sets the current position of the mouse to (`x`,
  `y`).
  (Based on [LÖVE](https://love2d.org/wiki/love.mouse.setPosition).)

* `App.mouse_down(mouse_button)` -- returns `true` if the button
  `mouse_button` is pressed. See `App.mousepressed` for `mouse_button` codes.
  (Based on [LÖVE](https://love2d.org/wiki/love.mouse.isDown).)

* `App.mouse_x()` -- returns the x coordinate of the current position of the
  mouse.
  (Based on [LÖVE](https://love2d.org/wiki/love.mouse.getX).)

* `App.mouse_y()` -- returns the x coordinate of the current position of the
  mouse.
  (Based on [LÖVE](https://love2d.org/wiki/love.mouse.getY).)

### keyboard primitives

* `App.is_cursor_movement(key)` -- return `true` if `key` is a cursor movement
  key (arrow keys, page-up/down, home/end)

* `App.cmd_down()`, `App.ctrl_down`, `App.alt_down()`, `App.shift_down()` --
  predicates for different modifier keys.

* `App.any_modifier_down()` -- returns `true` if any of the modifier keys is
  currently pressed.

* `App.key_down(key)` -- returns `true` if the given key is currently pressed.
  (Based on [LÖVE](https://love2d.org/wiki/love.keyboard.isDown).)

### interacting with files

* `App.open_for_reading(filename)` -- returns a file handle that you can
  [`read()`](https://www.lua.org/manual/5.1/manual.html#pdf-file:read) from.
  Make sure `filename` is an absolute path so that your app can work reliably
  by double-clicking on it.
  (Based on [Lua](https://www.lua.org/manual/5.1/manual.html#pdf-io.open).)

* `App.open_for_writing(filename)` -- returns a file handle that you can
  [`write()`](https://www.lua.org/manual/5.1/manual.html#pdf-file:write) to.
  Make sure `filename` is an absolute path so that your app can work reliably
  by double-clicking on it.
  (Based on [Lua](https://www.lua.org/manual/5.1/manual.html#pdf-io.open).)

* `json.encode(obj)` -- returns a JSON string for an object `obj` that will
  recreate `obj` when passed to `json.decode`. `obj` can be of most types but
  has some exceptions.
  (From [json.lua](https://github.com/rxi/json.lua).)

* `json.decode(obj)` -- turns a JSON string into a Lua object.
  (From [json.lua](https://github.com/rxi/json.lua).)

* `App.files(dir)` -- returns an unsorted array of the files and directories
  available under `dir`.
  (From [LÖVE](https://love2d.org/wiki/love.filesystem.getDirectoryItems).]

* `App.file_info(filename)` -- returns some information about
  `filename`, particularly whether it exists (non-`nil` return value) or not.
  (From [LÖVE](https://love2d.org/wiki/love.filesystem.getInfo).]

* `App.mkdir(path)` -- creates a directory. Make sure `path` is absolute.
  (From [LÖVE](https://love2d.org/wiki/love.filesystem.remove).]

* `App.remove(filename)` -- removes a file or empty directory. Definitely make
  sure `filename` is an absolute path.
  (From [LÖVE](https://love2d.org/wiki/love.filesystem.remove).]

There's much more I could include here; check out [the LÖVE manual](https://love2d.org/wiki/love.filesystem)
and [the Lua manual](https://www.lua.org/manual/5.1/manual.html#5.7).

### desiderata

* `App.get_time()` -- returns the number of seconds elapsed since some
  unspecified start time.
  (Based on [LÖVE](https://love2d.org/wiki/love.timer.getTime).)

* `App.get_clipboard()` -- returns a string with the current clipboard
  contents.
  (Based on [LÖVE](https://love2d.org/wiki/love.system.getClipboardText).)

* `App.set_clipboard(text)` -- stores the string `text` in the clipboard.
  (Based on [LÖVE](https://love2d.org/wiki/love.system.setClipboardText).)

* `array.find(arr, elem)` -- scan table `arr` for `elem` assuming it's
  organized as an array (just numeric indices).

* `array.any(arr, f)` -- scan table `arr` for any elements satisfying
  predicate `f`. Return first such element or `false` if none.

There's much more I could include here; check out [the LÖVE manual](https://love2d.org/wiki)
and [the Lua manual](https://www.lua.org/manual/5.1/manual.html).

### writing tests

* `App.screen.init{width=.., height=..}` -- creates a fake screen for a test

* `App.screen.check(y, expected_contents, msg)` -- verifies text written to
  the fake screen at `y`. This isn't very realistic; `y` must exactly match
  what was displayed, and the expected contents show everything printed to
  that `y` in chronological order, regardless of `x` coordinate. In spite of
  these limitations, you can write lots of useful tests with this.

* `App.run_after_textinput(t)` -- mimics keystrokes resulting in `t` and then
  draws one frame.

* `App.run_after_keychord(chord, key)` -- mimics the final `key` press
  resulting in `chord` and then draws one frame.

* `App.run_after_mouse_press(x,y, mouse_button)` -- mimics a mouse press down
  followed by drawing a frame.

* `App.run_after_mouse_release(x,y, mouse_button)` -- mimics a mouse release
  up followed by drawing a frame.

* `App.run_after_mouse_click(x,y, mouse_button)` -- mimics a mouse press down
  and mouse release up followed by drawing a frame.

* `App.wait_fake_time(t)` -- simulates the passage of time for `App.getTime()`.