A common question that comes up on the forum is how to wait for user input. The answer is Penlets don’t wait for more input – they react to events.
(The following post is a massaged version of this thread on the forums.)
Consider a simple penlet where you want to ask the user to write some buttons on a page that they can tap on later. The penlet might display a first label (with a verbal prompt as well if you are being fancy), wait for input until the user pauses say for a second or two or does a double tap, then displays the next prompt. So how do you write the code? Do you display a prompt, wait for input, display the next prompt, wait for input again? Yes, but not as sequential code.
Bottom line is you don’t wait for input – you react to events then return, remembering the state you were in so when the next event occurs you know where you were up to.
I find that, as with most event based models, a state machine is a good way to go. That is, draw a diagram with every single state your application can be in. There will be an initial state, then maybe a state for drawing a control on a page where double tap completes the control drawing and moves the app on to another state. You end up with lots of states circles and ways to move from one state to another (arrows between circles where you write on the arrow when the arrow should be followed – e.g. on double-tap).
Another way of thinking about it is to write a big table where you have a column for each event type you are interested in, and a row for each state your application can be in. For each state you work out what you want to have happen for each column. (Normally there is some action you want to have occur plus the new state to go to.) The advantage of this approach is it makes you think about what to do for every event that can occur in every state that exists. But it captures the same information as the diagram.
Note: You may have events other than just pen up, pen down, double tap etc such as timers going off, or controls being tapped etc. Depends on the complexity of your application logic.
How to implement state? Basically you have an integer variable (or a combination of several variables) holding the current state. This is normally done using a field (member variable) of the base penlet class. In each callback method you do a switch on the state variable to work out what code should be executed when the app is in a particular state.
It may be obvious, but with an event based system you cannot (easily) write procedural code where you suddenly in the middle of the code say "I would like to collect user input now". You must exit from your penlet back to the pen OS and wait until you are called again with the next event.
This is often called the "reactor" pattern. You react to events that occur then exit your code until the next event comes along to react to. You can think of it like playing tennis/squash/ping pong etc. Something happens and you react (hit the ball back), then get ready for the next thing (event) that happens.
Note: I don’t like using switch statements that much actually (which is the normal way to do a state machine). In play apps I have been writing I have instead being writing a class per state, then having the main penlet class delegate calls through to my current state object. It just makes the code a bit more manageable when the code gets complex. If you only have 3 or 4 states a variable is fine. But as the code grows, you start having code for a single state scattered all over the place. To me its much nicer to group all the code for a particular state in one place (a class). Its easier to add new states later. It also allows you to add you own application specific event types such as "pen moved". Pen moved events you might generate by having a timer going off say every 20ms while the pen is down, then asking the stroke storage the current pen position. Its nicer to write your application logic in terms of a “pen moved” event rather than having all the complex logic for detecting pen movements etc mixed in with your mail application logic. But I discussed that a bit in a separate blog post.