INPUT /  OUTPUT /  LANGUAGE





Events in Depth

You've used onClick since the chapter on sending input, and met onTimer and onResize along the way. This chapter covers the rest of the events your interface can respond to: the mouse, the keyboard, typing, and the user trying to close your program. It also introduces dialog boxes, for asking the user a quick question.

All the event fields work the same way: they're code fields, holding code that's carried out each time the event happens. Give them code when you create a tag, or add it later with .push.


Mouse events

FieldHappens when...
onClickthe user clicks the tag
onMouseDowna mouse button is pressed over the tag
onMouseUpa mouse button is released over the tag
onMouseInthe pointer moves onto the tag
onMouseOutthe pointer moves off the tag
onMouseMovethe pointer moves while it's over the tag
onContextClickthe user right-clicks the tag

Any presentation tag can respond to these, not just buttons. This box lights up while the pointer is over it:

<hover:box width=200 height=100 border=1 backgroundColor=null
    onMouseIn=<hover.backgroundColor {teal}>
    onMouseOut=<hover.backgroundColor null>
    {Point at me}>

onMouseDown and onMouseUp are useful when something should look different while it's held down, like a key on an on-screen keyboard.

Where is the mouse?

While mouse event code is running, mouse holds three values. The second and third are the pointer's position, in pixels, measured from the top-left corner of the tag. (The first relates to the mouse buttons.) So <mouse.@ 2> is how far across the tag the pointer is, and <mouse.@ 3> how far down.

<pad:box width=300 height=200 border=1
    onMouseMove=<where {x=}, <mouse.@ 2>, { y=}, <mouse.@ 3>> {}>
<where:span {Move the mouse over the box}>

Keyboard events

onKeyDown happens when a key is pressed, and onKeyUp when it's released. While their code is running, key holds a number identifying which key it was:

KeykeyKeykey
A to Z65 to 90Enter13
0 to 948 to 57Space32
Left, Up, Right, Down37, 38, 39, 40Backspace8
Shift16Tab9
Ctrl17

These numbers identify keys, not characters: A is 65 whether or not Shift is held down. To find the number for any other key, push <putLn key> into an onKeyDown and try it.

Keyboard events go to the tag the user is typing in, and also to the console itself. To respond to keys wherever the user is, use the console's onKeyDown:

<console.onKeyDown.push <if <eq key, 32> then=<putLn {/space}>>>

Pressing Enter in an input box

Users expect to be able to press Enter instead of clicking a button. Give the input box an onKeyDown that checks for key 13:

<message:input onKeyDown=<if <eq key, 13> then=<putLn message>, <message.clear>>>

Typing and focus

onInput happens each time the user changes what's in an input box, so you can respond as they type, for example to show how many characters they've used:

<tweet:input onInput=<count | sub 140, tweet.#>>
<count:span {140}> characters left

onFocus happens when a tag becomes the one the user is typing in, whether they clicked it or moved to it with Tab.

While the console carries out event code, it normally goes "busy": it greys out the interface and ignores clicks until the code has finished. That's usually what you want, but if code runs on every keystroke, the flickering can get in the way of typing. Print <console.busy false> to turn this off.

Dialog boxes

Sometimes you just need to ask the user a quick question. The dialog tag pops up a box with a message, waits for the user to respond, and generates their answer. It's a transient logic tag: once the user has answered, it's gone.

Asking for text

With text=true, the dialog has a box to type in. Combined with putLn, this gives your program a very quick way to ask a question. Python's input() prints its argument before waiting, so the whole thing fits on one line:

name = input('<putLn | dialog text=true {What is your name?}>')
print('Hello, ' + name + '!')

Offering choices

With choices, the user picks one of the options you give. The dialog generates the number of the choice they picked, starting from 1. A Cancel button is always included, and generates an empty value:

colour = input('<putLn | dialog {Pick a colour} choices={Red},{Green},{Blue}>')
# colour is '1', '2' or '3', or '' if the user cancelled

For a simple yes-or-no question, give a single choice and turn the result into something easy to check:

answer = input('<putLn | if <dialog {Delete everything?} choices={OK}> then={Y} else={N}>')
if answer == 'Y':
    ...

When the user tries to close your program

The console's onExit field is carried out when the user (or the system) tries to close the console. Without it, the console just closes. With it, your program gets the chance to respond, for example to save the user's work first, or to check they really meant it:

<console.onExit.push <putLn {/quit}>>

Now closing the window sends /quit to your program, just like a Quit button would, and it's up to your program to finish. You can even ask first:

<console.onExit.push
    <if <dialog {Quit without saving?} choices={Quit}> then=<putLn {/quit}>>
>

Example: moving with the arrow keys

This program moves a box around the screen with the arrow keys, entirely inside the console. The box is positioned with inline=false, as in the last chapter, and each arrow key changes its position by 10 pixels:

print('''
<console.title {Arrow keys}>
<x:scalar 100><y:scalar 100>
<player:box inline=false left=100 top=100 width=40 height=40 border=0 backgroundColor={orange} {}>
<console.onKeyDown.push
    <if <eq key, 37> then=<x | sub x, 10>>,
    <if <eq key, 39> then=<x | add x, 10>>,
    <if <eq key, 38> then=<y | sub y, 10>>,
    <if <eq key, 40> then=<y | add y, 10>>,
    <player.left x>,
    <player.top y>
>
''')
input()

Try it

  1. Stop the box in the arrow keys example from going off the left or top edge of the screen.
  2. Make a box that shows the word "Pressed!" only while the mouse button is held down over it.
  3. Make a drawing program: when the user clicks in a box, put a small dot there. (Hint: push a small box with inline=false into the drawing area, using the mouse position.)
  4. Add an onExit to the scoreboard from the chapter on instance names that asks "Are you sure?" before quitting.