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.
| Field | Happens when... |
|---|---|
| onClick | the user clicks the tag |
| onMouseDown | a mouse button is pressed over the tag |
| onMouseUp | a mouse button is released over the tag |
| onMouseIn | the pointer moves onto the tag |
| onMouseOut | the pointer moves off the tag |
| onMouseMove | the pointer moves while it's over the tag |
| onContextClick | the 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.
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}>
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:
| Key | key | Key | key |
|---|---|---|---|
| A to Z | 65 to 90 | Enter | 13 |
| 0 to 9 | 48 to 57 | Space | 32 |
| Left, Up, Right, Down | 37, 38, 39, 40 | Backspace | 8 |
| Shift | 16 | Tab | 9 |
| Ctrl | 17 |
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}>>>
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>>>
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.
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.
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 + '!')
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':
...
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}>>
>
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()
inline=false into the drawing area, using the mouse position.)